From a9bcc81f821d7c66f623779fa5147e728eb3c388 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 03:24:41 +0200 Subject: Adding upstream version 3.3.0+dfsg1. Signed-off-by: Daniel Baumann --- winpr/CMakeLists.txt | 375 ++ winpr/WinPRConfig.cmake.in | 10 + winpr/include/CMakeLists.txt | 57 + winpr/include/config/build-config.h.in | 22 + winpr/include/config/buildflags.h.in | 11 + winpr/include/config/config.h.in | 46 + winpr/include/config/version.h.in | 32 + winpr/include/config/wtypes.h.in | 606 +++ winpr/include/winpr/asn1.h | 212 + winpr/include/winpr/assert.h | 60 + winpr/include/winpr/bcrypt.h | 338 ++ winpr/include/winpr/bitstream.h | 186 + winpr/include/winpr/clipboard.h | 109 + winpr/include/winpr/cmdline.h | 183 + winpr/include/winpr/collections.h | 871 ++++ winpr/include/winpr/comm.h | 565 +++ winpr/include/winpr/cred.h | 62 + winpr/include/winpr/crt.h | 233 + winpr/include/winpr/crypto.h | 26 + winpr/include/winpr/custom-crypto.h | 269 ++ winpr/include/winpr/debug.h | 45 + winpr/include/winpr/dsparse.h | 127 + winpr/include/winpr/endian.h | 196 + winpr/include/winpr/environment.h | 144 + winpr/include/winpr/error.h | 3111 +++++++++++++ winpr/include/winpr/file.h | 550 +++ winpr/include/winpr/handle.h | 64 + winpr/include/winpr/image.h | 121 + winpr/include/winpr/ini.h | 157 + winpr/include/winpr/input.h | 909 ++++ winpr/include/winpr/interlocked.h | 216 + winpr/include/winpr/intrin.h | 93 + winpr/include/winpr/io.h | 254 ++ winpr/include/winpr/library.h | 119 + winpr/include/winpr/memory.h | 76 + winpr/include/winpr/ncrypt.h | 219 + winpr/include/winpr/nt.h | 1575 +++++++ winpr/include/winpr/ntlm.h | 68 + winpr/include/winpr/pack.h | 100 + winpr/include/winpr/path.h | 356 ++ winpr/include/winpr/pipe.h | 127 + winpr/include/winpr/platform.h | 352 ++ winpr/include/winpr/pool.h | 282 ++ winpr/include/winpr/print.h | 54 + winpr/include/winpr/registry.h | 426 ++ winpr/include/winpr/rpc.h | 725 +++ winpr/include/winpr/sam.h | 59 + winpr/include/winpr/schannel.h | 284 ++ winpr/include/winpr/secapi.h | 78 + winpr/include/winpr/security.h | 449 ++ winpr/include/winpr/shell.h | 108 + winpr/include/winpr/smartcard.h | 1217 +++++ winpr/include/winpr/spec.h | 986 +++++ winpr/include/winpr/ssl.h | 49 + winpr/include/winpr/sspi.h | 1436 ++++++ winpr/include/winpr/sspicli.h | 147 + winpr/include/winpr/stream.h | 842 ++++ winpr/include/winpr/string.h | 434 ++ winpr/include/winpr/strlst.h | 41 + winpr/include/winpr/synch.h | 423 ++ winpr/include/winpr/sysinfo.h | 358 ++ winpr/include/winpr/tchar.h | 70 + winpr/include/winpr/thread.h | 257 ++ winpr/include/winpr/timezone.h | 115 + winpr/include/winpr/tools/makecert.h | 50 + winpr/include/winpr/user.h | 296 ++ winpr/include/winpr/wincrypt.h | 738 ++++ winpr/include/winpr/windows.h | 130 + winpr/include/winpr/winpr.h | 129 + winpr/include/winpr/winsock.h | 366 ++ winpr/include/winpr/wlog.h | 246 ++ winpr/include/winpr/wtsapi.h | 1512 +++++++ winpr/libwinpr/CMakeLists.txt | 213 + winpr/libwinpr/bcrypt/CMakeLists.txt | 19 + winpr/libwinpr/bcrypt/ModuleOptions.cmake | 9 + winpr/libwinpr/bcrypt/bcrypt.c | 114 + winpr/libwinpr/clipboard/CMakeLists.txt | 28 + winpr/libwinpr/clipboard/ModuleOptions.cmake | 9 + winpr/libwinpr/clipboard/clipboard.c | 738 ++++ winpr/libwinpr/clipboard/clipboard.h | 77 + winpr/libwinpr/clipboard/synthetic.c | 826 ++++ winpr/libwinpr/clipboard/synthetic_file.c | 1262 ++++++ winpr/libwinpr/clipboard/synthetic_file.h | 27 + winpr/libwinpr/clipboard/test/CMakeLists.txt | 27 + .../libwinpr/clipboard/test/TestClipboardFormats.c | 82 + winpr/libwinpr/clipboard/test/TestUri.c | 69 + winpr/libwinpr/comm/CMakeLists.txt | 40 + winpr/libwinpr/comm/ModuleOptions.cmake | 9 + winpr/libwinpr/comm/comm.c | 1426 ++++++ winpr/libwinpr/comm/comm.h | 111 + winpr/libwinpr/comm/comm_io.c | 549 +++ winpr/libwinpr/comm/comm_ioctl.c | 731 +++ winpr/libwinpr/comm/comm_ioctl.h | 236 + winpr/libwinpr/comm/comm_sercx2_sys.c | 200 + winpr/libwinpr/comm/comm_sercx2_sys.h | 40 + winpr/libwinpr/comm/comm_sercx_sys.c | 266 ++ winpr/libwinpr/comm/comm_sercx_sys.h | 40 + winpr/libwinpr/comm/comm_serial_sys.c | 1637 +++++++ winpr/libwinpr/comm/comm_serial_sys.h | 40 + winpr/libwinpr/comm/test/CMakeLists.txt | 35 + winpr/libwinpr/comm/test/TestCommConfig.c | 148 + winpr/libwinpr/comm/test/TestCommDevice.c | 115 + winpr/libwinpr/comm/test/TestCommMonitor.c | 70 + winpr/libwinpr/comm/test/TestControlSettings.c | 123 + winpr/libwinpr/comm/test/TestGetCommState.c | 138 + winpr/libwinpr/comm/test/TestHandflow.c | 92 + winpr/libwinpr/comm/test/TestSerialChars.c | 178 + winpr/libwinpr/comm/test/TestSetCommState.c | 332 ++ winpr/libwinpr/comm/test/TestTimeouts.c | 138 + winpr/libwinpr/crt/CMakeLists.txt | 46 + winpr/libwinpr/crt/ModuleOptions.cmake | 9 + winpr/libwinpr/crt/alignment.c | 268 ++ winpr/libwinpr/crt/buffer.c | 50 + winpr/libwinpr/crt/casing.c | 726 +++ winpr/libwinpr/crt/conversion.c | 46 + winpr/libwinpr/crt/memory.c | 42 + winpr/libwinpr/crt/string.c | 832 ++++ winpr/libwinpr/crt/test/CMakeLists.txt | 30 + winpr/libwinpr/crt/test/TestAlignment.c | 87 + winpr/libwinpr/crt/test/TestFormatSpecifiers.c | 164 + winpr/libwinpr/crt/test/TestString.c | 188 + winpr/libwinpr/crt/test/TestTypes.c | 115 + winpr/libwinpr/crt/test/TestUnicodeConversion.c | 1289 ++++++ winpr/libwinpr/crt/unicode.c | 657 +++ winpr/libwinpr/crt/unicode.h | 32 + winpr/libwinpr/crt/unicode_android.c | 183 + winpr/libwinpr/crt/unicode_apple.m | 146 + winpr/libwinpr/crt/unicode_builtin.c | 695 +++ winpr/libwinpr/crt/unicode_icu.c | 237 + winpr/libwinpr/crypto/CMakeLists.txt | 59 + winpr/libwinpr/crypto/ModuleOptions.cmake | 9 + winpr/libwinpr/crypto/cert.c | 223 + winpr/libwinpr/crypto/cipher.c | 747 ++++ winpr/libwinpr/crypto/crypto.c | 301 ++ winpr/libwinpr/crypto/crypto.h | 43 + winpr/libwinpr/crypto/hash.c | 780 ++++ winpr/libwinpr/crypto/hmac_md5.c | 57 + winpr/libwinpr/crypto/hmac_md5.h | 19 + winpr/libwinpr/crypto/md4.c | 275 ++ winpr/libwinpr/crypto/md4.h | 46 + winpr/libwinpr/crypto/md5.c | 295 ++ winpr/libwinpr/crypto/md5.h | 48 + winpr/libwinpr/crypto/rand.c | 83 + winpr/libwinpr/crypto/rc4.c | 88 + winpr/libwinpr/crypto/rc4.h | 34 + winpr/libwinpr/crypto/test/CMakeLists.txt | 34 + .../test/TestCryptoCertEnumCertificatesInStore.c | 86 + winpr/libwinpr/crypto/test/TestCryptoCipher.c | 232 + winpr/libwinpr/crypto/test/TestCryptoHash.c | 316 ++ winpr/libwinpr/crypto/test/TestCryptoProtectData.c | 10 + .../libwinpr/crypto/test/TestCryptoProtectMemory.c | 55 + winpr/libwinpr/crypto/test/TestCryptoRand.c | 26 + winpr/libwinpr/dsparse/CMakeLists.txt | 26 + winpr/libwinpr/dsparse/ModuleOptions.cmake | 9 + winpr/libwinpr/dsparse/dsparse.c | 142 + winpr/libwinpr/dsparse/test/CMakeLists.txt | 25 + winpr/libwinpr/dsparse/test/TestDsMakeSpn.c | 151 + winpr/libwinpr/dummy.c | 5 + winpr/libwinpr/environment/CMakeLists.txt | 22 + winpr/libwinpr/environment/ModuleOptions.cmake | 9 + winpr/libwinpr/environment/environment.c | 708 +++ winpr/libwinpr/environment/test/CMakeLists.txt | 28 + .../test/TestEnvironmentGetEnvironmentStrings.c | 43 + .../environment/test/TestEnvironmentGetSetEB.c | 138 + .../test/TestEnvironmentMergeEnvironmentStrings.c | 34 + .../test/TestEnvironmentSetEnvironmentVariable.c | 72 + winpr/libwinpr/error/CMakeLists.txt | 22 + winpr/libwinpr/error/ModuleOptions.cmake | 9 + winpr/libwinpr/error/error.c | 99 + winpr/libwinpr/error/test/CMakeLists.txt | 26 + winpr/libwinpr/error/test/TestErrorSetLastError.c | 135 + winpr/libwinpr/file/CMakeLists.txt | 22 + winpr/libwinpr/file/ModuleOptions.cmake | 9 + winpr/libwinpr/file/file.c | 1483 +++++++ winpr/libwinpr/file/file.h | 66 + winpr/libwinpr/file/generic.c | 1378 ++++++ winpr/libwinpr/file/namedPipeClient.c | 305 ++ winpr/libwinpr/file/pattern.c | 371 ++ winpr/libwinpr/file/test/CMakeLists.txt | 58 + winpr/libwinpr/file/test/TestFileCreateFile.c | 94 + winpr/libwinpr/file/test/TestFileDeleteFile.c | 50 + winpr/libwinpr/file/test/TestFileFindFirstFile.c | 326 ++ winpr/libwinpr/file/test/TestFileFindFirstFileEx.c | 10 + winpr/libwinpr/file/test/TestFileFindNextFile.c | 99 + winpr/libwinpr/file/test/TestFileGetStdHandle.c | 49 + winpr/libwinpr/file/test/TestFilePatternMatch.c | 182 + winpr/libwinpr/file/test/TestFileReadFile.c | 10 + winpr/libwinpr/file/test/TestFileWriteFile.c | 10 + winpr/libwinpr/file/test/TestSetFileAttributes.c | 152 + winpr/libwinpr/handle/CMakeLists.txt | 23 + winpr/libwinpr/handle/ModuleOptions.cmake | 9 + winpr/libwinpr/handle/handle.c | 81 + winpr/libwinpr/handle/handle.h | 198 + winpr/libwinpr/handle/nonehandle.c | 82 + winpr/libwinpr/handle/nonehandle.h | 40 + winpr/libwinpr/input/CMakeLists.txt | 21 + winpr/libwinpr/input/ModuleOptions.cmake | 9 + winpr/libwinpr/input/keycode.c | 910 ++++ winpr/libwinpr/input/scancode.c | 190 + winpr/libwinpr/input/virtualkey.c | 459 ++ winpr/libwinpr/interlocked/CMakeLists.txt | 22 + winpr/libwinpr/interlocked/ModuleOptions.cmake | 9 + winpr/libwinpr/interlocked/interlocked.c | 488 ++ winpr/libwinpr/interlocked/module_5.1.def | 13 + winpr/libwinpr/interlocked/test/CMakeLists.txt | 28 + .../interlocked/test/TestInterlockedAccess.c | 200 + .../interlocked/test/TestInterlockedDList.c | 79 + .../interlocked/test/TestInterlockedSList.c | 85 + winpr/libwinpr/io/CMakeLists.txt | 22 + winpr/libwinpr/io/ModuleOptions.cmake | 9 + winpr/libwinpr/io/device.c | 234 + winpr/libwinpr/io/io.c | 276 ++ winpr/libwinpr/io/io.h | 37 + winpr/libwinpr/io/test/CMakeLists.txt | 25 + winpr/libwinpr/io/test/TestIoGetOverlappedResult.c | 10 + winpr/libwinpr/library/CMakeLists.txt | 22 + winpr/libwinpr/library/ModuleOptions.cmake | 9 + winpr/libwinpr/library/library.c | 381 ++ winpr/libwinpr/library/test/CMakeLists.txt | 33 + .../library/test/TestLibraryA/CMakeLists.txt | 29 + .../library/test/TestLibraryA/TestLibraryA.c | 14 + .../library/test/TestLibraryB/CMakeLists.txt | 30 + .../library/test/TestLibraryB/TestLibraryB.c | 14 + .../library/test/TestLibraryGetModuleFileName.c | 56 + .../library/test/TestLibraryGetProcAddress.c | 89 + .../libwinpr/library/test/TestLibraryLoadLibrary.c | 51 + winpr/libwinpr/log.h | 27 + winpr/libwinpr/memory/CMakeLists.txt | 22 + winpr/libwinpr/memory/ModuleOptions.cmake | 9 + winpr/libwinpr/memory/memory.c | 128 + winpr/libwinpr/memory/memory.h | 30 + winpr/libwinpr/memory/test/CMakeLists.txt | 23 + .../memory/test/TestMemoryCreateFileMapping.c | 8 + winpr/libwinpr/ncrypt/CMakeLists.txt | 57 + winpr/libwinpr/ncrypt/ModuleOptions.cmake | 9 + winpr/libwinpr/ncrypt/ncrypt.c | 347 ++ winpr/libwinpr/ncrypt/ncrypt.h | 94 + winpr/libwinpr/ncrypt/ncrypt_pkcs11.c | 1297 ++++++ winpr/libwinpr/ncrypt/test/CMakeLists.txt | 29 + winpr/libwinpr/ncrypt/test/TestNCryptProviders.c | 51 + winpr/libwinpr/ncrypt/test/TestNCryptSmartcard.c | 156 + winpr/libwinpr/nt/CMakeLists.txt | 30 + winpr/libwinpr/nt/ModuleOptions.cmake | 8 + winpr/libwinpr/nt/nt.c | 69 + winpr/libwinpr/nt/ntstatus.c | 4660 ++++++++++++++++++++ winpr/libwinpr/nt/test/CMakeLists.txt | 26 + winpr/libwinpr/nt/test/TestNtCurrentTeb.c | 24 + winpr/libwinpr/path/CMakeLists.txt | 26 + winpr/libwinpr/path/ModuleOptions.cmake | 9 + winpr/libwinpr/path/include/PathAllocCombine.c | 180 + winpr/libwinpr/path/include/PathCchAddExtension.c | 101 + winpr/libwinpr/path/include/PathCchAddSeparator.c | 64 + .../libwinpr/path/include/PathCchAddSeparatorEx.c | 66 + winpr/libwinpr/path/include/PathCchAppend.c | 131 + winpr/libwinpr/path/path.c | 1181 +++++ winpr/libwinpr/path/shell.c | 821 ++++ winpr/libwinpr/path/shell_ios.h | 9 + winpr/libwinpr/path/shell_ios.m | 54 + winpr/libwinpr/path/test/CMakeLists.txt | 48 + .../libwinpr/path/test/TestPathAllocCanonicalize.c | 12 + winpr/libwinpr/path/test/TestPathAllocCombine.c | 98 + winpr/libwinpr/path/test/TestPathCchAddBackslash.c | 100 + .../libwinpr/path/test/TestPathCchAddBackslashEx.c | 103 + winpr/libwinpr/path/test/TestPathCchAddExtension.c | 140 + winpr/libwinpr/path/test/TestPathCchAppend.c | 151 + winpr/libwinpr/path/test/TestPathCchAppendEx.c | 12 + winpr/libwinpr/path/test/TestPathCchCanonicalize.c | 12 + .../libwinpr/path/test/TestPathCchCanonicalizeEx.c | 12 + winpr/libwinpr/path/test/TestPathCchCombine.c | 12 + winpr/libwinpr/path/test/TestPathCchCombineEx.c | 12 + .../libwinpr/path/test/TestPathCchFindExtension.c | 116 + winpr/libwinpr/path/test/TestPathCchIsRoot.c | 12 + .../path/test/TestPathCchRemoveBackslash.c | 12 + .../path/test/TestPathCchRemoveBackslashEx.c | 12 + .../path/test/TestPathCchRemoveExtension.c | 12 + .../libwinpr/path/test/TestPathCchRemoveFileSpec.c | 12 + .../path/test/TestPathCchRenameExtension.c | 12 + winpr/libwinpr/path/test/TestPathCchSkipRoot.c | 12 + winpr/libwinpr/path/test/TestPathCchStripPrefix.c | 128 + winpr/libwinpr/path/test/TestPathCchStripToRoot.c | 12 + winpr/libwinpr/path/test/TestPathIsUNCEx.c | 52 + winpr/libwinpr/path/test/TestPathMakePath.c | 83 + winpr/libwinpr/path/test/TestPathShell.c | 58 + winpr/libwinpr/pipe/CMakeLists.txt | 22 + winpr/libwinpr/pipe/ModuleOptions.cmake | 9 + winpr/libwinpr/pipe/pipe.c | 930 ++++ winpr/libwinpr/pipe/pipe.h | 75 + winpr/libwinpr/pipe/test/CMakeLists.txt | 27 + winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c | 510 +++ .../pipe/test/TestPipeCreateNamedPipeOverlapped.c | 397 ++ winpr/libwinpr/pipe/test/TestPipeCreatePipe.c | 72 + winpr/libwinpr/pool/CMakeLists.txt | 41 + winpr/libwinpr/pool/ModuleOptions.cmake | 9 + winpr/libwinpr/pool/callback.c | 54 + winpr/libwinpr/pool/callback_cleanup.c | 140 + winpr/libwinpr/pool/cleanup_group.c | 140 + winpr/libwinpr/pool/io.c | 49 + winpr/libwinpr/pool/pool.c | 251 ++ winpr/libwinpr/pool/pool.h | 122 + winpr/libwinpr/pool/synch.c | 44 + winpr/libwinpr/pool/test/CMakeLists.txt | 30 + winpr/libwinpr/pool/test/TestPoolIO.c | 8 + winpr/libwinpr/pool/test/TestPoolSynch.c | 8 + winpr/libwinpr/pool/test/TestPoolThread.c | 41 + winpr/libwinpr/pool/test/TestPoolTimer.c | 8 + winpr/libwinpr/pool/test/TestPoolWork.c | 136 + winpr/libwinpr/pool/timer.c | 50 + winpr/libwinpr/pool/work.c | 199 + winpr/libwinpr/registry/CMakeLists.txt | 21 + winpr/libwinpr/registry/ModuleOptions.cmake | 9 + winpr/libwinpr/registry/registry.c | 554 +++ winpr/libwinpr/registry/registry_reg.c | 570 +++ winpr/libwinpr/registry/registry_reg.h | 72 + winpr/libwinpr/rpc/CMakeLists.txt | 28 + winpr/libwinpr/rpc/ModuleOptions.cmake | 9 + winpr/libwinpr/rpc/rpc.c | 928 ++++ winpr/libwinpr/security/CMakeLists.txt | 22 + winpr/libwinpr/security/ModuleOptions.cmake | 8 + winpr/libwinpr/security/security.c | 226 + winpr/libwinpr/security/security.h | 45 + winpr/libwinpr/security/test/CMakeLists.txt | 23 + winpr/libwinpr/security/test/TestSecurityToken.c | 9 + winpr/libwinpr/shell/CMakeLists.txt | 20 + winpr/libwinpr/shell/ModuleOptions.cmake | 9 + winpr/libwinpr/shell/shell.c | 145 + winpr/libwinpr/smartcard/CMakeLists.txt | 60 + winpr/libwinpr/smartcard/ModuleOptions.cmake | 9 + winpr/libwinpr/smartcard/smartcard.c | 1283 ++++++ winpr/libwinpr/smartcard/smartcard.h | 31 + winpr/libwinpr/smartcard/smartcard_inspect.c | 1363 ++++++ winpr/libwinpr/smartcard/smartcard_inspect.h | 30 + winpr/libwinpr/smartcard/smartcard_pcsc.c | 3357 ++++++++++++++ winpr/libwinpr/smartcard/smartcard_pcsc.h | 175 + winpr/libwinpr/smartcard/smartcard_windows.c | 126 + winpr/libwinpr/smartcard/smartcard_windows.h | 28 + winpr/libwinpr/smartcard/test/CMakeLists.txt | 26 + .../smartcard/test/TestSmartCardListReaders.c | 53 + .../libwinpr/smartcard/test/TestSmartCardStatus.c | 160 + winpr/libwinpr/sspi/CMakeLists.txt | 129 + winpr/libwinpr/sspi/CredSSP/credssp.c | 322 ++ winpr/libwinpr/sspi/CredSSP/credssp.h | 42 + winpr/libwinpr/sspi/Kerberos/kerberos.c | 1899 ++++++++ winpr/libwinpr/sspi/Kerberos/kerberos.h | 39 + winpr/libwinpr/sspi/Kerberos/krb5glue.h | 104 + winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c | 215 + winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c | 248 ++ winpr/libwinpr/sspi/ModuleOptions.cmake | 9 + winpr/libwinpr/sspi/NTLM/ntlm.c | 1526 +++++++ winpr/libwinpr/sspi/NTLM/ntlm.h | 301 ++ winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c | 775 ++++ winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h | 42 + winpr/libwinpr/sspi/NTLM/ntlm_compute.c | 887 ++++ winpr/libwinpr/sspi/NTLM/ntlm_compute.h | 64 + winpr/libwinpr/sspi/NTLM/ntlm_export.h | 40 + winpr/libwinpr/sspi/NTLM/ntlm_message.c | 1397 ++++++ winpr/libwinpr/sspi/NTLM/ntlm_message.h | 36 + winpr/libwinpr/sspi/Negotiate/negotiate.c | 1660 +++++++ winpr/libwinpr/sspi/Negotiate/negotiate.h | 57 + winpr/libwinpr/sspi/Schannel/schannel.c | 467 ++ winpr/libwinpr/sspi/Schannel/schannel.h | 53 + winpr/libwinpr/sspi/Schannel/schannel_openssl.c | 649 +++ winpr/libwinpr/sspi/Schannel/schannel_openssl.h | 52 + winpr/libwinpr/sspi/sspi.c | 1129 +++++ winpr/libwinpr/sspi/sspi.h | 93 + winpr/libwinpr/sspi/sspi_export.c | 345 ++ winpr/libwinpr/sspi/sspi_gss.c | 120 + winpr/libwinpr/sspi/sspi_gss.h | 85 + winpr/libwinpr/sspi/sspi_winpr.c | 2226 ++++++++++ winpr/libwinpr/sspi/sspi_winpr.h | 28 + winpr/libwinpr/sspi/test/CMakeLists.txt | 38 + .../sspi/test/TestAcquireCredentialsHandle.c | 60 + winpr/libwinpr/sspi/test/TestCredSSP.c | 8 + .../sspi/test/TestEnumerateSecurityPackages.c | 40 + .../sspi/test/TestInitializeSecurityContext.c | 115 + winpr/libwinpr/sspi/test/TestNTLM.c | 694 +++ .../sspi/test/TestQuerySecurityPackageInfo.c | 34 + winpr/libwinpr/sspi/test/TestSchannel.c | 854 ++++ winpr/libwinpr/sspicli/CMakeLists.txt | 18 + winpr/libwinpr/sspicli/ModuleOptions.cmake | 9 + winpr/libwinpr/sspicli/sspicli.c | 275 ++ winpr/libwinpr/synch/CMakeLists.txt | 40 + winpr/libwinpr/synch/ModuleOptions.cmake | 9 + winpr/libwinpr/synch/address.c | 46 + winpr/libwinpr/synch/barrier.c | 263 ++ winpr/libwinpr/synch/critical.c | 272 ++ winpr/libwinpr/synch/event.c | 584 +++ winpr/libwinpr/synch/event.h | 57 + winpr/libwinpr/synch/init.c | 96 + winpr/libwinpr/synch/mutex.c | 247 ++ winpr/libwinpr/synch/pollset.c | 274 ++ winpr/libwinpr/synch/pollset.h | 74 + winpr/libwinpr/synch/semaphore.c | 257 ++ winpr/libwinpr/synch/sleep.c | 148 + winpr/libwinpr/synch/synch.h | 159 + winpr/libwinpr/synch/test/CMakeLists.txt | 41 + winpr/libwinpr/synch/test/TestSynchAPC.c | 173 + winpr/libwinpr/synch/test/TestSynchBarrier.c | 257 ++ winpr/libwinpr/synch/test/TestSynchCritical.c | 363 ++ winpr/libwinpr/synch/test/TestSynchEvent.c | 94 + winpr/libwinpr/synch/test/TestSynchInit.c | 156 + .../libwinpr/synch/test/TestSynchMultipleThreads.c | 243 + winpr/libwinpr/synch/test/TestSynchMutex.c | 258 ++ winpr/libwinpr/synch/test/TestSynchSemaphore.c | 21 + winpr/libwinpr/synch/test/TestSynchThread.c | 131 + winpr/libwinpr/synch/test/TestSynchTimerQueue.c | 125 + winpr/libwinpr/synch/test/TestSynchWaitableTimer.c | 83 + .../synch/test/TestSynchWaitableTimerAPC.c | 92 + winpr/libwinpr/synch/timer.c | 1093 +++++ winpr/libwinpr/synch/wait.c | 583 +++ winpr/libwinpr/sysinfo/CMakeLists.txt | 31 + winpr/libwinpr/sysinfo/ModuleOptions.cmake | 9 + winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt | 20 + winpr/libwinpr/sysinfo/cpufeatures/NOTICE | 13 + winpr/libwinpr/sysinfo/cpufeatures/README | 4 + winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c | 1426 ++++++ winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h | 324 ++ winpr/libwinpr/sysinfo/sysinfo.c | 1122 +++++ winpr/libwinpr/sysinfo/test/CMakeLists.txt | 30 + winpr/libwinpr/sysinfo/test/TestCPUFeatures.c | 65 + winpr/libwinpr/sysinfo/test/TestGetComputerName.c | 366 ++ .../sysinfo/test/TestGetNativeSystemInfo.c | 29 + winpr/libwinpr/sysinfo/test/TestLocalTime.c | 21 + winpr/libwinpr/sysinfo/test/TestSystemTime.c | 21 + winpr/libwinpr/thread/CMakeLists.txt | 34 + winpr/libwinpr/thread/ModuleOptions.cmake | 9 + winpr/libwinpr/thread/apc.c | 271 ++ winpr/libwinpr/thread/apc.h | 85 + winpr/libwinpr/thread/argv.c | 279 ++ winpr/libwinpr/thread/process.c | 598 +++ winpr/libwinpr/thread/processor.c | 41 + winpr/libwinpr/thread/test/CMakeLists.txt | 27 + .../thread/test/TestThreadCommandLineToArgv.c | 74 + .../libwinpr/thread/test/TestThreadCreateProcess.c | 156 + winpr/libwinpr/thread/test/TestThreadExitThread.c | 53 + winpr/libwinpr/thread/thread.c | 1045 +++++ winpr/libwinpr/thread/thread.h | 95 + winpr/libwinpr/thread/tls.c | 72 + winpr/libwinpr/timezone/CMakeLists.txt | 18 + winpr/libwinpr/timezone/ModuleOptions.cmake | 9 + winpr/libwinpr/timezone/TimeZones.c | 4099 +++++++++++++++++ winpr/libwinpr/timezone/TimeZones.h | 34 + winpr/libwinpr/timezone/WindowsZones.c | 530 +++ winpr/libwinpr/timezone/WindowsZones.h | 18 + winpr/libwinpr/timezone/timezone.c | 581 +++ winpr/libwinpr/utils/CMakeLists.txt | 228 + winpr/libwinpr/utils/ModuleOptions.cmake | 9 + winpr/libwinpr/utils/android.c | 77 + winpr/libwinpr/utils/android.h | 30 + winpr/libwinpr/utils/asn1/asn1.c | 1490 +++++++ winpr/libwinpr/utils/cmdline.c | 850 ++++ winpr/libwinpr/utils/collections/ArrayList.c | 604 +++ winpr/libwinpr/utils/collections/BitStream.c | 176 + winpr/libwinpr/utils/collections/BufferPool.c | 558 +++ winpr/libwinpr/utils/collections/CountdownEvent.c | 203 + winpr/libwinpr/utils/collections/HashTable.c | 870 ++++ winpr/libwinpr/utils/collections/LinkedList.c | 385 ++ winpr/libwinpr/utils/collections/ListDictionary.c | 562 +++ winpr/libwinpr/utils/collections/MessagePipe.c | 80 + winpr/libwinpr/utils/collections/MessageQueue.c | 313 ++ winpr/libwinpr/utils/collections/Object.c | 42 + winpr/libwinpr/utils/collections/ObjectPool.c | 185 + winpr/libwinpr/utils/collections/PubSub.c | 265 ++ winpr/libwinpr/utils/collections/Queue.c | 345 ++ winpr/libwinpr/utils/collections/Stack.c | 252 ++ winpr/libwinpr/utils/collections/StreamPool.c | 407 ++ winpr/libwinpr/utils/corkscrew/backtrace.h | 120 + winpr/libwinpr/utils/corkscrew/debug.c | 260 ++ winpr/libwinpr/utils/corkscrew/debug.h | 40 + winpr/libwinpr/utils/corkscrew/demangle.h | 43 + winpr/libwinpr/utils/corkscrew/map_info.h | 76 + winpr/libwinpr/utils/corkscrew/ptrace.h | 139 + winpr/libwinpr/utils/corkscrew/symbol_table.h | 62 + winpr/libwinpr/utils/debug.c | 235 + winpr/libwinpr/utils/execinfo/debug.c | 94 + winpr/libwinpr/utils/execinfo/debug.h | 40 + winpr/libwinpr/utils/image.c | 1258 ++++++ winpr/libwinpr/utils/ini.c | 889 ++++ winpr/libwinpr/utils/ntlm.c | 182 + winpr/libwinpr/utils/print.c | 262 ++ winpr/libwinpr/utils/sam.c | 366 ++ winpr/libwinpr/utils/ssl.c | 447 ++ winpr/libwinpr/utils/stream.c | 523 +++ winpr/libwinpr/utils/stream.h | 28 + winpr/libwinpr/utils/strlst.c | 74 + winpr/libwinpr/utils/test/CMakeLists.txt | 55 + winpr/libwinpr/utils/test/TestASN1.c | 335 ++ winpr/libwinpr/utils/test/TestArrayList.c | 82 + winpr/libwinpr/utils/test/TestBacktrace.c | 34 + winpr/libwinpr/utils/test/TestBitStream.c | 86 + winpr/libwinpr/utils/test/TestBufferPool.c | 68 + winpr/libwinpr/utils/test/TestCmdLine.c | 352 ++ winpr/libwinpr/utils/test/TestHashTable.c | 448 ++ winpr/libwinpr/utils/test/TestImage.c | 232 + winpr/libwinpr/utils/test/TestIni.c | 160 + winpr/libwinpr/utils/test/TestLinkedList.c | 135 + winpr/libwinpr/utils/test/TestListDictionary.c | 178 + winpr/libwinpr/utils/test/TestMessagePipe.c | 105 + winpr/libwinpr/utils/test/TestMessageQueue.c | 56 + winpr/libwinpr/utils/test/TestPrint.c | 401 ++ winpr/libwinpr/utils/test/TestPubSub.c | 73 + winpr/libwinpr/utils/test/TestQueue.c | 58 + winpr/libwinpr/utils/test/TestStream.c | 682 +++ winpr/libwinpr/utils/test/TestStreamPool.c | 78 + winpr/libwinpr/utils/test/TestVersion.c | 47 + winpr/libwinpr/utils/test/TestWLog.c | 69 + winpr/libwinpr/utils/test/TestWLogCallback.c | 128 + winpr/libwinpr/utils/test/lodepng_32bit.bmp | Bin 0 -> 16522 bytes winpr/libwinpr/utils/test/lodepng_32bit.png | Bin 0 -> 3968 bytes winpr/libwinpr/utils/test/rgb.16.bmp | Bin 0 -> 1966218 bytes winpr/libwinpr/utils/test/rgb.16.nocolor.bmp | Bin 0 -> 1966150 bytes winpr/libwinpr/utils/test/rgb.16a.bmp | Bin 0 -> 1966218 bytes winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp | Bin 0 -> 1966150 bytes winpr/libwinpr/utils/test/rgb.16x.bmp | Bin 0 -> 1966218 bytes winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp | Bin 0 -> 1966150 bytes winpr/libwinpr/utils/test/rgb.24.bmp | Bin 0 -> 2949258 bytes winpr/libwinpr/utils/test/rgb.24.nocolor.bmp | Bin 0 -> 2949174 bytes winpr/libwinpr/utils/test/rgb.32.bmp | Bin 0 -> 3932298 bytes winpr/libwinpr/utils/test/rgb.32.nocolor.bmp | Bin 0 -> 3932230 bytes winpr/libwinpr/utils/test/rgb.32x.bmp | Bin 0 -> 3932298 bytes winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp | Bin 0 -> 3932230 bytes winpr/libwinpr/utils/test/rgb.bmp | Bin 0 -> 2949258 bytes winpr/libwinpr/utils/test/rgb.jpg | Bin 0 -> 280142 bytes winpr/libwinpr/utils/test/rgb.png | Bin 0 -> 70251 bytes winpr/libwinpr/utils/test/rgb.webp | Bin 0 -> 20912 bytes winpr/libwinpr/utils/unwind/debug.c | 132 + winpr/libwinpr/utils/unwind/debug.h | 41 + winpr/libwinpr/utils/windows/debug.c | 168 + winpr/libwinpr/utils/windows/debug.h | 40 + winpr/libwinpr/utils/winpr.c | 67 + winpr/libwinpr/utils/wlog/Appender.c | 176 + winpr/libwinpr/utils/wlog/Appender.h | 39 + winpr/libwinpr/utils/wlog/BinaryAppender.c | 238 + winpr/libwinpr/utils/wlog/BinaryAppender.h | 28 + winpr/libwinpr/utils/wlog/CallbackAppender.c | 168 + winpr/libwinpr/utils/wlog/CallbackAppender.h | 28 + winpr/libwinpr/utils/wlog/ConsoleAppender.c | 276 ++ winpr/libwinpr/utils/wlog/ConsoleAppender.h | 28 + winpr/libwinpr/utils/wlog/DataMessage.c | 48 + winpr/libwinpr/utils/wlog/DataMessage.h | 25 + winpr/libwinpr/utils/wlog/FileAppender.c | 287 ++ winpr/libwinpr/utils/wlog/FileAppender.h | 28 + winpr/libwinpr/utils/wlog/ImageMessage.c | 37 + winpr/libwinpr/utils/wlog/ImageMessage.h | 25 + winpr/libwinpr/utils/wlog/JournaldAppender.c | 210 + winpr/libwinpr/utils/wlog/JournaldAppender.h | 31 + winpr/libwinpr/utils/wlog/Layout.c | 375 ++ winpr/libwinpr/utils/wlog/Layout.h | 41 + winpr/libwinpr/utils/wlog/Message.c | 64 + winpr/libwinpr/utils/wlog/Message.h | 29 + winpr/libwinpr/utils/wlog/PacketMessage.c | 487 ++ winpr/libwinpr/utils/wlog/PacketMessage.h | 111 + winpr/libwinpr/utils/wlog/SyslogAppender.c | 137 + winpr/libwinpr/utils/wlog/SyslogAppender.h | 32 + winpr/libwinpr/utils/wlog/UdpAppender.c | 222 + winpr/libwinpr/utils/wlog/UdpAppender.h | 34 + winpr/libwinpr/utils/wlog/wlog.c | 1071 +++++ winpr/libwinpr/utils/wlog/wlog.h | 92 + winpr/libwinpr/winsock/CMakeLists.txt | 22 + winpr/libwinpr/winsock/ModuleOptions.cmake | 8 + winpr/libwinpr/winsock/winsock.c | 1290 ++++++ winpr/libwinpr/wtsapi/CMakeLists.txt | 30 + winpr/libwinpr/wtsapi/ModuleOptions.cmake | 9 + winpr/libwinpr/wtsapi/test/CMakeLists.txt | 74 + .../wtsapi/test/TestWtsApiEnumerateProcesses.c | 49 + .../wtsapi/test/TestWtsApiEnumerateSessions.c | 50 + .../wtsapi/test/TestWtsApiExtraDisconnectSession.c | 22 + .../test/TestWtsApiExtraDynamicVirtualChannel.c | 53 + .../wtsapi/test/TestWtsApiExtraLogoffSession.c | 22 + .../wtsapi/test/TestWtsApiExtraSendMessage.c | 30 + .../test/TestWtsApiExtraStartRemoteSessionEx.c | 31 + .../wtsapi/test/TestWtsApiExtraVirtualChannel.c | 53 + .../test/TestWtsApiQuerySessionInformation.c | 225 + .../wtsapi/test/TestWtsApiSessionNotification.c | 62 + .../wtsapi/test/TestWtsApiShutdownSystem.c | 35 + .../wtsapi/test/TestWtsApiWaitSystemEvent.c | 39 + winpr/libwinpr/wtsapi/wtsapi.c | 802 ++++ winpr/libwinpr/wtsapi/wtsapi_win32.c | 808 ++++ winpr/libwinpr/wtsapi/wtsapi_win32.h | 27 + winpr/test/CMakeLists.txt | 24 + winpr/test/TestIntrinsics.c | 121 + winpr/test/TestTypes.c | 214 + winpr/tools/CMakeLists.txt | 149 + winpr/tools/WinPR-toolsConfig.cmake.in | 12 + winpr/tools/hash-cli/CMakeLists.txt | 59 + winpr/tools/hash-cli/hash.c | 216 + winpr/tools/hash-cli/winpr-hash.1.in | 42 + winpr/tools/makecert-cli/CMakeLists.txt | 63 + winpr/tools/makecert-cli/main.c | 45 + winpr/tools/makecert-cli/winpr-makecert.1.in | 116 + winpr/tools/makecert/CMakeLists.txt | 49 + winpr/tools/makecert/makecert.c | 1165 +++++ winpr/tools/winpr-tools.pc.in | 15 + winpr/winpr.pc.in | 15 + winpr/wlog.7.in | 149 + 594 files changed, 142860 insertions(+) create mode 100644 winpr/CMakeLists.txt create mode 100644 winpr/WinPRConfig.cmake.in create mode 100644 winpr/include/CMakeLists.txt create mode 100644 winpr/include/config/build-config.h.in create mode 100644 winpr/include/config/buildflags.h.in create mode 100644 winpr/include/config/config.h.in create mode 100644 winpr/include/config/version.h.in create mode 100644 winpr/include/config/wtypes.h.in create mode 100644 winpr/include/winpr/asn1.h create mode 100644 winpr/include/winpr/assert.h create mode 100644 winpr/include/winpr/bcrypt.h create mode 100644 winpr/include/winpr/bitstream.h create mode 100644 winpr/include/winpr/clipboard.h create mode 100644 winpr/include/winpr/cmdline.h create mode 100644 winpr/include/winpr/collections.h create mode 100644 winpr/include/winpr/comm.h create mode 100644 winpr/include/winpr/cred.h create mode 100644 winpr/include/winpr/crt.h create mode 100644 winpr/include/winpr/crypto.h create mode 100644 winpr/include/winpr/custom-crypto.h create mode 100644 winpr/include/winpr/debug.h create mode 100644 winpr/include/winpr/dsparse.h create mode 100644 winpr/include/winpr/endian.h create mode 100644 winpr/include/winpr/environment.h create mode 100644 winpr/include/winpr/error.h create mode 100644 winpr/include/winpr/file.h create mode 100644 winpr/include/winpr/handle.h create mode 100644 winpr/include/winpr/image.h create mode 100644 winpr/include/winpr/ini.h create mode 100644 winpr/include/winpr/input.h create mode 100644 winpr/include/winpr/interlocked.h create mode 100644 winpr/include/winpr/intrin.h create mode 100644 winpr/include/winpr/io.h create mode 100644 winpr/include/winpr/library.h create mode 100644 winpr/include/winpr/memory.h create mode 100644 winpr/include/winpr/ncrypt.h create mode 100644 winpr/include/winpr/nt.h create mode 100644 winpr/include/winpr/ntlm.h create mode 100644 winpr/include/winpr/pack.h create mode 100644 winpr/include/winpr/path.h create mode 100644 winpr/include/winpr/pipe.h create mode 100644 winpr/include/winpr/platform.h create mode 100644 winpr/include/winpr/pool.h create mode 100644 winpr/include/winpr/print.h create mode 100644 winpr/include/winpr/registry.h create mode 100644 winpr/include/winpr/rpc.h create mode 100644 winpr/include/winpr/sam.h create mode 100644 winpr/include/winpr/schannel.h create mode 100644 winpr/include/winpr/secapi.h create mode 100644 winpr/include/winpr/security.h create mode 100644 winpr/include/winpr/shell.h create mode 100644 winpr/include/winpr/smartcard.h create mode 100644 winpr/include/winpr/spec.h create mode 100644 winpr/include/winpr/ssl.h create mode 100644 winpr/include/winpr/sspi.h create mode 100644 winpr/include/winpr/sspicli.h create mode 100644 winpr/include/winpr/stream.h create mode 100644 winpr/include/winpr/string.h create mode 100644 winpr/include/winpr/strlst.h create mode 100644 winpr/include/winpr/synch.h create mode 100644 winpr/include/winpr/sysinfo.h create mode 100644 winpr/include/winpr/tchar.h create mode 100644 winpr/include/winpr/thread.h create mode 100644 winpr/include/winpr/timezone.h create mode 100644 winpr/include/winpr/tools/makecert.h create mode 100644 winpr/include/winpr/user.h create mode 100644 winpr/include/winpr/wincrypt.h create mode 100644 winpr/include/winpr/windows.h create mode 100644 winpr/include/winpr/winpr.h create mode 100644 winpr/include/winpr/winsock.h create mode 100644 winpr/include/winpr/wlog.h create mode 100644 winpr/include/winpr/wtsapi.h create mode 100644 winpr/libwinpr/CMakeLists.txt create mode 100644 winpr/libwinpr/bcrypt/CMakeLists.txt create mode 100644 winpr/libwinpr/bcrypt/ModuleOptions.cmake create mode 100644 winpr/libwinpr/bcrypt/bcrypt.c create mode 100644 winpr/libwinpr/clipboard/CMakeLists.txt create mode 100644 winpr/libwinpr/clipboard/ModuleOptions.cmake create mode 100644 winpr/libwinpr/clipboard/clipboard.c create mode 100644 winpr/libwinpr/clipboard/clipboard.h create mode 100644 winpr/libwinpr/clipboard/synthetic.c create mode 100644 winpr/libwinpr/clipboard/synthetic_file.c create mode 100644 winpr/libwinpr/clipboard/synthetic_file.h create mode 100644 winpr/libwinpr/clipboard/test/CMakeLists.txt create mode 100644 winpr/libwinpr/clipboard/test/TestClipboardFormats.c create mode 100644 winpr/libwinpr/clipboard/test/TestUri.c create mode 100644 winpr/libwinpr/comm/CMakeLists.txt create mode 100644 winpr/libwinpr/comm/ModuleOptions.cmake create mode 100644 winpr/libwinpr/comm/comm.c create mode 100644 winpr/libwinpr/comm/comm.h create mode 100644 winpr/libwinpr/comm/comm_io.c create mode 100644 winpr/libwinpr/comm/comm_ioctl.c create mode 100644 winpr/libwinpr/comm/comm_ioctl.h create mode 100644 winpr/libwinpr/comm/comm_sercx2_sys.c create mode 100644 winpr/libwinpr/comm/comm_sercx2_sys.h create mode 100644 winpr/libwinpr/comm/comm_sercx_sys.c create mode 100644 winpr/libwinpr/comm/comm_sercx_sys.h create mode 100644 winpr/libwinpr/comm/comm_serial_sys.c create mode 100644 winpr/libwinpr/comm/comm_serial_sys.h create mode 100644 winpr/libwinpr/comm/test/CMakeLists.txt create mode 100644 winpr/libwinpr/comm/test/TestCommConfig.c create mode 100644 winpr/libwinpr/comm/test/TestCommDevice.c create mode 100644 winpr/libwinpr/comm/test/TestCommMonitor.c create mode 100644 winpr/libwinpr/comm/test/TestControlSettings.c create mode 100644 winpr/libwinpr/comm/test/TestGetCommState.c create mode 100644 winpr/libwinpr/comm/test/TestHandflow.c create mode 100644 winpr/libwinpr/comm/test/TestSerialChars.c create mode 100644 winpr/libwinpr/comm/test/TestSetCommState.c create mode 100644 winpr/libwinpr/comm/test/TestTimeouts.c create mode 100644 winpr/libwinpr/crt/CMakeLists.txt create mode 100644 winpr/libwinpr/crt/ModuleOptions.cmake create mode 100644 winpr/libwinpr/crt/alignment.c create mode 100644 winpr/libwinpr/crt/buffer.c create mode 100644 winpr/libwinpr/crt/casing.c create mode 100644 winpr/libwinpr/crt/conversion.c create mode 100644 winpr/libwinpr/crt/memory.c create mode 100644 winpr/libwinpr/crt/string.c create mode 100644 winpr/libwinpr/crt/test/CMakeLists.txt create mode 100644 winpr/libwinpr/crt/test/TestAlignment.c create mode 100644 winpr/libwinpr/crt/test/TestFormatSpecifiers.c create mode 100644 winpr/libwinpr/crt/test/TestString.c create mode 100644 winpr/libwinpr/crt/test/TestTypes.c create mode 100644 winpr/libwinpr/crt/test/TestUnicodeConversion.c create mode 100644 winpr/libwinpr/crt/unicode.c create mode 100644 winpr/libwinpr/crt/unicode.h create mode 100644 winpr/libwinpr/crt/unicode_android.c create mode 100644 winpr/libwinpr/crt/unicode_apple.m create mode 100644 winpr/libwinpr/crt/unicode_builtin.c create mode 100644 winpr/libwinpr/crt/unicode_icu.c create mode 100644 winpr/libwinpr/crypto/CMakeLists.txt create mode 100644 winpr/libwinpr/crypto/ModuleOptions.cmake create mode 100644 winpr/libwinpr/crypto/cert.c create mode 100644 winpr/libwinpr/crypto/cipher.c create mode 100644 winpr/libwinpr/crypto/crypto.c create mode 100644 winpr/libwinpr/crypto/crypto.h create mode 100644 winpr/libwinpr/crypto/hash.c create mode 100644 winpr/libwinpr/crypto/hmac_md5.c create mode 100644 winpr/libwinpr/crypto/hmac_md5.h create mode 100644 winpr/libwinpr/crypto/md4.c create mode 100644 winpr/libwinpr/crypto/md4.h create mode 100644 winpr/libwinpr/crypto/md5.c create mode 100644 winpr/libwinpr/crypto/md5.h create mode 100644 winpr/libwinpr/crypto/rand.c create mode 100644 winpr/libwinpr/crypto/rc4.c create mode 100644 winpr/libwinpr/crypto/rc4.h create mode 100644 winpr/libwinpr/crypto/test/CMakeLists.txt create mode 100644 winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c create mode 100644 winpr/libwinpr/crypto/test/TestCryptoCipher.c create mode 100644 winpr/libwinpr/crypto/test/TestCryptoHash.c create mode 100644 winpr/libwinpr/crypto/test/TestCryptoProtectData.c create mode 100644 winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c create mode 100644 winpr/libwinpr/crypto/test/TestCryptoRand.c create mode 100644 winpr/libwinpr/dsparse/CMakeLists.txt create mode 100644 winpr/libwinpr/dsparse/ModuleOptions.cmake create mode 100644 winpr/libwinpr/dsparse/dsparse.c create mode 100644 winpr/libwinpr/dsparse/test/CMakeLists.txt create mode 100644 winpr/libwinpr/dsparse/test/TestDsMakeSpn.c create mode 100644 winpr/libwinpr/dummy.c create mode 100644 winpr/libwinpr/environment/CMakeLists.txt create mode 100644 winpr/libwinpr/environment/ModuleOptions.cmake create mode 100644 winpr/libwinpr/environment/environment.c create mode 100644 winpr/libwinpr/environment/test/CMakeLists.txt create mode 100644 winpr/libwinpr/environment/test/TestEnvironmentGetEnvironmentStrings.c create mode 100644 winpr/libwinpr/environment/test/TestEnvironmentGetSetEB.c create mode 100644 winpr/libwinpr/environment/test/TestEnvironmentMergeEnvironmentStrings.c create mode 100644 winpr/libwinpr/environment/test/TestEnvironmentSetEnvironmentVariable.c create mode 100644 winpr/libwinpr/error/CMakeLists.txt create mode 100644 winpr/libwinpr/error/ModuleOptions.cmake create mode 100644 winpr/libwinpr/error/error.c create mode 100644 winpr/libwinpr/error/test/CMakeLists.txt create mode 100644 winpr/libwinpr/error/test/TestErrorSetLastError.c create mode 100644 winpr/libwinpr/file/CMakeLists.txt create mode 100644 winpr/libwinpr/file/ModuleOptions.cmake create mode 100644 winpr/libwinpr/file/file.c create mode 100644 winpr/libwinpr/file/file.h create mode 100644 winpr/libwinpr/file/generic.c create mode 100644 winpr/libwinpr/file/namedPipeClient.c create mode 100644 winpr/libwinpr/file/pattern.c create mode 100644 winpr/libwinpr/file/test/CMakeLists.txt create mode 100644 winpr/libwinpr/file/test/TestFileCreateFile.c create mode 100644 winpr/libwinpr/file/test/TestFileDeleteFile.c create mode 100644 winpr/libwinpr/file/test/TestFileFindFirstFile.c create mode 100644 winpr/libwinpr/file/test/TestFileFindFirstFileEx.c create mode 100644 winpr/libwinpr/file/test/TestFileFindNextFile.c create mode 100644 winpr/libwinpr/file/test/TestFileGetStdHandle.c create mode 100644 winpr/libwinpr/file/test/TestFilePatternMatch.c create mode 100644 winpr/libwinpr/file/test/TestFileReadFile.c create mode 100644 winpr/libwinpr/file/test/TestFileWriteFile.c create mode 100644 winpr/libwinpr/file/test/TestSetFileAttributes.c create mode 100644 winpr/libwinpr/handle/CMakeLists.txt create mode 100644 winpr/libwinpr/handle/ModuleOptions.cmake create mode 100644 winpr/libwinpr/handle/handle.c create mode 100644 winpr/libwinpr/handle/handle.h create mode 100644 winpr/libwinpr/handle/nonehandle.c create mode 100644 winpr/libwinpr/handle/nonehandle.h create mode 100644 winpr/libwinpr/input/CMakeLists.txt create mode 100644 winpr/libwinpr/input/ModuleOptions.cmake create mode 100644 winpr/libwinpr/input/keycode.c create mode 100644 winpr/libwinpr/input/scancode.c create mode 100644 winpr/libwinpr/input/virtualkey.c create mode 100644 winpr/libwinpr/interlocked/CMakeLists.txt create mode 100644 winpr/libwinpr/interlocked/ModuleOptions.cmake create mode 100644 winpr/libwinpr/interlocked/interlocked.c create mode 100644 winpr/libwinpr/interlocked/module_5.1.def create mode 100644 winpr/libwinpr/interlocked/test/CMakeLists.txt create mode 100644 winpr/libwinpr/interlocked/test/TestInterlockedAccess.c create mode 100644 winpr/libwinpr/interlocked/test/TestInterlockedDList.c create mode 100644 winpr/libwinpr/interlocked/test/TestInterlockedSList.c create mode 100644 winpr/libwinpr/io/CMakeLists.txt create mode 100644 winpr/libwinpr/io/ModuleOptions.cmake create mode 100644 winpr/libwinpr/io/device.c create mode 100644 winpr/libwinpr/io/io.c create mode 100644 winpr/libwinpr/io/io.h create mode 100644 winpr/libwinpr/io/test/CMakeLists.txt create mode 100644 winpr/libwinpr/io/test/TestIoGetOverlappedResult.c create mode 100644 winpr/libwinpr/library/CMakeLists.txt create mode 100644 winpr/libwinpr/library/ModuleOptions.cmake create mode 100644 winpr/libwinpr/library/library.c create mode 100644 winpr/libwinpr/library/test/CMakeLists.txt create mode 100644 winpr/libwinpr/library/test/TestLibraryA/CMakeLists.txt create mode 100644 winpr/libwinpr/library/test/TestLibraryA/TestLibraryA.c create mode 100644 winpr/libwinpr/library/test/TestLibraryB/CMakeLists.txt create mode 100644 winpr/libwinpr/library/test/TestLibraryB/TestLibraryB.c create mode 100644 winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c create mode 100644 winpr/libwinpr/library/test/TestLibraryGetProcAddress.c create mode 100644 winpr/libwinpr/library/test/TestLibraryLoadLibrary.c create mode 100644 winpr/libwinpr/log.h create mode 100644 winpr/libwinpr/memory/CMakeLists.txt create mode 100644 winpr/libwinpr/memory/ModuleOptions.cmake create mode 100644 winpr/libwinpr/memory/memory.c create mode 100644 winpr/libwinpr/memory/memory.h create mode 100644 winpr/libwinpr/memory/test/CMakeLists.txt create mode 100644 winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c create mode 100644 winpr/libwinpr/ncrypt/CMakeLists.txt create mode 100644 winpr/libwinpr/ncrypt/ModuleOptions.cmake create mode 100644 winpr/libwinpr/ncrypt/ncrypt.c create mode 100644 winpr/libwinpr/ncrypt/ncrypt.h create mode 100644 winpr/libwinpr/ncrypt/ncrypt_pkcs11.c create mode 100644 winpr/libwinpr/ncrypt/test/CMakeLists.txt create mode 100644 winpr/libwinpr/ncrypt/test/TestNCryptProviders.c create mode 100644 winpr/libwinpr/ncrypt/test/TestNCryptSmartcard.c create mode 100644 winpr/libwinpr/nt/CMakeLists.txt create mode 100644 winpr/libwinpr/nt/ModuleOptions.cmake create mode 100644 winpr/libwinpr/nt/nt.c create mode 100644 winpr/libwinpr/nt/ntstatus.c create mode 100644 winpr/libwinpr/nt/test/CMakeLists.txt create mode 100644 winpr/libwinpr/nt/test/TestNtCurrentTeb.c create mode 100644 winpr/libwinpr/path/CMakeLists.txt create mode 100644 winpr/libwinpr/path/ModuleOptions.cmake create mode 100644 winpr/libwinpr/path/include/PathAllocCombine.c create mode 100644 winpr/libwinpr/path/include/PathCchAddExtension.c create mode 100644 winpr/libwinpr/path/include/PathCchAddSeparator.c create mode 100644 winpr/libwinpr/path/include/PathCchAddSeparatorEx.c create mode 100644 winpr/libwinpr/path/include/PathCchAppend.c create mode 100644 winpr/libwinpr/path/path.c create mode 100644 winpr/libwinpr/path/shell.c create mode 100644 winpr/libwinpr/path/shell_ios.h create mode 100644 winpr/libwinpr/path/shell_ios.m create mode 100644 winpr/libwinpr/path/test/CMakeLists.txt create mode 100644 winpr/libwinpr/path/test/TestPathAllocCanonicalize.c create mode 100644 winpr/libwinpr/path/test/TestPathAllocCombine.c create mode 100644 winpr/libwinpr/path/test/TestPathCchAddBackslash.c create mode 100644 winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c create mode 100644 winpr/libwinpr/path/test/TestPathCchAddExtension.c create mode 100644 winpr/libwinpr/path/test/TestPathCchAppend.c create mode 100644 winpr/libwinpr/path/test/TestPathCchAppendEx.c create mode 100644 winpr/libwinpr/path/test/TestPathCchCanonicalize.c create mode 100644 winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c create mode 100644 winpr/libwinpr/path/test/TestPathCchCombine.c create mode 100644 winpr/libwinpr/path/test/TestPathCchCombineEx.c create mode 100644 winpr/libwinpr/path/test/TestPathCchFindExtension.c create mode 100644 winpr/libwinpr/path/test/TestPathCchIsRoot.c create mode 100644 winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c create mode 100644 winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c create mode 100644 winpr/libwinpr/path/test/TestPathCchRemoveExtension.c create mode 100644 winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c create mode 100644 winpr/libwinpr/path/test/TestPathCchRenameExtension.c create mode 100644 winpr/libwinpr/path/test/TestPathCchSkipRoot.c create mode 100644 winpr/libwinpr/path/test/TestPathCchStripPrefix.c create mode 100644 winpr/libwinpr/path/test/TestPathCchStripToRoot.c create mode 100644 winpr/libwinpr/path/test/TestPathIsUNCEx.c create mode 100644 winpr/libwinpr/path/test/TestPathMakePath.c create mode 100644 winpr/libwinpr/path/test/TestPathShell.c create mode 100644 winpr/libwinpr/pipe/CMakeLists.txt create mode 100644 winpr/libwinpr/pipe/ModuleOptions.cmake create mode 100644 winpr/libwinpr/pipe/pipe.c create mode 100644 winpr/libwinpr/pipe/pipe.h create mode 100644 winpr/libwinpr/pipe/test/CMakeLists.txt create mode 100644 winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c create mode 100644 winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c create mode 100644 winpr/libwinpr/pipe/test/TestPipeCreatePipe.c create mode 100644 winpr/libwinpr/pool/CMakeLists.txt create mode 100644 winpr/libwinpr/pool/ModuleOptions.cmake create mode 100644 winpr/libwinpr/pool/callback.c create mode 100644 winpr/libwinpr/pool/callback_cleanup.c create mode 100644 winpr/libwinpr/pool/cleanup_group.c create mode 100644 winpr/libwinpr/pool/io.c create mode 100644 winpr/libwinpr/pool/pool.c create mode 100644 winpr/libwinpr/pool/pool.h create mode 100644 winpr/libwinpr/pool/synch.c create mode 100644 winpr/libwinpr/pool/test/CMakeLists.txt create mode 100644 winpr/libwinpr/pool/test/TestPoolIO.c create mode 100644 winpr/libwinpr/pool/test/TestPoolSynch.c create mode 100644 winpr/libwinpr/pool/test/TestPoolThread.c create mode 100644 winpr/libwinpr/pool/test/TestPoolTimer.c create mode 100644 winpr/libwinpr/pool/test/TestPoolWork.c create mode 100644 winpr/libwinpr/pool/timer.c create mode 100644 winpr/libwinpr/pool/work.c create mode 100644 winpr/libwinpr/registry/CMakeLists.txt create mode 100644 winpr/libwinpr/registry/ModuleOptions.cmake create mode 100644 winpr/libwinpr/registry/registry.c create mode 100644 winpr/libwinpr/registry/registry_reg.c create mode 100644 winpr/libwinpr/registry/registry_reg.h create mode 100644 winpr/libwinpr/rpc/CMakeLists.txt create mode 100644 winpr/libwinpr/rpc/ModuleOptions.cmake create mode 100644 winpr/libwinpr/rpc/rpc.c create mode 100644 winpr/libwinpr/security/CMakeLists.txt create mode 100644 winpr/libwinpr/security/ModuleOptions.cmake create mode 100644 winpr/libwinpr/security/security.c create mode 100644 winpr/libwinpr/security/security.h create mode 100644 winpr/libwinpr/security/test/CMakeLists.txt create mode 100644 winpr/libwinpr/security/test/TestSecurityToken.c create mode 100644 winpr/libwinpr/shell/CMakeLists.txt create mode 100644 winpr/libwinpr/shell/ModuleOptions.cmake create mode 100644 winpr/libwinpr/shell/shell.c create mode 100644 winpr/libwinpr/smartcard/CMakeLists.txt create mode 100644 winpr/libwinpr/smartcard/ModuleOptions.cmake create mode 100644 winpr/libwinpr/smartcard/smartcard.c create mode 100644 winpr/libwinpr/smartcard/smartcard.h create mode 100644 winpr/libwinpr/smartcard/smartcard_inspect.c create mode 100644 winpr/libwinpr/smartcard/smartcard_inspect.h create mode 100644 winpr/libwinpr/smartcard/smartcard_pcsc.c create mode 100644 winpr/libwinpr/smartcard/smartcard_pcsc.h create mode 100644 winpr/libwinpr/smartcard/smartcard_windows.c create mode 100644 winpr/libwinpr/smartcard/smartcard_windows.h create mode 100644 winpr/libwinpr/smartcard/test/CMakeLists.txt create mode 100644 winpr/libwinpr/smartcard/test/TestSmartCardListReaders.c create mode 100644 winpr/libwinpr/smartcard/test/TestSmartCardStatus.c create mode 100644 winpr/libwinpr/sspi/CMakeLists.txt create mode 100644 winpr/libwinpr/sspi/CredSSP/credssp.c create mode 100644 winpr/libwinpr/sspi/CredSSP/credssp.h create mode 100644 winpr/libwinpr/sspi/Kerberos/kerberos.c create mode 100644 winpr/libwinpr/sspi/Kerberos/kerberos.h create mode 100644 winpr/libwinpr/sspi/Kerberos/krb5glue.h create mode 100644 winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c create mode 100644 winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c create mode 100644 winpr/libwinpr/sspi/ModuleOptions.cmake create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm.c create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm.h create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_compute.c create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_compute.h create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_export.h create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_message.c create mode 100644 winpr/libwinpr/sspi/NTLM/ntlm_message.h create mode 100644 winpr/libwinpr/sspi/Negotiate/negotiate.c create mode 100644 winpr/libwinpr/sspi/Negotiate/negotiate.h create mode 100644 winpr/libwinpr/sspi/Schannel/schannel.c create mode 100644 winpr/libwinpr/sspi/Schannel/schannel.h create mode 100644 winpr/libwinpr/sspi/Schannel/schannel_openssl.c create mode 100644 winpr/libwinpr/sspi/Schannel/schannel_openssl.h create mode 100644 winpr/libwinpr/sspi/sspi.c create mode 100644 winpr/libwinpr/sspi/sspi.h create mode 100644 winpr/libwinpr/sspi/sspi_export.c create mode 100644 winpr/libwinpr/sspi/sspi_gss.c create mode 100644 winpr/libwinpr/sspi/sspi_gss.h create mode 100644 winpr/libwinpr/sspi/sspi_winpr.c create mode 100644 winpr/libwinpr/sspi/sspi_winpr.h create mode 100644 winpr/libwinpr/sspi/test/CMakeLists.txt create mode 100644 winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c create mode 100644 winpr/libwinpr/sspi/test/TestCredSSP.c create mode 100644 winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c create mode 100644 winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c create mode 100644 winpr/libwinpr/sspi/test/TestNTLM.c create mode 100644 winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c create mode 100644 winpr/libwinpr/sspi/test/TestSchannel.c create mode 100644 winpr/libwinpr/sspicli/CMakeLists.txt create mode 100644 winpr/libwinpr/sspicli/ModuleOptions.cmake create mode 100644 winpr/libwinpr/sspicli/sspicli.c create mode 100644 winpr/libwinpr/synch/CMakeLists.txt create mode 100644 winpr/libwinpr/synch/ModuleOptions.cmake create mode 100644 winpr/libwinpr/synch/address.c create mode 100644 winpr/libwinpr/synch/barrier.c create mode 100644 winpr/libwinpr/synch/critical.c create mode 100644 winpr/libwinpr/synch/event.c create mode 100644 winpr/libwinpr/synch/event.h create mode 100644 winpr/libwinpr/synch/init.c create mode 100644 winpr/libwinpr/synch/mutex.c create mode 100644 winpr/libwinpr/synch/pollset.c create mode 100644 winpr/libwinpr/synch/pollset.h create mode 100644 winpr/libwinpr/synch/semaphore.c create mode 100644 winpr/libwinpr/synch/sleep.c create mode 100644 winpr/libwinpr/synch/synch.h create mode 100644 winpr/libwinpr/synch/test/CMakeLists.txt create mode 100644 winpr/libwinpr/synch/test/TestSynchAPC.c create mode 100644 winpr/libwinpr/synch/test/TestSynchBarrier.c create mode 100644 winpr/libwinpr/synch/test/TestSynchCritical.c create mode 100644 winpr/libwinpr/synch/test/TestSynchEvent.c create mode 100644 winpr/libwinpr/synch/test/TestSynchInit.c create mode 100644 winpr/libwinpr/synch/test/TestSynchMultipleThreads.c create mode 100644 winpr/libwinpr/synch/test/TestSynchMutex.c create mode 100644 winpr/libwinpr/synch/test/TestSynchSemaphore.c create mode 100644 winpr/libwinpr/synch/test/TestSynchThread.c create mode 100644 winpr/libwinpr/synch/test/TestSynchTimerQueue.c create mode 100644 winpr/libwinpr/synch/test/TestSynchWaitableTimer.c create mode 100644 winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c create mode 100644 winpr/libwinpr/synch/timer.c create mode 100644 winpr/libwinpr/synch/wait.c create mode 100644 winpr/libwinpr/sysinfo/CMakeLists.txt create mode 100644 winpr/libwinpr/sysinfo/ModuleOptions.cmake create mode 100644 winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt create mode 100644 winpr/libwinpr/sysinfo/cpufeatures/NOTICE create mode 100644 winpr/libwinpr/sysinfo/cpufeatures/README create mode 100644 winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c create mode 100644 winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h create mode 100644 winpr/libwinpr/sysinfo/sysinfo.c create mode 100644 winpr/libwinpr/sysinfo/test/CMakeLists.txt create mode 100644 winpr/libwinpr/sysinfo/test/TestCPUFeatures.c create mode 100644 winpr/libwinpr/sysinfo/test/TestGetComputerName.c create mode 100644 winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c create mode 100644 winpr/libwinpr/sysinfo/test/TestLocalTime.c create mode 100644 winpr/libwinpr/sysinfo/test/TestSystemTime.c create mode 100644 winpr/libwinpr/thread/CMakeLists.txt create mode 100644 winpr/libwinpr/thread/ModuleOptions.cmake create mode 100644 winpr/libwinpr/thread/apc.c create mode 100644 winpr/libwinpr/thread/apc.h create mode 100644 winpr/libwinpr/thread/argv.c create mode 100644 winpr/libwinpr/thread/process.c create mode 100644 winpr/libwinpr/thread/processor.c create mode 100644 winpr/libwinpr/thread/test/CMakeLists.txt create mode 100644 winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c create mode 100644 winpr/libwinpr/thread/test/TestThreadCreateProcess.c create mode 100644 winpr/libwinpr/thread/test/TestThreadExitThread.c create mode 100644 winpr/libwinpr/thread/thread.c create mode 100644 winpr/libwinpr/thread/thread.h create mode 100644 winpr/libwinpr/thread/tls.c create mode 100644 winpr/libwinpr/timezone/CMakeLists.txt create mode 100644 winpr/libwinpr/timezone/ModuleOptions.cmake create mode 100644 winpr/libwinpr/timezone/TimeZones.c create mode 100644 winpr/libwinpr/timezone/TimeZones.h create mode 100644 winpr/libwinpr/timezone/WindowsZones.c create mode 100644 winpr/libwinpr/timezone/WindowsZones.h create mode 100644 winpr/libwinpr/timezone/timezone.c create mode 100644 winpr/libwinpr/utils/CMakeLists.txt create mode 100644 winpr/libwinpr/utils/ModuleOptions.cmake create mode 100644 winpr/libwinpr/utils/android.c create mode 100644 winpr/libwinpr/utils/android.h create mode 100644 winpr/libwinpr/utils/asn1/asn1.c create mode 100644 winpr/libwinpr/utils/cmdline.c create mode 100644 winpr/libwinpr/utils/collections/ArrayList.c create mode 100644 winpr/libwinpr/utils/collections/BitStream.c create mode 100644 winpr/libwinpr/utils/collections/BufferPool.c create mode 100644 winpr/libwinpr/utils/collections/CountdownEvent.c create mode 100644 winpr/libwinpr/utils/collections/HashTable.c create mode 100644 winpr/libwinpr/utils/collections/LinkedList.c create mode 100644 winpr/libwinpr/utils/collections/ListDictionary.c create mode 100644 winpr/libwinpr/utils/collections/MessagePipe.c create mode 100644 winpr/libwinpr/utils/collections/MessageQueue.c create mode 100644 winpr/libwinpr/utils/collections/Object.c create mode 100644 winpr/libwinpr/utils/collections/ObjectPool.c create mode 100644 winpr/libwinpr/utils/collections/PubSub.c create mode 100644 winpr/libwinpr/utils/collections/Queue.c create mode 100644 winpr/libwinpr/utils/collections/Stack.c create mode 100644 winpr/libwinpr/utils/collections/StreamPool.c create mode 100644 winpr/libwinpr/utils/corkscrew/backtrace.h create mode 100644 winpr/libwinpr/utils/corkscrew/debug.c create mode 100644 winpr/libwinpr/utils/corkscrew/debug.h create mode 100644 winpr/libwinpr/utils/corkscrew/demangle.h create mode 100644 winpr/libwinpr/utils/corkscrew/map_info.h create mode 100644 winpr/libwinpr/utils/corkscrew/ptrace.h create mode 100644 winpr/libwinpr/utils/corkscrew/symbol_table.h create mode 100644 winpr/libwinpr/utils/debug.c create mode 100644 winpr/libwinpr/utils/execinfo/debug.c create mode 100644 winpr/libwinpr/utils/execinfo/debug.h create mode 100644 winpr/libwinpr/utils/image.c create mode 100644 winpr/libwinpr/utils/ini.c create mode 100644 winpr/libwinpr/utils/ntlm.c create mode 100644 winpr/libwinpr/utils/print.c create mode 100644 winpr/libwinpr/utils/sam.c create mode 100644 winpr/libwinpr/utils/ssl.c create mode 100644 winpr/libwinpr/utils/stream.c create mode 100644 winpr/libwinpr/utils/stream.h create mode 100644 winpr/libwinpr/utils/strlst.c create mode 100644 winpr/libwinpr/utils/test/CMakeLists.txt create mode 100644 winpr/libwinpr/utils/test/TestASN1.c create mode 100644 winpr/libwinpr/utils/test/TestArrayList.c create mode 100644 winpr/libwinpr/utils/test/TestBacktrace.c create mode 100644 winpr/libwinpr/utils/test/TestBitStream.c create mode 100644 winpr/libwinpr/utils/test/TestBufferPool.c create mode 100644 winpr/libwinpr/utils/test/TestCmdLine.c create mode 100644 winpr/libwinpr/utils/test/TestHashTable.c create mode 100644 winpr/libwinpr/utils/test/TestImage.c create mode 100644 winpr/libwinpr/utils/test/TestIni.c create mode 100644 winpr/libwinpr/utils/test/TestLinkedList.c create mode 100644 winpr/libwinpr/utils/test/TestListDictionary.c create mode 100644 winpr/libwinpr/utils/test/TestMessagePipe.c create mode 100644 winpr/libwinpr/utils/test/TestMessageQueue.c create mode 100644 winpr/libwinpr/utils/test/TestPrint.c create mode 100644 winpr/libwinpr/utils/test/TestPubSub.c create mode 100644 winpr/libwinpr/utils/test/TestQueue.c create mode 100644 winpr/libwinpr/utils/test/TestStream.c create mode 100644 winpr/libwinpr/utils/test/TestStreamPool.c create mode 100644 winpr/libwinpr/utils/test/TestVersion.c create mode 100644 winpr/libwinpr/utils/test/TestWLog.c create mode 100644 winpr/libwinpr/utils/test/TestWLogCallback.c create mode 100644 winpr/libwinpr/utils/test/lodepng_32bit.bmp create mode 100644 winpr/libwinpr/utils/test/lodepng_32bit.png create mode 100644 winpr/libwinpr/utils/test/rgb.16.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.16.nocolor.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.16a.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.16x.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.24.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.24.nocolor.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.32.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.32.nocolor.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.32x.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.bmp create mode 100644 winpr/libwinpr/utils/test/rgb.jpg create mode 100644 winpr/libwinpr/utils/test/rgb.png create mode 100644 winpr/libwinpr/utils/test/rgb.webp create mode 100644 winpr/libwinpr/utils/unwind/debug.c create mode 100644 winpr/libwinpr/utils/unwind/debug.h create mode 100644 winpr/libwinpr/utils/windows/debug.c create mode 100644 winpr/libwinpr/utils/windows/debug.h create mode 100644 winpr/libwinpr/utils/winpr.c create mode 100644 winpr/libwinpr/utils/wlog/Appender.c create mode 100644 winpr/libwinpr/utils/wlog/Appender.h create mode 100644 winpr/libwinpr/utils/wlog/BinaryAppender.c create mode 100644 winpr/libwinpr/utils/wlog/BinaryAppender.h create mode 100644 winpr/libwinpr/utils/wlog/CallbackAppender.c create mode 100644 winpr/libwinpr/utils/wlog/CallbackAppender.h create mode 100644 winpr/libwinpr/utils/wlog/ConsoleAppender.c create mode 100644 winpr/libwinpr/utils/wlog/ConsoleAppender.h create mode 100644 winpr/libwinpr/utils/wlog/DataMessage.c create mode 100644 winpr/libwinpr/utils/wlog/DataMessage.h create mode 100644 winpr/libwinpr/utils/wlog/FileAppender.c create mode 100644 winpr/libwinpr/utils/wlog/FileAppender.h create mode 100644 winpr/libwinpr/utils/wlog/ImageMessage.c create mode 100644 winpr/libwinpr/utils/wlog/ImageMessage.h create mode 100644 winpr/libwinpr/utils/wlog/JournaldAppender.c create mode 100644 winpr/libwinpr/utils/wlog/JournaldAppender.h create mode 100644 winpr/libwinpr/utils/wlog/Layout.c create mode 100644 winpr/libwinpr/utils/wlog/Layout.h create mode 100644 winpr/libwinpr/utils/wlog/Message.c create mode 100644 winpr/libwinpr/utils/wlog/Message.h create mode 100644 winpr/libwinpr/utils/wlog/PacketMessage.c create mode 100644 winpr/libwinpr/utils/wlog/PacketMessage.h create mode 100644 winpr/libwinpr/utils/wlog/SyslogAppender.c create mode 100644 winpr/libwinpr/utils/wlog/SyslogAppender.h create mode 100644 winpr/libwinpr/utils/wlog/UdpAppender.c create mode 100644 winpr/libwinpr/utils/wlog/UdpAppender.h create mode 100644 winpr/libwinpr/utils/wlog/wlog.c create mode 100644 winpr/libwinpr/utils/wlog/wlog.h create mode 100644 winpr/libwinpr/winsock/CMakeLists.txt create mode 100644 winpr/libwinpr/winsock/ModuleOptions.cmake create mode 100644 winpr/libwinpr/winsock/winsock.c create mode 100644 winpr/libwinpr/wtsapi/CMakeLists.txt create mode 100644 winpr/libwinpr/wtsapi/ModuleOptions.cmake create mode 100644 winpr/libwinpr/wtsapi/test/CMakeLists.txt create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c create mode 100644 winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c create mode 100644 winpr/libwinpr/wtsapi/wtsapi.c create mode 100644 winpr/libwinpr/wtsapi/wtsapi_win32.c create mode 100644 winpr/libwinpr/wtsapi/wtsapi_win32.h create mode 100644 winpr/test/CMakeLists.txt create mode 100644 winpr/test/TestIntrinsics.c create mode 100644 winpr/test/TestTypes.c create mode 100644 winpr/tools/CMakeLists.txt create mode 100644 winpr/tools/WinPR-toolsConfig.cmake.in create mode 100644 winpr/tools/hash-cli/CMakeLists.txt create mode 100644 winpr/tools/hash-cli/hash.c create mode 100644 winpr/tools/hash-cli/winpr-hash.1.in create mode 100644 winpr/tools/makecert-cli/CMakeLists.txt create mode 100644 winpr/tools/makecert-cli/main.c create mode 100644 winpr/tools/makecert-cli/winpr-makecert.1.in create mode 100644 winpr/tools/makecert/CMakeLists.txt create mode 100644 winpr/tools/makecert/makecert.c create mode 100644 winpr/tools/winpr-tools.pc.in create mode 100644 winpr/winpr.pc.in create mode 100644 winpr/wlog.7.in (limited to 'winpr') diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt new file mode 100644 index 0000000..c21668f --- /dev/null +++ b/winpr/CMakeLists.txt @@ -0,0 +1,375 @@ +# WinPR: Windows Portable Runtime +# winpr cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Include our extra modules +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/) + +if (NOT FREERDP_UNIFIED_BUILD) + cmake_minimum_required(VERSION 3.13) + + if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) + endif() + project(WinPR LANGUAGES C) + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED ON) + set(CMAKE_C_EXTENSIONS ON) + + include(CommonConfigOptions) + include(ConfigOptions) + + # Default to build shared libs + option(EXPORT_ALL_SYMBOLS "Export all symbols form library" OFF) + + if (EXPORT_ALL_SYMBOLS) + # set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + add_definitions(-DEXPORT_ALL_SYMBOLS) + endif() + + if(CMAKE_COMPILER_IS_GNUCC) + if(NOT EXPORT_ALL_SYMBOLS) + message(STATUS "GCC default symbol visibility: hidden") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + endif() + endif() + if(WITH_DEBUG_ALL) + message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!") + set(DEFAULT_DEBUG_OPTION "ON" CACHE INTERNAL "debug default") + else() + set(DEFAULT_DEBUG_OPTION "OFF" CACHE INTERNAL "debug default") + endif() +endif() + +if (WIN32 AND NOT UWP) + set(NATIVE_SSPI ON) +endif() + +if((NOT ANDROID AND NOT IOS AND NOT UWP) AND NOT WITH_MBEDTLS) + set(TOOLS_DEFAULT ON) +else() + set(TOOLS_DEFAULT OFF) +endif() + +if(WITH_MBEDTLS) + set(WITH_INTERNAL_RC4_DEFAULT ON) + set(WITH_INTERNAL_MD4_DEFAULT ON) + set(WITH_INTERNAL_MD5_DEFAULT OFF) +else() + set(WITH_INTERNAL_RC4_DEFAULT OFF) + set(WITH_INTERNAL_MD4_DEFAULT OFF) + set(WITH_INTERNAL_MD5_DEFAULT OFF) +endif() + +option(WITH_VERBOSE_WINPR_ASSERT "Compile with verbose WINPR_ASSERT." ON) +option(WITH_WINPR_TOOLS "Build WinPR helper binaries" ${TOOLS_DEFAULT}) +option(WITH_WINPR_DEPRECATED "Build WinPR deprecated symbols" OFF) +option(WITH_DEBUG_THREADS "Print thread debug messages, enables handle dump" ${DEFAULT_DEBUG_OPTION}) +option(WITH_DEBUG_EVENTS "Print event debug messages, enables handle dump" ${DEFAULT_DEBUG_OPTION}) +option(WITH_DEBUG_SYMBOLS "Pack debug symbols to installer" OFF) +option(WITH_NATIVE_SSPI "Use native SSPI modules" ${NATIVE_SSPI}) +option(WITH_SMARTCARD_INSPECT "Enable SmartCard API Inspector" OFF) +option(WITH_DEBUG_MUTEX "Print mutex debug messages" ${DEFAULT_DEBUG_OPTION}) +option(WITH_INTERNAL_RC4 "Use compiled in rc4 functions instead of OpenSSL/MBedTLS" ${WITH_INTERNAL_RC4_DEFAULT}) +option(WITH_INTERNAL_MD4 "Use compiled in md4 hash functions instead of OpenSSL/MBedTLS" ${WITH_INTERNAL_MD4_DEFAULT}) +option(WITH_INTERNAL_MD5 "Use compiled in md5 hash functions instead of OpenSSL/MBedTLS" ${WITH_INTERNAL_MD5_DEFAULT}) +option(WITH_UNICODE_BUILTIN "Use built-in Unicode conversion (don't use system-provided libraries)" OFF) + +# This option MUST be off to avoid symbol conflicts when loading an external SSPI module library +option(SSPI_DLL "Define and export SSPI API symbols for usage as a Windows SSPI DLL replacement" OFF) + +if(SSPI_DLL) + add_definitions("-DSSPI_DLL") +endif() + +option(WITH_DEBUG_NTLM "Print NTLM debug messages" ${DEFAULT_DEBUG_OPTION}) +if(WITH_DEBUG_NTLM) + message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!") +endif() + +option(WITH_DEBUG_NLA "Print authentication related debug messages." ${DEFAULT_DEBUG_OPTION}) +if(WITH_DEBUG_NLA) + message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!") +endif() + +if (WITH_WINPR_DEPRECATED) + add_definitions(-DWITH_WINPR_DEPRECATED) +endif() + +if (WITH_VERBOSE_WINPR_ASSERT) + add_definitions(-DWITH_VERBOSE_WINPR_ASSERT) +endif() + + +# Include cmake modules +include(CheckIncludeFiles) +include(CheckLibraryExists) +include(CheckSymbolExists) +include(CheckStructHasMember) +include(TestBigEndian) + +# Check for cmake compatibility (enable/disable features) +include(CheckCmakeCompat) +include(FindFeature) +include(FeatureSummary) +include(CheckCCompilerFlag) +include(InstallFreeRDPMan) +include(SetFreeRDPCMakeInstallDir) +include(CMakePackageConfigHelpers) + +if (NOT WIN32) + add_definitions(-DWINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT) +endif() + +# Soname versioning +set(VERSION_REGEX "^(.*)([0-9]+)\\.([0-9]+)\\.([0-9]+)-?(.*)") +set(RAW_VERSION_STRING "3.3.0") +if(EXISTS "${PROJECT_SOURCE_DIR}/.source_tag") + file(READ ${PROJECT_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) +elseif(USE_VERSION_FROM_GIT_TAG) + git_get_exact_tag(_GIT_TAG --tags --always) + if (NOT ${_GIT_TAG} STREQUAL "n/a") + string(REGEX MATCH ${VERSION_REGEX} FOUND_TAG "${_GIT_TAG}") + if (FOUND_TAG) + set(RAW_VERSION_STRING ${_GIT_TAG}) + endif() + endif() +endif() +string(STRIP ${RAW_VERSION_STRING} RAW_VERSION_STRING) + +string(REGEX REPLACE "${VERSION_REGEX}" "\\2" WINPR_VERSION_MAJOR "${RAW_VERSION_STRING}") +string(REGEX REPLACE "${VERSION_REGEX}" "\\3" WINPR_VERSION_MINOR "${RAW_VERSION_STRING}") +string(REGEX REPLACE "${VERSION_REGEX}" "\\4" WINPR_VERSION_REVISION "${RAW_VERSION_STRING}") +string(REGEX REPLACE "${VERSION_REGEX}" "\\5" WINPR_VERSION_SUFFIX "${RAW_VERSION_STRING}") + +set(WINPR_VERSION "${WINPR_VERSION_MAJOR}.${WINPR_VERSION_MINOR}.${WINPR_VERSION_REVISION}") +set(WINPR_API_VERSION "${WINPR_VERSION_MAJOR}") +if (WINPR_VERSION_SUFFIX) + set(WINPR_VERSION_FULL "${WINPR_VERSION}-${WINPR_VERSION_SUFFIX}") +else() + set(WINPR_VERSION_FULL "${WINPR_VERSION}") +endif() + +if(NOT IOS) + CHECK_SYMBOL_EXISTS(strndup string.h WINPR_HAVE_STRNDUP) + check_include_files(unistd.h WINPR_HAVE_UNISTD_H) + check_include_files(execinfo.h WINPR_HAVE_EXECINFO_HEADER) + if (WINPR_HAVE_EXECINFO_HEADER) + check_symbol_exists(backtrace execinfo.h WINPR_HAVE_EXECINFO_BACKTRACE) + check_symbol_exists(backtrace_symbols execinfo.h WINPR_HAVE_EXECINFO_BACKTRACE_SYMBOLS) + check_symbol_exists(backtrace_symbols_fd execinfo.h WINPR_HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD) + + # Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define + # the backtrace functions. Disable detection for these cases + if (WINPR_HAVE_EXECINFO_BACKTRACE AND WINPR_HAVE_EXECINFO_BACKTRACE_SYMBOLS AND WINPR_HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD) + set(WINPR_HAVE_EXECINFO_H ON) + endif() + endif() + check_include_files(inttypes.h WINPR_HAVE_INTTYPES_H) + check_include_files(stdint.h WINPR_HAVE_STDINT_H) + check_include_files(inttypes.h WINPR_HAVE_INTTYPES_H) +else(NOT IOS) + set(WINPR_HAVE_STDINT_H 1) + set(WINPR_HAVE_STRNDUP 1) + set(WINPR_HAVE_INTTYPES_H 1) +endif(NOT IOS) + +if (NOT IOS) + check_include_files(stdbool.h WINPR_HAVE_STDBOOL_H) + if (NOT WINPR_HAVE_STDBOOL_H) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../compat/stdbool) + endif() +else() + set(WINPR_HAVE_STDBOOL_H 1) +endif() + +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + +if(NOT IOS) + find_package(Threads REQUIRED) +endif() + +# Include files +if(NOT IOS) + check_include_files(fcntl.h WINPR_HAVE_FCNTL_H) + check_include_files(aio.h WINPR_HAVE_AIO_H) + check_include_files(sys/timerfd.h WINPR_HAVE_SYS_TIMERFD_H) + check_include_files(unistd.h WINPR_HAVE_UNISTD_H) + check_include_files(inttypes.h WINPR_HAVE_INTTYPES_H) + check_include_files(sys/filio.h WINPR_HAVE_SYS_FILIO_H) + check_include_files(sys/sockio.h WINPR_HAVE_SYS_SOCKIO_H) + check_include_files(syslog.h WINPR_HAVE_SYSLOG_H) + check_include_files(sys/select.h WINPR_HAVE_SYS_SELECT_H) + check_include_files(sys/eventfd.h WINPR_HAVE_SYS_EVENTFD_H) + check_include_files(unwind.h WINPR_HAVE_UNWIND_H) + if (WINPR_HAVE_SYS_EVENTFD_H) + check_symbol_exists(eventfd_read sys/eventfd.h WITH_EVENTFD_READ_WRITE) + endif() + + include(CheckFunctionExists) + check_function_exists(getlogin_r WINPR_HAVE_GETLOGIN_R) + check_function_exists(getpwuid_r WINPR_HAVE_GETPWUID_R) + check_struct_has_member("struct tm" tm_gmtoff time.h WINPR_HAVE_TM_GMTOFF) +else() + set(WINPR_HAVE_FCNTL_H 1) + set(WINPR_HAVE_UNISTD_H 1) + set(WINPR_HAVE_INTTYPES_H 1) + set(WINPR_HAVE_SYS_FILIO_H 1) + set(WINPR_HAVE_TM_GMTOFF 1) +endif() + +if(UNIX OR CYGWIN) + if (FREEBSD) + list(APPEND CMAKE_REQUIRED_INCLUDES ${EPOLLSHIM_INCLUDE_DIR}) + endif() + if (FREEBSD) + list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES ${EPOLLSHIM_INCLUDE_DIR}) + endif() + option(WITH_POLL "Check for and include poll.h" ON) + if (WITH_POLL) + check_include_files(poll.h WINPR_HAVE_POLL_H) + endif() +endif() + +if(NOT WIN32 AND NOT IOS) + if (NOT WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) + CHECK_LIBRARY_EXISTS(pthreads pthread_mutex_timedlock "" WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS) + endif (NOT WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) + + if (NOT WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) + CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) + endif (NOT WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) + + list(APPEND CMAKE_REQUIRED_LIBRARIES pthread) + CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) + + if (WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS) + set(WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK ON) + endif (WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL OR WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB OR WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIBS) + list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES pthread) +endif() + + set(OPENSSL_FEATURE_TYPE "RECOMMENDED") + set(OPENSSL_FEATURE_PURPOSE "cryptography") + set(OPENSSL_FEATURE_DESCRIPTION "encryption, certificate validation, hashing functions") + + set(MBEDTLS_FEATURE_TYPE "OPTIONAL") + set(MBEDTLS_FEATURE_PURPOSE "cryptography") + set(MBEDTLS_FEATURE_DESCRIPTION "encryption, certificate validation, hashing functions") + + option(WITH_LIBRESSL "build with LibreSSL" OFF) + if (WITH_LIBRESSL) + find_package(LibreSSL REQUIRED) + set(OPENSSL_INCLUDE_DIR ${LIBRESSL_INCLUDE_DIR}) + set(OPENSSL_LIBRARIES ${LIBRESSL_LIBRARIES}) + set(OPENSSL_CRYPTO_LIBRARIES ${LIBRESSL_LIBRARIES}) + set(WITH_OPENSSL ON) + set(OPENSSL_FOUND ON) + add_definitions("-DWITH_LIBRESSL") + add_definitions("-DWITH_OPENSSL") + else() + find_feature(OpenSSL ${OPENSSL_FEATURE_TYPE} ${OPENSSL_FEATURE_PURPOSE} ${OPENSSL_FEATURE_DESCRIPTION}) + find_feature(MbedTLS ${MBEDTLS_FEATURE_TYPE} ${MBEDTLS_FEATURE_PURPOSE} ${MBEDTLS_FEATURE_DESCRIPTION}) + endif() + + if (NOT OPENSSL_FOUND AND NOT MBEDTLS_FOUND AND NOT LibreSSL_FOUND) + message(FATAL_ERROR "OpenSSL or MBedTLS are required, none enabled/found") + endif() + + if(WITH_OPENSSL AND OPENSSL_FOUND) + add_definitions("-DWITH_OPENSSL") + endif() + + if(WITH_MBEDTLS AND MBEDTLS_FOUND) + add_definitions("-DWITH_MBEDTLS") + endif() + + enable_testing() + + if(MSVC) + set(TESTING_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + else() + set(TESTING_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Testing") +endif() + +if (NOT WIN32 AND NOT IOS AND NOT ANDROID) + set(PKCS11_DEFAULT ON) +else() + set(PKCS11_DEFAULT OFF) +endif() +option(WITH_PKCS11 "encryption, certificate validation, hashing functions" ${PKCS11_DEFAULT}) +if (WITH_PKCS11) + find_package(Pkcs11 REQUIRED) + add_definitions("-DWITH_PKCS11") +endif() + +if(BUILD_SHARED_LIBS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_DLL") +endif() + +add_definitions(-DWINPR_EXPORTS) + +# Enable 64bit file support on linux and FreeBSD. +if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux" OR FREEBSD) + add_definitions("-D_FILE_OFFSET_BITS=64") +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) + +set(WINPR_INCLUDE_DIR "include/winpr${WINPR_VERSION_MAJOR}") + + +add_subdirectory(libwinpr) + +if(WITH_WINPR_TOOLS) + add_subdirectory(tools) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + +add_subdirectory(include) + +set(MANPAGE_NAME wlog) +if (WITH_BINARY_VERSIONING) + set(MANPAGE_NAME wlog${WINPR_API_VERSION}) +endif() +configure_file(wlog.7.in ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.7 @ONLY) +install_freerdp_man(${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.7 7) +# Exporting + +export(PACKAGE winpr) + +SetFreeRDPCMakeInstallDir(WINPR_CMAKE_INSTALL_DIR "WinPR${WINPR_VERSION_MAJOR}") + +configure_package_config_file(WinPRConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/WinPRConfig.cmake + INSTALL_DESTINATION ${WINPR_CMAKE_INSTALL_DIR} + PATH_VARS WINPR_INCLUDE_DIR) + +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/WinPRConfigVersion.cmake + VERSION ${WINPR_VERSION} COMPATIBILITY SameMajorVersion) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/WinPRConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/WinPRConfigVersion.cmake + DESTINATION ${WINPR_CMAKE_INSTALL_DIR}) + +install(EXPORT WinPRTargets DESTINATION ${WINPR_CMAKE_INSTALL_DIR}) + +include(pkg-config-install-prefix) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr${WINPR_VERSION_MAJOR}.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr${WINPR_VERSION_MAJOR}.pc DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR}) diff --git a/winpr/WinPRConfig.cmake.in b/winpr/WinPRConfig.cmake.in new file mode 100644 index 0000000..00c69c9 --- /dev/null +++ b/winpr/WinPRConfig.cmake.in @@ -0,0 +1,10 @@ + +@PACKAGE_INIT@ + +set(WinPR_VERSION_MAJOR "@WINPR_VERSION_MAJOR@") +set(WinPR_VERSION_MINOR "@WINPR_VERSION_MINOR@") +set(WinPR_VERSION_REVISION "@WINPR_VERSION_REVISION@") + +set_and_check(WinPR_INCLUDE_DIR "@PACKAGE_WINPR_INCLUDE_DIR@") + +include("${CMAKE_CURRENT_LIST_DIR}/WinPRTargets.cmake") diff --git a/winpr/include/CMakeLists.txt b/winpr/include/CMakeLists.txt new file mode 100644 index 0000000..5dc4885 --- /dev/null +++ b/winpr/include/CMakeLists.txt @@ -0,0 +1,57 @@ +# WinPR: Windows Portable Runtime +# winpr cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +configure_file(config/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/winpr/version.h) +configure_file(config/wtypes.h.in ${CMAKE_CURRENT_BINARY_DIR}/winpr/wtypes.h) +configure_file(config/build-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/winpr/build-config.h) +configure_file(config/buildflags.h.in ${CMAKE_CURRENT_BINARY_DIR}/winpr/buildflags.h) +configure_file(config/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/winpr/config.h) + +file(GLOB_RECURSE WINPR_PUBLIC_COMMON_HEADERS + LIST_DIRECTORIES false + "winpr/*.h" +) + +set(WINPR_PUBLIC_TOOLS_HEADERS ${WINPR_PUBLIC_COMMON_HEADERS}) +list(FILTER WINPR_PUBLIC_TOOLS_HEADERS INCLUDE REGEX ".*winpr/tools.*") +list(FILTER WINPR_PUBLIC_COMMON_HEADERS EXCLUDE REGEX ".*winpr/tools.*") + +file(GLOB_RECURSE WINPR_PUBLIC_COMMON_BIN_HEADERS + LIST_DIRECTORIES false + "${CMAKE_CURRENT_BINARY_DIR}/*.h" +) +list(APPEND WINPR_PUBLIC_COMMON_HEADERS ${WINPR_PUBLIC_COMMON_BIN_HEADERS}) +list(SORT WINPR_PUBLIC_COMMON_HEADERS) + +set_property(TARGET winpr APPEND PROPERTY SOURCES + ${WINPR_PUBLIC_COMMON_HEADERS} +) + +if (WITH_WINPR_TOOLS) + set_property(TARGET winpr-tools APPEND PROPERTY SOURCES + ${WINPR_PUBLIC_TOOLS_HEADERS} + ) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ + DESTINATION ${WINPR_INCLUDE_DIR} + FILES_MATCHING PATTERN "*.h") + +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ + DESTINATION ${WINPR_INCLUDE_DIR} + FILES_MATCHING PATTERN "*.h") + diff --git a/winpr/include/config/build-config.h.in b/winpr/include/config/build-config.h.in new file mode 100644 index 0000000..823f418 --- /dev/null +++ b/winpr/include/config/build-config.h.in @@ -0,0 +1,22 @@ +#ifndef WINPR_BUILD_CONFIG_H +#define WINPR_BUILD_CONFIG_H + +#define WINPR_DATA_PATH "${WINPR_DATA_PATH}" +#define WINPR_KEYMAP_PATH "${WINPR_KEYMAP_PATH}" +#define WINPR_PLUGIN_PATH "${WINPR_PLUGIN_PATH}" + +#define WINPR_INSTALL_PREFIX "${WINPR_INSTALL_PREFIX}" + +#define WINPR_LIBRARY_PATH "${WINPR_LIBRARY_PATH}" + +#define WINPR_ADDIN_PATH "${WINPR_ADDIN_PATH}" + +#define WINPR_SHARED_LIBRARY_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}" +#define WINPR_SHARED_LIBRARY_PREFIX "${CMAKE_SHARED_LIBRARY_PREFIX}" + +#define WINPR_VENDOR_STRING "${VENDOR}" +#define WINPR_PRODUCT_STRING "${PRODUCT}" + +#define WINPR_PROXY_PLUGINDIR "${WINPR_PROXY_PLUGINDIR}" + +#endif /* WINPR_BUILD_CONFIG_H */ diff --git a/winpr/include/config/buildflags.h.in b/winpr/include/config/buildflags.h.in new file mode 100644 index 0000000..1e43c37 --- /dev/null +++ b/winpr/include/config/buildflags.h.in @@ -0,0 +1,11 @@ +#ifndef WINPR_BUILD_FLAGS_H +#define WINPR_BUILD_FLAGS_H + +#define WINPR_CFLAGS "${CMAKE_C_FLAGS}" +#define WINPR_COMPILER_ID "${CMAKE_C_COMPILER_ID}" +#define WINPR_COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}" +#define WINPR_TARGET_ARCH "${TARGET_ARCH}" +#define WINPR_BUILD_CONFIG "${WINPR_BUILD_CONFIG}" +#define WINPR_BUILD_TYPE "${CMAKE_BUILD_TYPE}" + +#endif /* WINPR_BUILD_FLAGS_H */ diff --git a/winpr/include/config/config.h.in b/winpr/include/config/config.h.in new file mode 100644 index 0000000..8d289aa --- /dev/null +++ b/winpr/include/config/config.h.in @@ -0,0 +1,46 @@ +#ifndef WINPR_CONFIG_H +#define WINPR_CONFIG_H + +/* Include files */ +#cmakedefine WINPR_HAVE_FCNTL_H +#cmakedefine WINPR_HAVE_UNISTD_H +#cmakedefine WINPR_HAVE_INTTYPES_H +#cmakedefine WINPR_HAVE_STDBOOL_H +#cmakedefine WINPR_HAVE_AIO_H +#cmakedefine WINPR_HAVE_SYS_FILIO_H +#cmakedefine WINPR_HAVE_SYS_SELECT_H +#cmakedefine WINPR_HAVE_SYS_SOCKIO_H +#cmakedefine WINPR_HAVE_SYS_EVENTFD_H +#cmakedefine WINPR_HAVE_SYS_TIMERFD_H +#cmakedefine WINPR_HAVE_TM_GMTOFF +#cmakedefine WINPR_HAVE_AIO_H +#cmakedefine WINPR_HAVE_POLL_H +#cmakedefine WINPR_HAVE_SYSLOG_H +#cmakedefine WINPR_HAVE_JOURNALD_H +#cmakedefine WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK +#cmakedefine WINPR_HAVE_EXECINFO_H +#cmakedefine WINPR_HAVE_GETLOGIN_R +#cmakedefine WINPR_HAVE_GETPWUID_R +#cmakedefine WINPR_HAVE_STRNDUP +#cmakedefine WINPR_HAVE_UNWIND_H +#cmakedefine WINPR_WITH_PNG + +#cmakedefine WINPR_HAVE_STRERROR_R + +#cmakedefine WITH_EVENTFD_READ_WRITE + +#cmakedefine WITH_NATIVE_SSPI +#cmakedefine WITH_INTERNAL_RC4 +#cmakedefine WITH_INTERNAL_MD4 +#cmakedefine WITH_INTERNAL_MD5 + +#cmakedefine WITH_DEBUG_NTLM +#cmakedefine WITH_DEBUG_THREADS +#cmakedefine WITH_DEBUG_EVENTS +#cmakedefine WITH_DEBUG_MUTEX + +#cmakedefine WINPR_UTILS_IMAGE_WEBP +#cmakedefine WINPR_UTILS_IMAGE_PNG +#cmakedefine WINPR_UTILS_IMAGE_JPEG + +#endif /* WINPR_CONFIG_H */ diff --git a/winpr/include/config/version.h.in b/winpr/include/config/version.h.in new file mode 100644 index 0000000..8b8c0ab --- /dev/null +++ b/winpr/include/config/version.h.in @@ -0,0 +1,32 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Version includes + * + * Copyright 2013 Thincast Technologies GmbH + * Copyright 2013 Bernhard Miklautz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WINPR_VERSION_H_ +#define WINPR_VERSION_H_ + +#define WINPR_VERSION_MAJOR ${WINPR_VERSION_MAJOR} +#define WINPR_VERSION_MINOR ${WINPR_VERSION_MINOR} +#define WINPR_VERSION_REVISION ${WINPR_VERSION_REVISION} +#define WINPR_VERSION_SUFFIX "${WINPR_VERSION_SUFFIX}" +#define WINPR_API_VERSION "${WINPR_API_VERSION}" +#define WINPR_VERSION "${WINPR_VERSION}" +#define WINPR_VERSION_FULL "${WINPR_VERSION_FULL}" +#define WINPR_GIT_REVISION "${GIT_REVISION}" + +#endif // _WINPR_VERSION_H_ diff --git a/winpr/include/config/wtypes.h.in b/winpr/include/config/wtypes.h.in new file mode 100644 index 0000000..14869d8 --- /dev/null +++ b/winpr/include/config/wtypes.h.in @@ -0,0 +1,606 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Data Types + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WTYPES_H +#define WINPR_WTYPES_H + +#include + +// C99 related macros +#if defined(__STDC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define WINPR_RESTRICT restrict +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +#define WINPR_RESTRICT __restrict +#else +#define WINPR_RESTRICT +#endif + +// C23 related macros +#if defined(__STDC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) +#define WINPR_FALLTHROUGH [[fallthrough]]; +#elif defined(__clang__) +#define WINPR_FALLTHROUGH __attribute__((fallthrough)); +#elif defined(__GNUC__) && (__GNUC__ >= 7) +#define WINPR_FALLTHROUGH __attribute__((fallthrough)); +#else +#define WINPR_FALLTHROUGH +#endif + +/* Set by CMake during configuration */ +#cmakedefine WINPR_HAVE_STDINT_H +#cmakedefine WINPR_HAVE_STDBOOL_H + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +/* Microsoft's inttypes.h is broken before MSVC 2015 */ +#cmakedefine WINPR_HAVE_INTTYPES_H +#endif + +/* MSDN: Windows Data Types - http://msdn.microsoft.com/en-us/library/aa383751/ */ +/* [MS-DTYP]: Windows Data Types - http://msdn.microsoft.com/en-us/library/cc230273/ */ + +#include +#include + +#include + +#ifdef WINPR_HAVE_STDBOOL_H +#include +#endif + +#ifdef WINPR_HAVE_STDINT_H +#include +#endif + +#ifdef WINPR_HAVE_INTTYPES_H +#include +#endif + +#include + +#if defined(_WIN32) || defined(__MINGW32__) +#include + +/* Handle missing ssize_t on Windows */ +#ifdef _WIN64 +typedef long long LONG_PTR; +#else +typedef long LONG_PTR; +#endif + +#if ssize_t +typedef ssize_t SSIZE_T +#else +typedef LONG_PTR SSIZE_T; +#endif + +#endif + +#if defined(__OBJC__) && defined(__APPLE__) +#include +#endif + +#ifndef CONST +#define CONST const +#endif + +#ifndef VOID +#define VOID void +#endif + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#if !defined(_WIN32) && !defined(__MINGW32__) + +#define CALLBACK + +#define WINAPI +#define CDECL + +#ifndef FAR +#define FAR +#endif + +#ifndef NEAR +#define NEAR +#endif + +#ifdef WINPR_HAVE_STDINT_H +typedef int8_t __int8; +typedef uint8_t __uint8; +typedef int16_t __int16; +typedef uint16_t __uint16; +typedef int32_t __int32; +typedef uint32_t __uint32; +typedef int64_t __int64; +typedef uint64_t __uint64; +#else +#if UCHAR_MAX == 0xFF +typedef signed char __int8; +typedef unsigned char __uint8; +#else +#error "8-bit type not configured" +#endif +#if USHRT_MAX == 0xFFFF +typedef short __int16; +typedef unsigned short __uint16; +#elif UINT_MAX == 0xFFFF +typedef int __int16; +typedef unsigned int __uint16; +#error "16-bit type not configured" +#endif +#if UINT_MAX == 0xFFFFFFFF +typedef int __int32; +typedef unsigned int __uint32; +#elif ULONG_MAX == 0xFFFFFFFF +typedef long __int32; +typedef unsigned long __uint32; +#else +#error "32-bit type not configured" +#endif +#if ULONG_MAX == 0xFFFFFFFFFFFFFFFF +typedef long __int64; +typedef unsigned long __uint64; +#elif ULLONG_MAX == 0xFFFFFFFFFFFFFFFF +typedef long long __int64; +typedef unsigned long long __uint64; +#else +#error "64-bit type not configured" +#endif +#endif /* WINPR_HAVE_STDINT_H */ + +#ifdef WINPR_HAVE_STDINT_H +#if defined(__ILP64__) || defined(__LP64__) +#define __int3264 int64_t +#define __uint3264 uint64_t +#else +#define __int3264 int32_t +#define __uint3264 uint32_t +#endif +#else +#if defined(__ILP64__) || defined(__LP64__) +#define __int3264 __int64 +#define __uint3264 __uint64 +#else +#define __int3264 __int32 +#define __uint3264 __uint32 +#endif +#endif /* WINPR_HAVE_STDINT_H */ + + +typedef void* PVOID, *LPVOID, *PVOID64, *LPVOID64; + +#ifndef XMD_H /* X11/Xmd.h typedef collision with BOOL */ +#ifndef __OBJC__ /* objc.h typedef collision with BOOL */ +#ifndef __APPLE__ +typedef __int32 BOOL; +#else /* __APPLE__ */ +#include + +/* ensure compatibility with objc libraries */ +#if (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0) && defined(__LP64__)) || (defined(TARGET_OS_WATCH) && (TARGET_OS_WATCH != 0)) +typedef bool BOOL; +#else +typedef signed char BOOL; +#endif +#endif /* __APPLE__ */ +#endif /* __OBJC__ */ +#endif /* XMD_H */ + +typedef BOOL* PBOOL, *LPBOOL; + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef XMD_H /* X11/Xmd.h typedef collision with BYTE */ +typedef __uint8 BYTE; +#endif /* XMD_H */ +typedef BYTE byte, *PBYTE, *LPBYTE; +typedef BYTE BOOLEAN, PBOOLEAN; + +#if CHAR_BIT == 8 +typedef char CHAR; +typedef unsigned char UCHAR; +#else +typedef __int8 CHAR; +typedef __uint8 UCHAR; +#endif +typedef CHAR CCHAR, *PCHAR, *LPCH, *PCH, *PSTR, *LPSTR; +typedef const CHAR* LPCCH, *PCCH, *LPCSTR, *PCSTR; +typedef UCHAR* PUCHAR; + +typedef __uint16 WCHAR; +typedef WCHAR UNICODE, *PWCHAR, *LPWCH, *PWCH, *BSTR, *LMSTR, *LPWSTR, *PWSTR; +typedef const WCHAR* LPCWCH, *PCWCH, *LMCSTR, *LPCWSTR, *PCWSTR; + +typedef __int16 SHORT, *PSHORT; +typedef __int32 INT, *PINT, *LPINT; +typedef __int32 LONG, *PLONG, *LPLONG; +typedef __int64 LONGLONG, *PLONGLONG; + +typedef __uint32 UINT, *PUINT, *LPUINT; +typedef __uint16 USHORT, *PUSHORT; +typedef __uint32 ULONG, *PULONG; +typedef __uint64 ULONGLONG, *PULONGLONG; + +#ifndef XMD_H /* X11/Xmd.h typedef collisions */ +typedef __int8 INT8; +typedef __int16 INT16; +typedef __int32 INT32; +typedef __int64 INT64; +#endif +typedef INT8* PINT8; +typedef INT16* PINT16; +typedef INT32* PINT32; +typedef INT64* PINT64; + +typedef __int32 LONG32, *PLONG32; +#ifndef LONG64 /* X11/Xmd.h uses/defines LONG64 */ +typedef __int64 LONG64, *PLONG64; +#endif + +typedef __uint8 UINT8, *PUINT8; +typedef __uint16 UINT16, *PUINT16; +typedef __uint32 UINT32, *PUINT32; +typedef __uint64 UINT64, *PUINT64; +typedef __uint64 ULONG64, *PULONG64; + +typedef __uint16 WORD, *PWORD, *LPWORD; +typedef __uint32 DWORD, DWORD32, *PDWORD, *LPDWORD, *PDWORD32; +typedef __uint64 DWORD64, DWORDLONG, QWORD, *PDWORD64, *PDWORDLONG, *PQWORD; + +typedef __int3264 INT_PTR, *PINT_PTR; +typedef __uint3264 UINT_PTR, *PUINT_PTR; +typedef __int3264 LONG_PTR, *PLONG_PTR; +typedef __uint3264 ULONG_PTR, *PULONG_PTR; +typedef __uint3264 DWORD_PTR, *PDWORD_PTR; + +typedef ULONG_PTR SIZE_T, *PSIZE_T; +typedef LONG_PTR SSIZE_T, *PSSIZE_T; + +typedef float FLOAT; + +typedef double DOUBLE; + +typedef void* HANDLE, *PHANDLE, *LPHANDLE; +typedef HANDLE HINSTANCE; +typedef HANDLE HMODULE; +typedef HANDLE HWND; +typedef HANDLE HBITMAP; +typedef HANDLE HICON; +typedef HANDLE HCURSOR; +typedef HANDLE HBRUSH; +typedef HANDLE HMENU; + +typedef DWORD HCALL; + +typedef ULONG error_status_t; +typedef LONG HRESULT; +typedef LONG SCODE; +typedef SCODE* PSCODE; + +typedef struct s_POINTL /* ptl */ +{ + LONG x; + LONG y; +} POINTL, *PPOINTL; + +typedef struct tagSIZE +{ + LONG cx; + LONG cy; +} SIZE, *PSIZE, *LPSIZE; + +typedef SIZE SIZEL; + +typedef struct s_GUID +{ + UINT32 Data1; + UINT16 Data2; + UINT16 Data3; + BYTE Data4[8]; +} GUID, UUID, *PGUID, *LPGUID, *LPCGUID; +typedef GUID CLSID; + +typedef struct s_LUID +{ + DWORD LowPart; + LONG HighPart; +} LUID, *PLUID; + +typedef GUID IID; +typedef IID* REFIID; + +#ifdef UNICODE +#define _T(x) L ## x +#else +#define _T(x) x +#endif + +#ifdef UNICODE +typedef LPWSTR PTSTR; +typedef LPWSTR LPTCH; +typedef LPWSTR LPTSTR; +typedef LPCWSTR LPCTSTR; +#else +typedef LPSTR PTSTR; +typedef LPSTR LPTCH; +typedef LPSTR LPTSTR; +typedef LPCSTR LPCTSTR; +#endif + +typedef union u_ULARGE_INTEGER +{ + struct + { + DWORD LowPart; + DWORD HighPart; + } DUMMYSTRUCTNAME; + + struct + { + DWORD LowPart; + DWORD HighPart; + } u; + + ULONGLONG QuadPart; +} ULARGE_INTEGER, *PULARGE_INTEGER; + +typedef union u_LARGE_INTEGER +{ + struct + { + DWORD LowPart; + LONG HighPart; + } DUMMYSTRUCTNAME; + + struct + { + DWORD LowPart; + LONG HighPart; + } u; + + LONGLONG QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; + +typedef struct s_FILETIME +{ + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME, *PFILETIME, *LPFILETIME; + +typedef struct s_SYSTEMTIME +{ + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; + +typedef struct s_RPC_SID_IDENTIFIER_AUTHORITY +{ + BYTE Value[6]; +} RPC_SID_IDENTIFIER_AUTHORITY; + +typedef DWORD SECURITY_INFORMATION, *PSECURITY_INFORMATION; + +typedef struct s_RPC_SID +{ + UCHAR Revision; + UCHAR SubAuthorityCount; + RPC_SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + ULONG SubAuthority[1]; +} RPC_SID, *PRPC_SID, *PSID; + +typedef struct s_ACL +{ + UCHAR AclRevision; + UCHAR Sbz1; + USHORT AclSize; + USHORT AceCount; + USHORT Sbz2; +} ACL, *PACL; + +typedef struct s_SECURITY_DESCRIPTOR +{ + UCHAR Revision; + UCHAR Sbz1; + USHORT Control; + PSID Owner; + PSID Group; + PACL Sacl; + PACL Dacl; +} SECURITY_DESCRIPTOR, *PSECURITY_DESCRIPTOR; + +typedef WORD SECURITY_DESCRIPTOR_CONTROL, *PSECURITY_DESCRIPTOR_CONTROL; + +typedef struct s_SECURITY_ATTRIBUTES +{ + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; +} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; + +typedef struct s_PROCESS_INFORMATION +{ + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; +} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION; + +typedef DWORD (*PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter); +typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE; + +typedef void* FARPROC; + +typedef struct tagDEC +{ + USHORT wReserved; + union + { + struct + { + BYTE scale; + BYTE sign; + } DUMMYSTRUCTNAME; + USHORT signscale; + } DUMMYUNIONNAME; + ULONG Hi32; + union + { + struct + { + ULONG Lo32; + ULONG Mid32; + } DUMMYSTRUCTNAME2; + ULONGLONG Lo64; + } DUMMYUNIONNAME2; +} DECIMAL; + +typedef DECIMAL* LPDECIMAL; + +#define DECIMAL_NEG ((BYTE) 0x80) +#define DECIMAL_SETZERO(dec) { (dec).Lo64 = 0; (dec).Hi32 = 0; (dec).signscale = 0; } + +typedef DWORD LCID; +typedef PDWORD PLCID; +typedef WORD LANGID; + +#endif /* _WIN32 not defined */ + +typedef void* PCONTEXT_HANDLE; +typedef PCONTEXT_HANDLE* PPCONTEXT_HANDLE; + +#ifndef _NTDEF +typedef LONG NTSTATUS; +typedef NTSTATUS* PNTSTATUS; +#endif + +#ifndef _LPCVOID_DEFINED +#define _LPCVOID_DEFINED +typedef const VOID* LPCVOID; +#endif + +#ifndef _LPCBYTE_DEFINED +#define _LPCBYTE_DEFINED +typedef const BYTE* LPCBYTE; +#endif + +/* integer format specifiers */ +#ifndef WINPR_HAVE_INTTYPES_H +#define PRId8 "hhd" +#define PRIi8 "hhi" +#define PRIu8 "hhu" +#define PRIo8 "hho" +#define PRIx8 "hhx" +#define PRIX8 "hhX" +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIu16 "hu" +#define PRIo16 "ho" +#define PRIx16 "hx" +#define PRIX16 "hX" +#if defined(_MSC_VER) +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIu32 "I32u" +#define PRIo32 "I32o" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIu64 "I64u" +#define PRIo64 "I64o" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#else +#define PRId32 "d" +#define PRIi32 "i" +#define PRIu32 "u" +#define PRIo32 "o" +#define PRIx32 "x" +#define PRIX32 "X" +#if ULONG_MAX == 0xFFFFFFFFFFFFFFFF +#define PRId64 "ld" +#define PRIi64 "li" +#define PRIu64 "lu" +#define PRIo64 "lo" +#define PRIx64 "lx" +#define PRIX64 "lX" +#else +#define PRId64 "lld" +#define PRIi64 "lli" +#define PRIu64 "llu" +#define PRIo64 "llo" +#define PRIx64 "llx" +#define PRIX64 "llX" +#endif +#endif /* _MSC_VER */ +#endif /* WINPR_HAVE_INTTYPES_H not defined*/ + +#ifndef SSIZE_MAX +#if defined(_POSIX_SSIZE_MAX) +#define SSIZE_MAX _POSIX_SSIZE_MAX +#elif defined(_WIN64) +#define SSIZE_MAX _I64_MAX +#elif defined(_WIN32) +#define SSIZE_MAX LONG_MAX +#else +#define SSIZE_MAX LONG_MAX +#endif +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +/* %z not supported before MSVC 2015 */ +#define PRIdz "Id" +#define PRIiz "Ii" +#define PRIuz "Iu" +#define PRIoz "Io" +#define PRIxz "Ix" +#define PRIXz "IX" +#else +#define PRIdz "zd" +#define PRIiz "zi" +#define PRIuz "zu" +#define PRIoz "zo" +#define PRIxz "zx" +#define PRIXz "zX" +#endif + + +#include + +#ifndef _WIN32 +#define _fseeki64(fp, offset, origin) fseeko(fp, offset, origin) +#define _ftelli64(fp) ftello(fp) +#endif + +WINPR_PRAGMA_DIAG_POP + +#endif /* WINPR_WTYPES_H */ diff --git a/winpr/include/winpr/asn1.h b/winpr/include/winpr/asn1.h new file mode 100644 index 0000000..d4b6443 --- /dev/null +++ b/winpr/include/winpr/asn1.h @@ -0,0 +1,212 @@ +/** + * WinPR: Windows Portable Runtime + * ASN1 encoder / decoder + * + * Copyright 2022 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_ASN1_H_ +#define WINPR_ASN1_H_ + +#include +#include +#include + +#define ER_TAG_MASK 0x1F + +enum +{ + ER_TAG_BOOLEAN = 0x01, + ER_TAG_INTEGER = 0x02, + ER_TAG_BIT_STRING = 0x03, + ER_TAG_OCTET_STRING = 0x04, + ER_TAG_NULL = 0x05, + ER_TAG_OBJECT_IDENTIFIER = 0x06, + ER_TAG_ENUMERATED = 0x0A, + ER_TAG_UTF8STRING = 0x0C, + ER_TAG_PRINTABLE_STRING = 0x13, + ER_TAG_IA5STRING = 0x16, + ER_TAG_UTCTIME = 0x17, + ER_TAG_GENERAL_STRING = 0x1B, + ER_TAG_GENERALIZED_TIME = 0x18, + + ER_TAG_APP = 0x60, + ER_TAG_SEQUENCE = 0x30, + ER_TAG_SEQUENCE_OF = 0x30, + ER_TAG_SET = 0x31, + ER_TAG_SET_OF = 0x31, + + ER_TAG_CONTEXTUAL = 0xA0 +}; + +/** @brief rules for encoding */ +typedef enum +{ + WINPR_ASN1_BER, + WINPR_ASN1_DER +} WinPrAsn1EncodingRule; + +typedef struct WinPrAsn1Encoder WinPrAsn1Encoder; + +struct WinPrAsn1Decoder +{ + WinPrAsn1EncodingRule encoding; + wStream source; +}; + +typedef struct WinPrAsn1Decoder WinPrAsn1Decoder; + +typedef BYTE WinPrAsn1_tag; +typedef BYTE WinPrAsn1_tagId; +typedef BOOL WinPrAsn1_BOOL; +typedef INT32 WinPrAsn1_INTEGER; +typedef INT32 WinPrAsn1_ENUMERATED; +typedef char* WinPrAsn1_STRING; +typedef char* WinPrAsn1_IA5STRING; +typedef struct +{ + size_t len; + BYTE* data; +} WinPrAsn1_MemoryChunk; + +typedef WinPrAsn1_MemoryChunk WinPrAsn1_OID; +typedef WinPrAsn1_MemoryChunk WinPrAsn1_OctetString; + +typedef struct +{ + UINT16 year; + UINT8 month; + UINT8 day; + UINT8 hour; + UINT8 minute; + UINT8 second; + char tz; +} WinPrAsn1_UTCTIME; + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + WINPR_API void WinPrAsn1FreeOID(WinPrAsn1_OID* poid); + WINPR_API void WinPrAsn1FreeOctetString(WinPrAsn1_OctetString* octets); + + /* decoder functions */ + + WINPR_API void WinPrAsn1Decoder_Init(WinPrAsn1Decoder* dec, WinPrAsn1EncodingRule encoding, + wStream* source); + WINPR_API void WinPrAsn1Decoder_InitMem(WinPrAsn1Decoder* dec, WinPrAsn1EncodingRule encoding, + const BYTE* source, size_t len); + + WINPR_API BOOL WinPrAsn1DecPeekTag(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag); + WINPR_API size_t WinPrAsn1DecReadTagAndLen(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, + size_t* len); + WINPR_API size_t WinPrAsn1DecPeekTagAndLen(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, + size_t* len); + WINPR_API size_t WinPrAsn1DecReadTagLenValue(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, + size_t* len, WinPrAsn1Decoder* value); + WINPR_API size_t WinPrAsn1DecReadBoolean(WinPrAsn1Decoder* dec, WinPrAsn1_BOOL* target); + WINPR_API size_t WinPrAsn1DecReadInteger(WinPrAsn1Decoder* dec, WinPrAsn1_INTEGER* target); + WINPR_API size_t WinPrAsn1DecReadEnumerated(WinPrAsn1Decoder* dec, + WinPrAsn1_ENUMERATED* target); + WINPR_API size_t WinPrAsn1DecReadOID(WinPrAsn1Decoder* dec, WinPrAsn1_OID* target, + BOOL allocate); + WINPR_API size_t WinPrAsn1DecReadOctetString(WinPrAsn1Decoder* dec, + WinPrAsn1_OctetString* target, BOOL allocate); + WINPR_API size_t WinPrAsn1DecReadIA5String(WinPrAsn1Decoder* dec, WinPrAsn1_IA5STRING* target); + WINPR_API size_t WinPrAsn1DecReadGeneralString(WinPrAsn1Decoder* dec, WinPrAsn1_STRING* target); + WINPR_API size_t WinPrAsn1DecReadUtcTime(WinPrAsn1Decoder* dec, WinPrAsn1_UTCTIME* target); + WINPR_API size_t WinPrAsn1DecReadNull(WinPrAsn1Decoder* dec); + + WINPR_API size_t WinPrAsn1DecReadApp(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, + WinPrAsn1Decoder* setDec); + WINPR_API size_t WinPrAsn1DecReadSequence(WinPrAsn1Decoder* dec, WinPrAsn1Decoder* seqDec); + WINPR_API size_t WinPrAsn1DecReadSet(WinPrAsn1Decoder* dec, WinPrAsn1Decoder* setDec); + + WINPR_API size_t WinPrAsn1DecReadContextualTag(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, + WinPrAsn1Decoder* ctxtDec); + WINPR_API size_t WinPrAsn1DecPeekContextualTag(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, + WinPrAsn1Decoder* ctxtDec); + + WINPR_API size_t WinPrAsn1DecReadContextualBool(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, + BOOL* error, WinPrAsn1_BOOL* target); + WINPR_API size_t WinPrAsn1DecReadContextualInteger(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, + BOOL* error, WinPrAsn1_INTEGER* target); + WINPR_API size_t WinPrAsn1DecReadContextualOID(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, + BOOL* error, WinPrAsn1_OID* target, + BOOL allocate); + WINPR_API size_t WinPrAsn1DecReadContextualOctetString(WinPrAsn1Decoder* dec, + WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1_OctetString* target, + BOOL allocate); + WINPR_API size_t WinPrAsn1DecReadContextualSequence(WinPrAsn1Decoder* dec, + WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1Decoder* target); + WINPR_API wStream WinPrAsn1DecGetStream(WinPrAsn1Decoder* dec); + + /* encoder functions */ + + WINPR_API void WinPrAsn1Encoder_Free(WinPrAsn1Encoder** penc); + WINPR_API WinPrAsn1Encoder* WinPrAsn1Encoder_New(WinPrAsn1EncodingRule encoding); + + WINPR_API void WinPrAsn1Encoder_Reset(WinPrAsn1Encoder* enc); + + WINPR_API BOOL WinPrAsn1EncAppContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId); + WINPR_API BOOL WinPrAsn1EncSeqContainer(WinPrAsn1Encoder* enc); + WINPR_API BOOL WinPrAsn1EncContextualSeqContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId); + WINPR_API BOOL WinPrAsn1EncSetContainer(WinPrAsn1Encoder* enc); + WINPR_API BOOL WinPrAsn1EncContextualSetContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId); + WINPR_API BOOL WinPrAsn1EncContextualContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId); + WINPR_API BOOL WinPrAsn1EncOctetStringContainer(WinPrAsn1Encoder* enc); + WINPR_API BOOL WinPrAsn1EncContextualOctetStringContainer(WinPrAsn1Encoder* enc, + WinPrAsn1_tagId tagId); + WINPR_API size_t WinPrAsn1EncEndContainer(WinPrAsn1Encoder* enc); + + WINPR_API size_t WinPrAsn1EncRawContent(WinPrAsn1Encoder* enc, const WinPrAsn1_MemoryChunk* c); + WINPR_API size_t WinPrAsn1EncContextualRawContent(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_MemoryChunk* c); + WINPR_API size_t WinPrAsn1EncInteger(WinPrAsn1Encoder* enc, WinPrAsn1_INTEGER integer); + WINPR_API size_t WinPrAsn1EncContextualInteger(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_INTEGER integer); + WINPR_API size_t WinPrAsn1EncBoolean(WinPrAsn1Encoder* enc, WinPrAsn1_BOOL b); + WINPR_API size_t WinPrAsn1EncContextualBoolean(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_BOOL b); + WINPR_API size_t WinPrAsn1EncEnumerated(WinPrAsn1Encoder* enc, WinPrAsn1_ENUMERATED e); + WINPR_API size_t WinPrAsn1EncContextualEnumerated(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_ENUMERATED e); + + WINPR_API size_t WinPrAsn1EncOID(WinPrAsn1Encoder* enc, const WinPrAsn1_OID* oid); + WINPR_API size_t WinPrAsn1EncContextualOID(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_OID* oid); + WINPR_API size_t WinPrAsn1EncOctetString(WinPrAsn1Encoder* enc, + const WinPrAsn1_OctetString* oid); + WINPR_API size_t WinPrAsn1EncContextualOctetString(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_OctetString* oid); + WINPR_API size_t WinPrAsn1EncIA5String(WinPrAsn1Encoder* enc, WinPrAsn1_IA5STRING ia5); + WINPR_API size_t WinPrAsn1EncGeneralString(WinPrAsn1Encoder* enc, WinPrAsn1_STRING str); + WINPR_API size_t WinPrAsn1EncContextualIA5String(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_IA5STRING ia5); + WINPR_API size_t WinPrAsn1EncUtcTime(WinPrAsn1Encoder* enc, const WinPrAsn1_UTCTIME* utc); + WINPR_API size_t WinPrAsn1EncContextualUtcTime(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_UTCTIME* utc); + + WINPR_API BOOL WinPrAsn1EncStreamSize(WinPrAsn1Encoder* enc, size_t* s); + WINPR_API BOOL WinPrAsn1EncToStream(WinPrAsn1Encoder* enc, wStream* s); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* WINPR_ASN1_H_ */ diff --git a/winpr/include/winpr/assert.h b/winpr/include/winpr/assert.h new file mode 100644 index 0000000..059cdeb --- /dev/null +++ b/winpr/include/winpr/assert.h @@ -0,0 +1,60 @@ +/** + * WinPR: Windows Portable Runtime + * Runtime ASSERT macros + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_ASSERT_H +#define WINPR_ASSERT_H + +#include +#include +#include +#include +#include + +#if defined(WITH_VERBOSE_WINPR_ASSERT) && (WITH_VERBOSE_WINPR_ASSERT != 0) +#ifdef __cplusplus +extern "C" +{ +#endif +#define WINPR_ASSERT(cond) \ + do \ + { \ + if (!(cond)) \ + winpr_int_assert(#cond, __FILE__, __func__, __LINE__); \ + } while (0) + + static INLINE WINPR_NORETURN(void winpr_int_assert(const char* condstr, const char* file, + const char* fkt, size_t line)) + { + wLog* _log_cached_ptr = WLog_Get("com.freerdp.winpr.assert"); + WLog_Print(_log_cached_ptr, WLOG_FATAL, "%s [%s:%s:%" PRIuz "]", condstr, file, fkt, line); + winpr_log_backtrace_ex(_log_cached_ptr, WLOG_FATAL, 20); + abort(); + } + +#ifdef __cplusplus +} +#endif + +#else +#include +#define WINPR_ASSERT(cond) assert(cond) +#endif + +#endif /* WINPR_ERROR_H */ diff --git a/winpr/include/winpr/bcrypt.h b/winpr/include/winpr/bcrypt.h new file mode 100644 index 0000000..db36a1f --- /dev/null +++ b/winpr/include/winpr/bcrypt.h @@ -0,0 +1,338 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API: Next Generation + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_BCRYPT_H +#define WINPR_BCRYPT_H + +#ifdef _WIN32 +#include +#else + +#include +#include + +typedef PVOID BCRYPT_HANDLE; +typedef PVOID BCRYPT_ALG_HANDLE; +typedef PVOID BCRYPT_KEY_HANDLE; +typedef PVOID BCRYPT_HASH_HANDLE; +typedef PVOID BCRYPT_SECRET_HANDLE; + +#define BCRYPT_RSA_ALGORITHM \ + (const WCHAR*)"R\x00S\x00" \ + "A\x00\x00" +#define BCRYPT_RSA_SIGN_ALGORITHM \ + (const WCHAR*)"R\x00S\x00" \ + "A\x00_\x00S\x00I\x00G\x00N\x00\x00" +#define BCRYPT_DH_ALGORITHM (const WCHAR*)"D\x00H\x00\x00" +#define BCRYPT_DSA_ALGORITHM \ + (const WCHAR*)"D\x00S\x00" \ + "A\x00\x00" +#define BCRYPT_RC2_ALGORITHM \ + (const WCHAR*)"R\x00" \ + "C\x002\x00\x00" +#define BCRYPT_RC4_ALGORITHM \ + (const WCHAR*)"R\x00" \ + "C\x004\x00\x00" +#define BCRYPT_AES_ALGORITHM \ + (const WCHAR*)"A\x00" \ + "E\x00S\x00\x00" +#define BCRYPT_DES_ALGORITHM \ + (const WCHAR*)"D\x00" \ + "E\x00S\x00\x00" +#define BCRYPT_DESX_ALGORITHM \ + (const WCHAR*)"D\x00" \ + "E\x00S\x00X\x00\x00" +#define BCRYPT_3DES_ALGORITHM \ + (const WCHAR*)"3\x00" \ + "D\x00" \ + "E\x00S\x00\x00" +#define BCRYPT_3DES_112_ALGORITHM \ + (const WCHAR*)"3\x00" \ + "D\x00" \ + "E\x00S\x00_\x001\x001\x002\x00\x00" +#define BCRYPT_MD2_ALGORITHM \ + (const WCHAR*)"M\x00" \ + "D\x002\x00\x00" +#define BCRYPT_MD4_ALGORITHM \ + (const WCHAR*)"M\x00" \ + "D\x004\x00\x00" +#define BCRYPT_MD5_ALGORITHM \ + (const WCHAR*)"M\x00" \ + "D\x005\x00\x00" +#define BCRYPT_SHA1_ALGORITHM \ + (const WCHAR*)"S\x00H\x00" \ + "A\x001\x00\x00" +#define BCRYPT_SHA256_ALGORITHM \ + (const WCHAR*)"S\x00H\x00" \ + "A\x002\x005\x006\x00\x00" +#define BCRYPT_SHA384_ALGORITHM \ + (const WCHAR*)"S\x00H\x00" \ + "A\x003\x008\x004\x00\x00" +#define BCRYPT_SHA512_ALGORITHM \ + (const WCHAR*)"S\x00H\x00" \ + "A\x005\x001\x002\x00\x00" +#define BCRYPT_AES_GMAC_ALGORITHM \ + (const WCHAR*)"A\x00" \ + "E\x00S\x00-\x00G\x00M\x00" \ + "A\x00" \ + "C\x00\x00" +#define BCRYPT_ECDSA_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00\x00" +#define BCRYPT_ECDSA_P256_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00_\x00P\x002\x005\x006\x00\x00" +#define BCRYPT_ECDSA_P384_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00_\x00P\x003\x008\x004\x00\x00" +#define BCRYPT_ECDSA_P521_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00_\x00P\x005\x002\x001\x00\x00" +#define BCRYPT_ECDH_P256_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00_\x00P\x002\x005\x006\x00\x00" +#define BCRYPT_ECDH_P384_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00_\x00P\x003\x008\x004\x00\x00" +#define BCRYPT_ECDH_P521_ALGORITHM \ + (const WCHAR*)"E\x00" \ + "C\x00" \ + "D\x00S\x00" \ + "A\x00_\x00P\x005\x002\x001\x00\x00" +#define BCRYPT_RNG_ALGORITHM (const WCHAR*)"R\x00N\x00G\x00\x00" +#define BCRYPT_RNG_FIPS186_DSA_ALGORITHM \ + (const WCHAR*)"F\x00I\x00P\x00S\x001\x008\x006\x00" \ + "D\x00S\x00" \ + "A\x00R\x00N\x00G\x00\x00" +#define BCRYPT_RNG_DUAL_EC_ALGORITHM \ + (const WCHAR*)"D\x00U\x00" \ + "A\x00L\x00" \ + "E\x00R\x00N\x00G\x00\x00" + +#define MS_PRIMITIVE_PROVIDER \ + (const WCHAR*)"M\x00i\x00" \ + "c\x00r\x00o\x00s\x00o\x00" \ + "f\x00t\x00 " \ + "\x00P\x00r\x00i\x00m\x00i\x00t\x00i\x00v\x00" \ + "e\x00 " \ + "\x00P\x00r\x00o\x00v\x00i\x00" \ + "d\x00" \ + "e\x00r\x00\x00" + +#define BCRYPT_ALG_HANDLE_HMAC_FLAG 0x00000008 +#define BCRYPT_PROV_DISPATCH 0x00000001 + +#define BCRYPT_OBJECT_LENGTH \ + (const WCHAR*)"O\x00" \ + "b\x00j\x00" \ + "e\x00" \ + "c\x00t\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00\x00" +#define BCRYPT_ALGORITHM_NAME \ + (const WCHAR*)"A\x00l\x00g\x00o\x00r\x00i\x00t\x00h\x00m\x00N\x00" \ + "a\x00m\x00" \ + "e\x00\x00" +#define BCRYPT_PROVIDER_HANDLE \ + (const WCHAR*)"P\x00r\x00o\x00v\x00i\x00" \ + "d\x00" \ + "e\x00r\x00H\x00" \ + "a\x00n\x00" \ + "d\x00l\x00" \ + "e\x00\x00" +#define BCRYPT_CHAINING_MODE \ + (const WCHAR*)"C\x00h\x00" \ + "a\x00i\x00n\x00i\x00n\x00g\x00M\x00o\x00" \ + "d\x00" \ + "e\x00\x00" +#define BCRYPT_BLOCK_LENGTH \ + (const WCHAR*)"B\x00l\x00o\x00" \ + "c\x00k\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00\x00" +#define BCRYPT_KEY_LENGTH \ + (const WCHAR*)"K\x00" \ + "e\x00y\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00\x00" +#define BCRYPT_KEY_OBJECT_LENGTH \ + (const WCHAR*)"K\x00" \ + "e\x00y\x00O\x00" \ + "b\x00j\x00" \ + "e\x00" \ + "c\x00t\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00" \ + "\x00" +#define BCRYPT_KEY_STRENGTH \ + (const WCHAR*)"K\x00" \ + "e\x00y\x00S\x00t\x00r\x00" \ + "e\x00n\x00g\x00t\x00h\x00\x00" +#define BCRYPT_KEY_LENGTHS \ + (const WCHAR*)"K\x00" \ + "e\x00y\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00s\x00\x00" +#define BCRYPT_BLOCK_SIZE_LIST \ + (const WCHAR*)"B\x00l\x00o\x00" \ + "c\x00k\x00S\x00i\x00z\x00" \ + "e\x00L\x00i\x00s\x00t\x00\x00" +#define BCRYPT_EFFECTIVE_KEY_LENGTH \ + (const WCHAR*)"E\x00" \ + "f\x00" \ + "f\x00" \ + "e\x00" \ + "c\x00t\x00i\x00v\x00" \ + "e\x00K\x00" \ + "e\x00y\x00L\x00" \ + "e\x00n\x00g" \ + "\x00t\x00h\x00\x00" +#define BCRYPT_HASH_LENGTH \ + (const WCHAR*)"H\x00" \ + "a\x00s\x00h\x00" \ + "D\x00i\x00g\x00" \ + "e\x00s\x00t\x00L\x00" \ + "e\x00n\x00g\x00t\x00h" \ + "\x00\x00" +#define BCRYPT_HASH_OID_LIST \ + (const WCHAR*)"H\x00" \ + "a\x00s\x00h\x00O\x00I\x00" \ + "D\x00L\x00i\x00s\x00t\x00\x00" +#define BCRYPT_PADDING_SCHEMES \ + (const WCHAR*)"P\x00" \ + "a\x00" \ + "d\x00" \ + "d\x00i\x00n\x00g\x00S\x00" \ + "c\x00h\x00" \ + "e\x00m\x00" \ + "e\x00s\x00\x00" +#define BCRYPT_SIGNATURE_LENGTH \ + (const WCHAR*)"S\x00i\x00g\x00n\x00" \ + "a\x00t\x00u\x00r\x00" \ + "e\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00" \ + "\x00" +#define BCRYPT_HASH_BLOCK_LENGTH \ + (const WCHAR*)"H\x00" \ + "a\x00s\x00h\x00" \ + "B\x00l\x00o\x00" \ + "c\x00k\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00" \ + "\x00" +#define BCRYPT_AUTH_TAG_LENGTH \ + (const WCHAR*)"A\x00u\x00t\x00h\x00T\x00" \ + "a\x00g\x00L\x00" \ + "e\x00n\x00g\x00t\x00h\x00\x00" +#define BCRYPT_PRIMITIVE_TYPE \ + (const WCHAR*)"P\x00r\x00i\x00m\x00i\x00t\x00i\x00v\x00" \ + "e\x00T\x00y\x00p\x00" \ + "e\x00\x00" +#define BCRYPT_IS_KEYED_HASH \ + (const WCHAR*)"I\x00s\x00K\x00" \ + "e\x00y\x00" \ + "e\x00" \ + "d\x00H\x00" \ + "a\x00s\x00h\x00\x00" + +#define BCRYPT_KEY_DATA_BLOB \ + (const WCHAR*)"K\x00" \ + "e\x00y\x00" \ + "D\x00" \ + "a\x00t\x00" \ + "a\x00" \ + "B\x00l\x00o\x00" \ + "b\x00\x00" + +#define BCRYPT_BLOCK_PADDING 0x00000001 + +#define BCRYPT_KEY_DATA_BLOB_MAGIC 0x4d42444b +#define BCRYPT_KEY_DATA_BLOB_VERSION1 0x1 + +typedef struct +{ + ULONG dwMagic; + ULONG dwVersion; + ULONG cbKeyData; +} BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API NTSTATUS BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE* phAlgorithm, LPCWSTR pszAlgId, + LPCWSTR pszImplementation, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE hAlgorithm, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptGetProperty(BCRYPT_HANDLE hObject, LPCWSTR pszProperty, + PUCHAR pbOutput, ULONG cbOutput, ULONG* pcbResult, + ULONG dwFlags); + + WINPR_API NTSTATUS BCryptCreateHash(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_HASH_HANDLE* phHash, + PUCHAR pbHashObject, ULONG cbHashObject, PUCHAR pbSecret, + ULONG cbSecret, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptDestroyHash(BCRYPT_HASH_HANDLE hHash); + + WINPR_API NTSTATUS BCryptHashData(BCRYPT_HASH_HANDLE hHash, PUCHAR pbInput, ULONG cbInput, + ULONG dwFlags); + + WINPR_API NTSTATUS BCryptFinishHash(BCRYPT_HASH_HANDLE hHash, PUCHAR pbOutput, ULONG cbOutput, + ULONG dwFlags); + + WINPR_API NTSTATUS BCryptGenRandom(BCRYPT_ALG_HANDLE hAlgorithm, PUCHAR pbBuffer, + ULONG cbBuffer, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptGenerateSymmetricKey(BCRYPT_ALG_HANDLE hAlgorithm, + BCRYPT_KEY_HANDLE* phKey, PUCHAR pbKeyObject, + ULONG cbKeyObject, PUCHAR pbSecret, + ULONG cbSecret, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptGenerateKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE* phKey, + ULONG dwLength, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptImportKey(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, + LPCWSTR pszBlobType, BCRYPT_KEY_HANDLE* phKey, + PUCHAR pbKeyObject, ULONG cbKeyObject, PUCHAR pbInput, + ULONG cbInput, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptDestroyKey(BCRYPT_KEY_HANDLE hKey); + + WINPR_API NTSTATUS BCryptEncrypt(BCRYPT_KEY_HANDLE hKey, PUCHAR pbInput, ULONG cbInput, + VOID* pPaddingInfo, PUCHAR pbIV, ULONG cbIV, PUCHAR pbOutput, + ULONG cbOutput, ULONG* pcbResult, ULONG dwFlags); + + WINPR_API NTSTATUS BCryptDecrypt(BCRYPT_KEY_HANDLE hKey, PUCHAR pbInput, ULONG cbInput, + VOID* pPaddingInfo, PUCHAR pbIV, ULONG cbIV, PUCHAR pbOutput, + ULONG cbOutput, ULONG* pcbResult, ULONG dwFlags); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIN32 */ +#endif /* WINPR_BCRYPT_H */ diff --git a/winpr/include/winpr/bitstream.h b/winpr/include/winpr/bitstream.h new file mode 100644 index 0000000..4a8327e --- /dev/null +++ b/winpr/include/winpr/bitstream.h @@ -0,0 +1,186 @@ +/* + * WinPR: Windows Portable Runtime + * BitStream Utils + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_BITSTREAM_H +#define WINPR_UTILS_BITSTREAM_H + +#include +#include +#include + +#include +#include + +typedef struct +{ + const BYTE* buffer; + BYTE* pointer; + UINT32 position; + UINT32 length; + UINT32 capacity; + UINT32 mask; + UINT32 offset; + UINT32 prefetch; + UINT32 accumulator; +} wBitStream; + +#define BITDUMP_MSB_FIRST 0x00000001 +#define BITDUMP_STDERR 0x00000002 + +#ifdef __cplusplus +extern "C" +{ +#endif + + static INLINE void BitStream_Prefetch(wBitStream* _bs) + { + WINPR_ASSERT(_bs); + + (_bs->prefetch) = 0; + if (((UINT32)(_bs->pointer - _bs->buffer) + 4) < (_bs->capacity)) + (_bs->prefetch) |= ((UINT32) * (_bs->pointer + 4) << 24); + if (((UINT32)(_bs->pointer - _bs->buffer) + 5) < (_bs->capacity)) + (_bs->prefetch) |= ((UINT32) * (_bs->pointer + 5) << 16); + if (((UINT32)(_bs->pointer - _bs->buffer) + 6) < (_bs->capacity)) + (_bs->prefetch) |= ((UINT32) * (_bs->pointer + 6) << 8); + if (((UINT32)(_bs->pointer - _bs->buffer) + 7) < (_bs->capacity)) + (_bs->prefetch) |= ((UINT32) * (_bs->pointer + 7) << 0); + } + + static INLINE void BitStream_Fetch(wBitStream* _bs) + { + WINPR_ASSERT(_bs); + (_bs->accumulator) = 0; + if (((UINT32)(_bs->pointer - _bs->buffer) + 0) < (_bs->capacity)) + (_bs->accumulator) |= ((UINT32) * (_bs->pointer + 0) << 24); + if (((UINT32)(_bs->pointer - _bs->buffer) + 1) < (_bs->capacity)) + (_bs->accumulator) |= ((UINT32) * (_bs->pointer + 1) << 16); + if (((UINT32)(_bs->pointer - _bs->buffer) + 2) < (_bs->capacity)) + (_bs->accumulator) |= ((UINT32) * (_bs->pointer + 2) << 8); + if (((UINT32)(_bs->pointer - _bs->buffer) + 3) < (_bs->capacity)) + (_bs->accumulator) |= ((UINT32) * (_bs->pointer + 3) << 0); + BitStream_Prefetch(_bs); + } + + static INLINE void BitStream_Flush(wBitStream* _bs) + { + WINPR_ASSERT(_bs); + if (((UINT32)(_bs->pointer - _bs->buffer) + 0) < (_bs->capacity)) + *(_bs->pointer + 0) = (BYTE)((UINT32)_bs->accumulator >> 24); + if (((UINT32)(_bs->pointer - _bs->buffer) + 1) < (_bs->capacity)) + *(_bs->pointer + 1) = (BYTE)((UINT32)_bs->accumulator >> 16); + if (((UINT32)(_bs->pointer - _bs->buffer) + 2) < (_bs->capacity)) + *(_bs->pointer + 2) = (BYTE)((UINT32)_bs->accumulator >> 8); + if (((UINT32)(_bs->pointer - _bs->buffer) + 3) < (_bs->capacity)) + *(_bs->pointer + 3) = (BYTE)((UINT32)_bs->accumulator >> 0); + } + + static INLINE void BitStream_Shift(wBitStream* _bs, UINT32 _nbits) + { + WINPR_ASSERT(_bs); + if (_nbits == 0) + { + } + else if ((_nbits > 0) && (_nbits < 32)) + { + _bs->accumulator <<= _nbits; + _bs->position += _nbits; + _bs->offset += _nbits; + if (_bs->offset < 32) + { + _bs->mask = (UINT32)((1UL << _nbits) - 1UL); + _bs->accumulator |= ((_bs->prefetch >> (32 - _nbits)) & _bs->mask); + _bs->prefetch <<= _nbits; + } + else + { + _bs->mask = (UINT32)((1UL << _nbits) - 1UL); + _bs->accumulator |= ((_bs->prefetch >> (32 - _nbits)) & _bs->mask); + _bs->prefetch <<= _nbits; + _bs->offset -= 32; + _bs->pointer += 4; + BitStream_Prefetch(_bs); + if (_bs->offset) + { + _bs->mask = (UINT32)((1UL << _bs->offset) - 1UL); + _bs->accumulator |= ((_bs->prefetch >> (32 - _bs->offset)) & _bs->mask); + _bs->prefetch <<= _bs->offset; + } + } + } + else + { + WLog_WARN("com.winpr.bitstream", "warning: BitStream_Shift(%u)", (unsigned)_nbits); + } + } + + static INLINE void BitStream_Shift32(wBitStream* _bs) + { + WINPR_ASSERT(_bs); + BitStream_Shift(_bs, 16); + BitStream_Shift(_bs, 16); + } + + static INLINE void BitStream_Write_Bits(wBitStream* _bs, UINT32 _bits, UINT32 _nbits) + { + WINPR_ASSERT(_bs); + _bs->position += _nbits; + _bs->offset += _nbits; + if (_bs->offset < 32) + { + _bs->accumulator |= (_bits << (32 - _bs->offset)); + } + else + { + _bs->offset -= 32; + _bs->mask = ((1 << (_nbits - _bs->offset)) - 1); + _bs->accumulator |= ((_bits >> _bs->offset) & _bs->mask); + BitStream_Flush(_bs); + _bs->accumulator = 0; + _bs->pointer += 4; + if (_bs->offset) + { + _bs->mask = (UINT32)((1UL << _bs->offset) - 1); + _bs->accumulator |= ((_bits & _bs->mask) << (32 - _bs->offset)); + } + } + } + + static INLINE size_t BitStream_GetRemainingLength(wBitStream* _bs) + { + WINPR_ASSERT(_bs); + return (_bs->length - _bs->position); + } + + WINPR_API void BitDump(const char* tag, UINT32 level, const BYTE* buffer, UINT32 length, + UINT32 flags); + WINPR_API UINT32 ReverseBits32(UINT32 bits, UINT32 nbits); + + WINPR_API void BitStream_Attach(wBitStream* bs, const BYTE* buffer, UINT32 capacity); + + WINPR_API void BitStream_Free(wBitStream* bs); + + WINPR_ATTR_MALLOC(BitStream_Free, 1) + WINPR_API wBitStream* BitStream_New(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_UTILS_BITSTREAM_H */ diff --git a/winpr/include/winpr/clipboard.h b/winpr/include/winpr/clipboard.h new file mode 100644 index 0000000..5a6a8ce --- /dev/null +++ b/winpr/include/winpr/clipboard.h @@ -0,0 +1,109 @@ +/** + * WinPR: Windows Portable Runtime + * Clipboard Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CLIPBOARD_H +#define WINPR_CLIPBOARD_H + +#include +#include + +typedef struct s_wClipboard wClipboard; + +typedef void* (*CLIPBOARD_SYNTHESIZE_FN)(wClipboard* clipboard, UINT32 formatId, const void* data, + UINT32* pSize); + +typedef struct +{ + UINT32 streamId; + UINT32 listIndex; +} wClipboardFileSizeRequest; + +typedef struct +{ + UINT32 streamId; + UINT32 listIndex; + UINT32 nPositionLow; + UINT32 nPositionHigh; + UINT32 cbRequested; +} wClipboardFileRangeRequest; + +typedef struct s_wClipboardDelegate wClipboardDelegate; + +struct s_wClipboardDelegate +{ + wClipboard* clipboard; + void* custom; + char* basePath; + + UINT (*ClientRequestFileSize)(wClipboardDelegate*, const wClipboardFileSizeRequest*); + UINT(*ClipboardFileSizeSuccess) + (wClipboardDelegate*, const wClipboardFileSizeRequest*, UINT64 fileSize); + UINT(*ClipboardFileSizeFailure) + (wClipboardDelegate*, const wClipboardFileSizeRequest*, UINT errorCode); + + UINT (*ClientRequestFileRange)(wClipboardDelegate*, const wClipboardFileRangeRequest*); + UINT(*ClipboardFileRangeSuccess) + (wClipboardDelegate*, const wClipboardFileRangeRequest*, const BYTE* data, UINT32 size); + UINT(*ClipboardFileRangeFailure) + (wClipboardDelegate*, const wClipboardFileRangeRequest*, UINT errorCode); + + BOOL (*IsFileNameComponentValid)(LPCWSTR lpFileName); +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void ClipboardLock(wClipboard* clipboard); + WINPR_API void ClipboardUnlock(wClipboard* clipboard); + + WINPR_API BOOL ClipboardEmpty(wClipboard* clipboard); + WINPR_API UINT32 ClipboardCountFormats(wClipboard* clipboard); + WINPR_API UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds); + + WINPR_API UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard); + WINPR_API UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds); + WINPR_API UINT32 ClipboardRegisterFormat(wClipboard* clipboard, const char* name); + + WINPR_API BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, + UINT32 syntheticId, + CLIPBOARD_SYNTHESIZE_FN pfnSynthesize); + + WINPR_API UINT32 ClipboardGetFormatId(wClipboard* clipboard, const char* name); + WINPR_API const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId); + WINPR_API void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize); + WINPR_API BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId, const void* data, + UINT32 size); + + WINPR_API UINT64 ClipboardGetOwner(wClipboard* clipboard); + WINPR_API void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId); + + WINPR_API wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard); + + WINPR_API wClipboard* ClipboardCreate(void); + WINPR_API void ClipboardDestroy(wClipboard* clipboard); + + WINPR_API const char* ClipboardGetFormatIdString(UINT32 formatId); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_CLIPBOARD_H */ diff --git a/winpr/include/winpr/cmdline.h b/winpr/include/winpr/cmdline.h new file mode 100644 index 0000000..5221df1 --- /dev/null +++ b/winpr/include/winpr/cmdline.h @@ -0,0 +1,183 @@ +/** + * WinPR: Windows Portable Runtime + * Command-Line Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CMDLINE_H +#define WINPR_CMDLINE_H + +#include +#include + +/* Command-Line Argument Flags */ + +#define COMMAND_LINE_INPUT_FLAG_MASK 0x0000FFFF +#define COMMAND_LINE_OUTPUT_FLAG_MASK 0xFFFF0000 + +/* Command-Line Argument Input Flags */ + +#define COMMAND_LINE_VALUE_FLAG 0x00000001 +#define COMMAND_LINE_VALUE_REQUIRED 0x00000002 +#define COMMAND_LINE_VALUE_OPTIONAL 0x00000004 +#define COMMAND_LINE_VALUE_BOOL 0x00000008 + +#define COMMAND_LINE_ADVANCED 0x00000100 +#define COMMAND_LINE_PRINT 0x00000200 +#define COMMAND_LINE_PRINT_HELP 0x00000400 +#define COMMAND_LINE_PRINT_VERSION 0x00000800 +#define COMMAND_LINE_PRINT_BUILDCONFIG 0x00001000 + +/* Command-Line Argument Output Flags */ + +#define COMMAND_LINE_ARGUMENT_PRESENT 0x80000000 +#define COMMAND_LINE_VALUE_PRESENT 0x40000000 + +/* Command-Line Parsing Flags */ + +#define COMMAND_LINE_SIGIL_NONE 0x00000001 +#define COMMAND_LINE_SIGIL_SLASH 0x00000002 +#define COMMAND_LINE_SIGIL_DASH 0x00000004 +#define COMMAND_LINE_SIGIL_DOUBLE_DASH 0x00000008 +#define COMMAND_LINE_SIGIL_PLUS_MINUS 0x00000010 +#define COMMAND_LINE_SIGIL_ENABLE_DISABLE 0x00000020 +#define COMMAND_LINE_SIGIL_NOT_ESCAPED 0x00000040 + +#define COMMAND_LINE_SEPARATOR_COLON 0x00000100 +#define COMMAND_LINE_SEPARATOR_EQUAL 0x00000200 +#define COMMAND_LINE_SEPARATOR_SPACE 0x00000400 + +/* Supress COMMAND_LINE_ERROR_NO_KEYWORD return. */ +#define COMMAND_LINE_IGN_UNKNOWN_KEYWORD 0x00001000 +#define COMMAND_LINE_SILENCE_PARSER 0x00002000 + +/* Command-Line Parsing Error Codes */ + +#define COMMAND_LINE_ERROR -1000 +#define COMMAND_LINE_ERROR_NO_KEYWORD -1001 +#define COMMAND_LINE_ERROR_UNEXPECTED_VALUE -1002 +#define COMMAND_LINE_ERROR_MISSING_VALUE -1003 +#define COMMAND_LINE_ERROR_MISSING_ARGUMENT -1004 +#define COMMAND_LINE_ERROR_UNEXPECTED_SIGIL -1005 +#define COMMAND_LINE_ERROR_MEMORY -1006 +#define COMMAND_LINE_ERROR_LAST -1999 + +/* Command-Line Parsing Status Codes */ + +#define COMMAND_LINE_STATUS_PRINT -2001 +#define COMMAND_LINE_STATUS_PRINT_HELP -2002 +#define COMMAND_LINE_STATUS_PRINT_VERSION -2003 +#define COMMAND_LINE_STATUS_PRINT_BUILDCONFIG -2004 +#define COMMAND_LINE_STATUS_PRINT_LAST -2999 + +/* Command-Line Macros */ + +#define CommandLineSwitchStart(_arg) \ + if (0) \ + { \ + } +#define CommandLineSwitchCase(_arg, _name) else if (strcmp(_arg->Name, _name) == 0) +#define CommandLineSwitchDefault(_arg) else +#define CommandLineSwitchEnd(_arg) + +#define BoolValueTrue ((LPSTR)1) +#define BoolValueFalse ((LPSTR)0) + +typedef struct +{ + LPCSTR Name; + DWORD Flags; + LPCSTR Format; + LPCSTR Default; + LPSTR Value; + LONG Index; + LPCSTR Alias; + LPCSTR Text; +} COMMAND_LINE_ARGUMENT_A; + +typedef struct +{ + LPCWSTR Name; + DWORD Flags; + LPCSTR Format; + LPWSTR Default; + LPWSTR Value; + LONG Index; + LPCWSTR Alias; + LPCWSTR Text; +} COMMAND_LINE_ARGUMENT_W; + +#ifdef UNICODE +#define COMMAND_LINE_ARGUMENT COMMAND_LINE_ARGUMENT_W +#else +#define COMMAND_LINE_ARGUMENT COMMAND_LINE_ARGUMENT_A +#endif + +typedef int (*COMMAND_LINE_PRE_FILTER_FN_A)(void* context, int index, int argc, LPSTR* argv); +typedef int (*COMMAND_LINE_PRE_FILTER_FN_W)(void* context, int index, int argc, LPWSTR* argv); + +typedef int (*COMMAND_LINE_POST_FILTER_FN_A)(void* context, COMMAND_LINE_ARGUMENT_A* arg); +typedef int (*COMMAND_LINE_POST_FILTER_FN_W)(void* context, COMMAND_LINE_ARGUMENT_W* arg); + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options); + WINPR_API int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options); + + WINPR_API int CommandLineParseArgumentsA(int argc, LPSTR* argv, + COMMAND_LINE_ARGUMENT_A* options, DWORD flags, + void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter, + COMMAND_LINE_POST_FILTER_FN_A postFilter); + WINPR_API int CommandLineParseArgumentsW(int argc, LPWSTR* argv, + COMMAND_LINE_ARGUMENT_W* options, DWORD flags, + void* context, COMMAND_LINE_PRE_FILTER_FN_W preFilter, + COMMAND_LINE_POST_FILTER_FN_W postFilter); + + WINPR_API const COMMAND_LINE_ARGUMENT_A* + CommandLineFindArgumentA(const COMMAND_LINE_ARGUMENT_A* options, LPCSTR Name); + WINPR_API const COMMAND_LINE_ARGUMENT_W* + CommandLineFindArgumentW(const COMMAND_LINE_ARGUMENT_W* options, LPCWSTR Name); + + WINPR_API const COMMAND_LINE_ARGUMENT_A* + CommandLineFindNextArgumentA(const COMMAND_LINE_ARGUMENT_A* argument); + + WINPR_API char** CommandLineParseCommaSeparatedValues(const char* list, size_t* count); + + WINPR_API char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list, + size_t* count); + + WINPR_API char* CommandLineToCommaSeparatedValues(int argc, char* argv[]); + WINPR_API char* CommandLineToCommaSeparatedValuesEx(int argc, char* argv[], + const char* filters[], size_t number); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define CommandLineClearArguments CommandLineClearArgumentsW +#define CommandLineParseArguments CommandLineParseArgumentsW +#define CommandLineFindArgument CommandLineFindArgumentW +#else +#define CommandLineClearArguments CommandLineClearArgumentsA +#define CommandLineParseArguments CommandLineParseArgumentsA +#define CommandLineFindArgument CommandLineFindArgumentA +#endif + +#endif /* WINPR_CMDLINE_H */ diff --git a/winpr/include/winpr/collections.h b/winpr/include/winpr/collections.h new file mode 100644 index 0000000..ec3de72 --- /dev/null +++ b/winpr/include/winpr/collections.h @@ -0,0 +1,871 @@ +/** + * WinPR: Windows Portable Runtime + * Collections + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_COLLECTIONS_H +#define WINPR_COLLECTIONS_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef void* (*OBJECT_NEW_FN)(const void* val); + typedef void (*OBJECT_INIT_FN)(void* obj); + typedef void (*OBJECT_UNINIT_FN)(void* obj); + typedef void (*OBJECT_FREE_FN)(void* obj); + typedef BOOL (*OBJECT_EQUALS_FN)(const void* objA, const void* objB); + + /** @struct wObject + * @brief This struct contains function pointer to initialize/free objects + * + * @var fnObjectNew A new function that creates a clone of the input + * @var fnObjectInit A function initializing an object, but not allocating it + * @var fnObjectUninit A function to deinitialize an object, but not free it + * @var fnObjectFree A function freeing an object + * @var fnObjectEquals A function to compare two objects + */ + typedef struct + { + OBJECT_NEW_FN fnObjectNew; + OBJECT_INIT_FN fnObjectInit; + OBJECT_UNINIT_FN fnObjectUninit; + OBJECT_FREE_FN fnObjectFree; + OBJECT_EQUALS_FN fnObjectEquals; + } wObject; + + /* utility function with compatible arguments for string data */ + WINPR_API void* winpr_ObjectStringClone(const void* pvstr); + WINPR_API void* winpr_ObjectWStringClone(const void* pvstr); + WINPR_API void winpr_ObjectStringFree(void* pvstr); + + /* System.Collections.Queue */ + + typedef struct s_wQueue wQueue; + + /** @brief Return the number of elements in the queue + * + * @param queue A pointer to a queue, must not be \b NULL + * + * @return the number of objects queued + */ + WINPR_API size_t Queue_Count(wQueue* queue); + + /** @brief Mutex-Lock a queue + * + * @param queue A pointer to a queue, must not be \b NULL + */ + WINPR_API void Queue_Lock(wQueue* queue); + + /** @brief Mutex-Unlock a queue + * + * @param queue A pointer to a queue, must not be \b NULL + */ + WINPR_API void Queue_Unlock(wQueue* queue); + + /** @brief Get an event handle for the queue, usable by \b WaitForSingleObject or \b + * WaitForMultipleObjects + * + * @param queue A pointer to a queue, must not be \b NULL + */ + WINPR_API HANDLE Queue_Event(wQueue* queue); + + /** @brief Mutex-Lock a queue + * + * @param queue A pointer to a queue, must not be \b NULL + * + * @return A pointer to a \b wObject that contains the allocation/cleanup handlers for queue + * elements + */ + WINPR_API wObject* Queue_Object(wQueue* queue); + + /** @brief Remove all elements from a queue, call \b wObject cleanup functions \b fnObjectFree + * + * @param queue A pointer to a queue, must not be \b NULL + */ + WINPR_API void Queue_Clear(wQueue* queue); + + /** @brief Check if the queue contains an object + * + * @param queue A pointer to a queue, must not be \b NULL + * @param obj The object to look for. \b fnObjectEquals is called internally + * + * @return \b TRUE if the object was found, \b FALSE otherwise. + */ + WINPR_API BOOL Queue_Contains(wQueue* queue, const void* obj); + + /** \brief Pushes a new element into the queue. + * If a \b fnObjectNew is set, the element is copied and the queue takes + * ownership of the memory, otherwise the ownership stays with the caller. + * + * \param queue The queue to operate on + * \param obj A pointer to the object to queue + * + * \return TRUE for success, FALSE if failed. + */ + WINPR_API BOOL Queue_Enqueue(wQueue* queue, const void* obj); + + /** \brief returns the element at the top of the queue. The element is removed from the queue, + * ownership of the element is passed on to the caller. + * + * \param queue The queue to check + * + * \return NULL if empty, a pointer to the memory on top of the queue otherwise. + */ + WINPR_API void* Queue_Dequeue(wQueue* queue); + + /** \brief returns the element at the top of the queue. The element is not removed from the + * queue, ownership of the element stays with the queue. + * + * \param queue The queue to check + * + * \return NULL if empty, a pointer to the memory on top of the queue otherwise. + */ + WINPR_API void* Queue_Peek(wQueue* queue); + + /** \brief Removes the element at the top of the queue. If fnObjectFree is set, the element is + * freed. This can be used in combination with Queue_Peek to handle an element and discard it + * with this function afterward. An alternative is Queue_Dequeue with calling the appropriate + * free function afterward. + * + * \param queue The queue to operate on + */ + WINPR_API void Queue_Discard(wQueue* queue); + + /** @brief Clean up a queue, free all resources (e.g. calls \b Queue_Clear) + * + * @param queue The queue to free, may be \b NULL + */ + WINPR_API void Queue_Free(wQueue* queue); + + /** @brief Creates a new queue + * + * @return A newly allocated queue or \b NULL in case of failure + */ + WINPR_ATTR_MALLOC(Queue_Free, 1) + WINPR_API wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor); + + /* System.Collections.Stack */ + + typedef struct s_wStack wStack; + + WINPR_API size_t Stack_Count(wStack* stack); + WINPR_API BOOL Stack_IsSynchronized(wStack* stack); + + WINPR_API wObject* Stack_Object(wStack* stack); + + WINPR_API void Stack_Clear(wStack* stack); + WINPR_API BOOL Stack_Contains(wStack* stack, const void* obj); + + WINPR_API void Stack_Push(wStack* stack, void* obj); + WINPR_API void* Stack_Pop(wStack* stack); + + WINPR_API void* Stack_Peek(wStack* stack); + + WINPR_API void Stack_Free(wStack* stack); + + WINPR_ATTR_MALLOC(Stack_Free, 1) + WINPR_API wStack* Stack_New(BOOL synchronized); + + /* System.Collections.ArrayList */ + + typedef struct s_wArrayList wArrayList; + + WINPR_API size_t ArrayList_Capacity(wArrayList* arrayList); + WINPR_API size_t ArrayList_Count(wArrayList* arrayList); + WINPR_API size_t ArrayList_Items(wArrayList* arrayList, ULONG_PTR** ppItems); + WINPR_API BOOL ArrayList_IsFixedSized(wArrayList* arrayList); + WINPR_API BOOL ArrayList_IsReadOnly(wArrayList* arrayList); + WINPR_API BOOL ArrayList_IsSynchronized(wArrayList* arrayList); + + WINPR_API void ArrayList_Lock(wArrayList* arrayList); + WINPR_API void ArrayList_Unlock(wArrayList* arrayList); + + WINPR_API void* ArrayList_GetItem(wArrayList* arrayList, size_t index); + WINPR_API BOOL ArrayList_SetItem(wArrayList* arrayList, size_t index, const void* obj); + + WINPR_API wObject* ArrayList_Object(wArrayList* arrayList); + + typedef BOOL (*ArrayList_ForEachFkt)(void* data, size_t index, va_list ap); + + WINPR_API BOOL ArrayList_ForEach(wArrayList* arrayList, ArrayList_ForEachFkt fkt, ...); + WINPR_API BOOL ArrayList_ForEachAP(wArrayList* arrayList, ArrayList_ForEachFkt fkt, va_list ap); + + WINPR_API void ArrayList_Clear(wArrayList* arrayList); + WINPR_API BOOL ArrayList_Contains(wArrayList* arrayList, const void* obj); + +#if defined(WITH_WINPR_DEPRECATED) + WINPR_API WINPR_DEPRECATED(int ArrayList_Add(wArrayList* arrayList, const void* obj)); +#endif + + WINPR_API BOOL ArrayList_Append(wArrayList* arrayList, const void* obj); + WINPR_API BOOL ArrayList_Insert(wArrayList* arrayList, size_t index, const void* obj); + + WINPR_API BOOL ArrayList_Remove(wArrayList* arrayList, const void* obj); + WINPR_API BOOL ArrayList_RemoveAt(wArrayList* arrayList, size_t index); + + WINPR_API SSIZE_T ArrayList_IndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex, + SSIZE_T count); + WINPR_API SSIZE_T ArrayList_LastIndexOf(wArrayList* arrayList, const void* obj, + SSIZE_T startIndex, SSIZE_T count); + + WINPR_API void ArrayList_Free(wArrayList* arrayList); + + WINPR_ATTR_MALLOC(ArrayList_Free, 1) + WINPR_API wArrayList* ArrayList_New(BOOL synchronized); + + /* System.Collections.DictionaryBase */ + + /* System.Collections.Specialized.ListDictionary */ + typedef struct s_wListDictionary wListDictionary; + + /** @brief Get the \b wObject function pointer struct for the \b key of the dictionary. + * + * @param listDictionary A dictionary to query, must not be \b NULL + * + * @return a \b wObject used to initialize the key object, \b NULL in case of failure + */ + WINPR_API wObject* ListDictionary_KeyObject(wListDictionary* listDictionary); + + /** @brief Get the \b wObject function pointer struct for the \b value of the dictionary. + * + * @param listDictionary A dictionary to query, must not be \b NULL + * + * @return a \b wObject used to initialize the value object, \b NULL in case of failure + */ + WINPR_API wObject* ListDictionary_ValueObject(wListDictionary* listDictionary); + + /** @brief Return the number of entries in the dictionary + * + * @param listDictionary A dictionary to query, must not be \b NULL + * + * @return the number of entries + */ + WINPR_API size_t ListDictionary_Count(wListDictionary* listDictionary); + + /** @brief mutex-lock a dictionary + * + * @param listDictionary A dictionary to query, must not be \b NULL + */ + WINPR_API void ListDictionary_Lock(wListDictionary* listDictionary); + /** @brief mutex-unlock a dictionary + * + * @param listDictionary A dictionary to query, must not be \b NULL + */ + WINPR_API void ListDictionary_Unlock(wListDictionary* listDictionary); + + /** @brief mutex-lock a dictionary + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param key The key identifying the entry, if set cloned with \b fnObjectNew + * @param value The value to store for the \b key. May be \b NULL. if set cloned with \b + * fnObjectNew + * + * @return \b TRUE for successfull addition, \b FALSE for failure + */ + WINPR_API BOOL ListDictionary_Add(wListDictionary* listDictionary, const void* key, + const void* value); + + /** @brief Remove an item from the dictionary and return the value. Cleanup is up to the caller. + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param key The key identifying the entry + * + * @return a pointer to the value stored or \b NULL in case of failure or not found + */ + WINPR_API void* ListDictionary_Take(wListDictionary* listDictionary, const void* key); + + /** @brief Remove an item from the dictionary and call \b fnObjectFree for key and value + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param key The key identifying the entry + */ + WINPR_API void ListDictionary_Remove(wListDictionary* listDictionary, const void* key); + + /** @brief Remove the head item from the dictionary and return the value. Cleanup is up to the + * caller. + * + * @param listDictionary A dictionary to query, must not be \b NULL + * + * @return a pointer to the value stored or \b NULL in case of failure or not found + */ + WINPR_API void* ListDictionary_Take_Head(wListDictionary* listDictionary); + + /** @brief Remove the head item from the dictionary and call \b fnObjectFree for key and value + * + * @param listDictionary A dictionary to query, must not be \b NULL + */ + WINPR_API void ListDictionary_Remove_Head(wListDictionary* listDictionary); + + /** @brief Remove all items from the dictionary and call \b fnObjectFree for key and value + * + * @param listDictionary A dictionary to query, must not be \b NULL + */ + WINPR_API void ListDictionary_Clear(wListDictionary* listDictionary); + + /** @brief Check if a dictionary contains \b key (\b fnObjectEquals of the key object is called) + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param key A key to look for + * + * @return \b TRUE if found, \b FALSE otherwise + */ + WINPR_API BOOL ListDictionary_Contains(wListDictionary* listDictionary, const void* key); + + /** @brief return all keys the dictionary contains + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param ppKeys A pointer to a \b ULONG_PTR array that will hold the result keys. Call \b free + * if no longer required + * + * @return the number of keys found in the dictionary or \b 0 if \b ppKeys is \b NULL + */ + WINPR_API size_t ListDictionary_GetKeys(wListDictionary* listDictionary, ULONG_PTR** ppKeys); + + /** @brief Get the value in the dictionary for a \b key. The ownership of the data stays with + * the dictionary. + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param key A key to look for (\b fnObjectEquals of the key object is called) + * + * @return A pointer to the data in the dictionary or \b NULL if not found + */ + WINPR_API void* ListDictionary_GetItemValue(wListDictionary* listDictionary, const void* key); + + /** @brief Set the value in the dictionary for a \b key. The entry must already exist, \b value + * is copied if \b fnObjectNew is set + * + * @param listDictionary A dictionary to query, must not be \b NULL + * @param key A key to look for (\b fnObjectEquals of the key object is called) + * @param value A pointer to the value to set + * + * @return \b TRUE for success, \b FALSE in case of failure + */ + WINPR_API BOOL ListDictionary_SetItemValue(wListDictionary* listDictionary, const void* key, + const void* value); + + /** @brief Free memory allocated by a dictionary. Calls \b ListDictionary_Clear + * + * @param listDictionary A dictionary to query, may be \b NULL + */ + WINPR_API void ListDictionary_Free(wListDictionary* listDictionary); + + /** @brief allocate a new dictionary + * + * @param synchronized Create the dictionary with automatic mutex lock + * + * @return A newly allocated dictionary or \b NULL in case of failure + */ + WINPR_ATTR_MALLOC(ListDictionary_Free, 1) + WINPR_API wListDictionary* ListDictionary_New(BOOL synchronized); + + /* System.Collections.Generic.LinkedList */ + + typedef struct s_wLinkedList wLinkedList; + + /** @brief Return the current number of elements in the linked list + * + * @param list A pointer to the list, must not be \b NULL + * + * @return the number of elements in the list + */ + WINPR_API size_t LinkedList_Count(wLinkedList* list); + + /** @brief Return the first element of the list, ownership stays with the list + * + * @param list A pointer to the list, must not be \b NULL + * + * @return A pointer to the element or \b NULL if empty + */ + WINPR_API void* LinkedList_First(wLinkedList* list); + + /** @brief Return the last element of the list, ownership stays with the list + * + * @param list A pointer to the list, must not be \b NULL + * + * @return A pointer to the element or \b NULL if empty + */ + WINPR_API void* LinkedList_Last(wLinkedList* list); + + /** @brief Check if the linked list contains a value + * + * @param list A pointer to the list, must not be \b NULL + * @param value A value to check for + * + * @return \b TRUE if found, \b FALSE otherwise + */ + WINPR_API BOOL LinkedList_Contains(wLinkedList* list, const void* value); + + /** @brief Remove all elements of the linked list. \b fnObjectUninit and \b fnObjectFree are + * called for each entry + * + * @param list A pointer to the list, must not be \b NULL + * + */ + WINPR_API void LinkedList_Clear(wLinkedList* list); + + /** @brief Add a new element at the start of the linked list. \b fnObjectNew and \b fnObjectInit + * is called for the new entry + * + * @param list A pointer to the list, must not be \b NULL + * @param value The value to add + * + * @return \b TRUE if successful, \b FALSE otherwise. + */ + WINPR_API BOOL LinkedList_AddFirst(wLinkedList* list, const void* value); + + /** @brief Add a new element at the end of the linked list. \b fnObjectNew and \b fnObjectInit + * is called for the new entry + * + * @param list A pointer to the list, must not be \b NULL + * @param value The value to add + * + * @return \b TRUE if successful, \b FALSE otherwise. + */ + WINPR_API BOOL LinkedList_AddLast(wLinkedList* list, const void* value); + + /** @brief Remove a element identified by \b value from the linked list. \b fnObjectUninit and + * \b fnObjectFree is called for the entry + * + * @param list A pointer to the list, must not be \b NULL + * @param value The value to remove + * + * @return \b TRUE if successful, \b FALSE otherwise. + */ + WINPR_API BOOL LinkedList_Remove(wLinkedList* list, const void* value); + + /** @brief Remove the first element from the linked list. \b fnObjectUninit and \b fnObjectFree + * is called for the entry + * + * @param list A pointer to the list, must not be \b NULL + * + */ + WINPR_API void LinkedList_RemoveFirst(wLinkedList* list); + + /** @brief Remove the last element from the linked list. \b fnObjectUninit and \b fnObjectFree + * is called for the entry + * + * @param list A pointer to the list, must not be \b NULL + * + */ + WINPR_API void LinkedList_RemoveLast(wLinkedList* list); + + /** @brief Move enumerator to the first element + * + * @param list A pointer to the list, must not be \b NULL + * + */ + WINPR_API void LinkedList_Enumerator_Reset(wLinkedList* list); + + /** @brief Return the value for the current position of the enumerator + * + * @param list A pointer to the list, must not be \b NULL + * + * @return A pointer to the current entry or \b NULL + */ + WINPR_API void* LinkedList_Enumerator_Current(wLinkedList* list); + + /** @brief Move enumerator to the next element + * + * @param list A pointer to the list, must not be \b NULL + * + * @return \b TRUE if the move was successful, \b FALSE if not (e.g. no more entries) + */ + WINPR_API BOOL LinkedList_Enumerator_MoveNext(wLinkedList* list); + + /** @brief Free a linked list + * + * @param list A pointer to the list, may be \b NULL + */ + WINPR_API void LinkedList_Free(wLinkedList* list); + + /** @brief Allocate a linked list + * + * @return A pointer to the newly allocated linked list or \b NULL in case of failure + */ + WINPR_ATTR_MALLOC(LinkedList_Free, 1) + WINPR_API wLinkedList* LinkedList_New(void); + + /** @brief Return the \b wObject function pointers for list elements + * + * @param list A pointer to the list, must not be \b NULL + * + * @return A pointer to the wObject or \b NULL in case of failure + */ + WINPR_API wObject* LinkedList_Object(wLinkedList* list); + + /* System.Collections.Generic.KeyValuePair */ + + /* Countdown Event */ + + typedef struct CountdownEvent wCountdownEvent; + + /** @brief return the current event count of the CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * + * @return The current event count + */ + WINPR_API size_t CountdownEvent_CurrentCount(wCountdownEvent* countdown); + + /** @brief return the initial event count of the CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * + * @return The initial event count + */ + WINPR_API size_t CountdownEvent_InitialCount(wCountdownEvent* countdown); + + /** @brief return the current event state of the CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * + * @return \b TRUE if set, \b FALSE otherwise + */ + WINPR_API BOOL CountdownEvent_IsSet(wCountdownEvent* countdown); + + /** @brief return the event HANDLE of the CountdownEvent to be used by \b WaitForSingleObject or + * \b WaitForMultipleObjects + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * + * @return a \b HANDLE or \b NULL in case of failure + */ + WINPR_API HANDLE CountdownEvent_WaitHandle(wCountdownEvent* countdown); + + /** @brief add \b signalCount to the current event count of the CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * @param signalCount The amount to add to CountdownEvent + * + */ + WINPR_API void CountdownEvent_AddCount(wCountdownEvent* countdown, size_t signalCount); + + /** @brief Increase the current event signal state of the CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * @param signalCount The amount of signaled events to add + * + * @return \b TRUE if event is set, \b FALSE otherwise + */ + WINPR_API BOOL CountdownEvent_Signal(wCountdownEvent* countdown, size_t signalCount); + + /** @brief reset the CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, must not be \b NULL + * + */ + WINPR_API void CountdownEvent_Reset(wCountdownEvent* countdown, size_t count); + + /** @brief Free a CountdownEvent + * + * @param countdown A pointer to a CountdownEvent, may be \b NULL + */ + WINPR_API void CountdownEvent_Free(wCountdownEvent* countdown); + + /** @brief Allocte a CountdownEvent with \b initialCount + * + * @param initialCount The initial value of the event + * + * @return The newly allocated event or \b NULL in case of failure + */ + WINPR_ATTR_MALLOC(CountdownEvent_Free, 1) + WINPR_API wCountdownEvent* CountdownEvent_New(size_t initialCount); + + /* Hash Table */ + + typedef UINT32 (*HASH_TABLE_HASH_FN)(const void* key); + + typedef struct s_wHashTable wHashTable; + + typedef BOOL (*HASH_TABLE_FOREACH_FN)(const void* key, void* value, void* arg); + + WINPR_API size_t HashTable_Count(wHashTable* table); + +#if defined(WITH_WINPR_DEPRECATED) + WINPR_API WINPR_DEPRECATED(int HashTable_Add(wHashTable* table, const void* key, + const void* value)); +#endif + + WINPR_API BOOL HashTable_Insert(wHashTable* table, const void* key, const void* value); + WINPR_API BOOL HashTable_Remove(wHashTable* table, const void* key); + WINPR_API void HashTable_Clear(wHashTable* table); + WINPR_API BOOL HashTable_Contains(wHashTable* table, const void* key); + WINPR_API BOOL HashTable_ContainsKey(wHashTable* table, const void* key); + WINPR_API BOOL HashTable_ContainsValue(wHashTable* table, const void* value); + WINPR_API void* HashTable_GetItemValue(wHashTable* table, const void* key); + WINPR_API BOOL HashTable_SetItemValue(wHashTable* table, const void* key, const void* value); + WINPR_API size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys); + WINPR_API BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg); + + WINPR_API UINT32 HashTable_PointerHash(const void* pointer); + WINPR_API BOOL HashTable_PointerCompare(const void* pointer1, const void* pointer2); + + WINPR_API UINT32 HashTable_StringHash(const void* key); + WINPR_API BOOL HashTable_StringCompare(const void* string1, const void* string2); + WINPR_API void* HashTable_StringClone(const void* str); + WINPR_API void HashTable_StringFree(void* str); + + WINPR_API void HashTable_Free(wHashTable* table); + + WINPR_ATTR_MALLOC(HashTable_Free, 1) + WINPR_API wHashTable* HashTable_New(BOOL synchronized); + + WINPR_API void HashTable_Lock(wHashTable* table); + WINPR_API void HashTable_Unlock(wHashTable* table); + + WINPR_API wObject* HashTable_KeyObject(wHashTable* table); + WINPR_API wObject* HashTable_ValueObject(wHashTable* table); + + WINPR_API BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn); + + /* Utility function to setup hash table for strings */ + WINPR_API BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues); + + /* BufferPool */ + + typedef struct s_wBufferPool wBufferPool; + + WINPR_API SSIZE_T BufferPool_GetPoolSize(wBufferPool* pool); + WINPR_API SSIZE_T BufferPool_GetBufferSize(wBufferPool* pool, const void* buffer); + + WINPR_API void* BufferPool_Take(wBufferPool* pool, SSIZE_T bufferSize); + WINPR_API BOOL BufferPool_Return(wBufferPool* pool, void* buffer); + WINPR_API void BufferPool_Clear(wBufferPool* pool); + + WINPR_API void BufferPool_Free(wBufferPool* pool); + + WINPR_ATTR_MALLOC(BufferPool_Free, 1) + WINPR_API wBufferPool* BufferPool_New(BOOL synchronized, SSIZE_T fixedSize, DWORD alignment); + + /* ObjectPool */ + + typedef struct s_wObjectPool wObjectPool; + + WINPR_API void* ObjectPool_Take(wObjectPool* pool); + WINPR_API void ObjectPool_Return(wObjectPool* pool, void* obj); + WINPR_API void ObjectPool_Clear(wObjectPool* pool); + + WINPR_API wObject* ObjectPool_Object(wObjectPool* pool); + + WINPR_API void ObjectPool_Free(wObjectPool* pool); + + WINPR_ATTR_MALLOC(ObjectPool_Free, 1) + WINPR_API wObjectPool* ObjectPool_New(BOOL synchronized); + + /* Message Queue */ + + typedef struct s_wMessage wMessage; + + typedef void (*MESSAGE_FREE_FN)(wMessage* message); + + struct s_wMessage + { + UINT32 id; + void* context; + void* wParam; + void* lParam; + UINT64 time; + MESSAGE_FREE_FN Free; + }; + + typedef struct s_wMessageQueue wMessageQueue; + +#define WMQ_QUIT 0xFFFFFFFF + + WINPR_API wObject* MessageQueue_Object(wMessageQueue* queue); + WINPR_API HANDLE MessageQueue_Event(wMessageQueue* queue); + WINPR_API BOOL MessageQueue_Wait(wMessageQueue* queue); + WINPR_API size_t MessageQueue_Size(wMessageQueue* queue); + + WINPR_API BOOL MessageQueue_Dispatch(wMessageQueue* queue, const wMessage* message); + WINPR_API BOOL MessageQueue_Post(wMessageQueue* queue, void* context, UINT32 type, void* wParam, + void* lParam); + WINPR_API BOOL MessageQueue_PostQuit(wMessageQueue* queue, int nExitCode); + + WINPR_API int MessageQueue_Get(wMessageQueue* queue, wMessage* message); + WINPR_API int MessageQueue_Peek(wMessageQueue* queue, wMessage* message, BOOL remove); + + /*! \brief Clears all elements in a message queue. + * + * \note If dynamically allocated data is part of the messages, + * a custom cleanup handler must be passed in the 'callback' + * argument for MessageQueue_New. + * + * \param queue The queue to clear. + * + * \return 0 in case of success or a error code otherwise. + */ + WINPR_API int MessageQueue_Clear(wMessageQueue* queue); + + /*! \brief Frees resources allocated by a message queue. + * This function will only free resources allocated + * internally. + * + * \note Empty the queue before calling this function with + * 'MessageQueue_Clear', 'MessageQueue_Get' or + * 'MessageQueue_Peek' to free all resources allocated + * by the message contained. + * + * \param queue A pointer to the queue to be freed. + */ + WINPR_API void MessageQueue_Free(wMessageQueue* queue); + + /*! \brief Creates a new message queue. + * If 'callback' is null, no custom cleanup will be done + * on message queue deallocation. + * If the 'callback' argument contains valid uninit or + * free functions those will be called by + * 'MessageQueue_Clear'. + * + * \param callback a pointer to custom initialization / cleanup functions. + * Can be NULL if not used. + * + * \return A pointer to a newly allocated MessageQueue or NULL. + */ + WINPR_ATTR_MALLOC(MessageQueue_Free, 1) + WINPR_API wMessageQueue* MessageQueue_New(const wObject* callback); + + /* Message Pipe */ + + typedef struct + { + wMessageQueue* In; + wMessageQueue* Out; + } wMessagePipe; + + WINPR_API void MessagePipe_PostQuit(wMessagePipe* pipe, int nExitCode); + + WINPR_API void MessagePipe_Free(wMessagePipe* pipe); + + WINPR_ATTR_MALLOC(MessagePipe_Free, 1) + WINPR_API wMessagePipe* MessagePipe_New(void); + + /* Publisher/Subscriber Pattern */ + + typedef struct + { + DWORD Size; + const char* Sender; + } wEventArgs; + + typedef void (*pEventHandler)(void* context, const wEventArgs* e); + +#ifdef __cplusplus +#define WINPR_EVENT_CAST(t, val) reinterpret_cast(val) +#else +#define WINPR_EVENT_CAST(t, val) (t)(val) +#endif + +#define MAX_EVENT_HANDLERS 32 + + typedef struct + { + const char* EventName; + wEventArgs EventArgs; + size_t EventHandlerCount; + pEventHandler EventHandlers[MAX_EVENT_HANDLERS]; + } wEventType; + +#define EventArgsInit(_event_args, _sender) \ + memset(_event_args, 0, sizeof(*_event_args)); \ + (_event_args)->e.Size = sizeof(*_event_args); \ + (_event_args)->e.Sender = _sender + +#define DEFINE_EVENT_HANDLER(name) \ + typedef void (*p##name##EventHandler)(void* context, const name##EventArgs* e) + +#define DEFINE_EVENT_RAISE(name) \ + static INLINE int PubSub_On##name(wPubSub* pubSub, void* context, const name##EventArgs* e) \ + { \ + WINPR_ASSERT(e); \ + return PubSub_OnEvent(pubSub, #name, context, &e->e); \ + } + +#define DEFINE_EVENT_SUBSCRIBE(name) \ + static INLINE int PubSub_Subscribe##name(wPubSub* pubSub, p##name##EventHandler EventHandler) \ + { \ + return PubSub_Subscribe(pubSub, #name, EventHandler); \ + } + +#define DEFINE_EVENT_UNSUBSCRIBE(name) \ + static INLINE int PubSub_Unsubscribe##name(wPubSub* pubSub, \ + p##name##EventHandler EventHandler) \ + { \ + return PubSub_Unsubscribe(pubSub, #name, EventHandler); \ + } + +#define DEFINE_EVENT_BEGIN(name) \ + typedef struct \ + { \ + wEventArgs e; + +#define DEFINE_EVENT_END(name) \ + } \ + name##EventArgs; \ + DEFINE_EVENT_HANDLER(name); \ + DEFINE_EVENT_RAISE(name) \ + DEFINE_EVENT_SUBSCRIBE(name) \ + DEFINE_EVENT_UNSUBSCRIBE(name) + +#define DEFINE_EVENT_ENTRY(name) \ + { \ +#name, { sizeof(name##EventArgs), NULL }, 0, \ + { \ + NULL \ + } \ + } + + typedef struct s_wPubSub wPubSub; + + WINPR_API void PubSub_Lock(wPubSub* pubSub); + WINPR_API void PubSub_Unlock(wPubSub* pubSub); + + WINPR_API wEventType* PubSub_GetEventTypes(wPubSub* pubSub, size_t* count); + WINPR_API void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, size_t count); + WINPR_API wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName); + + WINPR_API int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, ...); + WINPR_API int PubSub_Unsubscribe(wPubSub* pubSub, const char* EventName, ...); + + WINPR_API int PubSub_OnEvent(wPubSub* pubSub, const char* EventName, void* context, + const wEventArgs* e); + + WINPR_API void PubSub_Free(wPubSub* pubSub); + + WINPR_ATTR_MALLOC(PubSub_Free, 1) + WINPR_API wPubSub* PubSub_New(BOOL synchronized); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_COLLECTIONS_H */ diff --git a/winpr/include/winpr/comm.h b/winpr/include/winpr/comm.h new file mode 100644 index 0000000..9eb535c --- /dev/null +++ b/winpr/include/winpr/comm.h @@ -0,0 +1,565 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_COMM_H +#define WINPR_COMM_H + +#include +#include +#include +#include + +#if defined __linux__ && !defined ANDROID + +#define NOPARITY 0 +#define ODDPARITY 1 +#define EVENPARITY 2 +#define MARKPARITY 3 +#define SPACEPARITY 4 + +#define ONESTOPBIT 0 +#define ONE5STOPBITS 1 +#define TWOSTOPBITS 2 + +#ifndef IGNORE +#define IGNORE 0 +#endif + +#define CBR_110 110 +#define CBR_300 300 +#define CBR_600 600 +#define CBR_1200 1200 +#define CBR_2400 2400 +#define CBR_4800 4800 +#define CBR_9600 9600 +#define CBR_14400 14400 +#define CBR_19200 19200 +#define CBR_38400 38400 +#define CBR_56000 56000 +#define CBR_57600 57600 +#define CBR_115200 115200 +#define CBR_128000 128000 +#define CBR_256000 256000 + +#define CE_RXOVER 0x0001 +#define CE_OVERRUN 0x0002 +#define CE_RXPARITY 0x0004 +#define CE_FRAME 0x0008 +#define CE_BREAK 0x0010 +#define CE_TXFULL 0x0100 +#define CE_PTO 0x0200 +#define CE_IOE 0x0400 +#define CE_DNS 0x0800 +#define CE_OOP 0x1000 +#define CE_MODE 0x8000 + +#define IE_BADID (-1) +#define IE_OPEN (-2) +#define IE_NOPEN (-3) +#define IE_MEMORY (-4) +#define IE_DEFAULT (-5) +#define IE_HARDWARE (-10) +#define IE_BYTESIZE (-11) +#define IE_BAUDRATE (-12) + +#define EV_RXCHAR 0x0001 +#define EV_RXFLAG 0x0002 +#define EV_TXEMPTY 0x0004 +#define EV_CTS 0x0008 +#define EV_DSR 0x0010 +#define EV_RLSD 0x0020 +#define EV_BREAK 0x0040 +#define EV_ERR 0x0080 +#define EV_RING 0x0100 +#define EV_PERR 0x0200 +#define EV_RX80FULL 0x0400 +#define EV_EVENT1 0x0800 +#define EV_EVENT2 0x1000 + +#define SETXOFF 1 +#define SETXON 2 +#define SETRTS 3 +#define CLRRTS 4 +#define SETDTR 5 +#define CLRDTR 6 +#define RESETDEV 7 +#define SETBREAK 8 +#define CLRBREAK 9 + +#define PURGE_TXABORT 0x0001 +#define PURGE_RXABORT 0x0002 +#define PURGE_TXCLEAR 0x0004 +#define PURGE_RXCLEAR 0x0008 + +#define LPTx 0x80 + +#define MS_CTS_ON ((DWORD)0x0010) +#define MS_DSR_ON ((DWORD)0x0020) +#define MS_RING_ON ((DWORD)0x0040) +#define MS_RLSD_ON ((DWORD)0x0080) + +#define SP_SERIALCOMM ((DWORD)0x00000001) + +#define PST_UNSPECIFIED ((DWORD)0x00000000) +#define PST_RS232 ((DWORD)0x00000001) +#define PST_PARALLELPORT ((DWORD)0x00000002) +#define PST_RS422 ((DWORD)0x00000003) +#define PST_RS423 ((DWORD)0x00000004) +#define PST_RS449 ((DWORD)0x00000005) +#define PST_MODEM ((DWORD)0x00000006) +#define PST_FAX ((DWORD)0x00000021) +#define PST_SCANNER ((DWORD)0x00000022) +#define PST_NETWORK_BRIDGE ((DWORD)0x00000100) +#define PST_LAT ((DWORD)0x00000101) +#define PST_TCPIP_TELNET ((DWORD)0x00000102) +#define PST_X25 ((DWORD)0x00000103) + +#define PCF_DTRDSR ((DWORD)0x0001) +#define PCF_RTSCTS ((DWORD)0x0002) +#define PCF_RLSD ((DWORD)0x0004) +#define PCF_PARITY_CHECK ((DWORD)0x0008) +#define PCF_XONXOFF ((DWORD)0x0010) +#define PCF_SETXCHAR ((DWORD)0x0020) +#define PCF_TOTALTIMEOUTS ((DWORD)0x0040) +#define PCF_INTTIMEOUTS ((DWORD)0x0080) +#define PCF_SPECIALCHARS ((DWORD)0x0100) +#define PCF_16BITMODE ((DWORD)0x0200) + +#define SP_PARITY ((DWORD)0x0001) +#define SP_BAUD ((DWORD)0x0002) +#define SP_DATABITS ((DWORD)0x0004) +#define SP_STOPBITS ((DWORD)0x0008) +#define SP_HANDSHAKING ((DWORD)0x0010) +#define SP_PARITY_CHECK ((DWORD)0x0020) +#define SP_RLSD ((DWORD)0x0040) + +#define BAUD_075 ((DWORD)0x00000001) +#define BAUD_110 ((DWORD)0x00000002) +#define BAUD_134_5 ((DWORD)0x00000004) +#define BAUD_150 ((DWORD)0x00000008) +#define BAUD_300 ((DWORD)0x00000010) +#define BAUD_600 ((DWORD)0x00000020) +#define BAUD_1200 ((DWORD)0x00000040) +#define BAUD_1800 ((DWORD)0x00000080) +#define BAUD_2400 ((DWORD)0x00000100) +#define BAUD_4800 ((DWORD)0x00000200) +#define BAUD_7200 ((DWORD)0x00000400) +#define BAUD_9600 ((DWORD)0x00000800) +#define BAUD_14400 ((DWORD)0x00001000) +#define BAUD_19200 ((DWORD)0x00002000) +#define BAUD_38400 ((DWORD)0x00004000) +#define BAUD_56K ((DWORD)0x00008000) +#define BAUD_128K ((DWORD)0x00010000) +#define BAUD_115200 ((DWORD)0x00020000) +#define BAUD_57600 ((DWORD)0x00040000) +#define BAUD_USER ((DWORD)0x10000000) + +#define DATABITS_5 ((WORD)0x0001) +#define DATABITS_6 ((WORD)0x0002) +#define DATABITS_7 ((WORD)0x0004) +#define DATABITS_8 ((WORD)0x0008) +#define DATABITS_16 ((WORD)0x0010) +#define DATABITS_16X ((WORD)0x0020) + +#define STOPBITS_10 ((WORD)0x0001) +#define STOPBITS_15 ((WORD)0x0002) +#define STOPBITS_20 ((WORD)0x0004) + +#define PARITY_NONE ((WORD)0x0100) +#define PARITY_ODD ((WORD)0x0200) +#define PARITY_EVEN ((WORD)0x0400) +#define PARITY_MARK ((WORD)0x0800) +#define PARITY_SPACE ((WORD)0x1000) + +#define COMMPROP_INITIALIZED ((DWORD)0xE73CF52E) + +#define DTR_CONTROL_DISABLE 0x00 +#define DTR_CONTROL_ENABLE 0x01 +#define DTR_CONTROL_HANDSHAKE 0x02 + +#define RTS_CONTROL_DISABLE 0x00 +#define RTS_CONTROL_ENABLE 0x01 +#define RTS_CONTROL_HANDSHAKE 0x02 +#define RTS_CONTROL_TOGGLE 0x03 + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx +typedef struct +{ + DWORD DCBlength; + DWORD BaudRate; + DWORD fBinary : 1; + DWORD fParity : 1; + DWORD fOutxCtsFlow : 1; + DWORD fOutxDsrFlow : 1; + DWORD fDtrControl : 2; + DWORD fDsrSensitivity : 1; + DWORD fTXContinueOnXoff : 1; + DWORD fOutX : 1; + DWORD fInX : 1; + DWORD fErrorChar : 1; + DWORD fNull : 1; + DWORD fRtsControl : 2; + DWORD fAbortOnError : 1; + DWORD fDummy2 : 17; + WORD wReserved; + WORD XonLim; + WORD XoffLim; + BYTE ByteSize; + BYTE Parity; + BYTE StopBits; + char XonChar; + char XoffChar; + char ErrorChar; + char EofChar; + char EvtChar; + WORD wReserved1; +} DCB, *LPDCB; + +typedef struct +{ + DWORD dwSize; + WORD wVersion; + WORD wReserved; + DCB dcb; + DWORD dwProviderSubType; + DWORD dwProviderOffset; + DWORD dwProviderSize; + WCHAR wcProviderData[1]; +} COMMCONFIG, *LPCOMMCONFIG; + +typedef struct +{ + WORD wPacketLength; + WORD wPacketVersion; + DWORD dwServiceMask; + DWORD dwReserved1; + DWORD dwMaxTxQueue; + DWORD dwMaxRxQueue; + DWORD dwMaxBaud; + DWORD dwProvSubType; + DWORD dwProvCapabilities; + DWORD dwSettableParams; + DWORD dwSettableBaud; + WORD wSettableData; + WORD wSettableStopParity; + DWORD dwCurrentTxQueue; + DWORD dwCurrentRxQueue; + DWORD dwProvSpec1; + DWORD dwProvSpec2; + WCHAR wcProvChar[1]; +} COMMPROP, *LPCOMMPROP; + +typedef struct +{ + DWORD ReadIntervalTimeout; + DWORD ReadTotalTimeoutMultiplier; + DWORD ReadTotalTimeoutConstant; + DWORD WriteTotalTimeoutMultiplier; + DWORD WriteTotalTimeoutConstant; +} COMMTIMEOUTS, *LPCOMMTIMEOUTS; + +typedef struct +{ + DWORD fCtsHold : 1; + DWORD fDsrHold : 1; + DWORD fRlsdHold : 1; + DWORD fXoffHold : 1; + DWORD fXoffSent : 1; + DWORD fEof : 1; + DWORD fTxim : 1; + DWORD fReserved : 25; + DWORD cbInQue; + DWORD cbOutQue; +} COMSTAT, *LPCOMSTAT; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB); + WINPR_API BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB); + + WINPR_API BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, + LPCOMMTIMEOUTS lpCommTimeouts); + WINPR_API BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, + LPCOMMTIMEOUTS lpCommTimeouts); + + WINPR_API BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC); + WINPR_API BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC); + + WINPR_API BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize); + WINPR_API BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize); + + WINPR_API BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask); + WINPR_API BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask); + + WINPR_API BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat); + WINPR_API BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp); + + WINPR_API BOOL GetCommState(HANDLE hFile, LPDCB lpDCB); + WINPR_API BOOL SetCommState(HANDLE hFile, LPDCB lpDCB); + + WINPR_API BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); + WINPR_API BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts); + + WINPR_API BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize); + WINPR_API BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize); + + WINPR_API BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize); + WINPR_API BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize); + + WINPR_API BOOL SetCommBreak(HANDLE hFile); + WINPR_API BOOL ClearCommBreak(HANDLE hFile); + WINPR_API BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat); + + WINPR_API BOOL PurgeComm(HANDLE hFile, DWORD dwFlags); + WINPR_API BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue); + + WINPR_API BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc); + + WINPR_API BOOL TransmitCommChar(HANDLE hFile, char cChar); + + WINPR_API BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped); + +#ifdef UNICODE +#define BuildCommDCB BuildCommDCBW +#define BuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsW +#define CommConfigDialog CommConfigDialogW +#define GetDefaultCommConfig GetDefaultCommConfigW +#define SetDefaultCommConfig SetDefaultCommConfigW +#else +#define BuildCommDCB BuildCommDCBA +#define BuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsA +#define CommConfigDialog CommConfigDialogA +#define GetDefaultCommConfig GetDefaultCommConfigA +#define SetDefaultCommConfig SetDefaultCommConfigA +#endif + +/* Extended API */ + +/* FIXME: MAXULONG should be defined arround winpr/limits.h */ +#ifndef MAXULONG +#define MAXULONG (4294967295UL) +#endif + + /** + * IOCTLs table according the server's serial driver: + * http://msdn.microsoft.com/en-us/library/windows/hardware/dn265347%28v=vs.85%29.aspx + */ + typedef enum + { + SerialDriverUnknown = 0, + SerialDriverSerialSys, + SerialDriverSerCxSys, + SerialDriverSerCx2Sys /* default fallback, see also CommDeviceIoControl() */ + } SERIAL_DRIVER_ID; + + /* + * About DefineCommDevice() / QueryDosDevice() + * + * Did something close to QueryDosDevice() and DefineDosDevice() but with + * folowing constraints: + * - mappings are stored in a static array. + * - QueryCommDevice returns only the mappings that have been defined through + * DefineCommDevice() + */ + WINPR_API BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath); + WINPR_API DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax); + WINPR_API BOOL IsCommDevice(LPCTSTR lpDeviceName); + + /** + * A handle can only be created on defined devices with DefineCommDevice(). This + * also ensures that CommCreateFileA() has been registered through + * RegisterHandleCreator(). + */ + WINPR_API HANDLE CommCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + +#define IOCTL_SERIAL_SET_BAUD_RATE 0x001B0004 +#define IOCTL_SERIAL_GET_BAUD_RATE 0x001B0050 +#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C +#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054 +#define IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C +#define IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020 +/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */ +#define IOCTL_SERIAL_GET_CHARS 0x001B0058 +#define IOCTL_SERIAL_SET_CHARS 0x001B005C + +#define IOCTL_SERIAL_SET_DTR 0x001B0024 +#define IOCTL_SERIAL_CLR_DTR 0x001B0028 +#define IOCTL_SERIAL_RESET_DEVICE 0x001B002C +#define IOCTL_SERIAL_SET_RTS 0x001B0030 +#define IOCTL_SERIAL_CLR_RTS 0x001B0034 +#define IOCTL_SERIAL_SET_XOFF 0x001B0038 +#define IOCTL_SERIAL_SET_XON 0x001B003C +#define IOCTL_SERIAL_SET_BREAK_ON 0x001B0010 +#define IOCTL_SERIAL_SET_BREAK_OFF 0x001B0014 +#define IOCTL_SERIAL_SET_QUEUE_SIZE 0x001B0008 +#define IOCTL_SERIAL_GET_WAIT_MASK 0x001B0040 +#define IOCTL_SERIAL_SET_WAIT_MASK 0x001B0044 +#define IOCTL_SERIAL_WAIT_ON_MASK 0x001B0048 +#define IOCTL_SERIAL_IMMEDIATE_CHAR 0x001B0018 +#define IOCTL_SERIAL_PURGE 0x001B004C +#define IOCTL_SERIAL_GET_HANDFLOW 0x001B0060 +#define IOCTL_SERIAL_SET_HANDFLOW 0x001B0064 +#define IOCTL_SERIAL_GET_MODEMSTATUS 0x001B0068 +#define IOCTL_SERIAL_GET_DTRRTS 0x001B0078 + +/* according to [MS-RDPESP] it should be 0x001B0084, but servers send 0x001B006C */ +#define IOCTL_SERIAL_GET_COMMSTATUS 0x001B006C + +#define IOCTL_SERIAL_GET_PROPERTIES 0x001B0074 +/* IOCTL_SERIAL_XOFF_COUNTER 0x001B0070 */ +/* IOCTL_SERIAL_LSRMST_INSERT 0x001B007C */ +#define IOCTL_SERIAL_CONFIG_SIZE 0x001B0080 +/* IOCTL_SERIAL_GET_STATS 0x001B008C */ +/* IOCTL_SERIAL_CLEAR_STATS 0x001B0090 */ +/* IOCTL_SERIAL_GET_MODEM_CONTROL 0x001B0094 */ +/* IOCTL_SERIAL_SET_MODEM_CONTROL 0x001B0098 */ +/* IOCTL_SERIAL_SET_FIFO_CONTROL 0x001B009C */ + +/* IOCTL_PAR_QUERY_INFORMATION 0x00160004 */ +/* IOCTL_PAR_SET_INFORMATION 0x00160008 */ +/* IOCTL_PAR_QUERY_DEVICE_ID 0x0016000C */ +/* IOCTL_PAR_QUERY_DEVICE_ID_SIZE 0x00160010 */ +/* IOCTL_IEEE1284_GET_MODE 0x00160014 */ +/* IOCTL_IEEE1284_NEGOTIATE 0x00160018 */ +/* IOCTL_PAR_SET_WRITE_ADDRESS 0x0016001C */ +/* IOCTL_PAR_SET_READ_ADDRESS 0x00160020 */ +/* IOCTL_PAR_GET_DEVICE_CAPS 0x00160024 */ +/* IOCTL_PAR_GET_DEFAULT_MODES 0x00160028 */ +/* IOCTL_PAR_QUERY_RAW_DEVICE_ID 0x00160030 */ +/* IOCTL_PAR_IS_PORT_FREE 0x00160054 */ + +/* http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */ +#define IOCTL_USBPRINT_GET_1284_ID 0x220034 + + typedef struct + { + ULONG number; + const char* name; + } _SERIAL_IOCTL_NAME; + + static const _SERIAL_IOCTL_NAME _SERIAL_IOCTL_NAMES[] = { + { IOCTL_SERIAL_SET_BAUD_RATE, "IOCTL_SERIAL_SET_BAUD_RATE" }, + { IOCTL_SERIAL_GET_BAUD_RATE, "IOCTL_SERIAL_GET_BAUD_RATE" }, + { IOCTL_SERIAL_SET_LINE_CONTROL, "IOCTL_SERIAL_SET_LINE_CONTROL" }, + { IOCTL_SERIAL_GET_LINE_CONTROL, "IOCTL_SERIAL_GET_LINE_CONTROL" }, + { IOCTL_SERIAL_SET_TIMEOUTS, "IOCTL_SERIAL_SET_TIMEOUTS" }, + { IOCTL_SERIAL_GET_TIMEOUTS, "IOCTL_SERIAL_GET_TIMEOUTS" }, + { IOCTL_SERIAL_GET_CHARS, "IOCTL_SERIAL_GET_CHARS" }, + { IOCTL_SERIAL_SET_CHARS, "IOCTL_SERIAL_SET_CHARS" }, + { IOCTL_SERIAL_SET_DTR, "IOCTL_SERIAL_SET_DTR" }, + { IOCTL_SERIAL_CLR_DTR, "IOCTL_SERIAL_CLR_DTR" }, + { IOCTL_SERIAL_RESET_DEVICE, "IOCTL_SERIAL_RESET_DEVICE" }, + { IOCTL_SERIAL_SET_RTS, "IOCTL_SERIAL_SET_RTS" }, + { IOCTL_SERIAL_CLR_RTS, "IOCTL_SERIAL_CLR_RTS" }, + { IOCTL_SERIAL_SET_XOFF, "IOCTL_SERIAL_SET_XOFF" }, + { IOCTL_SERIAL_SET_XON, "IOCTL_SERIAL_SET_XON" }, + { IOCTL_SERIAL_SET_BREAK_ON, "IOCTL_SERIAL_SET_BREAK_ON" }, + { IOCTL_SERIAL_SET_BREAK_OFF, "IOCTL_SERIAL_SET_BREAK_OFF" }, + { IOCTL_SERIAL_SET_QUEUE_SIZE, "IOCTL_SERIAL_SET_QUEUE_SIZE" }, + { IOCTL_SERIAL_GET_WAIT_MASK, "IOCTL_SERIAL_GET_WAIT_MASK" }, + { IOCTL_SERIAL_SET_WAIT_MASK, "IOCTL_SERIAL_SET_WAIT_MASK" }, + { IOCTL_SERIAL_WAIT_ON_MASK, "IOCTL_SERIAL_WAIT_ON_MASK" }, + { IOCTL_SERIAL_IMMEDIATE_CHAR, "IOCTL_SERIAL_IMMEDIATE_CHAR" }, + { IOCTL_SERIAL_PURGE, "IOCTL_SERIAL_PURGE" }, + { IOCTL_SERIAL_GET_HANDFLOW, "IOCTL_SERIAL_GET_HANDFLOW" }, + { IOCTL_SERIAL_SET_HANDFLOW, "IOCTL_SERIAL_SET_HANDFLOW" }, + { IOCTL_SERIAL_GET_MODEMSTATUS, "IOCTL_SERIAL_GET_MODEMSTATUS" }, + { IOCTL_SERIAL_GET_DTRRTS, "IOCTL_SERIAL_GET_DTRRTS" }, + { IOCTL_SERIAL_GET_COMMSTATUS, "IOCTL_SERIAL_GET_COMMSTATUS" }, + { IOCTL_SERIAL_GET_PROPERTIES, "IOCTL_SERIAL_GET_PROPERTIES" }, + // {IOCTL_SERIAL_XOFF_COUNTER, "IOCTL_SERIAL_XOFF_COUNTER"}, + // {IOCTL_SERIAL_LSRMST_INSERT, "IOCTL_SERIAL_LSRMST_INSERT"}, + { IOCTL_SERIAL_CONFIG_SIZE, "IOCTL_SERIAL_CONFIG_SIZE" }, + // {IOCTL_SERIAL_GET_STATS, "IOCTL_SERIAL_GET_STATS"}, + // {IOCTL_SERIAL_CLEAR_STATS, "IOCTL_SERIAL_CLEAR_STATS"}, + // {IOCTL_SERIAL_GET_MODEM_CONTROL,"IOCTL_SERIAL_GET_MODEM_CONTROL"}, + // {IOCTL_SERIAL_SET_MODEM_CONTROL,"IOCTL_SERIAL_SET_MODEM_CONTROL"}, + // {IOCTL_SERIAL_SET_FIFO_CONTROL, "IOCTL_SERIAL_SET_FIFO_CONTROL"}, + + // {IOCTL_PAR_QUERY_INFORMATION, "IOCTL_PAR_QUERY_INFORMATION"}, + // {IOCTL_PAR_SET_INFORMATION, "IOCTL_PAR_SET_INFORMATION"}, + // {IOCTL_PAR_QUERY_DEVICE_ID, "IOCTL_PAR_QUERY_DEVICE_ID"}, + // {IOCTL_PAR_QUERY_DEVICE_ID_SIZE,"IOCTL_PAR_QUERY_DEVICE_ID_SIZE"}, + // {IOCTL_IEEE1284_GET_MODE, "IOCTL_IEEE1284_GET_MODE"}, + // {IOCTL_IEEE1284_NEGOTIATE, "IOCTL_IEEE1284_NEGOTIATE"}, + // {IOCTL_PAR_SET_WRITE_ADDRESS, "IOCTL_PAR_SET_WRITE_ADDRESS"}, + // {IOCTL_PAR_SET_READ_ADDRESS, "IOCTL_PAR_SET_READ_ADDRESS"}, + // {IOCTL_PAR_GET_DEVICE_CAPS, "IOCTL_PAR_GET_DEVICE_CAPS"}, + // {IOCTL_PAR_GET_DEFAULT_MODES, "IOCTL_PAR_GET_DEFAULT_MODES"}, + // {IOCTL_PAR_QUERY_RAW_DEVICE_ID, "IOCTL_PAR_QUERY_RAW_DEVICE_ID"}, + // {IOCTL_PAR_IS_PORT_FREE, "IOCTL_PAR_IS_PORT_FREE"}, + + { IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID" }, + + { 0, NULL } + }; + + /** + * FIXME: got a proper function name and place + */ + WINPR_API const char* _comm_serial_ioctl_name(ULONG number); + + /** + * FIXME: got a proper function name and place + */ + WINPR_API void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID); + + /** + * FIXME: got a proper function name and place + * + * permissive mode is disabled by default. + */ + WINPR_API BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive); + + /** + * FIXME: to be moved in comm_ioctl.h + */ + WINPR_API BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, + DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped); + + /** + * FIXME: to be moved in comm_io.h + */ + WINPR_API BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); + + /** + * FIXME: to be moved in comm_io.h + */ + WINPR_API BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); + +#ifdef __cplusplus +} +#endif + +#endif /* __linux__ */ + +#endif /* WINPR_COMM_H */ diff --git a/winpr/include/winpr/cred.h b/winpr/include/winpr/cred.h new file mode 100644 index 0000000..0c7ce8f --- /dev/null +++ b/winpr/include/winpr/cred.h @@ -0,0 +1,62 @@ +/** + * WinPR: Windows Portable Runtime + * Windows credentials + * + * Copyright 2022 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WINPR_CRED_H_ +#define WINPR_CRED_H_ + +#include + +#ifdef _WIN32 +#include +#else + +#define CERT_HASH_LENGTH 20 + +typedef enum +{ + CertCredential, + UsernameTargetCredential, + BinaryBlobCredential, + UsernameForPackedCredentials, + BinaryBlobForSystem +} CRED_MARSHAL_TYPE, + *PCRED_MARSHAL_TYPE; + +typedef struct +{ + ULONG cbSize; + UCHAR rgbHashOfCert[CERT_HASH_LENGTH]; +} CERT_CREDENTIAL_INFO, *PCERT_CREDENTIAL_INFO; + +#if 0 /* shall we implement these ? */ +WINPR_API BOOL CredMarshalCredentialA(CRED_MARSHAL_TYPE CredType, PVOID Credential, + LPSTR* MarshaledCredential); +WINPR_API BOOL CredMarshalCredentialW(CRED_MARSHAL_TYPE CredType, PVOID Credential, + LPWSTR* MarshaledCredential); + +#ifdef UNICODE +#define CredMarshalCredential CredMarshalCredentialW +#else +#define CredMarshalCredential CredMarshalCredentialA +#endif + +#endif /* 0 */ + +#endif /* _WIN32 */ + +#endif /* WINPR_CRED_H_ */ diff --git a/winpr/include/winpr/crt.h b/winpr/include/winpr/crt.h new file mode 100644 index 0000000..6c155ee --- /dev/null +++ b/winpr/include/winpr/crt.h @@ -0,0 +1,233 @@ +/** + * WinPR: Windows Portable Runtime + * C Run-Time Library Routines + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CRT_H +#define WINPR_CRT_H + +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef _WIN32 + +#include + +#ifndef _write +#define _write write +#endif + +#ifndef _strtoui64 +#define _strtoui64 strtoull +#endif /* _strtoui64 */ + +#ifndef _strtoi64 +#define _strtoi64 strtoll +#endif /* _strtoi64 */ + +#ifndef _rotl +static INLINE UINT32 _rotl(UINT32 value, int shift) +{ + return (value << shift) | (value >> (32 - shift)); +} +#endif /* _rotl */ + +#ifndef _rotl64 +static INLINE UINT64 _rotl64(UINT64 value, int shift) +{ + return (value << shift) | (value >> (64 - shift)); +} +#endif /* _rotl64 */ + +#ifndef _rotr +static INLINE UINT32 _rotr(UINT32 value, int shift) +{ + return (value >> shift) | (value << (32 - shift)); +} +#endif /* _rotr */ + +#ifndef _rotr64 +static INLINE UINT64 _rotr64(UINT64 value, int shift) +{ + return (value >> shift) | (value << (64 - shift)); +} +#endif /* _rotr64 */ + +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) + +#define _byteswap_ulong(_val) __builtin_bswap32(_val) +#define _byteswap_uint64(_val) __builtin_bswap64(_val) + +#else + +static INLINE UINT32 _byteswap_ulong(UINT32 _val) +{ + return (((_val) >> 24) | (((_val)&0x00FF0000) >> 8) | (((_val)&0x0000FF00) << 8) | + ((_val) << 24)); +} + +static INLINE UINT64 _byteswap_uint64(UINT64 _val) +{ + return (((_val) << 56) | (((_val) << 40) & 0xFF000000000000) | + (((_val) << 24) & 0xFF0000000000) | (((_val) << 8) & 0xFF00000000) | + (((_val) >> 8) & 0xFF000000) | (((_val) >> 24) & 0xFF0000) | (((_val) >> 40) & 0xFF00) | + ((_val) >> 56)); +} + +#endif /* (__GNUC__ > 4) || ... */ + +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) + +#define _byteswap_ushort(_val) __builtin_bswap16(_val) + +#else + +static INLINE UINT16 _byteswap_ushort(UINT16 _val) +{ +#ifdef __cplusplus +#define winpr_byteswap_cast(t, val) static_cast(val) +#else +#define winpr_byteswap_cast(t, val) (t)(val) +#endif + return winpr_byteswap_cast(UINT16, ((_val) >> 8U) | ((_val) << 8U)); +#undef winpr_byteswap_cast +} + +#endif /* (__GNUC__ > 4) || ... */ + +#define CopyMemory(Destination, Source, Length) memcpy((Destination), (Source), (Length)) +#define MoveMemory(Destination, Source, Length) memmove((Destination), (Source), (Length)) +#define FillMemory(Destination, Length, Fill) memset((Destination), (Fill), (Length)) +#define ZeroMemory(Destination, Length) memset((Destination), 0, (Length)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API PVOID SecureZeroMemory(PVOID ptr, SIZE_T cnt); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIN32 */ + +/* Data Alignment */ + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifndef _ERRNO_T_DEFINED +#define _ERRNO_T_DEFINED +typedef int errno_t; +#endif /* _ERRNO_T_DEFINED */ + +WINPR_PRAGMA_DIAG_POP + +#ifndef _WIN32 + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Data Conversion */ + + WINPR_API errno_t _itoa_s(int value, char* buffer, size_t sizeInCharacters, int radix); + + /* Buffer Manipulation */ + + WINPR_API errno_t memmove_s(void* dest, size_t numberOfElements, const void* src, size_t count); + WINPR_API errno_t wmemmove_s(WCHAR* dest, size_t numberOfElements, const WCHAR* src, + size_t count); +#ifdef __cplusplus +} +#endif + +#endif /* _WIN32 */ + +#if !defined(_WIN32) || (defined(__MINGW32__) && !defined(_UCRT)) +/* note: we use our own implementation of _aligned_XXX function when: + * - it's not win32 + * - it's mingw with native libs (not ucrt64) because we didn't managed to have it working + * and not have C runtime deadly mixes + */ +#if defined(WINPR_MSVCR_ALIGNMENT_EMULATE) +#define _aligned_malloc winpr_aligned_malloc +#define _aligned_realloc winpr_aligned_realloc +#define _aligned_recalloc winpr_aligned_recalloc +#define _aligned_offset_malloc winpr_aligned_offset_malloc +#define _aligned_offset_realloc winpr_aligned_offset_realloc +#define _aligned_offset_recalloc winpr_aligned_offset_recalloc +#define _aligned_msize winpr_aligned_msize +#define _aligned_free winpr_aligned_free +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void* winpr_aligned_malloc(size_t size, size_t alignment); + + WINPR_API void* winpr_aligned_calloc(size_t count, size_t size, size_t alignment); + + WINPR_API void* winpr_aligned_realloc(void* memblock, size_t size, size_t alignment); + + WINPR_API void* winpr_aligned_recalloc(void* memblock, size_t num, size_t size, + size_t alignment); + + WINPR_API void* winpr_aligned_offset_malloc(size_t size, size_t alignment, size_t offset); + + WINPR_API void* winpr_aligned_offset_realloc(void* memblock, size_t size, size_t alignment, + size_t offset); + + WINPR_API void* winpr_aligned_offset_recalloc(void* memblock, size_t num, size_t size, + size_t alignment, size_t offset); + + WINPR_API size_t winpr_aligned_msize(void* memblock, size_t alignment, size_t offset); + + WINPR_API void winpr_aligned_free(void* memblock); + +#ifdef __cplusplus +} +#endif + +#else +#define winpr_aligned_malloc _aligned_malloc +#define winpr_aligned_realloc _aligned_realloc +#define winpr_aligned_recalloc _aligned_recalloc +#define winpr_aligned_offset_malloc _aligned_offset_malloc +#define winpr_aligned_offset_realloc _aligned_offset_realloc +#define winpr_aligned_offset_recalloc _aligned_offset_recalloc +#define winpr_aligned_msize _aligned_msize +#define winpr_aligned_free _aligned_free +#endif /* !defined(_WIN32) || (defined(__MINGW32__) ... */ + +#if defined(_WIN32) && (!defined(__MINGW32__) || defined(_UCRT)) +#define winpr_aligned_calloc(count, size, alignment) _aligned_recalloc(NULL, count, size, alignment) +#endif /* defined(_WIN32) && (!defined(__MINGW32__) || defined(_UCRT)) */ + +#endif /* WINPR_CRT_H */ diff --git a/winpr/include/winpr/crypto.h b/winpr/include/winpr/crypto.h new file mode 100644 index 0000000..df38fed --- /dev/null +++ b/winpr/include/winpr/crypto.h @@ -0,0 +1,26 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CRYPTO_H +#define WINPR_CRYPTO_H + +#include +#include + +#endif /* WINPR_CRYPTO_H */ diff --git a/winpr/include/winpr/custom-crypto.h b/winpr/include/winpr/custom-crypto.h new file mode 100644 index 0000000..32ff5d4 --- /dev/null +++ b/winpr/include/winpr/custom-crypto.h @@ -0,0 +1,269 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CUSTOM_CRYPTO_H +#define WINPR_CUSTOM_CRYPTO_H + +#include +#include + +#include + +/** + * Custom Crypto API Abstraction Layer + */ + +#define WINPR_MD4_DIGEST_LENGTH 16 +#define WINPR_MD5_DIGEST_LENGTH 16 +#define WINPR_SHA1_DIGEST_LENGTH 20 +#define WINPR_SHA224_DIGEST_LENGTH 28 +#define WINPR_SHA256_DIGEST_LENGTH 32 +#define WINPR_SHA384_DIGEST_LENGTH 48 +#define WINPR_SHA512_DIGEST_LENGTH 64 +#define WINPR_RIPEMD160_DIGEST_LENGTH 20 +#define WINPR_SHA3_224_DIGEST_LENGTH 28 +#define WINPR_SHA3_256_DIGEST_LENGTH 32 +#define WINPR_SHA3_384_DIGEST_LENGTH 48 +#define WINPR_SHA3_512_DIGEST_LENGTH 64 +#define WINPR_SHAKE128_DIGEST_LENGTH 16 +#define WINPR_SHAKE256_DIGEST_LENGTH 32 + +/** + * HMAC + */ +typedef enum +{ + WINPR_MD_NONE = 0, + WINPR_MD_MD2 = 1, + WINPR_MD_MD4 = 2, + WINPR_MD_MD5 = 3, + WINPR_MD_SHA1 = 4, + WINPR_MD_SHA224 = 5, + WINPR_MD_SHA256 = 6, + WINPR_MD_SHA384 = 7, + WINPR_MD_SHA512 = 8, + WINPR_MD_RIPEMD160 = 9, + WINPR_MD_SHA3_224 = 10, + WINPR_MD_SHA3_256 = 11, + WINPR_MD_SHA3_384 = 12, + WINPR_MD_SHA3_512 = 13, + WINPR_MD_SHAKE128 = 14, + WINPR_MD_SHAKE256 = 15 +} WINPR_MD_TYPE; + +typedef struct winpr_hmac_ctx_private_st WINPR_HMAC_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API WINPR_MD_TYPE winpr_md_type_from_string(const char* name); + WINPR_API const char* winpr_md_type_to_string(WINPR_MD_TYPE md); + + WINPR_API void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx); + + WINPR_ATTR_MALLOC(winpr_HMAC_Free, 1) + WINPR_API WINPR_HMAC_CTX* winpr_HMAC_New(void); + WINPR_API BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const void* key, + size_t keylen); + WINPR_API BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const void* input, size_t ilen); + WINPR_API BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, void* output, size_t ilen); + + WINPR_API BOOL winpr_HMAC(WINPR_MD_TYPE md, const void* key, size_t keylen, const void* input, + size_t ilen, void* output, size_t olen); + +#ifdef __cplusplus +} +#endif + +/** + * Generic Digest API + */ + +typedef struct winpr_digest_ctx_private_st WINPR_DIGEST_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void winpr_Digest_Free(WINPR_DIGEST_CTX* ctx); + + WINPR_ATTR_MALLOC(winpr_Digest_Free, 1) + WINPR_API WINPR_DIGEST_CTX* winpr_Digest_New(void); + WINPR_API BOOL winpr_Digest_Init_Allow_FIPS(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md); + WINPR_API BOOL winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md); + WINPR_API BOOL winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const void* input, size_t ilen); + WINPR_API BOOL winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, void* output, size_t ilen); + + WINPR_API BOOL winpr_Digest_Allow_FIPS(WINPR_MD_TYPE md, const void* input, size_t ilen, + void* output, size_t olen); + WINPR_API BOOL winpr_Digest(WINPR_MD_TYPE md, const void* input, size_t ilen, void* output, + size_t olen); + + WINPR_API BOOL winpr_DigestSign_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md, void* key); + WINPR_API BOOL winpr_DigestSign_Update(WINPR_DIGEST_CTX* ctx, const void* input, size_t ilen); + WINPR_API BOOL winpr_DigestSign_Final(WINPR_DIGEST_CTX* ctx, void* output, size_t* piolen); + +#ifdef __cplusplus +} +#endif + +/** + * Random Number Generation + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int winpr_RAND(void* output, size_t len); + WINPR_API int winpr_RAND_pseudo(void* output, size_t len); + +#ifdef __cplusplus +} +#endif + +/** + * RC4 + */ + +typedef struct winpr_rc4_ctx_private_st WINPR_RC4_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void winpr_RC4_Free(WINPR_RC4_CTX* ctx); + + WINPR_ATTR_MALLOC(winpr_RC4_Free, 1) + WINPR_API WINPR_RC4_CTX* winpr_RC4_New_Allow_FIPS(const void* key, size_t keylen); + + WINPR_ATTR_MALLOC(winpr_RC4_Free, 1) + WINPR_API WINPR_RC4_CTX* winpr_RC4_New(const void* key, size_t keylen); + WINPR_API BOOL winpr_RC4_Update(WINPR_RC4_CTX* ctx, size_t length, const void* input, + void* output); + +#ifdef __cplusplus +} +#endif + +/** + * Generic Cipher API + */ + +#define WINPR_AES_BLOCK_SIZE 16 + +/* cipher operation types */ +#define WINPR_ENCRYPT 0 +#define WINPR_DECRYPT 1 + +/* cipher types */ +#define WINPR_CIPHER_NONE 0 +#define WINPR_CIPHER_NULL 1 +#define WINPR_CIPHER_AES_128_ECB 2 +#define WINPR_CIPHER_AES_192_ECB 3 +#define WINPR_CIPHER_AES_256_ECB 4 +#define WINPR_CIPHER_AES_128_CBC 5 +#define WINPR_CIPHER_AES_192_CBC 6 +#define WINPR_CIPHER_AES_256_CBC 7 +#define WINPR_CIPHER_AES_128_CFB128 8 +#define WINPR_CIPHER_AES_192_CFB128 9 +#define WINPR_CIPHER_AES_256_CFB128 10 +#define WINPR_CIPHER_AES_128_CTR 11 +#define WINPR_CIPHER_AES_192_CTR 12 +#define WINPR_CIPHER_AES_256_CTR 13 +#define WINPR_CIPHER_AES_128_GCM 14 +#define WINPR_CIPHER_AES_192_GCM 15 +#define WINPR_CIPHER_AES_256_GCM 16 +#define WINPR_CIPHER_CAMELLIA_128_ECB 17 +#define WINPR_CIPHER_CAMELLIA_192_ECB 18 +#define WINPR_CIPHER_CAMELLIA_256_ECB 19 +#define WINPR_CIPHER_CAMELLIA_128_CBC 20 +#define WINPR_CIPHER_CAMELLIA_192_CBC 21 +#define WINPR_CIPHER_CAMELLIA_256_CBC 22 +#define WINPR_CIPHER_CAMELLIA_128_CFB128 23 +#define WINPR_CIPHER_CAMELLIA_192_CFB128 24 +#define WINPR_CIPHER_CAMELLIA_256_CFB128 25 +#define WINPR_CIPHER_CAMELLIA_128_CTR 26 +#define WINPR_CIPHER_CAMELLIA_192_CTR 27 +#define WINPR_CIPHER_CAMELLIA_256_CTR 28 +#define WINPR_CIPHER_CAMELLIA_128_GCM 29 +#define WINPR_CIPHER_CAMELLIA_192_GCM 30 +#define WINPR_CIPHER_CAMELLIA_256_GCM 31 +#define WINPR_CIPHER_DES_ECB 32 +#define WINPR_CIPHER_DES_CBC 33 +#define WINPR_CIPHER_DES_EDE_ECB 34 +#define WINPR_CIPHER_DES_EDE_CBC 35 +#define WINPR_CIPHER_DES_EDE3_ECB 36 +#define WINPR_CIPHER_DES_EDE3_CBC 37 +#define WINPR_CIPHER_BLOWFISH_ECB 38 +#define WINPR_CIPHER_BLOWFISH_CBC 39 +#define WINPR_CIPHER_BLOWFISH_CFB64 40 +#define WINPR_CIPHER_BLOWFISH_CTR 41 +#define WINPR_CIPHER_ARC4_128 42 +#define WINPR_CIPHER_AES_128_CCM 43 +#define WINPR_CIPHER_AES_192_CCM 44 +#define WINPR_CIPHER_AES_256_CCM 45 +#define WINPR_CIPHER_CAMELLIA_128_CCM 46 +#define WINPR_CIPHER_CAMELLIA_192_CCM 47 +#define WINPR_CIPHER_CAMELLIA_256_CCM 48 + +typedef struct winpr_cipher_ctx_private_st WINPR_CIPHER_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void winpr_Cipher_Free(WINPR_CIPHER_CTX* ctx); + + WINPR_ATTR_MALLOC(winpr_Cipher_Free, 1) + WINPR_API WINPR_CIPHER_CTX* winpr_Cipher_New(int cipher, int op, const void* key, + const void* iv); + WINPR_API BOOL winpr_Cipher_SetPadding(WINPR_CIPHER_CTX* ctx, BOOL enabled); + WINPR_API BOOL winpr_Cipher_Update(WINPR_CIPHER_CTX* ctx, const void* input, size_t ilen, + void* output, size_t* olen); + WINPR_API BOOL winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, void* output, size_t* olen); + +#ifdef __cplusplus +} +#endif + +/** + * Key Generation + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int winpr_Cipher_BytesToKey(int cipher, WINPR_MD_TYPE md, const void* salt, + const void* data, size_t datal, size_t count, void* key, + void* iv); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_CUSTOM_CRYPTO_H */ diff --git a/winpr/include/winpr/debug.h b/winpr/include/winpr/debug.h new file mode 100644 index 0000000..43e6d21 --- /dev/null +++ b/winpr/include/winpr/debug.h @@ -0,0 +1,45 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_H +#define WINPR_DEBUG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + + WINPR_API void winpr_log_backtrace(const char* tag, DWORD level, DWORD size); + WINPR_API void winpr_log_backtrace_ex(wLog* log, DWORD level, DWORD size); + WINPR_API void* winpr_backtrace(DWORD size); + WINPR_API void winpr_backtrace_free(void* buffer); + WINPR_API char** winpr_backtrace_symbols(void* buffer, size_t* used); + WINPR_API void winpr_backtrace_symbols_fd(void* buffer, int fd); + WINPR_API char* winpr_strerror(DWORD dw, char* dmsg, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_WLOG_H */ diff --git a/winpr/include/winpr/dsparse.h b/winpr/include/winpr/dsparse.h new file mode 100644 index 0000000..63b2b54 --- /dev/null +++ b/winpr/include/winpr/dsparse.h @@ -0,0 +1,127 @@ +/** + * WinPR: Windows Portable Runtime + * Active Directory Domain Services Parsing Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DSPARSE_H +#define WINPR_DSPARSE_H + +#if defined(_WIN32) && !defined(_UWP) + +#include +#include + +#include + +#else + +#include +#include +#include +#include + +typedef enum +{ + DS_NAME_NO_FLAGS = 0x0, + DS_NAME_FLAG_SYNTACTICAL_ONLY = 0x1, + DS_NAME_FLAG_EVAL_AT_DC = 0x2, + DS_NAME_FLAG_GCVERIFY = 0x4, + DS_NAME_FLAG_TRUST_REFERRAL = 0x8 +} DS_NAME_FLAGS; + +typedef enum +{ + DS_UNKNOWN_NAME = 0, + DS_FQDN_1779_NAME = 1, + DS_NT4_ACCOUNT_NAME = 2, + DS_DISPLAY_NAME = 3, + DS_UNIQUE_ID_NAME = 6, + DS_CANONICAL_NAME = 7, + DS_USER_PRINCIPAL_NAME = 8, + DS_CANONICAL_NAME_EX = 9, + DS_SERVICE_PRINCIPAL_NAME = 10, + DS_SID_OR_SID_HISTORY_NAME = 11, + DS_DNS_DOMAIN_NAME = 12 +} DS_NAME_FORMAT; + +typedef enum +{ + DS_NAME_NO_ERROR = 0, + DS_NAME_ERROR_RESOLVING = 1, + DS_NAME_ERROR_NOT_FOUND = 2, + DS_NAME_ERROR_NOT_UNIQUE = 3, + DS_NAME_ERROR_NO_MAPPING = 4, + DS_NAME_ERROR_DOMAIN_ONLY = 5, + DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 6, + DS_NAME_ERROR_TRUST_REFERRAL = 7 +} DS_NAME_ERROR; + +typedef enum +{ + DS_SPN_DNS_HOST = 0, + DS_SPN_DN_HOST = 1, + DS_SPN_NB_HOST = 2, + DS_SPN_DOMAIN = 3, + DS_SPN_NB_DOMAIN = 4, + DS_SPN_SERVICE = 5 +} DS_SPN_NAME_TYPE; + +typedef struct +{ + DWORD status; + LPTSTR pDomain; + LPTSTR pName; +} DS_NAME_RESULT_ITEM, *PDS_NAME_RESULT_ITEM; + +typedef struct +{ + DWORD cItems; + PDS_NAME_RESULT_ITEM rItems; +} DS_NAME_RESULT, *PDS_NAME_RESULT; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef UNICODE +#define DsMakeSpn DsMakeSpnW +#else +#define DsMakeSpn DsMakeSpnA +#endif + + WINPR_API DWORD DsMakeSpnW(LPCWSTR ServiceClass, LPCWSTR ServiceName, LPCWSTR InstanceName, + USHORT InstancePort, LPCWSTR Referrer, DWORD* pcSpnLength, + LPWSTR pszSpn); + + WINPR_API DWORD DsMakeSpnA(LPCSTR ServiceClass, LPCSTR ServiceName, LPCSTR InstanceName, + USHORT InstancePort, LPCSTR Referrer, DWORD* pcSpnLength, + LPSTR pszSpn); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define DsMakeSpn DsMakeSpnW +#else +#define DsMakeSpn DsMakeSpnA +#endif + +#endif + +#endif /* WINPR_DSPARSE_H */ diff --git a/winpr/include/winpr/endian.h b/winpr/include/winpr/endian.h new file mode 100644 index 0000000..e29872b --- /dev/null +++ b/winpr/include/winpr/endian.h @@ -0,0 +1,196 @@ +/* + * WinPR: Windows Portable Runtime + * Endianness Macros + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_ENDIAN_H +#define WINPR_ENDIAN_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define Data_Read_UINT8_NE(_d, _v) \ + do \ + { \ + _v = *((const BYTE*)_d); \ + } while (0) + +#define Data_Read_UINT8(_d, _v) \ + do \ + { \ + _v = *((const BYTE*)_d); \ + } while (0) + +#define Data_Read_UINT16_NE(_d, _v) \ + do \ + { \ + _v = *((const UINT16*)_d); \ + } while (0) + +#define Data_Read_UINT16(_d, _v) \ + do \ + { \ + _v = (UINT16)(*((const BYTE*)_d)) + (UINT16)(((UINT16)(*((const BYTE*)_d + 1))) << 8); \ + } while (0) + +#define Data_Read_UINT16_BE(_d, _v) \ + do \ + { \ + _v = (((UINT16)(*(const BYTE*)_d)) << 8) + (UINT16)(*((const BYTE*)_d + 1)); \ + } while (0) + +#define Data_Read_UINT32_NE(_d, _v) \ + do \ + { \ + _v = *((UINT32*)_d); \ + } while (0) + +#define Data_Read_UINT32(_d, _v) \ + do \ + { \ + _v = (UINT32)(*((const BYTE*)_d)) + (((UINT32)(*((const BYTE*)_d + 1))) << 8) + \ + (((UINT32)(*((const BYTE*)_d + 2))) << 16) + \ + (((UINT32)(*((const BYTE*)_d + 3))) << 24); \ + } while (0) + +#define Data_Read_UINT32_BE(_d, _v) \ + do \ + { \ + _v = (((UINT32)(*((const BYTE*)_d))) << 24) + (((UINT32)(*((const BYTE*)_d + 1))) << 16) + \ + (((UINT32)(*((const BYTE*)_d + 2))) << 8) + (((UINT32)(*((const BYTE*)_d + 3)))); \ + } while (0) + +#define Data_Read_UINT64_NE(_d, _v) \ + do \ + { \ + _v = *((UINT64*)_d); \ + } while (0) + +#define Data_Read_UINT64(_d, _v) \ + do \ + { \ + _v = (UINT64)(*((const BYTE*)_d)) + (((UINT64)(*((const BYTE*)_d + 1))) << 8) + \ + (((UINT64)(*((const BYTE*)_d + 2))) << 16) + \ + (((UINT64)(*((const BYTE*)_d + 3))) << 24) + \ + (((UINT64)(*((const BYTE*)_d + 4))) << 32) + \ + (((UINT64)(*((const BYTE*)_d + 5))) << 40) + \ + (((UINT64)(*((const BYTE*)_d + 6))) << 48) + \ + (((UINT64)(*((const BYTE*)_d + 7))) << 56); \ + } while (0) + +#define Data_Read_UINT64_BE(_d, _v) \ + do \ + { \ + _v = (((UINT64)(*((const BYTE*)_d))) << 56) + (((UINT64)(*((const BYTE*)_d + 1))) << 48) + \ + (((UINT64)(*((const BYTE*)_d + 2))) << 40) + \ + (((UINT64)(*((const BYTE*)_d + 3))) << 32) + \ + (((UINT64)(*((const BYTE*)_d + 4))) << 24) + \ + (((UINT64)(*((const BYTE*)_d + 5))) << 16) + \ + (((UINT64)(*((const BYTE*)_d + 6))) << 8) + (((UINT64)(*((const BYTE*)_d + 7)))); \ + } while (0) + +#define Data_Write_UINT8_NE(_d, _v) \ + do \ + { \ + *((UINT8*)_d) = v; \ + } while (0) + +#define Data_Write_UINT8(_d, _v) \ + do \ + { \ + *_d = (UINT8)(_v); \ + } while (0) + +#define Data_Write_UINT16_NE(_d, _v) \ + do \ + { \ + *((UINT16*)_d) = _v; \ + } while (0) + +#define Data_Write_UINT16(_d, _v) \ + do \ + { \ + *((BYTE*)_d) = (_v)&0xFF; \ + *((BYTE*)_d + 1) = ((_v) >> 8) & 0xFF; \ + } while (0) + +#define Data_Write_UINT16_BE(_d, _v) \ + do \ + { \ + *((BYTE*)_d) = ((_v) >> 8) & 0xFF; \ + *((BYTE*)_d + 1) = (_v)&0xFF; \ + } while (0) + +#define Data_Write_UINT32_NE(_d, _v) \ + do \ + { \ + *((UINT32*)_d) = _v; \ + } while (0) + +#define Data_Write_UINT32(_d, _v) \ + do \ + { \ + *((BYTE*)_d) = (_v)&0xFF; \ + *((BYTE*)_d + 1) = ((_v) >> 8) & 0xFF; \ + *((BYTE*)_d + 2) = ((_v) >> 16) & 0xFF; \ + *((BYTE*)_d + 3) = ((_v) >> 24) & 0xFF; \ + } while (0) + +#define Data_Write_UINT32_BE(_d, _v) \ + do \ + { \ + Data_Write_UINT16_BE((BYTE*)_d, ((_v) >> 16 & 0xFFFF)); \ + Data_Write_UINT16_BE((BYTE*)_d + 2, ((_v)&0xFFFF)); \ + } while (0) + +#define Data_Write_UINT64_NE(_d, _v) \ + do \ + { \ + *((UINT64*)_d) = _v; \ + } while (0) + +#define Data_Write_UINT64(_d, _v) \ + do \ + { \ + *((BYTE*)_d) = (UINT64)(_v)&0xFF; \ + *((BYTE*)_d + 1) = ((UINT64)(_v) >> 8) & 0xFF; \ + *((BYTE*)_d + 2) = ((UINT64)(_v) >> 16) & 0xFF; \ + *((BYTE*)_d + 3) = ((UINT64)(_v) >> 24) & 0xFF; \ + *((BYTE*)_d + 4) = ((UINT64)(_v) >> 32) & 0xFF; \ + *((BYTE*)_d + 5) = ((UINT64)(_v) >> 40) & 0xFF; \ + *((BYTE*)_d + 6) = ((UINT64)(_v) >> 48) & 0xFF; \ + *((BYTE*)_d + 7) = ((UINT64)(_v) >> 56) & 0xFF; \ + } while (0) + +#define Data_Write_UINT64_BE(_d, _v) \ + do \ + { \ + Data_Write_UINT32_BE((BYTE*)_d, ((_v) >> 32 & 0xFFFFFFFF)); \ + Data_Write_UINT32_BE((BYTE*)_d + 4, ((_v)&0xFFFFFFFF)); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_ENDIAN_H */ diff --git a/winpr/include/winpr/environment.h b/winpr/include/winpr/environment.h new file mode 100644 index 0000000..f530d59 --- /dev/null +++ b/winpr/include/winpr/environment.h @@ -0,0 +1,144 @@ +/** + * WinPR: Windows Portable Runtime + * Process Environment Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2013 Thincast Technologies GmbH + * Copyright 2013 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_ENVIRONMENT_H +#define WINPR_ENVIRONMENT_H + +#include +#include + +#ifndef _WIN32 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API DWORD GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer); + WINPR_API DWORD GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer); + + WINPR_API BOOL SetCurrentDirectoryA(LPCSTR lpPathName); + WINPR_API BOOL SetCurrentDirectoryW(LPCWSTR lpPathName); + + WINPR_API DWORD SearchPathA(LPCSTR lpPath, LPCSTR lpFileName, LPCSTR lpExtension, + DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart); + WINPR_API DWORD SearchPathW(LPCWSTR lpPath, LPCWSTR lpFileName, LPCWSTR lpExtension, + DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR* lpFilePart); + + WINPR_API LPSTR GetCommandLineA(VOID); + WINPR_API LPWSTR GetCommandLineW(VOID); + + WINPR_API BOOL NeedCurrentDirectoryForExePathA(LPCSTR ExeName); + WINPR_API BOOL NeedCurrentDirectoryForExePathW(LPCWSTR ExeName); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define GetCurrentDirectory GetCurrentDirectoryW +#define SetCurrentDirectory SetCurrentDirectoryW +#define SearchPath SearchPathW +#define GetCommandLine GetCommandLineW +#define NeedCurrentDirectoryForExePath NeedCurrentDirectoryForExePathW +#else +#define GetCurrentDirectory GetCurrentDirectoryA +#define SetCurrentDirectory SetCurrentDirectoryA +#define SearchPath SearchPathA +#define GetCommandLine GetCommandLineA +#define NeedCurrentDirectoryForExePath NeedCurrentDirectoryForExePathA +#endif + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API DWORD GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize); + WINPR_API DWORD GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize); + + WINPR_API BOOL SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue); + WINPR_API BOOL SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue); + + /** + * A brief history of the GetEnvironmentStrings functions: + * http://blogs.msdn.com/b/oldnewthing/archive/2013/01/17/10385718.aspx + */ + + WINPR_API LPCH GetEnvironmentStrings(VOID); + WINPR_API LPWCH GetEnvironmentStringsW(VOID); + + WINPR_API BOOL SetEnvironmentStringsA(LPCH NewEnvironment); + WINPR_API BOOL SetEnvironmentStringsW(LPWCH NewEnvironment); + + WINPR_API DWORD ExpandEnvironmentStringsA(LPCSTR lpSrc, LPSTR lpDst, DWORD nSize); + WINPR_API DWORD ExpandEnvironmentStringsW(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize); + + WINPR_API BOOL FreeEnvironmentStringsA(LPCH lpszEnvironmentBlock); + WINPR_API BOOL FreeEnvironmentStringsW(LPWCH lpszEnvironmentBlock); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define GetEnvironmentVariable GetEnvironmentVariableW +#define SetEnvironmentVariable SetEnvironmentVariableW +#define GetEnvironmentStrings GetEnvironmentStringsW +#define SetEnvironmentStrings SetEnvironmentStringsW +#define ExpandEnvironmentStrings ExpandEnvironmentStringsW +#define FreeEnvironmentStrings FreeEnvironmentStringsW +#else +#define GetEnvironmentVariable GetEnvironmentVariableA +#define SetEnvironmentVariable SetEnvironmentVariableA +#define GetEnvironmentStringsA GetEnvironmentStrings +#define SetEnvironmentStrings SetEnvironmentStringsA +#define ExpandEnvironmentStrings ExpandEnvironmentStringsA +#define FreeEnvironmentStrings FreeEnvironmentStringsA +#endif + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API LPCH MergeEnvironmentStrings(PCSTR original, PCSTR merge); + + WINPR_API DWORD GetEnvironmentVariableEBA(LPCSTR envBlock, LPCSTR lpName, LPSTR lpBuffer, + DWORD nSize); + WINPR_API BOOL SetEnvironmentVariableEBA(LPSTR* envBlock, LPCSTR lpName, LPCSTR lpValue); + + WINPR_API char** EnvironmentBlockToEnvpA(LPCH lpszEnvironmentBlock); + + WINPR_API DWORD GetEnvironmentVariableX(const char* lpName, char* lpBuffer, DWORD nSize); + WINPR_API char* GetEnvAlloc(LPCSTR lpName); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_ENVIRONMENT_H */ diff --git a/winpr/include/winpr/error.h b/winpr/include/winpr/error.h new file mode 100644 index 0000000..5c1676a --- /dev/null +++ b/winpr/include/winpr/error.h @@ -0,0 +1,3111 @@ +/** + * WinPR: Windows Portable Runtime + * Error Handling Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_ERROR_H +#define WINPR_ERROR_H + +#include +#include +#include + +#ifdef _WIN32 + +#include + +/* mingw is possibly missing some definitions */ +#ifndef RPC_S_PROXY_ACCESS_DENIED +#define RPC_S_PROXY_ACCESS_DENIED 0x000006C1 +#endif + +#ifndef RPC_S_COOKIE_AUTH_FAILED +#define RPC_S_COOKIE_AUTH_FAILED 0x00000729 +#endif + +#ifndef ERROR_OPERATION_IN_PROGRESS +#define ERROR_OPERATION_IN_PROGRESS 0x00000149 +#endif + +#else + +#ifndef NO_ERROR +#define NO_ERROR 0 +#endif + +#define E_UNEXPECTED -2147418113l // 0x8000FFFFL +#define E_ACCESSDENIED -2147024891l // 0x80070005L +#define E_HANDLE -2147024890l // 0x80070006L +#define E_OUTOFMEMORY -2147024882l // 0x8007000EL + +#define E_INVALIDARG -2147024809l // 0x80070057L +#define E_NOTIMPL -2147467263l // 0x80004001L +#define E_NOINTERFACE -2147467262l // 0x80004002L +#define E_POINTER -2147467261l // 0x80004003L +#define E_ABORT -2147467260l // 0x80004004L +#define E_FAIL -2147467259l // 0x80004005L + +#define CO_E_INIT_TLS -2147467258l // 0x80004006l +#define CO_E_INIT_SHARED_ALLOCATOR -2147467257l // 0x80004007l +#define CO_E_INIT_MEMORY_ALLOCATOR -2147467256l // 0x80004008l +#define CO_E_INIT_CLASS_CACHE -2147467255l // 0x80004009l +#define CO_E_INIT_RPC_CHANNEL -2147467254l // 0x8000400Al +#define CO_E_INIT_TLS_SET_CHANNEL_CONTROL -2147467253l // 0x8000400Bl +#define CO_E_INIT_TLS_CHANNEL_CONTROL -2147467252l // 0x8000400Cl +#define CO_E_INIT_UNACCEPTED_USER_ALLOCATOR -2147467251l // 0x8000400Dl +#define CO_E_INIT_SCM_MUTEX_EXISTS -2147467250l // 0x8000400El +#define CO_E_INIT_SCM_FILE_MAPPING_EXISTS -2147467249l // 0x8000400Fl +#define CO_E_INIT_SCM_MAP_VIEW_OF_FILE -2147467248l // 0x80004010l +#define CO_E_INIT_SCM_EXEC_FAILURE -2147467247l // 0x80004011l +#define CO_E_INIT_ONLY_SINGLE_THREADED -2147467246l // 0x80004012l +#define CO_E_CANT_REMOTE -2147467245l // 0x80004013l +#define CO_E_BAD_SERVER_NAME -2147467244l // 0x80004014l +#define CO_E_WRONG_SERVER_IDENTITY -2147467243l // 0x80004015l +#define CO_E_OLE1DDE_DISABLED -2147467242l // 0x80004016l +#define CO_E_RUNAS_SYNTAX -2147467241l // 0x80004017l +#define CO_E_CREATEPROCESS_FAILURE -2147467240l // 0x80004018l +#define CO_E_RUNAS_CREATEPROCESS_FAILURE -2147467239l // 0x80004019l +#define CO_E_RUNAS_LOGON_FAILURE -2147467238l // 0x8000401Al +#define CO_E_LAUNCH_PERMSSION_DENIED -2147467237l // 0x8000401Bl +#define CO_E_START_SERVICE_FAILURE -2147467236l // 0x8000401Cl +#define CO_E_REMOTE_COMMUNICATION_FAILURE -2147467235l // 0x8000401Dl +#define CO_E_SERVER_START_TIMEOUT -2147467234l // 0x8000401El +#define CO_E_CLSREG_INCONSISTENT -2147467233l // 0x8000401Fl +#define CO_E_IIDREG_INCONSISTENT -2147467232l // 0x80004020l +#define CO_E_NOT_SUPPORTED -2147467231l // 0x80004021l +#define CO_E_RELOAD_DLL -2147467230l // 0x80004022l +#define CO_E_MSI_ERROR -2147467229l // 0x80004023l +#define CO_E_ATTEMPT_TO_CREATE_OUTSIDE_CLIENT_CONTEXT -2147467228l // 0x80004024l +#define CO_E_SERVER_PAUSED -2147467227l // 0x80004025l +#define CO_E_SERVER_NOT_PAUSED -2147467226l // 0x80004026l +#define CO_E_CLASS_DISABLED -2147467225l // 0x80004027l +#define CO_E_CLRNOTAVAILABLE -2147467224l // 0x80004028l +#define CO_E_ASYNC_WORK_REJECTED -2147467223l // 0x80004029l +#define CO_E_SERVER_INIT_TIMEOUT -2147467222l // 0x8000402Al +#define CO_E_NO_SECCTX_IN_ACTIVATE -2147467221l // 0x8000402Bl +#define CO_E_TRACKER_CONFIG -2147467216l // 0x80004030l +#define CO_E_THREADPOOL_CONFIG -2147467215l // 0x80004031l +#define CO_E_SXS_CONFIG -2147467214l // 0x80004032l +#define CO_E_MALFORMED_SPN -2147467213l // 0x80004033l + +#define FACILITY_WINDOWSUPDATE 36 +#define FACILITY_WINDOWS_CE 24 +#define FACILITY_WINDOWS 8 +#define FACILITY_URT 19 +#define FACILITY_UMI 22 +#define FACILITY_SXS 23 +#define FACILITY_STORAGE 3 +#define FACILITY_STATE_MANAGEMENT 34 +#define FACILITY_SSPI 9 +#define FACILITY_SCARD 16 +#define FACILITY_SETUPAPI 15 +#define FACILITY_SECURITY 9 +#define FACILITY_RPC 1 +#define FACILITY_WIN32 7 +#define FACILITY_CONTROL 10 +#define FACILITY_NULL 0 +#define FACILITY_METADIRECTORY 35 +#define FACILITY_MSMQ 14 +#define FACILITY_MEDIASERVER 13 +#define FACILITY_INTERNET 12 +#define FACILITY_ITF 4 +#define FACILITY_HTTP 25 +#define FACILITY_DPLAY 21 +#define FACILITY_DISPATCH 2 +#define FACILITY_DIRECTORYSERVICE 37 +#define FACILITY_CONFIGURATION 33 +#define FACILITY_COMPLUS 17 +#define FACILITY_CERT 11 +#define FACILITY_BACKGROUNDCOPY 32 +#define FACILITY_ACS 20 +#define FACILITY_AAF 18 + +#define FACILITY_NT_BIT 0x10000000 + +#define SEVERITY_SUCCESS 0 +#define SEVERITY_ERROR 1 + +#define HRESULT_CODE(hr) ((hr)&0xFFFF) +#define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1FFF) + +#define HRESULT_FROM_NT(x) (((x) | FACILITY_NT_BIT)) + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifdef __cplusplus +#define ERROR_CAST(t, val) static_cast(val) +#else +#define ERROR_CAST(t, val) (t)(val) +#endif +static INLINE HRESULT HRESULT_FROM_WIN32(unsigned long x) +{ + HRESULT hx = ERROR_CAST(HRESULT, x); + if (hx <= 0) + return hx; + return ERROR_CAST(HRESULT, (((x)&0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)); +} + +WINPR_PRAGMA_DIAG_POP + +#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) + +#define SUCCEEDED(hr) (((hr)) >= 0) +#define FAILED(hr) (((hr)) < 0) +#define IS_ERROR(Status) ((ERROR_CAST(unsigned long, Status)) >> 31 == SEVERITY_ERROR) + +#define MAKE_HRESULT(sev, fac, code) \ + ((HRESULT)((ERROR_CAST(unsigned long, sev) << 31) | (ERROR_CAST(unsigned long, fac) << 16) | \ + (ERROR_CAST(unsigned long, code)))) + +#define SCODE_CODE(sc) ((sc)&0xFFFF) +#define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1FFF) +#define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1) + +#define MAKE_SCODE(sev, fac, code) \ + ((SCODE)((ERROR_CAST(unsigned long, sev) << 31) | (ERROR_CAST(unsigned long, fac) << 16) | \ + (ERROR_CAST(unsigned long, code)))) + +#define S_OK (0L) +#define S_FALSE (1L) + +/* System Error Codes (0-499) */ + +#define ERROR_SUCCESS 0x00000000 +#define ERROR_INVALID_FUNCTION 0x00000001 +#define ERROR_FILE_NOT_FOUND 0x00000002 +#define ERROR_PATH_NOT_FOUND 0x00000003 +#define ERROR_TOO_MANY_OPEN_FILES 0x00000004 +#define ERROR_ACCESS_DENIED 0x00000005 +#define ERROR_INVALID_HANDLE 0x00000006 +#define ERROR_ARENA_TRASHED 0x00000007 +#define ERROR_NOT_ENOUGH_MEMORY 0x00000008 +#define ERROR_INVALID_BLOCK 0x00000009 +#define ERROR_BAD_ENVIRONMENT 0x0000000A +#define ERROR_BAD_FORMAT 0x0000000B +#define ERROR_INVALID_ACCESS 0x0000000C +#define ERROR_INVALID_DATA 0x0000000D +#define ERROR_OUTOFMEMORY 0x0000000E +#define ERROR_INVALID_DRIVE 0x0000000F +#define ERROR_CURRENT_DIRECTORY 0x00000010 +#define ERROR_NOT_SAME_DEVICE 0x00000011 +#define ERROR_NO_MORE_FILES 0x00000012 +#define ERROR_WRITE_PROTECT 0x00000013 +#define ERROR_BAD_UNIT 0x00000014 +#define ERROR_NOT_READY 0x00000015 +#define ERROR_BAD_COMMAND 0x00000016 +#define ERROR_CRC 0x00000017 +#define ERROR_BAD_LENGTH 0x00000018 +#define ERROR_SEEK 0x00000019 +#define ERROR_NOT_DOS_DISK 0x0000001A +#define ERROR_SECTOR_NOT_FOUND 0x0000001B +#define ERROR_OUT_OF_PAPER 0x0000001C +#define ERROR_WRITE_FAULT 0x0000001D +#define ERROR_READ_FAULT 0x0000001E +#define ERROR_GEN_FAILURE 0x0000001F +#define ERROR_SHARING_VIOLATION 0x00000020 +#define ERROR_LOCK_VIOLATION 0x00000021 +#define ERROR_WRONG_DISK 0x00000022 +#define ERROR_SHARING_BUFFER_EXCEEDED 0x00000024 +#define ERROR_HANDLE_EOF 0x00000026 +#define ERROR_HANDLE_DISK_FULL 0x00000027 +#define ERROR_NOT_SUPPORTED 0x00000032 +#define ERROR_REM_NOT_LIST 0x00000033 +#define ERROR_DUP_NAME 0x00000034 +#define ERROR_BAD_NETPATH 0x00000035 +#define ERROR_NETWORK_BUSY 0x00000036 +#define ERROR_DEV_NOT_EXIST 0x00000037 +#define ERROR_TOO_MANY_CMDS 0x00000038 +#define ERROR_ADAP_HDW_ERR 0x00000039 +#define ERROR_BAD_NET_RESP 0x0000003A +#define ERROR_UNEXP_NET_ERR 0x0000003B +#define ERROR_BAD_REM_ADAP 0x0000003C +#define ERROR_PRINTQ_FULL 0x0000003D +#define ERROR_NO_SPOOL_SPACE 0x0000003E +#define ERROR_PRINT_CANCELLED 0x0000003F +#define ERROR_NETNAME_DELETED 0x00000040 +#define ERROR_NETWORK_ACCESS_DENIED 0x00000041 +#define ERROR_BAD_DEV_TYPE 0x00000042 +#define ERROR_BAD_NET_NAME 0x00000043 +#define ERROR_TOO_MANY_NAMES 0x00000044 +#define ERROR_TOO_MANY_SESS 0x00000045 +#define ERROR_SHARING_PAUSED 0x00000046 +#define ERROR_REQ_NOT_ACCEP 0x00000047 +#define ERROR_REDIR_PAUSED 0x00000048 +#define ERROR_FILE_EXISTS 0x00000050 +#define ERROR_CANNOT_MAKE 0x00000052 +#define ERROR_FAIL_I24 0x00000053 +#define ERROR_OUT_OF_STRUCTURES 0x00000054 +#define ERROR_ALREADY_ASSIGNED 0x00000055 +#define ERROR_INVALID_PASSWORD 0x00000056 +#define ERROR_INVALID_PARAMETER 0x00000057 +#define ERROR_NET_WRITE_FAULT 0x00000058 +#define ERROR_NO_PROC_SLOTS 0x00000059 +#define ERROR_TOO_MANY_SEMAPHORES 0x00000064 +#define ERROR_EXCL_SEM_ALREADY_OWNED 0x00000065 +#define ERROR_SEM_IS_SET 0x00000066 +#define ERROR_TOO_MANY_SEM_REQUESTS 0x00000067 +#define ERROR_INVALID_AT_INTERRUPT_TIME 0x00000068 +#define ERROR_SEM_OWNER_DIED 0x00000069 +#define ERROR_SEM_USER_LIMIT 0x0000006A +#define ERROR_DISK_CHANGE 0x0000006B +#define ERROR_DRIVE_LOCKED 0x0000006C +#define ERROR_BROKEN_PIPE 0x0000006D +#define ERROR_OPEN_FAILED 0x0000006E +#define ERROR_BUFFER_OVERFLOW 0x0000006F +#define ERROR_DISK_FULL 0x00000070 +#define ERROR_NO_MORE_SEARCH_HANDLES 0x00000071 +#define ERROR_INVALID_TARGET_HANDLE 0x00000072 +#define ERROR_INVALID_CATEGORY 0x00000075 +#define ERROR_INVALID_VERIFY_SWITCH 0x00000076 +#define ERROR_BAD_DRIVER_LEVEL 0x00000077 +#define ERROR_CALL_NOT_IMPLEMENTED 0x00000078 +#define ERROR_SEM_TIMEOUT 0x00000079 +#define ERROR_INSUFFICIENT_BUFFER 0x0000007A +#define ERROR_INVALID_NAME 0x0000007B +#define ERROR_INVALID_LEVEL 0x0000007C +#define ERROR_NO_VOLUME_LABEL 0x0000007D +#define ERROR_MOD_NOT_FOUND 0x0000007E +#define ERROR_PROC_NOT_FOUND 0x0000007F +#define ERROR_WAIT_NO_CHILDREN 0x00000080 +#define ERROR_CHILD_NOT_COMPLETE 0x00000081 +#define ERROR_DIRECT_ACCESS_HANDLE 0x00000082 +#define ERROR_NEGATIVE_SEEK 0x00000083 +#define ERROR_SEEK_ON_DEVICE 0x00000084 +#define ERROR_IS_JOIN_TARGET 0x00000085 +#define ERROR_IS_JOINED 0x00000086 +#define ERROR_IS_SUBSTED 0x00000087 +#define ERROR_NOT_JOINED 0x00000088 +#define ERROR_NOT_SUBSTED 0x00000089 +#define ERROR_JOIN_TO_JOIN 0x0000008A +#define ERROR_SUBST_TO_SUBST 0x0000008B +#define ERROR_JOIN_TO_SUBST 0x0000008C +#define ERROR_SUBST_TO_JOIN 0x0000008D +#define ERROR_BUSY_DRIVE 0x0000008E +#define ERROR_SAME_DRIVE 0x0000008F +#define ERROR_DIR_NOT_ROOT 0x00000090 +#define ERROR_DIR_NOT_EMPTY 0x00000091 +#define ERROR_IS_SUBST_PATH 0x00000092 +#define ERROR_IS_JOIN_PATH 0x00000093 +#define ERROR_PATH_BUSY 0x00000094 +#define ERROR_IS_SUBST_TARGET 0x00000095 +#define ERROR_SYSTEM_TRACE 0x00000096 +#define ERROR_INVALID_EVENT_COUNT 0x00000097 +#define ERROR_TOO_MANY_MUXWAITERS 0x00000098 +#define ERROR_INVALID_LIST_FORMAT 0x00000099 +#define ERROR_LABEL_TOO_LONG 0x0000009A +#define ERROR_TOO_MANY_TCBS 0x0000009B +#define ERROR_SIGNAL_REFUSED 0x0000009C +#define ERROR_DISCARDED 0x0000009D +#define ERROR_NOT_LOCKED 0x0000009E +#define ERROR_BAD_THREADID_ADDR 0x0000009F +#define ERROR_BAD_ARGUMENTS 0x000000A0 +#define ERROR_BAD_PATHNAME 0x000000A1 +#define ERROR_SIGNAL_PENDING 0x000000A2 +#define ERROR_MAX_THRDS_REACHED 0x000000A4 +#define ERROR_LOCK_FAILED 0x000000A7 +#define ERROR_BUSY 0x000000AA +#define ERROR_DEVICE_SUPPORT_IN_PROGRESS 0x000000AB +#define ERROR_CANCEL_VIOLATION 0x000000AD +#define ERROR_ATOMIC_LOCKS_NOT_SUPPORTED 0x000000AE +#define ERROR_INVALID_SEGMENT_NUMBER 0x000000B4 +#define ERROR_INVALID_ORDINAL 0x000000B6 +#define ERROR_ALREADY_EXISTS 0x000000B7 +#define ERROR_INVALID_FLAG_NUMBER 0x000000BA +#define ERROR_SEM_NOT_FOUND 0x000000BB +#define ERROR_INVALID_STARTING_CODESEG 0x000000BC +#define ERROR_INVALID_STACKSEG 0x000000BD +#define ERROR_INVALID_MODULETYPE 0x000000BE +#define ERROR_INVALID_EXE_SIGNATURE 0x000000BF +#define ERROR_EXE_MARKED_INVALID 0x000000C0 +#define ERROR_BAD_EXE_FORMAT 0x000000C1 +#define ERROR_ITERATED_DATA_EXCEEDS_64k 0x000000C2 +#define ERROR_INVALID_MINALLOCSIZE 0x000000C3 +#define ERROR_DYNLINK_FROM_INVALID_RING 0x000000C4 +#define ERROR_IOPL_NOT_ENABLED 0x000000C5 +#define ERROR_INVALID_SEGDPL 0x000000C6 +#define ERROR_AUTODATASEG_EXCEEDS_64k 0x000000C7 +#define ERROR_RING2SEG_MUST_BE_MOVABLE 0x000000C8 +#define ERROR_RELOC_CHAIN_XEEDS_SEGLIM 0x000000C9 +#define ERROR_INFLOOP_IN_RELOC_CHAIN 0x000000CA +#define ERROR_ENVVAR_NOT_FOUND 0x000000CB +#define ERROR_NO_SIGNAL_SENT 0x000000CD +#define ERROR_FILENAME_EXCED_RANGE 0x000000CE +#define ERROR_RING2_STACK_IN_USE 0x000000CF +#define ERROR_META_EXPANSION_TOO_LONG 0x000000D0 +#define ERROR_INVALID_SIGNAL_NUMBER 0x000000D1 +#define ERROR_THREAD_1_INACTIVE 0x000000D2 +#define ERROR_LOCKED 0x000000D4 +#define ERROR_TOO_MANY_MODULES 0x000000D6 +#define ERROR_NESTING_NOT_ALLOWED 0x000000D7 +#define ERROR_EXE_MACHINE_TYPE_MISMATCH 0x000000D8 +#define ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY 0x000000D9 +#define ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY 0x000000DA +#define ERROR_FILE_CHECKED_OUT 0x000000DC +#define ERROR_CHECKOUT_REQUIRED 0x000000DD +#define ERROR_BAD_FILE_TYPE 0x000000DE +#define ERROR_FILE_TOO_LARGE 0x000000DF +#define ERROR_FORMS_AUTH_REQUIRED 0x000000E0 +#define ERROR_VIRUS_INFECTED 0x000000E1 +#define ERROR_VIRUS_DELETED 0x000000E2 +#define ERROR_PIPE_LOCAL 0x000000E5 +#define ERROR_BAD_PIPE 0x000000E6 +#define ERROR_PIPE_BUSY 0x000000E7 +#define ERROR_NO_DATA 0x000000E8 +#define ERROR_PIPE_NOT_CONNECTED 0x000000E9 +#define ERROR_MORE_DATA 0x000000EA +#define ERROR_VC_DISCONNECTED 0x000000F0 +#define ERROR_INVALID_EA_NAME 0x000000FE +#define ERROR_EA_LIST_INCONSISTENT 0x000000FF +#define WAIT_TIMEOUT 0x00000102 +#define ERROR_NO_MORE_ITEMS 0x00000103 +#define ERROR_CANNOT_COPY 0x0000010A +#define ERROR_DIRECTORY 0x0000010B +#define ERROR_EAS_DIDNT_FIT 0x00000113 +#define ERROR_EA_FILE_CORRUPT 0x00000114 +#define ERROR_EA_TABLE_FULL 0x00000115 +#define ERROR_INVALID_EA_HANDLE 0x00000116 +#define ERROR_EAS_NOT_SUPPORTED 0x0000011A +#define ERROR_NOT_OWNER 0x00000120 +#define ERROR_TOO_MANY_POSTS 0x0000012A +#define ERROR_PARTIAL_COPY 0x0000012B +#define ERROR_OPLOCK_NOT_GRANTED 0x0000012C +#define ERROR_INVALID_OPLOCK_PROTOCOL 0x0000012D +#define ERROR_DISK_TOO_FRAGMENTED 0x0000012E +#define ERROR_DELETE_PENDING 0x0000012F +#define ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING 0x00000130 +#define ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME 0x00000131 +#define ERROR_SECURITY_STREAM_IS_INCONSISTENT 0x00000132 +#define ERROR_INVALID_LOCK_RANGE 0x00000133 +#define ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT 0x00000134 +#define ERROR_NOTIFICATION_GUID_ALREADY_DEFINED 0x00000135 +#define ERROR_INVALID_EXCEPTION_HANDLER 0x00000136 +#define ERROR_DUPLICATE_PRIVILEGES 0x00000137 +#define ERROR_NO_RANGES_PROCESSED 0x00000138 +#define ERROR_NOT_ALLOWED_ON_SYSTEM_FILE 0x00000139 +#define ERROR_DISK_RESOURCES_EXHAUSTED 0x0000013A +#define ERROR_INVALID_TOKEN 0x0000013B +#define ERROR_DEVICE_FEATURE_NOT_SUPPORTED 0x0000013C +#define ERROR_MR_MID_NOT_FOUND 0x0000013D +#define ERROR_SCOPE_NOT_FOUND 0x0000013E +#define ERROR_UNDEFINED_SCOPE 0x0000013F +#define ERROR_INVALID_CAP 0x00000140 +#define ERROR_DEVICE_UNREACHABLE 0x00000141 +#define ERROR_DEVICE_NO_RESOURCES 0x00000142 +#define ERROR_DATA_CHECKSUM_ERROR 0x00000143 +#define ERROR_INTERMIXED_KERNEL_EA_OPERATION 0x00000144 +#define ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED 0x00000146 +#define ERROR_OFFSET_ALIGNMENT_VIOLATION 0x00000147 +#define ERROR_INVALID_FIELD_IN_PARAMETER_LIST 0x00000148 +#define ERROR_OPERATION_IN_PROGRESS 0x00000149 +#define ERROR_BAD_DEVICE_PATH 0x0000014A +#define ERROR_TOO_MANY_DESCRIPTORS 0x0000014B +#define ERROR_SCRUB_DATA_DISABLED 0x0000014C +#define ERROR_NOT_REDUNDANT_STORAGE 0x0000014D +#define ERROR_RESIDENT_FILE_NOT_SUPPORTED 0x0000014E +#define ERROR_COMPRESSED_FILE_NOT_SUPPORTED 0x0000014F +#define ERROR_DIRECTORY_NOT_SUPPORTED 0x00000150 +#define ERROR_NOT_READ_FROM_COPY 0x00000151 +#define ERROR_FAIL_NOACTION_REBOOT 0x0000015E +#define ERROR_FAIL_SHUTDOWN 0x0000015F +#define ERROR_FAIL_RESTART 0x00000160 +#define ERROR_MAX_SESSIONS_REACHED 0x00000161 +#define ERROR_THREAD_MODE_ALREADY_BACKGROUND 0x00000190 +#define ERROR_THREAD_MODE_NOT_BACKGROUND 0x00000191 +#define ERROR_PROCESS_MODE_ALREADY_BACKGROUND 0x00000192 +#define ERROR_PROCESS_MODE_NOT_BACKGROUND 0x00000193 +#define ERROR_INVALID_ADDRESS 0x000001E7 + +/* System Error Codes (500-999) */ + +#define ERROR_USER_PROFILE_LOAD 0x000001F4 +#define ERROR_ARITHMETIC_OVERFLOW 0x00000216 +#define ERROR_PIPE_CONNECTED 0x00000217 +#define ERROR_PIPE_LISTENING 0x00000218 +#define ERROR_VERIFIER_STOP 0x00000219 +#define ERROR_ABIOS_ERROR 0x0000021A +#define ERROR_WX86_WARNING 0x0000021B +#define ERROR_WX86_ERROR 0x0000021C +#define ERROR_TIMER_NOT_CANCELED 0x0000021D +#define ERROR_UNWIND 0x0000021E +#define ERROR_BAD_STACK 0x0000021F +#define ERROR_INVALID_UNWIND_TARGET 0x00000220 +#define ERROR_INVALID_PORT_ATTRIBUTES 0x00000221 +#define ERROR_PORT_MESSAGE_TOO_LONG 0x00000222 +#define ERROR_INVALID_QUOTA_LOWER 0x00000223 +#define ERROR_DEVICE_ALREADY_ATTACHED 0x00000224 +#define ERROR_INSTRUCTION_MISALIGNMENT 0x00000225 +#define ERROR_PROFILING_NOT_STARTED 0x00000226 +#define ERROR_PROFILING_NOT_STOPPED 0x00000227 +#define ERROR_COULD_NOT_INTERPRET 0x00000228 +#define ERROR_PROFILING_AT_LIMIT 0x00000229 +#define ERROR_CANT_WAIT 0x0000022A +#define ERROR_CANT_TERMINATE_SELF 0x0000022B +#define ERROR_UNEXPECTED_MM_CREATE_ERR 0x0000022C +#define ERROR_UNEXPECTED_MM_MAP_ERROR 0x0000022D +#define ERROR_UNEXPECTED_MM_EXTEND_ERR 0x0000022E +#define ERROR_BAD_FUNCTION_TABLE 0x0000022F +#define ERROR_NO_GUID_TRANSLATION 0x00000230 +#define ERROR_INVALID_LDT_SIZE 0x00000231 +#define ERROR_INVALID_LDT_OFFSET 0x00000233 +#define ERROR_INVALID_LDT_DESCRIPTOR 0x00000234 +#define ERROR_TOO_MANY_THREADS 0x00000235 +#define ERROR_THREAD_NOT_IN_PROCESS 0x00000236 +#define ERROR_PAGEFILE_QUOTA_EXCEEDED 0x00000237 +#define ERROR_LOGON_SERVER_CONFLICT 0x00000238 +#define ERROR_SYNCHRONIZATION_REQUIRED 0x00000239 +#define ERROR_NET_OPEN_FAILED 0x0000023A +#define ERROR_IO_PRIVILEGE_FAILED 0x0000023B +#define ERROR_CONTROL_C_EXIT 0x0000023C +#define ERROR_MISSING_SYSTEMFILE 0x0000023D +#define ERROR_UNHANDLED_EXCEPTION 0x0000023E +#define ERROR_APP_INIT_FAILURE 0x0000023F +#define ERROR_PAGEFILE_CREATE_FAILED 0x00000240 +#define ERROR_INVALID_IMAGE_HASH 0x00000241 +#define ERROR_NO_PAGEFILE 0x00000242 +#define ERROR_ILLEGAL_FLOAT_CONTEXT 0x00000243 +#define ERROR_NO_EVENT_PAIR 0x00000244 +#define ERROR_DOMAIN_CTRLR_CONFIG_ERROR 0x00000245 +#define ERROR_ILLEGAL_CHARACTER 0x00000246 +#define ERROR_UNDEFINED_CHARACTER 0x00000247 +#define ERROR_FLOPPY_VOLUME 0x00000248 +#define ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT 0x00000249 +#define ERROR_BACKUP_CONTROLLER 0x0000024A +#define ERROR_MUTANT_LIMIT_EXCEEDED 0x0000024B +#define ERROR_FS_DRIVER_REQUIRED 0x0000024C +#define ERROR_CANNOT_LOAD_REGISTRY_FILE 0x0000024D +#define ERROR_DEBUG_ATTACH_FAILED 0x0000024E +#define ERROR_SYSTEM_PROCESS_TERMINATED 0x0000024F +#define ERROR_DATA_NOT_ACCEPTED 0x00000250 +#define ERROR_VDM_HARD_ERROR 0x00000251 +#define ERROR_DRIVER_CANCEL_TIMEOUT 0x00000252 +#define ERROR_REPLY_MESSAGE_MISMATCH 0x00000253 +#define ERROR_LOST_WRITEBEHIND_DATA 0x00000254 +#define ERROR_CLIENT_SERVER_PARAMETERS_INVALID 0x00000255 +#define ERROR_NOT_TINY_STREAM 0x00000256 +#define ERROR_STACK_OVERFLOW_READ 0x00000257 +#define ERROR_CONVERT_TO_LARGE 0x00000258 +#define ERROR_FOUND_OUT_OF_SCOPE 0x00000259 +#define ERROR_ALLOCATE_BUCKET 0x0000025A +#define ERROR_MARSHALL_OVERFLOW 0x0000025B +#define ERROR_INVALID_VARIANT 0x0000025C +#define ERROR_BAD_COMPRESSION_BUFFER 0x0000025D +#define ERROR_AUDIT_FAILED 0x0000025E +#define ERROR_TIMER_RESOLUTION_NOT_SET 0x0000025F +#define ERROR_INSUFFICIENT_LOGON_INFO 0x00000260 +#define ERROR_BAD_DLL_ENTRYPOINT 0x00000261 +#define ERROR_BAD_SERVICE_ENTRYPOINT 0x00000262 +#define ERROR_IP_ADDRESS_CONFLICT1 0x00000263 +#define ERROR_IP_ADDRESS_CONFLICT2 0x00000264 +#define ERROR_REGISTRY_QUOTA_LIMIT 0x00000265 +#define ERROR_NO_CALLBACK_ACTIVE 0x00000266 +#define ERROR_PWD_TOO_SHORT 0x00000267 +#define ERROR_PWD_TOO_RECENT 0x00000268 +#define ERROR_PWD_HISTORY_CONFLICT 0x00000269 +#define ERROR_UNSUPPORTED_COMPRESSION 0x0000026A +#define ERROR_INVALID_HW_PROFILE 0x0000026B +#define ERROR_INVALID_PLUGPLAY_DEVICE_PATH 0x0000026C +#define ERROR_QUOTA_LIST_INCONSISTENT 0x0000026D +#define ERROR_EVALUATION_EXPIRATION 0x0000026E +#define ERROR_ILLEGAL_DLL_RELOCATION 0x0000026F +#define ERROR_DLL_INIT_FAILED_LOGOFF 0x00000270 +#define ERROR_VALIDATE_CONTINUE 0x00000271 +#define ERROR_NO_MORE_MATCHES 0x00000272 +#define ERROR_RANGE_LIST_CONFLICT 0x00000273 +#define ERROR_SERVER_SID_MISMATCH 0x00000274 +#define ERROR_CANT_ENABLE_DENY_ONLY 0x00000275 +#define ERROR_FLOAT_MULTIPLE_FAULTS 0x00000276 +#define ERROR_FLOAT_MULTIPLE_TRAPS 0x00000277 +#define ERROR_NOINTERFACE 0x00000278 +#define ERROR_DRIVER_FAILED_SLEEP 0x00000279 +#define ERROR_CORRUPT_SYSTEM_FILE 0x0000027A +#define ERROR_COMMITMENT_MINIMUM 0x0000027B +#define ERROR_PNP_RESTART_ENUMERATION 0x0000027C +#define ERROR_SYSTEM_IMAGE_BAD_SIGNATURE 0x0000027D +#define ERROR_PNP_REBOOT_REQUIRED 0x0000027E +#define ERROR_INSUFFICIENT_POWER 0x0000027F +#define ERROR_MULTIPLE_FAULT_VIOLATION 0x00000280 +#define ERROR_SYSTEM_SHUTDOWN 0x00000281 +#define ERROR_PORT_NOT_SET 0x00000282 +#define ERROR_DS_VERSION_CHECK_FAILURE 0x00000283 +#define ERROR_RANGE_NOT_FOUND 0x00000284 +#define ERROR_NOT_SAFE_MODE_DRIVER 0x00000286 +#define ERROR_FAILED_DRIVER_ENTRY 0x00000287 +#define ERROR_DEVICE_ENUMERATION_ERROR 0x00000288 +#define ERROR_MOUNT_POINT_NOT_RESOLVED 0x00000289 +#define ERROR_INVALID_DEVICE_OBJECT_PARAMETER 0x0000028A +/* The following is not a typo. It's the same spelling as in the Microsoft headers */ +#define ERROR_MCA_OCCURED 0x0000028B +#define ERROR_DRIVER_DATABASE_ERROR 0x0000028C +#define ERROR_SYSTEM_HIVE_TOO_LARGE 0x0000028D +#define ERROR_DRIVER_FAILED_PRIOR_UNLOAD 0x0000028E +#define ERROR_VOLSNAP_PREPARE_HIBERNATE 0x0000028F +#define ERROR_HIBERNATION_FAILURE 0x00000290 +#define ERROR_PWD_TOO_LONG 0x00000291 +#define ERROR_FILE_SYSTEM_LIMITATION 0x00000299 +#define ERROR_ASSERTION_FAILURE 0x0000029C +#define ERROR_ACPI_ERROR 0x0000029D +#define ERROR_WOW_ASSERTION 0x0000029E +#define ERROR_PNP_BAD_MPS_TABLE 0x0000029F +#define ERROR_PNP_TRANSLATION_FAILED 0x000002A0 +#define ERROR_PNP_IRQ_TRANSLATION_FAILED 0x000002A1 +#define ERROR_PNP_INVALID_ID 0x000002A2 +#define ERROR_WAKE_SYSTEM_DEBUGGER 0x000002A3 +#define ERROR_HANDLES_CLOSED 0x000002A4 +#define ERROR_EXTRANEOUS_INFORMATION 0x000002A5 +#define ERROR_RXACT_COMMIT_NECESSARY 0x000002A6 +#define ERROR_MEDIA_CHECK 0x000002A7 +#define ERROR_GUID_SUBSTITUTION_MADE 0x000002A8 +#define ERROR_STOPPED_ON_SYMLINK 0x000002A9 +#define ERROR_LONGJUMP 0x000002AA +#define ERROR_PLUGPLAY_QUERY_VETOED 0x000002AB +#define ERROR_UNWIND_CONSOLIDATE 0x000002AC +#define ERROR_REGISTRY_HIVE_RECOVERED 0x000002AD +#define ERROR_DLL_MIGHT_BE_INSECURE 0x000002AE +#define ERROR_DLL_MIGHT_BE_INCOMPATIBLE 0x000002AF +#define ERROR_DBG_EXCEPTION_NOT_HANDLED 0x000002B0 +#define ERROR_DBG_REPLY_LATER 0x000002B1 +#define ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE 0x000002B2 +#define ERROR_DBG_TERMINATE_THREAD 0x000002B3 +#define ERROR_DBG_TERMINATE_PROCESS 0x000002B4 +#define ERROR_DBG_CONTROL_C 0x000002B5 +#define ERROR_DBG_PRINTEXCEPTION_C 0x000002B6 +#define ERROR_DBG_RIPEXCEPTION 0x000002B7 +#define ERROR_DBG_CONTROL_BREAK 0x000002B8 +#define ERROR_DBG_COMMAND_EXCEPTION 0x000002B9 +#define ERROR_OBJECT_NAME_EXISTS 0x000002BA +#define ERROR_THREAD_WAS_SUSPENDED 0x000002BB +#define ERROR_IMAGE_NOT_AT_BASE 0x000002BC +#define ERROR_RXACT_STATE_CREATED 0x000002BD +#define ERROR_SEGMENT_NOTIFICATION 0x000002BE +#define ERROR_BAD_CURRENT_DIRECTORY 0x000002BF +#define ERROR_FT_READ_RECOVERY_FROM_BACKUP 0x000002C0 +#define ERROR_FT_WRITE_RECOVERY 0x000002C1 +#define ERROR_IMAGE_MACHINE_TYPE_MISMATCH 0x000002C2 +#define ERROR_RECEIVE_PARTIAL 0x000002C3 +#define ERROR_RECEIVE_EXPEDITED 0x000002C4 +#define ERROR_RECEIVE_PARTIAL_EXPEDITED 0x000002C5 +#define ERROR_EVENT_DONE 0x000002C6 +#define ERROR_EVENT_PENDING 0x000002C7 +#define ERROR_CHECKING_FILE_SYSTEM 0x000002C8 +#define ERROR_FATAL_APP_EXIT 0x000002C9 +#define ERROR_PREDEFINED_HANDLE 0x000002CA +#define ERROR_WAS_UNLOCKED 0x000002CB +#define ERROR_SERVICE_NOTIFICATION 0x000002CC +#define ERROR_WAS_LOCKED 0x000002CD +#define ERROR_LOG_HARD_ERROR 0x000002CE +#define ERROR_ALREADY_WIN32 0x000002CF +#define ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE 0x000002D0 +#define ERROR_NO_YIELD_PERFORMED 0x000002D1 +#define ERROR_TIMER_RESUME_IGNORED 0x000002D2 +#define ERROR_ARBITRATION_UNHANDLED 0x000002D3 +#define ERROR_CARDBUS_NOT_SUPPORTED 0x000002D4 +#define ERROR_MP_PROCESSOR_MISMATCH 0x000002D5 +#define ERROR_HIBERNATED 0x000002D6 +#define ERROR_RESUME_HIBERNATION 0x000002D7 +#define ERROR_FIRMWARE_UPDATED 0x000002D8 +#define ERROR_DRIVERS_LEAKING_LOCKED_PAGES 0x000002D9 +#define ERROR_WAKE_SYSTEM 0x000002DA +#define ERROR_WAIT_1 0x000002DB +#define ERROR_WAIT_2 0x000002DC +#define ERROR_WAIT_3 0x000002DD +#define ERROR_WAIT_63 0x000002DE +#define ERROR_ABANDONED_WAIT_0 0x000002DF +#define ERROR_ABANDONED_WAIT_63 0x000002E0 +#define ERROR_USER_APC 0x000002E1 +#define ERROR_KERNEL_APC 0x000002E2 +#define ERROR_ALERTED 0x000002E3 +#define ERROR_ELEVATION_REQUIRED 0x000002E4 +#define ERROR_REPARSE 0x000002E5 +#define ERROR_OPLOCK_BREAK_IN_PROGRESS 0x000002E6 +#define ERROR_VOLUME_MOUNTED 0x000002E7 +#define ERROR_RXACT_COMMITTED 0x000002E8 +#define ERROR_NOTIFY_CLEANUP 0x000002E9 +#define ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED 0x000002EA +#define ERROR_PAGE_FAULT_TRANSITION 0x000002EB +#define ERROR_PAGE_FAULT_DEMAND_ZERO 0x000002EC +#define ERROR_PAGE_FAULT_COPY_ON_WRITE 0x000002ED +#define ERROR_PAGE_FAULT_GUARD_PAGE 0x000002EE +#define ERROR_PAGE_FAULT_PAGING_FILE 0x000002EF +#define ERROR_CACHE_PAGE_LOCKED 0x000002F0 +#define ERROR_CRASH_DUMP 0x000002F1 +#define ERROR_BUFFER_ALL_ZEROS 0x000002F2 +#define ERROR_REPARSE_OBJECT 0x000002F3 +#define ERROR_RESOURCE_REQUIREMENTS_CHANGED 0x000002F4 +#define ERROR_TRANSLATION_COMPLETE 0x000002F5 +#define ERROR_NOTHING_TO_TERMINATE 0x000002F6 +#define ERROR_PROCESS_NOT_IN_JOB 0x000002F7 +#define ERROR_PROCESS_IN_JOB 0x000002F8 +#define ERROR_VOLSNAP_HIBERNATE_READY 0x000002F9 +#define ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY 0x000002FA +#define ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED 0x000002FB +#define ERROR_INTERRUPT_STILL_CONNECTED 0x000002FC +#define ERROR_WAIT_FOR_OPLOCK 0x000002FD +#define ERROR_DBG_EXCEPTION_HANDLED 0x000002FE +#define ERROR_DBG_CONTINUE 0x000002FF +#define ERROR_CALLBACK_POP_STACK 0x00000300 +#define ERROR_COMPRESSION_DISABLED 0x00000301 +#define ERROR_CANTFETCHBACKWARDS 0x00000302 +#define ERROR_CANTSCROLLBACKWARDS 0x00000303 +#define ERROR_ROWSNOTRELEASED 0x00000304 +#define ERROR_BAD_ACCESSOR_FLAGS 0x00000305 +#define ERROR_ERRORS_ENCOUNTERED 0x00000306 +#define ERROR_NOT_CAPABLE 0x00000307 +#define ERROR_REQUEST_OUT_OF_SEQUENCE 0x00000308 +#define ERROR_VERSION_PARSE_ERROR 0x00000309 +#define ERROR_BADSTARTPOSITION 0x0000030A +#define ERROR_MEMORY_HARDWARE 0x0000030B +#define ERROR_DISK_REPAIR_DISABLED 0x0000030C +#define ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE 0x0000030D +#define ERROR_SYSTEM_POWERSTATE_TRANSITION 0x0000030E +#define ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION 0x0000030F +#define ERROR_MCA_EXCEPTION 0x00000310 +#define ERROR_ACCESS_AUDIT_BY_POLICY 0x00000311 +#define ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY 0x00000312 +#define ERROR_ABANDON_HIBERFILE 0x00000313 +#define ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED 0x00000314 +#define ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR 0x00000315 +#define ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR 0x00000316 +#define ERROR_BAD_MCFG_TABLE 0x00000317 +#define ERROR_DISK_REPAIR_REDIRECTED 0x00000318 +#define ERROR_DISK_REPAIR_UNSUCCESSFUL 0x00000319 +#define ERROR_CORRUPT_LOG_OVERFULL 0x0000031A +#define ERROR_CORRUPT_LOG_CORRUPTED 0x0000031B +#define ERROR_CORRUPT_LOG_UNAVAILABLE 0x0000031C +#define ERROR_CORRUPT_LOG_DELETED_FULL 0x0000031D +#define ERROR_CORRUPT_LOG_CLEARED 0x0000031E +#define ERROR_ORPHAN_NAME_EXHAUSTED 0x0000031F +#define ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE 0x00000320 +#define ERROR_CANNOT_GRANT_REQUESTED_OPLOCK 0x00000321 +#define ERROR_CANNOT_BREAK_OPLOCK 0x00000322 +#define ERROR_OPLOCK_HANDLE_CLOSED 0x00000323 +#define ERROR_NO_ACE_CONDITION 0x00000324 +#define ERROR_INVALID_ACE_CONDITION 0x00000325 +#define ERROR_FILE_HANDLE_REVOKED 0x00000326 +#define ERROR_IMAGE_AT_DIFFERENT_BASE 0x00000327 +#define ERROR_EA_ACCESS_DENIED 0x000003E2 +#define ERROR_OPERATION_ABORTED 0x000003E3 +#define ERROR_IO_INCOMPLETE 0x000003E4 +#define ERROR_IO_PENDING 0x000003E5 +#define ERROR_NOACCESS 0x000003E6 +#define ERROR_SWAPERROR 0x000003E7 + +/* System Error Codes (1000-1299) */ + +#define ERROR_STACK_OVERFLOW 0x000003E9 +#define ERROR_INVALID_MESSAGE 0x000003EA +#define ERROR_CAN_NOT_COMPLETE 0x000003EB +#define ERROR_INVALID_FLAGS 0x000003EC +#define ERROR_UNRECOGNIZED_VOLUME 0x000003ED +#define ERROR_FILE_INVALID 0x000003EE +#define ERROR_FULLSCREEN_MODE 0x000003EF +#define ERROR_NO_TOKEN 0x000003F0 +#define ERROR_BADDB 0x000003F1 +#define ERROR_BADKEY 0x000003F2 +#define ERROR_CANTOPEN 0x000003F3 +#define ERROR_CANTREAD 0x000003F4 +#define ERROR_CANTWRITE 0x000003F5 +#define ERROR_REGISTRY_RECOVERED 0x000003F6 +#define ERROR_REGISTRY_CORRUPT 0x000003F7 +#define ERROR_REGISTRY_IO_FAILED 0x000003F8 +#define ERROR_NOT_REGISTRY_FILE 0x000003F9 +#define ERROR_KEY_DELETED 0x000003FA +#define ERROR_NO_LOG_SPACE 0x000003FB +#define ERROR_KEY_HAS_CHILDREN 0x000003FC +#define ERROR_CHILD_MUST_BE_VOLATILE 0x000003FD +#define ERROR_NOTIFY_ENUM_DIR 0x000003FE +#define ERROR_DEPENDENT_SERVICES_RUNNING 0x0000041B +#define ERROR_INVALID_SERVICE_CONTROL 0x0000041C +#define ERROR_SERVICE_REQUEST_TIMEOUT 0x0000041D +#define ERROR_SERVICE_NO_THREAD 0x0000041E +#define ERROR_SERVICE_DATABASE_LOCKED 0x0000041F +#define ERROR_SERVICE_ALREADY_RUNNING 0x00000420 +#define ERROR_INVALID_SERVICE_ACCOUNT 0x00000421 +#define ERROR_SERVICE_DISABLED 0x00000422 +#define ERROR_CIRCULAR_DEPENDENCY 0x00000423 +#define ERROR_SERVICE_DOES_NOT_EXIST 0x00000424 +#define ERROR_SERVICE_CANNOT_ACCEPT_CTRL 0x00000425 +#define ERROR_SERVICE_NOT_ACTIVE 0x00000426 +#define ERROR_FAILED_SERVICE_CONTROLLER_CONNECT 0x00000427 +#define ERROR_EXCEPTION_IN_SERVICE 0x00000428 +#define ERROR_DATABASE_DOES_NOT_EXIST 0x00000429 +#define ERROR_SERVICE_SPECIFIC_ERROR 0x0000042A +#define ERROR_PROCESS_ABORTED 0x0000042B +#define ERROR_SERVICE_DEPENDENCY_FAIL 0x0000042C +#define ERROR_SERVICE_LOGON_FAILED 0x0000042D +#define ERROR_SERVICE_START_HANG 0x0000042E +#define ERROR_INVALID_SERVICE_LOCK 0x0000042F +#define ERROR_SERVICE_MARKED_FOR_DELETE 0x00000430 +#define ERROR_SERVICE_EXISTS 0x00000431 +#define ERROR_ALREADY_RUNNING_LKG 0x00000432 +#define ERROR_SERVICE_DEPENDENCY_DELETED 0x00000433 +#define ERROR_BOOT_ALREADY_ACCEPTED 0x00000434 +#define ERROR_SERVICE_NEVER_STARTED 0x00000435 +#define ERROR_DUPLICATE_SERVICE_NAME 0x00000436 +#define ERROR_DIFFERENT_SERVICE_ACCOUNT 0x00000437 +#define ERROR_CANNOT_DETECT_DRIVER_FAILURE 0x00000438 +#define ERROR_CANNOT_DETECT_PROCESS_ABORT 0x00000439 +#define ERROR_NO_RECOVERY_PROGRAM 0x0000043A +#define ERROR_SERVICE_NOT_IN_EXE 0x0000043B +#define ERROR_NOT_SAFEBOOT_SERVICE 0x0000043C +#define ERROR_END_OF_MEDIA 0x0000044C +#define ERROR_FILEMARK_DETECTED 0x0000044D +#define ERROR_BEGINNING_OF_MEDIA 0x0000044E +#define ERROR_SETMARK_DETECTED 0x0000044F +#define ERROR_NO_DATA_DETECTED 0x00000450 +#define ERROR_PARTITION_FAILURE 0x00000451 +#define ERROR_INVALID_BLOCK_LENGTH 0x00000452 +#define ERROR_DEVICE_NOT_PARTITIONED 0x00000453 +#define ERROR_UNABLE_TO_LOCK_MEDIA 0x00000454 +#define ERROR_UNABLE_TO_UNLOAD_MEDIA 0x00000455 +#define ERROR_MEDIA_CHANGED 0x00000456 +#define ERROR_BUS_RESET 0x00000457 +#define ERROR_NO_MEDIA_IN_DRIVE 0x00000458 +#define ERROR_NO_UNICODE_TRANSLATION 0x00000459 +#define ERROR_DLL_INIT_FAILED 0x0000045A +#define ERROR_SHUTDOWN_IN_PROGRESS 0x0000045B +#define ERROR_NO_SHUTDOWN_IN_PROGRESS 0x0000045C +#define ERROR_IO_DEVICE 0x0000045D +#define ERROR_SERIAL_NO_DEVICE 0x0000045E +#define ERROR_IRQ_BUSY 0x0000045F +#define ERROR_MORE_WRITES 0x00000460 +#define ERROR_COUNTER_TIMEOUT 0x00000461 +#define ERROR_FLOPPY_ID_MARK_NOT_FOUND 0x00000462 +#define ERROR_FLOPPY_WRONG_CYLINDER 0x00000463 +#define ERROR_FLOPPY_UNKNOWN_ERROR 0x00000464 +#define ERROR_FLOPPY_BAD_REGISTERS 0x00000465 +#define ERROR_DISK_RECALIBRATE_FAILED 0x00000466 +#define ERROR_DISK_OPERATION_FAILED 0x00000467 +#define ERROR_DISK_RESET_FAILED 0x00000468 +#define ERROR_EOM_OVERFLOW 0x00000469 +#define ERROR_NOT_ENOUGH_SERVER_MEMORY 0x0000046A +#define ERROR_POSSIBLE_DEADLOCK 0x0000046B +#define ERROR_MAPPED_ALIGNMENT 0x0000046C +#define ERROR_SET_POWER_STATE_VETOED 0x00000474 +#define ERROR_SET_POWER_STATE_FAILED 0x00000475 +#define ERROR_TOO_MANY_LINKS 0x00000476 +#define ERROR_OLD_WIN_VERSION 0x0000047E +#define ERROR_APP_WRONG_OS 0x0000047F +#define ERROR_SINGLE_INSTANCE_APP 0x00000480 +#define ERROR_RMODE_APP 0x00000481 +#define ERROR_INVALID_DLL 0x00000482 +#define ERROR_NO_ASSOCIATION 0x00000483 +#define ERROR_DDE_FAIL 0x00000484 +#define ERROR_DLL_NOT_FOUND 0x00000485 +#define ERROR_NO_MORE_USER_HANDLES 0x00000486 +#define ERROR_MESSAGE_SYNC_ONLY 0x00000487 +#define ERROR_SOURCE_ELEMENT_EMPTY 0x00000488 +#define ERROR_DESTINATION_ELEMENT_FULL 0x00000489 +#define ERROR_ILLEGAL_ELEMENT_ADDRESS 0x0000048A +#define ERROR_MAGAZINE_NOT_PRESENT 0x0000048B +#define ERROR_DEVICE_REINITIALIZATION_NEEDED 0x0000048C +#define ERROR_DEVICE_REQUIRES_CLEANING 0x0000048D +#define ERROR_DEVICE_DOOR_OPEN 0x0000048E +#define ERROR_DEVICE_NOT_CONNECTED 0x0000048F +#define ERROR_NOT_FOUND 0x00000490 +#define ERROR_NO_MATCH 0x00000491 +#define ERROR_SET_NOT_FOUND 0x00000492 +#define ERROR_POINT_NOT_FOUND 0x00000493 +#define ERROR_NO_TRACKING_SERVICE 0x00000494 +#define ERROR_NO_VOLUME_ID 0x00000495 +#define ERROR_UNABLE_TO_REMOVE_REPLACED 0x00000497 +#define ERROR_UNABLE_TO_MOVE_REPLACEMENT 0x00000498 +#define ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 0x00000499 +#define ERROR_JOURNAL_DELETE_IN_PROGRESS 0x0000049A +#define ERROR_JOURNAL_NOT_ACTIVE 0x0000049B +#define ERROR_POTENTIAL_FILE_FOUND 0x0000049C +#define ERROR_JOURNAL_ENTRY_DELETED 0x0000049D +#define ERROR_SHUTDOWN_IS_SCHEDULED 0x000004A6 +#define ERROR_SHUTDOWN_USERS_LOGGED_ON 0x000004A7 +#define ERROR_BAD_DEVICE 0x000004B0 +#define ERROR_CONNECTION_UNAVAIL 0x000004B1 +#define ERROR_DEVICE_ALREADY_REMEMBERED 0x000004B2 +#define ERROR_NO_NET_OR_BAD_PATH 0x000004B3 +#define ERROR_BAD_PROVIDER 0x000004B4 +#define ERROR_CANNOT_OPEN_PROFILE 0x000004B5 +#define ERROR_BAD_PROFILE 0x000004B6 +#define ERROR_NOT_CONTAINER 0x000004B7 +#define ERROR_EXTENDED_ERROR 0x000004B8 +#define ERROR_INVALID_GROUPNAME 0x000004B9 +#define ERROR_INVALID_COMPUTERNAME 0x000004BA +#define ERROR_INVALID_EVENTNAME 0x000004BB +#define ERROR_INVALID_DOMAINNAME 0x000004BC +#define ERROR_INVALID_SERVICENAME 0x000004BD +#define ERROR_INVALID_NETNAME 0x000004BE +#define ERROR_INVALID_SHARENAME 0x000004BF +#define ERROR_INVALID_PASSWORDNAME 0x000004C0 +#define ERROR_INVALID_MESSAGENAME 0x000004C1 +#define ERROR_INVALID_MESSAGEDEST 0x000004C2 +#define ERROR_SESSION_CREDENTIAL_CONFLICT 0x000004C3 +#define ERROR_REMOTE_SESSION_LIMIT_EXCEEDED 0x000004C4 +#define ERROR_DUP_DOMAINNAME 0x000004C5 +#define ERROR_NO_NETWORK 0x000004C6 +#define ERROR_CANCELLED 0x000004C7 +#define ERROR_USER_MAPPED_FILE 0x000004C8 +#define ERROR_CONNECTION_REFUSED 0x000004C9 +#define ERROR_GRACEFUL_DISCONNECT 0x000004CA +#define ERROR_ADDRESS_ALREADY_ASSOCIATED 0x000004CB +#define ERROR_ADDRESS_NOT_ASSOCIATED 0x000004CC +#define ERROR_CONNECTION_INVALID 0x000004CD +#define ERROR_CONNECTION_ACTIVE 0x000004CE +#define ERROR_NETWORK_UNREACHABLE 0x000004CF +#define ERROR_HOST_UNREACHABLE 0x000004D0 +#define ERROR_PROTOCOL_UNREACHABLE 0x000004D1 +#define ERROR_PORT_UNREACHABLE 0x000004D2 +#define ERROR_REQUEST_ABORTED 0x000004D3 +#define ERROR_CONNECTION_ABORTED 0x000004D4 +#define ERROR_RETRY 0x000004D5 +#define ERROR_CONNECTION_COUNT_LIMIT 0x000004D6 +#define ERROR_LOGIN_TIME_RESTRICTION 0x000004D7 +#define ERROR_LOGIN_WKSTA_RESTRICTION 0x000004D8 +#define ERROR_INCORRECT_ADDRESS 0x000004D9 +#define ERROR_ALREADY_REGISTERED 0x000004DA +#define ERROR_SERVICE_NOT_FOUND 0x000004DB +#define ERROR_NOT_AUTHENTICATED 0x000004DC +#define ERROR_NOT_LOGGED_ON 0x000004DD +#define ERROR_CONTINUE 0x000004DE +#define ERROR_ALREADY_INITIALIZED 0x000004DF +#define ERROR_NO_MORE_DEVICES 0x000004E0 +#define ERROR_NO_SUCH_SITE 0x000004E1 +#define ERROR_DOMAIN_CONTROLLER_EXISTS 0x000004E2 +#define ERROR_ONLY_IF_CONNECTED 0x000004E3 +#define ERROR_OVERRIDE_NOCHANGES 0x000004E4 +#define ERROR_BAD_USER_PROFILE 0x000004E5 +#define ERROR_NOT_SUPPORTED_ON_SBS 0x000004E6 +#define ERROR_SERVER_SHUTDOWN_IN_PROGRESS 0x000004E7 +#define ERROR_HOST_DOWN 0x000004E8 +#define ERROR_NON_ACCOUNT_SID 0x000004E9 +#define ERROR_NON_DOMAIN_SID 0x000004EA +#define ERROR_APPHELP_BLOCK 0x000004EB +#define ERROR_ACCESS_DISABLED_BY_POLICY 0x000004EC +#define ERROR_REG_NAT_CONSUMPTION 0x000004ED +#define ERROR_CSCSHARE_OFFLINE 0x000004EE +#define ERROR_PKINIT_FAILURE 0x000004EF +#define ERROR_SMARTCARD_SUBSYSTEM_FAILURE 0x000004F0 +#define ERROR_DOWNGRADE_DETECTED 0x000004F1 +#define ERROR_MACHINE_LOCKED 0x000004F7 +#define ERROR_CALLBACK_SUPPLIED_INVALID_DATA 0x000004F9 +#define ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED 0x000004FA +#define ERROR_DRIVER_BLOCKED 0x000004FB +#define ERROR_INVALID_IMPORT_OF_NON_DLL 0x000004FC +#define ERROR_ACCESS_DISABLED_WEBBLADE 0x000004FD +#define ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER 0x000004FE +#define ERROR_RECOVERY_FAILURE 0x000004FF +#define ERROR_ALREADY_FIBER 0x00000500 +#define ERROR_ALREADY_THREAD 0x00000501 +#define ERROR_STACK_BUFFER_OVERRUN 0x00000502 +#define ERROR_PARAMETER_QUOTA_EXCEEDED 0x00000503 +#define ERROR_DEBUGGER_INACTIVE 0x00000504 +#define ERROR_DELAY_LOAD_FAILED 0x00000505 +#define ERROR_VDM_DISALLOWED 0x00000506 +#define ERROR_UNIDENTIFIED_ERROR 0x00000507 +#define ERROR_INVALID_CRUNTIME_PARAMETER 0x00000508 +#define ERROR_BEYOND_VDL 0x00000509 +#define ERROR_INCOMPATIBLE_SERVICE_SID_TYPE 0x0000050A +#define ERROR_DRIVER_PROCESS_TERMINATED 0x0000050B +#define ERROR_IMPLEMENTATION_LIMIT 0x0000050C +#define ERROR_PROCESS_IS_PROTECTED 0x0000050D +#define ERROR_SERVICE_NOTIFY_CLIENT_LAGGING 0x0000050E +#define ERROR_DISK_QUOTA_EXCEEDED 0x0000050F +#define ERROR_CONTENT_BLOCKED 0x00000510 +#define ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE 0x00000511 +#define ERROR_APP_HANG 0x00000512 +#define ERROR_INVALID_LABEL 0x00000513 + +/* System Error Codes (1300-1699) */ +#define ERROR_NOT_ALL_ASSIGNED 0x00000514 +#define ERROR_SOME_NOT_MAPPED 0x00000515 +#define ERROR_NO_QUOTAS_FOR_ACCOUNT 0x00000516 +#define ERROR_LOCAL_USER_SESSION_KEY 0x00000517 +#define ERROR_NULL_LM_PASSWORD 0x00000518 +#define ERROR_UNKNOWN_REVISION 0x00000519 +#define ERROR_REVISION_MISMATCH 0x0000051A +#define ERROR_INVALID_OWNER 0x0000051B +#define ERROR_INVALID_PRIMARY_GROUP 0x0000051C +#define ERROR_NO_IMPERSONATION_TOKEN 0x0000051D +#define ERROR_CANT_DISABLE_MANDATORY 0x0000051E +#define ERROR_NO_LOGON_SERVERS 0x0000051F +#define ERROR_NO_SUCH_LOGON_SESSION 0x00000520 +#define ERROR_NO_SUCH_PRIVILEGE 0x00000521 +#define ERROR_PRIVILEGE_NOT_HELD 0x00000522 +#define ERROR_INVALID_ACCOUNT_NAME 0x00000523 +#define ERROR_USER_EXISTS 0x00000524 +#define ERROR_NO_SUCH_USER 0x00000525 +#define ERROR_GROUP_EXISTS 0x00000526 +#define ERROR_NO_SUCH_GROUP 0x00000527 +#define ERROR_MEMBER_IN_GROUP 0x00000528 +#define ERROR_MEMBER_NOT_IN_GROUP 0x00000529 +#define ERROR_LAST_ADMIN 0x0000052A +#define ERROR_WRONG_PASSWORD 0x0000052B +#define ERROR_ILL_FORMED_PASSWORD 0x0000052C +#define ERROR_PASSWORD_RESTRICTION 0x0000052D +#define ERROR_LOGON_FAILURE 0x0000052E +#define ERROR_ACCOUNT_RESTRICTION 0x0000052F +#define ERROR_INVALID_LOGON_HOURS 0x00000530 +#define ERROR_INVALID_WORKSTATION 0x00000531 +#define ERROR_PASSWORD_EXPIRED 0x00000532 +#define ERROR_ACCOUNT_DISABLED 0x00000533 +#define ERROR_NONE_MAPPED 0x00000534 +#define ERROR_TOO_MANY_LUIDS_REQUESTED 0x00000535 +#define ERROR_LUIDS_EXHAUSTED 0x00000536 +#define ERROR_INVALID_SUB_AUTHORITY 0x00000537 +#define ERROR_INVALID_ACL 0x00000538 +#define ERROR_INVALID_SID 0x00000539 +#define ERROR_INVALID_SECURITY_DESCR 0x0000053A +#define ERROR_BAD_INHERITANCE_ACL 0x0000053C +#define ERROR_SERVER_DISABLED 0x0000053D +#define ERROR_SERVER_NOT_DISABLED 0x0000053E +#define ERROR_INVALID_ID_AUTHORITY 0x0000053F +#define ERROR_ALLOTTED_SPACE_EXCEEDED 0x00000540 +#define ERROR_INVALID_GROUP_ATTRIBUTES 0x00000541 +#define ERROR_BAD_IMPERSONATION_LEVEL 0x00000542 +#define ERROR_CANT_OPEN_ANONYMOUS 0x00000543 +#define ERROR_BAD_VALIDATION_CLASS 0x00000544 +#define ERROR_BAD_TOKEN_TYPE 0x00000545 +#define ERROR_NO_SECURITY_ON_OBJECT 0x00000546 +#define ERROR_CANT_ACCESS_DOMAIN_INFO 0x00000547 +#define ERROR_INVALID_SERVER_STATE 0x00000548 +#define ERROR_INVALID_DOMAIN_STATE 0x00000549 +#define ERROR_INVALID_DOMAIN_ROLE 0x0000054A +#define ERROR_NO_SUCH_DOMAIN 0x0000054B +#define ERROR_DOMAIN_EXISTS 0x0000054C +#define ERROR_DOMAIN_LIMIT_EXCEEDED 0x0000054D +#define ERROR_INTERNAL_DB_CORRUPTION 0x0000054E +#define ERROR_INTERNAL_ERROR 0x0000054F +#define ERROR_GENERIC_NOT_MAPPED 0x00000550 +#define ERROR_BAD_DESCRIPTOR_FORMAT 0x00000551 +#define ERROR_NOT_LOGON_PROCESS 0x00000552 +#define ERROR_LOGON_SESSION_EXISTS 0x00000553 +#define ERROR_NO_SUCH_PACKAGE 0x00000554 +#define ERROR_BAD_LOGON_SESSION_STATE 0x00000555 +#define ERROR_LOGON_SESSION_COLLISION 0x00000556 +#define ERROR_INVALID_LOGON_TYPE 0x00000557 +#define ERROR_CANNOT_IMPERSONATE 0x00000558 +#define ERROR_RXACT_INVALID_STATE 0x00000559 +#define ERROR_RXACT_COMMIT_FAILURE 0x0000055A +#define ERROR_SPECIAL_ACCOUNT 0x0000055B +#define ERROR_SPECIAL_GROUP 0x0000055C +#define ERROR_SPECIAL_USER 0x0000055D +#define ERROR_MEMBERS_PRIMARY_GROUP 0x0000055E +#define ERROR_TOKEN_ALREADY_IN_USE 0x0000055F +#define ERROR_NO_SUCH_ALIAS 0x00000560 +#define ERROR_MEMBER_NOT_IN_ALIAS 0x00000561 +#define ERROR_MEMBER_IN_ALIAS 0x00000562 +#define ERROR_ALIAS_EXISTS 0x00000563 +#define ERROR_LOGON_NOT_GRANTED 0x00000564 +#define ERROR_TOO_MANY_SECRETS 0x00000565 +#define ERROR_SECRET_TOO_LONG 0x00000566 +#define ERROR_INTERNAL_DB_ERROR 0x00000567 +#define ERROR_TOO_MANY_CONTEXT_IDS 0x00000568 +#define ERROR_LOGON_TYPE_NOT_GRANTED 0x00000569 +#define ERROR_NT_CROSS_ENCRYPTION_REQUIRED 0x0000056A +#define ERROR_NO_SUCH_MEMBER 0x0000056B +#define ERROR_INVALID_MEMBER 0x0000056C +#define ERROR_TOO_MANY_SIDS 0x0000056D +#define ERROR_LM_CROSS_ENCRYPTION_REQUIRED 0x0000056E +#define ERROR_NO_INHERITANCE 0x0000056F +#define ERROR_FILE_CORRUPT 0x00000570 +#define ERROR_DISK_CORRUPT 0x00000571 +#define ERROR_NO_USER_SESSION_KEY 0x00000572 +#define ERROR_LICENSE_QUOTA_EXCEEDED 0x00000573 +#define ERROR_WRONG_TARGET_NAME 0x00000574 +#define ERROR_MUTUAL_AUTH_FAILED 0x00000575 +#define ERROR_TIME_SKEW 0x00000576 +#define ERROR_CURRENT_DOMAIN_NOT_ALLOWED 0x00000577 +#define ERROR_INVALID_WINDOW_HANDLE 0x00000578 +#define ERROR_INVALID_MENU_HANDLE 0x00000579 +#define ERROR_INVALID_CURSOR_HANDLE 0x0000057A +#define ERROR_INVALID_ACCEL_HANDLE 0x0000057B +#define ERROR_INVALID_HOOK_HANDLE 0x0000057C +#define ERROR_INVALID_DWP_HANDLE 0x0000057D +#define ERROR_TLW_WITH_WSCHILD 0x0000057E +#define ERROR_CANNOT_FIND_WND_CLASS 0x0000057F +#define ERROR_WINDOW_OF_OTHER_THREAD 0x00000580 +#define ERROR_HOTKEY_ALREADY_REGISTERED 0x00000581 +#define ERROR_CLASS_ALREADY_EXISTS 0x00000582 +#define ERROR_CLASS_DOES_NOT_EXIST 0x00000583 +#define ERROR_CLASS_HAS_WINDOWS 0x00000584 +#define ERROR_INVALID_INDEX 0x00000585 +#define ERROR_INVALID_ICON_HANDLE 0x00000586 +#define ERROR_PRIVATE_DIALOG_INDEX 0x00000587 +#define ERROR_LISTBOX_ID_NOT_FOUND 0x00000588 +#define ERROR_NO_WILDCARD_CHARACTERS 0x00000589 +#define ERROR_CLIPBOARD_NOT_OPEN 0x0000058A +#define ERROR_HOTKEY_NOT_REGISTERED 0x0000058B +#define ERROR_WINDOW_NOT_DIALOG 0x0000058C +#define ERROR_CONTROL_ID_NOT_FOUND 0x0000058D +#define ERROR_INVALID_COMBOBOX_MESSAGE 0x0000058E +#define ERROR_WINDOW_NOT_COMBOBOX 0x0000058F +#define ERROR_INVALID_EDIT_HEIGHT 0x00000590 +#define ERROR_DC_NOT_FOUND 0x00000591 +#define ERROR_INVALID_HOOK_FILTER 0x00000592 +#define ERROR_INVALID_FILTER_PROC 0x00000593 +#define ERROR_HOOK_NEEDS_HMOD 0x00000594 +#define ERROR_GLOBAL_ONLY_HOOK 0x00000595 +#define ERROR_JOURNAL_HOOK_SET 0x00000596 +#define ERROR_HOOK_NOT_INSTALLED 0x00000597 +#define ERROR_INVALID_LB_MESSAGE 0x00000598 +#define ERROR_SETCOUNT_ON_BAD_LB 0x00000599 +#define ERROR_LB_WITHOUT_TABSTOPS 0x0000059A +#define ERROR_DESTROY_OBJECT_OF_OTHER_THREAD 0x0000059B +#define ERROR_CHILD_WINDOW_MENU 0x0000059C +#define ERROR_NO_SYSTEM_MENU 0x0000059D +#define ERROR_INVALID_MSGBOX_STYLE 0x0000059E +#define ERROR_INVALID_SPI_VALUE 0x0000059F +#define ERROR_SCREEN_ALREADY_LOCKED 0x000005A0 +#define ERROR_HWNDS_HAVE_DIFF_PARENT 0x000005A1 +#define ERROR_NOT_CHILD_WINDOW 0x000005A2 +#define ERROR_INVALID_GW_COMMAND 0x000005A3 +#define ERROR_INVALID_THREAD_ID 0x000005A4 +#define ERROR_NON_MDICHILD_WINDOW 0x000005A5 +#define ERROR_POPUP_ALREADY_ACTIVE 0x000005A6 +#define ERROR_NO_SCROLLBARS 0x000005A7 +#define ERROR_INVALID_SCROLLBAR_RANGE 0x000005A8 +#define ERROR_INVALID_SHOWWIN_COMMAND 0x000005A9 +#define ERROR_NO_SYSTEM_RESOURCES 0x000005AA +#define ERROR_NONPAGED_SYSTEM_RESOURCES 0x000005AB +#define ERROR_PAGED_SYSTEM_RESOURCES 0x000005AC +#define ERROR_WORKING_SET_QUOTA 0x000005AD +#define ERROR_PAGEFILE_QUOTA 0x000005AE +#define ERROR_COMMITMENT_LIMIT 0x000005AF +#define ERROR_MENU_ITEM_NOT_FOUND 0x000005B0 +#define ERROR_INVALID_KEYBOARD_HANDLE 0x000005B1 +#define ERROR_HOOK_TYPE_NOT_ALLOWED 0x000005B2 +#define ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION 0x000005B3 +#define ERROR_TIMEOUT 0x000005B4 +#define ERROR_INVALID_MONITOR_HANDLE 0x000005B5 +#define ERROR_INCORRECT_SIZE 0x000005B6 +#define ERROR_SYMLINK_CLASS_DISABLED 0x000005B7 +#define ERROR_SYMLINK_NOT_SUPPORTED 0x000005B8 +#define ERROR_XML_PARSE_ERROR 0x000005B9 +#define ERROR_XMLDSIG_ERROR 0x000005BA +#define ERROR_RESTART_APPLICATION 0x000005BB +#define ERROR_WRONG_COMPARTMENT 0x000005BC +#define ERROR_AUTHIP_FAILURE 0x000005BD +#define ERROR_NO_NVRAM_RESOURCES 0x000005BE +#define ERROR_NOT_GUI_PROCESS 0x000005BF +#define ERROR_EVENTLOG_FILE_CORRUPT 0x000005DC +#define ERROR_EVENTLOG_CANT_START 0x000005DD +#define ERROR_LOG_FILE_FULL 0x000005DE +#define ERROR_EVENTLOG_FILE_CHANGED 0x000005DF +#define ERROR_INVALID_TASK_NAME 0x0000060E +#define ERROR_INVALID_TASK_INDEX 0x0000060F +#define ERROR_THREAD_ALREADY_IN_TASK 0x00000610 +#define ERROR_INSTALL_SERVICE_FAILURE 0x00000641 +#define ERROR_INSTALL_USEREXIT 0x00000642 +#define ERROR_INSTALL_FAILURE 0x00000643 +#define ERROR_INSTALL_SUSPEND 0x00000644 +#define ERROR_UNKNOWN_PRODUCT 0x00000645 +#define ERROR_UNKNOWN_FEATURE 0x00000646 +#define ERROR_UNKNOWN_COMPONENT 0x00000647 +#define ERROR_UNKNOWN_PROPERTY 0x00000648 +#define ERROR_INVALID_HANDLE_STATE 0x00000649 +#define ERROR_BAD_CONFIGURATION 0x0000064A +#define ERROR_INDEX_ABSENT 0x0000064B +#define ERROR_INSTALL_SOURCE_ABSENT 0x0000064C +#define ERROR_INSTALL_PACKAGE_VERSION 0x0000064D +#define ERROR_PRODUCT_UNINSTALLED 0x0000064E +#define ERROR_BAD_QUERY_SYNTAX 0x0000064F +#define ERROR_INVALID_FIELD 0x00000650 +#define ERROR_DEVICE_REMOVED 0x00000651 +#define ERROR_INSTALL_ALREADY_RUNNING 0x00000652 +#define ERROR_INSTALL_PACKAGE_OPEN_FAILED 0x00000653 +#define ERROR_INSTALL_PACKAGE_INVALID 0x00000654 +#define ERROR_INSTALL_UI_FAILURE 0x00000655 +#define ERROR_INSTALL_LOG_FAILURE 0x00000656 +#define ERROR_INSTALL_LANGUAGE_UNSUPPORTED 0x00000657 +#define ERROR_INSTALL_TRANSFORM_FAILURE 0x00000658 +#define ERROR_INSTALL_PACKAGE_REJECTED 0x00000659 +#define ERROR_FUNCTION_NOT_CALLED 0x0000065A +#define ERROR_FUNCTION_FAILED 0x0000065B +#define ERROR_INVALID_TABLE 0x0000065C +#define ERROR_DATATYPE_MISMATCH 0x0000065D +#define ERROR_UNSUPPORTED_TYPE 0x0000065E +#define ERROR_CREATE_FAILED 0x0000065F +#define ERROR_INSTALL_TEMP_UNWRITABLE 0x00000660 +#define ERROR_INSTALL_PLATFORM_UNSUPPORTED 0x00000661 +#define ERROR_INSTALL_NOTUSED 0x00000662 +#define ERROR_PATCH_PACKAGE_OPEN_FAILED 0x00000663 +#define ERROR_PATCH_PACKAGE_INVALID 0x00000664 +#define ERROR_PATCH_PACKAGE_UNSUPPORTED 0x00000665 +#define ERROR_PRODUCT_VERSION 0x00000666 +#define ERROR_INVALID_COMMAND_LINE 0x00000667 +#define ERROR_INSTALL_REMOTE_DISALLOWED 0x00000668 +#define ERROR_SUCCESS_REBOOT_INITIATED 0x00000669 +#define ERROR_PATCH_TARGET_NOT_FOUND 0x0000066A +#define ERROR_PATCH_PACKAGE_REJECTED 0x0000066B +#define ERROR_INSTALL_TRANSFORM_REJECTED 0x0000066C +#define ERROR_INSTALL_REMOTE_PROHIBITED 0x0000066D +#define ERROR_PATCH_REMOVAL_UNSUPPORTED 0x0000066E +#define ERROR_UNKNOWN_PATCH 0x0000066F +#define ERROR_PATCH_NO_SEQUENCE 0x00000670 +#define ERROR_PATCH_REMOVAL_DISALLOWED 0x00000671 +#define ERROR_INVALID_PATCH_XML 0x00000672 +#define ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT 0x00000673 +#define ERROR_INSTALL_SERVICE_SAFEBOOT 0x00000674 +#define ERROR_FAIL_FAST_EXCEPTION 0x00000675 +#define ERROR_INSTALL_REJECTED 0x00000676 + +/* System Error Codes (1700-3999) */ + +#define RPC_S_INVALID_STRING_BINDING 0x000006A4 +#define RPC_S_WRONG_KIND_OF_BINDING 0x000006A5 +#define RPC_S_INVALID_BINDING 0x000006A6 +#define RPC_S_PROTSEQ_NOT_SUPPORTED 0x000006A7 +#define RPC_S_INVALID_RPC_PROTSEQ 0x000006A8 +#define RPC_S_INVALID_STRING_UUID 0x000006A9 +#define RPC_S_INVALID_ENDPOINT_FORMAT 0x000006AA +#define RPC_S_INVALID_NET_ADDR 0x000006AB +#define RPC_S_NO_ENDPOINT_FOUND 0x000006AC +#define RPC_S_INVALID_TIMEOUT 0x000006AD +#define RPC_S_OBJECT_NOT_FOUND 0x000006AE +#define RPC_S_ALREADY_REGISTERED 0x000006AF +#define RPC_S_TYPE_ALREADY_REGISTERED 0x000006B0 +#define RPC_S_ALREADY_LISTENING 0x000006B1 +#define RPC_S_NO_PROTSEQS_REGISTERED 0x000006B2 +#define RPC_S_NOT_LISTENING 0x000006B3 +#define RPC_S_UNKNOWN_MGR_TYPE 0x000006B4 +#define RPC_S_UNKNOWN_IF 0x000006B5 +#define RPC_S_NO_BINDINGS 0x000006B6 +#define RPC_S_NO_PROTSEQS 0x000006B7 +#define RPC_S_CANT_CREATE_ENDPOINT 0x000006B8 +#define RPC_S_OUT_OF_RESOURCES 0x000006B9 +#define RPC_S_SERVER_UNAVAILABLE 0x000006BA +#define RPC_S_SERVER_TOO_BUSY 0x000006BB +#define RPC_S_INVALID_NETWORK_OPTIONS 0x000006BC +#define RPC_S_NO_CALL_ACTIVE 0x000006BD +#define RPC_S_CALL_FAILED 0x000006BE +#define RPC_S_CALL_FAILED_DNE 0x000006BF +#define RPC_S_PROTOCOL_ERROR 0x000006C0 +#define RPC_S_PROXY_ACCESS_DENIED 0x000006C1 +#define RPC_S_UNSUPPORTED_TRANS_SYN 0x000006C2 +#define RPC_S_UNSUPPORTED_TYPE 0x000006C4 +#define RPC_S_INVALID_TAG 0x000006C5 +#define RPC_S_INVALID_BOUND 0x000006C6 +#define RPC_S_NO_ENTRY_NAME 0x000006C7 +#define RPC_S_INVALID_NAME_SYNTAX 0x000006C8 +#define RPC_S_UNSUPPORTED_NAME_SYNTAX 0x000006C9 +#define RPC_S_UUID_NO_ADDRESS 0x000006CB +#define RPC_S_DUPLICATE_ENDPOINT 0x000006CC +#define RPC_S_UNKNOWN_AUTHN_TYPE 0x000006CD +#define RPC_S_MAX_CALLS_TOO_SMALL 0x000006CE +#define RPC_S_STRING_TOO_LONG 0x000006CF +#define RPC_S_PROTSEQ_NOT_FOUND 0x000006D0 +#define RPC_S_PROCNUM_OUT_OF_RANGE 0x000006D1 +#define RPC_S_BINDING_HAS_NO_AUTH 0x000006D2 +#define RPC_S_UNKNOWN_AUTHN_SERVICE 0x000006D3 +#define RPC_S_UNKNOWN_AUTHN_LEVEL 0x000006D4 +#define RPC_S_INVALID_AUTH_IDENTITY 0x000006D5 +#define RPC_S_UNKNOWN_AUTHZ_SERVICE 0x000006D6 +#define EPT_S_INVALID_ENTRY 0x000006D7 +#define EPT_S_CANT_PERFORM_OP 0x000006D8 +#define EPT_S_NOT_REGISTERED 0x000006D9 +#define RPC_S_NOTHING_TO_EXPORT 0x000006DA +#define RPC_S_INCOMPLETE_NAME 0x000006DB +#define RPC_S_INVALID_VERS_OPTION 0x000006DC +#define RPC_S_NO_MORE_MEMBERS 0x000006DD +#define RPC_S_NOT_ALL_OBJS_UNEXPORTED 0x000006DE +#define RPC_S_INTERFACE_NOT_FOUND 0x000006DF +#define RPC_S_ENTRY_ALREADY_EXISTS 0x000006E0 +#define RPC_S_ENTRY_NOT_FOUND 0x000006E1 +#define RPC_S_NAME_SERVICE_UNAVAILABLE 0x000006E2 +#define RPC_S_INVALID_NAF_ID 0x000006E3 +#define RPC_S_CANNOT_SUPPORT 0x000006E4 +#define RPC_S_NO_CONTEXT_AVAILABLE 0x000006E5 +#define RPC_S_INTERNAL_ERROR 0x000006E6 +#define RPC_S_ZERO_DIVIDE 0x000006E7 +#define RPC_S_ADDRESS_ERROR 0x000006E8 +#define RPC_S_FP_DIV_ZERO 0x000006E9 +#define RPC_S_FP_UNDERFLOW 0x000006EA +#define RPC_S_FP_OVERFLOW 0x000006EB +#define RPC_X_NO_MORE_ENTRIES 0x000006EC +#define RPC_X_SS_CHAR_TRANS_OPEN_FAIL 0x000006ED +#define RPC_X_SS_CHAR_TRANS_SHORT_FILE 0x000006EE +#define RPC_X_SS_IN_NULL_CONTEXT 0x000006EF +#define RPC_X_SS_CONTEXT_DAMAGED 0x000006F1 +#define RPC_X_SS_HANDLES_MISMATCH 0x000006F2 +#define RPC_X_SS_CANNOT_GET_CALL_HANDLE 0x000006F3 +#define RPC_X_NULL_REF_POINTER 0x000006F4 +#define RPC_X_ENUM_VALUE_OUT_OF_RANGE 0x000006F5 +#define RPC_X_BYTE_COUNT_TOO_SMALL 0x000006F6 +#define RPC_X_BAD_STUB_DATA 0x000006F7 +#define ERROR_INVALID_USER_BUFFER 0x000006F8 +#define ERROR_UNRECOGNIZED_MEDIA 0x000006F9 +#define ERROR_NO_TRUST_LSA_SECRET 0x000006FA +#define ERROR_NO_TRUST_SAM_ACCOUNT 0x000006FB +#define ERROR_TRUSTED_DOMAIN_FAILURE 0x000006FC +#define ERROR_TRUSTED_RELATIONSHIP_FAILURE 0x000006FD +#define ERROR_TRUST_FAILURE 0x000006FE +#define RPC_S_CALL_IN_PROGRESS 0x000006FF +#define ERROR_NETLOGON_NOT_STARTED 0x00000700 +#define ERROR_ACCOUNT_EXPIRED 0x00000701 +#define ERROR_REDIRECTOR_HAS_OPEN_HANDLES 0x00000702 +#define ERROR_PRINTER_DRIVER_ALREADY_INSTALLED 0x00000703 +#define ERROR_UNKNOWN_PORT 0x00000704 +#define ERROR_UNKNOWN_PRINTER_DRIVER 0x00000705 +#define ERROR_UNKNOWN_PRINTPROCESSOR 0x00000706 +#define ERROR_INVALID_SEPARATOR_FILE 0x00000707 +#define ERROR_INVALID_PRIORITY 0x00000708 +#define ERROR_INVALID_PRINTER_NAME 0x00000709 +#define ERROR_PRINTER_ALREADY_EXISTS 0x0000070A +#define ERROR_INVALID_PRINTER_COMMAND 0x0000070B +#define ERROR_INVALID_DATATYPE 0x0000070C +#define ERROR_INVALID_ENVIRONMENT 0x0000070D +#define RPC_S_NO_MORE_BINDINGS 0x0000070E +#define ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0x0000070F +#define ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0x00000710 +#define ERROR_NOLOGON_SERVER_TRUST_ACCOUNT 0x00000711 +#define ERROR_DOMAIN_TRUST_INCONSISTENT 0x00000712 +#define ERROR_SERVER_HAS_OPEN_HANDLES 0x00000713 +#define ERROR_RESOURCE_DATA_NOT_FOUND 0x00000714 +#define ERROR_RESOURCE_TYPE_NOT_FOUND 0x00000715 +#define ERROR_RESOURCE_NAME_NOT_FOUND 0x00000716 +#define ERROR_RESOURCE_LANG_NOT_FOUND 0x00000717 +#define ERROR_NOT_ENOUGH_QUOTA 0x00000718 +#define RPC_S_NO_INTERFACES 0x00000719 +#define RPC_S_CALL_CANCELLED 0x0000071A +#define RPC_S_BINDING_INCOMPLETE 0x0000071B +#define RPC_S_COMM_FAILURE 0x0000071C +#define RPC_S_UNSUPPORTED_AUTHN_LEVEL 0x0000071D +#define RPC_S_NO_PRINC_NAME 0x0000071E +#define RPC_S_NOT_RPC_ERROR 0x0000071F +#define RPC_S_UUID_LOCAL_ONLY 0x00000720 +#define RPC_S_SEC_PKG_ERROR 0x00000721 +#define RPC_S_NOT_CANCELLED 0x00000722 +#define RPC_X_INVALID_ES_ACTION 0x00000723 +#define RPC_X_WRONG_ES_VERSION 0x00000724 +#define RPC_X_WRONG_STUB_VERSION 0x00000725 +#define RPC_X_INVALID_PIPE_OBJECT 0x00000726 +#define RPC_X_WRONG_PIPE_ORDER 0x00000727 +#define RPC_X_WRONG_PIPE_VERSION 0x00000728 +#define RPC_S_COOKIE_AUTH_FAILED 0x00000729 +#define RPC_S_GROUP_MEMBER_NOT_FOUND 0x0000076A +#define EPT_S_CANT_CREATE 0x0000076B +#define RPC_S_INVALID_OBJECT 0x0000076C +#define ERROR_INVALID_TIME 0x0000076D +#define ERROR_INVALID_FORM_NAME 0x0000076E +#define ERROR_INVALID_FORM_SIZE 0x0000076F +#define ERROR_ALREADY_WAITING 0x00000770 +#define ERROR_PRINTER_DELETED 0x00000771 +#define ERROR_INVALID_PRINTER_STATE 0x00000772 +#define ERROR_PASSWORD_MUST_CHANGE 0x00000773 +#define ERROR_DOMAIN_CONTROLLER_NOT_FOUND 0x00000774 +#define ERROR_ACCOUNT_LOCKED_OUT 0x00000775 +#define OR_INVALID_OXID 0x00000776 +#define OR_INVALID_OID 0x00000777 +#define OR_INVALID_SET 0x00000778 +#define RPC_S_SEND_INCOMPLETE 0x00000779 +#define RPC_S_INVALID_ASYNC_HANDLE 0x0000077A +#define RPC_S_INVALID_ASYNC_CALL 0x0000077B +#define RPC_X_PIPE_CLOSED 0x0000077C +#define RPC_X_PIPE_DISCIPLINE_ERROR 0x0000077D +#define RPC_X_PIPE_EMPTY 0x0000077E +#define ERROR_NO_SITENAME 0x0000077F +#define ERROR_CANT_ACCESS_FILE 0x00000780 +#define ERROR_CANT_RESOLVE_FILENAME 0x00000781 +#define RPC_S_ENTRY_TYPE_MISMATCH 0x00000782 +#define RPC_S_NOT_ALL_OBJS_EXPORTED 0x00000783 +#define RPC_S_INTERFACE_NOT_EXPORTED 0x00000784 +#define RPC_S_PROFILE_NOT_ADDED 0x00000785 +#define RPC_S_PRF_ELT_NOT_ADDED 0x00000786 +#define RPC_S_PRF_ELT_NOT_REMOVED 0x00000787 +#define RPC_S_GRP_ELT_NOT_ADDED 0x00000788 +#define RPC_S_GRP_ELT_NOT_REMOVED 0x00000789 +#define ERROR_KM_DRIVER_BLOCKED 0x0000078A +#define ERROR_CONTEXT_EXPIRED 0x0000078B +#define ERROR_PER_USER_TRUST_QUOTA_EXCEEDED 0x0000078C +#define ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED 0x0000078D +#define ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED 0x0000078E +#define ERROR_AUTHENTICATION_FIREWALL_FAILED 0x0000078F +#define ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED 0x00000790 +#define ERROR_NTLM_BLOCKED 0x00000791 +#define ERROR_PASSWORD_CHANGE_REQUIRED 0x00000792 +#define ERROR_INVALID_PIXEL_FORMAT 0x000007D0 +#define ERROR_BAD_DRIVER 0x000007D1 +#define ERROR_INVALID_WINDOW_STYLE 0x000007D2 +#define ERROR_METAFILE_NOT_SUPPORTED 0x000007D3 +#define ERROR_TRANSFORM_NOT_SUPPORTED 0x000007D4 +#define ERROR_CLIPPING_NOT_SUPPORTED 0x000007D5 +#define ERROR_INVALID_CMM 0x000007DA +#define ERROR_INVALID_PROFILE 0x000007DB +#define ERROR_TAG_NOT_FOUND 0x000007DC +#define ERROR_TAG_NOT_PRESENT 0x000007DD +#define ERROR_DUPLICATE_TAG 0x000007DE +#define ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE 0x000007DF +#define ERROR_PROFILE_NOT_FOUND 0x000007E0 +#define ERROR_INVALID_COLORSPACE 0x000007E1 +#define ERROR_ICM_NOT_ENABLED 0x000007E2 +#define ERROR_DELETING_ICM_XFORM 0x000007E3 +#define ERROR_INVALID_TRANSFORM 0x000007E4 +#define ERROR_COLORSPACE_MISMATCH 0x000007E5 +#define ERROR_INVALID_COLORINDEX 0x000007E6 +#define ERROR_PROFILE_DOES_NOT_MATCH_DEVICE 0x000007E7 +#define ERROR_CONNECTED_OTHER_PASSWORD 0x0000083C +#define ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT 0x0000083D +#define ERROR_BAD_USERNAME 0x0000089A +#define ERROR_NOT_CONNECTED 0x000008CA +#define ERROR_OPEN_FILES 0x00000961 +#define ERROR_ACTIVE_CONNECTIONS 0x00000962 +#define ERROR_DEVICE_IN_USE 0x00000964 +#define ERROR_UNKNOWN_PRINT_MONITOR 0x00000BB8 +#define ERROR_PRINTER_DRIVER_IN_USE 0x00000BB9 +#define ERROR_SPOOL_FILE_NOT_FOUND 0x00000BBA +#define ERROR_SPL_NO_STARTDOC 0x00000BBB +#define ERROR_SPL_NO_ADDJOB 0x00000BBC +#define ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED 0x00000BBD +#define ERROR_PRINT_MONITOR_ALREADY_INSTALLED 0x00000BBE +#define ERROR_INVALID_PRINT_MONITOR 0x00000BBF +#define ERROR_PRINT_MONITOR_IN_USE 0x00000BC0 +#define ERROR_PRINTER_HAS_JOBS_QUEUED 0x00000BC1 +#define ERROR_SUCCESS_REBOOT_REQUIRED 0x00000BC2 +#define ERROR_SUCCESS_RESTART_REQUIRED 0x00000BC3 +#define ERROR_PRINTER_NOT_FOUND 0x00000BC4 +#define ERROR_PRINTER_DRIVER_WARNED 0x00000BC5 +#define ERROR_PRINTER_DRIVER_BLOCKED 0x00000BC6 +#define ERROR_PRINTER_DRIVER_PACKAGE_IN_USE 0x00000BC7 +#define ERROR_CORE_DRIVER_PACKAGE_NOT_FOUND 0x00000BC8 +#define ERROR_FAIL_REBOOT_REQUIRED 0x00000BC9 +#define ERROR_FAIL_REBOOT_INITIATED 0x00000BCA +#define ERROR_PRINTER_DRIVER_DOWNLOAD_NEEDED 0x00000BCB +#define ERROR_PRINT_JOB_RESTART_REQUIRED 0x00000BCC +#define ERROR_INVALID_PRINTER_DRIVER_MANIFEST 0x00000BCD +#define ERROR_PRINTER_NOT_SHAREABLE 0x00000BCE +#define ERROR_REQUEST_PAUSED 0x00000BEA +#define ERROR_IO_REISSUE_AS_CACHED 0x00000F6E + +/* System Error Codes (4000-5999) */ + +#define ERROR_WINS_INTERNAL 0x00000FA0 +#define ERROR_CAN_NOT_DEL_LOCAL_WINS 0x00000FA1 +#define ERROR_STATIC_INIT 0x00000FA2 +#define ERROR_INC_BACKUP 0x00000FA3 +#define ERROR_FULL_BACKUP 0x00000FA4 +#define ERROR_REC_NON_EXISTENT 0x00000FA5 +#define ERROR_RPL_NOT_ALLOWED 0x00000FA6 +#define PEERDIST_ERROR_CONTENTINFO_VERSION_UNSUPPORTED 0x00000FD2 +#define PEERDIST_ERROR_CANNOT_PARSE_CONTENTINFO 0x00000FD3 +#define PEERDIST_ERROR_MISSING_DATA 0x00000FD4 +#define PEERDIST_ERROR_NO_MORE 0x00000FD5 +#define PEERDIST_ERROR_NOT_INITIALIZED 0x00000FD6 +#define PEERDIST_ERROR_ALREADY_INITIALIZED 0x00000FD7 +#define PEERDIST_ERROR_SHUTDOWN_IN_PROGRESS 0x00000FD8 +#define PEERDIST_ERROR_INVALIDATED 0x00000FD9 +#define PEERDIST_ERROR_ALREADY_EXISTS 0x00000FDA +#define PEERDIST_ERROR_OPERATION_NOTFOUND 0x00000FDB +#define PEERDIST_ERROR_ALREADY_COMPLETED 0x00000FDC +#define PEERDIST_ERROR_OUT_OF_BOUNDS 0x00000FDD +#define PEERDIST_ERROR_VERSION_UNSUPPORTED 0x00000FDE +#define PEERDIST_ERROR_INVALID_CONFIGURATION 0x00000FDF +#define PEERDIST_ERROR_NOT_LICENSED 0x00000FE0 +#define PEERDIST_ERROR_SERVICE_UNAVAILABLE 0x00000FE1 +#define PEERDIST_ERROR_TRUST_FAILURE 0x00000FE2 +#define ERROR_DHCP_ADDRESS_CONFLICT 0x00001004 +#define ERROR_WMI_GUID_NOT_FOUND 0x00001068 +#define ERROR_WMI_INSTANCE_NOT_FOUND 0x00001069 +#define ERROR_WMI_ITEMID_NOT_FOUND 0x0000106A +#define ERROR_WMI_TRY_AGAIN 0x0000106B +#define ERROR_WMI_DP_NOT_FOUND 0x0000106C +#define ERROR_WMI_UNRESOLVED_INSTANCE_REF 0x0000106D +#define ERROR_WMI_ALREADY_ENABLED 0x0000106E +#define ERROR_WMI_GUID_DISCONNECTED 0x0000106F +#define ERROR_WMI_SERVER_UNAVAILABLE 0x00001070 +#define ERROR_WMI_DP_FAILED 0x00001071 +#define ERROR_WMI_INVALID_MOF 0x00001072 +#define ERROR_WMI_INVALID_REGINFO 0x00001073 +#define ERROR_WMI_ALREADY_DISABLED 0x00001074 +#define ERROR_WMI_READ_ONLY 0x00001075 +#define ERROR_WMI_SET_FAILURE 0x00001076 +#define ERROR_NOT_APPCONTAINER 0x0000109A +#define ERROR_APPCONTAINER_REQUIRED 0x0000109B +#define ERROR_NOT_SUPPORTED_IN_APPCONTAINER 0x0000109C +#define ERROR_INVALID_PACKAGE_SID_LENGTH 0x0000109D +#define ERROR_INVALID_MEDIA 0x000010CC +#define ERROR_INVALID_LIBRARY 0x000010CD +#define ERROR_INVALID_MEDIA_POOL 0x000010CE +#define ERROR_DRIVE_MEDIA_MISMATCH 0x000010CF +#define ERROR_MEDIA_OFFLINE 0x000010D0 +#define ERROR_LIBRARY_OFFLINE 0x000010D1 +#define ERROR_EMPTY 0x000010D2 +#define ERROR_NOT_EMPTY 0x000010D3 +#define ERROR_MEDIA_UNAVAILABLE 0x000010D4 +#define ERROR_RESOURCE_DISABLED 0x000010D5 +#define ERROR_INVALID_CLEANER 0x000010D6 +#define ERROR_UNABLE_TO_CLEAN 0x000010D7 +#define ERROR_OBJECT_NOT_FOUND 0x000010D8 +#define ERROR_DATABASE_FAILURE 0x000010D9 +#define ERROR_DATABASE_FULL 0x000010DA +#define ERROR_MEDIA_INCOMPATIBLE 0x000010DB +#define ERROR_RESOURCE_NOT_PRESENT 0x000010DC +#define ERROR_INVALID_OPERATION 0x000010DD +#define ERROR_MEDIA_NOT_AVAILABLE 0x000010DE +#define ERROR_DEVICE_NOT_AVAILABLE 0x000010DF +#define ERROR_REQUEST_REFUSED 0x000010E0 +#define ERROR_INVALID_DRIVE_OBJECT 0x000010E1 +#define ERROR_LIBRARY_FULL 0x000010E2 +#define ERROR_MEDIUM_NOT_ACCESSIBLE 0x000010E3 +#define ERROR_UNABLE_TO_LOAD_MEDIUM 0x000010E4 +#define ERROR_UNABLE_TO_INVENTORY_DRIVE 0x000010E5 +#define ERROR_UNABLE_TO_INVENTORY_SLOT 0x000010E6 +#define ERROR_UNABLE_TO_INVENTORY_TRANSPORT 0x000010E7 +#define ERROR_TRANSPORT_FULL 0x000010E8 +#define ERROR_CONTROLLING_IEPORT 0x000010E9 +#define ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA 0x000010EA +#define ERROR_CLEANER_SLOT_SET 0x000010EB +#define ERROR_CLEANER_SLOT_NOT_SET 0x000010EC +#define ERROR_CLEANER_CARTRIDGE_SPENT 0x000010ED +#define ERROR_UNEXPECTED_OMID 0x000010EE +#define ERROR_CANT_DELETE_LAST_ITEM 0x000010EF +#define ERROR_MESSAGE_EXCEEDS_MAX_SIZE 0x000010F0 +#define ERROR_VOLUME_CONTAINS_SYS_FILES 0x000010F1 +#define ERROR_INDIGENOUS_TYPE 0x000010F2 +#define ERROR_NO_SUPPORTING_DRIVES 0x000010F3 +#define ERROR_CLEANER_CARTRIDGE_INSTALLED 0x000010F4 +#define ERROR_IEPORT_FULL 0x000010F5 +#define ERROR_FILE_OFFLINE 0x000010FE +#define ERROR_REMOTE_STORAGE_NOT_ACTIVE 0x000010FF +#define ERROR_REMOTE_STORAGE_MEDIA_ERROR 0x00001100 +#define ERROR_NOT_A_REPARSE_POINT 0x00001126 +#define ERROR_REPARSE_ATTRIBUTE_CONFLICT 0x00001127 +#define ERROR_INVALID_REPARSE_DATA 0x00001128 +#define ERROR_REPARSE_TAG_INVALID 0x00001129 +#define ERROR_REPARSE_TAG_MISMATCH 0x0000112A +#define ERROR_APP_DATA_NOT_FOUND 0x00001130 +#define ERROR_APP_DATA_EXPIRED 0x00001131 +#define ERROR_APP_DATA_CORRUPT 0x00001132 +#define ERROR_APP_DATA_LIMIT_EXCEEDED 0x00001133 +#define ERROR_APP_DATA_REBOOT_REQUIRED 0x00001134 +#define ERROR_SECUREBOOT_ROLLBACK_DETECTED 0x00001144 +#define ERROR_SECUREBOOT_POLICY_VIOLATION 0x00001145 +#define ERROR_SECUREBOOT_INVALID_POLICY 0x00001146 +#define ERROR_SECUREBOOT_POLICY_PUBLISHER_NOT_FOUND 0x00001147 +#define ERROR_SECUREBOOT_POLICY_NOT_SIGNED 0x00001148 +#define ERROR_SECUREBOOT_NOT_ENABLED 0x00001149 +#define ERROR_SECUREBOOT_FILE_REPLACED 0x0000114A +#define ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED 0x00001158 +#define ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED 0x00001159 +#define ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED 0x0000115A +#define ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED 0x0000115B +#define ERROR_VOLUME_NOT_SIS_ENABLED 0x00001194 +#define ERROR_DEPENDENT_RESOURCE_EXISTS 0x00001389 +#define ERROR_DEPENDENCY_NOT_FOUND 0x0000138A +#define ERROR_DEPENDENCY_ALREADY_EXISTS 0x0000138B +#define ERROR_RESOURCE_NOT_ONLINE 0x0000138C +#define ERROR_HOST_NODE_NOT_AVAILABLE 0x0000138D +#define ERROR_RESOURCE_NOT_AVAILABLE 0x0000138E +#define ERROR_RESOURCE_NOT_FOUND 0x0000138F +#define ERROR_SHUTDOWN_CLUSTER 0x00001390 +#define ERROR_CANT_EVICT_ACTIVE_NODE 0x00001391 +#define ERROR_OBJECT_ALREADY_EXISTS 0x00001392 +#define ERROR_OBJECT_IN_LIST 0x00001393 +#define ERROR_GROUP_NOT_AVAILABLE 0x00001394 +#define ERROR_GROUP_NOT_FOUND 0x00001395 +#define ERROR_GROUP_NOT_ONLINE 0x00001396 +#define ERROR_HOST_NODE_NOT_RESOURCE_OWNER 0x00001397 +#define ERROR_HOST_NODE_NOT_GROUP_OWNER 0x00001398 +#define ERROR_RESMON_CREATE_FAILED 0x00001399 +#define ERROR_RESMON_ONLINE_FAILED 0x0000139A +#define ERROR_RESOURCE_ONLINE 0x0000139B +#define ERROR_QUORUM_RESOURCE 0x0000139C +#define ERROR_NOT_QUORUM_CAPABLE 0x0000139D +#define ERROR_CLUSTER_SHUTTING_DOWN 0x0000139E +#define ERROR_INVALID_STATE 0x0000139F +#define ERROR_RESOURCE_PROPERTIES_STORED 0x000013A0 +#define ERROR_NOT_QUORUM_CLASS 0x000013A1 +#define ERROR_CORE_RESOURCE 0x000013A2 +#define ERROR_QUORUM_RESOURCE_ONLINE_FAILED 0x000013A3 +#define ERROR_QUORUMLOG_OPEN_FAILED 0x000013A4 +#define ERROR_CLUSTERLOG_CORRUPT 0x000013A5 +#define ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE 0x000013A6 +#define ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE 0x000013A7 +#define ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND 0x000013A8 +#define ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE 0x000013A9 +#define ERROR_QUORUM_OWNER_ALIVE 0x000013AA +#define ERROR_NETWORK_NOT_AVAILABLE 0x000013AB +#define ERROR_NODE_NOT_AVAILABLE 0x000013AC +#define ERROR_ALL_NODES_NOT_AVAILABLE 0x000013AD +#define ERROR_RESOURCE_FAILED 0x000013AE +#define ERROR_CLUSTER_INVALID_NODE 0x000013AF +#define ERROR_CLUSTER_NODE_EXISTS 0x000013B0 +#define ERROR_CLUSTER_JOIN_IN_PROGRESS 0x000013B1 +#define ERROR_CLUSTER_NODE_NOT_FOUND 0x000013B2 +#define ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND 0x000013B3 +#define ERROR_CLUSTER_NETWORK_EXISTS 0x000013B4 +#define ERROR_CLUSTER_NETWORK_NOT_FOUND 0x000013B5 +#define ERROR_CLUSTER_NETINTERFACE_EXISTS 0x000013B6 +#define ERROR_CLUSTER_NETINTERFACE_NOT_FOUND 0x000013B7 +#define ERROR_CLUSTER_INVALID_REQUEST 0x000013B8 +#define ERROR_CLUSTER_INVALID_NETWORK_PROVIDER 0x000013B9 +#define ERROR_CLUSTER_NODE_DOWN 0x000013BA +#define ERROR_CLUSTER_NODE_UNREACHABLE 0x000013BB +#define ERROR_CLUSTER_NODE_NOT_MEMBER 0x000013BC +#define ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS 0x000013BD +#define ERROR_CLUSTER_INVALID_NETWORK 0x000013BE +#define ERROR_CLUSTER_NODE_UP 0x000013C0 +#define ERROR_CLUSTER_IPADDR_IN_USE 0x000013C1 +#define ERROR_CLUSTER_NODE_NOT_PAUSED 0x000013C2 +#define ERROR_CLUSTER_NO_SECURITY_CONTEXT 0x000013C3 +#define ERROR_CLUSTER_NETWORK_NOT_INTERNAL 0x000013C4 +#define ERROR_CLUSTER_NODE_ALREADY_UP 0x000013C5 +#define ERROR_CLUSTER_NODE_ALREADY_DOWN 0x000013C6 +#define ERROR_CLUSTER_NETWORK_ALREADY_ONLINE 0x000013C7 +#define ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE 0x000013C8 +#define ERROR_CLUSTER_NODE_ALREADY_MEMBER 0x000013C9 +#define ERROR_CLUSTER_LAST_INTERNAL_NETWORK 0x000013CA +#define ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS 0x000013CB +#define ERROR_INVALID_OPERATION_ON_QUORUM 0x000013CC +#define ERROR_DEPENDENCY_NOT_ALLOWED 0x000013CD +#define ERROR_CLUSTER_NODE_PAUSED 0x000013CE +#define ERROR_NODE_CANT_HOST_RESOURCE 0x000013CF +#define ERROR_CLUSTER_NODE_NOT_READY 0x000013D0 +#define ERROR_CLUSTER_NODE_SHUTTING_DOWN 0x000013D1 +#define ERROR_CLUSTER_JOIN_ABORTED 0x000013D2 +#define ERROR_CLUSTER_INCOMPATIBLE_VERSIONS 0x000013D3 +#define ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED 0x000013D4 +#define ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED 0x000013D5 +#define ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND 0x000013D6 +#define ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED 0x000013D7 +#define ERROR_CLUSTER_RESNAME_NOT_FOUND 0x000013D8 +#define ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED 0x000013D9 +#define ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST 0x000013DA +#define ERROR_CLUSTER_DATABASE_SEQMISMATCH 0x000013DB +#define ERROR_RESMON_INVALID_STATE 0x000013DC +#define ERROR_CLUSTER_GUM_NOT_LOCKER 0x000013DD +#define ERROR_QUORUM_DISK_NOT_FOUND 0x000013DE +#define ERROR_DATABASE_BACKUP_CORRUPT 0x000013DF +#define ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT 0x000013E0 +#define ERROR_RESOURCE_PROPERTY_UNCHANGEABLE 0x000013E1 +#define ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE 0x00001702 +#define ERROR_CLUSTER_QUORUMLOG_NOT_FOUND 0x00001703 +#define ERROR_CLUSTER_MEMBERSHIP_HALT 0x00001704 +#define ERROR_CLUSTER_INSTANCE_ID_MISMATCH 0x00001705 +#define ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP 0x00001706 +#define ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH 0x00001707 +#define ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP 0x00001708 +#define ERROR_CLUSTER_PARAMETER_MISMATCH 0x00001709 +#define ERROR_NODE_CANNOT_BE_CLUSTERED 0x0000170A +#define ERROR_CLUSTER_WRONG_OS_VERSION 0x0000170B +#define ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME 0x0000170C +#define ERROR_CLUSCFG_ALREADY_COMMITTED 0x0000170D +#define ERROR_CLUSCFG_ROLLBACK_FAILED 0x0000170E +#define ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT 0x0000170F +#define ERROR_CLUSTER_OLD_VERSION 0x00001710 +#define ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME 0x00001711 +#define ERROR_CLUSTER_NO_NET_ADAPTERS 0x00001712 +#define ERROR_CLUSTER_POISONED 0x00001713 +#define ERROR_CLUSTER_GROUP_MOVING 0x00001714 +#define ERROR_CLUSTER_RESOURCE_TYPE_BUSY 0x00001715 +#define ERROR_RESOURCE_CALL_TIMED_OUT 0x00001716 +#define ERROR_INVALID_CLUSTER_IPV6_ADDRESS 0x00001717 +#define ERROR_CLUSTER_INTERNAL_INVALID_FUNCTION 0x00001718 +#define ERROR_CLUSTER_PARAMETER_OUT_OF_BOUNDS 0x00001719 +#define ERROR_CLUSTER_PARTIAL_SEND 0x0000171A +#define ERROR_CLUSTER_REGISTRY_INVALID_FUNCTION 0x0000171B +#define ERROR_CLUSTER_INVALID_STRING_TERMINATION 0x0000171C +#define ERROR_CLUSTER_INVALID_STRING_FORMAT 0x0000171D +#define ERROR_CLUSTER_DATABASE_TRANSACTION_IN_PROGRESS 0x0000171E +#define ERROR_CLUSTER_DATABASE_TRANSACTION_NOT_IN_PROGRESS 0x0000171F +#define ERROR_CLUSTER_NULL_DATA 0x00001720 +#define ERROR_CLUSTER_PARTIAL_READ 0x00001721 +#define ERROR_CLUSTER_PARTIAL_WRITE 0x00001722 +#define ERROR_CLUSTER_CANT_DESERIALIZE_DATA 0x00001723 +#define ERROR_DEPENDENT_RESOURCE_PROPERTY_CONFLICT 0x00001724 +#define ERROR_CLUSTER_NO_QUORUM 0x00001725 +#define ERROR_CLUSTER_INVALID_IPV6_NETWORK 0x00001726 +#define ERROR_CLUSTER_INVALID_IPV6_TUNNEL_NETWORK 0x00001727 +#define ERROR_QUORUM_NOT_ALLOWED_IN_THIS_GROUP 0x00001728 +#define ERROR_DEPENDENCY_TREE_TOO_COMPLEX 0x00001729 +#define ERROR_EXCEPTION_IN_RESOURCE_CALL 0x0000172A +#define ERROR_CLUSTER_RHS_FAILED_INITIALIZATION 0x0000172B +#define ERROR_CLUSTER_NOT_INSTALLED 0x0000172C +#define ERROR_CLUSTER_RESOURCES_MUST_BE_ONLINE_ON_THE_SAME_NODE 0x0000172D +#define ERROR_CLUSTER_MAX_NODES_IN_CLUSTER 0x0000172E +#define ERROR_CLUSTER_TOO_MANY_NODES 0x0000172F +#define ERROR_CLUSTER_OBJECT_ALREADY_USED 0x00001730 +#define ERROR_NONCORE_GROUPS_FOUND 0x00001731 +#define ERROR_FILE_SHARE_RESOURCE_CONFLICT 0x00001732 +#define ERROR_CLUSTER_EVICT_INVALID_REQUEST 0x00001733 +#define ERROR_CLUSTER_SINGLETON_RESOURCE 0x00001734 +#define ERROR_CLUSTER_GROUP_SINGLETON_RESOURCE 0x00001735 +#define ERROR_CLUSTER_RESOURCE_PROVIDER_FAILED 0x00001736 +#define ERROR_CLUSTER_RESOURCE_CONFIGURATION_ERROR 0x00001737 +#define ERROR_CLUSTER_GROUP_BUSY 0x00001738 +#define ERROR_CLUSTER_NOT_SHARED_VOLUME 0x00001739 +#define ERROR_CLUSTER_INVALID_SECURITY_DESCRIPTOR 0x0000173A +#define ERROR_CLUSTER_SHARED_VOLUMES_IN_USE 0x0000173B +#define ERROR_CLUSTER_USE_SHARED_VOLUMES_API 0x0000173C +#define ERROR_CLUSTER_BACKUP_IN_PROGRESS 0x0000173D +#define ERROR_NON_CSV_PATH 0x0000173E +#define ERROR_CSV_VOLUME_NOT_LOCAL 0x0000173F +#define ERROR_CLUSTER_WATCHDOG_TERMINATING 0x00001740 +#define ERROR_CLUSTER_RESOURCE_VETOED_MOVE_INCOMPATIBLE_NODES 0x00001741 +#define ERROR_CLUSTER_INVALID_NODE_WEIGHT 0x00001742 +#define ERROR_CLUSTER_RESOURCE_VETOED_CALL 0x00001743 +#define ERROR_RESMON_SYSTEM_RESOURCES_LACKING 0x00001744 +#define ERROR_CLUSTER_RESOURCE_VETOED_MOVE_NOT_ENOUGH_RESOURCES_ON_DESTINATION 0x00001745 +#define ERROR_CLUSTER_RESOURCE_VETOED_MOVE_NOT_ENOUGH_RESOURCES_ON_SOURCE 0x00001746 +#define ERROR_CLUSTER_GROUP_QUEUED 0x00001747 +#define ERROR_CLUSTER_RESOURCE_LOCKED_STATUS 0x00001748 +#define ERROR_CLUSTER_SHARED_VOLUME_FAILOVER_NOT_ALLOWED 0x00001749 +#define ERROR_CLUSTER_NODE_DRAIN_IN_PROGRESS 0x0000174A +#define ERROR_CLUSTER_DISK_NOT_CONNECTED 0x0000174B +#define ERROR_DISK_NOT_CSV_CAPABLE 0x0000174C +#define ERROR_RESOURCE_NOT_IN_AVAILABLE_STORAGE 0x0000174D +#define ERROR_CLUSTER_SHARED_VOLUME_REDIRECTED 0x0000174E +#define ERROR_CLUSTER_SHARED_VOLUME_NOT_REDIRECTED 0x0000174F +#define ERROR_CLUSTER_CANNOT_RETURN_PROPERTIES 0x00001750 +#define ERROR_CLUSTER_RESOURCE_CONTAINS_UNSUPPORTED_DIFF_AREA_FOR_SHARED_VOLUMES 0x00001751 +#define ERROR_CLUSTER_RESOURCE_IS_IN_MAINTENANCE_MODE 0x00001752 +#define ERROR_CLUSTER_AFFINITY_CONFLICT 0x00001753 +#define ERROR_CLUSTER_RESOURCE_IS_REPLICA_VIRTUAL_MACHINE 0x00001754 + +/* System Error Codes (6000-8199) */ + +#define ERROR_ENCRYPTION_FAILED 0x00001770 +#define ERROR_DECRYPTION_FAILED 0x00001771 +#define ERROR_FILE_ENCRYPTED 0x00001772 +#define ERROR_NO_RECOVERY_POLICY 0x00001773 +#define ERROR_NO_EFS 0x00001774 +#define ERROR_WRONG_EFS 0x00001775 +#define ERROR_NO_USER_KEYS 0x00001776 +#define ERROR_FILE_NOT_ENCRYPTED 0x00001777 +#define ERROR_NOT_EXPORT_FORMAT 0x00001778 +#define ERROR_FILE_READ_ONLY 0x00001779 +#define ERROR_DIR_EFS_DISALLOWED 0x0000177A +#define ERROR_EFS_SERVER_NOT_TRUSTED 0x0000177B +#define ERROR_BAD_RECOVERY_POLICY 0x0000177C +#define ERROR_EFS_ALG_BLOB_TOO_BIG 0x0000177D +#define ERROR_VOLUME_NOT_SUPPORT_EFS 0x0000177E +#define ERROR_EFS_DISABLED 0x0000177F +#define ERROR_EFS_VERSION_NOT_SUPPORT 0x00001780 +#define ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE 0x00001781 +#define ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER 0x00001782 +#define ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE 0x00001783 +#define ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE 0x00001784 +#define ERROR_CS_ENCRYPTION_FILE_NOT_CSE 0x00001785 +#define ERROR_ENCRYPTION_POLICY_DENIES_OPERATION 0x00001786 +#define ERROR_NO_BROWSER_SERVERS_FOUND 0x000017E6 +#define SCHED_E_SERVICE_NOT_LOCALSYSTEM 0x00001838 +#define ERROR_LOG_SECTOR_INVALID 0x000019C8 +#define ERROR_LOG_SECTOR_PARITY_INVALID 0x000019C9 +#define ERROR_LOG_SECTOR_REMAPPED 0x000019CA +#define ERROR_LOG_BLOCK_INCOMPLETE 0x000019CB +#define ERROR_LOG_INVALID_RANGE 0x000019CC +#define ERROR_LOG_BLOCKS_EXHAUSTED 0x000019CD +#define ERROR_LOG_READ_CONTEXT_INVALID 0x000019CE +#define ERROR_LOG_RESTART_INVALID 0x000019CF +#define ERROR_LOG_BLOCK_VERSION 0x000019D0 +#define ERROR_LOG_BLOCK_INVALID 0x000019D1 +#define ERROR_LOG_READ_MODE_INVALID 0x000019D2 +#define ERROR_LOG_NO_RESTART 0x000019D3 +#define ERROR_LOG_METADATA_CORRUPT 0x000019D4 +#define ERROR_LOG_METADATA_INVALID 0x000019D5 +#define ERROR_LOG_METADATA_INCONSISTENT 0x000019D6 +#define ERROR_LOG_RESERVATION_INVALID 0x000019D7 +#define ERROR_LOG_CANT_DELETE 0x000019D8 +#define ERROR_LOG_CONTAINER_LIMIT_EXCEEDED 0x000019D9 +#define ERROR_LOG_START_OF_LOG 0x000019DA +#define ERROR_LOG_POLICY_ALREADY_INSTALLED 0x000019DB +#define ERROR_LOG_POLICY_NOT_INSTALLED 0x000019DC +#define ERROR_LOG_POLICY_INVALID 0x000019DD +#define ERROR_LOG_POLICY_CONFLICT 0x000019DE +#define ERROR_LOG_PINNED_ARCHIVE_TAIL 0x000019DF +#define ERROR_LOG_RECORD_NONEXISTENT 0x000019E0 +#define ERROR_LOG_RECORDS_RESERVED_INVALID 0x000019E1 +#define ERROR_LOG_SPACE_RESERVED_INVALID 0x000019E2 +#define ERROR_LOG_TAIL_INVALID 0x000019E3 +#define ERROR_LOG_FULL 0x000019E4 +#define ERROR_COULD_NOT_RESIZE_LOG 0x000019E5 +#define ERROR_LOG_MULTIPLEXED 0x000019E6 +#define ERROR_LOG_DEDICATED 0x000019E7 +#define ERROR_LOG_ARCHIVE_NOT_IN_PROGRESS 0x000019E8 +#define ERROR_LOG_ARCHIVE_IN_PROGRESS 0x000019E9 +#define ERROR_LOG_EPHEMERAL 0x000019EA +#define ERROR_LOG_NOT_ENOUGH_CONTAINERS 0x000019EB +#define ERROR_LOG_CLIENT_ALREADY_REGISTERED 0x000019EC +#define ERROR_LOG_CLIENT_NOT_REGISTERED 0x000019ED +#define ERROR_LOG_FULL_HANDLER_IN_PROGRESS 0x000019EE +#define ERROR_LOG_CONTAINER_READ_FAILED 0x000019EF +#define ERROR_LOG_CONTAINER_WRITE_FAILED 0x000019F0 +#define ERROR_LOG_CONTAINER_OPEN_FAILED 0x000019F1 +#define ERROR_LOG_CONTAINER_STATE_INVALID 0x000019F2 +#define ERROR_LOG_STATE_INVALID 0x000019F3 +#define ERROR_LOG_PINNED 0x000019F4 +#define ERROR_LOG_METADATA_FLUSH_FAILED 0x000019F5 +#define ERROR_LOG_INCONSISTENT_SECURITY 0x000019F6 +#define ERROR_LOG_APPENDED_FLUSH_FAILED 0x000019F7 +#define ERROR_LOG_PINNED_RESERVATION 0x000019F8 +#define ERROR_INVALID_TRANSACTION 0x00001A2C +#define ERROR_TRANSACTION_NOT_ACTIVE 0x00001A2D +#define ERROR_TRANSACTION_REQUEST_NOT_VALID 0x00001A2E +#define ERROR_TRANSACTION_NOT_REQUESTED 0x00001A2F +#define ERROR_TRANSACTION_ALREADY_ABORTED 0x00001A30 +#define ERROR_TRANSACTION_ALREADY_COMMITTED 0x00001A31 +#define ERROR_TM_INITIALIZATION_FAILED 0x00001A32 +#define ERROR_RESOURCEMANAGER_READ_ONLY 0x00001A33 +#define ERROR_TRANSACTION_NOT_JOINED 0x00001A34 +#define ERROR_TRANSACTION_SUPERIOR_EXISTS 0x00001A35 +#define ERROR_CRM_PROTOCOL_ALREADY_EXISTS 0x00001A36 +#define ERROR_TRANSACTION_PROPAGATION_FAILED 0x00001A37 +#define ERROR_CRM_PROTOCOL_NOT_FOUND 0x00001A38 +#define ERROR_TRANSACTION_INVALID_MARSHALL_BUFFER 0x00001A39 +#define ERROR_CURRENT_TRANSACTION_NOT_VALID 0x00001A3A +#define ERROR_TRANSACTION_NOT_FOUND 0x00001A3B +#define ERROR_RESOURCEMANAGER_NOT_FOUND 0x00001A3C +#define ERROR_ENLISTMENT_NOT_FOUND 0x00001A3D +#define ERROR_TRANSACTIONMANAGER_NOT_FOUND 0x00001A3E +#define ERROR_TRANSACTIONMANAGER_NOT_ONLINE 0x00001A3F +#define ERROR_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION 0x00001A40 +#define ERROR_TRANSACTION_NOT_ROOT 0x00001A41 +#define ERROR_TRANSACTION_OBJECT_EXPIRED 0x00001A42 +#define ERROR_TRANSACTION_RESPONSE_NOT_ENLISTED 0x00001A43 +#define ERROR_TRANSACTION_RECORD_TOO_LONG 0x00001A44 +#define ERROR_IMPLICIT_TRANSACTION_NOT_SUPPORTED 0x00001A45 +#define ERROR_TRANSACTION_INTEGRITY_VIOLATED 0x00001A46 +#define ERROR_TRANSACTIONMANAGER_IDENTITY_MISMATCH 0x00001A47 +#define ERROR_RM_CANNOT_BE_FROZEN_FOR_SNAPSHOT 0x00001A48 +#define ERROR_TRANSACTION_MUST_WRITETHROUGH 0x00001A49 +#define ERROR_TRANSACTION_NO_SUPERIOR 0x00001A4A +#define ERROR_HEURISTIC_DAMAGE_POSSIBLE 0x00001A4B +#define ERROR_TRANSACTIONAL_CONFLICT 0x00001A90 +#define ERROR_RM_NOT_ACTIVE 0x00001A91 +#define ERROR_RM_METADATA_CORRUPT 0x00001A92 +#define ERROR_DIRECTORY_NOT_RM 0x00001A93 +#define ERROR_TRANSACTIONS_UNSUPPORTED_REMOTE 0x00001A95 +#define ERROR_LOG_RESIZE_INVALID_SIZE 0x00001A96 +#define ERROR_OBJECT_NO_LONGER_EXISTS 0x00001A97 +#define ERROR_STREAM_MINIVERSION_NOT_FOUND 0x00001A98 +#define ERROR_STREAM_MINIVERSION_NOT_VALID 0x00001A99 +#define ERROR_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION 0x00001A9A +#define ERROR_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT 0x00001A9B +#define ERROR_CANT_CREATE_MORE_STREAM_MINIVERSIONS 0x00001A9C +#define ERROR_REMOTE_FILE_VERSION_MISMATCH 0x00001A9E +#define ERROR_HANDLE_NO_LONGER_VALID 0x00001A9F +#define ERROR_NO_TXF_METADATA 0x00001AA0 +#define ERROR_LOG_CORRUPTION_DETECTED 0x00001AA1 +#define ERROR_CANT_RECOVER_WITH_HANDLE_OPEN 0x00001AA2 +#define ERROR_RM_DISCONNECTED 0x00001AA3 +#define ERROR_ENLISTMENT_NOT_SUPERIOR 0x00001AA4 +#define ERROR_RECOVERY_NOT_NEEDED 0x00001AA5 +#define ERROR_RM_ALREADY_STARTED 0x00001AA6 +#define ERROR_FILE_IDENTITY_NOT_PERSISTENT 0x00001AA7 +#define ERROR_CANT_BREAK_TRANSACTIONAL_DEPENDENCY 0x00001AA8 +#define ERROR_CANT_CROSS_RM_BOUNDARY 0x00001AA9 +#define ERROR_TXF_DIR_NOT_EMPTY 0x00001AAA +#define ERROR_INDOUBT_TRANSACTIONS_EXIST 0x00001AAB +#define ERROR_TM_VOLATILE 0x00001AAC +#define ERROR_ROLLBACK_TIMER_EXPIRED 0x00001AAD +#define ERROR_TXF_ATTRIBUTE_CORRUPT 0x00001AAE +#define ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION 0x00001AAF +#define ERROR_TRANSACTIONAL_OPEN_NOT_ALLOWED 0x00001AB0 +#define ERROR_LOG_GROWTH_FAILED 0x00001AB1 +#define ERROR_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE 0x00001AB2 +#define ERROR_TXF_METADATA_ALREADY_PRESENT 0x00001AB3 +#define ERROR_TRANSACTION_SCOPE_CALLBACKS_NOT_SET 0x00001AB4 +#define ERROR_TRANSACTION_REQUIRED_PROMOTION 0x00001AB5 +#define ERROR_CANNOT_EXECUTE_FILE_IN_TRANSACTION 0x00001AB6 +#define ERROR_TRANSACTIONS_NOT_FROZEN 0x00001AB7 +#define ERROR_TRANSACTION_FREEZE_IN_PROGRESS 0x00001AB8 +#define ERROR_NOT_SNAPSHOT_VOLUME 0x00001AB9 +#define ERROR_NO_SAVEPOINT_WITH_OPEN_FILES 0x00001ABA +#define ERROR_DATA_LOST_REPAIR 0x00001ABB +#define ERROR_SPARSE_NOT_ALLOWED_IN_TRANSACTION 0x00001ABC +#define ERROR_TM_IDENTITY_MISMATCH 0x00001ABD +#define ERROR_FLOATED_SECTION 0x00001ABE +#define ERROR_CANNOT_ACCEPT_TRANSACTED_WORK 0x00001ABF +#define ERROR_CANNOT_ABORT_TRANSACTIONS 0x00001AC0 +#define ERROR_BAD_CLUSTERS 0x00001AC1 +#define ERROR_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION 0x00001AC2 +#define ERROR_VOLUME_DIRTY 0x00001AC3 +#define ERROR_NO_LINK_TRACKING_IN_TRANSACTION 0x00001AC4 +#define ERROR_OPERATION_NOT_SUPPORTED_IN_TRANSACTION 0x00001AC5 +#define ERROR_EXPIRED_HANDLE 0x00001AC6 +#define ERROR_TRANSACTION_NOT_ENLISTED 0x00001AC7 +#define ERROR_CTX_WINSTATION_NAME_INVALID 0x00001B59 +#define ERROR_CTX_INVALID_PD 0x00001B5A +#define ERROR_CTX_PD_NOT_FOUND 0x00001B5B +#define ERROR_CTX_WD_NOT_FOUND 0x00001B5C +#define ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY 0x00001B5D +#define ERROR_CTX_SERVICE_NAME_COLLISION 0x00001B5E +#define ERROR_CTX_CLOSE_PENDING 0x00001B5F +#define ERROR_CTX_NO_OUTBUF 0x00001B60 +#define ERROR_CTX_MODEM_INF_NOT_FOUND 0x00001B61 +#define ERROR_CTX_INVALID_MODEMNAME 0x00001B62 +#define ERROR_CTX_MODEM_RESPONSE_ERROR 0x00001B63 +#define ERROR_CTX_MODEM_RESPONSE_TIMEOUT 0x00001B64 +#define ERROR_CTX_MODEM_RESPONSE_NO_CARRIER 0x00001B65 +#define ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE 0x00001B66 +#define ERROR_CTX_MODEM_RESPONSE_BUSY 0x00001B67 +#define ERROR_CTX_MODEM_RESPONSE_VOICE 0x00001B68 +#define ERROR_CTX_TD_ERROR 0x00001B69 +#define ERROR_CTX_WINSTATION_NOT_FOUND 0x00001B6E +#define ERROR_CTX_WINSTATION_ALREADY_EXISTS 0x00001B6F +#define ERROR_CTX_WINSTATION_BUSY 0x00001B70 +#define ERROR_CTX_BAD_VIDEO_MODE 0x00001B71 +#define ERROR_CTX_GRAPHICS_INVALID 0x00001B7B +#define ERROR_CTX_LOGON_DISABLED 0x00001B7D +#define ERROR_CTX_NOT_CONSOLE 0x00001B7E +#define ERROR_CTX_CLIENT_QUERY_TIMEOUT 0x00001B80 +#define ERROR_CTX_CONSOLE_DISCONNECT 0x00001B81 +#define ERROR_CTX_CONSOLE_CONNECT 0x00001B82 +#define ERROR_CTX_SHADOW_DENIED 0x00001B84 +#define ERROR_CTX_WINSTATION_ACCESS_DENIED 0x00001B85 +#define ERROR_CTX_INVALID_WD 0x00001B89 +#define ERROR_CTX_SHADOW_INVALID 0x00001B8A +#define ERROR_CTX_SHADOW_DISABLED 0x00001B8B +#define ERROR_CTX_CLIENT_LICENSE_IN_USE 0x00001B8C +#define ERROR_CTX_CLIENT_LICENSE_NOT_SET 0x00001B8D +#define ERROR_CTX_LICENSE_NOT_AVAILABLE 0x00001B8E +#define ERROR_CTX_LICENSE_CLIENT_INVALID 0x00001B8F +#define ERROR_CTX_LICENSE_EXPIRED 0x00001B90 +#define ERROR_CTX_SHADOW_NOT_RUNNING 0x00001B91 +#define ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE 0x00001B92 +#define ERROR_ACTIVATION_COUNT_EXCEEDED 0x00001B93 +#define ERROR_CTX_WINSTATIONS_DISABLED 0x00001B94 +#define ERROR_CTX_ENCRYPTION_LEVEL_REQUIRED 0x00001B95 +#define ERROR_CTX_SESSION_IN_USE 0x00001B96 +#define ERROR_CTX_NO_FORCE_LOGOFF 0x00001B97 +#define ERROR_CTX_ACCOUNT_RESTRICTION 0x00001B98 +#define ERROR_RDP_PROTOCOL_ERROR 0x00001B99 +#define ERROR_CTX_CDM_CONNECT 0x00001B9A +#define ERROR_CTX_CDM_DISCONNECT 0x00001B9B +#define ERROR_CTX_SECURITY_LAYER_ERROR 0x00001B9C +#define ERROR_TS_INCOMPATIBLE_SESSIONS 0x00001B9D +#define ERROR_TS_VIDEO_SUBSYSTEM_ERROR 0x00001B9E +#define FRS_ERR_INVALID_API_SEQUENCE 0x00001F41 +#define FRS_ERR_STARTING_SERVICE 0x00001F42 +#define FRS_ERR_STOPPING_SERVICE 0x00001F43 +#define FRS_ERR_INTERNAL_API 0x00001F44 +#define FRS_ERR_INTERNAL 0x00001F45 +#define FRS_ERR_SERVICE_COMM 0x00001F46 +#define FRS_ERR_INSUFFICIENT_PRIV 0x00001F47 +#define FRS_ERR_AUTHENTICATION 0x00001F48 +#define FRS_ERR_PARENT_INSUFFICIENT_PRIV 0x00001F49 +#define FRS_ERR_PARENT_AUTHENTICATION 0x00001F4A +#define FRS_ERR_CHILD_TO_PARENT_COMM 0x00001F4B +#define FRS_ERR_PARENT_TO_CHILD_COMM 0x00001F4C +#define FRS_ERR_SYSVOL_POPULATE 0x00001F4D +#define FRS_ERR_SYSVOL_POPULATE_TIMEOUT 0x00001F4E +#define FRS_ERR_SYSVOL_IS_BUSY 0x00001F4F +#define FRS_ERR_SYSVOL_DEMOTE 0x00001F50 +#define FRS_ERR_INVALID_SERVICE_PARAMETER 0x00001F51 + +/* System Error Codes (8200-8999) */ + +#define ERROR_DS_NOT_INSTALLED 0x00002008 +#define ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY 0x00002009 +#define ERROR_DS_NO_ATTRIBUTE_OR_VALUE 0x0000200A +#define ERROR_DS_INVALID_ATTRIBUTE_SYNTAX 0x0000200B +#define ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED 0x0000200C +#define ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS 0x0000200D +#define ERROR_DS_BUSY 0x0000200E +#define ERROR_DS_UNAVAILABLE 0x0000200F +#define ERROR_DS_NO_RIDS_ALLOCATED 0x00002010 +#define ERROR_DS_NO_MORE_RIDS 0x00002011 +#define ERROR_DS_INCORRECT_ROLE_OWNER 0x00002012 +#define ERROR_DS_RIDMGR_INIT_ERROR 0x00002013 +#define ERROR_DS_OBJ_CLASS_VIOLATION 0x00002014 +#define ERROR_DS_CANT_ON_NON_LEAF 0x00002015 +#define ERROR_DS_CANT_ON_RDN 0x00002016 +#define ERROR_DS_CANT_MOD_OBJ_CLASS 0x00002017 +#define ERROR_DS_CROSS_DOM_MOVE_ERROR 0x00002018 +#define ERROR_DS_GC_NOT_AVAILABLE 0x00002019 +#define ERROR_SHARED_POLICY 0x0000201A +#define ERROR_POLICY_OBJECT_NOT_FOUND 0x0000201B +#define ERROR_POLICY_ONLY_IN_DS 0x0000201C +#define ERROR_PROMOTION_ACTIVE 0x0000201D +#define ERROR_NO_PROMOTION_ACTIVE 0x0000201E +#define ERROR_DS_OPERATIONS_ERROR 0x00002020 +#define ERROR_DS_PROTOCOL_ERROR 0x00002021 +#define ERROR_DS_TIMELIMIT_EXCEEDED 0x00002022 +#define ERROR_DS_SIZELIMIT_EXCEEDED 0x00002023 +#define ERROR_DS_ADMIN_LIMIT_EXCEEDED 0x00002024 +#define ERROR_DS_COMPARE_FALSE 0x00002025 +#define ERROR_DS_COMPARE_TRUE 0x00002026 +#define ERROR_DS_AUTH_METHOD_NOT_SUPPORTED 0x00002027 +#define ERROR_DS_STRONG_AUTH_REQUIRED 0x00002028 +#define ERROR_DS_INAPPROPRIATE_AUTH 0x00002029 +#define ERROR_DS_AUTH_UNKNOWN 0x0000202A +#define ERROR_DS_REFERRAL 0x0000202B +#define ERROR_DS_UNAVAILABLE_CRIT_EXTENSION 0x0000202C +#define ERROR_DS_CONFIDENTIALITY_REQUIRED 0x0000202D +#define ERROR_DS_INAPPROPRIATE_MATCHING 0x0000202E +#define ERROR_DS_CONSTRAINT_VIOLATION 0x0000202F +#define ERROR_DS_NO_SUCH_OBJECT 0x00002030 +#define ERROR_DS_ALIAS_PROBLEM 0x00002031 +#define ERROR_DS_INVALID_DN_SYNTAX 0x00002032 +#define ERROR_DS_IS_LEAF 0x00002033 +#define ERROR_DS_ALIAS_DEREF_PROBLEM 0x00002034 +#define ERROR_DS_UNWILLING_TO_PERFORM 0x00002035 +#define ERROR_DS_LOOP_DETECT 0x00002036 +#define ERROR_DS_NAMING_VIOLATION 0x00002037 +#define ERROR_DS_OBJECT_RESULTS_TOO_LARGE 0x00002038 +#define ERROR_DS_AFFECTS_MULTIPLE_DSAS 0x00002039 +#define ERROR_DS_SERVER_DOWN 0x0000203A +#define ERROR_DS_LOCAL_ERROR 0x0000203B +#define ERROR_DS_ENCODING_ERROR 0x0000203C +#define ERROR_DS_DECODING_ERROR 0x0000203D +#define ERROR_DS_FILTER_UNKNOWN 0x0000203E +#define ERROR_DS_PARAM_ERROR 0x0000203F +#define ERROR_DS_NOT_SUPPORTED 0x00002040 +#define ERROR_DS_NO_RESULTS_RETURNED 0x00002041 +#define ERROR_DS_CONTROL_NOT_FOUND 0x00002042 +#define ERROR_DS_CLIENT_LOOP 0x00002043 +#define ERROR_DS_REFERRAL_LIMIT_EXCEEDED 0x00002044 +#define ERROR_DS_SORT_CONTROL_MISSING 0x00002045 +#define ERROR_DS_OFFSET_RANGE_ERROR 0x00002046 +#define ERROR_DS_RIDMGR_DISABLED 0x00002047 +#define ERROR_DS_ROOT_MUST_BE_NC 0x0000206D +#define ERROR_DS_ADD_REPLICA_INHIBITED 0x0000206E +#define ERROR_DS_ATT_NOT_DEF_IN_SCHEMA 0x0000206F +#define ERROR_DS_MAX_OBJ_SIZE_EXCEEDED 0x00002070 +#define ERROR_DS_OBJ_STRING_NAME_EXISTS 0x00002071 +#define ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA 0x00002072 +#define ERROR_DS_RDN_DOESNT_MATCH_SCHEMA 0x00002073 +#define ERROR_DS_NO_REQUESTED_ATTS_FOUND 0x00002074 +#define ERROR_DS_USER_BUFFER_TO_SMALL 0x00002075 +#define ERROR_DS_ATT_IS_NOT_ON_OBJ 0x00002076 +#define ERROR_DS_ILLEGAL_MOD_OPERATION 0x00002077 +#define ERROR_DS_OBJ_TOO_LARGE 0x00002078 +#define ERROR_DS_BAD_INSTANCE_TYPE 0x00002079 +#define ERROR_DS_MASTERDSA_REQUIRED 0x0000207A +#define ERROR_DS_OBJECT_CLASS_REQUIRED 0x0000207B +#define ERROR_DS_MISSING_REQUIRED_ATT 0x0000207C +#define ERROR_DS_ATT_NOT_DEF_FOR_CLASS 0x0000207D +#define ERROR_DS_ATT_ALREADY_EXISTS 0x0000207E +#define ERROR_DS_CANT_ADD_ATT_VALUES 0x00002080 +#define ERROR_DS_SINGLE_VALUE_CONSTRAINT 0x00002081 +#define ERROR_DS_RANGE_CONSTRAINT 0x00002082 +#define ERROR_DS_ATT_VAL_ALREADY_EXISTS 0x00002083 +#define ERROR_DS_CANT_REM_MISSING_ATT 0x00002084 +#define ERROR_DS_CANT_REM_MISSING_ATT_VAL 0x00002085 +#define ERROR_DS_ROOT_CANT_BE_SUBREF 0x00002086 +#define ERROR_DS_NO_CHAINING 0x00002087 +#define ERROR_DS_NO_CHAINED_EVAL 0x00002088 +#define ERROR_DS_NO_PARENT_OBJECT 0x00002089 +#define ERROR_DS_PARENT_IS_AN_ALIAS 0x0000208A +#define ERROR_DS_CANT_MIX_MASTER_AND_REPS 0x0000208B +#define ERROR_DS_CHILDREN_EXIST 0x0000208C +#define ERROR_DS_OBJ_NOT_FOUND 0x0000208D +#define ERROR_DS_ALIASED_OBJ_MISSING 0x0000208E +#define ERROR_DS_BAD_NAME_SYNTAX 0x0000208F +#define ERROR_DS_ALIAS_POINTS_TO_ALIAS 0x00002090 +#define ERROR_DS_CANT_DEREF_ALIAS 0x00002091 +#define ERROR_DS_OUT_OF_SCOPE 0x00002092 +#define ERROR_DS_OBJECT_BEING_REMOVED 0x00002093 +#define ERROR_DS_CANT_DELETE_DSA_OBJ 0x00002094 +#define ERROR_DS_GENERIC_ERROR 0x00002095 +#define ERROR_DS_DSA_MUST_BE_INT_MASTER 0x00002096 +#define ERROR_DS_CLASS_NOT_DSA 0x00002097 +#define ERROR_DS_INSUFF_ACCESS_RIGHTS 0x00002098 +#define ERROR_DS_ILLEGAL_SUPERIOR 0x00002099 +#define ERROR_DS_ATTRIBUTE_OWNED_BY_SAM 0x0000209A +#define ERROR_DS_NAME_TOO_MANY_PARTS 0x0000209B +#define ERROR_DS_NAME_TOO_LONG 0x0000209C +#define ERROR_DS_NAME_VALUE_TOO_LONG 0x0000209D +#define ERROR_DS_NAME_UNPARSEABLE 0x0000209E +#define ERROR_DS_NAME_TYPE_UNKNOWN 0x0000209F +#define ERROR_DS_NOT_AN_OBJECT 0x000020A0 +#define ERROR_DS_SEC_DESC_TOO_SHORT 0x000020A1 +#define ERROR_DS_SEC_DESC_INVALID 0x000020A2 +#define ERROR_DS_NO_DELETED_NAME 0x000020A3 +#define ERROR_DS_SUBREF_MUST_HAVE_PARENT 0x000020A4 +#define ERROR_DS_NCNAME_MUST_BE_NC 0x000020A5 +#define ERROR_DS_CANT_ADD_SYSTEM_ONLY 0x000020A6 +#define ERROR_DS_CLASS_MUST_BE_CONCRETE 0x000020A7 +#define ERROR_DS_INVALID_DMD 0x000020A8 +#define ERROR_DS_OBJ_GUID_EXISTS 0x000020A9 +#define ERROR_DS_NOT_ON_BACKLINK 0x000020AA +#define ERROR_DS_NO_CROSSREF_FOR_NC 0x000020AB +#define ERROR_DS_SHUTTING_DOWN 0x000020AC +#define ERROR_DS_UNKNOWN_OPERATION 0x000020AD +#define ERROR_DS_INVALID_ROLE_OWNER 0x000020AE +#define ERROR_DS_COULDNT_CONTACT_FSMO 0x000020AF +#define ERROR_DS_CROSS_NC_DN_RENAME 0x000020B0 +#define ERROR_DS_CANT_MOD_SYSTEM_ONLY 0x000020B1 +#define ERROR_DS_REPLICATOR_ONLY 0x000020B2 +#define ERROR_DS_OBJ_CLASS_NOT_DEFINED 0x000020B3 +#define ERROR_DS_OBJ_CLASS_NOT_SUBCLASS 0x000020B4 +#define ERROR_DS_NAME_REFERENCE_INVALID 0x000020B5 +#define ERROR_DS_CROSS_REF_EXISTS 0x000020B6 +#define ERROR_DS_CANT_DEL_MASTER_CROSSREF 0x000020B7 +#define ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD 0x000020B8 +#define ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX 0x000020B9 +#define ERROR_DS_DUP_RDN 0x000020BA +#define ERROR_DS_DUP_OID 0x000020BB +#define ERROR_DS_DUP_MAPI_ID 0x000020BC +#define ERROR_DS_DUP_SCHEMA_ID_GUID 0x000020BD +#define ERROR_DS_DUP_LDAP_DISPLAY_NAME 0x000020BE +#define ERROR_DS_SEMANTIC_ATT_TEST 0x000020BF +#define ERROR_DS_SYNTAX_MISMATCH 0x000020C0 +#define ERROR_DS_EXISTS_IN_MUST_HAVE 0x000020C1 +#define ERROR_DS_EXISTS_IN_MAY_HAVE 0x000020C2 +#define ERROR_DS_NONEXISTENT_MAY_HAVE 0x000020C3 +#define ERROR_DS_NONEXISTENT_MUST_HAVE 0x000020C4 +#define ERROR_DS_AUX_CLS_TEST_FAIL 0x000020C5 +#define ERROR_DS_NONEXISTENT_POSS_SUP 0x000020C6 +#define ERROR_DS_SUB_CLS_TEST_FAIL 0x000020C7 +#define ERROR_DS_BAD_RDN_ATT_ID_SYNTAX 0x000020C8 +#define ERROR_DS_EXISTS_IN_AUX_CLS 0x000020C9 +#define ERROR_DS_EXISTS_IN_SUB_CLS 0x000020CA +#define ERROR_DS_EXISTS_IN_POSS_SUP 0x000020CB +#define ERROR_DS_RECALCSCHEMA_FAILED 0x000020CC +#define ERROR_DS_TREE_DELETE_NOT_FINISHED 0x000020CD +#define ERROR_DS_CANT_DELETE 0x000020CE +#define ERROR_DS_ATT_SCHEMA_REQ_ID 0x000020CF +#define ERROR_DS_BAD_ATT_SCHEMA_SYNTAX 0x000020D0 +#define ERROR_DS_CANT_CACHE_ATT 0x000020D1 +#define ERROR_DS_CANT_CACHE_CLASS 0x000020D2 +#define ERROR_DS_CANT_REMOVE_ATT_CACHE 0x000020D3 +#define ERROR_DS_CANT_REMOVE_CLASS_CACHE 0x000020D4 +#define ERROR_DS_CANT_RETRIEVE_DN 0x000020D5 +#define ERROR_DS_MISSING_SUPREF 0x000020D6 +#define ERROR_DS_CANT_RETRIEVE_INSTANCE 0x000020D7 +#define ERROR_DS_CODE_INCONSISTENCY 0x000020D8 +#define ERROR_DS_DATABASE_ERROR 0x000020D9 +#define ERROR_DS_GOVERNSID_MISSING 0x000020DA +#define ERROR_DS_MISSING_EXPECTED_ATT 0x000020DB +#define ERROR_DS_NCNAME_MISSING_CR_REF 0x000020DC +#define ERROR_DS_SECURITY_CHECKING_ERROR 0x000020DD +#define ERROR_DS_SCHEMA_NOT_LOADED 0x000020DE +#define ERROR_DS_SCHEMA_ALLOC_FAILED 0x000020DF +#define ERROR_DS_ATT_SCHEMA_REQ_SYNTAX 0x000020E0 +#define ERROR_DS_GCVERIFY_ERROR 0x000020E1 +#define ERROR_DS_DRA_SCHEMA_MISMATCH 0x000020E2 +#define ERROR_DS_CANT_FIND_DSA_OBJ 0x000020E3 +#define ERROR_DS_CANT_FIND_EXPECTED_NC 0x000020E4 +#define ERROR_DS_CANT_FIND_NC_IN_CACHE 0x000020E5 +#define ERROR_DS_CANT_RETRIEVE_CHILD 0x000020E6 +#define ERROR_DS_SECURITY_ILLEGAL_MODIFY 0x000020E7 +#define ERROR_DS_CANT_REPLACE_HIDDEN_REC 0x000020E8 +#define ERROR_DS_BAD_HIERARCHY_FILE 0x000020E9 +#define ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED 0x000020EA +#define ERROR_DS_CONFIG_PARAM_MISSING 0x000020EB +#define ERROR_DS_COUNTING_AB_INDICES_FAILED 0x000020EC +#define ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED 0x000020ED +#define ERROR_DS_INTERNAL_FAILURE 0x000020EE +#define ERROR_DS_UNKNOWN_ERROR 0x000020EF +#define ERROR_DS_ROOT_REQUIRES_CLASS_TOP 0x000020F0 +#define ERROR_DS_REFUSING_FSMO_ROLES 0x000020F1 +#define ERROR_DS_MISSING_FSMO_SETTINGS 0x000020F2 +#define ERROR_DS_UNABLE_TO_SURRENDER_ROLES 0x000020F3 +#define ERROR_DS_DRA_GENERIC 0x000020F4 +#define ERROR_DS_DRA_INVALID_PARAMETER 0x000020F5 +#define ERROR_DS_DRA_BUSY 0x000020F6 +#define ERROR_DS_DRA_BAD_DN 0x000020F7 +#define ERROR_DS_DRA_BAD_NC 0x000020F8 +#define ERROR_DS_DRA_DN_EXISTS 0x000020F9 +#define ERROR_DS_DRA_INTERNAL_ERROR 0x000020FA +#define ERROR_DS_DRA_INCONSISTENT_DIT 0x000020FB +#define ERROR_DS_DRA_CONNECTION_FAILED 0x000020FC +#define ERROR_DS_DRA_BAD_INSTANCE_TYPE 0x000020FD +#define ERROR_DS_DRA_OUT_OF_MEM 0x000020FE +#define ERROR_DS_DRA_MAIL_PROBLEM 0x000020FF +#define ERROR_DS_DRA_REF_ALREADY_EXISTS 0x00002100 +#define ERROR_DS_DRA_REF_NOT_FOUND 0x00002101 +#define ERROR_DS_DRA_OBJ_IS_REP_SOURCE 0x00002102 +#define ERROR_DS_DRA_DB_ERROR 0x00002103 +#define ERROR_DS_DRA_NO_REPLICA 0x00002104 +#define ERROR_DS_DRA_ACCESS_DENIED 0x00002105 +#define ERROR_DS_DRA_NOT_SUPPORTED 0x00002106 +#define ERROR_DS_DRA_RPC_CANCELLED 0x00002107 +#define ERROR_DS_DRA_SOURCE_DISABLED 0x00002108 +#define ERROR_DS_DRA_SINK_DISABLED 0x00002109 +#define ERROR_DS_DRA_NAME_COLLISION 0x0000210A +#define ERROR_DS_DRA_SOURCE_REINSTALLED 0x0000210B +#define ERROR_DS_DRA_MISSING_PARENT 0x0000210C +#define ERROR_DS_DRA_PREEMPTED 0x0000210D +#define ERROR_DS_DRA_ABANDON_SYNC 0x0000210E +#define ERROR_DS_DRA_SHUTDOWN 0x0000210F +#define ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET 0x00002110 +#define ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA 0x00002111 +#define ERROR_DS_DRA_EXTN_CONNECTION_FAILED 0x00002112 +#define ERROR_DS_INSTALL_SCHEMA_MISMATCH 0x00002113 +#define ERROR_DS_DUP_LINK_ID 0x00002114 +#define ERROR_DS_NAME_ERROR_RESOLVING 0x00002115 +#define ERROR_DS_NAME_ERROR_NOT_FOUND 0x00002116 +#define ERROR_DS_NAME_ERROR_NOT_UNIQUE 0x00002117 +#define ERROR_DS_NAME_ERROR_NO_MAPPING 0x00002118 +#define ERROR_DS_NAME_ERROR_DOMAIN_ONLY 0x00002119 +#define ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING 0x0000211A +#define ERROR_DS_CONSTRUCTED_ATT_MOD 0x0000211B +#define ERROR_DS_WRONG_OM_OBJ_CLASS 0x0000211C +#define ERROR_DS_DRA_REPL_PENDING 0x0000211D +#define ERROR_DS_DS_REQUIRED 0x0000211E +#define ERROR_DS_INVALID_LDAP_DISPLAY_NAME 0x0000211F +#define ERROR_DS_NON_BASE_SEARCH 0x00002120 +#define ERROR_DS_CANT_RETRIEVE_ATTS 0x00002121 +#define ERROR_DS_BACKLINK_WITHOUT_LINK 0x00002122 +#define ERROR_DS_EPOCH_MISMATCH 0x00002123 +#define ERROR_DS_SRC_NAME_MISMATCH 0x00002124 +#define ERROR_DS_SRC_AND_DST_NC_IDENTICAL 0x00002125 +#define ERROR_DS_DST_NC_MISMATCH 0x00002126 +#define ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC 0x00002127 +#define ERROR_DS_SRC_GUID_MISMATCH 0x00002128 +#define ERROR_DS_CANT_MOVE_DELETED_OBJECT 0x00002129 +#define ERROR_DS_PDC_OPERATION_IN_PROGRESS 0x0000212A +#define ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD 0x0000212B +#define ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION 0x0000212C +#define ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS 0x0000212D +#define ERROR_DS_NC_MUST_HAVE_NC_PARENT 0x0000212E +#define ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE 0x0000212F +#define ERROR_DS_DST_DOMAIN_NOT_NATIVE 0x00002130 +#define ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER 0x00002131 +#define ERROR_DS_CANT_MOVE_ACCOUNT_GROUP 0x00002132 +#define ERROR_DS_CANT_MOVE_RESOURCE_GROUP 0x00002133 +#define ERROR_DS_INVALID_SEARCH_FLAG 0x00002134 +#define ERROR_DS_NO_TREE_DELETE_ABOVE_NC 0x00002135 +#define ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE 0x00002136 +#define ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE 0x00002137 +#define ERROR_DS_SAM_INIT_FAILURE 0x00002138 +#define ERROR_DS_SENSITIVE_GROUP_VIOLATION 0x00002139 +#define ERROR_DS_CANT_MOD_PRIMARYGROUPID 0x0000213A +#define ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD 0x0000213B +#define ERROR_DS_NONSAFE_SCHEMA_CHANGE 0x0000213C +#define ERROR_DS_SCHEMA_UPDATE_DISALLOWED 0x0000213D +#define ERROR_DS_CANT_CREATE_UNDER_SCHEMA 0x0000213E +#define ERROR_DS_INSTALL_NO_SRC_SCH_VERSION 0x0000213F +#define ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE 0x00002140 +#define ERROR_DS_INVALID_GROUP_TYPE 0x00002141 +#define ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN 0x00002142 +#define ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN 0x00002143 +#define ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER 0x00002144 +#define ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER 0x00002145 +#define ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER 0x00002146 +#define ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER 0x00002147 +#define ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER 0x00002148 +#define ERROR_DS_HAVE_PRIMARY_MEMBERS 0x00002149 +#define ERROR_DS_STRING_SD_CONVERSION_FAILED 0x0000214A +#define ERROR_DS_NAMING_MASTER_GC 0x0000214B +#define ERROR_DS_DNS_LOOKUP_FAILURE 0x0000214C +#define ERROR_DS_COULDNT_UPDATE_SPNS 0x0000214D +#define ERROR_DS_CANT_RETRIEVE_SD 0x0000214E +#define ERROR_DS_KEY_NOT_UNIQUE 0x0000214F +#define ERROR_DS_WRONG_LINKED_ATT_SYNTAX 0x00002150 +#define ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD 0x00002151 +#define ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY 0x00002152 +#define ERROR_DS_CANT_START 0x00002153 +#define ERROR_DS_INIT_FAILURE 0x00002154 +#define ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION 0x00002155 +#define ERROR_DS_SOURCE_DOMAIN_IN_FOREST 0x00002156 +#define ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST 0x00002157 +#define ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED 0x00002158 +#define ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN 0x00002159 +#define ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER 0x0000215A +#define ERROR_DS_SRC_SID_EXISTS_IN_FOREST 0x0000215B +#define ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH 0x0000215C +#define ERROR_SAM_INIT_FAILURE 0x0000215D +#define ERROR_DS_DRA_SCHEMA_INFO_SHIP 0x0000215E +#define ERROR_DS_DRA_SCHEMA_CONFLICT 0x0000215F +#define ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT 0x00002160 +#define ERROR_DS_DRA_OBJ_NC_MISMATCH 0x00002161 +#define ERROR_DS_NC_STILL_HAS_DSAS 0x00002162 +#define ERROR_DS_GC_REQUIRED 0x00002163 +#define ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY 0x00002164 +#define ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS 0x00002165 +#define ERROR_DS_CANT_ADD_TO_GC 0x00002166 +#define ERROR_DS_NO_CHECKPOINT_WITH_PDC 0x00002167 +#define ERROR_DS_SOURCE_AUDITING_NOT_ENABLED 0x00002168 +#define ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC 0x00002169 +#define ERROR_DS_INVALID_NAME_FOR_SPN 0x0000216A +#define ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS 0x0000216B +#define ERROR_DS_UNICODEPWD_NOT_IN_QUOTES 0x0000216C +#define ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED 0x0000216D +#define ERROR_DS_MUST_BE_RUN_ON_DST_DC 0x0000216E +#define ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER 0x0000216F +#define ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ 0x00002170 +#define ERROR_DS_INIT_FAILURE_CONSOLE 0x00002171 +#define ERROR_DS_SAM_INIT_FAILURE_CONSOLE 0x00002172 +#define ERROR_DS_FOREST_VERSION_TOO_HIGH 0x00002173 +#define ERROR_DS_DOMAIN_VERSION_TOO_HIGH 0x00002174 +#define ERROR_DS_FOREST_VERSION_TOO_LOW 0x00002175 +#define ERROR_DS_DOMAIN_VERSION_TOO_LOW 0x00002176 +#define ERROR_DS_INCOMPATIBLE_VERSION 0x00002177 +#define ERROR_DS_LOW_DSA_VERSION 0x00002178 +#define ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN 0x00002179 +#define ERROR_DS_NOT_SUPPORTED_SORT_ORDER 0x0000217A +#define ERROR_DS_NAME_NOT_UNIQUE 0x0000217B +#define ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 0x0000217C +#define ERROR_DS_OUT_OF_VERSION_STORE 0x0000217D +#define ERROR_DS_INCOMPATIBLE_CONTROLS_USED 0x0000217E +#define ERROR_DS_NO_REF_DOMAIN 0x0000217F +#define ERROR_DS_RESERVED_LINK_ID 0x00002180 +#define ERROR_DS_LINK_ID_NOT_AVAILABLE 0x00002181 +#define ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER 0x00002182 +#define ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE 0x00002183 +#define ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC 0x00002184 +#define ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG 0x00002185 +#define ERROR_DS_MODIFYDN_WRONG_GRANDPARENT 0x00002186 +#define ERROR_DS_NAME_ERROR_TRUST_REFERRAL 0x00002187 +#define ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER 0x00002188 +#define ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD 0x00002189 +#define ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 0x0000218A +#define ERROR_DS_THREAD_LIMIT_EXCEEDED 0x0000218B +#define ERROR_DS_NOT_CLOSEST 0x0000218C +#define ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF 0x0000218D +#define ERROR_DS_SINGLE_USER_MODE_FAILED 0x0000218E +#define ERROR_DS_NTDSCRIPT_SYNTAX_ERROR 0x0000218F +#define ERROR_DS_NTDSCRIPT_PROCESS_ERROR 0x00002190 +#define ERROR_DS_DIFFERENT_REPL_EPOCHS 0x00002191 +#define ERROR_DS_DRS_EXTENSIONS_CHANGED 0x00002192 +#define ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR 0x00002193 +#define ERROR_DS_NO_MSDS_INTID 0x00002194 +#define ERROR_DS_DUP_MSDS_INTID 0x00002195 +#define ERROR_DS_EXISTS_IN_RDNATTID 0x00002196 +#define ERROR_DS_AUTHORIZATION_FAILED 0x00002197 +#define ERROR_DS_INVALID_SCRIPT 0x00002198 +#define ERROR_DS_REMOTE_CROSSREF_OP_FAILED 0x00002199 +#define ERROR_DS_CROSS_REF_BUSY 0x0000219A +#define ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN 0x0000219B +#define ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC 0x0000219C +#define ERROR_DS_DUPLICATE_ID_FOUND 0x0000219D +#define ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT 0x0000219E +#define ERROR_DS_GROUP_CONVERSION_ERROR 0x0000219F +#define ERROR_DS_CANT_MOVE_APP_BASIC_GROUP 0x000021A0 +#define ERROR_DS_CANT_MOVE_APP_QUERY_GROUP 0x000021A1 +#define ERROR_DS_ROLE_NOT_VERIFIED 0x000021A2 +#define ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL 0x000021A3 +#define ERROR_DS_DOMAIN_RENAME_IN_PROGRESS 0x000021A4 +#define ERROR_DS_EXISTING_AD_CHILD_NC 0x000021A5 +#define ERROR_DS_REPL_LIFETIME_EXCEEDED 0x000021A6 +#define ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER 0x000021A7 +#define ERROR_DS_LDAP_SEND_QUEUE_FULL 0x000021A8 +#define ERROR_DS_DRA_OUT_SCHEDULE_WINDOW 0x000021A9 +#define ERROR_DS_POLICY_NOT_KNOWN 0x000021AA +#define ERROR_NO_SITE_SETTINGS_OBJECT 0x000021AB +#define ERROR_NO_SECRETS 0x000021AC +#define ERROR_NO_WRITABLE_DC_FOUND 0x000021AD +#define ERROR_DS_NO_SERVER_OBJECT 0x000021AE +#define ERROR_DS_NO_NTDSA_OBJECT 0x000021AF +#define ERROR_DS_NON_ASQ_SEARCH 0x000021B0 +#define ERROR_DS_AUDIT_FAILURE 0x000021B1 +#define ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE 0x000021B2 +#define ERROR_DS_INVALID_SEARCH_FLAG_TUPLE 0x000021B3 +#define ERROR_DS_HIERARCHY_TABLE_TOO_DEEP 0x000021B4 +#define ERROR_DS_DRA_CORRUPT_UTD_VECTOR 0x000021B5 +#define ERROR_DS_DRA_SECRETS_DENIED 0x000021B6 +#define ERROR_DS_RESERVED_MAPI_ID 0x000021B7 +#define ERROR_DS_MAPI_ID_NOT_AVAILABLE 0x000021B8 +#define ERROR_DS_DRA_MISSING_KRBTGT_SECRET 0x000021B9 +#define ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST 0x000021BA +#define ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST 0x000021BB +#define ERROR_INVALID_USER_PRINCIPAL_NAME 0x000021BC +#define ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS 0x000021BD +#define ERROR_DS_OID_NOT_FOUND 0x000021BE +#define ERROR_DS_DRA_RECYCLED_TARGET 0x000021BF +#define ERROR_DS_DISALLOWED_NC_REDIRECT 0x000021C0 +#define ERROR_DS_HIGH_ADLDS_FFL 0x000021C1 +#define ERROR_DS_HIGH_DSA_VERSION 0x000021C2 +#define ERROR_DS_LOW_ADLDS_FFL 0x000021C3 +#define ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION 0x000021C4 +#define ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED 0x000021C5 +#define ERROR_INCORRECT_ACCOUNT_TYPE 0x000021C6 + +/* System Error Codes (9000-11999) */ + +#define DNS_ERROR_RCODE_FORMAT_ERROR 0x00002329 +#define DNS_ERROR_RCODE_SERVER_FAILURE 0x0000232A +#define DNS_ERROR_RCODE_NAME_ERROR 0x0000232B +#define DNS_ERROR_RCODE_NOT_IMPLEMENTED 0x0000232C +#define DNS_ERROR_RCODE_REFUSED 0x0000232D +#define DNS_ERROR_RCODE_YXDOMAIN 0x0000232E +#define DNS_ERROR_RCODE_YXRRSET 0x0000232F +#define DNS_ERROR_RCODE_NXRRSET 0x00002330 +#define DNS_ERROR_RCODE_NOTAUTH 0x00002331 +#define DNS_ERROR_RCODE_NOTZONE 0x00002332 +#define DNS_ERROR_RCODE_BADSIG 0x00002338 +#define DNS_ERROR_RCODE_BADKEY 0x00002339 +#define DNS_ERROR_RCODE_BADTIME 0x0000233A +#define DNS_ERROR_KEYMASTER_REQUIRED 0x0000238D +#define DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE 0x0000238E +#define DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1 0x0000238F +#define DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS 0x00002390 +#define DNS_ERROR_UNSUPPORTED_ALGORITHM 0x00002391 +#define DNS_ERROR_INVALID_KEY_SIZE 0x00002392 +#define DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE 0x00002393 +#define DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION 0x00002394 +#define DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR 0x00002395 +#define DNS_ERROR_UNEXPECTED_CNG_ERROR 0x00002396 +#define DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION 0x00002397 +#define DNS_ERROR_KSP_NOT_ACCESSIBLE 0x00002398 +#define DNS_ERROR_TOO_MANY_SKDS 0x00002399 +#define DNS_ERROR_INVALID_ROLLOVER_PERIOD 0x0000239A +#define DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET 0x0000239B +#define DNS_ERROR_ROLLOVER_IN_PROGRESS 0x0000239C +#define DNS_ERROR_STANDBY_KEY_NOT_PRESENT 0x0000239D +#define DNS_ERROR_NOT_ALLOWED_ON_ZSK 0x0000239E +#define DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD 0x0000239F +#define DNS_ERROR_ROLLOVER_ALREADY_QUEUED 0x000023A0 +#define DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE 0x000023A1 +#define DNS_ERROR_BAD_KEYMASTER 0x000023A2 +#define DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD 0x000023A3 +#define DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT 0x000023A4 +#define DNS_ERROR_DNSSEC_IS_DISABLED 0x000023A5 +#define DNS_ERROR_INVALID_XML 0x000023A6 +#define DNS_ERROR_NO_VALID_TRUST_ANCHORS 0x000023A7 +#define DNS_ERROR_ROLLOVER_NOT_POKEABLE 0x000023A8 +#define DNS_ERROR_NSEC3_NAME_COLLISION 0x000023A9 +#define DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1 0x000023AA +#define DNS_INFO_NO_RECORDS 0x0000251D +#define DNS_ERROR_BAD_PACKET 0x0000251E +#define DNS_ERROR_NO_PACKET 0x0000251F +#define DNS_ERROR_RCODE 0x00002520 +#define DNS_ERROR_UNSECURE_PACKET 0x00002521 +#define DNS_REQUEST_PENDING 0x00002522 +#define DNS_ERROR_INVALID_TYPE 0x0000254F +#define DNS_ERROR_INVALID_IP_ADDRESS 0x00002550 +#define DNS_ERROR_INVALID_PROPERTY 0x00002551 +#define DNS_ERROR_TRY_AGAIN_LATER 0x00002552 +#define DNS_ERROR_NOT_UNIQUE 0x00002553 +#define DNS_ERROR_NON_RFC_NAME 0x00002554 +#define DNS_STATUS_FQDN 0x00002555 +#define DNS_STATUS_DOTTED_NAME 0x00002556 +#define DNS_STATUS_SINGLE_PART_NAME 0x00002557 +#define DNS_ERROR_INVALID_NAME_CHAR 0x00002558 +#define DNS_ERROR_NUMERIC_NAME 0x00002559 +#define DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER 0x0000255A +#define DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION 0x0000255B +#define DNS_ERROR_CANNOT_FIND_ROOT_HINTS 0x0000255C +#define DNS_ERROR_INCONSISTENT_ROOT_HINTS 0x0000255D +#define DNS_ERROR_DWORD_VALUE_TOO_SMALL 0x0000255E +#define DNS_ERROR_DWORD_VALUE_TOO_LARGE 0x0000255F +#define DNS_ERROR_BACKGROUND_LOADING 0x00002560 +#define DNS_ERROR_NOT_ALLOWED_ON_RODC 0x00002561 +#define DNS_ERROR_NOT_ALLOWED_UNDER_DNAME 0x00002562 +#define DNS_ERROR_DELEGATION_REQUIRED 0x00002563 +#define DNS_ERROR_INVALID_POLICY_TABLE 0x00002564 +#define DNS_ERROR_ZONE_DOES_NOT_EXIST 0x00002581 +#define DNS_ERROR_NO_ZONE_INFO 0x00002582 +#define DNS_ERROR_INVALID_ZONE_OPERATION 0x00002583 +#define DNS_ERROR_ZONE_CONFIGURATION_ERROR 0x00002584 +#define DNS_ERROR_ZONE_HAS_NO_SOA_RECORD 0x00002585 +#define DNS_ERROR_ZONE_HAS_NO_NS_RECORDS 0x00002586 +#define DNS_ERROR_ZONE_LOCKED 0x00002587 +#define DNS_ERROR_ZONE_CREATION_FAILED 0x00002588 +#define DNS_ERROR_ZONE_ALREADY_EXISTS 0x00002589 +#define DNS_ERROR_AUTOZONE_ALREADY_EXISTS 0x0000258A +#define DNS_ERROR_INVALID_ZONE_TYPE 0x0000258B +#define DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP 0x0000258C +#define DNS_ERROR_ZONE_NOT_SECONDARY 0x0000258D +#define DNS_ERROR_NEED_SECONDARY_ADDRESSES 0x0000258E +#define DNS_ERROR_WINS_INIT_FAILED 0x0000258F +#define DNS_ERROR_NEED_WINS_SERVERS 0x00002590 +#define DNS_ERROR_NBSTAT_INIT_FAILED 0x00002591 +#define DNS_ERROR_SOA_DELETE_INVALID 0x00002592 +#define DNS_ERROR_FORWARDER_ALREADY_EXISTS 0x00002593 +#define DNS_ERROR_ZONE_REQUIRES_MASTER_IP 0x00002594 +#define DNS_ERROR_ZONE_IS_SHUTDOWN 0x00002595 +#define DNS_ERROR_ZONE_LOCKED_FOR_SIGNING 0x00002596 +#define DNS_ERROR_PRIMARY_REQUIRES_DATAFILE 0x000025B3 +#define DNS_ERROR_INVALID_DATAFILE_NAME 0x000025B4 +#define DNS_ERROR_DATAFILE_OPEN_FAILURE 0x000025B5 +#define DNS_ERROR_FILE_WRITEBACK_FAILED 0x000025B6 +#define DNS_ERROR_DATAFILE_PARSING 0x000025B7 +#define DNS_ERROR_RECORD_DOES_NOT_EXIST 0x000025E5 +#define DNS_ERROR_RECORD_FORMAT 0x000025E6 +#define DNS_ERROR_NODE_CREATION_FAILED 0x000025E7 +#define DNS_ERROR_UNKNOWN_RECORD_TYPE 0x000025E8 +#define DNS_ERROR_RECORD_TIMED_OUT 0x000025E9 +#define DNS_ERROR_NAME_NOT_IN_ZONE 0x000025EA +#define DNS_ERROR_CNAME_LOOP 0x000025EB +#define DNS_ERROR_NODE_IS_CNAME 0x000025EC +#define DNS_ERROR_CNAME_COLLISION 0x000025ED +#define DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT 0x000025EE +#define DNS_ERROR_RECORD_ALREADY_EXISTS 0x000025EF +#define DNS_ERROR_SECONDARY_DATA 0x000025F0 +#define DNS_ERROR_NO_CREATE_CACHE_DATA 0x000025F1 +#define DNS_ERROR_NAME_DOES_NOT_EXIST 0x000025F2 +#define DNS_WARNING_PTR_CREATE_FAILED 0x000025F3 +#define DNS_WARNING_DOMAIN_UNDELETED 0x000025F4 +#define DNS_ERROR_DS_UNAVAILABLE 0x000025F5 +#define DNS_ERROR_DS_ZONE_ALREADY_EXISTS 0x000025F6 +#define DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE 0x000025F7 +#define DNS_ERROR_NODE_IS_DNAME 0x000025F8 +#define DNS_ERROR_DNAME_COLLISION 0x000025F9 +#define DNS_ERROR_ALIAS_LOOP 0x000025FA +#define DNS_INFO_AXFR_COMPLETE 0x00002617 +#define DNS_ERROR_AXFR 0x00002618 +#define DNS_INFO_ADDED_LOCAL_WINS 0x00002619 +#define DNS_STATUS_CONTINUE_NEEDED 0x00002649 +#define DNS_ERROR_NO_TCPIP 0x0000267B +#define DNS_ERROR_NO_DNS_SERVERS 0x0000267C +#define DNS_ERROR_DP_DOES_NOT_EXIST 0x000026AD +#define DNS_ERROR_DP_ALREADY_EXISTS 0x000026AE +#define DNS_ERROR_DP_NOT_ENLISTED 0x000026AF +#define DNS_ERROR_DP_ALREADY_ENLISTED 0x000026B0 +#define DNS_ERROR_DP_NOT_AVAILABLE 0x000026B1 +#define DNS_ERROR_DP_FSMO_ERROR 0x000026B2 +#define WSAEINTR 0x00002714 +#define WSAEBADF 0x00002719 +#define WSAEACCES 0x0000271D +#define WSAEFAULT 0x0000271E +#define WSAEINVAL 0x00002726 +#define WSAEMFILE 0x00002728 +#define WSAEWOULDBLOCK 0x00002733 +#define WSAEINPROGRESS 0x00002734 +#define WSAEALREADY 0x00002735 +#define WSAENOTSOCK 0x00002736 +#define WSAEDESTADDRREQ 0x00002737 +#define WSAEMSGSIZE 0x00002738 +#define WSAEPROTOTYPE 0x00002739 +#define WSAENOPROTOOPT 0x0000273A +#define WSAEPROTONOSUPPORT 0x0000273B +#define WSAESOCKTNOSUPPORT 0x0000273C +#define WSAEOPNOTSUPP 0x0000273D +#define WSAEPFNOSUPPORT 0x0000273E +#define WSAEAFNOSUPPORT 0x0000273F +#define WSAEADDRINUSE 0x00002740 +#define WSAEADDRNOTAVAIL 0x00002741 +#define WSAENETDOWN 0x00002742 +#define WSAENETUNREACH 0x00002743 +#define WSAENETRESET 0x00002744 +#define WSAECONNABORTED 0x00002745 +#define WSAECONNRESET 0x00002746 +#define WSAENOBUFS 0x00002747 +#define WSAEISCONN 0x00002748 +#define WSAENOTCONN 0x00002749 +#define WSAESHUTDOWN 0x0000274A +#define WSAETOOMANYREFS 0x0000274B +#define WSAETIMEDOUT 0x0000274C +#define WSAECONNREFUSED 0x0000274D +#define WSAELOOP 0x0000274E +#define WSAENAMETOOLONG 0x0000274F +#define WSAEHOSTDOWN 0x00002750 +#define WSAEHOSTUNREACH 0x00002751 +#define WSAENOTEMPTY 0x00002752 +#define WSAEPROCLIM 0x00002753 +#define WSAEUSERS 0x00002754 +#define WSAEDQUOT 0x00002755 +#define WSAESTALE 0x00002756 +#define WSAEREMOTE 0x00002757 +#define WSASYSNOTREADY 0x0000276B +#define WSAVERNOTSUPPORTED 0x0000276C +#define WSANOTINITIALISED 0x0000276D +#define WSAEDISCON 0x00002775 +#define WSAENOMORE 0x00002776 +#define WSAECANCELLED 0x00002777 +#define WSAEINVALIDPROCTABLE 0x00002778 +#define WSAEINVALIDPROVIDER 0x00002779 +#define WSAEPROVIDERFAILEDINIT 0x0000277A +#define WSASYSCALLFAILURE 0x0000277B +#define WSASERVICE_NOT_FOUND 0x0000277C +#define WSATYPE_NOT_FOUND 0x0000277D +#define WSA_E_NO_MORE 0x0000277E +#define WSA_E_CANCELLED 0x0000277F +#define WSAEREFUSED 0x00002780 +#define WSAHOST_NOT_FOUND 0x00002AF9 +#define WSATRY_AGAIN 0x00002AFA +#define WSANO_RECOVERY 0x00002AFB +#define WSANO_DATA 0x00002AFC +#define WSA_QOS_RECEIVERS 0x00002AFD +#define WSA_QOS_SENDERS 0x00002AFE +#define WSA_QOS_NO_SENDERS 0x00002AFF +#define WSA_QOS_NO_RECEIVERS 0x00002B00 +#define WSA_QOS_REQUEST_CONFIRMED 0x00002B01 +#define WSA_QOS_ADMISSION_FAILURE 0x00002B02 +#define WSA_QOS_POLICY_FAILURE 0x00002B03 +#define WSA_QOS_BAD_STYLE 0x00002B04 +#define WSA_QOS_BAD_OBJECT 0x00002B05 +#define WSA_QOS_TRAFFIC_CTRL_ERROR 0x00002B06 +#define WSA_QOS_GENERIC_ERROR 0x00002B07 +#define WSA_QOS_ESERVICETYPE 0x00002B08 +#define WSA_QOS_EFLOWSPEC 0x00002B09 +#define WSA_QOS_EPROVSPECBUF 0x00002B0A +#define WSA_QOS_EFILTERSTYLE 0x00002B0B +#define WSA_QOS_EFILTERTYPE 0x00002B0C +#define WSA_QOS_EFILTERCOUNT 0x00002B0D +#define WSA_QOS_EOBJLENGTH 0x00002B0E +#define WSA_QOS_EFLOWCOUNT 0x00002B0F +#define WSA_QOS_EUNKOWNPSOBJ 0x00002B10 +#define WSA_QOS_EPOLICYOBJ 0x00002B11 +#define WSA_QOS_EFLOWDESC 0x00002B12 +#define WSA_QOS_EPSFLOWSPEC 0x00002B13 +#define WSA_QOS_EPSFILTERSPEC 0x00002B14 +#define WSA_QOS_ESDMODEOBJ 0x00002B15 +#define WSA_QOS_ESHAPERATEOBJ 0x00002B16 +#define WSA_QOS_RESERVED_PETYPE 0x00002B17 +#define WSA_SECURE_HOST_NOT_FOUND 0x00002B18 +#define WSA_IPSEC_NAME_POLICY_ERROR 0x00002B19 + +/* System Error Codes (12000-15999) */ + +/* ERROR_INTERNET_* : (12000 - 12175) defined in WinInet.h */ + +#define ERROR_IPSEC_QM_POLICY_EXISTS 0x000032C8 +#define ERROR_IPSEC_QM_POLICY_NOT_FOUND 0x000032C9 +#define ERROR_IPSEC_QM_POLICY_IN_USE 0x000032CA +#define ERROR_IPSEC_MM_POLICY_EXISTS 0x000032CB +#define ERROR_IPSEC_MM_POLICY_NOT_FOUND 0x000032CC +#define ERROR_IPSEC_MM_POLICY_IN_USE 0x000032CD +#define ERROR_IPSEC_MM_FILTER_EXISTS 0x000032CE +#define ERROR_IPSEC_MM_FILTER_NOT_FOUND 0x000032CF +#define ERROR_IPSEC_TRANSPORT_FILTER_EXISTS 0x000032D0 +#define ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND 0x000032D1 +#define ERROR_IPSEC_MM_AUTH_EXISTS 0x000032D2 +#define ERROR_IPSEC_MM_AUTH_NOT_FOUND 0x000032D3 +#define ERROR_IPSEC_MM_AUTH_IN_USE 0x000032D4 +#define ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND 0x000032D5 +#define ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND 0x000032D6 +#define ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND 0x000032D7 +#define ERROR_IPSEC_TUNNEL_FILTER_EXISTS 0x000032D8 +#define ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND 0x000032D9 +#define ERROR_IPSEC_MM_FILTER_PENDING_DELETION 0x000032DA +#define ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION 0x000032DB +#define ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION 0x000032DC +#define ERROR_IPSEC_MM_POLICY_PENDING_DELETION 0x000032DD +#define ERROR_IPSEC_MM_AUTH_PENDING_DELETION 0x000032DE +#define ERROR_IPSEC_QM_POLICY_PENDING_DELETION 0x000032DF +#define WARNING_IPSEC_MM_POLICY_PRUNED 0x000032E0 +#define WARNING_IPSEC_QM_POLICY_PRUNED 0x000032E1 +#define ERROR_IPSEC_IKE_NEG_STATUS_BEGIN 0x000035E8 +#define ERROR_IPSEC_IKE_AUTH_FAIL 0x000035E9 +#define ERROR_IPSEC_IKE_ATTRIB_FAIL 0x000035EA +#define ERROR_IPSEC_IKE_NEGOTIATION_PENDING 0x000035EB +#define ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR 0x000035EC +#define ERROR_IPSEC_IKE_TIMED_OUT 0x000035ED +#define ERROR_IPSEC_IKE_NO_CERT 0x000035EE +#define ERROR_IPSEC_IKE_SA_DELETED 0x000035EF +#define ERROR_IPSEC_IKE_SA_REAPED 0x000035F0 +#define ERROR_IPSEC_IKE_MM_ACQUIRE_DROP 0x000035F1 +#define ERROR_IPSEC_IKE_QM_ACQUIRE_DROP 0x000035F2 +#define ERROR_IPSEC_IKE_QUEUE_DROP_MM 0x000035F3 +#define ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM 0x000035F4 +#define ERROR_IPSEC_IKE_DROP_NO_RESPONSE 0x000035F5 +#define ERROR_IPSEC_IKE_MM_DELAY_DROP 0x000035F6 +#define ERROR_IPSEC_IKE_QM_DELAY_DROP 0x000035F7 +#define ERROR_IPSEC_IKE_ERROR 0x000035F8 +#define ERROR_IPSEC_IKE_CRL_FAILED 0x000035F9 +#define ERROR_IPSEC_IKE_INVALID_KEY_USAGE 0x000035FA +#define ERROR_IPSEC_IKE_INVALID_CERT_TYPE 0x000035FB +#define ERROR_IPSEC_IKE_NO_PRIVATE_KEY 0x000035FC +#define ERROR_IPSEC_IKE_SIMULTANEOUS_REKEY 0x000035FD +#define ERROR_IPSEC_IKE_DH_FAIL 0x000035FE +#define ERROR_IPSEC_IKE_CRITICAL_PAYLOAD_NOT_RECOGNIZED 0x000035FF +#define ERROR_IPSEC_IKE_INVALID_HEADER 0x00003600 +#define ERROR_IPSEC_IKE_NO_POLICY 0x00003601 +#define ERROR_IPSEC_IKE_INVALID_SIGNATURE 0x00003602 +#define ERROR_IPSEC_IKE_KERBEROS_ERROR 0x00003603 +#define ERROR_IPSEC_IKE_NO_PUBLIC_KEY 0x00003604 +#define ERROR_IPSEC_IKE_PROCESS_ERR 0x00003605 +#define ERROR_IPSEC_IKE_PROCESS_ERR_SA 0x00003606 +#define ERROR_IPSEC_IKE_PROCESS_ERR_PROP 0x00003607 +#define ERROR_IPSEC_IKE_PROCESS_ERR_TRANS 0x00003608 +#define ERROR_IPSEC_IKE_PROCESS_ERR_KE 0x00003609 +#define ERROR_IPSEC_IKE_PROCESS_ERR_ID 0x0000360A +#define ERROR_IPSEC_IKE_PROCESS_ERR_CERT 0x0000360B +#define ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ 0x0000360C +#define ERROR_IPSEC_IKE_PROCESS_ERR_HASH 0x0000360D +#define ERROR_IPSEC_IKE_PROCESS_ERR_SIG 0x0000360E +#define ERROR_IPSEC_IKE_PROCESS_ERR_NONCE 0x0000360F +#define ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY 0x00003610 +#define ERROR_IPSEC_IKE_PROCESS_ERR_DELETE 0x00003611 +#define ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR 0x00003612 +#define ERROR_IPSEC_IKE_INVALID_PAYLOAD 0x00003613 +#define ERROR_IPSEC_IKE_LOAD_SOFT_SA 0x00003614 +#define ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN 0x00003615 +#define ERROR_IPSEC_IKE_INVALID_COOKIE 0x00003616 +#define ERROR_IPSEC_IKE_NO_PEER_CERT 0x00003617 +#define ERROR_IPSEC_IKE_PEER_CRL_FAILED 0x00003618 +#define ERROR_IPSEC_IKE_POLICY_CHANGE 0x00003619 +#define ERROR_IPSEC_IKE_NO_MM_POLICY 0x0000361A +#define ERROR_IPSEC_IKE_NOTCBPRIV 0x0000361B +#define ERROR_IPSEC_IKE_SECLOADFAIL 0x0000361C +#define ERROR_IPSEC_IKE_FAILSSPINIT 0x0000361D +#define ERROR_IPSEC_IKE_FAILQUERYSSP 0x0000361E +#define ERROR_IPSEC_IKE_SRVACQFAIL 0x0000361F +#define ERROR_IPSEC_IKE_SRVQUERYCRED 0x00003620 +#define ERROR_IPSEC_IKE_GETSPIFAIL 0x00003621 +#define ERROR_IPSEC_IKE_INVALID_FILTER 0x00003622 +#define ERROR_IPSEC_IKE_OUT_OF_MEMORY 0x00003623 +#define ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED 0x00003624 +#define ERROR_IPSEC_IKE_INVALID_POLICY 0x00003625 +#define ERROR_IPSEC_IKE_UNKNOWN_DOI 0x00003626 +#define ERROR_IPSEC_IKE_INVALID_SITUATION 0x00003627 +#define ERROR_IPSEC_IKE_DH_FAILURE 0x00003628 +#define ERROR_IPSEC_IKE_INVALID_GROUP 0x00003629 +#define ERROR_IPSEC_IKE_ENCRYPT 0x0000362A +#define ERROR_IPSEC_IKE_DECRYPT 0x0000362B +#define ERROR_IPSEC_IKE_POLICY_MATCH 0x0000362C +#define ERROR_IPSEC_IKE_UNSUPPORTED_ID 0x0000362D +#define ERROR_IPSEC_IKE_INVALID_HASH 0x0000362E +#define ERROR_IPSEC_IKE_INVALID_HASH_ALG 0x0000362F +#define ERROR_IPSEC_IKE_INVALID_HASH_SIZE 0x00003630 +#define ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG 0x00003631 +#define ERROR_IPSEC_IKE_INVALID_AUTH_ALG 0x00003632 +#define ERROR_IPSEC_IKE_INVALID_SIG 0x00003633 +#define ERROR_IPSEC_IKE_LOAD_FAILED 0x00003634 +#define ERROR_IPSEC_IKE_RPC_DELETE 0x00003635 +#define ERROR_IPSEC_IKE_BENIGN_REINIT 0x00003636 +#define ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY 0x00003637 +#define ERROR_IPSEC_IKE_INVALID_MAJOR_VERSION 0x00003638 +#define ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN 0x00003639 +#define ERROR_IPSEC_IKE_MM_LIMIT 0x0000363A +#define ERROR_IPSEC_IKE_NEGOTIATION_DISABLED 0x0000363B +#define ERROR_IPSEC_IKE_QM_LIMIT 0x0000363C +#define ERROR_IPSEC_IKE_MM_EXPIRED 0x0000363D +#define ERROR_IPSEC_IKE_PEER_MM_ASSUMED_INVALID 0x0000363E +#define ERROR_IPSEC_IKE_CERT_CHAIN_POLICY_MISMATCH 0x0000363F +#define ERROR_IPSEC_IKE_UNEXPECTED_MESSAGE_ID 0x00003640 +#define ERROR_IPSEC_IKE_INVALID_AUTH_PAYLOAD 0x00003641 +#define ERROR_IPSEC_IKE_DOS_COOKIE_SENT 0x00003642 +#define ERROR_IPSEC_IKE_SHUTTING_DOWN 0x00003643 +#define ERROR_IPSEC_IKE_CGA_AUTH_FAILED 0x00003644 +#define ERROR_IPSEC_IKE_PROCESS_ERR_NATOA 0x00003645 +#define ERROR_IPSEC_IKE_INVALID_MM_FOR_QM 0x00003646 +#define ERROR_IPSEC_IKE_QM_EXPIRED 0x00003647 +#define ERROR_IPSEC_IKE_TOO_MANY_FILTERS 0x00003648 +#define ERROR_IPSEC_IKE_NEG_STATUS_END 0x00003649 +#define ERROR_IPSEC_IKE_KILL_DUMMY_NAP_TUNNEL 0x0000364A +#define ERROR_IPSEC_IKE_INNER_IP_ASSIGNMENT_FAILURE 0x0000364B +#define ERROR_IPSEC_IKE_REQUIRE_CP_PAYLOAD_MISSING 0x0000364C +#define ERROR_IPSEC_KEY_MODULE_IMPERSONATION_NEGOTIATION_PENDING 0x0000364D +#define ERROR_IPSEC_IKE_COEXISTENCE_SUPPRESS 0x0000364E +#define ERROR_IPSEC_IKE_RATELIMIT_DROP 0x0000364F +#define ERROR_IPSEC_IKE_PEER_DOESNT_SUPPORT_MOBIKE 0x00003650 +#define ERROR_IPSEC_IKE_AUTHORIZATION_FAILURE 0x00003651 +#define ERROR_IPSEC_IKE_STRONG_CRED_AUTHORIZATION_FAILURE 0x00003652 +#define ERROR_IPSEC_IKE_AUTHORIZATION_FAILURE_WITH_OPTIONAL_RETRY 0x00003653 +#define ERROR_IPSEC_IKE_STRONG_CRED_AUTHORIZATION_AND_CERTMAP_FAILURE 0x00003654 +#define ERROR_IPSEC_IKE_NEG_STATUS_EXTENDED_END 0x00003655 +#define ERROR_IPSEC_BAD_SPI 0x00003656 +#define ERROR_IPSEC_SA_LIFETIME_EXPIRED 0x00003657 +#define ERROR_IPSEC_WRONG_SA 0x00003658 +#define ERROR_IPSEC_REPLAY_CHECK_FAILED 0x00003659 +#define ERROR_IPSEC_INVALID_PACKET 0x0000365A +#define ERROR_IPSEC_INTEGRITY_CHECK_FAILED 0x0000365B +#define ERROR_IPSEC_CLEAR_TEXT_DROP 0x0000365C +#define ERROR_IPSEC_AUTH_FIREWALL_DROP 0x0000365D +#define ERROR_IPSEC_THROTTLE_DROP 0x0000365E +#define ERROR_IPSEC_DOSP_BLOCK 0x00003665 +#define ERROR_IPSEC_DOSP_RECEIVED_MULTICAST 0x00003666 +#define ERROR_IPSEC_DOSP_INVALID_PACKET 0x00003667 +#define ERROR_IPSEC_DOSP_STATE_LOOKUP_FAILED 0x00003668 +#define ERROR_IPSEC_DOSP_MAX_ENTRIES 0x00003669 +#define ERROR_IPSEC_DOSP_KEYMOD_NOT_ALLOWED 0x0000366A +#define ERROR_IPSEC_DOSP_NOT_INSTALLED 0x0000366B +#define ERROR_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES 0x0000366C +#define ERROR_SXS_SECTION_NOT_FOUND 0x000036B0 +#define ERROR_SXS_CANT_GEN_ACTCTX 0x000036B1 +#define ERROR_SXS_INVALID_ACTCTXDATA_FORMAT 0x000036B2 +#define ERROR_SXS_ASSEMBLY_NOT_FOUND 0x000036B3 +#define ERROR_SXS_MANIFEST_FORMAT_ERROR 0x000036B4 +#define ERROR_SXS_MANIFEST_PARSE_ERROR 0x000036B5 +#define ERROR_SXS_ACTIVATION_CONTEXT_DISABLED 0x000036B6 +#define ERROR_SXS_KEY_NOT_FOUND 0x000036B7 +#define ERROR_SXS_VERSION_CONFLICT 0x000036B8 +#define ERROR_SXS_WRONG_SECTION_TYPE 0x000036B9 +#define ERROR_SXS_THREAD_QUERIES_DISABLED 0x000036BA +#define ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET 0x000036BB +#define ERROR_SXS_UNKNOWN_ENCODING_GROUP 0x000036BC +#define ERROR_SXS_UNKNOWN_ENCODING 0x000036BD +#define ERROR_SXS_INVALID_XML_NAMESPACE_URI 0x000036BE +#define ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED 0x000036BF +#define ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED 0x000036C0 +#define ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE 0x000036C1 +#define ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE 0x000036C2 +#define ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE 0x000036C3 +#define ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT 0x000036C4 +#define ERROR_SXS_DUPLICATE_DLL_NAME 0x000036C5 +#define ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME 0x000036C6 +#define ERROR_SXS_DUPLICATE_CLSID 0x000036C7 +#define ERROR_SXS_DUPLICATE_IID 0x000036C8 +#define ERROR_SXS_DUPLICATE_TLBID 0x000036C9 +#define ERROR_SXS_DUPLICATE_PROGID 0x000036CA +#define ERROR_SXS_DUPLICATE_ASSEMBLY_NAME 0x000036CB +#define ERROR_SXS_FILE_HASH_MISMATCH 0x000036CC +#define ERROR_SXS_POLICY_PARSE_ERROR 0x000036CD +#define ERROR_SXS_XML_E_MISSINGQUOTE 0x000036CE +#define ERROR_SXS_XML_E_COMMENTSYNTAX 0x000036CF +#define ERROR_SXS_XML_E_BADSTARTNAMECHAR 0x000036D0 +#define ERROR_SXS_XML_E_BADNAMECHAR 0x000036D1 +#define ERROR_SXS_XML_E_BADCHARINSTRING 0x000036D2 +#define ERROR_SXS_XML_E_XMLDECLSYNTAX 0x000036D3 +#define ERROR_SXS_XML_E_BADCHARDATA 0x000036D4 +#define ERROR_SXS_XML_E_MISSINGWHITESPACE 0x000036D5 +#define ERROR_SXS_XML_E_EXPECTINGTAGEND 0x000036D6 +#define ERROR_SXS_XML_E_MISSINGSEMICOLON 0x000036D7 +#define ERROR_SXS_XML_E_UNBALANCEDPAREN 0x000036D8 +#define ERROR_SXS_XML_E_INTERNALERROR 0x000036D9 +#define ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE 0x000036DA +#define ERROR_SXS_XML_E_INCOMPLETE_ENCODING 0x000036DB +#define ERROR_SXS_XML_E_MISSING_PAREN 0x000036DC +#define ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE 0x000036DD +#define ERROR_SXS_XML_E_MULTIPLE_COLONS 0x000036DE +#define ERROR_SXS_XML_E_INVALID_DECIMAL 0x000036DF +#define ERROR_SXS_XML_E_INVALID_HEXIDECIMAL 0x000036E0 +#define ERROR_SXS_XML_E_INVALID_UNICODE 0x000036E1 +#define ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK 0x000036E2 +#define ERROR_SXS_XML_E_UNEXPECTEDENDTAG 0x000036E3 +#define ERROR_SXS_XML_E_UNCLOSEDTAG 0x000036E4 +#define ERROR_SXS_XML_E_DUPLICATEATTRIBUTE 0x000036E5 +#define ERROR_SXS_XML_E_MULTIPLEROOTS 0x000036E6 +#define ERROR_SXS_XML_E_INVALIDATROOTLEVEL 0x000036E7 +#define ERROR_SXS_XML_E_BADXMLDECL 0x000036E8 +#define ERROR_SXS_XML_E_MISSINGROOT 0x000036E9 +#define ERROR_SXS_XML_E_UNEXPECTEDEOF 0x000036EA +#define ERROR_SXS_XML_E_BADPEREFINSUBSET 0x000036EB +#define ERROR_SXS_XML_E_UNCLOSEDSTARTTAG 0x000036EC +#define ERROR_SXS_XML_E_UNCLOSEDENDTAG 0x000036ED +#define ERROR_SXS_XML_E_UNCLOSEDSTRING 0x000036EE +#define ERROR_SXS_XML_E_UNCLOSEDCOMMENT 0x000036EF +#define ERROR_SXS_XML_E_UNCLOSEDDECL 0x000036F0 +#define ERROR_SXS_XML_E_UNCLOSEDCDATA 0x000036F1 +#define ERROR_SXS_XML_E_RESERVEDNAMESPACE 0x000036F2 +#define ERROR_SXS_XML_E_INVALIDENCODING 0x000036F3 +#define ERROR_SXS_XML_E_INVALIDSWITCH 0x000036F4 +#define ERROR_SXS_XML_E_BADXMLCASE 0x000036F5 +#define ERROR_SXS_XML_E_INVALID_STANDALONE 0x000036F6 +#define ERROR_SXS_XML_E_UNEXPECTED_STANDALONE 0x000036F7 +#define ERROR_SXS_XML_E_INVALID_VERSION 0x000036F8 +#define ERROR_SXS_XML_E_MISSINGEQUALS 0x000036F9 +#define ERROR_SXS_PROTECTION_RECOVERY_FAILED 0x000036FA +#define ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT 0x000036FB +#define ERROR_SXS_PROTECTION_CATALOG_NOT_VALID 0x000036FC +#define ERROR_SXS_UNTRANSLATABLE_HRESULT 0x000036FD +#define ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING 0x000036FE +#define ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE 0x000036FF +#define ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME 0x00003700 +#define ERROR_SXS_ASSEMBLY_MISSING 0x00003701 +#define ERROR_SXS_CORRUPT_ACTIVATION_STACK 0x00003702 +#define ERROR_SXS_CORRUPTION 0x00003703 +#define ERROR_SXS_EARLY_DEACTIVATION 0x00003704 +#define ERROR_SXS_INVALID_DEACTIVATION 0x00003705 +#define ERROR_SXS_MULTIPLE_DEACTIVATION 0x00003706 +#define ERROR_SXS_PROCESS_TERMINATION_REQUESTED 0x00003707 +#define ERROR_SXS_RELEASE_ACTIVATION_CONTEXT 0x00003708 +#define ERROR_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY 0x00003709 +#define ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE 0x0000370A +#define ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME 0x0000370B +#define ERROR_SXS_IDENTITY_DUPLICATE_ATTRIBUTE 0x0000370C +#define ERROR_SXS_IDENTITY_PARSE_ERROR 0x0000370D +#define ERROR_MALFORMED_SUBSTITUTION_STRING 0x0000370E +#define ERROR_SXS_INCORRECT_PUBLIC_KEY_TOKEN 0x0000370F +#define ERROR_UNMAPPED_SUBSTITUTION_STRING 0x00003710 +#define ERROR_SXS_ASSEMBLY_NOT_LOCKED 0x00003711 +#define ERROR_SXS_COMPONENT_STORE_CORRUPT 0x00003712 +#define ERROR_ADVANCED_INSTALLER_FAILED 0x00003713 +#define ERROR_XML_ENCODING_MISMATCH 0x00003714 +#define ERROR_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT 0x00003715 +#define ERROR_SXS_IDENTITIES_DIFFERENT 0x00003716 +#define ERROR_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT 0x00003717 +#define ERROR_SXS_FILE_NOT_PART_OF_ASSEMBLY 0x00003718 +#define ERROR_SXS_MANIFEST_TOO_BIG 0x00003719 +#define ERROR_SXS_SETTING_NOT_REGISTERED 0x0000371A +#define ERROR_SXS_TRANSACTION_CLOSURE_INCOMPLETE 0x0000371B +#define ERROR_SMI_PRIMITIVE_INSTALLER_FAILED 0x0000371C +#define ERROR_GENERIC_COMMAND_FAILED 0x0000371D +#define ERROR_SXS_FILE_HASH_MISSING 0x0000371E +#define ERROR_EVT_INVALID_CHANNEL_PATH 0x00003A98 +#define ERROR_EVT_INVALID_QUERY 0x00003A99 +#define ERROR_EVT_PUBLISHER_METADATA_NOT_FOUND 0x00003A9A +#define ERROR_EVT_EVENT_TEMPLATE_NOT_FOUND 0x00003A9B +#define ERROR_EVT_INVALID_PUBLISHER_NAME 0x00003A9C +#define ERROR_EVT_INVALID_EVENT_DATA 0x00003A9D +#define ERROR_EVT_CHANNEL_NOT_FOUND 0x00003A9F +#define ERROR_EVT_MALFORMED_XML_TEXT 0x00003AA0 +#define ERROR_EVT_SUBSCRIPTION_TO_DIRECT_CHANNEL 0x00003AA1 +#define ERROR_EVT_CONFIGURATION_ERROR 0x00003AA2 +#define ERROR_EVT_QUERY_RESULT_STALE 0x00003AA3 +#define ERROR_EVT_QUERY_RESULT_INVALID_POSITION 0x00003AA4 +#define ERROR_EVT_NON_VALIDATING_MSXML 0x00003AA5 +#define ERROR_EVT_FILTER_ALREADYSCOPED 0x00003AA6 +#define ERROR_EVT_FILTER_NOTELTSET 0x00003AA7 +#define ERROR_EVT_FILTER_INVARG 0x00003AA8 +#define ERROR_EVT_FILTER_INVTEST 0x00003AA9 +#define ERROR_EVT_FILTER_INVTYPE 0x00003AAA +#define ERROR_EVT_FILTER_PARSEERR 0x00003AAB +#define ERROR_EVT_FILTER_UNSUPPORTEDOP 0x00003AAC +#define ERROR_EVT_FILTER_UNEXPECTEDTOKEN 0x00003AAD +#define ERROR_EVT_INVALID_OPERATION_OVER_ENABLED_DIRECT_CHANNEL 0x00003AAE +#define ERROR_EVT_INVALID_CHANNEL_PROPERTY_VALUE 0x00003AAF +#define ERROR_EVT_INVALID_PUBLISHER_PROPERTY_VALUE 0x00003AB0 +#define ERROR_EVT_CHANNEL_CANNOT_ACTIVATE 0x00003AB1 +#define ERROR_EVT_FILTER_TOO_COMPLEX 0x00003AB2 +#define ERROR_EVT_MESSAGE_NOT_FOUND 0x00003AB3 +#define ERROR_EVT_MESSAGE_ID_NOT_FOUND 0x00003AB4 +#define ERROR_EVT_UNRESOLVED_VALUE_INSERT 0x00003AB5 +#define ERROR_EVT_UNRESOLVED_PARAMETER_INSERT 0x00003AB6 +#define ERROR_EVT_MAX_INSERTS_REACHED 0x00003AB7 +#define ERROR_EVT_EVENT_DEFINITION_NOT_FOUND 0x00003AB8 +#define ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND 0x00003AB9 +#define ERROR_EVT_VERSION_TOO_OLD 0x00003ABA +#define ERROR_EVT_VERSION_TOO_NEW 0x00003ABB +#define ERROR_EVT_CANNOT_OPEN_CHANNEL_OF_QUERY 0x00003ABC +#define ERROR_EVT_PUBLISHER_DISABLED 0x00003ABD +#define ERROR_EVT_FILTER_OUT_OF_RANGE 0x00003ABE +#define ERROR_EC_SUBSCRIPTION_CANNOT_ACTIVATE 0x00003AE8 +#define ERROR_EC_LOG_DISABLED 0x00003AE9 +#define ERROR_EC_CIRCULAR_FORWARDING 0x00003AEA +#define ERROR_EC_CREDSTORE_FULL 0x00003AEB +#define ERROR_EC_CRED_NOT_FOUND 0x00003AEC +#define ERROR_EC_NO_ACTIVE_CHANNEL 0x00003AED +#define ERROR_MUI_FILE_NOT_FOUND 0x00003AFC +#define ERROR_MUI_INVALID_FILE 0x00003AFD +#define ERROR_MUI_INVALID_RC_CONFIG 0x00003AFE +#define ERROR_MUI_INVALID_LOCALE_NAME 0x00003AFF +#define ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME 0x00003B00 +#define ERROR_MUI_FILE_NOT_LOADED 0x00003B01 +#define ERROR_RESOURCE_ENUM_USER_STOP 0x00003B02 +#define ERROR_MUI_INTLSETTINGS_UILANG_NOT_INSTALLED 0x00003B03 +#define ERROR_MUI_INTLSETTINGS_INVALID_LOCALE_NAME 0x00003B04 +#define ERROR_MRM_RUNTIME_NO_DEFAULT_OR_NEUTRAL_RESOURCE 0x00003B06 +#define ERROR_MRM_INVALID_PRICONFIG 0x00003B07 +#define ERROR_MRM_INVALID_FILE_TYPE 0x00003B08 +#define ERROR_MRM_UNKNOWN_QUALIFIER 0x00003B09 +#define ERROR_MRM_INVALID_QUALIFIER_VALUE 0x00003B0A +#define ERROR_MRM_NO_CANDIDATE 0x00003B0B +#define ERROR_MRM_NO_MATCH_OR_DEFAULT_CANDIDATE 0x00003B0C +#define ERROR_MRM_RESOURCE_TYPE_MISMATCH 0x00003B0D +#define ERROR_MRM_DUPLICATE_MAP_NAME 0x00003B0E +#define ERROR_MRM_DUPLICATE_ENTRY 0x00003B0F +#define ERROR_MRM_INVALID_RESOURCE_IDENTIFIER 0x00003B10 +#define ERROR_MRM_FILEPATH_TOO_LONG 0x00003B11 +#define ERROR_MRM_UNSUPPORTED_DIRECTORY_TYPE 0x00003B12 +#define ERROR_MRM_INVALID_PRI_FILE 0x00003B16 +#define ERROR_MRM_NAMED_RESOURCE_NOT_FOUND 0x00003B17 +#define ERROR_MRM_MAP_NOT_FOUND 0x00003B1F +#define ERROR_MRM_UNSUPPORTED_PROFILE_TYPE 0x00003B20 +#define ERROR_MRM_INVALID_QUALIFIER_OPERATOR 0x00003B21 +#define ERROR_MRM_INDETERMINATE_QUALIFIER_VALUE 0x00003B22 +#define ERROR_MRM_AUTOMERGE_ENABLED 0x00003B23 +#define ERROR_MRM_TOO_MANY_RESOURCES 0x00003B24 +#define ERROR_MCA_INVALID_CAPABILITIES_STRING 0x00003B60 +#define ERROR_MCA_INVALID_VCP_VERSION 0x00003B61 +#define ERROR_MCA_MONITOR_VIOLATES_MCCS_SPECIFICATION 0x00003B62 +#define ERROR_MCA_MCCS_VERSION_MISMATCH 0x00003B63 +#define ERROR_MCA_UNSUPPORTED_MCCS_VERSION 0x00003B64 +#define ERROR_MCA_INTERNAL_ERROR 0x00003B65 +#define ERROR_MCA_INVALID_TECHNOLOGY_TYPE_RETURNED 0x00003B66 +#define ERROR_MCA_UNSUPPORTED_COLOR_TEMPERATURE 0x00003B67 +#define ERROR_AMBIGUOUS_SYSTEM_DEVICE 0x00003B92 +#define ERROR_SYSTEM_DEVICE_NOT_FOUND 0x00003BC3 +#define ERROR_HASH_NOT_SUPPORTED 0x00003BC4 +#define ERROR_HASH_NOT_PRESENT 0x00003BC5 +#define ERROR_SECONDARY_IC_PROVIDER_NOT_REGISTERED 0x00003BD9 +#define ERROR_GPIO_CLIENT_INFORMATION_INVALID 0x00003BDA +#define ERROR_GPIO_VERSION_NOT_SUPPORTED 0x00003BDB +#define ERROR_GPIO_INVALID_REGISTRATION_PACKET 0x00003BDC +#define ERROR_GPIO_OPERATION_DENIED 0x00003BDD +#define ERROR_GPIO_INCOMPATIBLE_CONNECT_MODE 0x00003BDE +#define ERROR_GPIO_INTERRUPT_ALREADY_UNMASKED 0x00003BDF +#define ERROR_CANNOT_SWITCH_RUNLEVEL 0x00003C28 +#define ERROR_INVALID_RUNLEVEL_SETTING 0x00003C29 +#define ERROR_RUNLEVEL_SWITCH_TIMEOUT 0x00003C2A +#define ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT 0x00003C2B +#define ERROR_RUNLEVEL_SWITCH_IN_PROGRESS 0x00003C2C +#define ERROR_SERVICES_FAILED_AUTOSTART 0x00003C2D +#define ERROR_COM_TASK_STOP_PENDING 0x00003C8D +#define ERROR_INSTALL_OPEN_PACKAGE_FAILED 0x00003CF0 +#define ERROR_INSTALL_PACKAGE_NOT_FOUND 0x00003CF1 +#define ERROR_INSTALL_INVALID_PACKAGE 0x00003CF2 +#define ERROR_INSTALL_RESOLVE_DEPENDENCY_FAILED 0x00003CF3 +#define ERROR_INSTALL_OUT_OF_DISK_SPACE 0x00003CF4 +#define ERROR_INSTALL_NETWORK_FAILURE 0x00003CF5 +#define ERROR_INSTALL_REGISTRATION_FAILURE 0x00003CF6 +#define ERROR_INSTALL_DEREGISTRATION_FAILURE 0x00003CF7 +#define ERROR_INSTALL_CANCEL 0x00003CF8 +#define ERROR_INSTALL_FAILED 0x00003CF9 +#define ERROR_REMOVE_FAILED 0x00003CFA +#define ERROR_PACKAGE_ALREADY_EXISTS 0x00003CFB +#define ERROR_NEEDS_REMEDIATION 0x00003CFC +#define ERROR_INSTALL_PREREQUISITE_FAILED 0x00003CFD +#define ERROR_PACKAGE_REPOSITORY_CORRUPTED 0x00003CFE +#define ERROR_INSTALL_POLICY_FAILURE 0x00003CFF +#define ERROR_PACKAGE_UPDATING 0x00003D00 +#define ERROR_DEPLOYMENT_BLOCKED_BY_POLICY 0x00003D01 +#define ERROR_PACKAGES_IN_USE 0x00003D02 +#define ERROR_RECOVERY_FILE_CORRUPT 0x00003D03 +#define ERROR_INVALID_STAGED_SIGNATURE 0x00003D04 +#define ERROR_DELETING_EXISTING_APPLICATIONDATA_STORE_FAILED 0x00003D05 +#define ERROR_INSTALL_PACKAGE_DOWNGRADE 0x00003D06 +#define ERROR_SYSTEM_NEEDS_REMEDIATION 0x00003D07 +#define ERROR_APPX_INTEGRITY_FAILURE_CLR_NGEN 0x00003D08 +#define ERROR_RESILIENCY_FILE_CORRUPT 0x00003D09 +#define ERROR_INSTALL_FIREWALL_SERVICE_NOT_RUNNING 0x00003D0A +#define APPMODEL_ERROR_NO_PACKAGE 0x00003D54 +#define APPMODEL_ERROR_PACKAGE_RUNTIME_CORRUPT 0x00003D55 +#define APPMODEL_ERROR_PACKAGE_IDENTITY_CORRUPT 0x00003D56 +#define APPMODEL_ERROR_NO_APPLICATION 0x00003D57 +#define ERROR_STATE_LOAD_STORE_FAILED 0x00003DB8 +#define ERROR_STATE_GET_VERSION_FAILED 0x00003DB9 +#define ERROR_STATE_SET_VERSION_FAILED 0x00003DBA +#define ERROR_STATE_STRUCTURED_RESET_FAILED 0x00003DBB +#define ERROR_STATE_OPEN_CONTAINER_FAILED 0x00003DBC +#define ERROR_STATE_CREATE_CONTAINER_FAILED 0x00003DBD +#define ERROR_STATE_DELETE_CONTAINER_FAILED 0x00003DBE +#define ERROR_STATE_READ_SETTING_FAILED 0x00003DBF +#define ERROR_STATE_WRITE_SETTING_FAILED 0x00003DC0 +#define ERROR_STATE_DELETE_SETTING_FAILED 0x00003DC1 +#define ERROR_STATE_QUERY_SETTING_FAILED 0x00003DC2 +#define ERROR_STATE_READ_COMPOSITE_SETTING_FAILED 0x00003DC3 +#define ERROR_STATE_WRITE_COMPOSITE_SETTING_FAILED 0x00003DC4 +#define ERROR_STATE_ENUMERATE_CONTAINER_FAILED 0x00003DC5 +#define ERROR_STATE_ENUMERATE_SETTINGS_FAILED 0x00003DC6 +#define ERROR_STATE_COMPOSITE_SETTING_VALUE_SIZE_LIMIT_EXCEEDED 0x00003DC7 +#define ERROR_STATE_SETTING_VALUE_SIZE_LIMIT_EXCEEDED 0x00003DC8 +#define ERROR_STATE_SETTING_NAME_SIZE_LIMIT_EXCEEDED 0x00003DC9 +#define ERROR_STATE_CONTAINER_NAME_SIZE_LIMIT_EXCEEDED 0x00003DCA +#define ERROR_API_UNAVAILABLE 0x00003DE1 + +#ifndef FACILITY_WEBSERVICES +#define FACILITY_WEBSERVICES 61 +#define WS_S_ASYNC 0x003D0000 +#define WS_S_END 0x003D0001 +#define WS_E_INVALID_FORMAT 0x803D0000 +#define WS_E_OBJECT_FAULTED 0x803D0001 +#define WS_E_NUMERIC_OVERFLOW 0x803D0002 +#define WS_E_INVALID_OPERATION 0x803D0003 +#define WS_E_OPERATION_ABORTED 0x803D0004 +#define WS_E_ENDPOINT_ACCESS_DENIED 0x803D0005 +#define WS_E_OPERATION_TIMED_OUT 0x803D0006 +#define WS_E_OPERATION_ABANDONED 0x803D0007 +#define WS_E_QUOTA_EXCEEDED 0x803D0008 +#define WS_E_NO_TRANSLATION_AVAILABLE 0x803D0009 +#define WS_E_SECURITY_VERIFICATION_FAILURE 0x803D000A +#define WS_E_ADDRESS_IN_USE 0x803D000B +#define WS_E_ADDRESS_NOT_AVAILABLE 0x803D000C +#define WS_E_ENDPOINT_NOT_FOUND 0x803D000D +#define WS_E_ENDPOINT_NOT_AVAILABLE 0x803D000E +#define WS_E_ENDPOINT_FAILURE 0x803D000F +#define WS_E_ENDPOINT_UNREACHABLE 0x803D0010 +#define WS_E_ENDPOINT_ACTION_NOT_SUPPORTED 0x803D0011 +#define WS_E_ENDPOINT_TOO_BUSY 0x803D0012 +#define WS_E_ENDPOINT_FAULT_RECEIVED 0x803D0013 +#define WS_E_ENDPOINT_DISCONNECTED 0x803D0014 +#define WS_E_PROXY_FAILURE 0x803D0015 +#define WS_E_PROXY_ACCESS_DENIED 0x803D0016 +#define WS_E_NOT_SUPPORTED 0x803D0017 +#define WS_E_PROXY_REQUIRES_BASIC_AUTH 0x803D0018 +#define WS_E_PROXY_REQUIRES_DIGEST_AUTH 0x803D0019 +#define WS_E_PROXY_REQUIRES_NTLM_AUTH 0x803D001A +#define WS_E_PROXY_REQUIRES_NEGOTIATE_AUTH 0x803D001B +#define WS_E_SERVER_REQUIRES_BASIC_AUTH 0x803D001C +#define WS_E_SERVER_REQUIRES_DIGEST_AUTH 0x803D001D +#define WS_E_SERVER_REQUIRES_NTLM_AUTH 0x803D001E +#define WS_E_SERVER_REQUIRES_NEGOTIATE_AUTH 0x803D001F +#define WS_E_INVALID_ENDPOINT_URL 0x803D0020 +#define WS_E_OTHER 0x803D0021 +#define WS_E_SECURITY_TOKEN_EXPIRED 0x803D0022 +#define WS_E_SECURITY_SYSTEM_FAILURE 0x803D0023 +#endif + +#define NTE_BAD_UID (0x80090001) +#define NTE_BAD_HASH (0x80090002) +#define NTE_BAD_KEY (0x80090003) +#define NTE_BAD_LEN (0x80090004) +#define NTE_BAD_DATA (0x80090005) +#define NTE_BAD_SIGNATURE (0x80090006) +#define NTE_BAD_VER (0x80090007) +#define NTE_BAD_ALGID (0x80090008) +#define NTE_BAD_FLAGS (0x80090009) +#define NTE_BAD_TYPE (0x8009000A) +#define NTE_BAD_KEY_STATE (0x8009000B) +#define NTE_BAD_HASH_STATE (0x8009000C) +#define NTE_NO_KEY (0x8009000D) +#define NTE_NO_MEMORY (0x8009000E) +#define NTE_EXISTS (0x8009000F) +#define NTE_PERM (0x80090010) +#define NTE_NOT_FOUND (0x80090011) +#define NTE_DOUBLE_ENCRYPT (0x80090012) +#define NTE_BAD_PROVIDER (0x80090013) +#define NTE_BAD_PROV_TYPE (0x80090014) +#define NTE_BAD_PUBLIC_KEY (0x80090015) +#define NTE_BAD_KEYSET (0x80090016) +#define NTE_PROV_TYPE_NOT_DEF (0x80090017) +#define NTE_PROV_TYPE_ENTRY_BAD (0x80090018) +#define NTE_KEYSET_NOT_DEF (0x80090019) +#define NTE_KEYSET_ENTRY_BAD (0x8009001A) +#define NTE_PROV_TYPE_NO_MATCH (0x8009001B) +#define NTE_SIGNATURE_FILE_BAD (0x8009001C) +#define NTE_PROVIDER_DLL_FAIL (0x8009001D) +#define NTE_PROV_DLL_NOT_FOUND (0x8009001E) +#define NTE_BAD_KEYSET_PARAM (0x8009001F) +#define NTE_FAIL (0x80090020) +#define NTE_SYS_ERR (0x80090021) +#define NTE_SILENT_CONTEXT (0x80090022) +#define NTE_TOKEN_KEYSET_STORAGE_FULL (0x80090023) +#define NTE_TEMPORARY_PROFILE (0x80090024) +#define NTE_FIXEDPARAMETER (0x80090025) +#define NTE_NO_MORE_ITEMS ERROR_NO_MORE_ITEMS +#define NTE_NOT_SUPPORTED ERROR_NOT_SUPPORTED +#define NTE_INVALID_PARAMETER (0x80090027) + +#define EXCEPTION_MAXIMUM_PARAMETERS 15 + +typedef struct s_EXCEPTION_RECORD EXCEPTION_RECORD; +typedef struct s_EXCEPTION_RECORD* PEXCEPTION_RECORD; + +struct s_EXCEPTION_RECORD +{ + DWORD ExceptionCode; + DWORD ExceptionFlags; + PEXCEPTION_RECORD ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; +}; + +typedef void* PCONTEXT; + +typedef struct s_EXCEPTION_POINTERS +{ + PEXCEPTION_RECORD ExceptionRecord; + PCONTEXT ContextRecord; +} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; + +typedef LONG (*PTOP_LEVEL_EXCEPTION_FILTER)(PEXCEPTION_POINTERS ExceptionInfo); +typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER; + +typedef LONG (*PVECTORED_EXCEPTION_HANDLER)(PEXCEPTION_POINTERS ExceptionInfo); + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API UINT GetErrorMode(void); + + WINPR_API UINT SetErrorMode(UINT uMode); + + WINPR_API DWORD GetLastError(void); + + WINPR_API VOID SetLastError(DWORD dwErrCode); + + WINPR_API VOID RestoreLastError(DWORD dwErrCode); + + WINPR_API VOID RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, + DWORD nNumberOfArguments, CONST ULONG_PTR* lpArguments); + + WINPR_API LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo); + + WINPR_API LPTOP_LEVEL_EXCEPTION_FILTER + SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); + + WINPR_API PVOID AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler); + + WINPR_API ULONG RemoveVectoredExceptionHandler(PVOID Handle); + + WINPR_API PVOID AddVectoredContinueHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler); + + WINPR_API ULONG RemoveVectoredContinueHandler(PVOID Handle); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* WINPR_ERROR_H */ diff --git a/winpr/include/winpr/file.h b/winpr/include/winpr/file.h new file mode 100644 index 0000000..c455d74 --- /dev/null +++ b/winpr/include/winpr/file.h @@ -0,0 +1,550 @@ +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_FILE_H +#define WINPR_FILE_H + +#include +#include + +#include +#include +#include + +#ifndef _WIN32 + +#include + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) +#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF) +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) + +#define FILE_ATTRIBUTE_READONLY 0x00000001u +#define FILE_ATTRIBUTE_HIDDEN 0x00000002u +#define FILE_ATTRIBUTE_SYSTEM 0x00000004u +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010u +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020u +#define FILE_ATTRIBUTE_DEVICE 0x00000040u +#define FILE_ATTRIBUTE_NORMAL 0x00000080u +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100u +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200u +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400u +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800u +#define FILE_ATTRIBUTE_OFFLINE 0x00001000u +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000u +#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000u +#define FILE_ATTRIBUTE_VIRTUAL 0x00010000u + +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 + +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 + +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 + +#define FILE_FLAG_WRITE_THROUGH 0x80000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 +#define FILE_FLAG_POSIX_SEMANTICS 0x01000000 +#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 +#define FILE_FLAG_OPEN_NO_RECALL 0x00100000 +#define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 + +#define PAGE_NOACCESS 0x00000001 +#define PAGE_READONLY 0x00000002 +#define PAGE_READWRITE 0x00000004 +#define PAGE_WRITECOPY 0x00000008 +#define PAGE_EXECUTE 0x00000010 +#define PAGE_EXECUTE_READ 0x00000020 +#define PAGE_EXECUTE_READWRITE 0x00000040 +#define PAGE_EXECUTE_WRITECOPY 0x00000080 +#define PAGE_GUARD 0x00000100 +#define PAGE_NOCACHE 0x00000200 +#define PAGE_WRITECOMBINE 0x00000400 + +#define MEM_COMMIT 0x00001000 +#define MEM_RESERVE 0x00002000 +#define MEM_DECOMMIT 0x00004000 +#define MEM_RELEASE 0x00008000 +#define MEM_FREE 0x00010000 +#define MEM_PRIVATE 0x00020000 +#define MEM_MAPPED 0x00040000 +#define MEM_RESET 0x00080000 +#define MEM_TOP_DOWN 0x00100000 +#define MEM_WRITE_WATCH 0x00200000 +#define MEM_PHYSICAL 0x00400000 +#define MEM_4MB_PAGES 0x80000000 +#define MEM_IMAGE SEC_IMAGE + +#define SEC_NO_CHANGE 0x00400000 +#define SEC_FILE 0x00800000 +#define SEC_IMAGE 0x01000000 +#define SEC_VLM 0x02000000 +#define SEC_RESERVE 0x04000000 +#define SEC_COMMIT 0x08000000 +#define SEC_NOCACHE 0x10000000 +#define SEC_WRITECOMBINE 0x40000000 +#define SEC_LARGE_PAGES 0x80000000 + +#define SECTION_MAP_EXECUTE_EXPLICIT 0x00020 +#define SECTION_EXTEND_SIZE 0x00010 +#define SECTION_MAP_READ 0x00004 +#define SECTION_MAP_WRITE 0x00002 +#define SECTION_QUERY 0x00001 +#define SECTION_MAP_EXECUTE 0x00008 +#define SECTION_ALL_ACCESS 0xF001F + +#define FILE_MAP_COPY SECTION_QUERY +#define FILE_MAP_WRITE SECTION_MAP_WRITE +#define FILE_MAP_READ SECTION_MAP_READ +#define FILE_MAP_ALL_ACCESS SECTION_ALL_ACCESS +#define FILE_MAP_EXECUTE SECTION_MAP_EXECUTE_EXPLICIT + +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 + +#define FIND_FIRST_EX_CASE_SENSITIVE 0x1 +#define FIND_FIRST_EX_LARGE_FETCH 0x2 + +#define STD_INPUT_HANDLE (DWORD) - 10 +#define STD_OUTPUT_HANDLE (DWORD) - 11 +#define STD_ERROR_HANDLE (DWORD) - 12 + +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 + +#define LOCKFILE_FAIL_IMMEDIATELY 1 +#define LOCKFILE_EXCLUSIVE_LOCK 2 + +#define MOVEFILE_REPLACE_EXISTING 0x1 +#define MOVEFILE_COPY_ALLOWED 0x2 +#define MOVEFILE_DELAY_UNTIL_REBOOT 0x4 +#define MOVEFILE_WRITE_THROUGH 0x8 +#define MOVEFILE_CREATE_HARDLINK 0x10 +#define MOVEFILE_FAIL_IF_NOT_TRACKABLE 0x20 + +typedef union +{ + PVOID64 Buffer; + ULONGLONG Alignment; +} FILE_SEGMENT_ELEMENT, *PFILE_SEGMENT_ELEMENT; + +typedef struct +{ + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + CHAR cFileName[MAX_PATH]; + CHAR cAlternateFileName[14]; +} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA; + +typedef struct +{ + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + WCHAR cFileName[MAX_PATH]; + WCHAR cAlternateFileName[14]; +} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW; + +typedef struct +{ + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD dwVolumeSerialNumber; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD nNumberOfLinks; + DWORD nFileIndexHigh; + DWORD nFileIndexLow; +} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION, *LPBY_HANDLE_FILE_INFORMATION; + +typedef enum +{ + FindExInfoStandard, + FindExInfoMaxInfoLevel +} FINDEX_INFO_LEVELS; + +typedef enum +{ + FindExSearchNameMatch, + FindExSearchLimitToDirectories, + FindExSearchLimitToDevices, + FindExSearchMaxSearchOp +} FINDEX_SEARCH_OPS; + +typedef VOID (*LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, + LPOVERLAPPED lpOverlapped); + +#ifdef UNICODE +#define WIN32_FIND_DATA WIN32_FIND_DATAW +#define PWIN32_FIND_DATA PWIN32_FIND_DATAW +#define LPWIN32_FIND_DATA LPWIN32_FIND_DATAW +#else +#define WIN32_FIND_DATA WIN32_FIND_DATAA +#define PWIN32_FIND_DATA PWIN32_FIND_DATAA +#define LPWIN32_FIND_DATA LPWIN32_FIND_DATAA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + + WINPR_API HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + + WINPR_API BOOL DeleteFileA(LPCSTR lpFileName); + + WINPR_API BOOL DeleteFileW(LPCWSTR lpFileName); + + WINPR_API BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + + WINPR_API BOOL ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], + DWORD nNumberOfBytesToRead, LPDWORD lpReserved, + LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + + WINPR_API BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], + DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, + LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL FlushFileBuffers(HANDLE hFile); + + typedef struct + { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + } WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA; + + typedef enum + { + GetFileExInfoStandard, + GetFileExMaxInfoLevel + } GET_FILEEX_INFO_LEVELS; + + WINPR_API BOOL GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFileInformation); + + WINPR_API DWORD GetFileAttributesA(LPCSTR lpFileName); + + WINPR_API BOOL GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFileInformation); + + WINPR_API DWORD GetFileAttributesW(LPCWSTR lpFileName); + + WINPR_API BOOL GetFileInformationByHandle(HANDLE hFile, + LPBY_HANDLE_FILE_INFORMATION lpFileInformation); + + WINPR_API BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes); + + WINPR_API BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes); + + WINPR_API BOOL SetEndOfFile(HANDLE hFile); + + WINPR_API DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh); + + WINPR_API DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod); + + WINPR_API BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, + PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod); + + WINPR_API BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh); + + WINPR_API BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, + LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh); + + WINPR_API BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime, + const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime); + + WINPR_API HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData); + WINPR_API HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData); + + WINPR_API HANDLE FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, + LPVOID lpSearchFilter, DWORD dwAdditionalFlags); + WINPR_API HANDLE FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, + LPVOID lpSearchFilter, DWORD dwAdditionalFlags); + + WINPR_API BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData); + WINPR_API BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData); + + WINPR_API BOOL FindClose(HANDLE hFindFile); + + WINPR_API BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes); + WINPR_API BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes); + + WINPR_API BOOL RemoveDirectoryA(LPCSTR lpPathName); + WINPR_API BOOL RemoveDirectoryW(LPCWSTR lpPathName); + + WINPR_API HANDLE GetStdHandle(DWORD nStdHandle); + WINPR_API BOOL SetStdHandle(DWORD nStdHandle, HANDLE hHandle); + WINPR_API BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOldHandle); + + WINPR_API BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, + LPDWORD lpTotalNumberOfClusters); + + WINPR_API BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, + LPDWORD lpTotalNumberOfClusters); + + WINPR_API BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags); + + WINPR_API BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags); + + WINPR_API BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName); + + WINPR_API BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define CreateFile CreateFileW +#define DeleteFile DeleteFileW +#define FindFirstFile FindFirstFileW +#define FindFirstFileEx FindFirstFileExW +#define FindNextFile FindNextFileW +#define CreateDirectory CreateDirectoryW +#define RemoveDirectory RemoveDirectoryW +#define GetFileAttributesEx GetFileAttributesExW +#define GetFileAttributes GetFileAttributesW +#define SetFileAttributes SetFileAttributesW +#define GetDiskFreeSpace GetDiskFreeSpaceW +#define MoveFileEx MoveFileExW +#define MoveFile MoveFileW +#else +#define CreateFile CreateFileA +#define DeleteFile DeleteFileA +#define FindFirstFile FindFirstFileA +#define FindFirstFileEx FindFirstFileExA +#define FindNextFile FindNextFileA +#define CreateDirectory CreateDirectoryA +#define RemoveDirectory RemoveDirectoryA +#define GetFileAttributesEx GetFileAttributesExA +#define GetFileAttributes GetFileAttributesA +#define SetFileAttributes SetFileAttributesA +#define GetDiskFreeSpace GetDiskFreeSpaceA +#define MoveFileEx MoveFileExA +#define MoveFile MoveFileA +#endif + +/* Extra Functions */ + +typedef BOOL (*pcIsFileHandled)(LPCSTR lpFileName); +typedef HANDLE (*pcCreateFileA)(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + +typedef struct +{ + pcIsFileHandled IsHandled; + pcCreateFileA CreateFileA; +} HANDLE_CREATOR, *PHANDLE_CREATOR, *LPHANDLE_CREATOR; + +#endif /* _WIN32 */ + +WINPR_API BOOL ValidFileNameComponent(LPCWSTR lpFileName); + +#ifdef _UWP + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + + WINPR_API HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile); + + WINPR_API DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh); + + WINPR_API DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod); + + WINPR_API HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData); + WINPR_API HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData); + + WINPR_API DWORD GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, + LPSTR* lpFilePart); + + WINPR_API BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, + LPDWORD lpTotalNumberOfClusters); + + WINPR_API BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, + LPDWORD lpTotalNumberOfClusters); + + WINPR_API DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer); + + WINPR_API DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer); + + WINPR_API BOOL PathIsDirectoryEmptyA(LPCSTR pszPath); + + WINPR_API UINT GetACP(void); + +#ifdef UNICODE +#define CreateFile CreateFileW +#define FindFirstFile FindFirstFileW +#else +#define CreateFile CreateFileA +#define FindFirstFile FindFirstFileA +#endif + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define FindFirstFile FindFirstFileW +#else +#define FindFirstFile FindFirstFileA +#endif + +#endif + +#define WILDCARD_STAR 0x00000001 +#define WILDCARD_QM 0x00000002 +#define WILDCARD_DOS 0x00000100 +#define WILDCARD_DOS_STAR 0x00000110 +#define WILDCARD_DOS_QM 0x00000120 +#define WILDCARD_DOS_DOT 0x00000140 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern); + WINPR_API LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags); + + WINPR_API int UnixChangeFileMode(const char* filename, int flags); + + WINPR_API BOOL IsNamedPipeFileNameA(LPCSTR lpName); + WINPR_API char* GetNamedPipeNameWithoutPrefixA(LPCSTR lpName); + WINPR_API char* GetNamedPipeUnixDomainSocketBaseFilePathA(void); + WINPR_API char* GetNamedPipeUnixDomainSocketFilePathA(LPCSTR lpName); + + WINPR_API int GetNamePipeFileDescriptor(HANDLE hNamedPipe); + WINPR_API HANDLE GetFileHandleForFileDescriptor(int fd); + + WINPR_API FILE* winpr_fopen(const char* path, const char* mode); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_FILE_H */ diff --git a/winpr/include/winpr/handle.h b/winpr/include/winpr/handle.h new file mode 100644 index 0000000..ca2b4b7 --- /dev/null +++ b/winpr/include/winpr/handle.h @@ -0,0 +1,64 @@ +/** + * WinPR: Windows Portable Runtime + * Handle Management + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_HANDLE_H +#define WINPR_HANDLE_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define WINPR_FD_READ_BIT 0 +#define WINPR_FD_READ (1 << WINPR_FD_READ_BIT) + +#define WINPR_FD_WRITE_BIT 1 +#define WINPR_FD_WRITE (1 << WINPR_FD_WRITE_BIT) + +#ifndef _WIN32 + +#define DUPLICATE_CLOSE_SOURCE 0x00000001 +#define DUPLICATE_SAME_ACCESS 0x00000002 + +#define HANDLE_FLAG_INHERIT 0x00000001 +#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002 + + WINPR_API BOOL CloseHandle(HANDLE hObject); + + WINPR_API BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, + HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, + DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); + + WINPR_API BOOL GetHandleInformation(HANDLE hObject, LPDWORD lpdwFlags); + WINPR_API BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_HANDLE_H */ diff --git a/winpr/include/winpr/image.h b/winpr/include/winpr/image.h new file mode 100644 index 0000000..cf33c5f --- /dev/null +++ b/winpr/include/winpr/image.h @@ -0,0 +1,121 @@ +/** + * WinPR: Windows Portable Runtime + * Image Utils + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_IMAGE_H +#define WINPR_IMAGE_H + +#include +#include + +#pragma pack(push, 1) + +typedef struct +{ + BYTE bfType[2]; + UINT32 bfSize; + UINT16 bfReserved1; + UINT16 bfReserved2; + UINT32 bfOffBits; +} WINPR_BITMAP_FILE_HEADER; + +typedef struct +{ + UINT32 biSize; + INT32 biWidth; + INT32 biHeight; + UINT16 biPlanes; + UINT16 biBitCount; + UINT32 biCompression; + UINT32 biSizeImage; + INT32 biXPelsPerMeter; + INT32 biYPelsPerMeter; + UINT32 biClrUsed; + UINT32 biClrImportant; +} WINPR_BITMAP_INFO_HEADER; + +typedef struct +{ + UINT32 bcSize; + UINT16 bcWidth; + UINT16 bcHeight; + UINT16 bcPlanes; + UINT16 bcBitCount; +} WINPR_BITMAP_CORE_HEADER; + +#pragma pack(pop) + +#define WINPR_IMAGE_BITMAP 0 +#define WINPR_IMAGE_PNG 1 +#define WINPR_IMAGE_JPEG 2 +#define WINPR_IMAGE_WEBP 3 + +#define WINPR_IMAGE_BMP_HEADER_LEN 54 + +typedef struct +{ + int type; + UINT32 width; + UINT32 height; + BYTE* data; + UINT32 scanline; + UINT32 bitsPerPixel; + UINT32 bytesPerPixel; +} wImage; + +typedef enum +{ + WINPR_IMAGE_CMP_NO_FLAGS = 0, + WINPR_IMAGE_CMP_IGNORE_DEPTH = 1, + WINPR_IMAGE_CMP_IGNORE_ALPHA = 2, + WINPR_IMAGE_CMP_FUZZY = 4 +} wImageFlags; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int winpr_bitmap_write(const char* filename, const BYTE* data, size_t width, + size_t height, size_t bpp); + WINPR_API int winpr_bitmap_write_ex(const char* filename, const BYTE* data, size_t stride, + size_t width, size_t height, size_t bpp); + WINPR_API BYTE* winpr_bitmap_construct_header(size_t width, size_t height, size_t bpp); + + WINPR_API int winpr_image_write(wImage* image, const char* filename); + WINPR_API int winpr_image_write_ex(wImage* image, UINT32 format, const char* filename); + WINPR_API int winpr_image_read(wImage* image, const char* filename); + + WINPR_API void* winpr_image_write_buffer(wImage* image, UINT32 format, size_t* size); + WINPR_API int winpr_image_read_buffer(wImage* image, const BYTE* buffer, size_t size); + + WINPR_API void winpr_image_free(wImage* image, BOOL bFreeBuffer); + + WINPR_ATTR_MALLOC(winpr_image_free, 1) + WINPR_API wImage* winpr_image_new(void); + + WINPR_API BOOL winpr_image_format_is_supported(UINT32 format); + WINPR_API const char* winpr_image_format_extension(UINT32 format); + WINPR_API const char* winpr_image_format_mime(UINT32 format); + WINPR_API BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB, UINT32 flags); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_IMAGE_H */ diff --git a/winpr/include/winpr/ini.h b/winpr/include/winpr/ini.h new file mode 100644 index 0000000..6deefae --- /dev/null +++ b/winpr/include/winpr/ini.h @@ -0,0 +1,157 @@ +/** + * WinPR: Windows Portable Runtime + * .ini config file + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_INI_H +#define WINPR_UTILS_INI_H + +#include +#include + +typedef struct s_wIniFile wIniFile; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** @brief read an ini file from a buffer + * + * @param ini The instance to use, must not be \b NULL + * @param buffer The buffer to read from, must be a '\0' terminated string. + * + * @return > 0 for success, < 0 for failure + */ + WINPR_API int IniFile_ReadBuffer(wIniFile* ini, const char* buffer); + + /** @brief read an ini file from a file + * + * @param ini The instance to use, must not be \b NULL + * @param filename The name of the file to read from, must be a '\0' terminated string. + * + * @return > 0 for success, < 0 for failure + */ + WINPR_API int IniFile_ReadFile(wIniFile* ini, const char* filename); + + /** @brief write an ini instance to a buffer + * + * @param ini The instance to use, must not be \b NULL + * + * @return A newly allocated string, use \b free after use. \b NULL in case of failure + */ + WINPR_API char* IniFile_WriteBuffer(wIniFile* ini); + + /** @brief write an ini instance to a file + * + * @param ini The instance to use, must not be \b NULL + * @param filename The name of the file as '\0' terminated string. + * + * @return > 0 for success, < 0 for failure + */ + WINPR_API int IniFile_WriteFile(wIniFile* ini, const char* filename); + + /** @brief Get the number and names of sections in the ini instance + * + * @param ini The instance to use, must not be \b NULL + * @param count A buffer that will contain the number of sections + * + * @return A newly allocated array of strings (size \b count). Use \b free after use + */ + WINPR_API char** IniFile_GetSectionNames(wIniFile* ini, size_t* count); + + /** @brief Get the number and names of keys of a section in the ini instance + * + * @param ini The instance to use, must not be \b NULL + * @param section The name of the section as '\0' terminated string. + * @param count A buffer that will contain the number of sections + * + * @return A newly allocated array of strings (size \b count). Use \b free after use + */ + WINPR_API char** IniFile_GetSectionKeyNames(wIniFile* ini, const char* section, size_t* count); + + /** @brief Get an ini [section/key] value of type string + * + * @param ini The instance to use, must not be \b NULL + * @param section The name of the section as '\0' terminated string. + * @param key The name of the key as '\0' terminated string. + * + * @return The value of the [section/key] as '\0' terminated string or \b NULL + */ + WINPR_API const char* IniFile_GetKeyValueString(wIniFile* ini, const char* section, + const char* key); + + /** @brief Get an ini [section/key] value of type int + * + * @param ini The instance to use, must not be \b NULL + * @param section The name of the section as '\0' terminated string. + * @param key The name of the key as '\0' terminated string. + * + * @return The value of the [section/key] + */ + WINPR_API int IniFile_GetKeyValueInt(wIniFile* ini, const char* section, const char* key); + + /** @brief Set an ini [section/key] value of type string + * + * @param ini The instance to use, must not be \b NULL + * @param section The name of the section as '\0' terminated string. + * @param key The name of the key as '\0' terminated string. + * @param value The value of the [section/key] as '\0' terminated string. + * + * @return > 0 for success, < 0 for failure + */ + WINPR_API int IniFile_SetKeyValueString(wIniFile* ini, const char* section, const char* key, + const char* value); + + /** @brief Set an ini [section/key] value of type int + * + * @param ini The instance to use, must not be \b NULL + * @param section The name of the section as '\0' terminated string. + * @param key The name of the key as '\0' terminated string. + * @param value The value of the [section/key] + * + * @return > 0 for success, < 0 for failure + */ + WINPR_API int IniFile_SetKeyValueInt(wIniFile* ini, const char* section, const char* key, + int value); + + /** @brief Free a ini instance + * + * @param ini The instance to free, may be \b NULL + */ + WINPR_API void IniFile_Free(wIniFile* ini); + + /** @brief Create a new ini instance + * + * @return The newly allocated instance or \b NULL if failed. + */ + WINPR_ATTR_MALLOC(IniFile_Free, 1) + WINPR_API wIniFile* IniFile_New(void); + + /** @brief Clone a ini instance + * + * @param ini The instance to free, may be \b NULL + * + * @return the cloned instance or \b NULL in case of \b ini was \b NULL or failure + */ + WINPR_API wIniFile* IniFile_Clone(const wIniFile* ini); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_UTILS_INI_H */ diff --git a/winpr/include/winpr/input.h b/winpr/include/winpr/input.h new file mode 100644 index 0000000..9f5eda8 --- /dev/null +++ b/winpr/include/winpr/input.h @@ -0,0 +1,909 @@ +/** + * WinPR: Windows Portable Runtime + * Input Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_INPUT_H +#define WINPR_INPUT_H + +#include +#include + +/** + * Key Flags + */ + +#define KBDEXT 0x0100u +#define KBDMULTIVK 0x0200u +#define KBDSPECIAL 0x0400u +#define KBDNUMPAD 0x0800u +#define KBDUNICODE 0x1000u +#define KBDINJECTEDVK 0x2000u +#define KBDMAPPEDVK 0x4000u +#define KBDBREAK 0x8000u + +/* + * Virtual Key Codes (Windows): + * http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731/ + * http://msdn.microsoft.com/en-us/library/ms927178.aspx + */ + +/* Mouse buttons */ + +#define VK_LBUTTON 0x01 /* Left mouse button */ +#define VK_RBUTTON 0x02 /* Right mouse button */ +#define VK_CANCEL 0x03 /* Control-break processing */ +#define VK_MBUTTON 0x04 /* Middle mouse button (three-button mouse) */ +#define VK_XBUTTON1 0x05 /* Windows 2000/XP: X1 mouse button */ +#define VK_XBUTTON2 0x06 /* Windows 2000/XP: X2 mouse button */ + +/* 0x07 is undefined */ + +#define VK_BACK 0x08 /* BACKSPACE key */ +#define VK_TAB 0x09 /* TAB key */ + +/* 0x0A to 0x0B are reserved */ + +#define VK_CLEAR 0x0C /* CLEAR key */ +#define VK_RETURN 0x0D /* ENTER key */ + +/* 0x0E to 0x0F are undefined */ + +#define VK_SHIFT 0x10 /* SHIFT key */ +#define VK_CONTROL 0x11 /* CTRL key */ +#define VK_MENU 0x12 /* ALT key */ +#define VK_PAUSE 0x13 /* PAUSE key */ +#define VK_CAPITAL 0x14 /* CAPS LOCK key */ +#define VK_KANA 0x15 /* Input Method Editor (IME) Kana mode */ +#define VK_HANGUEL \ + 0x15 /* IME Hanguel mode (maintained for compatibility; use #define VK_HANGUL) \ + */ +#define VK_HANGUL 0x15 /* IME Hangul mode */ + +/* 0x16 is undefined */ + +#define VK_JUNJA 0x17 /* IME Junja mode */ +#define VK_FINAL 0x18 /* IME final mode */ +#define VK_HANJA 0x19 /* IME Hanja mode */ +#define VK_KANJI 0x19 /* IME Kanji mode */ + +/* 0x1A is undefined, use it for missing Hiragana/Katakana Toggle */ + +#define VK_HKTG 0x1A /* Hiragana/Katakana toggle */ +#define VK_ESCAPE 0x1B /* ESC key */ +#define VK_CONVERT 0x1C /* IME convert */ +#define VK_NONCONVERT 0x1D /* IME nonconvert */ +#define VK_ACCEPT 0x1E /* IME accept */ +#define VK_MODECHANGE 0x1F /* IME mode change request */ + +#define VK_SPACE 0x20 /* SPACEBAR */ +#define VK_PRIOR 0x21 /* PAGE UP key */ +#define VK_NEXT 0x22 /* PAGE DOWN key */ +#define VK_END 0x23 /* END key */ +#define VK_HOME 0x24 /* HOME key */ +#define VK_LEFT 0x25 /* LEFT ARROW key */ +#define VK_UP 0x26 /* UP ARROW key */ +#define VK_RIGHT 0x27 /* RIGHT ARROW key */ +#define VK_DOWN 0x28 /* DOWN ARROW key */ +#define VK_SELECT 0x29 /* SELECT key */ +#define VK_PRINT 0x2A /* PRINT key */ +#define VK_EXECUTE 0x2B /* EXECUTE key */ +#define VK_SNAPSHOT 0x2C /* PRINT SCREEN key */ +#define VK_INSERT 0x2D /* INS key */ +#define VK_DELETE 0x2E /* DEL key */ +#define VK_HELP 0x2F /* HELP key */ + +/* Digits, the last 4 bits of the code represent the corresponding digit */ + +#define VK_KEY_0 0x30 /* '0' key */ +#define VK_KEY_1 0x31 /* '1' key */ +#define VK_KEY_2 0x32 /* '2' key */ +#define VK_KEY_3 0x33 /* '3' key */ +#define VK_KEY_4 0x34 /* '4' key */ +#define VK_KEY_5 0x35 /* '5' key */ +#define VK_KEY_6 0x36 /* '6' key */ +#define VK_KEY_7 0x37 /* '7' key */ +#define VK_KEY_8 0x38 /* '8' key */ +#define VK_KEY_9 0x39 /* '9' key */ + +/* 0x3A to 0x40 are undefined */ + +/* The alphabet, the code corresponds to the capitalized letter in the ASCII code */ + +#define VK_KEY_A 0x41 /* 'A' key */ +#define VK_KEY_B 0x42 /* 'B' key */ +#define VK_KEY_C 0x43 /* 'C' key */ +#define VK_KEY_D 0x44 /* 'D' key */ +#define VK_KEY_E 0x45 /* 'E' key */ +#define VK_KEY_F 0x46 /* 'F' key */ +#define VK_KEY_G 0x47 /* 'G' key */ +#define VK_KEY_H 0x48 /* 'H' key */ +#define VK_KEY_I 0x49 /* 'I' key */ +#define VK_KEY_J 0x4A /* 'J' key */ +#define VK_KEY_K 0x4B /* 'K' key */ +#define VK_KEY_L 0x4C /* 'L' key */ +#define VK_KEY_M 0x4D /* 'M' key */ +#define VK_KEY_N 0x4E /* 'N' key */ +#define VK_KEY_O 0x4F /* 'O' key */ +#define VK_KEY_P 0x50 /* 'P' key */ +#define VK_KEY_Q 0x51 /* 'Q' key */ +#define VK_KEY_R 0x52 /* 'R' key */ +#define VK_KEY_S 0x53 /* 'S' key */ +#define VK_KEY_T 0x54 /* 'T' key */ +#define VK_KEY_U 0x55 /* 'U' key */ +#define VK_KEY_V 0x56 /* 'V' key */ +#define VK_KEY_W 0x57 /* 'W' key */ +#define VK_KEY_X 0x58 /* 'X' key */ +#define VK_KEY_Y 0x59 /* 'Y' key */ +#define VK_KEY_Z 0x5A /* 'Z' key */ + +#define VK_LWIN 0x5B /* Left Windows key (Microsoft Natural keyboard) */ +#define VK_RWIN 0x5C /* Right Windows key (Natural keyboard) */ +#define VK_APPS 0x5D /* Applications key (Natural keyboard) */ + +/* 0x5E is reserved */ + +#define VK_POWER 0x5E /* Power key */ + +#define VK_SLEEP 0x5F /* Computer Sleep key */ + +/* Numeric keypad digits, the last four bits of the code represent the corresponding digit */ + +#define VK_NUMPAD0 0x60 /* Numeric keypad '0' key */ +#define VK_NUMPAD1 0x61 /* Numeric keypad '1' key */ +#define VK_NUMPAD2 0x62 /* Numeric keypad '2' key */ +#define VK_NUMPAD3 0x63 /* Numeric keypad '3' key */ +#define VK_NUMPAD4 0x64 /* Numeric keypad '4' key */ +#define VK_NUMPAD5 0x65 /* Numeric keypad '5' key */ +#define VK_NUMPAD6 0x66 /* Numeric keypad '6' key */ +#define VK_NUMPAD7 0x67 /* Numeric keypad '7' key */ +#define VK_NUMPAD8 0x68 /* Numeric keypad '8' key */ +#define VK_NUMPAD9 0x69 /* Numeric keypad '9' key */ + +/* Numeric keypad operators and special keys */ + +#define VK_MULTIPLY 0x6A /* Multiply key */ +#define VK_ADD 0x6B /* Add key */ +#define VK_SEPARATOR 0x6C /* Separator key */ +#define VK_SUBTRACT 0x6D /* Subtract key */ +#define VK_DECIMAL 0x6E /* Decimal key */ +#define VK_DIVIDE 0x6F /* Divide key */ + +/* Function keys, from F1 to F24 */ + +#define VK_F1 0x70 /* F1 key */ +#define VK_F2 0x71 /* F2 key */ +#define VK_F3 0x72 /* F3 key */ +#define VK_F4 0x73 /* F4 key */ +#define VK_F5 0x74 /* F5 key */ +#define VK_F6 0x75 /* F6 key */ +#define VK_F7 0x76 /* F7 key */ +#define VK_F8 0x77 /* F8 key */ +#define VK_F9 0x78 /* F9 key */ +#define VK_F10 0x79 /* F10 key */ +#define VK_F11 0x7A /* F11 key */ +#define VK_F12 0x7B /* F12 key */ +#define VK_F13 0x7C /* F13 key */ +#define VK_F14 0x7D /* F14 key */ +#define VK_F15 0x7E /* F15 key */ +#define VK_F16 0x7F /* F16 key */ +#define VK_F17 0x80 /* F17 key */ +#define VK_F18 0x81 /* F18 key */ +#define VK_F19 0x82 /* F19 key */ +#define VK_F20 0x83 /* F20 key */ +#define VK_F21 0x84 /* F21 key */ +#define VK_F22 0x85 /* F22 key */ +#define VK_F23 0x86 /* F23 key */ +#define VK_F24 0x87 /* F24 key */ + +/* 0x88 to 0x8F are unassigned */ + +#define VK_NUMLOCK 0x90 /* NUM LOCK key */ +#define VK_SCROLL 0x91 /* SCROLL LOCK key */ + +/* 0x92 to 0x96 are OEM specific */ +/* 0x97 to 0x9F are unassigned */ + +/* Modifier keys */ + +#define VK_LSHIFT 0xA0 /* Left SHIFT key */ +#define VK_RSHIFT 0xA1 /* Right SHIFT key */ +#define VK_LCONTROL 0xA2 /* Left CONTROL key */ +#define VK_RCONTROL 0xA3 /* Right CONTROL key */ +#define VK_LMENU 0xA4 /* Left MENU key */ +#define VK_RMENU 0xA5 /* Right MENU key */ + +/* Browser related keys */ + +#define VK_BROWSER_BACK 0xA6 /* Windows 2000/XP: Browser Back key */ +#define VK_BROWSER_FORWARD 0xA7 /* Windows 2000/XP: Browser Forward key */ +#define VK_BROWSER_REFRESH 0xA8 /* Windows 2000/XP: Browser Refresh key */ +#define VK_BROWSER_STOP 0xA9 /* Windows 2000/XP: Browser Stop key */ +#define VK_BROWSER_SEARCH 0xAA /* Windows 2000/XP: Browser Search key */ +#define VK_BROWSER_FAVORITES 0xAB /* Windows 2000/XP: Browser Favorites key */ +#define VK_BROWSER_HOME 0xAC /* Windows 2000/XP: Browser Start and Home key */ + +/* Volume related keys */ + +#define VK_VOLUME_MUTE 0xAD /* Windows 2000/XP: Volume Mute key */ +#define VK_VOLUME_DOWN 0xAE /* Windows 2000/XP: Volume Down key */ +#define VK_VOLUME_UP 0xAF /* Windows 2000/XP: Volume Up key */ + +/* Media player related keys */ + +#define VK_MEDIA_NEXT_TRACK 0xB0 /* Windows 2000/XP: Next Track key */ +#define VK_MEDIA_PREV_TRACK 0xB1 /* Windows 2000/XP: Previous Track key */ +#define VK_MEDIA_STOP 0xB2 /* Windows 2000/XP: Stop Media key */ +#define VK_MEDIA_PLAY_PAUSE 0xB3 /* Windows 2000/XP: Play/Pause Media key */ + +/* Application launcher keys */ + +#define VK_LAUNCH_MAIL 0xB4 /* Windows 2000/XP: Start Mail key */ +#define VK_MEDIA_SELECT 0xB5 /* Windows 2000/XP: Select Media key */ +#define VK_LAUNCH_MEDIA_SELECT 0xB5 /* Windows 2000/XP: Select Media key */ +#define VK_LAUNCH_APP1 0xB6 /* Windows 2000/XP: Start Application 1 key */ +#define VK_LAUNCH_APP2 0xB7 /* Windows 2000/XP: Start Application 2 key */ + +/* 0xB8 and 0xB9 are reserved */ + +/* OEM keys */ + +#define VK_OEM_1 0xBA /* Used for miscellaneous characters; it can vary by keyboard. */ + /* Windows 2000/XP: For the US standard keyboard, the ';:' key */ + +#define VK_OEM_PLUS 0xBB /* Windows 2000/XP: For any country/region, the '+' key */ +#define VK_OEM_COMMA 0xBC /* Windows 2000/XP: For any country/region, the ',' key */ +#define VK_OEM_MINUS 0xBD /* Windows 2000/XP: For any country/region, the '-' key */ +#define VK_OEM_PERIOD 0xBE /* Windows 2000/XP: For any country/region, the '.' key */ + +#define VK_OEM_2 0xBF /* Used for miscellaneous characters; it can vary by keyboard. */ + /* Windows 2000/XP: For the US standard keyboard, the '/?' key */ + +#define VK_OEM_3 0xC0 /* Used for miscellaneous characters; it can vary by keyboard. */ + /* Windows 2000/XP: For the US standard keyboard, the '`~' key */ + +/* 0xC1 to 0xD7 are reserved */ +#define VK_ABNT_C1 0xC1 /* Brazilian (ABNT) Keyboard */ +#define VK_ABNT_C2 0xC2 /* Brazilian (ABNT) Keyboard */ + +/* 0xD8 to 0xDA are unassigned */ + +#define VK_OEM_4 0xDB /* Used for miscellaneous characters; it can vary by keyboard. */ + /* Windows 2000/XP: For the US standard keyboard, the '[{' key */ + +#define VK_OEM_5 0xDC /* Used for miscellaneous characters; it can vary by keyboard. */ + /* Windows 2000/XP: For the US standard keyboard, the '\|' key */ + +#define VK_OEM_6 0xDD /* Used for miscellaneous characters; it can vary by keyboard. */ + /* Windows 2000/XP: For the US standard keyboard, the ']}' key */ + +#define VK_OEM_7 0xDE /* Used for miscellaneous characters; it can vary by keyboard. */ +/* Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key */ + +#define VK_OEM_8 0xDF /* Used for miscellaneous characters; it can vary by keyboard. */ + +/* 0xE0 is reserved */ + +#define VK_OEM_AX 0xE1 /* AX key on Japanese AX keyboard */ + +#define VK_OEM_102 0xE2 /* Windows 2000/XP: Either the angle bracket key or */ + /* the backslash key on the RT 102-key keyboard */ + +/* 0xE3 and 0xE4 are OEM specific */ + +#define VK_PROCESSKEY \ + 0xE5 /* Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key \ + */ + +/* 0xE6 is OEM specific */ + +#define VK_PACKET \ + 0xE7 /* Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. */ +/* The #define VK_PACKET key is the low word of a 32-bit Virtual Key value used */ +/* for non-keyboard input methods. For more information, */ +/* see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP */ + +/* 0xE8 is unassigned */ +/* 0xE9 to 0xF5 are OEM specific */ + +#define VK_OEM_RESET 0xE9 +#define VK_OEM_JUMP 0xEA +#define VK_OEM_PA1 0xEB +#define VK_OEM_PA2 0xEC +#define VK_OEM_PA3 0xED +#define VK_OEM_WSCTRL 0xEE +#define VK_OEM_CUSEL 0xEF +#define VK_OEM_ATTN 0xF0 +#define VK_OEM_FINISH 0xF1 +#define VK_OEM_COPY 0xF2 +#define VK_OEM_AUTO 0xF3 +#define VK_OEM_ENLW 0xF4 +#define VK_OEM_BACKTAB 0xF5 + +#define VK_ATTN 0xF6 /* Attn key */ +#define VK_CRSEL 0xF7 /* CrSel key */ +#define VK_EXSEL 0xF8 /* ExSel key */ +#define VK_EREOF 0xF9 /* Erase EOF key */ +#define VK_PLAY 0xFA /* Play key */ +#define VK_ZOOM 0xFB /* Zoom key */ +#define VK_NONAME 0xFC /* Reserved */ +#define VK_PA1 0xFD /* PA1 key */ +#define VK_OEM_CLEAR 0xFE /* Clear key */ + +#define VK_NONE 0xFF /* no key */ + +/** + * For East Asian Input Method Editors (IMEs) + * the following additional virtual keyboard definitions must be observed. + */ + +#define VK_DBE_ALPHANUMERIC 0xF0 /* Changes the mode to alphanumeric. */ +#define VK_DBE_KATAKANA 0xF1 /* Changes the mode to Katakana. */ +#define VK_DBE_HIRAGANA 0xF2 /* Changes the mode to Hiragana. */ +#define VK_DBE_SBCSCHAR 0xF3 /* Changes the mode to single-byte characters. */ +#define VK_DBE_DBCSCHAR 0xF4 /* Changes the mode to double-byte characters. */ +#define VK_DBE_ROMAN 0xF5 /* Changes the mode to Roman characters. */ +#define VK_DBE_NOROMAN 0xF6 /* Changes the mode to non-Roman characters. */ +#define VK_DBE_ENTERWORDREGISTERMODE 0xF7 /* Activates the word registration dialog box. */ +#define VK_DBE_ENTERIMECONFIGMODE \ + 0xF8 /* Activates a dialog box for setting up an IME environment. */ +#define VK_DBE_FLUSHSTRING 0xF9 /* Deletes the undetermined string without determining it. */ +#define VK_DBE_CODEINPUT 0xFA /* Changes the mode to code input. */ +#define VK_DBE_NOCODEINPUT 0xFB /* Changes the mode to no-code input. */ + +/* + * Virtual Scan Codes + */ + +/** + * Keyboard Type 4 + */ + +#define KBD4_T00 VK_NONE +#define KBD4_T01 VK_ESCAPE +#define KBD4_T02 VK_KEY_1 +#define KBD4_T03 VK_KEY_2 +#define KBD4_T04 VK_KEY_3 +#define KBD4_T05 VK_KEY_4 +#define KBD4_T06 VK_KEY_5 +#define KBD4_T07 VK_KEY_6 +#define KBD4_T08 VK_KEY_7 +#define KBD4_T09 VK_KEY_8 +#define KBD4_T0A VK_KEY_9 +#define KBD4_T0B VK_KEY_0 +#define KBD4_T0C VK_OEM_MINUS +#define KBD4_T0D VK_OEM_PLUS /* NE */ +#define KBD4_T0E VK_BACK +#define KBD4_T0F VK_TAB +#define KBD4_T10 VK_KEY_Q +#define KBD4_T11 VK_KEY_W +#define KBD4_T12 VK_KEY_E +#define KBD4_T13 VK_KEY_R +#define KBD4_T14 VK_KEY_T +#define KBD4_T15 VK_KEY_Y +#define KBD4_T16 VK_KEY_U +#define KBD4_T17 VK_KEY_I +#define KBD4_T18 VK_KEY_O +#define KBD4_T19 VK_KEY_P +#define KBD4_T1A VK_OEM_4 /* NE */ +#define KBD4_T1B VK_OEM_6 /* NE */ +#define KBD4_T1C VK_RETURN +#define KBD4_T1D VK_LCONTROL +#define KBD4_T1E VK_KEY_A +#define KBD4_T1F VK_KEY_S +#define KBD4_T20 VK_KEY_D +#define KBD4_T21 VK_KEY_F +#define KBD4_T22 VK_KEY_G +#define KBD4_T23 VK_KEY_H +#define KBD4_T24 VK_KEY_J +#define KBD4_T25 VK_KEY_K +#define KBD4_T26 VK_KEY_L +#define KBD4_T27 VK_OEM_1 /* NE */ +#define KBD4_T28 VK_OEM_7 /* NE */ +#define KBD4_T29 VK_OEM_3 /* NE */ +#define KBD4_T2A VK_LSHIFT +#define KBD4_T2B VK_OEM_5 +#define KBD4_T2C VK_KEY_Z +#define KBD4_T2D VK_KEY_X +#define KBD4_T2E VK_KEY_C +#define KBD4_T2F VK_KEY_V +#define KBD4_T30 VK_KEY_B +#define KBD4_T31 VK_KEY_N +#define KBD4_T32 VK_KEY_M +#define KBD4_T33 VK_OEM_COMMA +#define KBD4_T34 VK_OEM_PERIOD +#define KBD4_T35 VK_OEM_2 +#define KBD4_T36 VK_RSHIFT +#define KBD4_T37 VK_MULTIPLY +#define KBD4_T38 VK_LMENU +#define KBD4_T39 VK_SPACE +#define KBD4_T3A VK_CAPITAL +#define KBD4_T3B VK_F1 +#define KBD4_T3C VK_F2 +#define KBD4_T3D VK_F3 +#define KBD4_T3E VK_F4 +#define KBD4_T3F VK_F5 +#define KBD4_T40 VK_F6 +#define KBD4_T41 VK_F7 +#define KBD4_T42 VK_F8 +#define KBD4_T43 VK_F9 +#define KBD4_T44 VK_F10 +#define KBD4_T45 VK_NUMLOCK +#define KBD4_T46 VK_SCROLL +#define KBD4_T47 VK_NUMPAD7 /* VK_HOME */ +#define KBD4_T48 VK_NUMPAD8 /* VK_UP */ +#define KBD4_T49 VK_NUMPAD9 /* VK_PRIOR */ +#define KBD4_T4A VK_SUBTRACT +#define KBD4_T4B VK_NUMPAD4 /* VK_LEFT */ +#define KBD4_T4C VK_NUMPAD5 /* VK_CLEAR */ +#define KBD4_T4D VK_NUMPAD6 /* VK_RIGHT */ +#define KBD4_T4E VK_ADD +#define KBD4_T4F VK_NUMPAD1 /* VK_END */ +#define KBD4_T50 VK_NUMPAD2 /* VK_DOWN */ +#define KBD4_T51 VK_NUMPAD3 /* VK_NEXT */ +#define KBD4_T52 VK_NUMPAD0 /* VK_INSERT */ +#define KBD4_T53 VK_DECIMAL /* VK_DELETE */ +#define KBD4_T54 VK_SNAPSHOT +#define KBD4_T55 VK_NONE +#define KBD4_T56 VK_OEM_102 /* NE */ +#define KBD4_T57 VK_F11 /* NE */ +#define KBD4_T58 VK_F12 /* NE */ +#define KBD4_T59 VK_CLEAR +#define KBD4_T5A VK_OEM_WSCTRL +#define KBD4_T5B VK_OEM_FINISH +#define KBD4_T5C VK_OEM_JUMP +#define KBD4_T5D VK_EREOF +#define KBD4_T5E VK_OEM_BACKTAB +#define KBD4_T5F VK_OEM_AUTO +#define KBD4_T60 VK_NONE +#define KBD4_T61 VK_NONE +#define KBD4_T62 VK_ZOOM +#define KBD4_T63 VK_HELP +#define KBD4_T64 VK_F13 +#define KBD4_T65 VK_F14 +#define KBD4_T66 VK_F15 +#define KBD4_T67 VK_F16 +#define KBD4_T68 VK_F17 +#define KBD4_T69 VK_F18 +#define KBD4_T6A VK_F19 +#define KBD4_T6B VK_F20 +#define KBD4_T6C VK_F21 +#define KBD4_T6D VK_F22 +#define KBD4_T6E VK_F23 +#define KBD4_T6F VK_OEM_PA3 +#define KBD4_T70 VK_NONE +#define KBD4_T71 VK_OEM_RESET +#define KBD4_T72 VK_NONE +#define KBD4_T73 VK_ABNT_C1 +#define KBD4_T74 VK_NONE +#define KBD4_T75 VK_NONE +#define KBD4_T76 VK_F24 +#define KBD4_T77 VK_NONE +#define KBD4_T78 VK_NONE +#define KBD4_T79 VK_NONE +#define KBD4_T7A VK_NONE +#define KBD4_T7B VK_OEM_PA1 +#define KBD4_T7C VK_TAB +#define KBD4_T7D VK_NONE +#define KBD4_T7E VK_ABNT_C2 +#define KBD4_T7F VK_OEM_PA2 + +#define KBD4_X10 VK_MEDIA_PREV_TRACK +#define KBD4_X19 VK_MEDIA_NEXT_TRACK +#define KBD4_X1C VK_RETURN +#define KBD4_X1D VK_RCONTROL +#define KBD4_X20 VK_VOLUME_MUTE +#define KBD4_X21 VK_LAUNCH_APP2 +#define KBD4_X22 VK_MEDIA_PLAY_PAUSE +#define KBD4_X24 VK_MEDIA_STOP +#define KBD4_X2E VK_VOLUME_DOWN +#define KBD4_X30 VK_VOLUME_UP +#define KBD4_X32 VK_BROWSER_HOME +#define KBD4_X35 VK_DIVIDE +#define KBD4_X37 VK_SNAPSHOT +#define KBD4_X38 VK_RMENU +#define KBD4_X46 VK_PAUSE /* VK_CANCEL */ +#define KBD4_X47 VK_HOME +#define KBD4_X48 VK_UP +#define KBD4_X49 VK_PRIOR +#define KBD4_X4B VK_LEFT +#define KBD4_X4D VK_RIGHT +#define KBD4_X4F VK_END +#define KBD4_X50 VK_DOWN +#define KBD4_X51 VK_NEXT /* NE */ +#define KBD4_X52 VK_INSERT +#define KBD4_X53 VK_DELETE +#define KBD4_X5B VK_LWIN +#define KBD4_X5C VK_RWIN +#define KBD4_X5D VK_APPS +#define KBD4_X5E VK_POWER +#define KBD4_X5F VK_SLEEP +#define KBD4_X65 VK_BROWSER_SEARCH +#define KBD4_X66 VK_BROWSER_FAVORITES +#define KBD4_X67 VK_BROWSER_REFRESH +#define KBD4_X68 VK_BROWSER_STOP +#define KBD4_X69 VK_BROWSER_FORWARD +#define KBD4_X6A VK_BROWSER_BACK +#define KBD4_X6B VK_LAUNCH_APP1 +#define KBD4_X6C VK_LAUNCH_MAIL +#define KBD4_X6D VK_LAUNCH_MEDIA_SELECT + +#define KBD4_Y1D VK_PAUSE + +/** + * Keyboard Type 7 + */ + +#define KBD7_T00 VK_NONE +#define KBD7_T01 VK_ESCAPE +#define KBD7_T02 VK_KEY_1 +#define KBD7_T03 VK_KEY_2 +#define KBD7_T04 VK_KEY_3 +#define KBD7_T05 VK_KEY_4 +#define KBD7_T06 VK_KEY_5 +#define KBD7_T07 VK_KEY_6 +#define KBD7_T08 VK_KEY_7 +#define KBD7_T09 VK_KEY_8 +#define KBD7_T0A VK_KEY_9 +#define KBD7_T0B VK_KEY_0 +#define KBD7_T0C VK_OEM_MINUS +#define KBD7_T0D VK_OEM_PLUS +#define KBD7_T0E VK_BACK +#define KBD7_T0F VK_TAB +#define KBD7_T10 VK_KEY_Q +#define KBD7_T11 VK_KEY_W +#define KBD7_T12 VK_KEY_E +#define KBD7_T13 VK_KEY_R +#define KBD7_T14 VK_KEY_T +#define KBD7_T15 VK_KEY_Y +#define KBD7_T16 VK_KEY_U +#define KBD7_T17 VK_KEY_I +#define KBD7_T18 VK_KEY_O +#define KBD7_T19 VK_KEY_P +#define KBD7_T1A VK_OEM_4 /* NE */ +#define KBD7_T1B VK_OEM_6 /* NE */ +#define KBD7_T1C VK_RETURN +#define KBD7_T1D VK_LCONTROL +#define KBD7_T1E VK_KEY_A +#define KBD7_T1F VK_KEY_S +#define KBD7_T20 VK_KEY_D +#define KBD7_T21 VK_KEY_F +#define KBD7_T22 VK_KEY_G +#define KBD7_T23 VK_KEY_H +#define KBD7_T24 VK_KEY_J +#define KBD7_T25 VK_KEY_K +#define KBD7_T26 VK_KEY_L +#define KBD7_T27 VK_OEM_1 +#define KBD7_T28 VK_OEM_7 +#define KBD7_T29 VK_OEM_3 /* NE */ +#define KBD7_T2A VK_LSHIFT +#define KBD7_T2B VK_OEM_5 /* NE */ +#define KBD7_T2C VK_KEY_Z +#define KBD7_T2D VK_KEY_X +#define KBD7_T2E VK_KEY_C +#define KBD7_T2F VK_KEY_V +#define KBD7_T30 VK_KEY_B +#define KBD7_T31 VK_KEY_N +#define KBD7_T32 VK_KEY_M +#define KBD7_T33 VK_OEM_COMMA +#define KBD7_T34 VK_OEM_PERIOD +#define KBD7_T35 VK_OEM_2 +#define KBD7_T36 VK_RSHIFT +#define KBD7_T37 VK_MULTIPLY +#define KBD7_T38 VK_LMENU +#define KBD7_T39 VK_SPACE +#define KBD7_T3A VK_CAPITAL +#define KBD7_T3B VK_F1 +#define KBD7_T3C VK_F2 +#define KBD7_T3D VK_F3 +#define KBD7_T3E VK_F4 +#define KBD7_T3F VK_F5 +#define KBD7_T40 VK_F6 +#define KBD7_T41 VK_F7 +#define KBD7_T42 VK_F8 +#define KBD7_T43 VK_F9 +#define KBD7_T44 VK_F10 +#define KBD7_T45 VK_NUMLOCK +#define KBD7_T46 VK_SCROLL +#define KBD7_T47 VK_NUMPAD7 /* VK_HOME */ +#define KBD7_T48 VK_NUMPAD8 /* VK_UP */ +#define KBD7_T49 VK_NUMPAD9 /* VK_PRIOR */ +#define KBD7_T4A VK_SUBTRACT +#define KBD7_T4B VK_NUMPAD4 /* VK_LEFT */ +#define KBD7_T4C VK_NUMPAD5 /* VK_CLEAR */ +#define KBD7_T4D VK_NUMPAD6 /* VK_RIGHT */ +#define KBD7_T4E VK_ADD +#define KBD7_T4F VK_NUMPAD1 /* VK_END */ +#define KBD7_T50 VK_NUMPAD2 /* VK_DOWN */ +#define KBD7_T51 VK_NUMPAD3 /* VK_NEXT */ +#define KBD7_T52 VK_NUMPAD0 /* VK_INSERT */ +#define KBD7_T53 VK_DECIMAL /* VK_DELETE */ +#define KBD7_T54 VK_SNAPSHOT +#define KBD7_T55 VK_NONE +#define KBD7_T56 VK_OEM_102 +#define KBD7_T57 VK_F11 +#define KBD7_T58 VK_F12 +#define KBD7_T59 VK_CLEAR +#define KBD7_T5A VK_NONAME /* NE */ +#define KBD7_T5B VK_NONAME /* NE */ +#define KBD7_T5C VK_NONAME /* NE */ +#define KBD7_T5D VK_EREOF +#define KBD7_T5E VK_NONE /* NE */ +#define KBD7_T5F VK_NONAME /* NE */ +#define KBD7_T60 VK_NONE +#define KBD7_T61 VK_NONE /* NE */ +#define KBD7_T62 VK_NONE /* NE */ +#define KBD7_T63 VK_NONE +#define KBD7_T64 VK_F13 +#define KBD7_T65 VK_F14 +#define KBD7_T66 VK_F15 +#define KBD7_T67 VK_F16 +#define KBD7_T68 VK_F17 +#define KBD7_T69 VK_F18 +#define KBD7_T6A VK_F19 +#define KBD7_T6B VK_F20 +#define KBD7_T6C VK_F21 +#define KBD7_T6D VK_F22 +#define KBD7_T6E VK_F23 +#define KBD7_T6F VK_NONE /* NE */ +#define KBD7_T70 VK_HKTG /* NE */ +#define KBD7_T71 VK_NONE /* NE */ +#define KBD7_T72 VK_NONE +#define KBD7_T73 VK_ABNT_C1 +#define KBD7_T74 VK_NONE +#define KBD7_T75 VK_NONE +#define KBD7_T76 VK_F24 +#define KBD7_T77 VK_NONE +#define KBD7_T78 VK_NONE +#define KBD7_T79 VK_CONVERT /* NE */ +#define KBD7_T7A VK_NONE +#define KBD7_T7B VK_NONCONVERT /* NE */ +#define KBD7_T7C VK_TAB +#define KBD7_T7D VK_OEM_8 +#define KBD7_T7E VK_ABNT_C2 +#define KBD7_T7F VK_OEM_PA2 + +#define KBD7_X10 VK_MEDIA_PREV_TRACK +#define KBD7_X19 VK_MEDIA_NEXT_TRACK +#define KBD7_X1C VK_RETURN +#define KBD7_X1D VK_RCONTROL +#define KBD7_X20 VK_VOLUME_MUTE +#define KBD7_X21 VK_LAUNCH_APP2 +#define KBD7_X22 VK_MEDIA_PLAY_PAUSE +#define KBD7_X24 VK_MEDIA_STOP +#define KBD7_X2E VK_VOLUME_DOWN +#define KBD7_X30 VK_VOLUME_UP +#define KBD7_X32 VK_BROWSER_HOME +#define KBD7_X33 VK_NONE +#define KBD7_X35 VK_DIVIDE +#define KBD7_X37 VK_SNAPSHOT +#define KBD7_X38 VK_RMENU +#define KBD7_X42 VK_NONE +#define KBD7_X43 VK_NONE +#define KBD7_X44 VK_NONE +#define KBD7_X46 VK_CANCEL +#define KBD7_X47 VK_HOME +#define KBD7_X48 VK_UP +#define KBD7_X49 VK_PRIOR +#define KBD7_X4B VK_LEFT +#define KBD7_X4D VK_RIGHT +#define KBD7_X4F VK_END +#define KBD7_X50 VK_DOWN +#define KBD7_X51 VK_NEXT +#define KBD7_X52 VK_INSERT +#define KBD7_X53 VK_DELETE +#define KBD7_X5B VK_LWIN +#define KBD7_X5C VK_RWIN +#define KBD7_X5D VK_APPS +#define KBD7_X5E VK_POWER +#define KBD7_X5F VK_SLEEP +#define KBD7_X65 VK_BROWSER_SEARCH +#define KBD7_X66 VK_BROWSER_FAVORITES +#define KBD7_X67 VK_BROWSER_REFRESH +#define KBD7_X68 VK_BROWSER_STOP +#define KBD7_X69 VK_BROWSER_FORWARD +#define KBD7_X6A VK_BROWSER_BACK +#define KBD7_X6B VK_LAUNCH_APP1 +#define KBD7_X6C VK_LAUNCH_MAIL +#define KBD7_X6D VK_LAUNCH_MEDIA_SELECT +#define KBD7_XF1 VK_NONE /* NE */ +#define KBD7_XF2 VK_NONE /* NE */ + +#define KBD7_Y1D VK_PAUSE + +/** + * X11 Keycodes + */ + +/** + * Mac OS X + */ + +#define APPLE_VK_ANSI_A 0x00 +#define APPLE_VK_ANSI_S 0x01 +#define APPLE_VK_ANSI_D 0x02 +#define APPLE_VK_ANSI_F 0x03 +#define APPLE_VK_ANSI_H 0x04 +#define APPLE_VK_ANSI_G 0x05 +#define APPLE_VK_ANSI_Z 0x06 +#define APPLE_VK_ANSI_X 0x07 +#define APPLE_VK_ANSI_C 0x08 +#define APPLE_VK_ANSI_V 0x09 +#define APPLE_VK_ISO_Section 0x0A +#define APPLE_VK_ANSI_B 0x0B +#define APPLE_VK_ANSI_Q 0x0C +#define APPLE_VK_ANSI_W 0x0D +#define APPLE_VK_ANSI_E 0x0E +#define APPLE_VK_ANSI_R 0x0F +#define APPLE_VK_ANSI_Y 0x10 +#define APPLE_VK_ANSI_T 0x11 +#define APPLE_VK_ANSI_1 0x12 +#define APPLE_VK_ANSI_2 0x13 +#define APPLE_VK_ANSI_3 0x14 +#define APPLE_VK_ANSI_4 0x15 +#define APPLE_VK_ANSI_6 0x16 +#define APPLE_VK_ANSI_5 0x17 +#define APPLE_VK_ANSI_Equal 0x18 +#define APPLE_VK_ANSI_9 0x19 +#define APPLE_VK_ANSI_7 0x1A +#define APPLE_VK_ANSI_Minus 0x1B +#define APPLE_VK_ANSI_8 0x1C +#define APPLE_VK_ANSI_0 0x1D +#define APPLE_VK_ANSI_RightBracket 0x1E +#define APPLE_VK_ANSI_O 0x1F +#define APPLE_VK_ANSI_U 0x20 +#define APPLE_VK_ANSI_LeftBracket 0x21 +#define APPLE_VK_ANSI_I 0x22 +#define APPLE_VK_ANSI_P 0x23 +#define APPLE_VK_Return 0x24 +#define APPLE_VK_ANSI_L 0x25 +#define APPLE_VK_ANSI_J 0x26 +#define APPLE_VK_ANSI_Quote 0x27 +#define APPLE_VK_ANSI_K 0x28 +#define APPLE_VK_ANSI_Semicolon 0x29 +#define APPLE_VK_ANSI_Backslash 0x2A +#define APPLE_VK_ANSI_Comma 0x2B +#define APPLE_VK_ANSI_Slash 0x2C +#define APPLE_VK_ANSI_N 0x2D +#define APPLE_VK_ANSI_M 0x2E +#define APPLE_VK_ANSI_Period 0x2F +#define APPLE_VK_Tab 0x30 +#define APPLE_VK_Space 0x31 +#define APPLE_VK_ANSI_Grave 0x32 +#define APPLE_VK_Delete 0x33 +#define APPLE_VK_0x34 0x34 +#define APPLE_VK_Escape 0x35 +#define APPLE_VK_0x36 0x36 +#define APPLE_VK_Command 0x37 +#define APPLE_VK_Shift 0x38 +#define APPLE_VK_CapsLock 0x39 +#define APPLE_VK_Option 0x3A +#define APPLE_VK_Control 0x3B +#define APPLE_VK_RightShift 0x3C +#define APPLE_VK_RightOption 0x3D +#define APPLE_VK_RightControl 0x3E +#define APPLE_VK_Function 0x3F +#define APPLE_VK_F17 0x40 +#define APPLE_VK_ANSI_KeypadDecimal 0x41 +#define APPLE_VK_0x42 0x42 +#define APPLE_VK_ANSI_KeypadMultiply 0x43 +#define APPLE_VK_0x44 0x44 +#define APPLE_VK_ANSI_KeypadPlus 0x45 +#define APPLE_VK_0x46 0x46 +#define APPLE_VK_ANSI_KeypadClear 0x47 +#define APPLE_VK_VolumeUp 0x48 +#define APPLE_VK_VolumeDown 0x49 +#define APPLE_VK_Mute 0x4A +#define APPLE_VK_ANSI_KeypadDivide 0x4B +#define APPLE_VK_ANSI_KeypadEnter 0x4C +#define APPLE_VK_0x4D 0x4D +#define APPLE_VK_ANSI_KeypadMinus 0x4E +#define APPLE_VK_F18 0x4F +#define APPLE_VK_F19 0x50 +#define APPLE_VK_ANSI_KeypadEquals 0x51 +#define APPLE_VK_ANSI_Keypad0 0x52 +#define APPLE_VK_ANSI_Keypad1 0x53 +#define APPLE_VK_ANSI_Keypad2 0x54 +#define APPLE_VK_ANSI_Keypad3 0x55 +#define APPLE_VK_ANSI_Keypad4 0x56 +#define APPLE_VK_ANSI_Keypad5 0x57 +#define APPLE_VK_ANSI_Keypad6 0x58 +#define APPLE_VK_ANSI_Keypad7 0x59 +#define APPLE_VK_F20 0x5A +#define APPLE_VK_ANSI_Keypad8 0x5B +#define APPLE_VK_ANSI_Keypad9 0x5C +#define APPLE_VK_JIS_Yen 0x5D +#define APPLE_VK_JIS_Underscore 0x5E +#define APPLE_VK_JIS_KeypadComma 0x5F +#define APPLE_VK_F5 0x60 +#define APPLE_VK_F6 0x61 +#define APPLE_VK_F7 0x62 +#define APPLE_VK_F3 0x63 +#define APPLE_VK_F8 0x64 +#define APPLE_VK_F9 0x65 +#define APPLE_VK_JIS_Eisu 0x66 +#define APPLE_VK_F11 0x67 +#define APPLE_VK_JIS_Kana 0x68 +#define APPLE_VK_F13 0x69 +#define APPLE_VK_F16 0x6A +#define APPLE_VK_F14 0x6B +#define APPLE_VK_F10 0x6D +#define APPLE_VK_0x6C 0x6C +#define APPLE_VK_0x6E 0x6E +#define APPLE_VK_F12 0x6F +#define APPLE_VK_0x70 0x70 +#define APPLE_VK_F15 0x71 +#define APPLE_VK_Help 0x72 +#define APPLE_VK_Home 0x73 +#define APPLE_VK_PageUp 0x74 +#define APPLE_VK_ForwardDelete 0x75 +#define APPLE_VK_F4 0x76 +#define APPLE_VK_End 0x77 +#define APPLE_VK_F2 0x78 +#define APPLE_VK_PageDown 0x79 +#define APPLE_VK_F1 0x7A +#define APPLE_VK_LeftArrow 0x7B +#define APPLE_VK_RightArrow 0x7C +#define APPLE_VK_DownArrow 0x7D +#define APPLE_VK_UpArrow 0x7E + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* [MS-RDPBCGR] 2.2.1.3.2 Client Core Data (TS_UD_CS_CORE) KeyboardType */ + enum WINPR_KBD_TYPE + { + WINPR_KBD_TYPE_IBM_PC_XT = 0x00000001, /* IBM PC/XT or compatible (83-key) keyboard */ + WINPR_KBD_TYPE_OLIVETTI_ICO = 0x00000002, /* Olivetti "ICO" (102-key) keyboard */ + WINPR_KBD_TYPE_IBM_PC_AT = 0x00000003, /* IBM PC/AT (84-key) and similar keyboards */ + WINPR_KBD_TYPE_IBM_ENHANCED = 0x00000004, /* IBM enhanced (101-key or 102-key) keyboard */ + WINPR_KBD_TYPE_NOKIA_1050 = 0x00000005, /* Nokia 1050 and similar keyboards */ + WINPR_KBD_TYPE_NOKIA_9140 = 0x00000006, /* Nokia 9140 and similar keyboards */ + WINPR_KBD_TYPE_JAPANESE = 0x00000007 /* Japanese keyboard */ + }; + + /** + * Functions + */ + + WINPR_API const char* GetVirtualKeyName(DWORD vkcode); + WINPR_API DWORD GetVirtualKeyCodeFromName(const char* vkname); + WINPR_API DWORD GetVirtualKeyCodeFromXkbKeyName(const char* xkbname); + + WINPR_API DWORD GetVirtualKeyCodeFromVirtualScanCode(DWORD scancode, + DWORD /* WINPR_KBD_TYPE */ dwKeyboardType); + WINPR_API DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, + DWORD /* WINPR_KBD_TYPE */ dwKeyboardType); + + typedef enum + { + WINPR_KEYCODE_TYPE_NONE = 0x00000000, + WINPR_KEYCODE_TYPE_APPLE = 0x00000001, + WINPR_KEYCODE_TYPE_EVDEV = 0x00000002, + WINPR_KEYCODE_TYPE_XKB = 0x00000003 + } WINPR_KEYCODE_TYPE; + + WINPR_API DWORD GetVirtualKeyCodeFromKeycode(DWORD keycode, WINPR_KEYCODE_TYPE type); + WINPR_API DWORD GetKeycodeFromVirtualKeyCode(DWORD keycode, WINPR_KEYCODE_TYPE type); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_INPUT_H */ diff --git a/winpr/include/winpr/interlocked.h b/winpr/include/winpr/interlocked.h new file mode 100644 index 0000000..a0f9521 --- /dev/null +++ b/winpr/include/winpr/interlocked.h @@ -0,0 +1,216 @@ +/** + * WinPR: Windows Portable Runtime + * Interlocked Singly-Linked Lists + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_INTERLOCKED_H +#define WINPR_INTERLOCKED_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _WIN32 + +#ifndef CONTAINING_RECORD +#define CONTAINING_RECORD(address, type, field) \ + ((type*)(((ULONG_PTR)address) - (ULONG_PTR)(&(((type*)0)->field)))) +#endif + + typedef struct S_WINPR_LIST_ENTRY WINPR_LIST_ENTRY; + typedef struct S_WINPR_LIST_ENTRY* WINPR_PLIST_ENTRY; + + struct S_WINPR_LIST_ENTRY + { + WINPR_PLIST_ENTRY Flink; + WINPR_PLIST_ENTRY Blink; + }; + + typedef struct S_WINPR_SINGLE_LIST_ENTRY WINPR_SINGLE_LIST_ENTRY; + typedef struct S_WINPR_SINGLE_LIST_ENTRY* WINPR_PSINGLE_LIST_ENTRY; + + struct S_WINPR_SINGLE_LIST_ENTRY + { + WINPR_PSINGLE_LIST_ENTRY Next; + }; + + typedef struct WINPR_LIST_ENTRY32 + { + DWORD Flink; + DWORD Blink; + } WINPR_LIST_ENTRY32; + typedef WINPR_LIST_ENTRY32* WINPR_PLIST_ENTRY32; + + typedef struct WINPR_LIST_ENTRY64 + { + ULONGLONG Flink; + ULONGLONG Blink; + } WINPR_LIST_ENTRY64; + typedef WINPR_LIST_ENTRY64* WINPR_PLIST_ENTRY64; + +#ifdef _WIN64 + + typedef struct S_WINPR_SLIST_ENTRY* WINPR_PSLIST_ENTRY; + typedef struct DECLSPEC_ALIGN(16) S_WINPR_SLIST_ENTRY + { + WINPR_PSLIST_ENTRY Next; + } WINPR_SLIST_ENTRY; + +#else /* _WIN64 */ + + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#define WINPR_SLIST_ENTRY WINPR_SINGLE_LIST_ENTRY +#define _WINPR_SLIST_ENTRY _WINPR_SINGLE_LIST_ENTRY +#define WINPR_PSLIST_ENTRY WINPR_PSINGLE_LIST_ENTRY + + WINPR_PRAGMA_DIAG_POP + +#endif /* _WIN64 */ + +#ifdef _WIN64 + + typedef union DECLSPEC_ALIGN(16) + { + struct + { + ULONGLONG Alignment; + ULONGLONG Region; + } DUMMYSTRUCTNAME; + + struct + { + ULONGLONG Depth : 16; + ULONGLONG Sequence : 9; + ULONGLONG NextEntry : 39; + ULONGLONG HeaderType : 1; + ULONGLONG Init : 1; + ULONGLONG Reserved : 59; + ULONGLONG Region : 3; + } Header8; + + struct + { + ULONGLONG Depth : 16; + ULONGLONG Sequence : 48; + ULONGLONG HeaderType : 1; + ULONGLONG Reserved : 3; + ULONGLONG NextEntry : 60; + } HeaderX64; + } WINPR_SLIST_HEADER, *WINPR_PSLIST_HEADER; + +#else /* _WIN64 */ + + typedef union + { + ULONGLONG Alignment; + + struct + { + WINPR_SLIST_ENTRY Next; + WORD Depth; + WORD Sequence; + } DUMMYSTRUCTNAME; + } WINPR_SLIST_HEADER, *WINPR_PSLIST_HEADER; + +#endif /* _WIN64 */ + + /* Singly-Linked List */ + + WINPR_API VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead); + + WINPR_API WINPR_PSLIST_ENTRY InterlockedPushEntrySList(WINPR_PSLIST_HEADER ListHead, + WINPR_PSLIST_ENTRY ListEntry); + WINPR_API WINPR_PSLIST_ENTRY InterlockedPushListSListEx(WINPR_PSLIST_HEADER ListHead, + WINPR_PSLIST_ENTRY List, + WINPR_PSLIST_ENTRY ListEnd, + ULONG Count); + WINPR_API WINPR_PSLIST_ENTRY InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead); + WINPR_API WINPR_PSLIST_ENTRY InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead); + + WINPR_API USHORT QueryDepthSList(WINPR_PSLIST_HEADER ListHead); + + WINPR_API LONG InterlockedIncrement(LONG volatile* Addend); + WINPR_API LONG InterlockedDecrement(LONG volatile* Addend); + + WINPR_API LONG InterlockedExchange(LONG volatile* Target, LONG Value); + WINPR_API LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value); + + WINPR_API LONG InterlockedCompareExchange(LONG volatile* Destination, LONG Exchange, + LONG Comperand); + + WINPR_API PVOID InterlockedCompareExchangePointer(PVOID volatile* Destination, PVOID Exchange, + PVOID Comperand); + +#else /* _WIN32 */ +#define WINPR_LIST_ENTRY LIST_ENTRY +#define WINPR_PLIST_ENTRY PLIST_ENTRY + +#define WINPR_SINGLE_LIST_ENTRY SINGLE_LIST_ENTRY +#define WINPR_PSINGLE_LIST_ENTRY PSINGLE_LIST_ENTRY + +#define WINPR_SLIST_ENTRY SLIST_ENTRY +#define WINPR_PSLIST_ENTRY PSLIST_ENTRY + +#define WINPR_SLIST_HEADER SLIST_HEADER +#define WINPR_PSLIST_HEADER PSLIST_HEADER + +#endif /* _WIN32 */ + +#if (!defined(_WIN32) || \ + (defined(_WIN32) && (_WIN32_WINNT < 0x0502) && !defined(InterlockedCompareExchange64))) +#define WINPR_INTERLOCKED_COMPARE_EXCHANGE64 1 +#endif + +#ifdef WINPR_INTERLOCKED_COMPARE_EXCHANGE64 + + WINPR_API LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, + LONGLONG Exchange, LONGLONG Comperand); + +#endif + + /* Doubly-Linked List */ + + WINPR_API VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead); + + WINPR_API BOOL IsListEmpty(const WINPR_LIST_ENTRY* ListHead); + + WINPR_API BOOL RemoveEntryList(WINPR_PLIST_ENTRY Entry); + + WINPR_API VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry); + WINPR_API WINPR_PLIST_ENTRY RemoveHeadList(WINPR_PLIST_ENTRY ListHead); + + WINPR_API VOID InsertTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry); + WINPR_API WINPR_PLIST_ENTRY RemoveTailList(WINPR_PLIST_ENTRY ListHead); + WINPR_API VOID AppendTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY ListToAppend); + + WINPR_API VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead, WINPR_PSINGLE_LIST_ENTRY Entry); + WINPR_API WINPR_PSINGLE_LIST_ENTRY PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_INTERLOCKED_H */ diff --git a/winpr/include/winpr/intrin.h b/winpr/include/winpr/intrin.h new file mode 100644 index 0000000..066f45f --- /dev/null +++ b/winpr/include/winpr/intrin.h @@ -0,0 +1,93 @@ +/** + * WinPR: Windows Portable Runtime + * C Run-Time Library Routines + * + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 Bernhard Miklautz + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_INTRIN_H +#define WINPR_INTRIN_H + +#if !defined(_WIN32) || defined(__MINGW32__) + +/** + * __lzcnt16, __lzcnt, __lzcnt64: + * http://msdn.microsoft.com/en-us/library/bb384809/ + * + * Beware: the result of __builtin_clz(0) is undefined + */ + +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) + +static INLINE UINT32 __lzcnt(UINT32 _val32) +{ + return ((UINT32)__builtin_clz(_val32)); +} + +#if !(defined(__MINGW32__) && defined(__clang__)) +static INLINE UINT16 __lzcnt16(UINT16 _val16) +{ + return ((UINT16)(__builtin_clz((UINT32)_val16) - 16)); +} +#endif /* !(defined(__MINGW32__) && defined(__clang__)) */ + +#else /* (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2) */ + +static INLINE UINT32 __lzcnt(UINT32 x) +{ + unsigned y; + int n = 32; + y = x >> 16; + if (y != 0) + { + n = n - 16; + x = y; + } + y = x >> 8; + if (y != 0) + { + n = n - 8; + x = y; + } + y = x >> 4; + if (y != 0) + { + n = n - 4; + x = y; + } + y = x >> 2; + if (y != 0) + { + n = n - 2; + x = y; + } + y = x >> 1; + if (y != 0) + return n - 2; + return n - x; +} + +static INLINE UINT16 __lzcnt16(UINT16 x) +{ + return ((UINT16)__lzcnt((UINT32)x)); +} + +#endif /* (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2) */ + +#endif /* !defined(_WIN32) || defined(__MINGW32__) */ + +#endif /* WINPR_INTRIN_H */ diff --git a/winpr/include/winpr/io.h b/winpr/include/winpr/io.h new file mode 100644 index 0000000..2a0e34c --- /dev/null +++ b/winpr/include/winpr/io.h @@ -0,0 +1,254 @@ +/** + * WinPR: Windows Portable Runtime + * Asynchronous I/O Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_IO_H +#define WINPR_IO_H + +#include +#include + +#ifdef _WIN32 + +#include + +#else + +#include + +typedef struct +{ + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union + { + struct + { + DWORD Offset; + DWORD OffsetHigh; + }; + PVOID Pointer; + }; + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; + +typedef struct +{ + ULONG_PTR lpCompletionKey; + LPOVERLAPPED lpOverlapped; + ULONG_PTR Internal; + DWORD dwNumberOfBytesTransferred; +} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, BOOL bWait); + + WINPR_API BOOL GetOverlappedResultEx(HANDLE hFile, LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, DWORD dwMilliseconds, + BOOL bAlertable); + + WINPR_API BOOL DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, + LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped); + + WINPR_API HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, + ULONG_PTR CompletionKey, + DWORD NumberOfConcurrentThreads); + + WINPR_API BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, + LPDWORD lpNumberOfBytesTransferred, + PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, + DWORD dwMilliseconds); + + WINPR_API BOOL GetQueuedCompletionStatusEx(HANDLE CompletionPort, + LPOVERLAPPED_ENTRY lpCompletionPortEntries, + ULONG ulCount, PULONG ulNumEntriesRemoved, + DWORD dwMilliseconds, BOOL fAlertable); + + WINPR_API BOOL PostQueuedCompletionStatus(HANDLE CompletionPort, + DWORD dwNumberOfBytesTransferred, + ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL CancelIo(HANDLE hFile); + + WINPR_API BOOL CancelIoEx(HANDLE hFile, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL CancelSynchronousIo(HANDLE hThread); + +#ifdef __cplusplus +} +#endif + +#define DEVICE_TYPE ULONG + +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000a +#define FILE_DEVICE_KEYBOARD 0x0000000b +#define FILE_DEVICE_MAILSLOT 0x0000000c +#define FILE_DEVICE_MIDI_IN 0x0000000d +#define FILE_DEVICE_MIDI_OUT 0x0000000e +#define FILE_DEVICE_MOUSE 0x0000000f +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_SCREEN 0x0000001c +#define FILE_DEVICE_SOUND 0x0000001d +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002a +#define FILE_DEVICE_MODEM 0x0000002b +#define FILE_DEVICE_VDM 0x0000002c +#define FILE_DEVICE_MASS_STORAGE 0x0000002d +#define FILE_DEVICE_SMB 0x0000002e +#define FILE_DEVICE_KS 0x0000002f +#define FILE_DEVICE_CHANGER 0x00000030 +#define FILE_DEVICE_SMARTCARD 0x00000031 +#define FILE_DEVICE_ACPI 0x00000032 +#define FILE_DEVICE_DVD 0x00000033 +#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 +#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 +#define FILE_DEVICE_DFS_VOLUME 0x00000036 +#define FILE_DEVICE_SERENUM 0x00000037 +#define FILE_DEVICE_TERMSRV 0x00000038 +#define FILE_DEVICE_KSEC 0x00000039 +#define FILE_DEVICE_FIPS 0x0000003A +#define FILE_DEVICE_INFINIBAND 0x0000003B +#define FILE_DEVICE_VMBUS 0x0000003E +#define FILE_DEVICE_CRYPT_PROVIDER 0x0000003F +#define FILE_DEVICE_WPD 0x00000040 +#define FILE_DEVICE_BLUETOOTH 0x00000041 +#define FILE_DEVICE_MT_COMPOSITE 0x00000042 +#define FILE_DEVICE_MT_TRANSPORT 0x00000043 +#define FILE_DEVICE_BIOMETRIC 0x00000044 +#define FILE_DEVICE_PMI 0x00000045 + +#define CTL_CODE(DeviceType, Function, Method, Access) \ + (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) + +#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode) (((DWORD)(ctrlCode & 0xFFFF0000)) >> 16) + +#define METHOD_FROM_CTL_CODE(ctrlCode) ((DWORD)(ctrlCode & 3)) + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +#define FILE_ANY_ACCESS 0 +#define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) +#define FILE_READ_ACCESS (0x0001) +#define FILE_WRITE_ACCESS (0x0002) + +/* + * WinPR I/O Manager Custom API + */ + +typedef HANDLE PDRIVER_OBJECT_EX; +typedef HANDLE PDEVICE_OBJECT_EX; + +WINPR_API NTSTATUS _IoCreateDeviceEx(PDRIVER_OBJECT_EX DriverObject, ULONG DeviceExtensionSize, + PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, + ULONG DeviceCharacteristics, BOOLEAN Exclusive, + PDEVICE_OBJECT_EX* DeviceObject); + +WINPR_API VOID _IoDeleteDeviceEx(PDEVICE_OBJECT_EX DeviceObject); + +#endif + +#ifdef _UWP + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, BOOL bWait); + + WINPR_API BOOL DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, + LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped); + + WINPR_API HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, + ULONG_PTR CompletionKey, + DWORD NumberOfConcurrentThreads); + + WINPR_API BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, + LPDWORD lpNumberOfBytesTransferred, + PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, + DWORD dwMilliseconds); + + WINPR_API BOOL GetQueuedCompletionStatusEx(HANDLE CompletionPort, + LPOVERLAPPED_ENTRY lpCompletionPortEntries, + ULONG ulCount, PULONG ulNumEntriesRemoved, + DWORD dwMilliseconds, BOOL fAlertable); + + WINPR_API BOOL PostQueuedCompletionStatus(HANDLE CompletionPort, + DWORD dwNumberOfBytesTransferred, + ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL CancelIo(HANDLE hFile); + + WINPR_API BOOL CancelSynchronousIo(HANDLE hThread); + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * Extended API + */ + +#define ACCESS_FROM_CTL_CODE(ctrlCode) ((DWORD)((ctrlCode >> 14) & 0x3)) +#define FUNCTION_FROM_CTL_CODE(ctrlCode) ((DWORD)((ctrlCode >> 2) & 0xFFF)) + +#endif /* WINPR_IO_H */ diff --git a/winpr/include/winpr/library.h b/winpr/include/winpr/library.h new file mode 100644 index 0000000..8cdd6db --- /dev/null +++ b/winpr/include/winpr/library.h @@ -0,0 +1,119 @@ +/** + * WinPR: Windows Portable Runtime + * Library Loader + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_LIBRARY_H +#define WINPR_LIBRARY_H + +#include +#include + +#if !defined(_WIN32) || defined(_UWP) + +typedef HANDLE DLL_DIRECTORY_COOKIE; + +#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200 +#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 +#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 +#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400 + +#define DONT_RESOLVE_DLL_REFERENCES 0x00000001 +#define LOAD_LIBRARY_AS_DATAFILE 0x00000002 +#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 +#define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x00000010 +#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x00000020 +#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x00000040 +#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 +#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200 +#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400 +#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 +#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API DLL_DIRECTORY_COOKIE AddDllDirectory(PCWSTR NewDirectory); + WINPR_API BOOL RemoveDllDirectory(DLL_DIRECTORY_COOKIE Cookie); + WINPR_API BOOL SetDefaultDllDirectories(DWORD DirectoryFlags); + + WINPR_API HMODULE LoadLibraryA(LPCSTR lpLibFileName); + WINPR_API HMODULE LoadLibraryW(LPCWSTR lpLibFileName); + + WINPR_API HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); + WINPR_API HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define LoadLibrary LoadLibraryW +#define LoadLibraryEx LoadLibraryExW +#else +#define LoadLibrary LoadLibraryA +#define LoadLibraryEx LoadLibraryExA +#endif + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API HMODULE LoadLibraryX(LPCSTR lpLibFileName); + WINPR_API HMODULE LoadLibraryExX(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); + +#ifdef __cplusplus +} +#endif + +#if !defined(_WIN32) && !defined(__CYGWIN__) + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API HMODULE GetModuleHandleA(LPCSTR lpModuleName); + WINPR_API HMODULE GetModuleHandleW(LPCWSTR lpModuleName); + + WINPR_API DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize); + WINPR_API DWORD GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize); + + WINPR_API FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName); + + WINPR_API BOOL FreeLibrary(HMODULE hLibModule); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define GetModuleHandle GetModuleHandleW +#define GetModuleFileName GetModuleFileNameW +#else +#define GetModuleHandle GetModuleHandleA +#define GetModuleFileName GetModuleFileNameA +#endif + +#endif + +#endif /* WINPR_LIBRARY_H */ diff --git a/winpr/include/winpr/memory.h b/winpr/include/winpr/memory.h new file mode 100644 index 0000000..850d6b2 --- /dev/null +++ b/winpr/include/winpr/memory.h @@ -0,0 +1,76 @@ +/** + * WinPR: Windows Portable Runtime + * Memory Allocation + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_MEMORY_H +#define WINPR_MEMORY_H + +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef _WIN32 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API HANDLE CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, + DWORD flProtect, DWORD dwMaximumSizeHigh, + DWORD dwMaximumSizeLow, LPCSTR lpName); + WINPR_API HANDLE CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, + DWORD flProtect, DWORD dwMaximumSizeHigh, + DWORD dwMaximumSizeLow, LPCWSTR lpName); + + WINPR_API HANDLE OpenFileMappingA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName); + WINPR_API HANDLE OpenFileMappingW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName); + + WINPR_API LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, + DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, + SIZE_T dwNumberOfBytesToMap); + + WINPR_API LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, + DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, + SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress); + + WINPR_API BOOL FlushViewOfFile(LPCVOID lpBaseAddress, SIZE_T dwNumberOfBytesToFlush); + + WINPR_API BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define CreateFileMapping CreateFileMappingW +#define OpenFileMapping OpenFileMappingW +#else +#define CreateFileMapping CreateFileMappingA +#define OpenFileMapping OpenFileMappingA +#endif + +#endif + +#endif /* WINPR_MEMORY_H */ diff --git a/winpr/include/winpr/ncrypt.h b/winpr/include/winpr/ncrypt.h new file mode 100644 index 0000000..5f83a98 --- /dev/null +++ b/winpr/include/winpr/ncrypt.h @@ -0,0 +1,219 @@ +/** + * WinPR: Windows Portable Runtime + * NCrypt library + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_INCLUDE_WINPR_NCRYPT_H_ +#define WINPR_INCLUDE_WINPR_NCRYPT_H_ + +#ifdef _WIN32 +#include +#include +#else + +#include +#include + +#ifndef __SECSTATUS_DEFINED__ +typedef LONG SECURITY_STATUS; +#define __SECSTATUS_DEFINED__ +#endif + +typedef ULONG_PTR NCRYPT_HANDLE; +typedef ULONG_PTR NCRYPT_PROV_HANDLE; +typedef ULONG_PTR NCRYPT_KEY_HANDLE; + +#define MS_KEY_STORAGE_PROVIDER \ + (const WCHAR*)"M\x00i\x00" \ + "c\x00r\x00o\x00s\x00o\x00" \ + "f\x00t\x00 " \ + "\x00S\x00o\x00" \ + "f\x00t\x00w\x00" \ + "a\x00r\x00" \ + "e\x00 \x00K\x00" \ + "e\x00y\x00 " \ + "\x00S\x00t\x00o\x00r\x00" \ + "a\x00g\x00" \ + "e\x00 " \ + "\x00P\x00r\x00o\x00v\x00i\x00" \ + "d\x00" \ + "e\x00r\x00\x00" +#define MS_SMART_CARD_KEY_STORAGE_PROVIDER \ + (const WCHAR*)"M\x00i\x00" \ + "c\x00r\x00o\x00s\x00o\x00" \ + "f\x00t\x00 \x00S\x00m\x00" \ + "a\x00r\x00t\x00 " \ + "\x00" \ + "C\x00" \ + "a\x00r\x00" \ + "d\x00 \x00K\x00" \ + "e\x00y\x00 " \ + "\x00S\x00t\x00o\x00r\x00" \ + "a\x00g\x00" \ + "e\x00 " \ + "\x00P\x00r\x00o\x00v\x00i\x00" \ + "d\x00" \ + "e\x00r\x00\x00" + +#define MS_SCARD_PROV_A "Microsoft Base Smart Card Crypto Provider" +#define MS_SCARD_PROV \ + (const WCHAR*)("M\x00i\x00" \ + "c\x00r\x00o\x00s\x00o\x00" \ + "f\x00t\x00 \x00" \ + "B\x00" \ + "a\x00s\x00" \ + "e\x00 " \ + "\x00S\x00m\x00" \ + "a\x00r\x00t\x00 \x00" \ + "C\x00" \ + "a\x00r\x00" \ + "d\x00 " \ + "\x00" \ + "C\x00r\x00y\x00p\x00t\x00o\x00 " \ + "\x00P\x00r\x00o\x00v\x00i\x00" \ + "d\x00" \ + "e\x00r\x00\x00") + +#define MS_PLATFORM_KEY_STORAGE_PROVIDER \ + (const WCHAR*)"M\x00i\x00" \ + "c\x00r\x00o\x00s\x00o\x00" \ + "f\x00t\x00 " \ + "\x00P\x00l\x00" \ + "a\x00t\x00" \ + "f\x00o\x00r\x00m\x00 " \ + "\x00" \ + "C\x00r\x00y\x00p\x00t\x00o\x00 " \ + "\x00P\x00r\x00o\x00v\x00i\x00" \ + "d\x00" \ + "e\x00r\x00\x00" + +#define NCRYPT_CERTIFICATE_PROPERTY \ + (const WCHAR*)"S\x00m\x00" \ + "a\x00r\x00t\x00" \ + "C\x00" \ + "a\x00r\x00" \ + "d\x00K\x00" \ + "e\x00y\x00" \ + "C\x00" \ + "e\x00r\x00t" \ + "\x00i\x00" \ + "f\x00i\x00" \ + "c\x00" \ + "a\x00t\x00" \ + "e\x00\x00" +#define NCRYPT_NAME_PROPERTY (const WCHAR*)"N\x00a\x00m\x00e\x00\x00" +#define NCRYPT_UNIQUE_NAME_PROPERTY \ + (const WCHAR*)"U\x00n\x00i\x00q\x00u\x00" \ + "e\x00 \x00N\x00" \ + "a\x00m\x00" \ + "e\x00\x00" +#define NCRYPT_READER_PROPERTY \ + (const WCHAR*)"S\x00m\x00" \ + "a\x00r\x00t\x00" \ + "C\x00" \ + "a\x00r\x00" \ + "d\x00R\x00" \ + "e\x00" \ + "a\x00" \ + "d\x00" \ + "e\x00r\x00\x00" + +/* winpr specific properties */ +#define NCRYPT_WINPR_SLOTID (const WCHAR*)"S\x00l\x00o\x00t\x00\x00" + +#define NCRYPT_MACHINE_KEY_FLAG 0x20 +#define NCRYPT_SILENT_FLAG 0x40 + +/** @brief a key name descriptor */ +typedef struct NCryptKeyName +{ + LPWSTR pszName; + LPWSTR pszAlgid; + DWORD dwLegacyKeySpec; + DWORD dwFlags; +} NCryptKeyName; + +/** @brief a provider name descriptor */ +typedef struct NCryptProviderName +{ + LPWSTR pszName; + LPWSTR pszComment; +} NCryptProviderName; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, + NCryptProviderName** ppProviderList, + DWORD dwFlags); + + WINPR_API SECURITY_STATUS NCryptOpenStorageProvider(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, DWORD dwFlags); + + WINPR_API SECURITY_STATUS NCryptEnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope, + NCryptKeyName** ppKeyName, PVOID* ppEnumState, + DWORD dwFlags); + + WINPR_API SECURITY_STATUS NCryptOpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey, + LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, + DWORD dwFlags); + + WINPR_API SECURITY_STATUS NCryptGetProperty(NCRYPT_HANDLE hObject, LPCWSTR pszProperty, + PBYTE pbOutput, DWORD cbOutput, DWORD* pcbResult, + DWORD dwFlags); + + WINPR_API SECURITY_STATUS NCryptFreeObject(NCRYPT_HANDLE hObject); + WINPR_API SECURITY_STATUS NCryptFreeBuffer(PVOID pvInput); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * custom NCryptOpenStorageProvider that allows to provide a list of modules to load + * + * @param phProvider [out] resulting provider handle + * @param dwFlags [in] the flags to use + * @param modulePaths [in] an array of library path to try to load ended with a NULL string + * @return ERROR_SUCCESS or an NTE error code something failed + */ + WINPR_API SECURITY_STATUS winpr_NCryptOpenStorageProviderEx(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, + DWORD dwFlags, LPCSTR* modulePaths); + + /** + * Gives a string representation of a SECURITY_STATUS + * + * @param status [in] SECURITY_STATUS that we want as string + * @return the string representation of status + */ + WINPR_API const char* winpr_NCryptSecurityStatusError(SECURITY_STATUS status); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_INCLUDE_WINPR_NCRYPT_H_ */ diff --git a/winpr/include/winpr/nt.h b/winpr/include/winpr/nt.h new file mode 100644 index 0000000..2662b48 --- /dev/null +++ b/winpr/include/winpr/nt.h @@ -0,0 +1,1575 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Native System Services + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_NT_H +#define WINPR_NT_H + +#include +#include +#include + +#ifdef __cplusplus +#define STATUS_CAST(t, val) static_cast(val) +#else +#define STATUS_CAST(t, val) (t)(val) +#endif + +#ifndef _WIN32 + +/* Defined in winnt.h, do not redefine */ +#define STATUS_WAIT_0 STATUS_CAST(NTSTATUS, 0x00000000L) +#define STATUS_ABANDONED_WAIT_0 STATUS_CAST(NTSTATUS, 0x00000080L) +#define STATUS_USER_APC STATUS_CAST(NTSTATUS, 0x000000C0L) +#define STATUS_TIMEOUT STATUS_CAST(NTSTATUS, 0x00000102L) +#define STATUS_PENDING STATUS_CAST(NTSTATUS, 0x00000103L) +#define DBG_EXCEPTION_HANDLED STATUS_CAST(NTSTATUS, 0x00010001L) +#define DBG_CONTINUE STATUS_CAST(NTSTATUS, 0x00010002L) +#define STATUS_SEGMENT_NOTIFICATION STATUS_CAST(NTSTATUS, 0x40000005L) +#define STATUS_FATAL_APP_EXIT STATUS_CAST(NTSTATUS, 0x40000015L) +#define DBG_TERMINATE_THREAD STATUS_CAST(NTSTATUS, 0x40010003L) +#define DBG_TERMINATE_PROCESS STATUS_CAST(NTSTATUS, 0x40010004L) +#define DBG_CONTROL_C STATUS_CAST(NTSTATUS, 0x40010005L) +#define DBG_PRINTEXCEPTION_C STATUS_CAST(NTSTATUS, 0x40010006L) +#define DBG_RIPEXCEPTION STATUS_CAST(NTSTATUS, 0x40010007L) +#define DBG_CONTROL_BREAK STATUS_CAST(NTSTATUS, 0x40010008L) +#define DBG_COMMAND_EXCEPTION STATUS_CAST(NTSTATUS, 0x40010009L) +#define STATUS_GUARD_PAGE_VIOLATION STATUS_CAST(NTSTATUS, 0x80000001L) +#define STATUS_DATATYPE_MISALIGNMENT STATUS_CAST(NTSTATUS, 0x80000002L) +#define STATUS_BREAKPOINT STATUS_CAST(NTSTATUS, 0x80000003L) +#define STATUS_SINGLE_STEP STATUS_CAST(NTSTATUS, 0x80000004L) +#define STATUS_LONGJUMP STATUS_CAST(NTSTATUS, 0x80000026L) +#define STATUS_UNWIND_CONSOLIDATE STATUS_CAST(NTSTATUS, 0x80000029L) +#define DBG_EXCEPTION_NOT_HANDLED STATUS_CAST(NTSTATUS, 0x80010001L) +#define STATUS_ACCESS_VIOLATION STATUS_CAST(NTSTATUS, 0xC0000005L) +#define STATUS_IN_PAGE_ERROR STATUS_CAST(NTSTATUS, 0xC0000006L) +#define STATUS_INVALID_HANDLE STATUS_CAST(NTSTATUS, 0xC0000008L) +#define STATUS_INVALID_PARAMETER STATUS_CAST(NTSTATUS, 0xC000000DL) +#define STATUS_NO_MEMORY STATUS_CAST(NTSTATUS, 0xC0000017L) +#define STATUS_ILLEGAL_INSTRUCTION STATUS_CAST(NTSTATUS, 0xC000001DL) +#define STATUS_NONCONTINUABLE_EXCEPTION STATUS_CAST(NTSTATUS, 0xC0000025L) +#define STATUS_INVALID_DISPOSITION STATUS_CAST(NTSTATUS, 0xC0000026L) +#define STATUS_ARRAY_BOUNDS_EXCEEDED STATUS_CAST(NTSTATUS, 0xC000008CL) +#define STATUS_FLOAT_DENORMAL_OPERAND STATUS_CAST(NTSTATUS, 0xC000008DL) +#define STATUS_FLOAT_DIVIDE_BY_ZERO STATUS_CAST(NTSTATUS, 0xC000008EL) +#define STATUS_FLOAT_INEXACT_RESULT STATUS_CAST(NTSTATUS, 0xC000008FL) +#define STATUS_FLOAT_INVALID_OPERATION STATUS_CAST(NTSTATUS, 0xC0000090L) +#define STATUS_FLOAT_OVERFLOW STATUS_CAST(NTSTATUS, 0xC0000091L) +#define STATUS_FLOAT_STACK_CHECK STATUS_CAST(NTSTATUS, 0xC0000092L) +#define STATUS_FLOAT_UNDERFLOW STATUS_CAST(NTSTATUS, 0xC0000093L) +#define STATUS_INTEGER_DIVIDE_BY_ZERO STATUS_CAST(NTSTATUS, 0xC0000094L) +#define STATUS_INTEGER_OVERFLOW STATUS_CAST(NTSTATUS, 0xC0000095L) +#define STATUS_PRIVILEGED_INSTRUCTION STATUS_CAST(NTSTATUS, 0xC0000096L) +#define STATUS_STACK_OVERFLOW STATUS_CAST(NTSTATUS, 0xC00000FDL) +#define STATUS_DLL_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000135L) +#define STATUS_ORDINAL_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000138L) +#define STATUS_ENTRYPOINT_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000139L) +#define STATUS_CONTROL_C_EXIT STATUS_CAST(NTSTATUS, 0xC000013AL) +#define STATUS_DLL_INIT_FAILED STATUS_CAST(NTSTATUS, 0xC0000142L) +#define STATUS_FLOAT_MULTIPLE_FAULTS STATUS_CAST(NTSTATUS, 0xC00002B4L) +#define STATUS_FLOAT_MULTIPLE_TRAPS STATUS_CAST(NTSTATUS, 0xC00002B5L) +#define STATUS_REG_NAT_CONSUMPTION STATUS_CAST(NTSTATUS, 0xC00002C9L) +#define STATUS_STACK_BUFFER_OVERRUN STATUS_CAST(NTSTATUS, 0xC0000409L) +#define STATUS_INVALID_CRUNTIME_PARAMETER STATUS_CAST(NTSTATUS, 0xC0000417L) +#define STATUS_ASSERTION_FAILURE STATUS_CAST(NTSTATUS, 0xC0000420L) +#define STATUS_SXS_EARLY_DEACTIVATION STATUS_CAST(NTSTATUS, 0xC015000FL) +#define STATUS_SXS_INVALID_DEACTIVATION STATUS_CAST(NTSTATUS, 0xC0150010L) + +#endif + +/* Defined in wincred.h, do not redefine */ + +#if defined(_WIN32) && !defined(_UWP) + +#include + +#else + +#define STATUS_LOGON_FAILURE STATUS_CAST(NTSTATUS, 0xC000006DL) +#define STATUS_WRONG_PASSWORD STATUS_CAST(NTSTATUS, 0xC000006AL) +#define STATUS_PASSWORD_EXPIRED STATUS_CAST(NTSTATUS, 0xC0000071L) +#define STATUS_PASSWORD_MUST_CHANGE STATUS_CAST(NTSTATUS, 0xC0000224L) +#define STATUS_ACCESS_DENIED STATUS_CAST(NTSTATUS, 0xC0000022L) +#define STATUS_DOWNGRADE_DETECTED STATUS_CAST(NTSTATUS, 0xC0000388L) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED STATUS_CAST(NTSTATUS, 0xC0000413L) +#define STATUS_ACCOUNT_DISABLED STATUS_CAST(NTSTATUS, 0xC0000072L) +#define STATUS_ACCOUNT_RESTRICTION STATUS_CAST(NTSTATUS, 0xC000006EL) +#define STATUS_ACCOUNT_LOCKED_OUT STATUS_CAST(NTSTATUS, 0xC0000234L) +#define STATUS_ACCOUNT_EXPIRED STATUS_CAST(NTSTATUS, 0xC0000193L) +#define STATUS_LOGON_TYPE_NOT_GRANTED STATUS_CAST(NTSTATUS, 0xC000015BL) + +#endif + +#define FACILITY_DEBUGGER 0x1 +#define FACILITY_RPC_RUNTIME 0x2 +#define FACILITY_RPC_STUBS 0x3 +#define FACILITY_IO_ERROR_CODE 0x4 +#define FACILITY_TERMINAL_SERVER 0xA +#define FACILITY_USB_ERROR_CODE 0x10 +#define FACILITY_HID_ERROR_CODE 0x11 +#define FACILITY_FIREWIRE_ERROR_CODE 0x12 +#define FACILITY_CLUSTER_ERROR_CODE 0x13 +#define FACILITY_ACPI_ERROR_CODE 0x14 +#define FACILITY_SXS_ERROR_CODE 0x15 + +/** + * NTSTATUS codes + */ + +#if !defined(STATUS_SUCCESS) +#define STATUS_SUCCESS STATUS_CAST(NTSTATUS, 0x00000000) +#endif + +#define STATUS_SEVERITY_SUCCESS 0x0 +#define STATUS_SEVERITY_INFORMATIONAL 0x1 +#define STATUS_SEVERITY_WARNING 0x2 +#define STATUS_SEVERITY_ERROR 0x3 + +#define STATUS_WAIT_1 STATUS_CAST(NTSTATUS, 0x00000001) +#define STATUS_WAIT_2 STATUS_CAST(NTSTATUS, 0x00000002) +#define STATUS_WAIT_3 STATUS_CAST(NTSTATUS, 0x00000003) +#define STATUS_WAIT_63 STATUS_CAST(NTSTATUS, 0x0000003f) +#define STATUS_ABANDONED STATUS_CAST(NTSTATUS, 0x00000080) +#define STATUS_ABANDONED_WAIT_63 STATUS_CAST(NTSTATUS, 0x000000BF) +//#define STATUS_USER_APC STATUS_CAST(NTSTATUS,0x000000C0) +#define STATUS_KERNEL_APC STATUS_CAST(NTSTATUS, 0x00000100) +#define STATUS_ALERTED STATUS_CAST(NTSTATUS, 0x00000101) +//#define STATUS_TIMEOUT STATUS_CAST(NTSTATUS,0x00000102) +//#define STATUS_PENDING STATUS_CAST(NTSTATUS,0x00000103) +#define STATUS_REPARSE STATUS_CAST(NTSTATUS, 0x00000104) +#define STATUS_MORE_ENTRIES STATUS_CAST(NTSTATUS, 0x00000105) +#define STATUS_NOT_ALL_ASSIGNED STATUS_CAST(NTSTATUS, 0x00000106) +#define STATUS_SOME_NOT_MAPPED STATUS_CAST(NTSTATUS, 0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS STATUS_CAST(NTSTATUS, 0x00000108) +#define STATUS_VOLUME_MOUNTED STATUS_CAST(NTSTATUS, 0x00000109) +#define STATUS_RXACT_COMMITTED STATUS_CAST(NTSTATUS, 0x0000010A) +#define STATUS_NOTIFY_CLEANUP STATUS_CAST(NTSTATUS, 0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR STATUS_CAST(NTSTATUS, 0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT STATUS_CAST(NTSTATUS, 0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED STATUS_CAST(NTSTATUS, 0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION STATUS_CAST(NTSTATUS, 0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO STATUS_CAST(NTSTATUS, 0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE STATUS_CAST(NTSTATUS, 0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE STATUS_CAST(NTSTATUS, 0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE STATUS_CAST(NTSTATUS, 0x00000114) +#define STATUS_CACHE_PAGE_LOCKED STATUS_CAST(NTSTATUS, 0x00000115) +#define STATUS_CRASH_DUMP STATUS_CAST(NTSTATUS, 0x00000116) +#define STATUS_BUFFER_ALL_ZEROS STATUS_CAST(NTSTATUS, 0x00000117) +#define STATUS_REPARSE_OBJECT STATUS_CAST(NTSTATUS, 0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED STATUS_CAST(NTSTATUS, 0x00000119) +#define STATUS_TRANSLATION_COMPLETE STATUS_CAST(NTSTATUS, 0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY STATUS_CAST(NTSTATUS, 0x00000121) +#define STATUS_NOTHING_TO_TERMINATE STATUS_CAST(NTSTATUS, 0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB STATUS_CAST(NTSTATUS, 0x00000123) +#define STATUS_PROCESS_IN_JOB STATUS_CAST(NTSTATUS, 0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY STATUS_CAST(NTSTATUS, 0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY STATUS_CAST(NTSTATUS, 0x00000126) + +#define STATUS_OBJECT_NAME_EXISTS STATUS_CAST(NTSTATUS, 0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED STATUS_CAST(NTSTATUS, 0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE STATUS_CAST(NTSTATUS, 0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE STATUS_CAST(NTSTATUS, 0x40000003) +#define STATUS_RXACT_STATE_CREATED STATUS_CAST(NTSTATUS, 0x40000004) +//#define STATUS_SEGMENT_NOTIFICATION STATUS_CAST(NTSTATUS,0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY STATUS_CAST(NTSTATUS, 0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY STATUS_CAST(NTSTATUS, 0x40000007) +#define STATUS_SERIAL_MORE_WRITES STATUS_CAST(NTSTATUS, 0x40000008) +#define STATUS_REGISTRY_RECOVERED STATUS_CAST(NTSTATUS, 0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP STATUS_CAST(NTSTATUS, 0x4000000A) +#define STATUS_FT_WRITE_RECOVERY STATUS_CAST(NTSTATUS, 0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT STATUS_CAST(NTSTATUS, 0x4000000C) +#define STATUS_NULL_LM_PASSWORD STATUS_CAST(NTSTATUS, 0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH STATUS_CAST(NTSTATUS, 0x4000000E) +#define STATUS_RECEIVE_PARTIAL STATUS_CAST(NTSTATUS, 0x4000000F) +#define STATUS_RECEIVE_EXPEDITED STATUS_CAST(NTSTATUS, 0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED STATUS_CAST(NTSTATUS, 0x40000011) +#define STATUS_EVENT_DONE STATUS_CAST(NTSTATUS, 0x40000012) +#define STATUS_EVENT_PENDING STATUS_CAST(NTSTATUS, 0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM STATUS_CAST(NTSTATUS, 0x40000014) +//#define STATUS_FATAL_APP_EXIT STATUS_CAST(NTSTATUS,0x40000015) +#define STATUS_PREDEFINED_HANDLE STATUS_CAST(NTSTATUS, 0x40000016) +#define STATUS_WAS_UNLOCKED STATUS_CAST(NTSTATUS, 0x40000017) +#define STATUS_SERVICE_NOTIFICATION STATUS_CAST(NTSTATUS, 0x40000018) +#define STATUS_WAS_LOCKED STATUS_CAST(NTSTATUS, 0x40000019) +#define STATUS_LOG_HARD_ERROR STATUS_CAST(NTSTATUS, 0x4000001A) +#define STATUS_ALREADY_WIN32 STATUS_CAST(NTSTATUS, 0x4000001B) +#define STATUS_WX86_UNSIMULATE STATUS_CAST(NTSTATUS, 0x4000001C) +#define STATUS_WX86_CONTINUE STATUS_CAST(NTSTATUS, 0x4000001D) +#define STATUS_WX86_SINGLE_STEP STATUS_CAST(NTSTATUS, 0x4000001E) +#define STATUS_WX86_BREAKPOINT STATUS_CAST(NTSTATUS, 0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE STATUS_CAST(NTSTATUS, 0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE STATUS_CAST(NTSTATUS, 0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN STATUS_CAST(NTSTATUS, 0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE STATUS_CAST(NTSTATUS, 0x40000023) +#define STATUS_NO_YIELD_PERFORMED STATUS_CAST(NTSTATUS, 0x40000024) +#define STATUS_TIMER_RESUME_IGNORED STATUS_CAST(NTSTATUS, 0x40000025) +#define STATUS_ARBITRATION_UNHANDLED STATUS_CAST(NTSTATUS, 0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0x40000027) +#define STATUS_WX86_CREATEWX86TIB STATUS_CAST(NTSTATUS, 0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH STATUS_CAST(NTSTATUS, 0x40000029) +#define STATUS_HIBERNATED STATUS_CAST(NTSTATUS, 0x4000002A) +#define STATUS_RESUME_HIBERNATION STATUS_CAST(NTSTATUS, 0x4000002B) +#define STATUS_FIRMWARE_UPDATED STATUS_CAST(NTSTATUS, 0x4000002C) +#define STATUS_WAKE_SYSTEM STATUS_CAST(NTSTATUS, 0x40000294) +#define STATUS_DS_SHUTTING_DOWN STATUS_CAST(NTSTATUS, 0x40000370) + +#define RPC_NT_UUID_LOCAL_ONLY STATUS_CAST(NTSTATUS, 0x40020056) +#define RPC_NT_SEND_INCOMPLETE STATUS_CAST(NTSTATUS, 0x400200AF) + +#define STATUS_CTX_CDM_CONNECT STATUS_CAST(NTSTATUS, 0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT STATUS_CAST(NTSTATUS, 0x400A0005) + +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT STATUS_CAST(NTSTATUS, 0x4015000D) + +//#define STATUS_GUARD_PAGE_VIOLATION STATUS_CAST(NTSTATUS,0x80000001) +//#define STATUS_DATATYPE_MISALIGNMENT STATUS_CAST(NTSTATUS,0x80000002) +//#define STATUS_BREAKPOINT STATUS_CAST(NTSTATUS,0x80000003) +//#define STATUS_SINGLE_STEP STATUS_CAST(NTSTATUS,0x80000004) +#define STATUS_BUFFER_OVERFLOW STATUS_CAST(NTSTATUS, 0x80000005) +#define STATUS_NO_MORE_FILES STATUS_CAST(NTSTATUS, 0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER STATUS_CAST(NTSTATUS, 0x80000007) + +#define STATUS_HANDLES_CLOSED STATUS_CAST(NTSTATUS, 0x8000000A) +#define STATUS_NO_INHERITANCE STATUS_CAST(NTSTATUS, 0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE STATUS_CAST(NTSTATUS, 0x8000000C) +#define STATUS_PARTIAL_COPY STATUS_CAST(NTSTATUS, 0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY STATUS_CAST(NTSTATUS, 0x8000000E) +#define STATUS_DEVICE_POWERED_OFF STATUS_CAST(NTSTATUS, 0x8000000F) +#define STATUS_DEVICE_OFF_LINE STATUS_CAST(NTSTATUS, 0x80000010) +#define STATUS_DEVICE_BUSY STATUS_CAST(NTSTATUS, 0x80000011) +#define STATUS_NO_MORE_EAS STATUS_CAST(NTSTATUS, 0x80000012) +#define STATUS_INVALID_EA_NAME STATUS_CAST(NTSTATUS, 0x80000013) +#define STATUS_EA_LIST_INCONSISTENT STATUS_CAST(NTSTATUS, 0x80000014) +#define STATUS_INVALID_EA_FLAG STATUS_CAST(NTSTATUS, 0x80000015) +#define STATUS_VERIFY_REQUIRED STATUS_CAST(NTSTATUS, 0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION STATUS_CAST(NTSTATUS, 0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY STATUS_CAST(NTSTATUS, 0x80000018) +#define STATUS_NO_MORE_ENTRIES STATUS_CAST(NTSTATUS, 0x8000001A) +#define STATUS_FILEMARK_DETECTED STATUS_CAST(NTSTATUS, 0x8000001B) +#define STATUS_MEDIA_CHANGED STATUS_CAST(NTSTATUS, 0x8000001C) +#define STATUS_BUS_RESET STATUS_CAST(NTSTATUS, 0x8000001D) +#define STATUS_END_OF_MEDIA STATUS_CAST(NTSTATUS, 0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA STATUS_CAST(NTSTATUS, 0x8000001F) +#define STATUS_MEDIA_CHECK STATUS_CAST(NTSTATUS, 0x80000020) +#define STATUS_SETMARK_DETECTED STATUS_CAST(NTSTATUS, 0x80000021) +#define STATUS_NO_DATA_DETECTED STATUS_CAST(NTSTATUS, 0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES STATUS_CAST(NTSTATUS, 0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES STATUS_CAST(NTSTATUS, 0x80000024) +#define STATUS_ALREADY_DISCONNECTED STATUS_CAST(NTSTATUS, 0x80000025) +//#define STATUS_LONGJUMP STATUS_CAST(NTSTATUS,0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED STATUS_CAST(NTSTATUS, 0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED STATUS_CAST(NTSTATUS, 0x80000028) +//#define STATUS_UNWIND_CONSOLIDATE STATUS_CAST(NTSTATUS,0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED STATUS_CAST(NTSTATUS, 0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE STATUS_CAST(NTSTATUS, 0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE STATUS_CAST(NTSTATUS, 0x8000002C) + +#define STATUS_DEVICE_REQUIRES_CLEANING STATUS_CAST(NTSTATUS, 0x80000288) +#define STATUS_DEVICE_DOOR_OPEN STATUS_CAST(NTSTATUS, 0x80000289) + +#define STATUS_CLUSTER_NODE_ALREADY_UP STATUS_CAST(NTSTATUS, 0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN STATUS_CAST(NTSTATUS, 0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE STATUS_CAST(NTSTATUS, 0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE STATUS_CAST(NTSTATUS, 0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER STATUS_CAST(NTSTATUS, 0x80130005) + +//#define STATUS_WAIT_0 STATUS_CAST(NTSTATUS,0x00000000) +#define STATUS_UNSUCCESSFUL STATUS_CAST(NTSTATUS, 0xC0000001) +#define STATUS_NOT_IMPLEMENTED STATUS_CAST(NTSTATUS, 0xC0000002) +#define STATUS_INVALID_INFO_CLASS STATUS_CAST(NTSTATUS, 0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH STATUS_CAST(NTSTATUS, 0xC0000004) +//#define STATUS_ACCESS_VIOLATION STATUS_CAST(NTSTATUS,0xC0000005) +//#define STATUS_IN_PAGE_ERROR STATUS_CAST(NTSTATUS,0xC0000006) +#define STATUS_PAGEFILE_QUOTA STATUS_CAST(NTSTATUS, 0xC0000007) +//#define STATUS_INVALID_HANDLE STATUS_CAST(NTSTATUS,0xC0000008) +#define STATUS_BAD_INITIAL_STACK STATUS_CAST(NTSTATUS, 0xC0000009) +#define STATUS_BAD_INITIAL_PC STATUS_CAST(NTSTATUS, 0xC000000A) +#define STATUS_INVALID_CID STATUS_CAST(NTSTATUS, 0xC000000B) +#define STATUS_TIMER_NOT_CANCELED STATUS_CAST(NTSTATUS, 0xC000000C) +//#define STATUS_INVALID_PARAMETER STATUS_CAST(NTSTATUS,0xC000000D) +#define STATUS_NO_SUCH_DEVICE STATUS_CAST(NTSTATUS, 0xC000000E) +#define STATUS_NO_SUCH_FILE STATUS_CAST(NTSTATUS, 0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST STATUS_CAST(NTSTATUS, 0xC0000010) +#define STATUS_END_OF_FILE STATUS_CAST(NTSTATUS, 0xC0000011) +#define STATUS_WRONG_VOLUME STATUS_CAST(NTSTATUS, 0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE STATUS_CAST(NTSTATUS, 0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA STATUS_CAST(NTSTATUS, 0xC0000014) +#define STATUS_NONEXISTENT_SECTOR STATUS_CAST(NTSTATUS, 0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED STATUS_CAST(NTSTATUS, 0xC0000016) +//#define STATUS_NO_MEMORY STATUS_CAST(NTSTATUS,0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES STATUS_CAST(NTSTATUS, 0xC0000018) +#define STATUS_NOT_MAPPED_VIEW STATUS_CAST(NTSTATUS, 0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM STATUS_CAST(NTSTATUS, 0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION STATUS_CAST(NTSTATUS, 0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE STATUS_CAST(NTSTATUS, 0xC000001C) +//#define STATUS_ILLEGAL_INSTRUCTION STATUS_CAST(NTSTATUS,0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE STATUS_CAST(NTSTATUS, 0xC000001E) +#define STATUS_INVALID_VIEW_SIZE STATUS_CAST(NTSTATUS, 0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION STATUS_CAST(NTSTATUS, 0xC0000020) +#define STATUS_ALREADY_COMMITTED STATUS_CAST(NTSTATUS, 0xC0000021) +//#define STATUS_ACCESS_DENIED STATUS_CAST(NTSTATUS,0xC0000022) +#define STATUS_BUFFER_TOO_SMALL STATUS_CAST(NTSTATUS, 0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH STATUS_CAST(NTSTATUS, 0xC0000024) +//#define STATUS_NONCONTINUABLE_EXCEPTION STATUS_CAST(NTSTATUS,0xC0000025) +//#define STATUS_INVALID_DISPOSITION STATUS_CAST(NTSTATUS,0xC0000026) +#define STATUS_UNWIND STATUS_CAST(NTSTATUS, 0xC0000027) +#define STATUS_BAD_STACK STATUS_CAST(NTSTATUS, 0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET STATUS_CAST(NTSTATUS, 0xC0000029) +#define STATUS_NOT_LOCKED STATUS_CAST(NTSTATUS, 0xC000002A) +#define STATUS_PARITY_ERROR STATUS_CAST(NTSTATUS, 0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM STATUS_CAST(NTSTATUS, 0xC000002C) +#define STATUS_NOT_COMMITTED STATUS_CAST(NTSTATUS, 0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES STATUS_CAST(NTSTATUS, 0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG STATUS_CAST(NTSTATUS, 0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX STATUS_CAST(NTSTATUS, 0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER STATUS_CAST(NTSTATUS, 0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR STATUS_CAST(NTSTATUS, 0xC0000032) +#define STATUS_OBJECT_NAME_INVALID STATUS_CAST(NTSTATUS, 0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION STATUS_CAST(NTSTATUS, 0xC0000035) +#define STATUS_PORT_DISCONNECTED STATUS_CAST(NTSTATUS, 0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED STATUS_CAST(NTSTATUS, 0xC0000038) +#define STATUS_OBJECT_PATH_INVALID STATUS_CAST(NTSTATUS, 0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD STATUS_CAST(NTSTATUS, 0xC000003B) +#define STATUS_DATA_OVERRUN STATUS_CAST(NTSTATUS, 0xC000003C) +#define STATUS_DATA_LATE_ERROR STATUS_CAST(NTSTATUS, 0xC000003D) +#define STATUS_DATA_ERROR STATUS_CAST(NTSTATUS, 0xC000003E) +#define STATUS_CRC_ERROR STATUS_CAST(NTSTATUS, 0xC000003F) +#define STATUS_SECTION_TOO_BIG STATUS_CAST(NTSTATUS, 0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED STATUS_CAST(NTSTATUS, 0xC0000041) +#define STATUS_INVALID_PORT_HANDLE STATUS_CAST(NTSTATUS, 0xC0000042) +#define STATUS_SHARING_VIOLATION STATUS_CAST(NTSTATUS, 0xC0000043) +#define STATUS_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION STATUS_CAST(NTSTATUS, 0xC0000045) +#define STATUS_MUTANT_NOT_OWNED STATUS_CAST(NTSTATUS, 0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000047) +#define STATUS_PORT_ALREADY_SET STATUS_CAST(NTSTATUS, 0xC0000048) +#define STATUS_SECTION_NOT_IMAGE STATUS_CAST(NTSTATUS, 0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED STATUS_CAST(NTSTATUS, 0xC000004A) +#define STATUS_THREAD_IS_TERMINATING STATUS_CAST(NTSTATUS, 0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT STATUS_CAST(NTSTATUS, 0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP STATUS_CAST(NTSTATUS, 0xC000004D) +#define STATUS_SECTION_PROTECTION STATUS_CAST(NTSTATUS, 0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0xC000004F) +#define STATUS_EA_TOO_LARGE STATUS_CAST(NTSTATUS, 0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY STATUS_CAST(NTSTATUS, 0xC0000051) +#define STATUS_NO_EAS_ON_FILE STATUS_CAST(NTSTATUS, 0xC0000052) +#define STATUS_EA_CORRUPT_ERROR STATUS_CAST(NTSTATUS, 0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT STATUS_CAST(NTSTATUS, 0xC0000054) +#define STATUS_LOCK_NOT_GRANTED STATUS_CAST(NTSTATUS, 0xC0000055) +#define STATUS_DELETE_PENDING STATUS_CAST(NTSTATUS, 0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0xC0000057) +#define STATUS_UNKNOWN_REVISION STATUS_CAST(NTSTATUS, 0xC0000058) +#define STATUS_REVISION_MISMATCH STATUS_CAST(NTSTATUS, 0xC0000059) +#define STATUS_INVALID_OWNER STATUS_CAST(NTSTATUS, 0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP STATUS_CAST(NTSTATUS, 0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN STATUS_CAST(NTSTATUS, 0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY STATUS_CAST(NTSTATUS, 0xC000005D) +#define STATUS_NO_LOGON_SERVERS STATUS_CAST(NTSTATUS, 0xC000005E) +#ifndef STATUS_NO_SUCH_LOGON_SESSION +#define STATUS_NO_SUCH_LOGON_SESSION STATUS_CAST(NTSTATUS, 0xC000005F) +#endif +#define STATUS_NO_SUCH_PRIVILEGE STATUS_CAST(NTSTATUS, 0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD STATUS_CAST(NTSTATUS, 0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME STATUS_CAST(NTSTATUS, 0xC0000062) +#define STATUS_USER_EXISTS STATUS_CAST(NTSTATUS, 0xC0000063) +#ifndef STATUS_NO_SUCH_USER +#define STATUS_NO_SUCH_USER STATUS_CAST(NTSTATUS, 0xC0000064) +#endif +#define STATUS_GROUP_EXISTS STATUS_CAST(NTSTATUS, 0xC0000065) +#define STATUS_NO_SUCH_GROUP STATUS_CAST(NTSTATUS, 0xC0000066) +#define STATUS_MEMBER_IN_GROUP STATUS_CAST(NTSTATUS, 0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP STATUS_CAST(NTSTATUS, 0xC0000068) +#define STATUS_LAST_ADMIN STATUS_CAST(NTSTATUS, 0xC0000069) +//#define STATUS_WRONG_PASSWORD STATUS_CAST(NTSTATUS,0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD STATUS_CAST(NTSTATUS, 0xC000006B) +#define STATUS_PASSWORD_RESTRICTION STATUS_CAST(NTSTATUS, 0xC000006C) +//#define STATUS_LOGON_FAILURE STATUS_CAST(NTSTATUS,0xC000006D) +//#define STATUS_ACCOUNT_RESTRICTION STATUS_CAST(NTSTATUS,0xC000006E) +#define STATUS_INVALID_LOGON_HOURS STATUS_CAST(NTSTATUS, 0xC000006F) +#define STATUS_INVALID_WORKSTATION STATUS_CAST(NTSTATUS, 0xC0000070) +//#define STATUS_PASSWORD_EXPIRED STATUS_CAST(NTSTATUS,0xC0000071) +//#define STATUS_ACCOUNT_DISABLED STATUS_CAST(NTSTATUS,0xC0000072) +#define STATUS_NONE_MAPPED STATUS_CAST(NTSTATUS, 0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED STATUS_CAST(NTSTATUS, 0xC0000074) +#define STATUS_LUIDS_EXHAUSTED STATUS_CAST(NTSTATUS, 0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY STATUS_CAST(NTSTATUS, 0xC0000076) +#define STATUS_INVALID_ACL STATUS_CAST(NTSTATUS, 0xC0000077) +#define STATUS_INVALID_SID STATUS_CAST(NTSTATUS, 0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR STATUS_CAST(NTSTATUS, 0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT STATUS_CAST(NTSTATUS, 0xC000007B) +#define STATUS_NO_TOKEN STATUS_CAST(NTSTATUS, 0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL STATUS_CAST(NTSTATUS, 0xC000007D) +#define STATUS_RANGE_NOT_LOCKED STATUS_CAST(NTSTATUS, 0xC000007E) +#define STATUS_DISK_FULL STATUS_CAST(NTSTATUS, 0xC000007F) +#define STATUS_SERVER_DISABLED STATUS_CAST(NTSTATUS, 0xC0000080) +#define STATUS_SERVER_NOT_DISABLED STATUS_CAST(NTSTATUS, 0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED STATUS_CAST(NTSTATUS, 0xC0000082) +#define STATUS_GUIDS_EXHAUSTED STATUS_CAST(NTSTATUS, 0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY STATUS_CAST(NTSTATUS, 0xC0000084) +#define STATUS_AGENTS_EXHAUSTED STATUS_CAST(NTSTATUS, 0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL STATUS_CAST(NTSTATUS, 0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED STATUS_CAST(NTSTATUS, 0xC0000087) +#define STATUS_NOT_MAPPED_DATA STATUS_CAST(NTSTATUS, 0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC000008B) +//#define STATUS_ARRAY_BOUNDS_EXCEEDED STATUS_CAST(NTSTATUS,0xC000008C) +//#define STATUS_FLOAT_DENORMAL_OPERAND STATUS_CAST(NTSTATUS,0xC000008D) +//#define STATUS_FLOAT_DIVIDE_BY_ZERO STATUS_CAST(NTSTATUS,0xC000008E) +//#define STATUS_FLOAT_INEXACT_RESULT STATUS_CAST(NTSTATUS,0xC000008F) +//#define STATUS_FLOAT_INVALID_OPERATION STATUS_CAST(NTSTATUS,0xC0000090) +//#define STATUS_FLOAT_OVERFLOW STATUS_CAST(NTSTATUS,0xC0000091) +//#define STATUS_FLOAT_STACK_CHECK STATUS_CAST(NTSTATUS,0xC0000092) +//#define STATUS_FLOAT_UNDERFLOW STATUS_CAST(NTSTATUS,0xC0000093) +//#define STATUS_INTEGER_DIVIDE_BY_ZERO STATUS_CAST(NTSTATUS,0xC0000094) +//#define STATUS_INTEGER_OVERFLOW STATUS_CAST(NTSTATUS,0xC0000095) +//#define STATUS_PRIVILEGED_INSTRUCTION STATUS_CAST(NTSTATUS,0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES STATUS_CAST(NTSTATUS, 0xC0000097) +#define STATUS_FILE_INVALID STATUS_CAST(NTSTATUS, 0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES STATUS_CAST(NTSTATUS, 0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND STATUS_CAST(NTSTATUS, 0xC000009B) +#define STATUS_DEVICE_DATA_ERROR STATUS_CAST(NTSTATUS, 0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED STATUS_CAST(NTSTATUS, 0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE STATUS_CAST(NTSTATUS, 0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE STATUS_CAST(NTSTATUS, 0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED STATUS_CAST(NTSTATUS, 0xC00000A0) +#define STATUS_WORKING_SET_QUOTA STATUS_CAST(NTSTATUS, 0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED STATUS_CAST(NTSTATUS, 0xC00000A2) +#define STATUS_DEVICE_NOT_READY STATUS_CAST(NTSTATUS, 0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES STATUS_CAST(NTSTATUS, 0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL STATUS_CAST(NTSTATUS, 0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS STATUS_CAST(NTSTATUS, 0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS STATUS_CAST(NTSTATUS, 0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE STATUS_CAST(NTSTATUS, 0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD STATUS_CAST(NTSTATUS, 0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT STATUS_CAST(NTSTATUS, 0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE STATUS_CAST(NTSTATUS, 0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE STATUS_CAST(NTSTATUS, 0xC00000AC) +#define STATUS_INVALID_PIPE_STATE STATUS_CAST(NTSTATUS, 0xC00000AD) +#define STATUS_PIPE_BUSY STATUS_CAST(NTSTATUS, 0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION STATUS_CAST(NTSTATUS, 0xC00000AF) +#define STATUS_PIPE_DISCONNECTED STATUS_CAST(NTSTATUS, 0xC00000B0) +#define STATUS_PIPE_CLOSING STATUS_CAST(NTSTATUS, 0xC00000B1) +#define STATUS_PIPE_CONNECTED STATUS_CAST(NTSTATUS, 0xC00000B2) +#define STATUS_PIPE_LISTENING STATUS_CAST(NTSTATUS, 0xC00000B3) +#define STATUS_INVALID_READ_MODE STATUS_CAST(NTSTATUS, 0xC00000B4) +#define STATUS_IO_TIMEOUT STATUS_CAST(NTSTATUS, 0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED STATUS_CAST(NTSTATUS, 0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED STATUS_CAST(NTSTATUS, 0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED STATUS_CAST(NTSTATUS, 0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET STATUS_CAST(NTSTATUS, 0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY STATUS_CAST(NTSTATUS, 0xC00000BA) +#define STATUS_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING STATUS_CAST(NTSTATUS, 0xC00000BC) +#define STATUS_DUPLICATE_NAME STATUS_CAST(NTSTATUS, 0xC00000BD) +#define STATUS_BAD_NETWORK_PATH STATUS_CAST(NTSTATUS, 0xC00000BE) +#define STATUS_NETWORK_BUSY STATUS_CAST(NTSTATUS, 0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST STATUS_CAST(NTSTATUS, 0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS STATUS_CAST(NTSTATUS, 0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR STATUS_CAST(NTSTATUS, 0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE STATUS_CAST(NTSTATUS, 0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR STATUS_CAST(NTSTATUS, 0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER STATUS_CAST(NTSTATUS, 0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL STATUS_CAST(NTSTATUS, 0xC00000C6) +#define STATUS_NO_SPOOL_SPACE STATUS_CAST(NTSTATUS, 0xC00000C7) +#define STATUS_PRINT_CANCELLED STATUS_CAST(NTSTATUS, 0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED STATUS_CAST(NTSTATUS, 0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED STATUS_CAST(NTSTATUS, 0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE STATUS_CAST(NTSTATUS, 0xC00000CB) +#define STATUS_BAD_NETWORK_NAME STATUS_CAST(NTSTATUS, 0xC00000CC) +#define STATUS_TOO_MANY_NAMES STATUS_CAST(NTSTATUS, 0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS STATUS_CAST(NTSTATUS, 0xC00000CE) +#define STATUS_SHARING_PAUSED STATUS_CAST(NTSTATUS, 0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED STATUS_CAST(NTSTATUS, 0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED STATUS_CAST(NTSTATUS, 0xC00000D1) +#define STATUS_NET_WRITE_FAULT STATUS_CAST(NTSTATUS, 0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT STATUS_CAST(NTSTATUS, 0xC00000D3) +#define STATUS_NOT_SAME_DEVICE STATUS_CAST(NTSTATUS, 0xC00000D4) +#define STATUS_FILE_RENAMED STATUS_CAST(NTSTATUS, 0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED STATUS_CAST(NTSTATUS, 0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT STATUS_CAST(NTSTATUS, 0xC00000D7) +#define STATUS_CANT_WAIT STATUS_CAST(NTSTATUS, 0xC00000D8) +#define STATUS_PIPE_EMPTY STATUS_CAST(NTSTATUS, 0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO STATUS_CAST(NTSTATUS, 0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF STATUS_CAST(NTSTATUS, 0xC00000DB) +#define STATUS_INVALID_SERVER_STATE STATUS_CAST(NTSTATUS, 0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE STATUS_CAST(NTSTATUS, 0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE STATUS_CAST(NTSTATUS, 0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN STATUS_CAST(NTSTATUS, 0xC00000DF) +#define STATUS_DOMAIN_EXISTS STATUS_CAST(NTSTATUS, 0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED STATUS_CAST(NTSTATUS, 0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED STATUS_CAST(NTSTATUS, 0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL STATUS_CAST(NTSTATUS, 0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION STATUS_CAST(NTSTATUS, 0xC00000E4) +#define STATUS_INTERNAL_ERROR STATUS_CAST(NTSTATUS, 0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED STATUS_CAST(NTSTATUS, 0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT STATUS_CAST(NTSTATUS, 0xC00000E7) +#define STATUS_INVALID_USER_BUFFER STATUS_CAST(NTSTATUS, 0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR STATUS_CAST(NTSTATUS, 0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR STATUS_CAST(NTSTATUS, 0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR STATUS_CAST(NTSTATUS, 0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR STATUS_CAST(NTSTATUS, 0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS STATUS_CAST(NTSTATUS, 0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS STATUS_CAST(NTSTATUS, 0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 STATUS_CAST(NTSTATUS, 0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 STATUS_CAST(NTSTATUS, 0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 STATUS_CAST(NTSTATUS, 0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 STATUS_CAST(NTSTATUS, 0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 STATUS_CAST(NTSTATUS, 0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 STATUS_CAST(NTSTATUS, 0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 STATUS_CAST(NTSTATUS, 0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 STATUS_CAST(NTSTATUS, 0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 STATUS_CAST(NTSTATUS, 0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 STATUS_CAST(NTSTATUS, 0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 STATUS_CAST(NTSTATUS, 0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 STATUS_CAST(NTSTATUS, 0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED STATUS_CAST(NTSTATUS, 0xC00000FB) +#define STATUS_REDIRECTOR_STARTED STATUS_CAST(NTSTATUS, 0xC00000FC) +//#define STATUS_STACK_OVERFLOW STATUS_CAST(NTSTATUS,0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE STATUS_CAST(NTSTATUS, 0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE STATUS_CAST(NTSTATUS, 0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY STATUS_CAST(NTSTATUS, 0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR STATUS_CAST(NTSTATUS, 0xC0000102) +#define STATUS_NOT_A_DIRECTORY STATUS_CAST(NTSTATUS, 0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE STATUS_CAST(NTSTATUS, 0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION STATUS_CAST(NTSTATUS, 0xC0000105) +#define STATUS_NAME_TOO_LONG STATUS_CAST(NTSTATUS, 0xC0000106) +#define STATUS_FILES_OPEN STATUS_CAST(NTSTATUS, 0xC0000107) +#define STATUS_CONNECTION_IN_USE STATUS_CAST(NTSTATUS, 0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING STATUS_CAST(NTSTATUS, 0xC000010A) +#define STATUS_INVALID_LOGON_TYPE STATUS_CAST(NTSTATUS, 0xC000010B) +#define STATUS_NO_GUID_TRANSLATION STATUS_CAST(NTSTATUS, 0xC000010C) +#define STATUS_CANNOT_IMPERSONATE STATUS_CAST(NTSTATUS, 0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED STATUS_CAST(NTSTATUS, 0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT STATUS_CAST(NTSTATUS, 0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST STATUS_CAST(NTSTATUS, 0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED STATUS_CAST(NTSTATUS, 0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER STATUS_CAST(NTSTATUS, 0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND STATUS_CAST(NTSTATUS, 0xC0000113) +#define STATUS_ABIOS_INVALID_LID STATUS_CAST(NTSTATUS, 0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE STATUS_CAST(NTSTATUS, 0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR STATUS_CAST(NTSTATUS, 0xC0000116) +#define STATUS_NO_LDT STATUS_CAST(NTSTATUS, 0xC0000117) +#define STATUS_INVALID_LDT_SIZE STATUS_CAST(NTSTATUS, 0xC0000118) +#define STATUS_INVALID_LDT_OFFSET STATUS_CAST(NTSTATUS, 0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR STATUS_CAST(NTSTATUS, 0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT STATUS_CAST(NTSTATUS, 0xC000011B) +#define STATUS_RXACT_INVALID_STATE STATUS_CAST(NTSTATUS, 0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE STATUS_CAST(NTSTATUS, 0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO STATUS_CAST(NTSTATUS, 0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES STATUS_CAST(NTSTATUS, 0xC000011F) +#define STATUS_CANCELLED STATUS_CAST(NTSTATUS, 0xC0000120) +#define STATUS_CANNOT_DELETE STATUS_CAST(NTSTATUS, 0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME STATUS_CAST(NTSTATUS, 0xC0000122) +#define STATUS_FILE_DELETED STATUS_CAST(NTSTATUS, 0xC0000123) +#define STATUS_SPECIAL_ACCOUNT STATUS_CAST(NTSTATUS, 0xC0000124) +#define STATUS_SPECIAL_GROUP STATUS_CAST(NTSTATUS, 0xC0000125) +#define STATUS_SPECIAL_USER STATUS_CAST(NTSTATUS, 0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP STATUS_CAST(NTSTATUS, 0xC0000127) +#define STATUS_FILE_CLOSED STATUS_CAST(NTSTATUS, 0xC0000128) +#define STATUS_TOO_MANY_THREADS STATUS_CAST(NTSTATUS, 0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS STATUS_CAST(NTSTATUS, 0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE STATUS_CAST(NTSTATUS, 0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC000012C) +#define STATUS_COMMITMENT_LIMIT STATUS_CAST(NTSTATUS, 0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT STATUS_CAST(NTSTATUS, 0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ STATUS_CAST(NTSTATUS, 0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT STATUS_CAST(NTSTATUS, 0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 STATUS_CAST(NTSTATUS, 0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT STATUS_CAST(NTSTATUS, 0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC STATUS_CAST(NTSTATUS, 0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED STATUS_CAST(NTSTATUS, 0xC0000134) +//#define STATUS_DLL_NOT_FOUND STATUS_CAST(NTSTATUS,0xC0000135) +#define STATUS_OPEN_FAILED STATUS_CAST(NTSTATUS, 0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED STATUS_CAST(NTSTATUS, 0xC0000137) +//#define STATUS_ORDINAL_NOT_FOUND STATUS_CAST(NTSTATUS,0xC0000138) +//#define STATUS_ENTRYPOINT_NOT_FOUND STATUS_CAST(NTSTATUS,0xC0000139) +//#define STATUS_CONTROL_C_EXIT STATUS_CAST(NTSTATUS,0xC000013A) +#define STATUS_LOCAL_DISCONNECT STATUS_CAST(NTSTATUS, 0xC000013B) +#define STATUS_REMOTE_DISCONNECT STATUS_CAST(NTSTATUS, 0xC000013C) +#define STATUS_REMOTE_RESOURCES STATUS_CAST(NTSTATUS, 0xC000013D) +#define STATUS_LINK_FAILED STATUS_CAST(NTSTATUS, 0xC000013E) +#define STATUS_LINK_TIMEOUT STATUS_CAST(NTSTATUS, 0xC000013F) +#define STATUS_INVALID_CONNECTION STATUS_CAST(NTSTATUS, 0xC0000140) +#define STATUS_INVALID_ADDRESS STATUS_CAST(NTSTATUS, 0xC0000141) +//#define STATUS_DLL_INIT_FAILED STATUS_CAST(NTSTATUS,0xC0000142) +#define STATUS_MISSING_SYSTEMFILE STATUS_CAST(NTSTATUS, 0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION STATUS_CAST(NTSTATUS, 0xC0000144) +#define STATUS_APP_INIT_FAILURE STATUS_CAST(NTSTATUS, 0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED STATUS_CAST(NTSTATUS, 0xC0000146) +#define STATUS_NO_PAGEFILE STATUS_CAST(NTSTATUS, 0xC0000147) +#define STATUS_INVALID_LEVEL STATUS_CAST(NTSTATUS, 0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE STATUS_CAST(NTSTATUS, 0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT STATUS_CAST(NTSTATUS, 0xC000014A) +#define STATUS_PIPE_BROKEN STATUS_CAST(NTSTATUS, 0xC000014B) +#define STATUS_REGISTRY_CORRUPT STATUS_CAST(NTSTATUS, 0xC000014C) +#define STATUS_REGISTRY_IO_FAILED STATUS_CAST(NTSTATUS, 0xC000014D) +#define STATUS_NO_EVENT_PAIR STATUS_CAST(NTSTATUS, 0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME STATUS_CAST(NTSTATUS, 0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED STATUS_CAST(NTSTATUS, 0xC0000150) +#define STATUS_NO_SUCH_ALIAS STATUS_CAST(NTSTATUS, 0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS STATUS_CAST(NTSTATUS, 0xC0000152) +#define STATUS_MEMBER_IN_ALIAS STATUS_CAST(NTSTATUS, 0xC0000153) +#define STATUS_ALIAS_EXISTS STATUS_CAST(NTSTATUS, 0xC0000154) +#define STATUS_LOGON_NOT_GRANTED STATUS_CAST(NTSTATUS, 0xC0000155) +#define STATUS_TOO_MANY_SECRETS STATUS_CAST(NTSTATUS, 0xC0000156) +#define STATUS_SECRET_TOO_LONG STATUS_CAST(NTSTATUS, 0xC0000157) +#define STATUS_INTERNAL_DB_ERROR STATUS_CAST(NTSTATUS, 0xC0000158) +#define STATUS_FULLSCREEN_MODE STATUS_CAST(NTSTATUS, 0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS STATUS_CAST(NTSTATUS, 0xC000015A) +//#define STATUS_LOGON_TYPE_NOT_GRANTED STATUS_CAST(NTSTATUS,0xC000015B) +#define STATUS_NOT_REGISTRY_FILE STATUS_CAST(NTSTATUS, 0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED STATUS_CAST(NTSTATUS, 0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR STATUS_CAST(NTSTATUS, 0xC000015E) +#define STATUS_FT_MISSING_MEMBER STATUS_CAST(NTSTATUS, 0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY STATUS_CAST(NTSTATUS, 0xC0000160) +#define STATUS_ILLEGAL_CHARACTER STATUS_CAST(NTSTATUS, 0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER STATUS_CAST(NTSTATUS, 0xC0000162) +#define STATUS_UNDEFINED_CHARACTER STATUS_CAST(NTSTATUS, 0xC0000163) +#define STATUS_FLOPPY_VOLUME STATUS_CAST(NTSTATUS, 0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER STATUS_CAST(NTSTATUS, 0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR STATUS_CAST(NTSTATUS, 0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS STATUS_CAST(NTSTATUS, 0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED STATUS_CAST(NTSTATUS, 0xC0000169) +#define STATUS_DISK_OPERATION_FAILED STATUS_CAST(NTSTATUS, 0xC000016A) +#define STATUS_DISK_RESET_FAILED STATUS_CAST(NTSTATUS, 0xC000016B) +#define STATUS_SHARED_IRQ_BUSY STATUS_CAST(NTSTATUS, 0xC000016C) +#define STATUS_FT_ORPHANING STATUS_CAST(NTSTATUS, 0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT STATUS_CAST(NTSTATUS, 0xC000016E) + +#define STATUS_PARTITION_FAILURE STATUS_CAST(NTSTATUS, 0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH STATUS_CAST(NTSTATUS, 0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED STATUS_CAST(NTSTATUS, 0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA STATUS_CAST(NTSTATUS, 0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA STATUS_CAST(NTSTATUS, 0xC0000176) +#define STATUS_EOM_OVERFLOW STATUS_CAST(NTSTATUS, 0xC0000177) +#define STATUS_NO_MEDIA STATUS_CAST(NTSTATUS, 0xC0000178) +#define STATUS_NO_SUCH_MEMBER STATUS_CAST(NTSTATUS, 0xC000017A) +#define STATUS_INVALID_MEMBER STATUS_CAST(NTSTATUS, 0xC000017B) +#define STATUS_KEY_DELETED STATUS_CAST(NTSTATUS, 0xC000017C) +#define STATUS_NO_LOG_SPACE STATUS_CAST(NTSTATUS, 0xC000017D) +#define STATUS_TOO_MANY_SIDS STATUS_CAST(NTSTATUS, 0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED STATUS_CAST(NTSTATUS, 0xC000017F) +#define STATUS_KEY_HAS_CHILDREN STATUS_CAST(NTSTATUS, 0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE STATUS_CAST(NTSTATUS, 0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR STATUS_CAST(NTSTATUS, 0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR STATUS_CAST(NTSTATUS, 0xC0000183) +#define STATUS_INVALID_DEVICE_STATE STATUS_CAST(NTSTATUS, 0xC0000184) +#define STATUS_IO_DEVICE_ERROR STATUS_CAST(NTSTATUS, 0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR STATUS_CAST(NTSTATUS, 0xC0000186) +#define STATUS_BACKUP_CONTROLLER STATUS_CAST(NTSTATUS, 0xC0000187) +#define STATUS_LOG_FILE_FULL STATUS_CAST(NTSTATUS, 0xC0000188) +#define STATUS_TOO_LATE STATUS_CAST(NTSTATUS, 0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET STATUS_CAST(NTSTATUS, 0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT STATUS_CAST(NTSTATUS, 0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE STATUS_CAST(NTSTATUS, 0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE STATUS_CAST(NTSTATUS, 0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT STATUS_CAST(NTSTATUS, 0xC000018E) +#define STATUS_EVENTLOG_CANT_START STATUS_CAST(NTSTATUS, 0xC000018F) +#define STATUS_TRUST_FAILURE STATUS_CAST(NTSTATUS, 0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED STATUS_CAST(NTSTATUS, 0xC0000192) +//#define STATUS_ACCOUNT_EXPIRED STATUS_CAST(NTSTATUS,0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK STATUS_CAST(NTSTATUS, 0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT STATUS_CAST(NTSTATUS, 0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT STATUS_CAST(NTSTATUS, 0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED STATUS_CAST(NTSTATUS, 0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT STATUS_CAST(NTSTATUS, 0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT STATUS_CAST(NTSTATUS, 0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT STATUS_CAST(NTSTATUS, 0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT STATUS_CAST(NTSTATUS, 0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED STATUS_CAST(NTSTATUS, 0xC000019C) +#define STATUS_NO_USER_SESSION_KEY STATUS_CAST(NTSTATUS, 0xC0000202) +#define STATUS_USER_SESSION_DELETED STATUS_CAST(NTSTATUS, 0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES STATUS_CAST(NTSTATUS, 0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE STATUS_CAST(NTSTATUS, 0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT STATUS_CAST(NTSTATUS, 0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD STATUS_CAST(NTSTATUS, 0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES STATUS_CAST(NTSTATUS, 0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS STATUS_CAST(NTSTATUS, 0xC000020A) +#define STATUS_ADDRESS_CLOSED STATUS_CAST(NTSTATUS, 0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED STATUS_CAST(NTSTATUS, 0xC000020C) +#define STATUS_CONNECTION_RESET STATUS_CAST(NTSTATUS, 0xC000020D) +#define STATUS_TOO_MANY_NODES STATUS_CAST(NTSTATUS, 0xC000020E) +#define STATUS_TRANSACTION_ABORTED STATUS_CAST(NTSTATUS, 0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT STATUS_CAST(NTSTATUS, 0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE STATUS_CAST(NTSTATUS, 0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH STATUS_CAST(NTSTATUS, 0xC0000212) +#define STATUS_TRANSACTION_RESPONDED STATUS_CAST(NTSTATUS, 0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID STATUS_CAST(NTSTATUS, 0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE STATUS_CAST(NTSTATUS, 0xC0000215) +#define STATUS_NOT_SERVER_SESSION STATUS_CAST(NTSTATUS, 0xC0000216) +#define STATUS_NOT_CLIENT_SESSION STATUS_CAST(NTSTATUS, 0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE STATUS_CAST(NTSTATUS, 0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED STATUS_CAST(NTSTATUS, 0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED STATUS_CAST(NTSTATUS, 0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED STATUS_CAST(NTSTATUS, 0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND STATUS_CAST(NTSTATUS, 0xC000021C) +#define STATUS_VDM_HARD_ERROR STATUS_CAST(NTSTATUS, 0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT STATUS_CAST(NTSTATUS, 0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH STATUS_CAST(NTSTATUS, 0xC000021F) +#define STATUS_MAPPED_ALIGNMENT STATUS_CAST(NTSTATUS, 0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH STATUS_CAST(NTSTATUS, 0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA STATUS_CAST(NTSTATUS, 0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID STATUS_CAST(NTSTATUS, 0xC0000223) +//#define STATUS_PASSWORD_MUST_CHANGE STATUS_CAST(NTSTATUS,0xC0000224) +#define STATUS_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000225) +#define STATUS_NOT_TINY_STREAM STATUS_CAST(NTSTATUS, 0xC0000226) +#define STATUS_RECOVERY_FAILURE STATUS_CAST(NTSTATUS, 0xC0000227) +#define STATUS_STACK_OVERFLOW_READ STATUS_CAST(NTSTATUS, 0xC0000228) +#define STATUS_FAIL_CHECK STATUS_CAST(NTSTATUS, 0xC0000229) +#define STATUS_DUPLICATE_OBJECTID STATUS_CAST(NTSTATUS, 0xC000022A) +#define STATUS_OBJECTID_EXISTS STATUS_CAST(NTSTATUS, 0xC000022B) +#define STATUS_CONVERT_TO_LARGE STATUS_CAST(NTSTATUS, 0xC000022C) +#define STATUS_RETRY STATUS_CAST(NTSTATUS, 0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE STATUS_CAST(NTSTATUS, 0xC000022E) +#define STATUS_ALLOCATE_BUCKET STATUS_CAST(NTSTATUS, 0xC000022F) +#define STATUS_PROPSET_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000230) +#define STATUS_MARSHALL_OVERFLOW STATUS_CAST(NTSTATUS, 0xC0000231) +#define STATUS_INVALID_VARIANT STATUS_CAST(NTSTATUS, 0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000233) +//#define STATUS_ACCOUNT_LOCKED_OUT STATUS_CAST(NTSTATUS,0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE STATUS_CAST(NTSTATUS, 0xC0000235) +#define STATUS_CONNECTION_REFUSED STATUS_CAST(NTSTATUS, 0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT STATUS_CAST(NTSTATUS, 0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED STATUS_CAST(NTSTATUS, 0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED STATUS_CAST(NTSTATUS, 0xC0000239) +#define STATUS_CONNECTION_INVALID STATUS_CAST(NTSTATUS, 0xC000023A) +#define STATUS_CONNECTION_ACTIVE STATUS_CAST(NTSTATUS, 0xC000023B) +#define STATUS_NETWORK_UNREACHABLE STATUS_CAST(NTSTATUS, 0xC000023C) +#define STATUS_HOST_UNREACHABLE STATUS_CAST(NTSTATUS, 0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE STATUS_CAST(NTSTATUS, 0xC000023E) +#define STATUS_PORT_UNREACHABLE STATUS_CAST(NTSTATUS, 0xC000023F) +#define STATUS_REQUEST_ABORTED STATUS_CAST(NTSTATUS, 0xC0000240) +#define STATUS_CONNECTION_ABORTED STATUS_CAST(NTSTATUS, 0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER STATUS_CAST(NTSTATUS, 0xC0000242) +#define STATUS_USER_MAPPED_FILE STATUS_CAST(NTSTATUS, 0xC0000243) +#define STATUS_AUDIT_FAILED STATUS_CAST(NTSTATUS, 0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET STATUS_CAST(NTSTATUS, 0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT STATUS_CAST(NTSTATUS, 0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION STATUS_CAST(NTSTATUS, 0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION STATUS_CAST(NTSTATUS, 0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH STATUS_CAST(NTSTATUS, 0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO STATUS_CAST(NTSTATUS, 0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT STATUS_CAST(NTSTATUS, 0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT STATUS_CAST(NTSTATUS, 0xC0000252) +#define STATUS_LPC_REPLY_LOST STATUS_CAST(NTSTATUS, 0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 STATUS_CAST(NTSTATUS, 0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 STATUS_CAST(NTSTATUS, 0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT STATUS_CAST(NTSTATUS, 0xC0000256) +#define STATUS_PATH_NOT_COVERED STATUS_CAST(NTSTATUS, 0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE STATUS_CAST(NTSTATUS, 0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000259) +#define STATUS_PWD_TOO_SHORT STATUS_CAST(NTSTATUS, 0xC000025A) +#define STATUS_PWD_TOO_RECENT STATUS_CAST(NTSTATUS, 0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT STATUS_CAST(NTSTATUS, 0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE STATUS_CAST(NTSTATUS, 0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION STATUS_CAST(NTSTATUS, 0xC000025F) +#define STATUS_INVALID_HW_PROFILE STATUS_CAST(NTSTATUS, 0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH STATUS_CAST(NTSTATUS, 0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED STATUS_CAST(NTSTATUS, 0xC0000264) +#define STATUS_TOO_MANY_LINKS STATUS_CAST(NTSTATUS, 0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT STATUS_CAST(NTSTATUS, 0xC0000266) +#define STATUS_FILE_IS_OFFLINE STATUS_CAST(NTSTATUS, 0xC0000267) +#define STATUS_EVALUATION_EXPIRATION STATUS_CAST(NTSTATUS, 0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION STATUS_CAST(NTSTATUS, 0xC0000269) +#define STATUS_LICENSE_VIOLATION STATUS_CAST(NTSTATUS, 0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF STATUS_CAST(NTSTATUS, 0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD STATUS_CAST(NTSTATUS, 0xC000026C) +#define STATUS_DFS_UNAVAILABLE STATUS_CAST(NTSTATUS, 0xC000026D) +#define STATUS_VOLUME_DISMOUNTED STATUS_CAST(NTSTATUS, 0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR STATUS_CAST(NTSTATUS, 0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK STATUS_CAST(NTSTATUS, 0xC0000270) +#define STATUS_VALIDATE_CONTINUE STATUS_CAST(NTSTATUS, 0xC0000271) +#define STATUS_NO_MATCH STATUS_CAST(NTSTATUS, 0xC0000272) +#define STATUS_NO_MORE_MATCHES STATUS_CAST(NTSTATUS, 0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT STATUS_CAST(NTSTATUS, 0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID STATUS_CAST(NTSTATUS, 0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH STATUS_CAST(NTSTATUS, 0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID STATUS_CAST(NTSTATUS, 0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED STATUS_CAST(NTSTATUS, 0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED STATUS_CAST(NTSTATUS, 0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT STATUS_CAST(NTSTATUS, 0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT STATUS_CAST(NTSTATUS, 0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY STATUS_CAST(NTSTATUS, 0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL STATUS_CAST(NTSTATUS, 0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS STATUS_CAST(NTSTATUS, 0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT STATUS_CAST(NTSTATUS, 0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED STATUS_CAST(NTSTATUS, 0xC0000287) +#define STATUS_ENCRYPTION_FAILED STATUS_CAST(NTSTATUS, 0xC000028A) +#define STATUS_DECRYPTION_FAILED STATUS_CAST(NTSTATUS, 0xC000028B) +#define STATUS_RANGE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC000028C) +#define STATUS_NO_RECOVERY_POLICY STATUS_CAST(NTSTATUS, 0xC000028D) +#define STATUS_NO_EFS STATUS_CAST(NTSTATUS, 0xC000028E) +#define STATUS_WRONG_EFS STATUS_CAST(NTSTATUS, 0xC000028F) +#define STATUS_NO_USER_KEYS STATUS_CAST(NTSTATUS, 0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED STATUS_CAST(NTSTATUS, 0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT STATUS_CAST(NTSTATUS, 0xC0000292) +#define STATUS_FILE_ENCRYPTED STATUS_CAST(NTSTATUS, 0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0000297) +#define STATUS_WMI_TRY_AGAIN STATUS_CAST(NTSTATUS, 0xC0000298) +#define STATUS_SHARED_POLICY STATUS_CAST(NTSTATUS, 0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS STATUS_CAST(NTSTATUS, 0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED STATUS_CAST(NTSTATUS, 0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE STATUS_CAST(NTSTATUS, 0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR STATUS_CAST(NTSTATUS, 0xC000029E) +#define STATUS_NO_TRACKING_SERVICE STATUS_CAST(NTSTATUS, 0xC000029F) +#define STATUS_SERVER_SID_MISMATCH STATUS_CAST(NTSTATUS, 0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE STATUS_CAST(NTSTATUS, 0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX STATUS_CAST(NTSTATUS, 0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED STATUS_CAST(NTSTATUS, 0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS STATUS_CAST(NTSTATUS, 0xC00002A4) +#define STATUS_DS_BUSY STATUS_CAST(NTSTATUS, 0xC00002A5) +#define STATUS_DS_UNAVAILABLE STATUS_CAST(NTSTATUS, 0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED STATUS_CAST(NTSTATUS, 0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS STATUS_CAST(NTSTATUS, 0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER STATUS_CAST(NTSTATUS, 0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR STATUS_CAST(NTSTATUS, 0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION STATUS_CAST(NTSTATUS, 0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF STATUS_CAST(NTSTATUS, 0xC00002AC) +#define STATUS_DS_CANT_ON_RDN STATUS_CAST(NTSTATUS, 0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS STATUS_CAST(NTSTATUS, 0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED STATUS_CAST(NTSTATUS, 0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE STATUS_CAST(NTSTATUS, 0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED STATUS_CAST(NTSTATUS, 0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT STATUS_CAST(NTSTATUS, 0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY STATUS_CAST(NTSTATUS, 0xC00002B3) +//#define STATUS_FLOAT_MULTIPLE_FAULTS STATUS_CAST(NTSTATUS,0xC00002B4) +//#define STATUS_FLOAT_MULTIPLE_TRAPS STATUS_CAST(NTSTATUS,0xC00002B5) +#define STATUS_DEVICE_REMOVED STATUS_CAST(NTSTATUS, 0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS STATUS_CAST(NTSTATUS, 0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE STATUS_CAST(NTSTATUS, 0xC00002B8) +#define STATUS_NOINTERFACE STATUS_CAST(NTSTATUS, 0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED STATUS_CAST(NTSTATUS, 0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP STATUS_CAST(NTSTATUS, 0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED STATUS_CAST(NTSTATUS, 0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE STATUS_CAST(NTSTATUS, 0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR STATUS_CAST(NTSTATUS, 0xC00002C5) +#define STATUS_WMI_READ_ONLY STATUS_CAST(NTSTATUS, 0xC00002C6) +#define STATUS_WMI_SET_FAILURE STATUS_CAST(NTSTATUS, 0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM STATUS_CAST(NTSTATUS, 0xC00002C8) +//#define STATUS_REG_NAT_CONSUMPTION STATUS_CAST(NTSTATUS,0xC00002C9) +#define STATUS_TRANSPORT_FULL STATUS_CAST(NTSTATUS, 0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE STATUS_CAST(NTSTATUS, 0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED STATUS_CAST(NTSTATUS, 0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION STATUS_CAST(NTSTATUS, 0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION STATUS_CAST(NTSTATUS, 0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED STATUS_CAST(NTSTATUS, 0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID STATUS_CAST(NTSTATUS, 0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE STATUS_CAST(NTSTATUS, 0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED STATUS_CAST(NTSTATUS, 0xC00002D2) +#define STATUS_POWER_STATE_INVALID STATUS_CAST(NTSTATUS, 0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE STATUS_CAST(NTSTATUS, 0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN STATUS_CAST(NTSTATUS, 0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN STATUS_CAST(NTSTATUS, 0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER STATUS_CAST(NTSTATUS, 0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER STATUS_CAST(NTSTATUS, 0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER STATUS_CAST(NTSTATUS, 0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER STATUS_CAST(NTSTATUS, 0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER STATUS_CAST(NTSTATUS, 0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS STATUS_CAST(NTSTATUS, 0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0xC00002DD) +#define STATUS_INSUFFICIENT_POWER STATUS_CAST(NTSTATUS, 0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD STATUS_CAST(NTSTATUS, 0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY STATUS_CAST(NTSTATUS, 0xC00002E0) +#define STATUS_DS_CANT_START STATUS_CAST(NTSTATUS, 0xC00002E1) +#define STATUS_DS_INIT_FAILURE STATUS_CAST(NTSTATUS, 0xC00002E2) +#define STATUS_SAM_INIT_FAILURE STATUS_CAST(NTSTATUS, 0xC00002E3) +#define STATUS_DS_GC_REQUIRED STATUS_CAST(NTSTATUS, 0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY STATUS_CAST(NTSTATUS, 0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS STATUS_CAST(NTSTATUS, 0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION STATUS_CAST(NTSTATUS, 0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED STATUS_CAST(NTSTATUS, 0xC00002E9) +#define STATUS_CANNOT_MAKE STATUS_CAST(NTSTATUS, 0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN STATUS_CAST(NTSTATUS, 0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE STATUS_CAST(NTSTATUS, 0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE STATUS_CAST(NTSTATUS, 0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED STATUS_CAST(NTSTATUS, 0xC00002EE) +#define STATUS_NO_TGT_REPLY STATUS_CAST(NTSTATUS, 0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC00002F0) +#define STATUS_NO_IP_ADDRESSES STATUS_CAST(NTSTATUS, 0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE STATUS_CAST(NTSTATUS, 0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID STATUS_CAST(NTSTATUS, 0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED STATUS_CAST(NTSTATUS, 0xC00002F4) +#define STATUS_MUST_BE_KDC STATUS_CAST(NTSTATUS, 0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS STATUS_CAST(NTSTATUS, 0xC00002F7) +#define STATUS_NO_PA_DATA STATUS_CAST(NTSTATUS, 0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH STATUS_CAST(NTSTATUS, 0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED STATUS_CAST(NTSTATUS, 0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST STATUS_CAST(NTSTATUS, 0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER STATUS_CAST(NTSTATUS, 0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE STATUS_CAST(NTSTATUS, 0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS STATUS_CAST(NTSTATUS, 0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS STATUS_CAST(NTSTATUS, 0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS STATUS_CAST(NTSTATUS, 0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED STATUS_CAST(NTSTATUS, 0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED STATUS_CAST(NTSTATUS, 0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED STATUS_CAST(NTSTATUS, 0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED STATUS_CAST(NTSTATUS, 0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE STATUS_CAST(NTSTATUS, 0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE STATUS_CAST(NTSTATUS, 0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT STATUS_CAST(NTSTATUS, 0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED STATUS_CAST(NTSTATUS, 0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR STATUS_CAST(NTSTATUS, 0xC0000309) +#define STATUS_CSS_REGION_MISMATCH STATUS_CAST(NTSTATUS, 0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED STATUS_CAST(NTSTATUS, 0xC000030B) +#define STATUS_PKINIT_FAILURE STATUS_CAST(NTSTATUS, 0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE STATUS_CAST(NTSTATUS, 0xC0000321) +#define STATUS_NO_KERB_KEY STATUS_CAST(NTSTATUS, 0xC0000322) +#define STATUS_HOST_DOWN STATUS_CAST(NTSTATUS, 0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH STATUS_CAST(NTSTATUS, 0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG STATUS_CAST(NTSTATUS, 0xC0000352) +#define STATUS_PORT_NOT_SET STATUS_CAST(NTSTATUS, 0xC0000353) +#define STATUS_DEBUGGER_INACTIVE STATUS_CAST(NTSTATUS, 0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE STATUS_CAST(NTSTATUS, 0xC0000355) +#define STATUS_AUDITING_DISABLED STATUS_CAST(NTSTATUS, 0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT STATUS_CAST(NTSTATUS, 0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER STATUS_CAST(NTSTATUS, 0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 STATUS_CAST(NTSTATUS, 0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 STATUS_CAST(NTSTATUS, 0xC000035A) +#define STATUS_BAD_BINDINGS STATUS_CAST(NTSTATUS, 0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED STATUS_CAST(NTSTATUS, 0xC000035C) +#define STATUS_APPHELP_BLOCK STATUS_CAST(NTSTATUS, 0xC000035D) +#define STATUS_ALL_SIDS_FILTERED STATUS_CAST(NTSTATUS, 0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER STATUS_CAST(NTSTATUS, 0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT STATUS_CAST(NTSTATUS, 0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH STATUS_CAST(NTSTATUS, 0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER STATUS_CAST(NTSTATUS, 0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER STATUS_CAST(NTSTATUS, 0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY STATUS_CAST(NTSTATUS, 0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR STATUS_CAST(NTSTATUS, 0xC0000366) +#define STATUS_WAIT_FOR_OPLOCK STATUS_CAST(NTSTATUS, 0x00000367) +#define STATUS_MOUNT_POINT_NOT_RESOLVED STATUS_CAST(NTSTATUS, 0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER STATUS_CAST(NTSTATUS, 0xC0000369) +/* The following is not a typo. It's the same spelling as in the Microsoft headers */ +#define STATUS_MCA_OCCURED STATUS_CAST(NTSTATUS, 0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL STATUS_CAST(NTSTATUS, 0xC000036B) +#define STATUS_DRIVER_BLOCKED STATUS_CAST(NTSTATUS, 0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR STATUS_CAST(NTSTATUS, 0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE STATUS_CAST(NTSTATUS, 0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL STATUS_CAST(NTSTATUS, 0xC000036F) +#define STATUS_SMARTCARD_WRONG_PIN STATUS_CAST(NTSTATUS, 0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED STATUS_CAST(NTSTATUS, 0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED STATUS_CAST(NTSTATUS, 0xC0000382) +#define STATUS_SMARTCARD_NO_CARD STATUS_CAST(NTSTATUS, 0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER STATUS_CAST(NTSTATUS, 0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE STATUS_CAST(NTSTATUS, 0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET STATUS_CAST(NTSTATUS, 0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR STATUS_CAST(NTSTATUS, 0xC0000387) +//#define STATUS_DOWNGRADE_DETECTED STATUS_CAST(NTSTATUS,0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED STATUS_CAST(NTSTATUS, 0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED STATUS_CAST(NTSTATUS, 0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C STATUS_CAST(NTSTATUS, 0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE STATUS_CAST(NTSTATUS, 0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED STATUS_CAST(NTSTATUS, 0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD STATUS_CAST(NTSTATUS, 0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT STATUS_CAST(NTSTATUS, 0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE STATUS_CAST(NTSTATUS, 0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND STATUS_CAST(NTSTATUS, 0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR STATUS_CAST(NTSTATUS, 0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE STATUS_CAST(NTSTATUS, 0xC0000407) +#define STATUS_USER2USER_REQUIRED STATUS_CAST(NTSTATUS, 0xC0000408) +//#define STATUS_STACK_BUFFER_OVERRUN STATUS_CAST(NTSTATUS,0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT STATUS_CAST(NTSTATUS, 0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE STATUS_CAST(NTSTATUS, 0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC STATUS_CAST(NTSTATUS, 0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC STATUS_CAST(NTSTATUS, 0xC000040D) +#define STATUS_KDC_CERT_EXPIRED STATUS_CAST(NTSTATUS, 0xC000040E) +#define STATUS_KDC_CERT_REVOKED STATUS_CAST(NTSTATUS, 0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED STATUS_CAST(NTSTATUS, 0xC0000410) +#define STATUS_HIBERNATION_FAILURE STATUS_CAST(NTSTATUS, 0xC0000411) +#define STATUS_DELAY_LOAD_FAILED STATUS_CAST(NTSTATUS, 0xC0000412) +//#define STATUS_AUTHENTICATION_FIREWALL_FAILED STATUS_CAST(NTSTATUS,0xC0000413) +#define STATUS_VDM_DISALLOWED STATUS_CAST(NTSTATUS, 0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD STATUS_CAST(NTSTATUS, 0xC0000415) +//#define STATUS_INVALID_CRUNTIME_PARAMETER STATUS_CAST(NTSTATUS,0xC0000417) +//#define STATUS_ASSERTION_FAILURE STATUS_CAST(NTSTATUS,0xC0000420L) +#define STATUS_CALLBACK_POP_STACK STATUS_CAST(NTSTATUS, 0xC0000423) +#define STATUS_WOW_ASSERTION STATUS_CAST(NTSTATUS, 0xC0009898) + +#define RPC_NT_INVALID_STRING_BINDING STATUS_CAST(NTSTATUS, 0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING STATUS_CAST(NTSTATUS, 0xC0020002) +#define RPC_NT_INVALID_BINDING STATUS_CAST(NTSTATUS, 0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED STATUS_CAST(NTSTATUS, 0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ STATUS_CAST(NTSTATUS, 0xC0020005) +#define RPC_NT_INVALID_STRING_UUID STATUS_CAST(NTSTATUS, 0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT STATUS_CAST(NTSTATUS, 0xC0020007) +#define RPC_NT_INVALID_NET_ADDR STATUS_CAST(NTSTATUS, 0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND STATUS_CAST(NTSTATUS, 0xC0020009) +#define RPC_NT_INVALID_TIMEOUT STATUS_CAST(NTSTATUS, 0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC002000B) +#define RPC_NT_ALREADY_REGISTERED STATUS_CAST(NTSTATUS, 0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED STATUS_CAST(NTSTATUS, 0xC002000D) +#define RPC_NT_ALREADY_LISTENING STATUS_CAST(NTSTATUS, 0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED STATUS_CAST(NTSTATUS, 0xC002000F) +#define RPC_NT_NOT_LISTENING STATUS_CAST(NTSTATUS, 0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE STATUS_CAST(NTSTATUS, 0xC0020011) +#define RPC_NT_UNKNOWN_IF STATUS_CAST(NTSTATUS, 0xC0020012) +#define RPC_NT_NO_BINDINGS STATUS_CAST(NTSTATUS, 0xC0020013) +#define RPC_NT_NO_PROTSEQS STATUS_CAST(NTSTATUS, 0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT STATUS_CAST(NTSTATUS, 0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES STATUS_CAST(NTSTATUS, 0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE STATUS_CAST(NTSTATUS, 0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY STATUS_CAST(NTSTATUS, 0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS STATUS_CAST(NTSTATUS, 0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE STATUS_CAST(NTSTATUS, 0xC002001A) +#define RPC_NT_CALL_FAILED STATUS_CAST(NTSTATUS, 0xC002001B) +#define RPC_NT_CALL_FAILED_DNE STATUS_CAST(NTSTATUS, 0xC002001C) +#define RPC_NT_PROTOCOL_ERROR STATUS_CAST(NTSTATUS, 0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN STATUS_CAST(NTSTATUS, 0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE STATUS_CAST(NTSTATUS, 0xC0020021) +#define RPC_NT_INVALID_TAG STATUS_CAST(NTSTATUS, 0xC0020022) +#define RPC_NT_INVALID_BOUND STATUS_CAST(NTSTATUS, 0xC0020023) +#define RPC_NT_NO_ENTRY_NAME STATUS_CAST(NTSTATUS, 0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX STATUS_CAST(NTSTATUS, 0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX STATUS_CAST(NTSTATUS, 0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS STATUS_CAST(NTSTATUS, 0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT STATUS_CAST(NTSTATUS, 0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE STATUS_CAST(NTSTATUS, 0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL STATUS_CAST(NTSTATUS, 0xC002002B) +#define RPC_NT_STRING_TOO_LONG STATUS_CAST(NTSTATUS, 0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE STATUS_CAST(NTSTATUS, 0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH STATUS_CAST(NTSTATUS, 0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE STATUS_CAST(NTSTATUS, 0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL STATUS_CAST(NTSTATUS, 0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY STATUS_CAST(NTSTATUS, 0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE STATUS_CAST(NTSTATUS, 0xC0020033) +#define EPT_NT_INVALID_ENTRY STATUS_CAST(NTSTATUS, 0xC0020034) +#define EPT_NT_CANT_PERFORM_OP STATUS_CAST(NTSTATUS, 0xC0020035) +#define EPT_NT_NOT_REGISTERED STATUS_CAST(NTSTATUS, 0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT STATUS_CAST(NTSTATUS, 0xC0020037) +#define RPC_NT_INCOMPLETE_NAME STATUS_CAST(NTSTATUS, 0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION STATUS_CAST(NTSTATUS, 0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS STATUS_CAST(NTSTATUS, 0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED STATUS_CAST(NTSTATUS, 0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS STATUS_CAST(NTSTATUS, 0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE STATUS_CAST(NTSTATUS, 0xC002003F) +#define RPC_NT_INVALID_NAF_ID STATUS_CAST(NTSTATUS, 0xC0020040) +#define RPC_NT_CANNOT_SUPPORT STATUS_CAST(NTSTATUS, 0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE STATUS_CAST(NTSTATUS, 0xC0020042) +#define RPC_NT_INTERNAL_ERROR STATUS_CAST(NTSTATUS, 0xC0020043) +#define RPC_NT_ZERO_DIVIDE STATUS_CAST(NTSTATUS, 0xC0020044) +#define RPC_NT_ADDRESS_ERROR STATUS_CAST(NTSTATUS, 0xC0020045) +#define RPC_NT_FP_DIV_ZERO STATUS_CAST(NTSTATUS, 0xC0020046) +#define RPC_NT_FP_UNDERFLOW STATUS_CAST(NTSTATUS, 0xC0020047) +#define RPC_NT_FP_OVERFLOW STATUS_CAST(NTSTATUS, 0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS STATUS_CAST(NTSTATUS, 0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS STATUS_CAST(NTSTATUS, 0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC002004B) +#define EPT_NT_CANT_CREATE STATUS_CAST(NTSTATUS, 0xC002004C) +#define RPC_NT_INVALID_OBJECT STATUS_CAST(NTSTATUS, 0xC002004D) +#define RPC_NT_NO_INTERFACES STATUS_CAST(NTSTATUS, 0xC002004F) +#define RPC_NT_CALL_CANCELLED STATUS_CAST(NTSTATUS, 0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE STATUS_CAST(NTSTATUS, 0xC0020051) +#define RPC_NT_COMM_FAILURE STATUS_CAST(NTSTATUS, 0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL STATUS_CAST(NTSTATUS, 0xC0020053) +#define RPC_NT_NO_PRINC_NAME STATUS_CAST(NTSTATUS, 0xC0020054) +#define RPC_NT_NOT_RPC_ERROR STATUS_CAST(NTSTATUS, 0xC0020055) +#define RPC_NT_SEC_PKG_ERROR STATUS_CAST(NTSTATUS, 0xC0020057) +#define RPC_NT_NOT_CANCELLED STATUS_CAST(NTSTATUS, 0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE STATUS_CAST(NTSTATUS, 0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL STATUS_CAST(NTSTATUS, 0xC0020063) + +#define RPC_NT_NO_MORE_ENTRIES STATUS_CAST(NTSTATUS, 0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL STATUS_CAST(NTSTATUS, 0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE STATUS_CAST(NTSTATUS, 0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT STATUS_CAST(NTSTATUS, 0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH STATUS_CAST(NTSTATUS, 0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED STATUS_CAST(NTSTATUS, 0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH STATUS_CAST(NTSTATUS, 0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE STATUS_CAST(NTSTATUS, 0xC0030008) +#define RPC_NT_NULL_REF_POINTER STATUS_CAST(NTSTATUS, 0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE STATUS_CAST(NTSTATUS, 0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL STATUS_CAST(NTSTATUS, 0xC003000B) +#define RPC_NT_BAD_STUB_DATA STATUS_CAST(NTSTATUS, 0xC003000C) +#define RPC_NT_INVALID_ES_ACTION STATUS_CAST(NTSTATUS, 0xC0030059) +#define RPC_NT_WRONG_ES_VERSION STATUS_CAST(NTSTATUS, 0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION STATUS_CAST(NTSTATUS, 0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT STATUS_CAST(NTSTATUS, 0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION STATUS_CAST(NTSTATUS, 0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION STATUS_CAST(NTSTATUS, 0xC003005E) +#define RPC_NT_PIPE_CLOSED STATUS_CAST(NTSTATUS, 0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR STATUS_CAST(NTSTATUS, 0xC0030060) +#define RPC_NT_PIPE_EMPTY STATUS_CAST(NTSTATUS, 0xC0030061) + +#define STATUS_PNP_BAD_MPS_TABLE STATUS_CAST(NTSTATUS, 0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED STATUS_CAST(NTSTATUS, 0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED STATUS_CAST(NTSTATUS, 0xC0040037) +#define STATUS_PNP_INVALID_ID STATUS_CAST(NTSTATUS, 0xC0040038) + +#define STATUS_ACPI_INVALID_OPCODE STATUS_CAST(NTSTATUS, 0xC0140001L) +#define STATUS_ACPI_STACK_OVERFLOW STATUS_CAST(NTSTATUS, 0xC0140002L) +#define STATUS_ACPI_ASSERT_FAILED STATUS_CAST(NTSTATUS, 0xC0140003L) +#define STATUS_ACPI_INVALID_INDEX STATUS_CAST(NTSTATUS, 0xC0140004L) +#define STATUS_ACPI_INVALID_ARGUMENT STATUS_CAST(NTSTATUS, 0xC0140005L) +#define STATUS_ACPI_FATAL STATUS_CAST(NTSTATUS, 0xC0140006L) +#define STATUS_ACPI_INVALID_SUPERNAME STATUS_CAST(NTSTATUS, 0xC0140007L) +#define STATUS_ACPI_INVALID_ARGTYPE STATUS_CAST(NTSTATUS, 0xC0140008L) +#define STATUS_ACPI_INVALID_OBJTYPE STATUS_CAST(NTSTATUS, 0xC0140009L) +#define STATUS_ACPI_INVALID_TARGETTYPE STATUS_CAST(NTSTATUS, 0xC014000AL) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT STATUS_CAST(NTSTATUS, 0xC014000BL) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED STATUS_CAST(NTSTATUS, 0xC014000CL) +#define STATUS_ACPI_INVALID_EVENTTYPE STATUS_CAST(NTSTATUS, 0xC014000DL) +#define STATUS_ACPI_HANDLER_COLLISION STATUS_CAST(NTSTATUS, 0xC014000EL) +#define STATUS_ACPI_INVALID_DATA STATUS_CAST(NTSTATUS, 0xC014000FL) +#define STATUS_ACPI_INVALID_REGION STATUS_CAST(NTSTATUS, 0xC0140010L) +#define STATUS_ACPI_INVALID_ACCESS_SIZE STATUS_CAST(NTSTATUS, 0xC0140011L) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK STATUS_CAST(NTSTATUS, 0xC0140012L) +#define STATUS_ACPI_ALREADY_INITIALIZED STATUS_CAST(NTSTATUS, 0xC0140013L) +#define STATUS_ACPI_NOT_INITIALIZED STATUS_CAST(NTSTATUS, 0xC0140014L) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL STATUS_CAST(NTSTATUS, 0xC0140015L) +#define STATUS_ACPI_MUTEX_NOT_OWNED STATUS_CAST(NTSTATUS, 0xC0140016L) +#define STATUS_ACPI_MUTEX_NOT_OWNER STATUS_CAST(NTSTATUS, 0xC0140017L) +#define STATUS_ACPI_RS_ACCESS STATUS_CAST(NTSTATUS, 0xC0140018L) +#define STATUS_ACPI_INVALID_TABLE STATUS_CAST(NTSTATUS, 0xC0140019L) +#define STATUS_ACPI_REG_HANDLER_FAILED STATUS_CAST(NTSTATUS, 0xC0140020L) +#define STATUS_ACPI_POWER_REQUEST_FAILED STATUS_CAST(NTSTATUS, 0xC0140021L) + +#define STATUS_CTX_WINSTATION_NAME_INVALID STATUS_CAST(NTSTATUS, 0xC00A0001) +#define STATUS_CTX_INVALID_PD STATUS_CAST(NTSTATUS, 0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING STATUS_CAST(NTSTATUS, 0xC00A0006) +#define STATUS_CTX_NO_OUTBUF STATUS_CAST(NTSTATUS, 0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME STATUS_CAST(NTSTATUS, 0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR STATUS_CAST(NTSTATUS, 0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT STATUS_CAST(NTSTATUS, 0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER STATUS_CAST(NTSTATUS, 0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE STATUS_CAST(NTSTATUS, 0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY STATUS_CAST(NTSTATUS, 0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE STATUS_CAST(NTSTATUS, 0xC00A000F) +#define STATUS_CTX_TD_ERROR STATUS_CAST(NTSTATUS, 0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID STATUS_CAST(NTSTATUS, 0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE STATUS_CAST(NTSTATUS, 0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED STATUS_CAST(NTSTATUS, 0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION STATUS_CAST(NTSTATUS, 0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY STATUS_CAST(NTSTATUS, 0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE STATUS_CAST(NTSTATUS, 0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID STATUS_CAST(NTSTATUS, 0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE STATUS_CAST(NTSTATUS, 0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT STATUS_CAST(NTSTATUS, 0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT STATUS_CAST(NTSTATUS, 0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT STATUS_CAST(NTSTATUS, 0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED STATUS_CAST(NTSTATUS, 0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED STATUS_CAST(NTSTATUS, 0xC00A002B) +#define STATUS_CTX_INVALID_WD STATUS_CAST(NTSTATUS, 0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID STATUS_CAST(NTSTATUS, 0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED STATUS_CAST(NTSTATUS, 0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR STATUS_CAST(NTSTATUS, 0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET STATUS_CAST(NTSTATUS, 0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE STATUS_CAST(NTSTATUS, 0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE STATUS_CAST(NTSTATUS, 0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING STATUS_CAST(NTSTATUS, 0xC00A0036) + +#define STATUS_CLUSTER_INVALID_NODE STATUS_CAST(NTSTATUS, 0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS STATUS_CAST(NTSTATUS, 0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS STATUS_CAST(NTSTATUS, 0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS STATUS_CAST(NTSTATUS, 0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS STATUS_CAST(NTSTATUS, 0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST STATUS_CAST(NTSTATUS, 0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER STATUS_CAST(NTSTATUS, 0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN STATUS_CAST(NTSTATUS, 0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE STATUS_CAST(NTSTATUS, 0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER STATUS_CAST(NTSTATUS, 0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS STATUS_CAST(NTSTATUS, 0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK STATUS_CAST(NTSTATUS, 0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS STATUS_CAST(NTSTATUS, 0xC0130011) +#define STATUS_CLUSTER_NODE_UP STATUS_CAST(NTSTATUS, 0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED STATUS_CAST(NTSTATUS, 0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED STATUS_CAST(NTSTATUS, 0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT STATUS_CAST(NTSTATUS, 0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL STATUS_CAST(NTSTATUS, 0xC0130016) +#define STATUS_CLUSTER_POISONED STATUS_CAST(NTSTATUS, 0xC0130017) + +#define STATUS_SXS_SECTION_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX STATUS_CAST(NTSTATUS, 0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT STATUS_CAST(NTSTATUS, 0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR STATUS_CAST(NTSTATUS, 0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR STATUS_CAST(NTSTATUS, 0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED STATUS_CAST(NTSTATUS, 0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND STATUS_CAST(NTSTATUS, 0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT STATUS_CAST(NTSTATUS, 0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE STATUS_CAST(NTSTATUS, 0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED STATUS_CAST(NTSTATUS, 0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING STATUS_CAST(NTSTATUS, 0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET STATUS_CAST(NTSTATUS, 0xC015000E) +//#define STATUS_SXS_EARLY_DEACTIVATION STATUS_CAST(NTSTATUS,0xC015000F) +//#define STATUS_SXS_INVALID_DEACTIVATION STATUS_CAST(NTSTATUS,0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION STATUS_CAST(NTSTATUS, 0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY STATUS_CAST(NTSTATUS, 0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED STATUS_CAST(NTSTATUS, 0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK STATUS_CAST(NTSTATUS, 0xC0150014) +#define STATUS_SXS_CORRUPTION STATUS_CAST(NTSTATUS, 0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE STATUS_CAST(NTSTATUS, 0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME STATUS_CAST(NTSTATUS, 0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE STATUS_CAST(NTSTATUS, 0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR STATUS_CAST(NTSTATUS, 0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT STATUS_CAST(NTSTATUS, 0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH STATUS_CAST(NTSTATUS, 0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT STATUS_CAST(NTSTATUS, 0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT STATUS_CAST(NTSTATUS, 0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT STATUS_CAST(NTSTATUS, 0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY STATUS_CAST(NTSTATUS, 0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED STATUS_CAST(NTSTATUS, 0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH STATUS_CAST(NTSTATUS, 0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG STATUS_CAST(NTSTATUS, 0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED STATUS_CAST(NTSTATUS, 0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE STATUS_CAST(NTSTATUS, 0xC0150024) +#define STATUS_SXS_PRIMITIVE_INSTALLER_FAILED STATUS_CAST(NTSTATUS, 0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED STATUS_CAST(NTSTATUS, 0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING STATUS_CAST(NTSTATUS, 0xC0150027) + +/* Defined in winternl.h, always define since we do not include this header */ + +/* defined in ntstatus.h */ +#if !defined(NTSTATUS_FROM_WIN32) && !defined(INLINE_NTSTATUS_FROM_WIN32) +static INLINE NTSTATUS NTSTATUS_FROM_WIN32(long x) +{ + return x <= 0 ? STATUS_CAST(NTSTATUS, x) + : STATUS_CAST(NTSTATUS, ((x)&0x0000FFFF) | (0x7 << 16) | 0xC0000000); +} +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) + +/** + * winternl.h contains an incomplete definition of enum FILE_INFORMATION_CLASS + * avoid conflict by prefixing the winternl.h definition by _WINTERNL_ and then + * make a complete definition of enum FILE_INFORMATION_CLASS ourselves. + * + * For more information, refer to [MS-FSCC]: File System Control Codes: + * http://msdn.microsoft.com/en-us/library/cc231987.aspx + */ + +#define FILE_INFORMATION_CLASS _WINTERNL_FILE_INFORMATION_CLASS +#define _FILE_INFORMATION_CLASS _WINTERNL__FILE_INFORMATION_CLASS +#define FileDirectoryInformation _WINTERNL_FileDirectoryInformation + +#include + +#undef FILE_INFORMATION_CLASS +#undef _FILE_INFORMATION_CLASS +#undef FileDirectoryInformation + +#elif defined(_WIN32) +#include +#endif + +#ifndef __MINGW32__ +typedef enum +{ + FileDirectoryInformation = 1, + FileFullDirectoryInformation, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileUnknownInformation1, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation +} FILE_INFORMATION_CLASS; +#endif /* !__MINGW32__ */ + +#if !defined(_WIN32) || defined(__MINGW32__) +/* defined in */ +#define FILE_SUPERSEDED 0x00000000 +#define FILE_OPENED 0x00000001 +#define FILE_CREATED 0x00000002 +#define FILE_OVERWRITTEN 0x00000003 +#define FILE_EXISTS 0x00000004 +#define FILE_DOES_NOT_EXIST 0x00000005 +#endif + +#if !defined(_WIN32) || defined(_UWP) + +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 +#define FILE_MAXIMUM_DISPOSITION 0x00000005 + +#define FILE_DIRECTORY_FILE 0x00000001 +#define FILE_WRITE_THROUGH 0x00000002 +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 +#define FILE_CREATE_TREE_CONNECTION 0x00000080 + +#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 +#define FILE_NO_EA_KNOWLEDGE 0x00000200 +#define FILE_OPEN_REMOTE_INSTANCE 0x00000400 +#define FILE_RANDOM_ACCESS 0x00000800 + +#define FILE_DELETE_ON_CLOSE 0x00001000 +#define FILE_OPEN_BY_FILE_ID 0x00002000 +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#define FILE_NO_COMPRESSION 0x00008000 + +#define FILE_OPEN_REQUIRING_OPLOCK 0x00010000 + +#define FILE_RESERVE_OPFILTER 0x00100000 +#define FILE_OPEN_REPARSE_POINT 0x00200000 +#define FILE_OPEN_NO_RECALL 0x00400000 +#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 + +#define FILE_VALID_OPTION_FLAGS 0x00FFFFFF +#define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032 +#define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032 +#define FILE_VALID_SET_FLAGS 0x00000036 + +typedef CONST char* PCSZ; + +typedef struct +{ + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} STRING; +typedef STRING* PSTRING; + +typedef STRING ANSI_STRING; +typedef PSTRING PANSI_STRING; +typedef PSTRING PCANSI_STRING; + +typedef STRING OEM_STRING; +typedef PSTRING POEM_STRING; +typedef CONST STRING* PCOEM_STRING; + +typedef struct +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; + +#define OBJ_INHERIT 0x00000002L +#define OBJ_PERMANENT 0x00000010L +#define OBJ_EXCLUSIVE 0x00000020L +#define OBJ_CASE_INSENSITIVE 0x00000040L +#define OBJ_OPENIF 0x00000080L +#define OBJ_OPENLINK 0x00000100L +#define OBJ_KERNEL_HANDLE 0x00000200L +#define OBJ_FORCE_ACCESS_CHECK 0x00000400L +#define OBJ_VALID_ATTRIBUTES 0x000007F2L + +typedef struct +{ + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES; +typedef OBJECT_ATTRIBUTES* POBJECT_ATTRIBUTES; + +typedef struct +{ + union + { +#ifdef _WIN32 + NTSTATUS Status; +#else + NTSTATUS status; +#endif + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef VOID (*PIO_APC_ROUTINE)(PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG Reserved); + +#endif + +#if !defined(_WIN32) + +typedef struct S_PEB PEB; +typedef struct S_PEB* PPEB; + +typedef struct S_TEB TEB; +typedef struct S_TEB* PTEB; + +/** + * Process Environment Block + */ + +typedef struct +{ + DWORD ThreadId; + TEB* ThreadEnvironmentBlock; +} THREAD_BLOCK_ID; + +struct S_PEB +{ + DWORD ThreadCount; + DWORD ThreadArraySize; + THREAD_BLOCK_ID* Threads; +}; + +/* + * Thread Environment Block + */ + +struct S_TEB +{ + PEB* ProcessEnvironmentBlock; + + DWORD LastErrorValue; + PVOID TlsSlots[64]; +}; + +#define GENERIC_READ 0x80000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_ALL 0x10000000 + +#define DELETE 0x00010000 +#define READ_CONTROL 0x00020000 +#define WRITE_DAC 0x00040000 +#define WRITE_OWNER 0x00080000 +#define SYNCHRONIZE 0x00100000 +#define STANDARD_RIGHTS_REQUIRED 0x000F0000 +#define STANDARD_RIGHTS_READ 0x00020000 +#define STANDARD_RIGHTS_WRITE 0x00020000 +#define STANDARD_RIGHTS_EXECUTE 0x00020000 +#define STANDARD_RIGHTS_ALL 0x001F0000 +#define SPECIFIC_RIGHTS_ALL 0x0000FFFF +#define ACCESS_SYSTEM_SECURITY 0x01000000 +#define MAXIMUM_ALLOWED 0x02000000 + +#define FILE_READ_DATA 0x0001 +#define FILE_LIST_DIRECTORY 0x0001 +#define FILE_WRITE_DATA 0x0002 +#define FILE_ADD_FILE 0x0002 +#define FILE_APPEND_DATA 0x0004 +#define FILE_ADD_SUBDIRECTORY 0x0004 +#define FILE_CREATE_PIPE_INSTANCE 0x0004 +#define FILE_READ_EA 0x0008 +#define FILE_WRITE_EA 0x0010 +#define FILE_EXECUTE 0x0020 +#define FILE_TRAVERSE 0x0020 +#define FILE_DELETE_CHILD 0x0040 +#define FILE_READ_ATTRIBUTES 0x0080 +#define FILE_WRITE_ATTRIBUTES 0x0100 + +#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) +#define FILE_GENERIC_READ \ + (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE) +#define FILE_GENERIC_WRITE \ + (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ + FILE_APPEND_DATA | SYNCHRONIZE) +#define FILE_GENERIC_EXECUTE \ + (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE) + +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 + +typedef DWORD ACCESS_MASK; +typedef ACCESS_MASK* PACCESS_MASK; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API PTEB NtCurrentTeb(void); + +#ifdef __cplusplus +} +#endif + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API const char* NtStatus2Tag(DWORD ntstatus); + WINPR_API const char* Win32ErrorCode2Tag(UINT16 code); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_NT_H */ diff --git a/winpr/include/winpr/ntlm.h b/winpr/include/winpr/ntlm.h new file mode 100644 index 0000000..85b3f29 --- /dev/null +++ b/winpr/include/winpr/ntlm.h @@ -0,0 +1,68 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_NTLM_H +#define WINPR_UTILS_NTLM_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef SECURITY_STATUS (*psPeerComputeNtlmHash)(void* client, + const SEC_WINNT_AUTH_IDENTITY* authIdentity, + const SecBuffer* ntproofvalue, + const BYTE* randkey, const BYTE* mic, + const SecBuffer* micvalue, BYTE* ntlmhash); + + WINPR_API BOOL NTOWFv1W(LPWSTR Password, UINT32 PasswordLength, BYTE* NtHash); + WINPR_API BOOL NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash); + + WINPR_API BOOL NTOWFv2W(LPWSTR Password, UINT32 PasswordLength, LPWSTR User, UINT32 UserLength, + LPWSTR Domain, UINT32 DomainLength, BYTE* NtHash); + WINPR_API BOOL NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, UINT32 UserLength, + LPSTR Domain, UINT32 DomainLength, BYTE* NtHash); + + WINPR_API BOOL NTOWFv2FromHashW(BYTE* NtHashV1, LPWSTR User, UINT32 UserLength, LPWSTR Domain, + UINT32 DomainLength, BYTE* NtHash); + WINPR_API BOOL NTOWFv2FromHashA(BYTE* NtHashV1, LPSTR User, UINT32 UserLength, LPSTR Domain, + UINT32 DomainLength, BYTE* NtHash); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define NTOWFv1 NTOWFv1W +#define NTOWFv2 NTOWFv2W +#define NTOWFv2FromHash NTOWFv2FromHashW +#else +#define NTOWFv1 NTOWFv1A +#define NTOWFv2 NTOWFv2A +#define NTOWFv2FromHash NTOWFv2FromHashA +#endif + +#endif /* WINPR_UTILS_NTLM_H */ diff --git a/winpr/include/winpr/pack.h b/winpr/include/winpr/pack.h new file mode 100644 index 0000000..f97ba9a --- /dev/null +++ b/winpr/include/winpr/pack.h @@ -0,0 +1,100 @@ +/** + * WinPR: Windows Portable Runtime + * Pragma Pack + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This header is meant to be repeatedly included + * after defining the operation to be done: + * + * #define WINPR_PACK_PUSH + * #include // enables packing + * + * #define WINPR_PACK_POP + * #include // disables packing + * + * On each include, WINPR_PACK_* macros are undefined. + */ + +#if !defined(__APPLE__) +#ifndef WINPR_PRAGMA_PACK_EXT +#define WINPR_PRAGMA_PACK_EXT +#endif +#endif + +#ifdef PRAGMA_PACK_PUSH +#ifndef PRAGMA_PACK_PUSH1 +#define PRAGMA_PACK_PUSH1 +#endif +#undef PRAGMA_PACK_PUSH +#endif + +#ifdef PRAGMA_PACK_PUSH1 +#ifdef WINPR_PRAGMA_PACK_EXT +#pragma pack(push, 1) +#else +#pragma pack(1) +#endif +#undef PRAGMA_PACK_PUSH1 +#endif + +#ifdef PRAGMA_PACK_PUSH2 +#ifdef WINPR_PRAGMA_PACK_EXT +#pragma pack(push, 2) +#else +#pragma pack(2) +#endif +#undef PRAGMA_PACK_PUSH2 +#endif + +#ifdef PRAGMA_PACK_PUSH4 +#ifdef WINPR_PRAGMA_PACK_EXT +#pragma pack(push, 4) +#else +#pragma pack(4) +#endif +#undef PRAGMA_PACK_PUSH4 +#endif + +#ifdef PRAGMA_PACK_PUSH8 +#ifdef WINPR_PRAGMA_PACK_EXT +#pragma pack(push, 8) +#else +#pragma pack(8) +#endif +#undef PRAGMA_PACK_PUSH8 +#endif + +#ifdef PRAGMA_PACK_PUSH16 +#ifdef WINPR_PRAGMA_PACK_EXT +#pragma pack(push, 16) +#else +#pragma pack(16) +#endif +#undef PRAGMA_PACK_PUSH16 +#endif + +#ifdef PRAGMA_PACK_POP +#ifdef WINPR_PRAGMA_PACK_EXT +#pragma pack(pop) +#else +#pragma pack() +#endif +#undef PRAGMA_PACK_POP +#endif + +#undef WINPR_PRAGMA_PACK_EXT diff --git a/winpr/include/winpr/path.h b/winpr/include/winpr/path.h new file mode 100644 index 0000000..4554f49 --- /dev/null +++ b/winpr/include/winpr/path.h @@ -0,0 +1,356 @@ +/** + * WinPR: Windows Portable Runtime + * Path Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_PATH_H +#define WINPR_PATH_H + +#include +#include +#include +#include + +//#define WINPR_HAVE_PATHCCH_H 1 + +#ifdef WINPR_HAVE_PATHCCH_H + +#include + +#else + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define PATHCCH_ALLOW_LONG_PATHS \ + 0x00000001 /* Allow building of \\?\ paths if longer than MAX_PATH */ + +#define VOLUME_PREFIX _T("\\\\?\\Volume") +#define VOLUME_PREFIX_LEN ((sizeof(VOLUME_PREFIX) / sizeof(TCHAR)) - 1) + + /* + * Maximum number of characters we support using the "\\?\" syntax + * (0x7FFF + 1 for NULL terminator) + */ + +#define PATHCCH_MAX_CCH 0x8000 + + WINPR_API HRESULT PathCchAddBackslashA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchAddBackslashW(PWSTR pszPath, size_t cchPath); + + WINPR_API HRESULT PathCchRemoveBackslashA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchRemoveBackslashW(PWSTR pszPath, size_t cchPath); + + WINPR_API HRESULT PathCchAddBackslashExA(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining); + WINPR_API HRESULT PathCchAddBackslashExW(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining); + + WINPR_API HRESULT PathCchRemoveBackslashExA(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining); + WINPR_API HRESULT PathCchRemoveBackslashExW(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining); + + WINPR_API HRESULT PathCchAddExtensionA(PSTR pszPath, size_t cchPath, PCSTR pszExt); + WINPR_API HRESULT PathCchAddExtensionW(PWSTR pszPath, size_t cchPath, PCWSTR pszExt); + + WINPR_API HRESULT PathCchAppendA(PSTR pszPath, size_t cchPath, PCSTR pszMore); + WINPR_API HRESULT PathCchAppendW(PWSTR pszPath, size_t cchPath, PCWSTR pszMore); + + WINPR_API HRESULT PathCchAppendExA(PSTR pszPath, size_t cchPath, PCSTR pszMore, + unsigned long dwFlags); + WINPR_API HRESULT PathCchAppendExW(PWSTR pszPath, size_t cchPath, PCWSTR pszMore, + unsigned long dwFlags); + + WINPR_API HRESULT PathCchCanonicalizeA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn); + WINPR_API HRESULT PathCchCanonicalizeW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn); + + WINPR_API HRESULT PathCchCanonicalizeExA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, + unsigned long dwFlags); + WINPR_API HRESULT PathCchCanonicalizeExW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, + unsigned long dwFlags); + + WINPR_API HRESULT PathAllocCanonicalizeA(PCSTR pszPathIn, unsigned long dwFlags, + PSTR* ppszPathOut); + WINPR_API HRESULT PathAllocCanonicalizeW(PCWSTR pszPathIn, unsigned long dwFlags, + PWSTR* ppszPathOut); + + WINPR_API HRESULT PathCchCombineA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, + PCSTR pszMore); + WINPR_API HRESULT PathCchCombineW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, + PCWSTR pszMore); + + WINPR_API HRESULT PathCchCombineExA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, + PCSTR pszMore, unsigned long dwFlags); + WINPR_API HRESULT PathCchCombineExW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, + PCWSTR pszMore, unsigned long dwFlags); + + WINPR_API HRESULT PathAllocCombineA(PCSTR pszPathIn, PCSTR pszMore, unsigned long dwFlags, + PSTR* ppszPathOut); + WINPR_API HRESULT PathAllocCombineW(PCWSTR pszPathIn, PCWSTR pszMore, unsigned long dwFlags, + PWSTR* ppszPathOut); + + WINPR_API HRESULT PathCchFindExtensionA(PCSTR pszPath, size_t cchPath, PCSTR* ppszExt); + WINPR_API HRESULT PathCchFindExtensionW(PCWSTR pszPath, size_t cchPath, PCWSTR* ppszExt); + + WINPR_API HRESULT PathCchRenameExtensionA(PSTR pszPath, size_t cchPath, PCSTR pszExt); + WINPR_API HRESULT PathCchRenameExtensionW(PWSTR pszPath, size_t cchPath, PCWSTR pszExt); + + WINPR_API HRESULT PathCchRemoveExtensionA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchRemoveExtensionW(PWSTR pszPath, size_t cchPath); + + WINPR_API BOOL PathCchIsRootA(PCSTR pszPath); + WINPR_API BOOL PathCchIsRootW(PCWSTR pszPath); + + WINPR_API BOOL PathIsUNCExA(PCSTR pszPath, PCSTR* ppszServer); + WINPR_API BOOL PathIsUNCExW(PCWSTR pszPath, PCWSTR* ppszServer); + + WINPR_API HRESULT PathCchSkipRootA(PCSTR pszPath, PCSTR* ppszRootEnd); + WINPR_API HRESULT PathCchSkipRootW(PCWSTR pszPath, PCWSTR* ppszRootEnd); + + WINPR_API HRESULT PathCchStripToRootA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchStripToRootW(PWSTR pszPath, size_t cchPath); + + WINPR_API HRESULT PathCchStripPrefixA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchStripPrefixW(PWSTR pszPath, size_t cchPath); + + WINPR_API HRESULT PathCchRemoveFileSpecA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchRemoveFileSpecW(PWSTR pszPath, size_t cchPath); + +#ifdef UNICODE +#define PathCchAddBackslash PathCchAddBackslashW +#define PathCchRemoveBackslash PathCchRemoveBackslashW +#define PathCchAddBackslashEx PathCchAddBackslashExW +#define PathCchRemoveBackslashEx PathCchRemoveBackslashExW +#define PathCchAddExtension PathCchAddExtensionW +#define PathCchAppend PathCchAppendW +#define PathCchAppendEx PathCchAppendExW +#define PathCchCanonicalize PathCchCanonicalizeW +#define PathCchCanonicalizeEx PathCchCanonicalizeExW +#define PathAllocCanonicalize PathAllocCanonicalizeW +#define PathCchCombine PathCchCombineW +#define PathCchCombineEx PathCchCombineExW +#define PathAllocCombine PathAllocCombineW +#define PathCchFindExtension PathCchFindExtensionW +#define PathCchRenameExtension PathCchRenameExtensionW +#define PathCchRemoveExtension PathCchRemoveExtensionW +#define PathCchIsRoot PathCchIsRootW +#define PathIsUNCEx PathIsUNCExW +#define PathCchSkipRoot PathCchSkipRootW +#define PathCchStripToRoot PathCchStripToRootW +#define PathCchStripPrefix PathCchStripPrefixW +#define PathCchRemoveFileSpec PathCchRemoveFileSpecW +#else +#define PathCchAddBackslash PathCchAddBackslashA +#define PathCchRemoveBackslash PathCchRemoveBackslashA +#define PathCchAddBackslashEx PathCchAddBackslashExA +#define PathCchRemoveBackslashEx PathCchRemoveBackslashExA +#define PathCchAddExtension PathCchAddExtensionA +#define PathCchAppend PathCchAppendA +#define PathCchAppendEx PathCchAppendExA +#define PathCchCanonicalize PathCchCanonicalizeA +#define PathCchCanonicalizeEx PathCchCanonicalizeExA +#define PathAllocCanonicalize PathAllocCanonicalizeA +#define PathCchCombine PathCchCombineA +#define PathCchCombineEx PathCchCombineExA +#define PathAllocCombine PathAllocCombineA +#define PathCchFindExtension PathCchFindExtensionA +#define PathCchRenameExtension PathCchRenameExtensionA +#define PathCchRemoveExtension PathCchRemoveExtensionA +#define PathCchIsRoot PathCchIsRootA +#define PathIsUNCEx PathIsUNCExA +#define PathCchSkipRoot PathCchSkipRootA +#define PathCchStripToRoot PathCchStripToRootA +#define PathCchStripPrefix PathCchStripPrefixA +#define PathCchRemoveFileSpec PathCchRemoveFileSpecA +#endif + + /* Unix-style Paths */ + + WINPR_API HRESULT PathCchAddSlashA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchAddSlashW(PWSTR pszPath, size_t cchPath); + + WINPR_API HRESULT PathCchAddSlashExA(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining); + WINPR_API HRESULT PathCchAddSlashExW(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining); + + WINPR_API HRESULT UnixPathCchAddExtensionA(PSTR pszPath, size_t cchPath, PCSTR pszExt); + WINPR_API HRESULT UnixPathCchAddExtensionW(PWSTR pszPath, size_t cchPath, PCWSTR pszExt); + + WINPR_API HRESULT UnixPathCchAppendA(PSTR pszPath, size_t cchPath, PCSTR pszMore); + WINPR_API HRESULT UnixPathCchAppendW(PWSTR pszPath, size_t cchPath, PCWSTR pszMore); + + WINPR_API HRESULT UnixPathAllocCombineA(PCSTR pszPathIn, PCSTR pszMore, unsigned long dwFlags, + PSTR* ppszPathOut); + WINPR_API HRESULT UnixPathAllocCombineW(PCWSTR pszPathIn, PCWSTR pszMore, unsigned long dwFlags, + PWSTR* ppszPathOut); + +#ifdef UNICODE +#define PathCchAddSlash PathCchAddSlashW +#define PathCchAddSlashEx PathCchAddSlashExW +#define UnixPathCchAddExtension UnixPathCchAddExtensionW +#define UnixPathCchAppend UnixPathCchAppendW +#define UnixPathAllocCombine UnixPathAllocCombineW +#else +#define PathCchAddSlash PathCchAddSlashA +#define PathCchAddSlashEx PathCchAddSlashExA +#define UnixPathCchAddExtension UnixPathCchAddExtensionA +#define UnixPathCchAppend UnixPathCchAppendA +#define UnixPathAllocCombine UnixPathAllocCombineA +#endif + + /* Native-style Paths */ + + WINPR_API HRESULT PathCchAddSeparatorA(PSTR pszPath, size_t cchPath); + WINPR_API HRESULT PathCchAddSeparatorW(PWSTR pszPath, size_t cchPath); + + WINPR_API HRESULT PathCchAddSeparatorExA(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining); + WINPR_API HRESULT PathCchAddSeparatorExW(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining); + + WINPR_API HRESULT NativePathCchAddExtensionA(PSTR pszPath, size_t cchPath, PCSTR pszExt); + WINPR_API HRESULT NativePathCchAddExtensionW(PWSTR pszPath, size_t cchPath, PCWSTR pszExt); + + WINPR_API HRESULT NativePathCchAppendA(PSTR pszPath, size_t cchPath, PCSTR pszMore); + WINPR_API HRESULT NativePathCchAppendW(PWSTR pszPath, size_t cchPath, PCWSTR pszMore); + + WINPR_API HRESULT NativePathAllocCombineA(PCSTR pszPathIn, PCSTR pszMore, unsigned long dwFlags, + PSTR* ppszPathOut); + WINPR_API HRESULT NativePathAllocCombineW(PCWSTR pszPathIn, PCWSTR pszMore, + unsigned long dwFlags, PWSTR* ppszPathOut); + +#ifdef UNICODE +#define PathCchAddSeparator PathCchAddSeparatorW +#define PathCchAddSeparatorEx PathCchAddSeparatorExW +#define NativePathCchAddExtension NativePathCchAddExtensionW +#define NativePathCchAppend NativePathCchAppendW +#define NativePathAllocCombine NativePathAllocCombineW +#else +#define PathCchAddSeparator PathCchAddSeparatorA +#define PathCchAddSeparatorEx PathCchAddSeparatorExA +#define NativePathCchAddExtension NativePathCchAddExtensionA +#define NativePathCchAppend NativePathCchAppendA +#define NativePathAllocCombine NativePathAllocCombineA +#endif + + /* Path Portability Functions */ + +#define PATH_STYLE_WINDOWS 0x00000001 +#define PATH_STYLE_UNIX 0x00000002 +#define PATH_STYLE_NATIVE 0x00000003 + +#define PATH_SHARED_LIB_EXT_WITH_DOT 0x00000001 +#define PATH_SHARED_LIB_EXT_APPLE_SO 0x00000002 +#define PATH_SHARED_LIB_EXT_EXPLICIT 0x80000000 +#define PATH_SHARED_LIB_EXT_EXPLICIT_DLL 0x80000001 +#define PATH_SHARED_LIB_EXT_EXPLICIT_SO 0x80000002 +#define PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB 0x80000003 + + WINPR_API HRESULT PathCchConvertStyleA(PSTR pszPath, size_t cchPath, unsigned long dwFlags); + WINPR_API HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlags); + + WINPR_API char PathGetSeparatorA(unsigned long dwFlags); + WINPR_API WCHAR PathGetSeparatorW(unsigned long dwFlags); + + WINPR_API PCSTR PathGetSharedLibraryExtensionA(unsigned long dwFlags); + WINPR_API PCWSTR PathGetSharedLibraryExtensionW(unsigned long dwFlags); + +#ifdef UNICODE +#define PathCchConvertStyle PathCchConvertStyleW +#define PathGetSeparator PathGetSeparatorW +#define PathGetSharedLibraryExtension PathGetSharedLibraryExtensionW +#else +#define PathCchConvertStyle PathCchConvertStyleA +#define PathGetSeparator PathGetSeparatorW +#define PathGetSharedLibraryExtension PathGetSharedLibraryExtensionA +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/** + * Shell Path Functions + */ + +#define KNOWN_PATH_HOME 1 +#define KNOWN_PATH_TEMP 2 +#define KNOWN_PATH_XDG_DATA_HOME 3 +#define KNOWN_PATH_XDG_CONFIG_HOME 4 +#define KNOWN_PATH_XDG_CACHE_HOME 5 +#define KNOWN_PATH_XDG_RUNTIME_DIR 6 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API const char* GetKnownPathIdString(int id); + WINPR_API char* GetKnownPath(int id); + WINPR_API char* GetKnownSubPath(int id, const char* path); + WINPR_API char* GetEnvironmentPath(char* name); + WINPR_API char* GetEnvironmentSubPath(char* name, const char* path); + WINPR_API char* GetCombinedPath(const char* basePath, const char* subPath); + + WINPR_API BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes); + WINPR_API BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes); + +#if !defined(_WIN32) || defined(_UWP) + + WINPR_API BOOL PathIsRelativeA(LPCSTR pszPath); + WINPR_API BOOL PathIsRelativeW(LPCWSTR pszPath); + + WINPR_API BOOL PathFileExistsA(LPCSTR pszPath); + WINPR_API BOOL PathFileExistsW(LPCWSTR pszPath); + + WINPR_API BOOL PathIsDirectoryEmptyA(LPCSTR pszPath); + WINPR_API BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath); + +#ifdef UNICODE +#define PathFileExists PathFileExistsW +#define PathIsDirectoryEmpty PathIsDirectoryEmptyW +#else +#define PathFileExists PathFileExistsA +#define PathIsDirectoryEmpty PathIsDirectoryEmptyA +#endif + +#endif + + WINPR_API BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName); + WINPR_API BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags); + WINPR_API BOOL winpr_DeleteFile(const char* lpFileName); + WINPR_API BOOL winpr_RemoveDirectory(LPCSTR lpPathName); + WINPR_API BOOL winpr_RemoveDirectory_RecursiveA(LPCSTR lpPathName); + WINPR_API BOOL winpr_RemoveDirectory_RecursiveW(LPCWSTR lpPathName); + WINPR_API BOOL winpr_PathFileExists(const char* pszPath); + WINPR_API BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes); + +#ifdef __cplusplus +} +#endif + +#ifdef _WIN32 +#include +#endif + +#endif /* WINPR_PATH_H */ diff --git a/winpr/include/winpr/pipe.h b/winpr/include/winpr/pipe.h new file mode 100644 index 0000000..932fda5 --- /dev/null +++ b/winpr/include/winpr/pipe.h @@ -0,0 +1,127 @@ +/** + * WinPR: Windows Portable Runtime + * Pipe Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_PIPE_H +#define WINPR_PIPE_H + +#include +#include +#include +#include +#include + +#ifndef _WIN32 + +#define PIPE_UNLIMITED_INSTANCES 0xFF + +#define PIPE_ACCESS_INBOUND 0x00000001 +#define PIPE_ACCESS_OUTBOUND 0x00000002 +#define PIPE_ACCESS_DUPLEX 0x00000003 + +#define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 +#define FILE_FLAG_WRITE_THROUGH 0x80000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 + +#define PIPE_CLIENT_END 0x00000000 +#define PIPE_SERVER_END 0x00000001 + +#define PIPE_TYPE_BYTE 0x00000000 +#define PIPE_TYPE_MESSAGE 0x00000004 + +#define PIPE_READMODE_BYTE 0x00000000 +#define PIPE_READMODE_MESSAGE 0x00000002 + +#define PIPE_WAIT 0x00000000 +#define PIPE_NOWAIT 0x00000001 + +#define PIPE_ACCEPT_REMOTE_CLIENTS 0x00000000 +#define PIPE_REJECT_REMOTE_CLIENTS 0x00000008 + +#define NMPWAIT_USE_DEFAULT_WAIT 0x00000000 +#define NMPWAIT_NOWAIT 0x00000001 +#define NMPWAIT_WAIT_FOREVER 0xFFFFFFFF + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Unnamed pipe + */ + + WINPR_API BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, + LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize); + + /** + * Named pipe + */ + + WINPR_API HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, + DWORD nMaxInstances, DWORD nOutBufferSize, + DWORD nInBufferSize, DWORD nDefaultTimeOut, + LPSECURITY_ATTRIBUTES lpSecurityAttributes); + WINPR_API HANDLE CreateNamedPipeW(LPCWSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, + DWORD nMaxInstances, DWORD nOutBufferSize, + DWORD nInBufferSize, DWORD nDefaultTimeOut, + LPSECURITY_ATTRIBUTES lpSecurityAttributes); + + WINPR_API BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL DisconnectNamedPipe(HANDLE hNamedPipe); + + WINPR_API BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, + LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, + LPDWORD lpBytesLeftThisMessage); + + WINPR_API BOOL TransactNamedPipe(HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize, + LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, + LPOVERLAPPED lpOverlapped); + + WINPR_API BOOL WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut); + WINPR_API BOOL WaitNamedPipeW(LPCWSTR lpNamedPipeName, DWORD nTimeOut); + + WINPR_API BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, + LPDWORD lpMaxCollectionCount, + LPDWORD lpCollectDataTimeout); + + WINPR_API BOOL ImpersonateNamedPipeClient(HANDLE hNamedPipe); + + WINPR_API BOOL GetNamedPipeClientComputerNameA(HANDLE Pipe, LPCSTR ClientComputerName, + ULONG ClientComputerNameLength); + WINPR_API BOOL GetNamedPipeClientComputerNameW(HANDLE Pipe, LPCWSTR ClientComputerName, + ULONG ClientComputerNameLength); + +#ifdef UNICODE +#define CreateNamedPipe CreateNamedPipeW +#define WaitNamedPipe WaitNamedPipeW +#define GetNamedPipeClientComputerName GetNamedPipeClientComputerNameW +#else +#define CreateNamedPipe CreateNamedPipeA +#define WaitNamedPipe WaitNamedPipeA +#define GetNamedPipeClientComputerName GetNamedPipeClientComputerNameA +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* WINPR_PIPE_H */ diff --git a/winpr/include/winpr/platform.h b/winpr/include/winpr/platform.h new file mode 100644 index 0000000..00f9d22 --- /dev/null +++ b/winpr/include/winpr/platform.h @@ -0,0 +1,352 @@ +/** + * WinPR: Windows Portable Runtime + * Platform-Specific Definitions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_PLATFORM_H +#define WINPR_PLATFORM_H + +#include + +#if defined(__clang__) +#define WINPR_PRAGMA_DIAG_PUSH _Pragma("clang diagnostic push") +#define WINPR_PRAGMA_DIAG_IGNORED_PEDANTIC _Pragma("clang diagnostic ignored \"-Wpedantic\"") +#define WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES \ + _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"") +#define WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES \ + _Pragma("clang diagnostic ignored \"-Wstrict-prototypes\"") +#define WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO \ + _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"") +#define WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST \ + _Pragma("clang diagnostic ignored \"-Watomic-implicit-seq-cst\"") +#define WINPR_PRAGMA_DIAG_IGNORED_UNUSED_CONST_VAR \ + _Pragma("clang diagnostic ignored \"-Wunused-const-variable\"") +#define WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY \ + _Pragma("clang diagnostic ignored \"-Wformat-security\"") +#define WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC \ + _Pragma("clang diagnostic ignored \"-Wmismatched-dealloc\"") +#define WINPR_PRAGMA_DIAG_POP _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +#define WINPR_PRAGMA_DIAG_PUSH _Pragma("GCC diagnostic push") +#define WINPR_PRAGMA_DIAG_IGNORED_PEDANTIC _Pragma("GCC diagnostic ignored \"-Wpedantic\"") +#define WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES \ + _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") +#define WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES \ + _Pragma("GCC diagnostic ignored \"-Wstrict-prototypes\"") +#define WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO /* not supported _Pragma("GCC diagnostic \ + ignored \"-Wreserved-id-macro\"") */ +#define WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST /* not supported _Pragma("GCC diagnostic \ + ignored \ + \"-Watomic-implicit-seq-cst\"") */ +#define WINPR_PRAGMA_DIAG_IGNORED_UNUSED_CONST_VAR \ + _Pragma("GCC diagnostic ignored \"-Wunused-const-variable\"") +#define WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY \ + _Pragma("GCC diagnostic ignored \"-Wformat-security\"") +#define WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC \ + _Pragma("GCC diagnostic ignored \"-Wmismatched-dealloc\"") +#define WINPR_PRAGMA_DIAG_POP _Pragma("GCC diagnostic pop") +#else +#define WINPR_PRAGMA_DIAG_PUSH +#define WINPR_PRAGMA_DIAG_IGNORED_PEDANTIC +#define WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES +#define WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES +#define WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO +#define WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST +#define WINPR_PRAGMA_DIAG_IGNORED_UNUSED_CONST_VAR +#define WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY +#define WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC +#define WINPR_PRAGMA_DIAG_POP +#endif + +WINPR_PRAGMA_DIAG_PUSH + +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +/* + * Processor Architectures: + * http://sourceforge.net/p/predef/wiki/Architectures/ + * + * Visual Studio Predefined Macros: + * http://msdn.microsoft.com/en-ca/library/vstudio/b0084kay.aspx + */ + +/* Intel x86 (_M_IX86) */ + +#if defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(__X86__) || defined(_X86_) || \ + defined(__I86__) || defined(__IA32__) || defined(__THW_INTEL__) || defined(__INTEL__) +#ifndef _M_IX86 +#define _M_IX86 1 +#endif +#endif + +/* AMD64 (_M_AMD64) */ + +#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_X64) +#ifndef _M_AMD64 +#define _M_AMD64 1 +#endif +#endif + +/* Intel x86 or AMD64 (_M_IX86_AMD64) */ + +#if defined(_M_IX86) || defined(_M_AMD64) +#ifndef _M_IX86_AMD64 +#define _M_IX86_AMD64 1 +#endif +#endif + +/* ARM (_M_ARM) */ + +#if defined(__arm__) || defined(__thumb__) || defined(__TARGET_ARCH_ARM) || \ + defined(__TARGET_ARCH_THUMB) +#ifndef _M_ARM +#define _M_ARM 1 +#endif +#endif + +/* ARM64 (_M_ARM64) */ + +#if defined(__aarch64__) +#ifndef _M_ARM64 +#define _M_ARM64 1 +#endif +#endif + +/* MIPS (_M_MIPS) */ + +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(__MIPS__) +#ifndef _M_MIPS +#define _M_MIPS 1 +#endif +#endif + +/* MIPS64 (_M_MIPS64) */ + +#if defined(mips64) || defined(__mips64) || defined(__mips64__) || defined(__MIPS64__) +#ifndef _M_MIPS64 +#define _M_MIPS64 1 +#endif +#endif + +/* PowerPC (_M_PPC) */ + +#if defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) || \ + defined(_ARCH_PPC) +#ifndef _M_PPC +#define _M_PPC 1 +#endif +#endif + +/* Intel Itanium (_M_IA64) */ + +#if defined(__ia64) || defined(__ia64__) || defined(_IA64) || defined(__IA64__) +#ifndef _M_IA64 +#define _M_IA64 1 +#endif +#endif + +/* Alpha (_M_ALPHA) */ + +#if defined(__alpha) || defined(__alpha__) +#ifndef _M_ALPHA +#define _M_ALPHA 1 +#endif +#endif + +/* SPARC (_M_SPARC) */ + +#if defined(__sparc) || defined(__sparc__) +#ifndef _M_SPARC +#define _M_SPARC 1 +#endif +#endif + +/* E2K (_M_E2K) */ + +#if defined(__e2k__) +#ifndef _M_E2K +#define _M_E2K 1 +#endif +#endif + +/** + * Operating Systems: + * http://sourceforge.net/p/predef/wiki/OperatingSystems/ + */ + +/* Windows (_WIN32) */ + +/* WinRT (_WINRT) */ + +#if defined(WINAPI_FAMILY) +#if (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#ifndef _WINRT +#define _WINRT 1 +#endif +#endif +#endif + +#if defined(__cplusplus_winrt) +#ifndef _WINRT +#define _WINRT 1 +#endif +#endif + +/* Linux (__linux__) */ + +#if defined(linux) || defined(__linux) +#ifndef __linux__ +#define __linux__ 1 +#endif +#endif + +/* GNU/Linux (__gnu_linux__) */ + +/* Apple Platforms (iOS, Mac OS X) */ + +#if (defined(__APPLE__) && defined(__MACH__)) + +#include + +#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) + +/* iOS (__IOS__) */ + +#ifndef __IOS__ +#define __IOS__ 1 +#endif + +#elif (TARGET_OS_MAC == 1) + +/* Mac OS X (__MACOSX__) */ + +#ifndef __MACOSX__ +#define __MACOSX__ 1 +#endif + +#endif +#endif + +/* Android (__ANDROID__) */ + +/* Cygwin (__CYGWIN__) */ + +/* FreeBSD (__FreeBSD__) */ + +/* NetBSD (__NetBSD__) */ + +/* OpenBSD (__OpenBSD__) */ + +/* DragonFly (__DragonFly__) */ + +/* Solaris (__sun) */ + +#if defined(sun) +#ifndef __sun +#define __sun 1 +#endif +#endif + +/* IRIX (__sgi) */ + +#if defined(sgi) +#ifndef __sgi +#define __sgi 1 +#endif +#endif + +/* AIX (_AIX) */ + +#if defined(__TOS_AIX__) +#ifndef _AIX +#define _AIX 1 +#endif +#endif + +/* HP-UX (__hpux) */ + +#if defined(hpux) || defined(_hpux) +#ifndef __hpux +#define __hpux 1 +#endif +#endif + +/* BeOS (__BEOS__) */ + +/* QNX (__QNXNTO__) */ + +/** + * Endianness: + * http://sourceforge.net/p/predef/wiki/Endianness/ + */ + +#if defined(__gnu_linux__) +#include +#endif + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ + defined(__DragonFly__) || defined(__APPLE__) +#include +#endif + +/* Big-Endian */ + +#ifdef __BYTE_ORDER + +#if (__BYTE_ORDER == __BIG_ENDIAN) +#ifndef __BIG_ENDIAN__ +#define __BIG_ENDIAN__ 1 +#endif +#endif + +#else + +#if defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIPSEB) || \ + defined(__MIPSEB) || defined(__MIPSEB__) +#ifndef __BIG_ENDIAN__ +#define __BIG_ENDIAN__ 1 +#endif +#endif + +#endif /* __BYTE_ORDER */ + +/* Little-Endian */ + +#ifdef __BYTE_ORDER + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +#ifndef __LITTLE_ENDIAN__ +#define __LITTLE_ENDIAN__ 1 +#endif +#endif + +#else + +#if defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(__MIPSEL) || defined(__MIPSEL__) || defined(__e2k__) +#ifndef __LITTLE_ENDIAN__ +#define __LITTLE_ENDIAN__ 1 +#endif +#endif + +#endif /* __BYTE_ORDER */ + +WINPR_PRAGMA_DIAG_POP + +#endif /* WINPR_PLATFORM_H */ diff --git a/winpr/include/winpr/pool.h b/winpr/include/winpr/pool.h new file mode 100644 index 0000000..3160ae3 --- /dev/null +++ b/winpr/include/winpr/pool.h @@ -0,0 +1,282 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_POOL_H +#define WINPR_POOL_H + +#include +#include + +#include +#include + +#ifndef _WIN32 + +typedef DWORD TP_VERSION, *PTP_VERSION; + +typedef struct S_TP_CALLBACK_INSTANCE TP_CALLBACK_INSTANCE, *PTP_CALLBACK_INSTANCE; + +typedef VOID (*PTP_SIMPLE_CALLBACK)(PTP_CALLBACK_INSTANCE Instance, PVOID Context); + +typedef struct S_TP_POOL TP_POOL, *PTP_POOL; + +typedef struct +{ + SIZE_T StackReserve; + SIZE_T StackCommit; +} TP_POOL_STACK_INFORMATION, *PTP_POOL_STACK_INFORMATION; + +typedef struct S_TP_CLEANUP_GROUP TP_CLEANUP_GROUP, *PTP_CLEANUP_GROUP; + +typedef VOID (*PTP_CLEANUP_GROUP_CANCEL_CALLBACK)(PVOID ObjectContext, PVOID CleanupContext); + +typedef struct +{ + TP_VERSION Version; + PTP_POOL Pool; + PTP_CLEANUP_GROUP CleanupGroup; + PTP_CLEANUP_GROUP_CANCEL_CALLBACK CleanupGroupCancelCallback; + PVOID RaceDll; + PTP_SIMPLE_CALLBACK FinalizationCallback; + + union + { + DWORD Flags; + struct + { + DWORD LongFunction : 1; + DWORD Persistent : 1; + DWORD Private : 30; + } s; + } u; +} TP_CALLBACK_ENVIRON_V1; + +typedef TP_CALLBACK_ENVIRON_V1 TP_CALLBACK_ENVIRON, *PTP_CALLBACK_ENVIRON; + +typedef struct S_TP_WORK TP_WORK, *PTP_WORK; +typedef struct S_TP_TIMER TP_TIMER, *PTP_TIMER; + +typedef DWORD TP_WAIT_RESULT; +typedef struct S_TP_WAIT TP_WAIT, *PTP_WAIT; + +typedef struct S_TP_IO TP_IO, *PTP_IO; + +typedef VOID (*PTP_WORK_CALLBACK)(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work); +typedef VOID (*PTP_TIMER_CALLBACK)(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer); +typedef VOID (*PTP_WAIT_CALLBACK)(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WAIT Wait, + TP_WAIT_RESULT WaitResult); + +#endif /* _WIN32 not defined */ + +/* +There is a bug in the Win8 header that defines the IO +callback unconditionally. Versions of Windows greater +than XP will conditionally define it. The following +logic tries to fix that. +*/ +#ifdef _THREADPOOLAPISET_H_ +#define PTP_WIN32_IO_CALLBACK_DEFINED 1 +#else +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) +#define PTP_WIN32_IO_CALLBACK_DEFINED 1 +#endif +#endif + +#ifndef PTP_WIN32_IO_CALLBACK_DEFINED + +typedef VOID (*PTP_WIN32_IO_CALLBACK)(PTP_CALLBACK_INSTANCE Instance, PVOID Context, + PVOID Overlapped, ULONG IoResult, + ULONG_PTR NumberOfBytesTransferred, PTP_IO Io); + +#endif + +#if !defined(_WIN32) +#define WINPR_THREAD_POOL 1 +#elif defined(_WIN32) && (_WIN32_WINNT < 0x0600) +#define WINPR_THREAD_POOL 1 +#elif defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR < 8) +#define WINPR_THREAD_POOL 1 +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Synch */ + +#ifdef WINPR_THREAD_POOL + + WINPR_API PTP_WAIT winpr_CreateThreadpoolWait(PTP_WAIT_CALLBACK pfnwa, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); + WINPR_API VOID winpr_CloseThreadpoolWait(PTP_WAIT pwa); + WINPR_API VOID winpr_SetThreadpoolWait(PTP_WAIT pwa, HANDLE h, PFILETIME pftTimeout); + WINPR_API VOID winpr_WaitForThreadpoolWaitCallbacks(PTP_WAIT pwa, BOOL fCancelPendingCallbacks); + +#define CreateThreadpoolWait winpr_CreateThreadpoolWait +#define CloseThreadpoolWait winpr_CloseThreadpoolWait +#define SetThreadpoolWait winpr_SetThreadpoolWait +#define WaitForThreadpoolWaitCallbacks winpr_WaitForThreadpoolWaitCallbacks + + /* Work */ + + WINPR_API PTP_WORK winpr_CreateThreadpoolWork(PTP_WORK_CALLBACK pfnwk, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); + WINPR_API VOID winpr_CloseThreadpoolWork(PTP_WORK pwk); + WINPR_API VOID winpr_SubmitThreadpoolWork(PTP_WORK pwk); + WINPR_API BOOL winpr_TrySubmitThreadpoolCallback(PTP_SIMPLE_CALLBACK pfns, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); + WINPR_API VOID winpr_WaitForThreadpoolWorkCallbacks(PTP_WORK pwk, BOOL fCancelPendingCallbacks); + +#define CreateThreadpoolWork winpr_CreateThreadpoolWork +#define CloseThreadpoolWork winpr_CloseThreadpoolWork +#define SubmitThreadpoolWork winpr_SubmitThreadpoolWork +#define TrySubmitThreadpoolCallback winpr_TrySubmitThreadpoolCallback +#define WaitForThreadpoolWorkCallbacks winpr_WaitForThreadpoolWorkCallbacks + + /* Timer */ + + WINPR_API PTP_TIMER winpr_CreateThreadpoolTimer(PTP_TIMER_CALLBACK pfnti, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); + WINPR_API VOID winpr_CloseThreadpoolTimer(PTP_TIMER pti); + WINPR_API BOOL winpr_IsThreadpoolTimerSet(PTP_TIMER pti); + WINPR_API VOID winpr_SetThreadpoolTimer(PTP_TIMER pti, PFILETIME pftDueTime, DWORD msPeriod, + DWORD msWindowLength); + WINPR_API VOID winpr_WaitForThreadpoolTimerCallbacks(PTP_TIMER pti, + BOOL fCancelPendingCallbacks); + +#define CreateThreadpoolTimer winpr_CreateThreadpoolTimer +#define CloseThreadpoolTimer winpr_CloseThreadpoolTimer +#define IsThreadpoolTimerSet winpr_IsThreadpoolTimerSet +#define SetThreadpoolTimer winpr_SetThreadpoolTimer +#define WaitForThreadpoolTimerCallbacks winpr_WaitForThreadpoolTimerCallbacks + + /* I/O */ + + WINPR_API PTP_IO winpr_CreateThreadpoolIo(HANDLE fl, PTP_WIN32_IO_CALLBACK pfnio, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); + WINPR_API VOID winpr_CloseThreadpoolIo(PTP_IO pio); + WINPR_API VOID winpr_StartThreadpoolIo(PTP_IO pio); + WINPR_API VOID winpr_CancelThreadpoolIo(PTP_IO pio); + WINPR_API VOID winpr_WaitForThreadpoolIoCallbacks(PTP_IO pio, BOOL fCancelPendingCallbacks); + +#define CreateThreadpoolIo winpr_CreateThreadpoolIo +#define CloseThreadpoolIo winpr_CloseThreadpoolIo +#define StartThreadpoolIo winpr_StartThreadpoolIo +#define CancelThreadpoolIo winpr_CancelThreadpoolIo +#define WaitForThreadpoolIoCallbacks winpr_WaitForThreadpoolIoCallbacks + + /* Clean-up Group */ + + WINPR_API VOID winpr_SetThreadpoolCallbackCleanupGroup(PTP_CALLBACK_ENVIRON pcbe, + PTP_CLEANUP_GROUP ptpcg, + PTP_CLEANUP_GROUP_CANCEL_CALLBACK pfng); + WINPR_API PTP_CLEANUP_GROUP winpr_CreateThreadpoolCleanupGroup(void); + WINPR_API VOID winpr_CloseThreadpoolCleanupGroupMembers(PTP_CLEANUP_GROUP ptpcg, + BOOL fCancelPendingCallbacks, + PVOID pvCleanupContext); + WINPR_API VOID winpr_CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg); + +#define SetThreadpoolCallbackCleanupGroup winpr_SetThreadpoolCallbackCleanupGroup +#define CreateThreadpoolCleanupGroup winpr_CreateThreadpoolCleanupGroup +#define CloseThreadpoolCleanupGroupMembers winpr_CloseThreadpoolCleanupGroupMembers +#define CloseThreadpoolCleanupGroup winpr_CloseThreadpoolCleanupGroup + + /* Pool */ + + WINPR_API PTP_POOL winpr_CreateThreadpool(PVOID reserved); + WINPR_API VOID winpr_CloseThreadpool(PTP_POOL ptpp); + WINPR_API BOOL winpr_SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic); + WINPR_API VOID winpr_SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost); + +#define CreateThreadpool winpr_CreateThreadpool +#define CloseThreadpool winpr_CloseThreadpool +#define SetThreadpoolThreadMinimum winpr_SetThreadpoolThreadMinimum +#define SetThreadpoolThreadMaximum winpr_SetThreadpoolThreadMaximum + + /* Callback */ + + WINPR_API BOOL winpr_CallbackMayRunLong(PTP_CALLBACK_INSTANCE pci); + + /* Callback Clean-up */ + + WINPR_API VOID winpr_SetEventWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE evt); + WINPR_API VOID winpr_ReleaseSemaphoreWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE sem, + DWORD crel); + WINPR_API VOID winpr_ReleaseMutexWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE mut); + WINPR_API VOID winpr_LeaveCriticalSectionWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, + PCRITICAL_SECTION pcs); + WINPR_API VOID winpr_FreeLibraryWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HMODULE mod); + WINPR_API VOID winpr_DisassociateCurrentThreadFromCallback(PTP_CALLBACK_INSTANCE pci); + +#define SetEventWhenCallbackReturns winpr_SetEventWhenCallbackReturns +#define ReleaseSemaphoreWhenCallbackReturns winpr_ReleaseSemaphoreWhenCallbackReturns +#define ReleaseMutexWhenCallbackReturns winpr_ReleaseMutexWhenCallbackReturns +#define LeaveCriticalSectionWhenCallbackReturns winpr_LeaveCriticalSectionWhenCallbackReturns +#define FreeLibraryWhenCallbackReturns winpr_FreeLibraryWhenCallbackReturns +#define DisassociateCurrentThreadFromCallback winpr_DisassociateCurrentThreadFromCallback + +#endif /* WINPR_THREAD_POOL */ + +#if !defined(_WIN32) +#define WINPR_CALLBACK_ENVIRON 1 +#elif defined(_WIN32) && (_WIN32_WINNT < 0x0600) +#define WINPR_CALLBACK_ENVIRON 1 +#elif defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR < 9) +#define WINPR_CALLBACK_ENVIRON 1 +#endif + +#ifdef WINPR_CALLBACK_ENVIRON + /* some version of mingw are missing Callback Environment functions */ + + /* Callback Environment */ + + static INLINE VOID InitializeThreadpoolEnvironment(PTP_CALLBACK_ENVIRON pcbe) + { + const TP_CALLBACK_ENVIRON empty = { 0 }; + *pcbe = empty; + pcbe->Version = 1; + } + + static INLINE VOID DestroyThreadpoolEnvironment(PTP_CALLBACK_ENVIRON pcbe) + { + /* no actions, this may change in a future release. */ + } + + static INLINE VOID SetThreadpoolCallbackPool(PTP_CALLBACK_ENVIRON pcbe, PTP_POOL ptpp) + { + pcbe->Pool = ptpp; + } + + static INLINE VOID SetThreadpoolCallbackRunsLong(PTP_CALLBACK_ENVIRON pcbe) + { + pcbe->u.s.LongFunction = 1; + } + + static INLINE VOID SetThreadpoolCallbackLibrary(PTP_CALLBACK_ENVIRON pcbe, PVOID mod) + { + pcbe->RaceDll = mod; + } +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_POOL_H */ diff --git a/winpr/include/winpr/print.h b/winpr/include/winpr/print.h new file mode 100644 index 0000000..beb44f1 --- /dev/null +++ b/winpr/include/winpr/print.h @@ -0,0 +1,54 @@ +/** + * WinPR: Windows Portable Runtime + * Print Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_PRINT_H +#define WINPR_UTILS_PRINT_H + +#include +#include +#include + +#include +#include +#include + +#define WINPR_HEXDUMP_LINE_LENGTH 16 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void winpr_HexDump(const char* tag, UINT32 lvl, const void* data, size_t length); + WINPR_API void winpr_HexLogDump(wLog* log, UINT32 lvl, const void* data, size_t length); + WINPR_API void winpr_CArrayDump(const char* tag, UINT32 lvl, const void* data, size_t length, + size_t width); + + WINPR_API char* winpr_BinToHexString(const BYTE* data, size_t length, BOOL space); + WINPR_API size_t winpr_BinToHexStringBuffer(const BYTE* data, size_t length, char* dstStr, + size_t dstSize, BOOL space); + + WINPR_API size_t winpr_HexStringToBinBuffer(const char* str, size_t strLength, BYTE* data, + size_t dataLength); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_UTILS_PRINT_H */ diff --git a/winpr/include/winpr/registry.h b/winpr/include/winpr/registry.h new file mode 100644 index 0000000..e596a39 --- /dev/null +++ b/winpr/include/winpr/registry.h @@ -0,0 +1,426 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Registry + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_REGISTRY_H +#define WINPR_REGISTRY_H + +#include + +#if defined(_WIN32) && !defined(_UWP) + +#include + +#else + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include +#include +#include + +#ifndef _WIN32 + +#define OWNER_SECURITY_INFORMATION 0x00000001 +#define GROUP_SECURITY_INFORMATION 0x00000002 +#define DACL_SECURITY_INFORMATION 0x00000004 +#define SACL_SECURITY_INFORMATION 0x00000008 + +#define REG_OPTION_RESERVED 0x00000000 +#define REG_OPTION_NON_VOLATILE 0x00000000 +#define REG_OPTION_VOLATILE 0x00000001 +#define REG_OPTION_CREATE_LINK 0x00000002 +#define REG_OPTION_BACKUP_RESTORE 0x00000004 +#define REG_OPTION_OPEN_LINK 0x00000008 + +#define REG_CREATED_NEW_KEY 0x00000001 +#define REG_OPENED_EXISTING_KEY 0x00000002 + +#define REG_NOTIFY_CHANGE_NAME 0x01 +#define REG_NOTIFY_CHANGE_ATTRIBUTES 0x02 +#define REG_NOTIFY_CHANGE_LAST_SET 0x04 +#define REG_NOTIFY_CHANGE_SECURITY 0x08 + +#define KEY_QUERY_VALUE 0x00000001 +#define KEY_SET_VALUE 0x00000002 +#define KEY_CREATE_SUB_KEY 0x00000004 +#define KEY_ENUMERATE_SUB_KEYS 0x00000008 +#define KEY_NOTIFY 0x00000010 +#define KEY_CREATE_LINK 0x00000020 +#define KEY_WOW64_64KEY 0x00000100 +#define KEY_WOW64_32KEY 0x00000200 +#define KEY_WOW64_RES 0x00000300 + +#define REG_WHOLE_HIVE_VOLATILE 0x00000001 +#define REG_REFRESH_HIVE 0x00000002 +#define REG_NO_LAZY_FLUSH 0x00000004 +#define REG_FORCE_RESTORE 0x00000008 + +#define KEY_READ \ + ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & \ + (~SYNCHRONIZE)) + +#define KEY_WRITE ((STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE)) + +#define KEY_EXECUTE ((KEY_READ) & (~SYNCHRONIZE)) + +#define KEY_ALL_ACCESS \ + ((STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | \ + KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) & \ + (~SYNCHRONIZE)) + + typedef enum + { + REG_NONE = 0, + REG_SZ = 1, + REG_EXPAND_SZ = 2, + REG_BINARY = 3, + REG_DWORD = 4, + REG_DWORD_LITTLE_ENDIAN = REG_DWORD, + REG_DWORD_BIG_ENDIAN = 5, + REG_LINK = 6, + REG_MULTI_SZ = 7, + REG_RESOURCE_LIST = 8, + REG_FULL_RESOURCE_DESCRIPTOR = 9, + REG_RESOURCE_REQUIREMENTS_LIST = 10, + REG_QWORD = 11, + REG_QWORD_LITTLE_ENDIAN = REG_QWORD + } eRegTypes; + + typedef HANDLE HKEY; + typedef HANDLE* PHKEY; + +#endif + + typedef ACCESS_MASK REGSAM; + +#define HKEY_CLASSES_ROOT ((HKEY)(LONG_PTR)(LONG)0x80000000) +#define HKEY_CURRENT_USER ((HKEY)(LONG_PTR)(LONG)0x80000001) +#define HKEY_LOCAL_MACHINE ((HKEY)(LONG_PTR)(LONG)0x80000002) +#define HKEY_USERS ((HKEY)(LONG_PTR)(LONG)0x80000003) +#define HKEY_PERFORMANCE_DATA ((HKEY)(LONG_PTR)(LONG)0x80000004) +#define HKEY_PERFORMANCE_TEXT ((HKEY)(LONG_PTR)(LONG)0x80000050) +#define HKEY_PERFORMANCE_NLSTEXT ((HKEY)(LONG_PTR)(LONG)0x80000060) +#define HKEY_CURRENT_CONFIG ((HKEY)(LONG_PTR)(LONG)0x80000005) +#define HKEY_DYN_DATA ((HKEY)(LONG_PTR)(LONG)0x80000006) +#define HKEY_CURRENT_USER_LOCAL_SETTINGS ((HKEY)(LONG_PTR)(LONG)0x80000007) + +#define RRF_RT_REG_NONE 0x00000001 +#define RRF_RT_REG_SZ 0x00000002 +#define RRF_RT_REG_EXPAND_SZ 0x00000004 +#define RRF_RT_REG_BINARY 0x00000008 +#define RRF_RT_REG_DWORD 0x00000010 +#define RRF_RT_REG_MULTI_SZ 0x00000020 +#define RRF_RT_REG_QWORD 0x00000040 + +#define RRF_RT_DWORD (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD) +#define RRF_RT_QWORD (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD) +#define RRF_RT_ANY 0x0000FFFF + +#define RRF_NOEXPAND 0x10000000 +#define RRF_ZEROONFAILURE 0x20000000 + + struct val_context + { + int valuelen; + LPVOID value_context; + LPVOID val_buff_ptr; + }; + + typedef struct val_context* PVALCONTEXT; + + typedef struct pvalueA + { + LPSTR pv_valuename; + int pv_valuelen; + LPVOID pv_value_context; + DWORD pv_type; + } PVALUEA, *PPVALUEA; + + typedef struct pvalueW + { + LPWSTR pv_valuename; + int pv_valuelen; + LPVOID pv_value_context; + DWORD pv_type; + } PVALUEW, *PPVALUEW; + +#ifdef UNICODE + typedef PVALUEW PVALUE; + typedef PPVALUEW PPVALUE; +#else +typedef PVALUEA PVALUE; +typedef PPVALUEA PPVALUE; +#endif + + typedef struct value_entA + { + LPSTR ve_valuename; + DWORD ve_valuelen; + DWORD_PTR ve_valueptr; + DWORD ve_type; + } VALENTA, *PVALENTA; + + typedef struct value_entW + { + LPWSTR ve_valuename; + DWORD ve_valuelen; + DWORD_PTR ve_valueptr; + DWORD ve_type; + } VALENTW, *PVALENTW; + +#ifdef UNICODE + typedef VALENTW VALENT; + typedef PVALENTW PVALENT; +#else +typedef VALENTA VALENT; +typedef PVALENTA PVALENT; +#endif + + WINPR_API LONG RegCloseKey(HKEY hKey); + + WINPR_API LONG RegCopyTreeW(HKEY hKeySrc, LPCWSTR lpSubKey, HKEY hKeyDest); + WINPR_API LONG RegCopyTreeA(HKEY hKeySrc, LPCSTR lpSubKey, HKEY hKeyDest); + +#ifdef UNICODE +#define RegCopyTree RegCopyTreeW +#else +#define RegCopyTree RegCopyTreeA +#endif + + WINPR_API LONG RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, + DWORD dwOptions, REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, + LPDWORD lpdwDisposition); + WINPR_API LONG RegCreateKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD Reserved, LPSTR lpClass, + DWORD dwOptions, REGSAM samDesired, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, + LPDWORD lpdwDisposition); + +#ifdef UNICODE +#define RegCreateKeyEx RegCreateKeyExW +#else +#define RegCreateKeyEx RegCreateKeyExA +#endif + + WINPR_API LONG RegDeleteKeyExW(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved); + WINPR_API LONG RegDeleteKeyExA(HKEY hKey, LPCSTR lpSubKey, REGSAM samDesired, DWORD Reserved); + +#ifdef UNICODE +#define RegDeleteKeyEx RegDeleteKeyExW +#else +#define RegDeleteKeyEx RegDeleteKeyExA +#endif + + WINPR_API LONG RegDeleteTreeW(HKEY hKey, LPCWSTR lpSubKey); + WINPR_API LONG RegDeleteTreeA(HKEY hKey, LPCSTR lpSubKey); + +#ifdef UNICODE +#define RegDeleteTree RegDeleteTreeW +#else +#define RegDeleteTree RegDeleteTreeA +#endif + + WINPR_API LONG RegDeleteValueW(HKEY hKey, LPCWSTR lpValueName); + WINPR_API LONG RegDeleteValueA(HKEY hKey, LPCSTR lpValueName); + +#ifdef UNICODE +#define RegDeleteValue RegDeleteValueW +#else +#define RegDeleteValue RegDeleteValueA +#endif + + WINPR_API LONG RegDisablePredefinedCacheEx(void); + + WINPR_API LONG RegEnumKeyExW(HKEY hKey, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcName, + LPDWORD lpReserved, LPWSTR lpClass, LPDWORD lpcClass, + PFILETIME lpftLastWriteTime); + WINPR_API LONG RegEnumKeyExA(HKEY hKey, DWORD dwIndex, LPSTR lpName, LPDWORD lpcName, + LPDWORD lpReserved, LPSTR lpClass, LPDWORD lpcClass, + PFILETIME lpftLastWriteTime); + +#ifdef UNICODE +#define RegEnumKeyEx RegEnumKeyExW +#else +#define RegEnumKeyEx RegEnumKeyExA +#endif + + WINPR_API LONG RegEnumValueW(HKEY hKey, DWORD dwIndex, LPWSTR lpValueName, + LPDWORD lpcchValueName, LPDWORD lpReserved, LPDWORD lpType, + LPBYTE lpData, LPDWORD lpcbData); + WINPR_API LONG RegEnumValueA(HKEY hKey, DWORD dwIndex, LPSTR lpValueName, + LPDWORD lpcchValueName, LPDWORD lpReserved, LPDWORD lpType, + LPBYTE lpData, LPDWORD lpcbData); + +#ifdef UNICODE +#define RegEnumValue RegEnumValueW +#else +#define RegEnumValue RegEnumValueA +#endif + + WINPR_API LONG RegFlushKey(HKEY hKey); + + WINPR_API LONG RegGetKeySecurity(HKEY hKey, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + LPDWORD lpcbSecurityDescriptor); + + WINPR_API LONG RegGetValueW(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, + LPDWORD pdwType, PVOID pvData, LPDWORD pcbData); + WINPR_API LONG RegGetValueA(HKEY hkey, LPCSTR lpSubKey, LPCSTR lpValue, DWORD dwFlags, + LPDWORD pdwType, PVOID pvData, LPDWORD pcbData); + +#ifdef UNICODE +#define RegGetValue RegGetValueW +#else +#define RegGetValue RegGetValueA +#endif + + WINPR_API LONG RegLoadAppKeyW(LPCWSTR lpFile, PHKEY phkResult, REGSAM samDesired, + DWORD dwOptions, DWORD Reserved); + WINPR_API LONG RegLoadAppKeyA(LPCSTR lpFile, PHKEY phkResult, REGSAM samDesired, + DWORD dwOptions, DWORD Reserved); + +#ifdef UNICODE +#define RegLoadAppKey RegLoadAppKeyW +#else +#define RegLoadAppKey RegLoadAppKeyA +#endif + + WINPR_API LONG RegLoadKeyW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpFile); + WINPR_API LONG RegLoadKeyA(HKEY hKey, LPCSTR lpSubKey, LPCSTR lpFile); + +#ifdef UNICODE +#define RegLoadKey RegLoadKeyW +#else +#define RegLoadKey RegLoadKeyA +#endif + + WINPR_API LONG RegLoadMUIStringW(HKEY hKey, LPCWSTR pszValue, LPWSTR pszOutBuf, DWORD cbOutBuf, + LPDWORD pcbData, DWORD Flags, LPCWSTR pszDirectory); + WINPR_API LONG RegLoadMUIStringA(HKEY hKey, LPCSTR pszValue, LPSTR pszOutBuf, DWORD cbOutBuf, + LPDWORD pcbData, DWORD Flags, LPCSTR pszDirectory); + +#ifdef UNICODE +#define RegLoadMUIString RegLoadMUIStringW +#else +#define RegLoadMUIString RegLoadMUIStringA +#endif + + WINPR_API LONG RegNotifyChangeKeyValue(HKEY hKey, BOOL bWatchSubtree, DWORD dwNotifyFilter, + HANDLE hEvent, BOOL fAsynchronous); + + WINPR_API LONG RegOpenCurrentUser(REGSAM samDesired, PHKEY phkResult); + + WINPR_API LONG RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, + PHKEY phkResult); + WINPR_API LONG RegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, + PHKEY phkResult); + +#ifdef UNICODE +#define RegOpenKeyEx RegOpenKeyExW +#else +#define RegOpenKeyEx RegOpenKeyExA +#endif + + WINPR_API LONG RegOpenUserClassesRoot(HANDLE hToken, DWORD dwOptions, REGSAM samDesired, + PHKEY phkResult); + + WINPR_API LONG RegQueryInfoKeyW(HKEY hKey, LPWSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, + LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen, + LPDWORD lpcMaxClassLen, LPDWORD lpcValues, + LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, + LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime); + WINPR_API LONG RegQueryInfoKeyA(HKEY hKey, LPSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, + LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen, + LPDWORD lpcMaxClassLen, LPDWORD lpcValues, + LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, + LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime); + +#ifdef UNICODE +#define RegQueryInfoKey RegQueryInfoKeyW +#else +#define RegQueryInfoKey RegQueryInfoKeyA +#endif + + WINPR_API LONG RegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, + LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); + WINPR_API LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, + LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); + +#ifdef UNICODE +#define RegQueryValueEx RegQueryValueExW +#else +#define RegQueryValueEx RegQueryValueExA +#endif + + WINPR_API LONG RegRestoreKeyW(HKEY hKey, LPCWSTR lpFile, DWORD dwFlags); + WINPR_API LONG RegRestoreKeyA(HKEY hKey, LPCSTR lpFile, DWORD dwFlags); + +#ifdef UNICODE +#define RegRestoreKey RegRestoreKeyW +#else +#define RegRestoreKey RegRestoreKeyA +#endif + + WINPR_API LONG RegSaveKeyExW(HKEY hKey, LPCWSTR lpFile, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD Flags); + WINPR_API LONG RegSaveKeyExA(HKEY hKey, LPCSTR lpFile, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD Flags); + +#ifdef UNICODE +#define RegSaveKeyEx RegSaveKeyExW +#else +#define RegSaveKeyEx RegSaveKeyExA +#endif + + WINPR_API LONG RegSetKeySecurity(HKEY hKey, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor); + + WINPR_API LONG RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, + const BYTE* lpData, DWORD cbData); + WINPR_API LONG RegSetValueExA(HKEY hKey, LPCSTR lpValueName, DWORD Reserved, DWORD dwType, + const BYTE* lpData, DWORD cbData); + +#ifdef UNICODE +#define RegSetValueEx RegSetValueExW +#else +#define RegSetValueEx RegSetValueExA +#endif + + WINPR_API LONG RegUnLoadKeyW(HKEY hKey, LPCWSTR lpSubKey); + WINPR_API LONG RegUnLoadKeyA(HKEY hKey, LPCSTR lpSubKey); + +#ifdef UNICODE +#define RegUnLoadKey RegUnLoadKeyW +#else +#define RegUnLoadKey RegUnLoadKeyA +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* WINPR_REGISTRY_H */ diff --git a/winpr/include/winpr/rpc.h b/winpr/include/winpr/rpc.h new file mode 100644 index 0000000..4bfb3af --- /dev/null +++ b/winpr/include/winpr/rpc.h @@ -0,0 +1,725 @@ +/** + * WinPR: Windows Portable Runtime + * Microsoft Remote Procedure Call (MSRPC) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_RPC_H +#define WINPR_RPC_H + +#include + +typedef struct +{ + UINT32 ContextType; + GUID ContextUuid; +} CONTEXT_HANDLE; + +typedef PCONTEXT_HANDLE PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE; +typedef PCONTEXT_HANDLE PTUNNEL_CONTEXT_HANDLE_SERIALIZE; + +typedef PCONTEXT_HANDLE PCHANNEL_CONTEXT_HANDLE_NOSERIALIZE; +typedef PCONTEXT_HANDLE PCHANNEL_CONTEXT_HANDLE_SERIALIZE; + +#if defined(_WIN32) && !defined(_UWP) + +#include + +#else + +#include +#include +#include +#include +#include + +#define RPC_S_OK ERROR_SUCCESS +#define RPC_S_INVALID_ARG ERROR_INVALID_PARAMETER +#define RPC_S_OUT_OF_MEMORY ERROR_OUTOFMEMORY +#define RPC_S_OUT_OF_THREADS ERROR_MAX_THRDS_REACHED +#define RPC_S_INVALID_LEVEL ERROR_INVALID_PARAMETER +#define RPC_S_BUFFER_TOO_SMALL ERROR_INSUFFICIENT_BUFFER +#define RPC_S_INVALID_SECURITY_DESC ERROR_INVALID_SECURITY_DESCR +#define RPC_S_ACCESS_DENIED ERROR_ACCESS_DENIED +#define RPC_S_SERVER_OUT_OF_MEMORY ERROR_NOT_ENOUGH_SERVER_MEMORY +#define RPC_S_ASYNC_CALL_PENDING ERROR_IO_PENDING +#define RPC_S_UNKNOWN_PRINCIPAL ERROR_NONE_MAPPED +#define RPC_S_TIMEOUT ERROR_TIMEOUT + +#define RPC_X_NO_MEMORY RPC_S_OUT_OF_MEMORY +#define RPC_X_INVALID_BOUND RPC_S_INVALID_BOUND +#define RPC_X_INVALID_TAG RPC_S_INVALID_TAG +#define RPC_X_ENUM_VALUE_TOO_LARGE RPC_X_ENUM_VALUE_OUT_OF_RANGE +#define RPC_X_SS_CONTEXT_MISMATCH ERROR_INVALID_HANDLE +#define RPC_X_INVALID_BUFFER ERROR_INVALID_USER_BUFFER +#define RPC_X_PIPE_APP_MEMORY ERROR_OUTOFMEMORY +#define RPC_X_INVALID_PIPE_OPERATION RPC_X_WRONG_PIPE_ORDER + +#define RPC_VAR_ENTRY __cdecl + +typedef long RPC_STATUS; + +#ifndef _WIN32 +typedef CHAR* RPC_CSTR; +typedef WCHAR* RPC_WSTR; +#endif + +typedef void* I_RPC_HANDLE; +typedef I_RPC_HANDLE RPC_BINDING_HANDLE; +typedef RPC_BINDING_HANDLE handle_t; + +typedef struct +{ + unsigned long Count; + RPC_BINDING_HANDLE BindingH[1]; +} RPC_BINDING_VECTOR; +#define rpc_binding_vector_t RPC_BINDING_VECTOR + +typedef struct +{ + unsigned long Count; + UUID* Uuid[1]; +} UUID_VECTOR; +#define uuid_vector_t UUID_VECTOR + +typedef void* RPC_IF_HANDLE; + +typedef struct +{ + UUID Uuid; + unsigned short VersMajor; + unsigned short VersMinor; +} RPC_IF_ID; + +#define RPC_C_BINDING_INFINITE_TIMEOUT 10 +#define RPC_C_BINDING_MIN_TIMEOUT 0 +#define RPC_C_BINDING_DEFAULT_TIMEOUT 5 +#define RPC_C_BINDING_MAX_TIMEOUT 9 + +#define RPC_C_CANCEL_INFINITE_TIMEOUT -1 + +#define RPC_C_LISTEN_MAX_CALLS_DEFAULT 1234 +#define RPC_C_PROTSEQ_MAX_REQS_DEFAULT 10 + +#define RPC_C_BIND_TO_ALL_NICS 1 +#define RPC_C_USE_INTERNET_PORT 0x1 +#define RPC_C_USE_INTRANET_PORT 0x2 +#define RPC_C_DONT_FAIL 0x4 + +#define RPC_C_MQ_TEMPORARY 0x0000 +#define RPC_C_MQ_PERMANENT 0x0001 +#define RPC_C_MQ_CLEAR_ON_OPEN 0x0002 +#define RPC_C_MQ_USE_EXISTING_SECURITY 0x0004 +#define RPC_C_MQ_AUTHN_LEVEL_NONE 0x0000 +#define RPC_C_MQ_AUTHN_LEVEL_PKT_INTEGRITY 0x0008 +#define RPC_C_MQ_AUTHN_LEVEL_PKT_PRIVACY 0x0010 + +#define RPC_C_OPT_MQ_DELIVERY 1 +#define RPC_C_OPT_MQ_PRIORITY 2 +#define RPC_C_OPT_MQ_JOURNAL 3 +#define RPC_C_OPT_MQ_ACKNOWLEDGE 4 +#define RPC_C_OPT_MQ_AUTHN_SERVICE 5 +#define RPC_C_OPT_MQ_AUTHN_LEVEL 6 +#define RPC_C_OPT_MQ_TIME_TO_REACH_QUEUE 7 +#define RPC_C_OPT_MQ_TIME_TO_BE_RECEIVED 8 +#define RPC_C_OPT_BINDING_NONCAUSAL 9 +#define RPC_C_OPT_SECURITY_CALLBACK 10 +#define RPC_C_OPT_UNIQUE_BINDING 11 +#define RPC_C_OPT_CALL_TIMEOUT 12 +#define RPC_C_OPT_DONT_LINGER 13 +#define RPC_C_OPT_MAX_OPTIONS 14 + +#define RPC_C_MQ_EXPRESS 0 +#define RPC_C_MQ_RECOVERABLE 1 + +#define RPC_C_MQ_JOURNAL_NONE 0 +#define RPC_C_MQ_JOURNAL_DEADLETTER 1 +#define RPC_C_MQ_JOURNAL_ALWAYS 2 + +#define RPC_C_FULL_CERT_CHAIN 0x0001 + +typedef struct +{ + unsigned int Count; + unsigned char* Protseq[1]; +} RPC_PROTSEQ_VECTORA; + +typedef struct +{ + unsigned int Count; + unsigned short* Protseq[1]; +} RPC_PROTSEQ_VECTORW; + +#ifdef UNICODE +#define RPC_PROTSEQ_VECTOR RPC_PROTSEQ_VECTORW +#else +#define RPC_PROTSEQ_VECTOR RPC_PROTSEQ_VECTORA +#endif + +typedef struct +{ + unsigned int Length; + unsigned long EndpointFlags; + unsigned long NICFlags; +} RPC_POLICY, *PRPC_POLICY; + +typedef void RPC_OBJECT_INQ_FN(UUID* ObjectUuid, UUID* TypeUuid, RPC_STATUS* pStatus); +typedef RPC_STATUS RPC_IF_CALLBACK_FN(RPC_IF_HANDLE InterfaceUuid, void* Context); +typedef void RPC_SECURITY_CALLBACK_FN(void* Context); + +#define RPC_MGR_EPV void + +typedef struct +{ + unsigned int Count; + unsigned long Stats[1]; +} RPC_STATS_VECTOR; + +#define RPC_C_STATS_CALLS_IN 0 +#define RPC_C_STATS_CALLS_OUT 1 +#define RPC_C_STATS_PKTS_IN 2 +#define RPC_C_STATS_PKTS_OUT 3 + +typedef struct +{ + unsigned long Count; + RPC_IF_ID* IfId[1]; +} RPC_IF_ID_VECTOR; + +#ifndef _WIN32 + +typedef void* RPC_AUTH_IDENTITY_HANDLE; +typedef void* RPC_AUTHZ_HANDLE; + +#define RPC_C_AUTHN_LEVEL_DEFAULT 0 +#define RPC_C_AUTHN_LEVEL_NONE 1 +#define RPC_C_AUTHN_LEVEL_CONNECT 2 +#define RPC_C_AUTHN_LEVEL_CALL 3 +#define RPC_C_AUTHN_LEVEL_PKT 4 +#define RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 5 +#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY 6 + +#define RPC_C_IMP_LEVEL_DEFAULT 0 +#define RPC_C_IMP_LEVEL_ANONYMOUS 1 +#define RPC_C_IMP_LEVEL_IDENTIFY 2 +#define RPC_C_IMP_LEVEL_IMPERSONATE 3 +#define RPC_C_IMP_LEVEL_DELEGATE 4 + +#define RPC_C_QOS_IDENTITY_STATIC 0 +#define RPC_C_QOS_IDENTITY_DYNAMIC 1 + +#define RPC_C_QOS_CAPABILITIES_DEFAULT 0x0 +#define RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH 0x1 +#define RPC_C_QOS_CAPABILITIES_MAKE_FULLSIC 0x2 +#define RPC_C_QOS_CAPABILITIES_ANY_AUTHORITY 0x4 +#define RPC_C_QOS_CAPABILITIES_IGNORE_DELEGATE_FAILURE 0x8 +#define RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT 0x10 + +#define RPC_C_PROTECT_LEVEL_DEFAULT (RPC_C_AUTHN_LEVEL_DEFAULT) +#define RPC_C_PROTECT_LEVEL_NONE (RPC_C_AUTHN_LEVEL_NONE) +#define RPC_C_PROTECT_LEVEL_CONNECT (RPC_C_AUTHN_LEVEL_CONNECT) +#define RPC_C_PROTECT_LEVEL_CALL (RPC_C_AUTHN_LEVEL_CALL) +#define RPC_C_PROTECT_LEVEL_PKT (RPC_C_AUTHN_LEVEL_PKT) +#define RPC_C_PROTECT_LEVEL_PKT_INTEGRITY (RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) +#define RPC_C_PROTECT_LEVEL_PKT_PRIVACY (RPC_C_AUTHN_LEVEL_PKT_PRIVACY) + +#define RPC_C_AUTHN_NONE 0 +#define RPC_C_AUTHN_DCE_PRIVATE 1 +#define RPC_C_AUTHN_DCE_PUBLIC 2 +#define RPC_C_AUTHN_DEC_PUBLIC 4 +#define RPC_C_AUTHN_GSS_NEGOTIATE 9 +#define RPC_C_AUTHN_WINNT 10 +#define RPC_C_AUTHN_GSS_SCHANNEL 14 +#define RPC_C_AUTHN_GSS_KERBEROS 16 +#define RPC_C_AUTHN_DPA 17 +#define RPC_C_AUTHN_MSN 18 +#define RPC_C_AUTHN_DIGEST 21 +#define RPC_C_AUTHN_MQ 100 +#define RPC_C_AUTHN_DEFAULT 0xFFFFFFFFL + +#define RPC_C_NO_CREDENTIALS ((RPC_AUTH_IDENTITY_HANDLE)MAXUINT_PTR) + +#define RPC_C_SECURITY_QOS_VERSION 1L +#define RPC_C_SECURITY_QOS_VERSION_1 1L + +typedef struct +{ + unsigned long Version; + unsigned long Capabilities; + unsigned long IdentityTracking; + unsigned long ImpersonationType; +} RPC_SECURITY_QOS, *PRPC_SECURITY_QOS; + +#define RPC_C_SECURITY_QOS_VERSION_2 2L + +#define RPC_C_AUTHN_INFO_TYPE_HTTP 1 + +#define RPC_C_HTTP_AUTHN_TARGET_SERVER 1 +#define RPC_C_HTTP_AUTHN_TARGET_PROXY 2 + +#define RPC_C_HTTP_AUTHN_SCHEME_BASIC 0x00000001 +#define RPC_C_HTTP_AUTHN_SCHEME_NTLM 0x00000002 +#define RPC_C_HTTP_AUTHN_SCHEME_PASSPORT 0x00000004 +#define RPC_C_HTTP_AUTHN_SCHEME_DIGEST 0x00000008 +#define RPC_C_HTTP_AUTHN_SCHEME_NEGOTIATE 0x00000010 +#define RPC_C_HTTP_AUTHN_SCHEME_CERT 0x00010000 + +#define RPC_C_HTTP_FLAG_USE_SSL 1 +#define RPC_C_HTTP_FLAG_USE_FIRST_AUTH_SCHEME 2 +#define RPC_C_HTTP_FLAG_IGNORE_CERT_CN_INVALID 8 + +typedef struct +{ + SEC_WINNT_AUTH_IDENTITY_W* TransportCredentials; + unsigned long Flags; + unsigned long AuthenticationTarget; + unsigned long NumberOfAuthnSchemes; + unsigned long* AuthnSchemes; + unsigned short* ServerCertificateSubject; +} RPC_HTTP_TRANSPORT_CREDENTIALS_W, *PRPC_HTTP_TRANSPORT_CREDENTIALS_W; + +typedef struct +{ + SEC_WINNT_AUTH_IDENTITY_A* TransportCredentials; + unsigned long Flags; + unsigned long AuthenticationTarget; + unsigned long NumberOfAuthnSchemes; + unsigned long* AuthnSchemes; + unsigned char* ServerCertificateSubject; +} RPC_HTTP_TRANSPORT_CREDENTIALS_A, *PRPC_HTTP_TRANSPORT_CREDENTIALS_A; + +typedef struct +{ + unsigned long Version; + unsigned long Capabilities; + unsigned long IdentityTracking; + unsigned long ImpersonationType; + unsigned long AdditionalSecurityInfoType; + union + { + RPC_HTTP_TRANSPORT_CREDENTIALS_W* HttpCredentials; + } u; +} RPC_SECURITY_QOS_V2_W, *PRPC_SECURITY_QOS_V2_W; + +typedef struct +{ + unsigned long Version; + unsigned long Capabilities; + unsigned long IdentityTracking; + unsigned long ImpersonationType; + unsigned long AdditionalSecurityInfoType; + union + { + RPC_HTTP_TRANSPORT_CREDENTIALS_A* HttpCredentials; + } u; +} RPC_SECURITY_QOS_V2_A, *PRPC_SECURITY_QOS_V2_A; + +#define RPC_C_SECURITY_QOS_VERSION_3 3L + +typedef struct +{ + unsigned long Version; + unsigned long Capabilities; + unsigned long IdentityTracking; + unsigned long ImpersonationType; + unsigned long AdditionalSecurityInfoType; + union + { + RPC_HTTP_TRANSPORT_CREDENTIALS_W* HttpCredentials; + } u; + void* Sid; +} RPC_SECURITY_QOS_V3_W, *PRPC_SECURITY_QOS_V3_W; + +typedef struct +{ + unsigned long Version; + unsigned long Capabilities; + unsigned long IdentityTracking; + unsigned long ImpersonationType; + unsigned long AdditionalSecurityInfoType; + union + { + RPC_HTTP_TRANSPORT_CREDENTIALS_A* HttpCredentials; + } u; + void* Sid; +} RPC_SECURITY_QOS_V3_A, *PRPC_SECURITY_QOS_V3_A; + +typedef enum +{ + RPCHTTP_RS_REDIRECT = 1, + RPCHTTP_RS_ACCESS_1, + RPCHTTP_RS_SESSION, + RPCHTTP_RS_ACCESS_2, + RPCHTTP_RS_INTERFACE +} RPC_HTTP_REDIRECTOR_STAGE; + +typedef RPC_STATUS (*RPC_NEW_HTTP_PROXY_CHANNEL)( + RPC_HTTP_REDIRECTOR_STAGE RedirectorStage, unsigned short* ServerName, + unsigned short* ServerPort, unsigned short* RemoteUser, unsigned short* AuthType, + void* ResourceUuid, void* Metadata, void* SessionId, void* Interface, void* Reserved, + unsigned long Flags, unsigned short** NewServerName, unsigned short** NewServerPort); + +typedef void (*RPC_HTTP_PROXY_FREE_STRING)(unsigned short* String); + +#define RPC_C_AUTHZ_NONE 0 +#define RPC_C_AUTHZ_NAME 1 +#define RPC_C_AUTHZ_DCE 2 +#define RPC_C_AUTHZ_DEFAULT 0xFFFFFFFF + +#endif + +typedef void (*RPC_AUTH_KEY_RETRIEVAL_FN)(void* Arg, unsigned short* ServerPrincName, + unsigned long KeyVer, void** Key, RPC_STATUS* pStatus); + +#define DCE_C_ERROR_STRING_LEN 256 + +typedef I_RPC_HANDLE* RPC_EP_INQ_HANDLE; + +#define RPC_C_EP_ALL_ELTS 0 +#define RPC_C_EP_MATCH_BY_IF 1 +#define RPC_C_EP_MATCH_BY_OBJ 2 +#define RPC_C_EP_MATCH_BY_BOTH 3 + +#define RPC_C_VERS_ALL 1 +#define RPC_C_VERS_COMPATIBLE 2 +#define RPC_C_VERS_EXACT 3 +#define RPC_C_VERS_MAJOR_ONLY 4 +#define RPC_C_VERS_UPTO 5 + +typedef int (*RPC_MGMT_AUTHORIZATION_FN)(RPC_BINDING_HANDLE ClientBinding, + unsigned long RequestedMgmtOperation, RPC_STATUS* pStatus); + +#define RPC_C_MGMT_INQ_IF_IDS 0 +#define RPC_C_MGMT_INQ_PRINC_NAME 1 +#define RPC_C_MGMT_INQ_STATS 2 +#define RPC_C_MGMT_IS_SERVER_LISTEN 3 +#define RPC_C_MGMT_STOP_SERVER_LISTEN 4 + +#define RPC_C_PARM_MAX_PACKET_LENGTH 1 +#define RPC_C_PARM_BUFFER_LENGTH 2 + +#define RPC_IF_AUTOLISTEN 0x0001 +#define RPC_IF_OLE 0x0002 +#define RPC_IF_ALLOW_UNKNOWN_AUTHORITY 0x0004 +#define RPC_IF_ALLOW_SECURE_ONLY 0x0008 +#define RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH 0x0010 +#define RPC_IF_ALLOW_LOCAL_ONLY 0x0020 +#define RPC_IF_SEC_NO_CACHE 0x0040 + +typedef struct +{ + unsigned long Version; + unsigned long Flags; + unsigned long ComTimeout; + unsigned long CallTimeout; +} RPC_BINDING_HANDLE_OPTIONS_V1, RPC_BINDING_HANDLE_OPTIONS; + +typedef struct +{ + unsigned long Version; + unsigned short* ServerPrincName; + unsigned long AuthnLevel; + unsigned long AuthnSvc; + SEC_WINNT_AUTH_IDENTITY* AuthIdentity; + RPC_SECURITY_QOS* SecurityQos; +} RPC_BINDING_HANDLE_SECURITY_V1, RPC_BINDING_HANDLE_SECURITY; + +typedef struct +{ + unsigned long Version; + unsigned long Flags; + unsigned long ProtocolSequence; + unsigned short* NetworkAddress; + unsigned short* StringEndpoint; + union + { + unsigned short* Reserved; + } u1; + UUID ObjectUuid; +} RPC_BINDING_HANDLE_TEMPLATE_V1, RPC_BINDING_HANDLE_TEMPLATE; + +#define RPC_CALL_STATUS_IN_PROGRESS 0x01 +#define RPC_CALL_STATUS_CANCELLED 0x02 +#define RPC_CALL_STATUS_DISCONNECTED 0x03 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API RPC_STATUS RpcBindingCopy(RPC_BINDING_HANDLE SourceBinding, + RPC_BINDING_HANDLE* DestinationBinding); + WINPR_API RPC_STATUS RpcBindingFree(RPC_BINDING_HANDLE* Binding); + WINPR_API RPC_STATUS RpcBindingSetOption(RPC_BINDING_HANDLE hBinding, unsigned long option, + ULONG_PTR optionValue); + WINPR_API RPC_STATUS RpcBindingInqOption(RPC_BINDING_HANDLE hBinding, unsigned long option, + ULONG_PTR* pOptionValue); + WINPR_API RPC_STATUS RpcBindingFromStringBindingA(RPC_CSTR StringBinding, + RPC_BINDING_HANDLE* Binding); + WINPR_API RPC_STATUS RpcBindingFromStringBindingW(RPC_WSTR StringBinding, + RPC_BINDING_HANDLE* Binding); + WINPR_API RPC_STATUS RpcSsGetContextBinding(void* ContextHandle, RPC_BINDING_HANDLE* Binding); + WINPR_API RPC_STATUS RpcBindingInqObject(RPC_BINDING_HANDLE Binding, UUID* ObjectUuid); + WINPR_API RPC_STATUS RpcBindingReset(RPC_BINDING_HANDLE Binding); + WINPR_API RPC_STATUS RpcBindingSetObject(RPC_BINDING_HANDLE Binding, UUID* ObjectUuid); + WINPR_API RPC_STATUS RpcMgmtInqDefaultProtectLevel(unsigned long AuthnSvc, + unsigned long* AuthnLevel); + WINPR_API RPC_STATUS RpcBindingToStringBindingA(RPC_BINDING_HANDLE Binding, + RPC_CSTR* StringBinding); + WINPR_API RPC_STATUS RpcBindingToStringBindingW(RPC_BINDING_HANDLE Binding, + RPC_WSTR* StringBinding); + WINPR_API RPC_STATUS RpcBindingVectorFree(RPC_BINDING_VECTOR** BindingVector); + WINPR_API RPC_STATUS RpcStringBindingComposeA(RPC_CSTR ObjUuid, RPC_CSTR Protseq, + RPC_CSTR NetworkAddr, RPC_CSTR Endpoint, + RPC_CSTR Options, RPC_CSTR* StringBinding); + WINPR_API RPC_STATUS RpcStringBindingComposeW(RPC_WSTR ObjUuid, RPC_WSTR Protseq, + RPC_WSTR NetworkAddr, RPC_WSTR Endpoint, + RPC_WSTR Options, RPC_WSTR* StringBinding); + WINPR_API RPC_STATUS RpcStringBindingParseA(RPC_CSTR StringBinding, RPC_CSTR* ObjUuid, + RPC_CSTR* Protseq, RPC_CSTR* NetworkAddr, + RPC_CSTR* Endpoint, RPC_CSTR* NetworkOptions); + WINPR_API RPC_STATUS RpcStringBindingParseW(RPC_WSTR StringBinding, RPC_WSTR* ObjUuid, + RPC_WSTR* Protseq, RPC_WSTR* NetworkAddr, + RPC_WSTR* Endpoint, RPC_WSTR* NetworkOptions); + WINPR_API RPC_STATUS RpcStringFreeA(RPC_CSTR* String); + WINPR_API RPC_STATUS RpcStringFreeW(RPC_WSTR* String); + WINPR_API RPC_STATUS RpcIfInqId(RPC_IF_HANDLE RpcIfHandle, RPC_IF_ID* RpcIfId); + WINPR_API RPC_STATUS RpcNetworkIsProtseqValidA(RPC_CSTR Protseq); + WINPR_API RPC_STATUS RpcNetworkIsProtseqValidW(RPC_WSTR Protseq); + WINPR_API RPC_STATUS RpcMgmtInqComTimeout(RPC_BINDING_HANDLE Binding, unsigned int* Timeout); + WINPR_API RPC_STATUS RpcMgmtSetComTimeout(RPC_BINDING_HANDLE Binding, unsigned int Timeout); + WINPR_API RPC_STATUS RpcMgmtSetCancelTimeout(long Timeout); + WINPR_API RPC_STATUS RpcNetworkInqProtseqsA(RPC_PROTSEQ_VECTORA** ProtseqVector); + WINPR_API RPC_STATUS RpcNetworkInqProtseqsW(RPC_PROTSEQ_VECTORW** ProtseqVector); + WINPR_API RPC_STATUS RpcObjectInqType(UUID* ObjUuid, UUID* TypeUuid); + WINPR_API RPC_STATUS RpcObjectSetInqFn(RPC_OBJECT_INQ_FN* InquiryFn); + WINPR_API RPC_STATUS RpcObjectSetType(UUID* ObjUuid, UUID* TypeUuid); + WINPR_API RPC_STATUS RpcProtseqVectorFreeA(RPC_PROTSEQ_VECTORA** ProtseqVector); + WINPR_API RPC_STATUS RpcProtseqVectorFreeW(RPC_PROTSEQ_VECTORW** ProtseqVector); + WINPR_API RPC_STATUS RpcServerInqBindings(RPC_BINDING_VECTOR** BindingVector); + WINPR_API RPC_STATUS RpcServerInqIf(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + RPC_MGR_EPV** MgrEpv); + WINPR_API RPC_STATUS RpcServerListen(unsigned int MinimumCallThreads, unsigned int MaxCalls, + unsigned int DontWait); + WINPR_API RPC_STATUS RpcServerRegisterIf(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + RPC_MGR_EPV* MgrEpv); + WINPR_API RPC_STATUS RpcServerRegisterIfEx(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + RPC_MGR_EPV* MgrEpv, unsigned int Flags, + unsigned int MaxCalls, + RPC_IF_CALLBACK_FN* IfCallback); + WINPR_API RPC_STATUS RpcServerRegisterIf2(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + RPC_MGR_EPV* MgrEpv, unsigned int Flags, + unsigned int MaxCalls, unsigned int MaxRpcSize, + RPC_IF_CALLBACK_FN* IfCallbackFn); + WINPR_API RPC_STATUS RpcServerUnregisterIf(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + unsigned int WaitForCallsToComplete); + WINPR_API RPC_STATUS RpcServerUnregisterIfEx(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + int RundownContextHandles); + WINPR_API RPC_STATUS RpcServerUseAllProtseqs(unsigned int MaxCalls, void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseAllProtseqsEx(unsigned int MaxCalls, void* SecurityDescriptor, + PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseAllProtseqsIf(unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseAllProtseqsIfEx(unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor, PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseProtseqA(RPC_CSTR Protseq, unsigned int MaxCalls, + void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseProtseqExA(RPC_CSTR Protseq, unsigned int MaxCalls, + void* SecurityDescriptor, PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseProtseqW(RPC_WSTR Protseq, unsigned int MaxCalls, + void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseProtseqExW(RPC_WSTR Protseq, unsigned int MaxCalls, + void* SecurityDescriptor, PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseProtseqEpA(RPC_CSTR Protseq, unsigned int MaxCalls, + RPC_CSTR Endpoint, void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseProtseqEpExA(RPC_CSTR Protseq, unsigned int MaxCalls, + RPC_CSTR Endpoint, void* SecurityDescriptor, + PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseProtseqEpW(RPC_WSTR Protseq, unsigned int MaxCalls, + RPC_WSTR Endpoint, void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseProtseqEpExW(RPC_WSTR Protseq, unsigned int MaxCalls, + RPC_WSTR Endpoint, void* SecurityDescriptor, + PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseProtseqIfA(RPC_CSTR Protseq, unsigned int MaxCalls, + RPC_IF_HANDLE IfSpec, void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseProtseqIfExA(RPC_CSTR Protseq, unsigned int MaxCalls, + RPC_IF_HANDLE IfSpec, void* SecurityDescriptor, + PRPC_POLICY Policy); + WINPR_API RPC_STATUS RpcServerUseProtseqIfW(RPC_WSTR Protseq, unsigned int MaxCalls, + RPC_IF_HANDLE IfSpec, void* SecurityDescriptor); + WINPR_API RPC_STATUS RpcServerUseProtseqIfExW(RPC_WSTR Protseq, unsigned int MaxCalls, + RPC_IF_HANDLE IfSpec, void* SecurityDescriptor, + PRPC_POLICY Policy); + WINPR_API void RpcServerYield(void); + WINPR_API RPC_STATUS RpcMgmtStatsVectorFree(RPC_STATS_VECTOR** StatsVector); + WINPR_API RPC_STATUS RpcMgmtInqStats(RPC_BINDING_HANDLE Binding, RPC_STATS_VECTOR** Statistics); + WINPR_API RPC_STATUS RpcMgmtIsServerListening(RPC_BINDING_HANDLE Binding); + WINPR_API RPC_STATUS RpcMgmtStopServerListening(RPC_BINDING_HANDLE Binding); + WINPR_API RPC_STATUS RpcMgmtWaitServerListen(void); + WINPR_API RPC_STATUS RpcMgmtSetServerStackSize(unsigned long ThreadStackSize); + WINPR_API void RpcSsDontSerializeContext(void); + WINPR_API RPC_STATUS RpcMgmtEnableIdleCleanup(void); + WINPR_API RPC_STATUS RpcMgmtInqIfIds(RPC_BINDING_HANDLE Binding, RPC_IF_ID_VECTOR** IfIdVector); + WINPR_API RPC_STATUS RpcIfIdVectorFree(RPC_IF_ID_VECTOR** IfIdVector); + WINPR_API RPC_STATUS RpcMgmtInqServerPrincNameA(RPC_BINDING_HANDLE Binding, + unsigned long AuthnSvc, + RPC_CSTR* ServerPrincName); + WINPR_API RPC_STATUS RpcMgmtInqServerPrincNameW(RPC_BINDING_HANDLE Binding, + unsigned long AuthnSvc, + RPC_WSTR* ServerPrincName); + WINPR_API RPC_STATUS RpcServerInqDefaultPrincNameA(unsigned long AuthnSvc, RPC_CSTR* PrincName); + WINPR_API RPC_STATUS RpcServerInqDefaultPrincNameW(unsigned long AuthnSvc, RPC_WSTR* PrincName); + WINPR_API RPC_STATUS RpcEpResolveBinding(RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec); + WINPR_API RPC_STATUS RpcNsBindingInqEntryNameA(RPC_BINDING_HANDLE Binding, + unsigned long EntryNameSyntax, + RPC_CSTR* EntryName); + WINPR_API RPC_STATUS RpcNsBindingInqEntryNameW(RPC_BINDING_HANDLE Binding, + unsigned long EntryNameSyntax, + RPC_WSTR* EntryName); + + WINPR_API RPC_STATUS RpcImpersonateClient(RPC_BINDING_HANDLE BindingHandle); + WINPR_API RPC_STATUS RpcRevertToSelfEx(RPC_BINDING_HANDLE BindingHandle); + WINPR_API RPC_STATUS RpcRevertToSelf(void); + WINPR_API RPC_STATUS RpcBindingInqAuthClientA(RPC_BINDING_HANDLE ClientBinding, + RPC_AUTHZ_HANDLE* Privs, + RPC_CSTR* ServerPrincName, + unsigned long* AuthnLevel, + unsigned long* AuthnSvc, unsigned long* AuthzSvc); + WINPR_API RPC_STATUS RpcBindingInqAuthClientW(RPC_BINDING_HANDLE ClientBinding, + RPC_AUTHZ_HANDLE* Privs, + RPC_WSTR* ServerPrincName, + unsigned long* AuthnLevel, + unsigned long* AuthnSvc, unsigned long* AuthzSvc); + WINPR_API RPC_STATUS RpcBindingInqAuthClientExA(RPC_BINDING_HANDLE ClientBinding, + RPC_AUTHZ_HANDLE* Privs, + RPC_CSTR* ServerPrincName, + unsigned long* AuthnLevel, + unsigned long* AuthnSvc, + unsigned long* AuthzSvc, unsigned long Flags); + WINPR_API RPC_STATUS RpcBindingInqAuthClientExW(RPC_BINDING_HANDLE ClientBinding, + RPC_AUTHZ_HANDLE* Privs, + RPC_WSTR* ServerPrincName, + unsigned long* AuthnLevel, + unsigned long* AuthnSvc, + unsigned long* AuthzSvc, unsigned long Flags); + WINPR_API RPC_STATUS RpcBindingInqAuthInfoA(RPC_BINDING_HANDLE Binding, + RPC_CSTR* ServerPrincName, + unsigned long* AuthnLevel, unsigned long* AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, + unsigned long* AuthzSvc); + WINPR_API RPC_STATUS RpcBindingInqAuthInfoW(RPC_BINDING_HANDLE Binding, + RPC_WSTR* ServerPrincName, + unsigned long* AuthnLevel, unsigned long* AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, + unsigned long* AuthzSvc); + WINPR_API RPC_STATUS RpcBindingSetAuthInfoA(RPC_BINDING_HANDLE Binding, + RPC_CSTR ServerPrincName, unsigned long AuthnLevel, + unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, + unsigned long AuthzSvc); + WINPR_API RPC_STATUS RpcBindingSetAuthInfoExA(RPC_BINDING_HANDLE Binding, + RPC_CSTR ServerPrincName, + unsigned long AuthnLevel, unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, + unsigned long AuthzSvc, + RPC_SECURITY_QOS* SecurityQos); + WINPR_API RPC_STATUS RpcBindingSetAuthInfoW(RPC_BINDING_HANDLE Binding, + RPC_WSTR ServerPrincName, unsigned long AuthnLevel, + unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, + unsigned long AuthzSvc); + WINPR_API RPC_STATUS RpcBindingSetAuthInfoExW(RPC_BINDING_HANDLE Binding, + RPC_WSTR ServerPrincName, + unsigned long AuthnLevel, unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, + unsigned long AuthzSvc, + RPC_SECURITY_QOS* SecurityQOS); + WINPR_API RPC_STATUS RpcBindingInqAuthInfoExA( + RPC_BINDING_HANDLE Binding, RPC_CSTR* ServerPrincName, unsigned long* AuthnLevel, + unsigned long* AuthnSvc, RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, unsigned long* AuthzSvc, + unsigned long RpcQosVersion, RPC_SECURITY_QOS* SecurityQOS); + WINPR_API RPC_STATUS RpcBindingInqAuthInfoExW( + RPC_BINDING_HANDLE Binding, RPC_WSTR* ServerPrincName, unsigned long* AuthnLevel, + unsigned long* AuthnSvc, RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, unsigned long* AuthzSvc, + unsigned long RpcQosVersion, RPC_SECURITY_QOS* SecurityQOS); + + WINPR_API RPC_STATUS RpcServerRegisterAuthInfoA(RPC_CSTR ServerPrincName, + unsigned long AuthnSvc, + RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFn, void* Arg); + WINPR_API RPC_STATUS RpcServerRegisterAuthInfoW(RPC_WSTR ServerPrincName, + unsigned long AuthnSvc, + RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFn, void* Arg); + + WINPR_API RPC_STATUS RpcBindingServerFromClient(RPC_BINDING_HANDLE ClientBinding, + RPC_BINDING_HANDLE* ServerBinding); + WINPR_API DECLSPEC_NORETURN void RpcRaiseException(RPC_STATUS exception); + WINPR_API RPC_STATUS RpcTestCancel(void); + WINPR_API RPC_STATUS RpcServerTestCancel(RPC_BINDING_HANDLE BindingHandle); + WINPR_API RPC_STATUS RpcCancelThread(void* Thread); + WINPR_API RPC_STATUS RpcCancelThreadEx(void* Thread, long Timeout); + + WINPR_API RPC_STATUS UuidCreate(UUID* Uuid); + WINPR_API RPC_STATUS UuidCreateSequential(UUID* Uuid); + WINPR_API RPC_STATUS UuidToStringA(const UUID* Uuid, RPC_CSTR* StringUuid); + WINPR_API RPC_STATUS UuidFromStringA(RPC_CSTR StringUuid, UUID* Uuid); + WINPR_API RPC_STATUS UuidToStringW(const UUID* Uuid, RPC_WSTR* StringUuid); + WINPR_API RPC_STATUS UuidFromStringW(RPC_WSTR StringUuid, UUID* Uuid); + WINPR_API signed int UuidCompare(const UUID* Uuid1, const UUID* Uuid2, RPC_STATUS* Status); + WINPR_API RPC_STATUS UuidCreateNil(UUID* NilUuid); + WINPR_API int UuidEqual(const UUID* Uuid1, const UUID* Uuid2, RPC_STATUS* Status); + WINPR_API unsigned short UuidHash(const UUID* Uuid, RPC_STATUS* Status); + WINPR_API int UuidIsNil(const UUID* Uuid, RPC_STATUS* Status); + + WINPR_API RPC_STATUS RpcEpRegisterNoReplaceA(RPC_IF_HANDLE IfSpec, + RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_CSTR Annotation); + WINPR_API RPC_STATUS RpcEpRegisterNoReplaceW(RPC_IF_HANDLE IfSpec, + RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_WSTR Annotation); + WINPR_API RPC_STATUS RpcEpRegisterA(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_CSTR Annotation); + WINPR_API RPC_STATUS RpcEpRegisterW(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_WSTR Annotation); + WINPR_API RPC_STATUS RpcEpUnregister(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector); + + WINPR_API RPC_STATUS DceErrorInqTextA(RPC_STATUS RpcStatus, RPC_CSTR ErrorText); + WINPR_API RPC_STATUS DceErrorInqTextW(RPC_STATUS RpcStatus, RPC_WSTR ErrorText); + + WINPR_API RPC_STATUS RpcMgmtEpEltInqBegin(RPC_BINDING_HANDLE EpBinding, + unsigned long InquiryType, RPC_IF_ID* IfId, + unsigned long VersOption, UUID* ObjectUuid, + RPC_EP_INQ_HANDLE* InquiryContext); + WINPR_API RPC_STATUS RpcMgmtEpEltInqDone(RPC_EP_INQ_HANDLE* InquiryContext); + WINPR_API RPC_STATUS RpcMgmtEpEltInqNextA(RPC_EP_INQ_HANDLE InquiryContext, RPC_IF_ID* IfId, + RPC_BINDING_HANDLE* Binding, UUID* ObjectUuid, + RPC_CSTR* Annotation); + WINPR_API RPC_STATUS RpcMgmtEpEltInqNextW(RPC_EP_INQ_HANDLE InquiryContext, RPC_IF_ID* IfId, + RPC_BINDING_HANDLE* Binding, UUID* ObjectUuid, + RPC_WSTR* Annotation); + WINPR_API RPC_STATUS RpcMgmtEpUnregister(RPC_BINDING_HANDLE EpBinding, RPC_IF_ID* IfId, + RPC_BINDING_HANDLE Binding, UUID* ObjectUuid); + WINPR_API RPC_STATUS RpcMgmtSetAuthorizationFn(RPC_MGMT_AUTHORIZATION_FN AuthorizationFn); + + WINPR_API RPC_STATUS RpcServerInqBindingHandle(RPC_BINDING_HANDLE* Binding); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* WINPR_RPC_H */ diff --git a/winpr/include/winpr/sam.h b/winpr/include/winpr/sam.h new file mode 100644 index 0000000..c1efaa1 --- /dev/null +++ b/winpr/include/winpr/sam.h @@ -0,0 +1,59 @@ +/** + * WinPR: Windows Portable Runtime + * Security Accounts Manager (SAM) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_SAM_H +#define WINPR_UTILS_SAM_H + +#include +#include + +typedef struct winpr_sam WINPR_SAM; + +struct winpr_sam_entry +{ + LPSTR User; + UINT32 UserLength; + LPSTR Domain; + UINT32 DomainLength; + BYTE LmHash[16]; + BYTE NtHash[16]; +}; +typedef struct winpr_sam_entry WINPR_SAM_ENTRY; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, + LPCSTR Domain, UINT32 DomainLength); + WINPR_API WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, + LPCWSTR Domain, UINT32 DomainLength); + + WINPR_API void SamResetEntry(WINPR_SAM_ENTRY* entry); + WINPR_API void SamFreeEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry); + + WINPR_API WINPR_SAM* SamOpen(const char* filename, BOOL readOnly); + WINPR_API void SamClose(WINPR_SAM* sam); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_UTILS_SAM_H */ diff --git a/winpr/include/winpr/schannel.h b/winpr/include/winpr/schannel.h new file mode 100644 index 0000000..e4d5fab --- /dev/null +++ b/winpr/include/winpr/schannel.h @@ -0,0 +1,284 @@ +/** + * WinPR: Windows Portable Runtime + * Schannel Security Package + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_SCHANNEL_H +#define WINPR_SSPI_SCHANNEL_H + +#include +#include + +#if defined(_WIN32) && !defined(_UWP) + +#include + +#else + +#define SCHANNEL_NAME_A "Schannel" +#define SCHANNEL_NAME_W L"Schannel" + +#ifdef _UNICODE +#define SCHANNEL_NAME SCHANNEL_NAME_W +#else +#define SCHANNEL_NAME SCHANNEL_NAME_A +#endif + +#define SECPKG_ATTR_SUPPORTED_ALGS 86 +#define SECPKG_ATTR_CIPHER_STRENGTHS 87 +#define SECPKG_ATTR_SUPPORTED_PROTOCOLS 88 + +typedef struct +{ + DWORD cSupportedAlgs; + ALG_ID* palgSupportedAlgs; +} SecPkgCred_SupportedAlgs, *PSecPkgCred_SupportedAlgs; + +typedef struct +{ + DWORD dwMinimumCipherStrength; + DWORD dwMaximumCipherStrength; +} SecPkgCred_CipherStrengths, *PSecPkgCred_CipherStrengths; + +typedef struct +{ + DWORD grbitProtocol; +} SecPkgCred_SupportedProtocols, *PSecPkgCred_SupportedProtocols; + +enum eTlsSignatureAlgorithm +{ + TlsSignatureAlgorithm_Anonymous = 0, + TlsSignatureAlgorithm_Rsa = 1, + TlsSignatureAlgorithm_Dsa = 2, + TlsSignatureAlgorithm_Ecdsa = 3 +}; + +enum eTlsHashAlgorithm +{ + TlsHashAlgorithm_None = 0, + TlsHashAlgorithm_Md5 = 1, + TlsHashAlgorithm_Sha1 = 2, + TlsHashAlgorithm_Sha224 = 3, + TlsHashAlgorithm_Sha256 = 4, + TlsHashAlgorithm_Sha384 = 5, + TlsHashAlgorithm_Sha512 = 6 +}; + +#define SCH_CRED_V1 0x00000001 +#define SCH_CRED_V2 0x00000002 +#define SCH_CRED_VERSION 0x00000002 +#define SCH_CRED_V3 0x00000003 +#define SCHANNEL_CRED_VERSION 0x00000004 + +typedef struct +{ + DWORD dwVersion; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; + + DWORD cSupportedAlgs; + ALG_ID* palgSupportedAlgs; + + DWORD grbitEnabledProtocols; + DWORD dwMinimumCipherStrength; + DWORD dwMaximumCipherStrength; + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD dwCredFormat; +} SCHANNEL_CRED, *PSCHANNEL_CRED; + +#define SCH_CRED_FORMAT_CERT_CONTEXT 0x00000000 +#define SCH_CRED_FORMAT_CERT_HASH 0x00000001 +#define SCH_CRED_FORMAT_CERT_HASH_STORE 0x00000002 + +#define SCH_CRED_MAX_STORE_NAME_SIZE 128 +#define SCH_CRED_MAX_SUPPORTED_ALGS 256 +#define SCH_CRED_MAX_SUPPORTED_CERTS 100 + +typedef struct +{ + DWORD dwLength; + DWORD dwFlags; + HCRYPTPROV hProv; + BYTE ShaHash[20]; +} SCHANNEL_CERT_HASH, *PSCHANNEL_CERT_HASH; + +typedef struct +{ + DWORD dwLength; + DWORD dwFlags; + HCRYPTPROV hProv; + BYTE ShaHash[20]; + WCHAR pwszStoreName[SCH_CRED_MAX_STORE_NAME_SIZE]; +} SCHANNEL_CERT_HASH_STORE, *PSCHANNEL_CERT_HASH_STORE; + +#define SCH_MACHINE_CERT_HASH 0x00000001 + +#define SCH_CRED_NO_SYSTEM_MAPPER 0x00000002 +#define SCH_CRED_NO_SERVERNAME_CHECK 0x00000004 +#define SCH_CRED_MANUAL_CRED_VALIDATION 0x00000008 +#define SCH_CRED_NO_DEFAULT_CREDS 0x00000010 +#define SCH_CRED_AUTO_CRED_VALIDATION 0x00000020 +#define SCH_CRED_USE_DEFAULT_CREDS 0x00000040 +#define SCH_CRED_DISABLE_RECONNECTS 0x00000080 + +#define SCH_CRED_REVOCATION_CHECK_END_CERT 0x00000100 +#define SCH_CRED_REVOCATION_CHECK_CHAIN 0x00000200 +#define SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x00000400 +#define SCH_CRED_IGNORE_NO_REVOCATION_CHECK 0x00000800 +#define SCH_CRED_IGNORE_REVOCATION_OFFLINE 0x00001000 + +#define SCH_CRED_RESTRICTED_ROOTS 0x00002000 +#define SCH_CRED_REVOCATION_CHECK_CACHE_ONLY 0x00004000 +#define SCH_CRED_CACHE_ONLY_URL_RETRIEVAL 0x00008000 + +#define SCH_CRED_MEMORY_STORE_CERT 0x00010000 + +#define SCH_CRED_CACHE_ONLY_URL_RETRIEVAL_ON_CREATE 0x00020000 + +#define SCH_SEND_ROOT_CERT 0x00040000 +#define SCH_CRED_SNI_CREDENTIAL 0x00080000 +#define SCH_CRED_SNI_ENABLE_OCSP 0x00100000 +#define SCH_SEND_AUX_RECORD 0x00200000 + +#define SCHANNEL_RENEGOTIATE 0 +#define SCHANNEL_SHUTDOWN 1 +#define SCHANNEL_ALERT 2 +#define SCHANNEL_SESSION 3 + +typedef struct +{ + DWORD dwTokenType; + DWORD dwAlertType; + DWORD dwAlertNumber; +} SCHANNEL_ALERT_TOKEN; + +#define TLS1_ALERT_WARNING 1 +#define TLS1_ALERT_FATAL 2 + +#define TLS1_ALERT_CLOSE_NOTIFY 0 +#define TLS1_ALERT_UNEXPECTED_MESSAGE 10 +#define TLS1_ALERT_BAD_RECORD_MAC 20 +#define TLS1_ALERT_DECRYPTION_FAILED 21 +#define TLS1_ALERT_RECORD_OVERFLOW 22 +#define TLS1_ALERT_DECOMPRESSION_FAIL 30 +#define TLS1_ALERT_HANDSHAKE_FAILURE 40 +#define TLS1_ALERT_BAD_CERTIFICATE 42 +#define TLS1_ALERT_UNSUPPORTED_CERT 43 +#define TLS1_ALERT_CERTIFICATE_REVOKED 44 +#define TLS1_ALERT_CERTIFICATE_EXPIRED 45 +#define TLS1_ALERT_CERTIFICATE_UNKNOWN 46 +#define TLS1_ALERT_ILLEGAL_PARAMETER 47 +#define TLS1_ALERT_UNKNOWN_CA 48 +#define TLS1_ALERT_ACCESS_DENIED 49 +#define TLS1_ALERT_DECODE_ERROR 50 +#define TLS1_ALERT_DECRYPT_ERROR 51 +#define TLS1_ALERT_EXPORT_RESTRICTION 60 +#define TLS1_ALERT_PROTOCOL_VERSION 70 +#define TLS1_ALERT_INSUFFIENT_SECURITY 71 +#define TLS1_ALERT_INTERNAL_ERROR 80 +#define TLS1_ALERT_USER_CANCELED 90 +#define TLS1_ALERT_NO_RENEGOTIATION 100 +#define TLS1_ALERT_UNSUPPORTED_EXT 110 + +#define SSL_SESSION_ENABLE_RECONNECTS 1 +#define SSL_SESSION_DISABLE_RECONNECTS 2 + +typedef struct +{ + DWORD dwTokenType; + DWORD dwFlags; +} SCHANNEL_SESSION_TOKEN; + +typedef struct +{ + DWORD cbLength; + ALG_ID aiHash; + DWORD cbHash; + BYTE HashValue[36]; + BYTE CertThumbprint[20]; +} SCHANNEL_CLIENT_SIGNATURE, *PSCHANNEL_CLIENT_SIGNATURE; + +#define SP_PROT_SSL3_SERVER 0x00000010 +#define SP_PROT_SSL3_CLIENT 0x00000020 +#define SP_PROT_SSL3 (SP_PROT_SSL3_SERVER | SP_PROT_SSL3_CLIENT) + +#define SP_PROT_TLS1_SERVER 0x00000040 +#define SP_PROT_TLS1_CLIENT 0x00000080 +#define SP_PROT_TLS1 (SP_PROT_TLS1_SERVER | SP_PROT_TLS1_CLIENT) + +#define SP_PROT_SSL3TLS1_CLIENTS (SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT) +#define SP_PROT_SSL3TLS1_SERVERS (SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER) +#define SP_PROT_SSL3TLS1 (SP_PROT_SSL3 | SP_PROT_TLS1) + +#define SP_PROT_UNI_SERVER 0x40000000 +#define SP_PROT_UNI_CLIENT 0x80000000 +#define SP_PROT_UNI (SP_PROT_UNI_SERVER | SP_PROT_UNI_CLIENT) + +#define SP_PROT_ALL 0xFFFFFFFF +#define SP_PROT_NONE 0 +#define SP_PROT_CLIENTS (SP_PROT_SSL3_CLIENT | SP_PROT_UNI_CLIENT | SP_PROT_TLS1_CLIENT) +#define SP_PROT_SERVERS (SP_PROT_SSL3_SERVER | SP_PROT_UNI_SERVER | SP_PROT_TLS1_SERVER) + +#define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER +#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT +#define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_0_CLIENT) + +#define SP_PROT_TLS1_1_SERVER 0x00000100 +#define SP_PROT_TLS1_1_CLIENT 0x00000200 +#define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_1_CLIENT) + +#define SP_PROT_TLS1_2_SERVER 0x00000400 +#define SP_PROT_TLS1_2_CLIENT 0x00000800 +#define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_2_CLIENT) + +#define SP_PROT_DTLS_SERVER 0x00010000 +#define SP_PROT_DTLS_CLIENT 0x00020000 +#define SP_PROT_DTLS (SP_PROT_DTLS_SERVER | SP_PROT_DTLS_CLIENT) + +#define SP_PROT_DTLS1_0_SERVER SP_PROT_DTLS_SERVER +#define SP_PROT_DTLS1_0_CLIENT SP_PROT_DTLS_CLIENT +#define SP_PROT_DTLS1_0 (SP_PROT_DTLS1_0_SERVER | SP_PROT_DTLS1_0_CLIENT) + +#define SP_PROT_DTLS1_X_SERVER SP_PROT_DTLS1_0_SERVER + +#define SP_PROT_DTLS1_X_CLIENT SP_PROT_DTLS1_0_CLIENT + +#define SP_PROT_DTLS1_X (SP_PROT_DTLS1_X_SERVER | SP_PROT_DTLS1_X_CLIENT) + +#define SP_PROT_TLS1_1PLUS_SERVER (SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER) +#define SP_PROT_TLS1_1PLUS_CLIENT (SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT) + +#define SP_PROT_TLS1_1PLUS (SP_PROT_TLS1_1PLUS_SERVER | SP_PROT_TLS1_1PLUS_CLIENT) + +#define SP_PROT_TLS1_X_SERVER \ + (SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER) +#define SP_PROT_TLS1_X_CLIENT \ + (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT) +#define SP_PROT_TLS1_X (SP_PROT_TLS1_X_SERVER | SP_PROT_TLS1_X_CLIENT) + +#define SP_PROT_SSL3TLS1_X_CLIENTS (SP_PROT_TLS1_X_CLIENT | SP_PROT_SSL3_CLIENT) +#define SP_PROT_SSL3TLS1_X_SERVERS (SP_PROT_TLS1_X_SERVER | SP_PROT_SSL3_SERVER) +#define SP_PROT_SSL3TLS1_X (SP_PROT_SSL3 | SP_PROT_TLS1_X) + +#define SP_PROT_X_CLIENTS (SP_PROT_CLIENTS | SP_PROT_TLS1_X_CLIENT | SP_PROT_DTLS1_X_CLIENT) +#define SP_PROT_X_SERVERS (SP_PROT_SERVERS | SP_PROT_TLS1_X_SERVER | SP_PROT_DTLS1_X_SERVER) + +#endif + +#endif /* WINPR_SSPI_SCHANNEL_H */ diff --git a/winpr/include/winpr/secapi.h b/winpr/include/winpr/secapi.h new file mode 100644 index 0000000..e191070 --- /dev/null +++ b/winpr/include/winpr/secapi.h @@ -0,0 +1,78 @@ +/** + * WinPR: Windows Portable Runtime + * Schannel Security Package + * + * Copyright 2023 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SECAPI_H_ +#define WINPR_SECAPI_H_ + +#ifdef _WIN32 +#define _NTDEF_ +#include +#else + +#include + +typedef enum _KERB_LOGON_SUBMIT_TYPE +{ + KerbInteractiveLogon = 2, + KerbSmartCardLogon = 6, + KerbWorkstationUnlockLogon = 7, + KerbSmartCardUnlockLogon = 8, + KerbProxyLogon = 9, + KerbTicketLogon = 10, + KerbTicketUnlockLogon = 11, + KerbS4ULogon = 12, + KerbCertificateLogon = 13, + KerbCertificateS4ULogon = 14, + KerbCertificateUnlockLogon = 15, + KerbNoElevationLogon = 83, + KerbLuidLogon = 84 +} KERB_LOGON_SUBMIT_TYPE, + *PKERB_LOGON_SUBMIT_TYPE; + +typedef struct _KERB_TICKET_LOGON +{ + KERB_LOGON_SUBMIT_TYPE MessageType; + ULONG Flags; + ULONG ServiceTicketLength; + ULONG TicketGrantingTicketLength; + PUCHAR ServiceTicket; + PUCHAR TicketGrantingTicket; +} KERB_TICKET_LOGON, *PKERB_TICKET_LOGON; + +#define KERB_LOGON_FLAG_ALLOW_EXPIRED_TICKET 0x1 + +#define MSV1_0_OWF_PASSWORD_LENGTH 16 + +typedef struct _MSV1_0_SUPPLEMENTAL_CREDENTIAL +{ + ULONG Version; + ULONG Flags; + UCHAR LmPassword[MSV1_0_OWF_PASSWORD_LENGTH]; + UCHAR NtPassword[MSV1_0_OWF_PASSWORD_LENGTH]; +} MSV1_0_SUPPLEMENTAL_CREDENTIAL, *PMSV1_0_SUPPLEMENTAL_CREDENTIAL; + +#define MSV1_0_CRED_VERSION_REMOTE 0xffff0002 + +#endif /* _WIN32 */ + +#ifndef KERB_LOGON_FLAG_REDIRECTED +#define KERB_LOGON_FLAG_REDIRECTED 0x2 +#endif + +#endif /* WINPR_SECAPI_H_ */ diff --git a/winpr/include/winpr/security.h b/winpr/include/winpr/security.h new file mode 100644 index 0000000..0d71b21 --- /dev/null +++ b/winpr/include/winpr/security.h @@ -0,0 +1,449 @@ +/** + * WinPR: Windows Portable Runtime + * Security Definitions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SECURITY_H +#define WINPR_SECURITY_H + +#include +#include + +/** + * Windows Integrity Mechanism Design: + * http://msdn.microsoft.com/en-us/library/bb625963.aspx + */ + +#ifndef _WIN32 + +#include + +#define ANYSIZE_ARRAY 1 + +typedef enum +{ + SecurityAnonymous, + SecurityIdentification, + SecurityImpersonation, + SecurityDelegation +} SECURITY_IMPERSONATION_LEVEL, + *PSECURITY_IMPERSONATION_LEVEL; + +#define SECURITY_MAX_IMPERSONATION_LEVEL SecurityDelegation +#define SECURITY_MIN_IMPERSONATION_LEVEL SecurityAnonymous +#define DEFAULT_IMPERSONATION_LEVEL SecurityImpersonation +#define VALID_IMPERSONATION_LEVEL(L) \ + (((L) >= SECURITY_MIN_IMPERSONATION_LEVEL) && ((L) <= SECURITY_MAX_IMPERSONATION_LEVEL)) + +#define TOKEN_ASSIGN_PRIMARY (0x0001) +#define TOKEN_DUPLICATE (0x0002) +#define TOKEN_IMPERSONATE (0x0004) +#define TOKEN_QUERY (0x0008) +#define TOKEN_QUERY_SOURCE (0x0010) +#define TOKEN_ADJUST_PRIVILEGES (0x0020) +#define TOKEN_ADJUST_GROUPS (0x0040) +#define TOKEN_ADJUST_DEFAULT (0x0080) +#define TOKEN_ADJUST_SESSIONID (0x0100) + +#define TOKEN_ALL_ACCESS_P \ + (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | \ + TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | \ + TOKEN_ADJUST_DEFAULT) + +#define TOKEN_ALL_ACCESS (TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID) + +#define TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) + +#define TOKEN_WRITE \ + (STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT) + +#define TOKEN_EXECUTE (STANDARD_RIGHTS_EXECUTE) + +#define TOKEN_MANDATORY_POLICY_OFF 0x0 +#define TOKEN_MANDATORY_POLICY_NO_WRITE_UP 0x1 +#define TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN 0x2 + +#define TOKEN_MANDATORY_POLICY_VALID_MASK \ + (TOKEN_MANDATORY_POLICY_NO_WRITE_UP | TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN) + +#define POLICY_AUDIT_SUBCATEGORY_COUNT (56) + +#define TOKEN_SOURCE_LENGTH 8 + +#define SID_REVISION (1) +#define SID_MAX_SUB_AUTHORITIES (15) +#define SID_RECOMMENDED_SUB_AUTHORITIES (1) + +#define SID_HASH_SIZE 32 + +#define SECURITY_MANDATORY_UNTRUSTED_RID 0x0000 +#define SECURITY_MANDATORY_LOW_RID 0x1000 +#define SECURITY_MANDATORY_MEDIUM_RID 0x2000 +#define SECURITY_MANDATORY_HIGH_RID 0x3000 +#define SECURITY_MANDATORY_SYSTEM_RID 0x4000 + +#define SECURITY_NULL_SID_AUTHORITY \ + { \ + 0, 0, 0, 0, 0, 0 \ + } +#define SECURITY_WORLD_SID_AUTHORITY \ + { \ + 0, 0, 0, 0, 0, 1 \ + } +#define SECURITY_LOCAL_SID_AUTHORITY \ + { \ + 0, 0, 0, 0, 0, 2 \ + } +#define SECURITY_CREATOR_SID_AUTHORITY \ + { \ + 0, 0, 0, 0, 0, 3 \ + } +#define SECURITY_NON_UNIQUE_AUTHORITY \ + { \ + 0, 0, 0, 0, 0, 4 \ + } +#define SECURITY_RESOURCE_MANAGER_AUTHORITY \ + { \ + 0, 0, 0, 0, 0, 9 \ + } + +#define SECURITY_NULL_RID (0x00000000L) +#define SECURITY_WORLD_RID (0x00000000L) +#define SECURITY_LOCAL_RID (0x00000000L) +#define SECURITY_LOCAL_LOGON_RID (0x00000001L) + +#define SECURITY_CREATOR_OWNER_RID (0x00000000L) +#define SECURITY_CREATOR_GROUP_RID (0x00000001L) +#define SECURITY_CREATOR_OWNER_SERVER_RID (0x00000002L) +#define SECURITY_CREATOR_GROUP_SERVER_RID (0x00000003L) +#define SECURITY_CREATOR_OWNER_RIGHTS_RID (0x00000004L) + +typedef PVOID PACCESS_TOKEN; +typedef PVOID PCLAIMS_BLOB; + +typedef struct +{ + LUID Luid; + DWORD Attributes; +} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES; +typedef LUID_AND_ATTRIBUTES LUID_AND_ATTRIBUTES_ARRAY[ANYSIZE_ARRAY]; +typedef LUID_AND_ATTRIBUTES_ARRAY* PLUID_AND_ATTRIBUTES_ARRAY; + +typedef struct +{ + BYTE Value[6]; +} SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY; + +typedef struct +{ + BYTE Revision; + BYTE SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + DWORD SubAuthority[ANYSIZE_ARRAY]; +} SID, *PISID; + +typedef enum +{ + SidTypeUser = 1, + SidTypeGroup, + SidTypeDomain, + SidTypeAlias, + SidTypeWellKnownGroup, + SidTypeDeletedAccount, + SidTypeInvalid, + SidTypeUnknown, + SidTypeComputer, + SidTypeLabel +} SID_NAME_USE, + *PSID_NAME_USE; + +typedef struct +{ + PSID Sid; + DWORD Attributes; +} SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES; + +typedef SID_AND_ATTRIBUTES SID_AND_ATTRIBUTES_ARRAY[ANYSIZE_ARRAY]; +typedef SID_AND_ATTRIBUTES_ARRAY* PSID_AND_ATTRIBUTES_ARRAY; + +typedef ULONG_PTR SID_HASH_ENTRY, *PSID_HASH_ENTRY; + +typedef struct +{ + DWORD SidCount; + PSID_AND_ATTRIBUTES SidAttr; + SID_HASH_ENTRY Hash[SID_HASH_SIZE]; +} SID_AND_ATTRIBUTES_HASH, *PSID_AND_ATTRIBUTES_HASH; + +typedef enum +{ + TokenPrimary = 1, + TokenImpersonation +} TOKEN_TYPE; +typedef TOKEN_TYPE* PTOKEN_TYPE; + +typedef enum +{ + TokenElevationTypeDefault = 1, + TokenElevationTypeFull, + TokenElevationTypeLimited +} TOKEN_ELEVATION_TYPE, + *PTOKEN_ELEVATION_TYPE; + +typedef enum +{ + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin, + TokenElevationType, + TokenLinkedToken, + TokenElevation, + TokenHasRestrictions, + TokenAccessInformation, + TokenVirtualizationAllowed, + TokenVirtualizationEnabled, + TokenIntegrityLevel, + TokenUIAccess, + TokenMandatoryPolicy, + TokenLogonSid, + TokenIsAppContainer, + TokenCapabilities, + TokenAppContainerSid, + TokenAppContainerNumber, + TokenUserClaimAttributes, + TokenDeviceClaimAttributes, + TokenRestrictedUserClaimAttributes, + TokenRestrictedDeviceClaimAttributes, + TokenDeviceGroups, + TokenRestrictedDeviceGroups, + TokenSecurityAttributes, + TokenIsRestricted, + MaxTokenInfoClass +} TOKEN_INFORMATION_CLASS, + *PTOKEN_INFORMATION_CLASS; + +typedef struct +{ + SID_AND_ATTRIBUTES User; +} TOKEN_USER, *PTOKEN_USER; + +typedef struct +{ + DWORD GroupCount; + SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY]; +} TOKEN_GROUPS, *PTOKEN_GROUPS; + +typedef struct +{ + DWORD PrivilegeCount; + LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; +} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES; + +typedef struct +{ + PSID Owner; +} TOKEN_OWNER, *PTOKEN_OWNER; + +typedef struct +{ + PSID PrimaryGroup; +} TOKEN_PRIMARY_GROUP, *PTOKEN_PRIMARY_GROUP; + +typedef struct +{ + PACL DefaultDacl; +} TOKEN_DEFAULT_DACL, *PTOKEN_DEFAULT_DACL; + +typedef struct +{ + PCLAIMS_BLOB UserClaims; +} TOKEN_USER_CLAIMS, *PTOKEN_USER_CLAIMS; + +typedef struct +{ + PCLAIMS_BLOB DeviceClaims; +} TOKEN_DEVICE_CLAIMS, *PTOKEN_DEVICE_CLAIMS; + +typedef struct +{ + DWORD SidCount; + DWORD SidLength; + PSID_AND_ATTRIBUTES Sids; + DWORD RestrictedSidCount; + DWORD RestrictedSidLength; + PSID_AND_ATTRIBUTES RestrictedSids; + DWORD PrivilegeCount; + DWORD PrivilegeLength; + PLUID_AND_ATTRIBUTES Privileges; + LUID AuthenticationId; +} TOKEN_GROUPS_AND_PRIVILEGES, *PTOKEN_GROUPS_AND_PRIVILEGES; + +typedef struct +{ + HANDLE LinkedToken; +} TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN; + +typedef struct +{ + DWORD TokenIsElevated; +} TOKEN_ELEVATION, *PTOKEN_ELEVATION; + +typedef struct +{ + SID_AND_ATTRIBUTES Label; +} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL; + +typedef struct +{ + DWORD Policy; +} TOKEN_MANDATORY_POLICY, *PTOKEN_MANDATORY_POLICY; + +typedef struct +{ + PSID_AND_ATTRIBUTES_HASH SidHash; + PSID_AND_ATTRIBUTES_HASH RestrictedSidHash; + PTOKEN_PRIVILEGES Privileges; + LUID AuthenticationId; + TOKEN_TYPE TokenType; + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; + TOKEN_MANDATORY_POLICY MandatoryPolicy; + DWORD Flags; + DWORD AppContainerNumber; + PSID PackageSid; + PSID_AND_ATTRIBUTES_HASH CapabilitiesHash; +} TOKEN_ACCESS_INFORMATION, *PTOKEN_ACCESS_INFORMATION; + +typedef struct +{ + BYTE PerUserPolicy[((POLICY_AUDIT_SUBCATEGORY_COUNT) >> 1) + 1]; +} TOKEN_AUDIT_POLICY, *PTOKEN_AUDIT_POLICY; + +typedef struct +{ + CHAR SourceName[TOKEN_SOURCE_LENGTH]; + LUID SourceIdentifier; +} TOKEN_SOURCE, *PTOKEN_SOURCE; + +typedef struct +{ + LUID TokenId; + LUID AuthenticationId; + LARGE_INTEGER ExpirationTime; + TOKEN_TYPE TokenType; + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; + DWORD DynamicCharged; + DWORD DynamicAvailable; + DWORD GroupCount; + DWORD PrivilegeCount; + LUID ModifiedId; +} TOKEN_STATISTICS, *PTOKEN_STATISTICS; + +typedef struct +{ + LUID TokenId; + LUID AuthenticationId; + LUID ModifiedId; + TOKEN_SOURCE TokenSource; +} TOKEN_CONTROL, *PTOKEN_CONTROL; + +typedef struct +{ + LUID OriginatingLogonSession; +} TOKEN_ORIGIN, *PTOKEN_ORIGIN; + +typedef enum +{ + MandatoryLevelUntrusted = 0, + MandatoryLevelLow, + MandatoryLevelMedium, + MandatoryLevelHigh, + MandatoryLevelSystem, + MandatoryLevelSecureProcess, + MandatoryLevelCount +} MANDATORY_LEVEL, + *PMANDATORY_LEVEL; + +typedef struct +{ + PSID TokenAppContainer; +} TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD dwRevision); + WINPR_API DWORD GetSecurityDescriptorLength(PSECURITY_DESCRIPTOR pSecurityDescriptor); + WINPR_API BOOL IsValidSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor); + + WINPR_API BOOL GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSECURITY_DESCRIPTOR_CONTROL pControl, + LPDWORD lpdwRevision); + WINPR_API BOOL SetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, + SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet); + + WINPR_API BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + LPBOOL lpbDaclPresent, PACL* pDacl, + LPBOOL lpbDaclDefaulted); + WINPR_API BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + BOOL bDaclPresent, PACL pDacl, BOOL bDaclDefaulted); + + WINPR_API BOOL GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSID* pGroup, LPBOOL lpbGroupDefaulted); + WINPR_API BOOL SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pGroup, + BOOL bGroupDefaulted); + + WINPR_API BOOL GetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSID* pOwner, LPBOOL lpbOwnerDefaulted); + WINPR_API BOOL SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pOwner, + BOOL bOwnerDefaulted); + + WINPR_API DWORD GetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, + PUCHAR RMControl); + WINPR_API DWORD SetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, + PUCHAR RMControl); + + WINPR_API BOOL GetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + LPBOOL lpbSaclPresent, PACL* pSacl, + LPBOOL lpbSaclDefaulted); + WINPR_API BOOL SetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + BOOL bSaclPresent, PACL pSacl, BOOL bSaclDefaulted); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* WINPR_SECURITY_H */ diff --git a/winpr/include/winpr/shell.h b/winpr/include/winpr/shell.h new file mode 100644 index 0000000..376ebb1 --- /dev/null +++ b/winpr/include/winpr/shell.h @@ -0,0 +1,108 @@ +/** + * WinPR: Windows Portable Runtime + * Shell Functions + * + * Copyright 2015 Dell Software + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SHELL_H +#define WINPR_SHELL_H + +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#include +#include + +#else + +/* Shell clipboard formats */ +typedef struct +{ + DWORD dwFlags; + CLSID clsid; + SIZEL sizel; + POINTL pointl; + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + WCHAR cFileName[260]; +} FILEDESCRIPTORW; + +/* Legacy definition, some types do not match the windows equivalent. */ +typedef struct +{ + DWORD dwFlags; + BYTE clsid[16]; + BYTE sizel[8]; + BYTE pointl[8]; + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + WCHAR cFileName[260]; +} FILEDESCRIPTOR; + +/* FILEDESCRIPTOR.dwFlags */ +typedef enum +{ + FD_CLSID = 0x00000001, + FD_SIZEPOINT = 0x00000002, + FD_ATTRIBUTES = 0x00000004, + FD_CREATETIME = 0x00000008, + FD_ACCESSTIME = 0x00000010, + FD_WRITESTIME = 0x00000020, + FD_FILESIZE = 0x00000040, + FD_PROGRESSUI = 0x00004000, + FD_LINKUI = 0x00008000, +} FD_FLAGS; +#define FD_UNICODE 0x80000000 + +/* Deprecated, here for compatibility */ +#define FD_SHOWPROGRESSUI FD_PROGRESSUI + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL GetUserProfileDirectoryA(HANDLE hToken, LPSTR lpProfileDir, LPDWORD lpcchSize); + + WINPR_API BOOL GetUserProfileDirectoryW(HANDLE hToken, LPWSTR lpProfileDir, LPDWORD lpcchSize); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define GetUserProfileDirectory GetUserProfileDirectoryW +#else +#define GetUserProfileDirectory GetUserProfileDirectoryA +#endif + +#endif + +#endif /* WINPR_SHELL_H */ diff --git a/winpr/include/winpr/smartcard.h b/winpr/include/winpr/smartcard.h new file mode 100644 index 0000000..278786c --- /dev/null +++ b/winpr/include/winpr/smartcard.h @@ -0,0 +1,1217 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_H +#define WINPR_SMARTCARD_H + +#include +#include +#include + +#include +#include + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifndef _WINSCARD_H_ +#define _WINSCARD_H_ /* do not include winscard.h */ +#endif + +WINPR_PRAGMA_DIAG_POP + +#ifndef SCARD_S_SUCCESS + +#define SCARD_S_SUCCESS NO_ERROR + +#define SCARD_F_INTERNAL_ERROR -2146435071l // (0x80100001L) +#define SCARD_E_CANCELLED -2146435070l // (0x80100002L) +#define SCARD_E_INVALID_HANDLE -2146435069l // (0x80100003L) +#define SCARD_E_INVALID_PARAMETER -2146435068l // (0x80100004L) +#define SCARD_E_INVALID_TARGET -2146435067l // (0x80100005L) +#define SCARD_E_NO_MEMORY -2146435066l // (0x80100006L) +#define SCARD_F_WAITED_TOO_LONG -2146435065l // (0x80100007L) +#define SCARD_E_INSUFFICIENT_BUFFER -2146435064l // (0x80100008L) +#define SCARD_E_UNKNOWN_READER -2146435063l // (0x80100009L) +#define SCARD_E_TIMEOUT -2146435062l // (0x8010000AL) +#define SCARD_E_SHARING_VIOLATION -2146435061l // (0x8010000BL) +#define SCARD_E_NO_SMARTCARD -2146435060l // (0x8010000CL) +#define SCARD_E_UNKNOWN_CARD -2146435059l // (0x8010000DL) +#define SCARD_E_CANT_DISPOSE -2146435058l // (0x8010000EL) +#define SCARD_E_PROTO_MISMATCH -2146435057l // (0x8010000FL) +#define SCARD_E_NOT_READY -2146435056l // (0x80100010L) +#define SCARD_E_INVALID_VALUE -2146435055l // (0x80100011L) +#define SCARD_E_SYSTEM_CANCELLED -2146435054l // (0x80100012L) +#define SCARD_F_COMM_ERROR -2146435053l // (0x80100013L) +#define SCARD_F_UNKNOWN_ERROR -2146435052l // (0x80100014L) +#define SCARD_E_INVALID_ATR -2146435051l // (0x80100015L) +#define SCARD_E_NOT_TRANSACTED -2146435050l // (0x80100016L) +#define SCARD_E_READER_UNAVAILABLE -2146435049l // (0x80100017L) +#define SCARD_P_SHUTDOWN -2146435048l // (0x80100018L) +#define SCARD_E_PCI_TOO_SMALL -2146435047l // (0x80100019L) +#define SCARD_E_READER_UNSUPPORTED -2146435046l // (0x8010001AL) +#define SCARD_E_DUPLICATE_READER -2146435045l // (0x8010001BL) +#define SCARD_E_CARD_UNSUPPORTED -2146435044l // (0x8010001CL) +#define SCARD_E_NO_SERVICE -2146435043l // (0x8010001DL) +#define SCARD_E_SERVICE_STOPPED -2146435042l // (0x8010001EL) +#define SCARD_E_UNEXPECTED -2146435041l // (0x8010001FL) +#define SCARD_E_ICC_INSTALLATION -2146435040l // (0x80100020L) +#define SCARD_E_ICC_CREATEORDER -2146435039l // (0x80100021L) +#define SCARD_E_UNSUPPORTED_FEATURE -2146435038l // (0x80100022L) +#define SCARD_E_DIR_NOT_FOUND -2146435037l // (0x80100023L) +#define SCARD_E_FILE_NOT_FOUND -2146435036l // (0x80100024L) +#define SCARD_E_NO_DIR -2146435035l // (0x80100025L) +#define SCARD_E_NO_FILE -2146435034l // (0x80100026L) +#define SCARD_E_NO_ACCESS -2146435033l // (0x80100027L) +#define SCARD_E_WRITE_TOO_MANY -2146435032l // (0x80100028L) +#define SCARD_E_BAD_SEEK -2146435031l // (0x80100029L) +#define SCARD_E_INVALID_CHV -2146435030l // (0x8010002AL) +#define SCARD_E_UNKNOWN_RES_MNG -2146435029l // (0x8010002BL) +#define SCARD_E_NO_SUCH_CERTIFICATE -2146435028l // (0x8010002CL) +#define SCARD_E_CERTIFICATE_UNAVAILABLE -2146435027l // (0x8010002DL) +#define SCARD_E_NO_READERS_AVAILABLE -2146435026l // (0x8010002EL) +#define SCARD_E_COMM_DATA_LOST -2146435025l // (0x8010002FL) +#define SCARD_E_NO_KEY_CONTAINER -2146435024l // (0x80100030L) +#define SCARD_E_SERVER_TOO_BUSY -2146435023l // (0x80100031L) +#define SCARD_E_PIN_CACHE_EXPIRED -2146435022l // (0x80100032L) +#define SCARD_E_NO_PIN_CACHE -2146435021l // (0x80100033L) +#define SCARD_E_READ_ONLY_CARD -2146435020l // (0x80100034L) + +#define SCARD_W_UNSUPPORTED_CARD -2146434971l // (0x80100065L) +#define SCARD_W_UNRESPONSIVE_CARD -2146434970l // (0x80100066L) +#define SCARD_W_UNPOWERED_CARD -2146434969l // (0x80100067L) +#define SCARD_W_RESET_CARD -2146434968l // (0x80100068L) +#define SCARD_W_REMOVED_CARD -2146434967l // (0x80100069L) +#define SCARD_W_SECURITY_VIOLATION -2146434966l // (0x8010006AL) +#define SCARD_W_WRONG_CHV -2146434965l // (0x8010006BL) +#define SCARD_W_CHV_BLOCKED -2146434964l // (0x8010006CL) +#define SCARD_W_EOF -2146434963l // (0x8010006DL) +#define SCARD_W_CANCELLED_BY_USER -2146434962l // (0x8010006EL) +#define SCARD_W_CARD_NOT_AUTHENTICATED -2146434961l // (0x8010006FL) +#define SCARD_W_CACHE_ITEM_NOT_FOUND -2146434960l // (0x80100070L) +#define SCARD_W_CACHE_ITEM_STALE -2146434959l // (0x80100071L) +#define SCARD_W_CACHE_ITEM_TOO_BIG -2146434958l // (0x80100072L) + +#endif + +/* ------------------------ missing definition with mingw --------------------*/ +#ifndef SCARD_E_PIN_CACHE_EXPIRED +#define SCARD_E_PIN_CACHE_EXPIRED -2146435022l // (0x80100032L) +#endif + +#ifndef SCARD_E_NO_PIN_CACHE +#define SCARD_E_NO_PIN_CACHE -2146435021l // (0x80100033L) +#endif + +#ifndef SCARD_E_READ_ONLY_CARD +#define SCARD_E_READ_ONLY_CARD -2146435020l // (0x80100034L) +#endif + +#ifndef SCARD_W_CACHE_ITEM_TOO_BIG +#define SCARD_W_CACHE_ITEM_TOO_BIG -2146434958l // (0x80100072L) +#endif +/* -------------------------------------------------------------------------- */ + +#define SCARD_ATR_LENGTH 33 + +#define SCARD_PROTOCOL_UNDEFINED 0x00000000u +#define SCARD_PROTOCOL_T0 0x00000001u +#define SCARD_PROTOCOL_T1 0x00000002u +#define SCARD_PROTOCOL_RAW 0x00010000u + +#define SCARD_PROTOCOL_Tx (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) +#define SCARD_PROTOCOL_DEFAULT 0x80000000u +#define SCARD_PROTOCOL_OPTIMAL 0x00000000u + +#define SCARD_POWER_DOWN 0 +#define SCARD_COLD_RESET 1 +#define SCARD_WARM_RESET 2 + +#define SCARD_CTL_CODE(code) \ + CTL_CODE(FILE_DEVICE_SMARTCARD, (code), METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SMARTCARD_POWER SCARD_CTL_CODE(1) +#define IOCTL_SMARTCARD_GET_ATTRIBUTE SCARD_CTL_CODE(2) +#define IOCTL_SMARTCARD_SET_ATTRIBUTE SCARD_CTL_CODE(3) +#define IOCTL_SMARTCARD_CONFISCATE SCARD_CTL_CODE(4) +#define IOCTL_SMARTCARD_TRANSMIT SCARD_CTL_CODE(5) +#define IOCTL_SMARTCARD_EJECT SCARD_CTL_CODE(6) +#define IOCTL_SMARTCARD_SWALLOW SCARD_CTL_CODE(7) +#define IOCTL_SMARTCARD_IS_PRESENT SCARD_CTL_CODE(10) +#define IOCTL_SMARTCARD_IS_ABSENT SCARD_CTL_CODE(11) +#define IOCTL_SMARTCARD_SET_PROTOCOL SCARD_CTL_CODE(12) +#define IOCTL_SMARTCARD_GET_STATE SCARD_CTL_CODE(14) +#define IOCTL_SMARTCARD_GET_LAST_ERROR SCARD_CTL_CODE(15) +#define IOCTL_SMARTCARD_GET_PERF_CNTR SCARD_CTL_CODE(16) + +#define IOCTL_SMARTCARD_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400) + +#define MAXIMUM_ATTR_STRING_LENGTH 32 +#define MAXIMUM_SMARTCARD_READERS 10 + +#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag))) + +#define SCARD_CLASS_VENDOR_INFO 1 +#define SCARD_CLASS_COMMUNICATIONS 2 +#define SCARD_CLASS_PROTOCOL 3 +#define SCARD_CLASS_POWER_MGMT 4 +#define SCARD_CLASS_SECURITY 5 +#define SCARD_CLASS_MECHANICAL 6 +#define SCARD_CLASS_VENDOR_DEFINED 7 +#define SCARD_CLASS_IFD_PROTOCOL 8 +#define SCARD_CLASS_ICC_STATE 9 +#define SCARD_CLASS_PERF 0x7FFE +#define SCARD_CLASS_SYSTEM 0x7FFF + +#define SCARD_ATTR_VENDOR_NAME SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0100) +#define SCARD_ATTR_VENDOR_IFD_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0101) +#define SCARD_ATTR_VENDOR_IFD_VERSION SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0102) +#define SCARD_ATTR_VENDOR_IFD_SERIAL_NO SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0103) +#define SCARD_ATTR_CHANNEL_ID SCARD_ATTR_VALUE(SCARD_CLASS_COMMUNICATIONS, 0x0110) +#define SCARD_ATTR_PROTOCOL_TYPES SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0120) +#define SCARD_ATTR_DEFAULT_CLK SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0121) +#define SCARD_ATTR_MAX_CLK SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0122) +#define SCARD_ATTR_DEFAULT_DATA_RATE SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0123) +#define SCARD_ATTR_MAX_DATA_RATE SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0124) +#define SCARD_ATTR_MAX_IFSD SCARD_ATTR_VALUE(SCARD_CLASS_PROTOCOL, 0x0125) +#define SCARD_ATTR_POWER_MGMT_SUPPORT SCARD_ATTR_VALUE(SCARD_CLASS_POWER_MGMT, 0x0131) +#define SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE SCARD_ATTR_VALUE(SCARD_CLASS_SECURITY, 0x0140) +#define SCARD_ATTR_USER_AUTH_INPUT_DEVICE SCARD_ATTR_VALUE(SCARD_CLASS_SECURITY, 0x0142) +#define SCARD_ATTR_CHARACTERISTICS SCARD_ATTR_VALUE(SCARD_CLASS_MECHANICAL, 0x0150) + +#define SCARD_ATTR_CURRENT_PROTOCOL_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0201) +#define SCARD_ATTR_CURRENT_CLK SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0202) +#define SCARD_ATTR_CURRENT_F SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0203) +#define SCARD_ATTR_CURRENT_D SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0204) +#define SCARD_ATTR_CURRENT_N SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0205) +#define SCARD_ATTR_CURRENT_W SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0206) +#define SCARD_ATTR_CURRENT_IFSC SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0207) +#define SCARD_ATTR_CURRENT_IFSD SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0208) +#define SCARD_ATTR_CURRENT_BWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x0209) +#define SCARD_ATTR_CURRENT_CWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020a) +#define SCARD_ATTR_CURRENT_EBC_ENCODING SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020b) +#define SCARD_ATTR_EXTENDED_BWT SCARD_ATTR_VALUE(SCARD_CLASS_IFD_PROTOCOL, 0x020c) + +#define SCARD_ATTR_ICC_PRESENCE SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0300) +#define SCARD_ATTR_ICC_INTERFACE_STATUS SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0301) +#define SCARD_ATTR_CURRENT_IO_STATE SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0302) +#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) +#define SCARD_ATTR_ICC_TYPE_PER_ATR SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0304) + +#define SCARD_ATTR_ESC_RESET SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA000) +#define SCARD_ATTR_ESC_CANCEL SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA003) +#define SCARD_ATTR_ESC_AUTHREQUEST SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA005) +#define SCARD_ATTR_MAXINPUT SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA007) + +#define SCARD_ATTR_DEVICE_UNIT SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0001) +#define SCARD_ATTR_DEVICE_IN_USE SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0002) +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0003) +#define SCARD_ATTR_DEVICE_SYSTEM_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0004) +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0005) +#define SCARD_ATTR_DEVICE_SYSTEM_NAME_W SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0006) +#define SCARD_ATTR_SUPRESS_T1_IFS_REQUEST SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0007) + +#define SCARD_PERF_NUM_TRANSMISSIONS SCARD_ATTR_VALUE(SCARD_CLASS_PERF, 0x0001) +#define SCARD_PERF_BYTES_TRANSMITTED SCARD_ATTR_VALUE(SCARD_CLASS_PERF, 0x0002) +#define SCARD_PERF_TRANSMISSION_TIME SCARD_ATTR_VALUE(SCARD_CLASS_PERF, 0x0003) + +#ifdef UNICODE +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_W +#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_W +#else +#define SCARD_ATTR_DEVICE_FRIENDLY_NAME SCARD_ATTR_DEVICE_FRIENDLY_NAME_A +#define SCARD_ATTR_DEVICE_SYSTEM_NAME SCARD_ATTR_DEVICE_SYSTEM_NAME_A +#endif + +#define SCARD_T0_HEADER_LENGTH 7 +#define SCARD_T0_CMD_LENGTH 5 + +#define SCARD_T1_PROLOGUE_LENGTH 3 +#define SCARD_T1_EPILOGUE_LENGTH 2 +#define SCARD_T1_MAX_IFS 254 + +#define SCARD_UNKNOWN 0 +#define SCARD_ABSENT 1 +#define SCARD_PRESENT 2 +#define SCARD_SWALLOWED 3 +#define SCARD_POWERED 4 +#define SCARD_NEGOTIABLE 5 +#define SCARD_SPECIFIC 6 + +#pragma pack(push, 1) + +typedef struct +{ + DWORD dwProtocol; + DWORD cbPciLength; +} SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST; +typedef const SCARD_IO_REQUEST* LPCSCARD_IO_REQUEST; + +typedef struct +{ + BYTE bCla, bIns, bP1, bP2, bP3; +} SCARD_T0_COMMAND, *LPSCARD_T0_COMMAND; + +typedef struct +{ + SCARD_IO_REQUEST ioRequest; + BYTE bSw1, bSw2; + union + { + SCARD_T0_COMMAND CmdBytes; + BYTE rgbHeader[5]; + } DUMMYUNIONNAME; +} SCARD_T0_REQUEST; + +typedef SCARD_T0_REQUEST *PSCARD_T0_REQUEST, *LPSCARD_T0_REQUEST; + +typedef struct +{ + SCARD_IO_REQUEST ioRequest; +} SCARD_T1_REQUEST; +typedef SCARD_T1_REQUEST *PSCARD_T1_REQUEST, *LPSCARD_T1_REQUEST; + +#define SCARD_READER_SWALLOWS 0x00000001 +#define SCARD_READER_EJECTS 0x00000002 +#define SCARD_READER_CONFISCATES 0x00000004 + +#define SCARD_READER_TYPE_SERIAL 0x01 +#define SCARD_READER_TYPE_PARALELL 0x02 +#define SCARD_READER_TYPE_KEYBOARD 0x04 +#define SCARD_READER_TYPE_SCSI 0x08 +#define SCARD_READER_TYPE_IDE 0x10 +#define SCARD_READER_TYPE_USB 0x20 +#define SCARD_READER_TYPE_PCMCIA 0x40 +#define SCARD_READER_TYPE_TPM 0x80 +#define SCARD_READER_TYPE_NFC 0x100 +#define SCARD_READER_TYPE_UICC 0x200 +#define SCARD_READER_TYPE_VENDOR 0xF0 + +#ifndef WINSCARDAPI +#define WINSCARDAPI WINPR_API +#endif + +typedef ULONG_PTR SCARDCONTEXT; +typedef SCARDCONTEXT *PSCARDCONTEXT, *LPSCARDCONTEXT; + +typedef ULONG_PTR SCARDHANDLE; +typedef SCARDHANDLE *PSCARDHANDLE, *LPSCARDHANDLE; + +#define SCARD_AUTOALLOCATE (DWORD)(-1) + +#define SCARD_SCOPE_USER 0 +#define SCARD_SCOPE_TERMINAL 1 +#define SCARD_SCOPE_SYSTEM 2 + +#define SCARD_STATE_UNAWARE 0x00000000 +#define SCARD_STATE_IGNORE 0x00000001 +#define SCARD_STATE_CHANGED 0x00000002 +#define SCARD_STATE_UNKNOWN 0x00000004 +#define SCARD_STATE_UNAVAILABLE 0x00000008 +#define SCARD_STATE_EMPTY 0x00000010 +#define SCARD_STATE_PRESENT 0x00000020 +#define SCARD_STATE_ATRMATCH 0x00000040 +#define SCARD_STATE_EXCLUSIVE 0x00000080 +#define SCARD_STATE_INUSE 0x00000100 +#define SCARD_STATE_MUTE 0x00000200 +#define SCARD_STATE_UNPOWERED 0x00000400 + +#define SCARD_SHARE_EXCLUSIVE 1 +#define SCARD_SHARE_SHARED 2 +#define SCARD_SHARE_DIRECT 3 + +#define SCARD_LEAVE_CARD 0 +#define SCARD_RESET_CARD 1 +#define SCARD_UNPOWER_CARD 2 +#define SCARD_EJECT_CARD 3 + +#define SC_DLG_MINIMAL_UI 0x01 +#define SC_DLG_NO_UI 0x02 +#define SC_DLG_FORCE_UI 0x04 + +#define SCERR_NOCARDNAME 0x4000 +#define SCERR_NOGUIDS 0x8000 + +typedef SCARDHANDLE(WINAPI* LPOCNCONNPROCA)(SCARDCONTEXT hSCardContext, LPSTR szReader, + LPSTR mszCards, PVOID pvUserData); +typedef SCARDHANDLE(WINAPI* LPOCNCONNPROCW)(SCARDCONTEXT hSCardContext, LPWSTR szReader, + LPWSTR mszCards, PVOID pvUserData); + +typedef BOOL(WINAPI* LPOCNCHKPROC)(SCARDCONTEXT hSCardContext, SCARDHANDLE hCard, PVOID pvUserData); +typedef void(WINAPI* LPOCNDSCPROC)(SCARDCONTEXT hSCardContext, SCARDHANDLE hCard, PVOID pvUserData); + +#define SCARD_READER_SEL_AUTH_PACKAGE ((DWORD)-629) + +#define SCARD_AUDIT_CHV_FAILURE 0x0 +#define SCARD_AUDIT_CHV_SUCCESS 0x1 + +#define SCardListCardTypes SCardListCards + +#define PCSCardIntroduceCardType(hContext, szCardName, pbAtr, pbAtrMask, cbAtrLen, \ + pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount) \ + SCardIntroduceCardType(hContext, szCardName, pguidPrimaryProvider, rgguidInterfaces, \ + dwInterfaceCount, pbAtr, pbAtrMask, cbAtrLen) + +#define SCardGetReaderCapabilities SCardGetAttrib +#define SCardSetReaderCapabilities SCardSetAttrib + +typedef struct +{ + LPSTR szReader; + LPVOID pvUserData; + DWORD dwCurrentState; + DWORD dwEventState; + DWORD cbAtr; + BYTE rgbAtr[36]; +} SCARD_READERSTATEA, *PSCARD_READERSTATEA, *LPSCARD_READERSTATEA; + +typedef struct +{ + LPWSTR szReader; + LPVOID pvUserData; + DWORD dwCurrentState; + DWORD dwEventState; + DWORD cbAtr; + BYTE rgbAtr[36]; +} SCARD_READERSTATEW, *PSCARD_READERSTATEW, *LPSCARD_READERSTATEW; + +typedef struct +{ + DWORD cbAtr; + BYTE rgbAtr[36]; + BYTE rgbMask[36]; +} SCARD_ATRMASK, *PSCARD_ATRMASK, *LPSCARD_ATRMASK; + +typedef struct +{ + DWORD dwStructSize; + LPSTR lpstrGroupNames; + DWORD nMaxGroupNames; + LPCGUID rgguidInterfaces; + DWORD cguidInterfaces; + LPSTR lpstrCardNames; + DWORD nMaxCardNames; + LPOCNCHKPROC lpfnCheck; + LPOCNCONNPROCA lpfnConnect; + LPOCNDSCPROC lpfnDisconnect; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; +} OPENCARD_SEARCH_CRITERIAA, *POPENCARD_SEARCH_CRITERIAA, *LPOPENCARD_SEARCH_CRITERIAA; + +typedef struct +{ + DWORD dwStructSize; + LPWSTR lpstrGroupNames; + DWORD nMaxGroupNames; + LPCGUID rgguidInterfaces; + DWORD cguidInterfaces; + LPWSTR lpstrCardNames; + DWORD nMaxCardNames; + LPOCNCHKPROC lpfnCheck; + LPOCNCONNPROCW lpfnConnect; + LPOCNDSCPROC lpfnDisconnect; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; +} OPENCARD_SEARCH_CRITERIAW, *POPENCARD_SEARCH_CRITERIAW, *LPOPENCARD_SEARCH_CRITERIAW; + +typedef struct +{ + DWORD dwStructSize; + SCARDCONTEXT hSCardContext; + HWND hwndOwner; + DWORD dwFlags; + LPCSTR lpstrTitle; + LPCSTR lpstrSearchDesc; + HICON hIcon; + POPENCARD_SEARCH_CRITERIAA pOpenCardSearchCriteria; + LPOCNCONNPROCA lpfnConnect; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; + LPSTR lpstrRdr; + DWORD nMaxRdr; + LPSTR lpstrCard; + DWORD nMaxCard; + DWORD dwActiveProtocol; + SCARDHANDLE hCardHandle; +} OPENCARDNAME_EXA, *POPENCARDNAME_EXA, *LPOPENCARDNAME_EXA; + +typedef struct +{ + DWORD dwStructSize; + SCARDCONTEXT hSCardContext; + HWND hwndOwner; + DWORD dwFlags; + LPCWSTR lpstrTitle; + LPCWSTR lpstrSearchDesc; + HICON hIcon; + POPENCARD_SEARCH_CRITERIAW pOpenCardSearchCriteria; + LPOCNCONNPROCW lpfnConnect; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; + LPWSTR lpstrRdr; + DWORD nMaxRdr; + LPWSTR lpstrCard; + DWORD nMaxCard; + DWORD dwActiveProtocol; + SCARDHANDLE hCardHandle; +} OPENCARDNAME_EXW, *POPENCARDNAME_EXW, *LPOPENCARDNAME_EXW; + +#define OPENCARDNAMEA_EX OPENCARDNAME_EXA +#define OPENCARDNAMEW_EX OPENCARDNAME_EXW +#define POPENCARDNAMEA_EX POPENCARDNAME_EXA +#define POPENCARDNAMEW_EX POPENCARDNAME_EXW +#define LPOPENCARDNAMEA_EX LPOPENCARDNAME_EXA +#define LPOPENCARDNAMEW_EX LPOPENCARDNAME_EXW + +typedef enum +{ + RSR_MATCH_TYPE_READER_AND_CONTAINER = 1, + RSR_MATCH_TYPE_SERIAL_NUMBER, + RSR_MATCH_TYPE_ALL_CARDS +} READER_SEL_REQUEST_MATCH_TYPE; + +typedef struct +{ + DWORD dwShareMode; + DWORD dwPreferredProtocols; + READER_SEL_REQUEST_MATCH_TYPE MatchType; + union + { + struct + { + DWORD cbReaderNameOffset; + DWORD cchReaderNameLength; + DWORD cbContainerNameOffset; + DWORD cchContainerNameLength; + DWORD dwDesiredCardModuleVersion; + DWORD dwCspFlags; + } ReaderAndContainerParameter; + struct + { + DWORD cbSerialNumberOffset; + DWORD cbSerialNumberLength; + DWORD dwDesiredCardModuleVersion; + } SerialNumberParameter; + }; +} READER_SEL_REQUEST, *PREADER_SEL_REQUEST; + +typedef struct +{ + DWORD cbReaderNameOffset; + DWORD cchReaderNameLength; + DWORD cbCardNameOffset; + DWORD cchCardNameLength; +} READER_SEL_RESPONSE, *PREADER_SEL_RESPONSE; + +typedef struct +{ + DWORD dwStructSize; + HWND hwndOwner; + SCARDCONTEXT hSCardContext; + LPSTR lpstrGroupNames; + DWORD nMaxGroupNames; + LPSTR lpstrCardNames; + DWORD nMaxCardNames; + LPCGUID rgguidInterfaces; + DWORD cguidInterfaces; + LPSTR lpstrRdr; + DWORD nMaxRdr; + LPSTR lpstrCard; + DWORD nMaxCard; + LPCSTR lpstrTitle; + DWORD dwFlags; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; + DWORD dwActiveProtocol; + LPOCNCONNPROCA lpfnConnect; + LPOCNCHKPROC lpfnCheck; + LPOCNDSCPROC lpfnDisconnect; + SCARDHANDLE hCardHandle; +} OPENCARDNAMEA, *POPENCARDNAMEA, *LPOPENCARDNAMEA; + +typedef struct +{ + DWORD dwStructSize; + HWND hwndOwner; + SCARDCONTEXT hSCardContext; + LPWSTR lpstrGroupNames; + DWORD nMaxGroupNames; + LPWSTR lpstrCardNames; + DWORD nMaxCardNames; + LPCGUID rgguidInterfaces; + DWORD cguidInterfaces; + LPWSTR lpstrRdr; + DWORD nMaxRdr; + LPWSTR lpstrCard; + DWORD nMaxCard; + LPCWSTR lpstrTitle; + DWORD dwFlags; + LPVOID pvUserData; + DWORD dwShareMode; + DWORD dwPreferredProtocols; + DWORD dwActiveProtocol; + LPOCNCONNPROCW lpfnConnect; + LPOCNCHKPROC lpfnCheck; + LPOCNDSCPROC lpfnDisconnect; + SCARDHANDLE hCardHandle; +} OPENCARDNAMEW, *POPENCARDNAMEW, *LPOPENCARDNAMEW; + +#pragma pack(pop) + +#ifdef UNICODE +#define LPOCNCONNPROC LPOCNCONNPROCW +#define SCARD_READERSTATE SCARD_READERSTATEW +#define PSCARD_READERSTATE PSCARD_READERSTATEW +#define LPSCARD_READERSTATE LPSCARD_READERSTATEW +#define OPENCARD_SEARCH_CRITERIA OPENCARD_SEARCH_CRITERIAW +#define LOPENCARD_SEARCH_CRITERIA LOPENCARD_SEARCH_CRITERIAW +#define LPOPENCARD_SEARCH_CRITERIA LPOPENCARD_SEARCH_CRITERIAW +#define OPENCARDNAME_EX OPENCARDNAME_EXW +#define LOPENCARDNAME_EX LOPENCARDNAME_EXW +#define LPOPENCARDNAME_EX LPOPENCARDNAME_EXW +#define OPENCARDNAME OPENCARDNAMEW +#define LOPENCARDNAME LOPENCARDNAMEW +#define LPOPENCARDNAME LPOPENCARDNAMEW +#else +#define LPOCNCONNPROC LPOCNCONNPROCA +#define SCARD_READERSTATE SCARD_READERSTATEA +#define PSCARD_READERSTATE PSCARD_READERSTATEA +#define LPSCARD_READERSTATE LPSCARD_READERSTATEA +#define OPENCARD_SEARCH_CRITERIA OPENCARD_SEARCH_CRITERIAA +#define LOPENCARD_SEARCH_CRITERIA LOPENCARD_SEARCH_CRITERIAA +#define LPOPENCARD_SEARCH_CRITERIA LPOPENCARD_SEARCH_CRITERIAA +#define OPENCARDNAME_EX OPENCARDNAME_EXA +#define LOPENCARDNAME_EX LOPENCARDNAME_EXA +#define LPOPENCARDNAME_EX LPOPENCARDNAME_EXA +#define OPENCARDNAME OPENCARDNAMEA +#define LOPENCARDNAME LOPENCARDNAMEA +#define LPOPENCARDNAME LPOPENCARDNAMEA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API extern const SCARD_IO_REQUEST g_rgSCardT0Pci; + WINPR_API extern const SCARD_IO_REQUEST g_rgSCardT1Pci; + WINPR_API extern const SCARD_IO_REQUEST g_rgSCardRawPci; + +#define SCARD_PCI_T0 (&g_rgSCardT0Pci) +#define SCARD_PCI_T1 (&g_rgSCardT1Pci) +#define SCARD_PCI_RAW (&g_rgSCardRawPci) + + WINSCARDAPI LONG WINAPI SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, LPSCARDCONTEXT phContext); + + WINSCARDAPI LONG WINAPI SCardReleaseContext(SCARDCONTEXT hContext); + + WINSCARDAPI LONG WINAPI SCardIsValidContext(SCARDCONTEXT hContext); + + WINSCARDAPI LONG WINAPI SCardListReaderGroupsA(SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups); + WINSCARDAPI LONG WINAPI SCardListReaderGroupsW(SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups); + + WINSCARDAPI LONG WINAPI SCardListReadersA(SCARDCONTEXT hContext, LPCSTR mszGroups, + LPSTR mszReaders, LPDWORD pcchReaders); + WINSCARDAPI LONG WINAPI SCardListReadersW(SCARDCONTEXT hContext, LPCWSTR mszGroups, + LPWSTR mszReaders, LPDWORD pcchReaders); + + WINSCARDAPI LONG WINAPI SCardListCardsA(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + CHAR* mszCards, LPDWORD pcchCards); + + WINSCARDAPI LONG WINAPI SCardListCardsW(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + WCHAR* mszCards, LPDWORD pcchCards); + + WINSCARDAPI LONG WINAPI SCardListInterfacesA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces); + WINSCARDAPI LONG WINAPI SCardListInterfacesW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces); + + WINSCARDAPI LONG WINAPI SCardGetProviderIdA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidProviderId); + WINSCARDAPI LONG WINAPI SCardGetProviderIdW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidProviderId); + + WINSCARDAPI LONG WINAPI SCardGetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, CHAR* szProvider, + LPDWORD pcchProvider); + WINSCARDAPI LONG WINAPI SCardGetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider); + + WINSCARDAPI LONG WINAPI SCardIntroduceReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName); + WINSCARDAPI LONG WINAPI SCardIntroduceReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName); + + WINSCARDAPI LONG WINAPI SCardForgetReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName); + WINSCARDAPI LONG WINAPI SCardForgetReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName); + + WINSCARDAPI LONG WINAPI SCardIntroduceReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName); + WINSCARDAPI LONG WINAPI SCardIntroduceReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName); + + WINSCARDAPI LONG WINAPI SCardForgetReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName); + WINSCARDAPI LONG WINAPI SCardForgetReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName); + + WINSCARDAPI LONG WINAPI SCardAddReaderToGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName); + WINSCARDAPI LONG WINAPI SCardAddReaderToGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName); + + WINSCARDAPI LONG WINAPI SCardRemoveReaderFromGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName); + WINSCARDAPI LONG WINAPI SCardRemoveReaderFromGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName); + + WINSCARDAPI LONG WINAPI SCardIntroduceCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, + DWORD dwInterfaceCount, LPCBYTE pbAtr, + LPCBYTE pbAtrMask, DWORD cbAtrLen); + WINSCARDAPI LONG WINAPI SCardIntroduceCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, + DWORD dwInterfaceCount, LPCBYTE pbAtr, + LPCBYTE pbAtrMask, DWORD cbAtrLen); + + WINSCARDAPI LONG WINAPI SCardSetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, LPCSTR szProvider); + WINSCARDAPI LONG WINAPI SCardSetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, LPCWSTR szProvider); + + WINSCARDAPI LONG WINAPI SCardForgetCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName); + WINSCARDAPI LONG WINAPI SCardForgetCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName); + + WINSCARDAPI LONG WINAPI SCardFreeMemory(SCARDCONTEXT hContext, LPVOID pvMem); + + WINSCARDAPI HANDLE WINAPI SCardAccessStartedEvent(void); + + WINSCARDAPI void WINAPI SCardReleaseStartedEvent(void); + + WINSCARDAPI LONG WINAPI SCardLocateCardsA(SCARDCONTEXT hContext, LPCSTR mszCards, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders); + WINSCARDAPI LONG WINAPI SCardLocateCardsW(SCARDCONTEXT hContext, LPCWSTR mszCards, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders); + + WINSCARDAPI LONG WINAPI SCardLocateCardsByATRA(SCARDCONTEXT hContext, + LPSCARD_ATRMASK rgAtrMasks, DWORD cAtrs, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders); + WINSCARDAPI LONG WINAPI SCardLocateCardsByATRW(SCARDCONTEXT hContext, + LPSCARD_ATRMASK rgAtrMasks, DWORD cAtrs, + LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders); + + WINSCARDAPI LONG WINAPI SCardGetStatusChangeA(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders); + WINSCARDAPI LONG WINAPI SCardGetStatusChangeW(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders); + + WINSCARDAPI LONG WINAPI SCardCancel(SCARDCONTEXT hContext); + + WINSCARDAPI LONG WINAPI SCardConnectA(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol); + WINSCARDAPI LONG WINAPI SCardConnectW(SCARDCONTEXT hContext, LPCWSTR szReader, + DWORD dwShareMode, DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); + + WINSCARDAPI LONG WINAPI SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, + DWORD dwPreferredProtocols, DWORD dwInitialization, + LPDWORD pdwActiveProtocol); + + WINSCARDAPI LONG WINAPI SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition); + + WINSCARDAPI LONG WINAPI SCardBeginTransaction(SCARDHANDLE hCard); + + WINSCARDAPI LONG WINAPI SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition); + + WINSCARDAPI LONG WINAPI SCardCancelTransaction(SCARDHANDLE hCard); + + WINSCARDAPI LONG WINAPI SCardState(SCARDHANDLE hCard, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen); + + WINSCARDAPI LONG WINAPI SCardStatusA(SCARDHANDLE hCard, LPSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen); + WINSCARDAPI LONG WINAPI SCardStatusW(SCARDHANDLE hCard, LPWSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen); + + WINSCARDAPI LONG WINAPI SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, + LPCBYTE pbSendBuffer, DWORD cbSendLength, + LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength); + + WINSCARDAPI LONG WINAPI SCardGetTransmitCount(SCARDHANDLE hCard, LPDWORD pcTransmitCount); + + WINSCARDAPI LONG WINAPI SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, + DWORD cbInBufferSize, LPVOID lpOutBuffer, + DWORD cbOutBufferSize, LPDWORD lpBytesReturned); + + WINSCARDAPI LONG WINAPI SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen); + + WINSCARDAPI LONG WINAPI SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, + DWORD cbAttrLen); + + WINSCARDAPI LONG WINAPI SCardUIDlgSelectCardA(LPOPENCARDNAMEA_EX pDlgStruc); + WINSCARDAPI LONG WINAPI SCardUIDlgSelectCardW(LPOPENCARDNAMEW_EX pDlgStruc); + + WINSCARDAPI LONG WINAPI GetOpenCardNameA(LPOPENCARDNAMEA pDlgStruc); + WINSCARDAPI LONG WINAPI GetOpenCardNameW(LPOPENCARDNAMEW pDlgStruc); + + WINSCARDAPI LONG WINAPI SCardDlgExtendedError(void); + + WINSCARDAPI LONG WINAPI SCardReadCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD* DataLen); + WINSCARDAPI LONG WINAPI SCardReadCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD* DataLen); + + WINSCARDAPI LONG WINAPI SCardWriteCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD DataLen); + WINSCARDAPI LONG WINAPI SCardWriteCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD DataLen); + + WINSCARDAPI LONG WINAPI SCardGetReaderIconA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon); + WINSCARDAPI LONG WINAPI SCardGetReaderIconW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon); + + WINSCARDAPI LONG WINAPI SCardGetDeviceTypeIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId); + WINSCARDAPI LONG WINAPI SCardGetDeviceTypeIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId); + + WINSCARDAPI LONG WINAPI SCardGetReaderDeviceInstanceIdA(SCARDCONTEXT hContext, + LPCSTR szReaderName, + LPSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId); + WINSCARDAPI LONG WINAPI SCardGetReaderDeviceInstanceIdW(SCARDCONTEXT hContext, + LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId); + + WINSCARDAPI LONG WINAPI SCardListReadersWithDeviceInstanceIdA(SCARDCONTEXT hContext, + LPCSTR szDeviceInstanceId, + LPSTR mszReaders, + LPDWORD pcchReaders); + WINSCARDAPI LONG WINAPI SCardListReadersWithDeviceInstanceIdW(SCARDCONTEXT hContext, + LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, + LPDWORD pcchReaders); + + WINSCARDAPI LONG WINAPI SCardAudit(SCARDCONTEXT hContext, DWORD dwEvent); + +#ifdef UNICODE +#define SCardListReaderGroups SCardListReaderGroupsW +#define SCardListReaders SCardListReadersW +#define SCardListCards SCardListCardsW +#define SCardListInterfaces SCardListInterfacesW +#define SCardGetProviderId SCardGetProviderIdW +#define SCardGetCardTypeProviderName SCardGetCardTypeProviderNameW +#define SCardIntroduceReaderGroup SCardIntroduceReaderGroupW +#define SCardForgetReaderGroup SCardForgetReaderGroupW +#define SCardIntroduceReader SCardIntroduceReaderW +#define SCardForgetReader SCardForgetReaderW +#define SCardAddReaderToGroup SCardAddReaderToGroupW +#define SCardRemoveReaderFromGroup SCardRemoveReaderFromGroupW +#define SCardIntroduceCardType SCardIntroduceCardTypeW +#define SCardSetCardTypeProviderName SCardSetCardTypeProviderNameW +#define SCardForgetCardType SCardForgetCardTypeW +#define SCardLocateCards SCardLocateCardsW +#define SCardLocateCardsByATR SCardLocateCardsByATRW +#define SCardGetStatusChange SCardGetStatusChangeW +#define SCardConnect SCardConnectW +#define SCardStatus SCardStatusW +#define SCardUIDlgSelectCard SCardUIDlgSelectCardW +#define GetOpenCardName GetOpenCardNameW +#define SCardReadCache SCardReadCacheW +#define SCardWriteCache SCardWriteCacheW +#define SCardGetReaderIcon SCardGetReaderIconW +#define SCardGetDeviceTypeId SCardGetDeviceTypeIdW +#define SCardGetReaderDeviceInstanceId SCardGetReaderDeviceInstanceIdW +#define SCardListReadersWithDeviceInstanceId SCardListReadersWithDeviceInstanceIdW +#else +#define SCardListReaderGroups SCardListReaderGroupsA +#define SCardListReaders SCardListReadersA +#define SCardListCards SCardListCardsA +#define SCardListInterfaces SCardListInterfacesA +#define SCardGetProviderId SCardGetProviderIdA +#define SCardGetCardTypeProviderName SCardGetCardTypeProviderNameA +#define SCardIntroduceReaderGroup SCardIntroduceReaderGroupA +#define SCardForgetReaderGroup SCardForgetReaderGroupA +#define SCardIntroduceReader SCardIntroduceReaderA +#define SCardForgetReader SCardForgetReaderA +#define SCardAddReaderToGroup SCardAddReaderToGroupA +#define SCardRemoveReaderFromGroup SCardRemoveReaderFromGroupA +#define SCardIntroduceCardType SCardIntroduceCardTypeA +#define SCardSetCardTypeProviderName SCardSetCardTypeProviderNameA +#define SCardForgetCardType SCardForgetCardTypeA +#define SCardLocateCards SCardLocateCardsA +#define SCardLocateCardsByATR SCardLocateCardsByATRA +#define SCardGetStatusChange SCardGetStatusChangeA +#define SCardConnect SCardConnectA +#define SCardStatus SCardStatusA +#define SCardUIDlgSelectCard SCardUIDlgSelectCardA +#define GetOpenCardName GetOpenCardNameA +#define SCardReadCache SCardReadCacheA +#define SCardWriteCache SCardWriteCacheA +#define SCardGetReaderIcon SCardGetReaderIconA +#define SCardGetDeviceTypeId SCardGetDeviceTypeIdA +#define SCardGetReaderDeviceInstanceId SCardGetReaderDeviceInstanceIdA +#define SCardListReadersWithDeviceInstanceId SCardListReadersWithDeviceInstanceIdA +#endif + +#ifdef __cplusplus +} +#endif + +/** + * Extended API + */ + +typedef LONG(WINAPI* fnSCardEstablishContext)(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, LPSCARDCONTEXT phContext); + +typedef LONG(WINAPI* fnSCardReleaseContext)(SCARDCONTEXT hContext); + +typedef LONG(WINAPI* fnSCardIsValidContext)(SCARDCONTEXT hContext); + +typedef LONG(WINAPI* fnSCardListReaderGroupsA)(SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups); +typedef LONG(WINAPI* fnSCardListReaderGroupsW)(SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups); + +typedef LONG(WINAPI* fnSCardListReadersA)(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, + LPDWORD pcchReaders); +typedef LONG(WINAPI* fnSCardListReadersW)(SCARDCONTEXT hContext, LPCWSTR mszGroups, + LPWSTR mszReaders, LPDWORD pcchReaders); + +typedef LONG(WINAPI* fnSCardListCardsA)(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + CHAR* mszCards, LPDWORD pcchCards); + +typedef LONG(WINAPI* fnSCardListCardsW)(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + WCHAR* mszCards, LPDWORD pcchCards); + +typedef LONG(WINAPI* fnSCardListInterfacesA)(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces); +typedef LONG(WINAPI* fnSCardListInterfacesW)(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces); + +typedef LONG(WINAPI* fnSCardGetProviderIdA)(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidProviderId); +typedef LONG(WINAPI* fnSCardGetProviderIdW)(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidProviderId); + +typedef LONG(WINAPI* fnSCardGetCardTypeProviderNameA)(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, CHAR* szProvider, + LPDWORD pcchProvider); +typedef LONG(WINAPI* fnSCardGetCardTypeProviderNameW)(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider); + +typedef LONG(WINAPI* fnSCardIntroduceReaderGroupA)(SCARDCONTEXT hContext, LPCSTR szGroupName); +typedef LONG(WINAPI* fnSCardIntroduceReaderGroupW)(SCARDCONTEXT hContext, LPCWSTR szGroupName); + +typedef LONG(WINAPI* fnSCardForgetReaderGroupA)(SCARDCONTEXT hContext, LPCSTR szGroupName); +typedef LONG(WINAPI* fnSCardForgetReaderGroupW)(SCARDCONTEXT hContext, LPCWSTR szGroupName); + +typedef LONG(WINAPI* fnSCardIntroduceReaderA)(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName); +typedef LONG(WINAPI* fnSCardIntroduceReaderW)(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName); + +typedef LONG(WINAPI* fnSCardForgetReaderA)(SCARDCONTEXT hContext, LPCSTR szReaderName); +typedef LONG(WINAPI* fnSCardForgetReaderW)(SCARDCONTEXT hContext, LPCWSTR szReaderName); + +typedef LONG(WINAPI* fnSCardAddReaderToGroupA)(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName); +typedef LONG(WINAPI* fnSCardAddReaderToGroupW)(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName); + +typedef LONG(WINAPI* fnSCardRemoveReaderFromGroupA)(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName); +typedef LONG(WINAPI* fnSCardRemoveReaderFromGroupW)(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName); + +typedef LONG(WINAPI* fnSCardIntroduceCardTypeA)(SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen); +typedef LONG(WINAPI* fnSCardIntroduceCardTypeW)(SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen); + +typedef LONG(WINAPI* fnSCardSetCardTypeProviderNameA)(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, LPCSTR szProvider); +typedef LONG(WINAPI* fnSCardSetCardTypeProviderNameW)(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, LPCWSTR szProvider); + +typedef LONG(WINAPI* fnSCardForgetCardTypeA)(SCARDCONTEXT hContext, LPCSTR szCardName); +typedef LONG(WINAPI* fnSCardForgetCardTypeW)(SCARDCONTEXT hContext, LPCWSTR szCardName); + +typedef LONG(WINAPI* fnSCardFreeMemory)(SCARDCONTEXT hContext, LPVOID pvMem); + +typedef HANDLE(WINAPI* fnSCardAccessStartedEvent)(void); + +typedef void(WINAPI* fnSCardReleaseStartedEvent)(void); + +typedef LONG(WINAPI* fnSCardLocateCardsA)(SCARDCONTEXT hContext, LPCSTR mszCards, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders); +typedef LONG(WINAPI* fnSCardLocateCardsW)(SCARDCONTEXT hContext, LPCWSTR mszCards, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders); + +typedef LONG(WINAPI* fnSCardLocateCardsByATRA)(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders); +typedef LONG(WINAPI* fnSCardLocateCardsByATRW)(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders); + +typedef LONG(WINAPI* fnSCardGetStatusChangeA)(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders); +typedef LONG(WINAPI* fnSCardGetStatusChangeW)(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders); + +typedef LONG(WINAPI* fnSCardCancel)(SCARDCONTEXT hContext); + +typedef LONG(WINAPI* fnSCardConnectA)(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol); +typedef LONG(WINAPI* fnSCardConnectW)(SCARDCONTEXT hContext, LPCWSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol); + +typedef LONG(WINAPI* fnSCardReconnect)(SCARDHANDLE hCard, DWORD dwShareMode, + DWORD dwPreferredProtocols, DWORD dwInitialization, + LPDWORD pdwActiveProtocol); + +typedef LONG(WINAPI* fnSCardDisconnect)(SCARDHANDLE hCard, DWORD dwDisposition); + +typedef LONG(WINAPI* fnSCardBeginTransaction)(SCARDHANDLE hCard); + +typedef LONG(WINAPI* fnSCardEndTransaction)(SCARDHANDLE hCard, DWORD dwDisposition); + +typedef LONG(WINAPI* fnSCardCancelTransaction)(SCARDHANDLE hCard); + +typedef LONG(WINAPI* fnSCardState)(SCARDHANDLE hCard, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen); + +typedef LONG(WINAPI* fnSCardStatusA)(SCARDHANDLE hCard, LPSTR mszReaderNames, LPDWORD pcchReaderLen, + LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, + LPDWORD pcbAtrLen); +typedef LONG(WINAPI* fnSCardStatusW)(SCARDHANDLE hCard, LPWSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen); + +typedef LONG(WINAPI* fnSCardTransmit)(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, + LPCBYTE pbSendBuffer, DWORD cbSendLength, + LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength); + +typedef LONG(WINAPI* fnSCardGetTransmitCount)(SCARDHANDLE hCard, LPDWORD pcTransmitCount); + +typedef LONG(WINAPI* fnSCardControl)(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, + DWORD cbInBufferSize, LPVOID lpOutBuffer, + DWORD cbOutBufferSize, LPDWORD lpBytesReturned); + +typedef LONG(WINAPI* fnSCardGetAttrib)(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen); + +typedef LONG(WINAPI* fnSCardSetAttrib)(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, + DWORD cbAttrLen); + +typedef LONG(WINAPI* fnSCardUIDlgSelectCardA)(LPOPENCARDNAMEA_EX pDlgStruc); +typedef LONG(WINAPI* fnSCardUIDlgSelectCardW)(LPOPENCARDNAMEW_EX pDlgStruc); + +typedef LONG(WINAPI* fnGetOpenCardNameA)(LPOPENCARDNAMEA pDlgStruc); +typedef LONG(WINAPI* fnGetOpenCardNameW)(LPOPENCARDNAMEW pDlgStruc); + +typedef LONG(WINAPI* fnSCardDlgExtendedError)(void); + +typedef LONG(WINAPI* fnSCardReadCacheA)(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD* DataLen); +typedef LONG(WINAPI* fnSCardReadCacheW)(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD* DataLen); + +typedef LONG(WINAPI* fnSCardWriteCacheA)(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD DataLen); +typedef LONG(WINAPI* fnSCardWriteCacheW)(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD DataLen); + +typedef LONG(WINAPI* fnSCardGetReaderIconA)(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon); +typedef LONG(WINAPI* fnSCardGetReaderIconW)(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon); + +typedef LONG(WINAPI* fnSCardGetDeviceTypeIdA)(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId); +typedef LONG(WINAPI* fnSCardGetDeviceTypeIdW)(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId); + +typedef LONG(WINAPI* fnSCardGetReaderDeviceInstanceIdA)(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId); +typedef LONG(WINAPI* fnSCardGetReaderDeviceInstanceIdW)(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId); + +typedef LONG(WINAPI* fnSCardListReadersWithDeviceInstanceIdA)(SCARDCONTEXT hContext, + LPCSTR szDeviceInstanceId, + LPSTR mszReaders, + LPDWORD pcchReaders); +typedef LONG(WINAPI* fnSCardListReadersWithDeviceInstanceIdW)(SCARDCONTEXT hContext, + LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, + LPDWORD pcchReaders); + +typedef LONG(WINAPI* fnSCardAudit)(SCARDCONTEXT hContext, DWORD dwEvent); + +typedef struct +{ + DWORD dwVersion; + DWORD dwFlags; + + fnSCardEstablishContext pfnSCardEstablishContext; + fnSCardReleaseContext pfnSCardReleaseContext; + fnSCardIsValidContext pfnSCardIsValidContext; + fnSCardListReaderGroupsA pfnSCardListReaderGroupsA; + fnSCardListReaderGroupsW pfnSCardListReaderGroupsW; + fnSCardListReadersA pfnSCardListReadersA; + fnSCardListReadersW pfnSCardListReadersW; + fnSCardListCardsA pfnSCardListCardsA; + fnSCardListCardsW pfnSCardListCardsW; + fnSCardListInterfacesA pfnSCardListInterfacesA; + fnSCardListInterfacesW pfnSCardListInterfacesW; + fnSCardGetProviderIdA pfnSCardGetProviderIdA; + fnSCardGetProviderIdW pfnSCardGetProviderIdW; + fnSCardGetCardTypeProviderNameA pfnSCardGetCardTypeProviderNameA; + fnSCardGetCardTypeProviderNameW pfnSCardGetCardTypeProviderNameW; + fnSCardIntroduceReaderGroupA pfnSCardIntroduceReaderGroupA; + fnSCardIntroduceReaderGroupW pfnSCardIntroduceReaderGroupW; + fnSCardForgetReaderGroupA pfnSCardForgetReaderGroupA; + fnSCardForgetReaderGroupW pfnSCardForgetReaderGroupW; + fnSCardIntroduceReaderA pfnSCardIntroduceReaderA; + fnSCardIntroduceReaderW pfnSCardIntroduceReaderW; + fnSCardForgetReaderA pfnSCardForgetReaderA; + fnSCardForgetReaderW pfnSCardForgetReaderW; + fnSCardAddReaderToGroupA pfnSCardAddReaderToGroupA; + fnSCardAddReaderToGroupW pfnSCardAddReaderToGroupW; + fnSCardRemoveReaderFromGroupA pfnSCardRemoveReaderFromGroupA; + fnSCardRemoveReaderFromGroupW pfnSCardRemoveReaderFromGroupW; + fnSCardIntroduceCardTypeA pfnSCardIntroduceCardTypeA; + fnSCardIntroduceCardTypeW pfnSCardIntroduceCardTypeW; + fnSCardSetCardTypeProviderNameA pfnSCardSetCardTypeProviderNameA; + fnSCardSetCardTypeProviderNameW pfnSCardSetCardTypeProviderNameW; + fnSCardForgetCardTypeA pfnSCardForgetCardTypeA; + fnSCardForgetCardTypeW pfnSCardForgetCardTypeW; + fnSCardFreeMemory pfnSCardFreeMemory; + fnSCardAccessStartedEvent pfnSCardAccessStartedEvent; + fnSCardReleaseStartedEvent pfnSCardReleaseStartedEvent; + fnSCardLocateCardsA pfnSCardLocateCardsA; + fnSCardLocateCardsW pfnSCardLocateCardsW; + fnSCardLocateCardsByATRA pfnSCardLocateCardsByATRA; + fnSCardLocateCardsByATRW pfnSCardLocateCardsByATRW; + fnSCardGetStatusChangeA pfnSCardGetStatusChangeA; + fnSCardGetStatusChangeW pfnSCardGetStatusChangeW; + fnSCardCancel pfnSCardCancel; + fnSCardConnectA pfnSCardConnectA; + fnSCardConnectW pfnSCardConnectW; + fnSCardReconnect pfnSCardReconnect; + fnSCardDisconnect pfnSCardDisconnect; + fnSCardBeginTransaction pfnSCardBeginTransaction; + fnSCardEndTransaction pfnSCardEndTransaction; + fnSCardCancelTransaction pfnSCardCancelTransaction; + fnSCardState pfnSCardState; + fnSCardStatusA pfnSCardStatusA; + fnSCardStatusW pfnSCardStatusW; + fnSCardTransmit pfnSCardTransmit; + fnSCardGetTransmitCount pfnSCardGetTransmitCount; + fnSCardControl pfnSCardControl; + fnSCardGetAttrib pfnSCardGetAttrib; + fnSCardSetAttrib pfnSCardSetAttrib; + fnSCardUIDlgSelectCardA pfnSCardUIDlgSelectCardA; + fnSCardUIDlgSelectCardW pfnSCardUIDlgSelectCardW; + fnGetOpenCardNameA pfnGetOpenCardNameA; + fnGetOpenCardNameW pfnGetOpenCardNameW; + fnSCardDlgExtendedError pfnSCardDlgExtendedError; + fnSCardReadCacheA pfnSCardReadCacheA; + fnSCardReadCacheW pfnSCardReadCacheW; + fnSCardWriteCacheA pfnSCardWriteCacheA; + fnSCardWriteCacheW pfnSCardWriteCacheW; + fnSCardGetReaderIconA pfnSCardGetReaderIconA; + fnSCardGetReaderIconW pfnSCardGetReaderIconW; + fnSCardGetDeviceTypeIdA pfnSCardGetDeviceTypeIdA; + fnSCardGetDeviceTypeIdW pfnSCardGetDeviceTypeIdW; + fnSCardGetReaderDeviceInstanceIdA pfnSCardGetReaderDeviceInstanceIdA; + fnSCardGetReaderDeviceInstanceIdW pfnSCardGetReaderDeviceInstanceIdW; + fnSCardListReadersWithDeviceInstanceIdA pfnSCardListReadersWithDeviceInstanceIdA; + fnSCardListReadersWithDeviceInstanceIdW pfnSCardListReadersWithDeviceInstanceIdW; + fnSCardAudit pfnSCardAudit; +} SCardApiFunctionTable; +typedef SCardApiFunctionTable* PSCardApiFunctionTable; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINSCARDAPI const char* WINAPI SCardGetErrorString(LONG errorCode); + WINSCARDAPI const char* WINAPI SCardGetAttributeString(DWORD dwAttrId); + WINSCARDAPI const char* WINAPI SCardGetProtocolString(DWORD dwProtocols); + WINSCARDAPI const char* WINAPI SCardGetShareModeString(DWORD dwShareMode); + WINSCARDAPI const char* WINAPI SCardGetDispositionString(DWORD dwDisposition); + WINSCARDAPI const char* WINAPI SCardGetScopeString(DWORD dwScope); + WINSCARDAPI const char* WINAPI SCardGetCardStateString(DWORD dwCardState); + WINSCARDAPI char* WINAPI SCardGetReaderStateString(DWORD dwReaderState); + + WINPR_API BOOL WinSCard_LoadApiTableFunctions(PSCardApiFunctionTable pWinSCardApiTable, + HMODULE hWinSCardLibrary); + WINPR_API const SCardApiFunctionTable* WinPR_GetSCardApiFunctionTable(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_SMARTCARD_H */ diff --git a/winpr/include/winpr/spec.h b/winpr/include/winpr/spec.h new file mode 100644 index 0000000..3ebf1b1 --- /dev/null +++ b/winpr/include/winpr/spec.h @@ -0,0 +1,986 @@ +/** + * WinPR: Windows Portable Runtime + * Compiler Specification Strings + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SPEC_H +#define WINPR_SPEC_H + +#include + +#ifdef _WIN32 + +#include +#ifndef _COM_Outptr_ +#define _COM_Outptr_ +#endif + +#else + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#define DUMMYUNIONNAME u +#define DUMMYUNIONNAME1 u1 +#define DUMMYUNIONNAME2 u2 +#define DUMMYUNIONNAME3 u3 +#define DUMMYUNIONNAME4 u4 +#define DUMMYUNIONNAME5 u5 +#define DUMMYUNIONNAME6 u6 +#define DUMMYUNIONNAME7 u7 +#define DUMMYUNIONNAME8 u8 + +#define DUMMYSTRUCTNAME s +#define DUMMYSTRUCTNAME1 s1 +#define DUMMYSTRUCTNAME2 s2 +#define DUMMYSTRUCTNAME3 s3 +#define DUMMYSTRUCTNAME4 s4 +#define DUMMYSTRUCTNAME5 s5 + +#if (defined(_M_AMD64) || defined(_M_ARM)) && !defined(_WIN32) +#define _UNALIGNED __unaligned +#else +#define _UNALIGNED +#endif + +#ifndef DECLSPEC_ALIGN +#if defined(_MSC_VER) && (_MSC_VER >= 1300) && !defined(MIDL_PASS) +#define DECLSPEC_ALIGN(x) __declspec(align(x)) +#elif defined(__GNUC__) +#define DECLSPEC_ALIGN(x) __attribute__((__aligned__(x))) +#else +#define DECLSPEC_ALIGN(x) +#endif +#endif /* DECLSPEC_ALIGN */ + +#ifdef _M_AMD64 +#define MEMORY_ALLOCATION_ALIGNMENT 16 +#else +#define MEMORY_ALLOCATION_ALIGNMENT 8 +#endif + +#ifdef __GNUC__ +#ifndef __declspec +#define __declspec(e) __attribute__((e)) +#endif +#endif + +#ifndef DECLSPEC_NORETURN +#if (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__)) +#define DECLSPEC_NORETURN __declspec(noreturn) +#else +#define DECLSPEC_NORETURN +#endif +#endif /* DECLSPEC_NORETURN */ + +/** + * Header Annotations: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa383701/ + */ + +#define __field_bcount(size) __notnull __byte_writableTo(size) +#define __field_ecount(size) __notnull __elem_writableTo(size) +#define __post_invalid _Post_ __notvalid + +#define __deref_in +#define __deref_in_ecount(size) +#define __deref_in_bcount(size) +#define __deref_in_opt +#define __deref_in_ecount_opt(size) +#define __deref_in_bcount_opt(size) +#define __deref_opt_in +#define __deref_opt_in_ecount(size) +#define __deref_opt_in_bcount(size) +#define __deref_opt_in_opt +#define __deref_opt_in_ecount_opt(size) +#define __deref_opt_in_bcount_opt(size) +#define __out_awcount(expr, size) +#define __in_awcount(expr, size) +#define __nullnullterminated +#define __in_data_source(src_sym) +#define __kernel_entry +#define __out_data_source(src_sym) +#define __analysis_noreturn +#define _Check_return_opt_ +#define _Check_return_wat_ + +#define __inner_exceptthat +#define __inner_typefix(ctype) +#define _Always_(annos) +#define _Analysis_noreturn_ +#define _Analysis_assume_(expr) +#define _At_(target, annos) +#define _At_buffer_(target, iter, bound, annos) +#define _Check_return_ +#define _COM_Outptr_ +#define _COM_Outptr_opt_ +#define _COM_Outptr_opt_result_maybenull_ +#define _COM_Outptr_result_maybenull_ +#define _Const_ +#define _Deref_in_bound_ +#define _Deref_in_range_(lb, ub) +#define _Deref_inout_bound_ +#define _Deref_inout_z_ +#define _Deref_inout_z_bytecap_c_(size) +#define _Deref_inout_z_cap_c_(size) +#define _Deref_opt_out_ +#define _Deref_opt_out_opt_ +#define _Deref_opt_out_opt_z_ +#define _Deref_opt_out_z_ +#define _Deref_out_ +#define _Deref_out_bound_ +#define _Deref_out_opt_ +#define _Deref_out_opt_z_ +#define _Deref_out_range_(lb, ub) +#define _Deref_out_z_ +#define _Deref_out_z_bytecap_c_(size) +#define _Deref_out_z_cap_c_(size) +#define _Deref_post_bytecap_(size) +#define _Deref_post_bytecap_c_(size) +#define _Deref_post_bytecap_x_(size) +#define _Deref_post_bytecount_(size) +#define _Deref_post_bytecount_c_(size) +#define _Deref_post_bytecount_x_(size) +#define _Deref_post_cap_(size) +#define _Deref_post_cap_c_(size) +#define _Deref_post_cap_x_(size) +#define _Deref_post_count_(size) +#define _Deref_post_count_c_(size) +#define _Deref_post_count_x_(size) +#define _Deref_post_maybenull_ +#define _Deref_post_notnull_ +#define _Deref_post_null_ +#define _Deref_post_opt_bytecap_(size) +#define _Deref_post_opt_bytecap_c_(size) +#define _Deref_post_opt_bytecap_x_(size) +#define _Deref_post_opt_bytecount_(size) +#define _Deref_post_opt_bytecount_c_(size) +#define _Deref_post_opt_bytecount_x_(size) +#define _Deref_post_opt_cap_(size) +#define _Deref_post_opt_cap_c_(size) +#define _Deref_post_opt_cap_x_(size) +#define _Deref_post_opt_count_(size) +#define _Deref_post_opt_count_c_(size) +#define _Deref_post_opt_count_x_(size) +#define _Deref_post_opt_valid_ +#define _Deref_post_opt_valid_bytecap_(size) +#define _Deref_post_opt_valid_bytecap_c_(size) +#define _Deref_post_opt_valid_bytecap_x_(size) +#define _Deref_post_opt_valid_cap_(size) +#define _Deref_post_opt_valid_cap_c_(size) +#define _Deref_post_opt_valid_cap_x_(size) +#define _Deref_post_opt_z_ +#define _Deref_post_opt_z_bytecap_(size) +#define _Deref_post_opt_z_bytecap_c_(size) +#define _Deref_post_opt_z_bytecap_x_(size) +#define _Deref_post_opt_z_cap_(size) +#define _Deref_post_opt_z_cap_c_(size) +#define _Deref_post_opt_z_cap_x_(size) +#define _Deref_post_valid_ +#define _Deref_post_valid_bytecap_(size) +#define _Deref_post_valid_bytecap_c_(size) +#define _Deref_post_valid_bytecap_x_(size) +#define _Deref_post_valid_cap_(size) +#define _Deref_post_valid_cap_c_(size) +#define _Deref_post_valid_cap_x_(size) +#define _Deref_post_z_ +#define _Deref_post_z_bytecap_(size) +#define _Deref_post_z_bytecap_c_(size) +#define _Deref_post_z_bytecap_x_(size) +#define _Deref_post_z_cap_(size) +#define _Deref_post_z_cap_c_(size) +#define _Deref_post_z_cap_x_(size) +#define _Deref_pre_bytecap_(size) +#define _Deref_pre_bytecap_c_(size) +#define _Deref_pre_bytecap_x_(size) +#define _Deref_pre_bytecount_(size) +#define _Deref_pre_bytecount_c_(size) +#define _Deref_pre_bytecount_x_(size) +#define _Deref_pre_cap_(size) +#define _Deref_pre_cap_c_(size) +#define _Deref_pre_cap_x_(size) +#define _Deref_pre_count_(size) +#define _Deref_pre_count_c_(size) +#define _Deref_pre_count_x_(size) +#define _Deref_pre_invalid_ +#define _Deref_pre_maybenull_ +#define _Deref_pre_notnull_ +#define _Deref_pre_null_ +#define _Deref_pre_opt_bytecap_(size) +#define _Deref_pre_opt_bytecap_c_(size) +#define _Deref_pre_opt_bytecap_x_(size) +#define _Deref_pre_opt_bytecount_(size) +#define _Deref_pre_opt_bytecount_c_(size) +#define _Deref_pre_opt_bytecount_x_(size) +#define _Deref_pre_opt_cap_(size) +#define _Deref_pre_opt_cap_c_(size) +#define _Deref_pre_opt_cap_x_(size) +#define _Deref_pre_opt_count_(size) +#define _Deref_pre_opt_count_c_(size) +#define _Deref_pre_opt_count_x_(size) +#define _Deref_pre_opt_valid_ +#define _Deref_pre_opt_valid_bytecap_(size) +#define _Deref_pre_opt_valid_bytecap_c_(size) +#define _Deref_pre_opt_valid_bytecap_x_(size) +#define _Deref_pre_opt_valid_cap_(size) +#define _Deref_pre_opt_valid_cap_c_(size) +#define _Deref_pre_opt_valid_cap_x_(size) +#define _Deref_pre_opt_z_ +#define _Deref_pre_opt_z_bytecap_(size) +#define _Deref_pre_opt_z_bytecap_c_(size) +#define _Deref_pre_opt_z_bytecap_x_(size) +#define _Deref_pre_opt_z_cap_(size) +#define _Deref_pre_opt_z_cap_c_(size) +#define _Deref_pre_opt_z_cap_x_(size) +#define _Deref_pre_readonly_ +#define _Deref_pre_valid_ +#define _Deref_pre_valid_bytecap_(size) +#define _Deref_pre_valid_bytecap_c_(size) +#define _Deref_pre_valid_bytecap_x_(size) +#define _Deref_pre_valid_cap_(size) +#define _Deref_pre_valid_cap_c_(size) +#define _Deref_pre_valid_cap_x_(size) +#define _Deref_pre_writeonly_ +#define _Deref_pre_z_ +#define _Deref_pre_z_bytecap_(size) +#define _Deref_pre_z_bytecap_c_(size) +#define _Deref_pre_z_bytecap_x_(size) +#define _Deref_pre_z_cap_(size) +#define _Deref_pre_z_cap_c_(size) +#define _Deref_pre_z_cap_x_(size) +#define _Deref_prepost_bytecap_(size) +#define _Deref_prepost_bytecap_x_(size) +#define _Deref_prepost_bytecount_(size) +#define _Deref_prepost_bytecount_x_(size) +#define _Deref_prepost_cap_(size) +#define _Deref_prepost_cap_x_(size) +#define _Deref_prepost_count_(size) +#define _Deref_prepost_count_x_(size) +#define _Deref_prepost_opt_bytecap_(size) +#define _Deref_prepost_opt_bytecap_x_(size) +#define _Deref_prepost_opt_bytecount_(size) +#define _Deref_prepost_opt_bytecount_x_(size) +#define _Deref_prepost_opt_cap_(size) +#define _Deref_prepost_opt_cap_x_(size) +#define _Deref_prepost_opt_count_(size) +#define _Deref_prepost_opt_count_x_(size) +#define _Deref_prepost_opt_valid_ +#define _Deref_prepost_opt_valid_bytecap_(size) +#define _Deref_prepost_opt_valid_bytecap_x_(size) +#define _Deref_prepost_opt_valid_cap_(size) +#define _Deref_prepost_opt_valid_cap_x_(size) +#define _Deref_prepost_opt_z_ +#define _Deref_prepost_opt_z_bytecap_(size) +#define _Deref_prepost_opt_z_cap_(size) +#define _Deref_prepost_valid_ +#define _Deref_prepost_valid_bytecap_(size) +#define _Deref_prepost_valid_bytecap_x_(size) +#define _Deref_prepost_valid_cap_(size) +#define _Deref_prepost_valid_cap_x_(size) +#define _Deref_prepost_z_ +#define _Deref_prepost_z_bytecap_(size) +#define _Deref_prepost_z_cap_(size) +#define _Deref_ret_bound_ +#define _Deref_ret_opt_z_ +#define _Deref_ret_range_(lb, ub) +#define _Deref_ret_z_ +#define _Deref2_pre_readonly_ +#define _Field_range_(min, max) +#define _Field_size_(size) +#define _Field_size_bytes_(size) +#define _Field_size_bytes_full_(size) +#define _Field_size_bytes_full_opt_(size) +#define _Field_size_bytes_opt_(size) +#define _Field_size_bytes_part_(size, count) +#define _Field_size_bytes_part_opt_(size, count) +#define _Field_size_full_(size) +#define _Field_size_full_opt_(size) +#define _Field_size_opt_(size) +#define _Field_size_part_(size, count) +#define _Field_size_part_opt_(size, count) +#define _Field_z_ +#define _Function_class_(x) +#define _Group_(annos) +#define _In_ +#define _In_bound_ +#define _In_bytecount_(size) +#define _In_bytecount_c_(size) +#define _In_bytecount_x_(size) +#define _In_count_(size) +#define _In_count_c_(size) +#define _In_count_x_(size) +#define _In_defensive_(annotes) +#define _In_opt_ +#define _In_opt_bytecount_(size) +#define _In_opt_bytecount_c_(size) +#define _In_opt_bytecount_x_(size) +#define _In_opt_count_(size) +#define _In_opt_count_c_(size) +#define _In_opt_count_x_(size) +#define _In_opt_ptrdiff_count_(size) +#define _In_opt_z_ +#define _In_opt_z_bytecount_(size) +#define _In_opt_z_bytecount_c_(size) +#define _In_opt_z_count_(size) +#define _In_opt_z_count_c_(size) +#define _In_ptrdiff_count_(size) +#define _In_range_(lb, ub) +#define _In_reads_(size) +#define _In_reads_bytes_(size) +#define _In_reads_bytes_opt_(size) +#define _In_reads_opt_(size) +#define _In_reads_opt_z_(size) +#define _In_reads_or_z_(size) +#define _In_reads_to_ptr_(ptr) +#define _In_reads_to_ptr_opt_(ptr) +#define _In_reads_to_ptr_opt_z_(ptr) +#define _In_reads_to_ptr_z_(ptr) +#define _In_reads_z_(size) +#define _In_z_ +#define _In_z_bytecount_(size) +#define _In_z_bytecount_c_(size) +#define _In_z_count_(size) +#define _In_z_count_c_(size) +#define _Inout_ +#define _Inout_bytecap_(size) +#define _Inout_bytecap_c_(size) +#define _Inout_bytecap_x_(size) +#define _Inout_bytecount_(size) +#define _Inout_bytecount_c_(size) +#define _Inout_bytecount_x_(size) +#define _Inout_cap_(size) +#define _Inout_cap_c_(size) +#define _Inout_cap_x_(size) +#define _Inout_count_(size) +#define _Inout_count_c_(size) +#define _Inout_count_x_(size) +#define _Inout_defensive_(annotes) +#define _Inout_opt_ +#define _Inout_opt_bytecap_(size) +#define _Inout_opt_bytecap_c_(size) +#define _Inout_opt_bytecap_x_(size) +#define _Inout_opt_bytecount_(size) +#define _Inout_opt_bytecount_c_(size) +#define _Inout_opt_bytecount_x_(size) +#define _Inout_opt_cap_(size) +#define _Inout_opt_cap_c_(size) +#define _Inout_opt_cap_x_(size) +#define _Inout_opt_count_(size) +#define _Inout_opt_count_c_(size) +#define _Inout_opt_count_x_(size) +#define _Inout_opt_ptrdiff_count_(size) +#define _Inout_opt_z_ +#define _Inout_opt_z_bytecap_(size) +#define _Inout_opt_z_bytecap_c_(size) +#define _Inout_opt_z_bytecap_x_(size) +#define _Inout_opt_z_bytecount_(size) +#define _Inout_opt_z_bytecount_c_(size) +#define _Inout_opt_z_cap_(size) +#define _Inout_opt_z_cap_c_(size) +#define _Inout_opt_z_cap_x_(size) +#define _Inout_opt_z_count_(size) +#define _Inout_opt_z_count_c_(size) +#define _Inout_ptrdiff_count_(size) +#define _Inout_updates_(size) +#define _Inout_updates_all_(size) +#define _Inout_updates_all_opt_(size) +#define _Inout_updates_bytes_(size) +#define _Inout_updates_bytes_all_(size) +#define _Inout_updates_bytes_all_opt_(size) +#define _Inout_updates_bytes_opt_(size) +#define _Inout_updates_bytes_to_(size, count) +#define _Inout_updates_bytes_to_opt_(size, count) +#define _Inout_updates_opt_(size) +#define _Inout_updates_opt_z_(size) +#define _Inout_updates_to_(size, count) +#define _Inout_updates_to_opt_(size, count) +#define _Inout_updates_z_(size) +#define _Inout_z_ +#define _Inout_z_bytecap_(size) +#define _Inout_z_bytecap_c_(size) +#define _Inout_z_bytecap_x_(size) +#define _Inout_z_bytecount_(size) +#define _Inout_z_bytecount_c_(size) +#define _Inout_z_cap_(size) +#define _Inout_z_cap_c_(size) +#define _Inout_z_cap_x_(size) +#define _Inout_z_count_(size) +#define _Inout_z_count_c_(size) +#define _Interlocked_operand_ +#define _Literal_ +#define _Maybenull_ +#define _Maybevalid_ +#define _Maybe_raises_SEH_exception +#define _Must_inspect_result_ +#define _Notliteral_ +#define _Notnull_ +#define _Notref_ +#define _Notvalid_ +#define _Null_ +#define _Null_terminated_ +#define _NullNull_terminated_ +#define _On_failure_(annos) +#define _Out_ +#define _Out_bound_ +#define _Out_bytecap_(size) +#define _Out_bytecap_c_(size) +#define _Out_bytecap_post_bytecount_(cap, count) +#define _Out_bytecap_x_(size) +#define _Out_bytecapcount_(capcount) +#define _Out_bytecapcount_x_(capcount) +#define _Out_cap_(size) +#define _Out_cap_c_(size) +#define _Out_cap_m_(mult, size) +#define _Out_cap_post_count_(cap, count) +#define _Out_cap_x_(size) +#define _Out_capcount_(capcount) +#define _Out_capcount_x_(capcount) +#define _Out_defensive_(annotes) +#define _Out_opt_ +#define _Out_opt_bytecap_(size) +#define _Out_opt_bytecap_c_(size) +#define _Out_opt_bytecap_post_bytecount_(cap, count) +#define _Out_opt_bytecap_x_(size) +#define _Out_opt_bytecapcount_(capcount) +#define _Out_opt_bytecapcount_x_(capcount) +#define _Out_opt_cap_(size) +#define _Out_opt_cap_c_(size) +#define _Out_opt_cap_m_(mult, size) +#define _Out_opt_cap_post_count_(cap, count) +#define _Out_opt_cap_x_(size) +#define _Out_opt_capcount_(capcount) +#define _Out_opt_capcount_x_(capcount) +#define _Out_opt_ptrdiff_cap_(size) +#define _Out_opt_z_bytecap_(size) +#define _Out_opt_z_bytecap_c_(size) +#define _Out_opt_z_bytecap_post_bytecount_(cap, count) +#define _Out_opt_z_bytecap_x_(size) +#define _Out_opt_z_bytecapcount_(capcount) +#define _Out_opt_z_cap_(size) +#define _Out_opt_z_cap_c_(size) +#define _Out_opt_z_cap_m_(mult, size) +#define _Out_opt_z_cap_post_count_(cap, count) +#define _Out_opt_z_cap_x_(size) +#define _Out_opt_z_capcount_(capcount) +#define _Out_ptrdiff_cap_(size) +#define _Out_range_(lb, ub) +#define _Out_writes_(size) +#define _Out_writes_all_(size) +#define _Out_writes_all_opt_(size) +#define _Out_writes_bytes_(size) +#define _Out_writes_bytes_all_(size) +#define _Out_writes_bytes_all_opt_(size) +#define _Out_writes_bytes_opt_(size) +#define _Out_writes_bytes_to_(size, count) +#define _Out_writes_bytes_to_opt_(size, count) +#define _Out_writes_opt_(size) +#define _Out_writes_opt_z_(size) +#define _Out_writes_to_(size, count) +#define _Out_writes_to_opt_(size, count) +#define _Out_writes_to_ptr_(ptr) +#define _Out_writes_to_ptr_opt_(ptr) +#define _Out_writes_to_ptr_opt_z_(ptr) +#define _Out_writes_to_ptr_z_(ptr) +#define _Out_writes_z_(size) +#define _Out_z_bytecap_(size) +#define _Out_z_bytecap_c_(size) +#define _Out_z_bytecap_post_bytecount_(cap, count) +#define _Out_z_bytecap_x_(size) +#define _Out_z_bytecapcount_(capcount) +#define _Out_z_cap_(size) +#define _Out_z_cap_c_(size) +#define _Out_z_cap_m_(mult, size) +#define _Out_z_cap_post_count_(cap, count) +#define _Out_z_cap_x_(size) +#define _Out_z_capcount_(capcount) +#define _Outptr_ +#define _Outptr_opt_ +#define _Outptr_opt_result_buffer_(size) +#define _Outptr_opt_result_buffer_all_(size) +#define _Outptr_opt_result_buffer_all_maybenull_(size) +#define _Outptr_opt_result_buffer_maybenull_(size) +#define _Outptr_opt_result_buffer_to_(size, count) +#define _Outptr_opt_result_buffer_to_maybenull_(size, count) +#define _Outptr_opt_result_bytebuffer_(size) +#define _Outptr_opt_result_bytebuffer_all_(size) +#define _Outptr_opt_result_bytebuffer_all_maybenull_(size) +#define _Outptr_opt_result_bytebuffer_maybenull_(size) +#define _Outptr_opt_result_bytebuffer_to_(size, count) +#define _Outptr_opt_result_bytebuffer_to_maybenull_(size, count) +#define _Outptr_opt_result_maybenull_ +#define _Outptr_opt_result_maybenull_z_ +#define _Outptr_opt_result_nullonfailure_ +#define _Outptr_opt_result_z_ +#define _Outptr_result_buffer_(size) +#define _Outptr_result_buffer_all_(size) +#define _Outptr_result_buffer_all_maybenull_(size) +#define _Outptr_result_buffer_maybenull_(size) +#define _Outptr_result_buffer_to_(size, count) +#define _Outptr_result_buffer_to_maybenull_(size, count) +#define _Outptr_result_bytebuffer_(size) +#define _Outptr_result_bytebuffer_all_(size) +#define _Outptr_result_bytebuffer_all_maybenull_(size) +#define _Outptr_result_bytebuffer_maybenull_(size) +#define _Outptr_result_bytebuffer_to_(size, count) +#define _Outptr_result_bytebuffer_to_maybenull_(size, count) +#define _Outptr_result_maybenull_ +#define _Outptr_result_maybenull_z_ +#define _Outptr_result_nullonfailure_ +#define _Outptr_result_z_ +#define _Outref_ +#define _Outref_result_buffer_(size) +#define _Outref_result_buffer_all_(size) +#define _Outref_result_buffer_all_maybenull_(size) +#define _Outref_result_buffer_maybenull_(size) +#define _Outref_result_buffer_to_(size, count) +#define _Outref_result_buffer_to_maybenull_(size, count) +#define _Outref_result_bytebuffer_(size) +#define _Outref_result_bytebuffer_all_(size) +#define _Outref_result_bytebuffer_all_maybenull_(size) +#define _Outref_result_bytebuffer_maybenull_(size) +#define _Outref_result_bytebuffer_to_(size, count) +#define _Outref_result_bytebuffer_to_maybenull_(size, count) +#define _Outref_result_maybenull_ +#define _Outref_result_nullonfailure_ +#define _Points_to_data_ +#define _Post_ +#define _Post_bytecap_(size) +#define _Post_bytecount_(size) +#define _Post_bytecount_c_(size) +#define _Post_bytecount_x_(size) +#define _Post_cap_(size) +#define _Post_count_(size) +#define _Post_count_c_(size) +#define _Post_count_x_(size) +#define _Post_defensive_ +#define _Post_equal_to_(expr) +#define _Post_invalid_ +#define _Post_maybenull_ +#define _Post_maybez_ +#define _Post_notnull_ +#define _Post_null_ +#define _Post_ptr_invalid_ +#define _Post_readable_byte_size_(size) +#define _Post_readable_size_(size) +#define _Post_satisfies_(cond) +#define _Post_valid_ +#define _Post_writable_byte_size_(size) +#define _Post_writable_size_(size) +#define _Post_z_ +#define _Post_z_bytecount_(size) +#define _Post_z_bytecount_c_(size) +#define _Post_z_bytecount_x_(size) +#define _Post_z_count_(size) +#define _Post_z_count_c_(size) +#define _Post_z_count_x_(size) +#define _Pre_ +#define _Pre_bytecap_(size) +#define _Pre_bytecap_c_(size) +#define _Pre_bytecap_x_(size) +#define _Pre_bytecount_(size) +#define _Pre_bytecount_c_(size) +#define _Pre_bytecount_x_(size) +#define _Pre_cap_(size) +#define _Pre_cap_c_(size) +#define _Pre_cap_c_one_ +#define _Pre_cap_for_(param) +#define _Pre_cap_m_(mult, size) +#define _Pre_cap_x_(size) +#define _Pre_count_(size) +#define _Pre_count_c_(size) +#define _Pre_count_x_(size) +#define _Pre_defensive_ +#define _Pre_equal_to_(expr) +#define _Pre_invalid_ +#define _Pre_maybenull_ +#define _Pre_notnull_ +#define _Pre_null_ +#define _Pre_opt_bytecap_(size) +#define _Pre_opt_bytecap_c_(size) +#define _Pre_opt_bytecap_x_(size) +#define _Pre_opt_bytecount_(size) +#define _Pre_opt_bytecount_c_(size) +#define _Pre_opt_bytecount_x_(size) +#define _Pre_opt_cap_(size) +#define _Pre_opt_cap_c_(size) +#define _Pre_opt_cap_c_one_ +#define _Pre_opt_cap_for_(param) +#define _Pre_opt_cap_m_(mult, size) +#define _Pre_opt_cap_x_(size) +#define _Pre_opt_count_(size) +#define _Pre_opt_count_c_(size) +#define _Pre_opt_count_x_(size) +#define _Pre_opt_ptrdiff_cap_(ptr) +#define _Pre_opt_ptrdiff_count_(ptr) +#define _Pre_opt_valid_ +#define _Pre_opt_valid_bytecap_(size) +#define _Pre_opt_valid_bytecap_c_(size) +#define _Pre_opt_valid_bytecap_x_(size) +#define _Pre_opt_valid_cap_(size) +#define _Pre_opt_valid_cap_c_(size) +#define _Pre_opt_valid_cap_x_(size) +#define _Pre_opt_z_ +#define _Pre_opt_z_bytecap_(size) +#define _Pre_opt_z_bytecap_c_(size) +#define _Pre_opt_z_bytecap_x_(size) +#define _Pre_opt_z_cap_(size) +#define _Pre_opt_z_cap_c_(size) +#define _Pre_opt_z_cap_x_(size) +#define _Pre_ptrdiff_cap_(ptr) +#define _Pre_ptrdiff_count_(ptr) +#define _Pre_readable_byte_size_(size) +#define _Pre_readable_size_(size) +#define _Pre_readonly_ +#define _Pre_satisfies_(cond) +#define _Pre_unknown_ +#define _Pre_valid_ +#define _Pre_valid_bytecap_(size) +#define _Pre_valid_bytecap_c_(size) +#define _Pre_valid_bytecap_x_(size) +#define _Pre_valid_cap_(size) +#define _Pre_valid_cap_c_(size) +#define _Pre_valid_cap_x_(size) +#define _Pre_writable_byte_size_(size) +#define _Pre_writable_size_(size) +#define _Pre_writeonly_ +#define _Pre_z_ +#define _Pre_z_bytecap_(size) +#define _Pre_z_bytecap_c_(size) +#define _Pre_z_bytecap_x_(size) +#define _Pre_z_cap_(size) +#define _Pre_z_cap_c_(size) +#define _Pre_z_cap_x_(size) +#define _Prepost_bytecount_(size) +#define _Prepost_bytecount_c_(size) +#define _Prepost_bytecount_x_(size) +#define _Prepost_count_(size) +#define _Prepost_count_c_(size) +#define _Prepost_count_x_(size) +#define _Prepost_opt_bytecount_(size) +#define _Prepost_opt_bytecount_c_(size) +#define _Prepost_opt_bytecount_x_(size) +#define _Prepost_opt_count_(size) +#define _Prepost_opt_count_c_(size) +#define _Prepost_opt_count_x_(size) +#define _Prepost_opt_valid_ +#define _Prepost_opt_z_ +#define _Prepost_valid_ +#define _Prepost_z_ +#define _Printf_format_string_ +#define _Raises_SEH_exception_ +#define _Maybe_raises_SEH_exception_ +#define _Readable_bytes_(size) +#define _Readable_elements_(size) +#define _Reserved_ +#define _Result_nullonfailure_ +#define _Result_zeroonfailure_ +#define __inner_callback +#define _Ret_ +#define _Ret_bound_ +#define _Ret_bytecap_(size) +#define _Ret_bytecap_c_(size) +#define _Ret_bytecap_x_(size) +#define _Ret_bytecount_(size) +#define _Ret_bytecount_c_(size) +#define _Ret_bytecount_x_(size) +#define _Ret_cap_(size) +#define _Ret_cap_c_(size) +#define _Ret_cap_x_(size) +#define _Ret_count_(size) +#define _Ret_count_c_(size) +#define _Ret_count_x_(size) +#define _Ret_maybenull_ +#define _Ret_maybenull_z_ +#define _Ret_notnull_ +#define _Ret_null_ +#define _Ret_opt_ +#define _Ret_opt_bytecap_(size) +#define _Ret_opt_bytecap_c_(size) +#define _Ret_opt_bytecap_x_(size) +#define _Ret_opt_bytecount_(size) +#define _Ret_opt_bytecount_c_(size) +#define _Ret_opt_bytecount_x_(size) +#define _Ret_opt_cap_(size) +#define _Ret_opt_cap_c_(size) +#define _Ret_opt_cap_x_(size) +#define _Ret_opt_count_(size) +#define _Ret_opt_count_c_(size) +#define _Ret_opt_count_x_(size) +#define _Ret_opt_valid_ +#define _Ret_opt_z_ +#define _Ret_opt_z_bytecap_(size) +#define _Ret_opt_z_bytecount_(size) +#define _Ret_opt_z_cap_(size) +#define _Ret_opt_z_count_(size) +#define _Ret_range_(lb, ub) +#define _Ret_valid_ +#define _Ret_writes_(size) +#define _Ret_writes_bytes_(size) +#define _Ret_writes_bytes_maybenull_(size) +#define _Ret_writes_bytes_to_(size, count) +#define _Ret_writes_bytes_to_maybenull_(size, count) +#define _Ret_writes_maybenull_(size) +#define _Ret_writes_maybenull_z_(size) +#define _Ret_writes_to_(size, count) +#define _Ret_writes_to_maybenull_(size, count) +#define _Ret_writes_z_(size) +#define _Ret_z_ +#define _Ret_z_bytecap_(size) +#define _Ret_z_bytecount_(size) +#define _Ret_z_cap_(size) +#define _Ret_z_count_(size) +#define _Return_type_success_(expr) +#define _Scanf_format_string_ +#define _Scanf_s_format_string_ +#define _Struct_size_bytes_(size) +#define _Success_(expr) +#define _Unchanged_(e) +#define _Use_decl_annotations_ +#define _Valid_ +#define _When_(expr, annos) +#define _Writable_bytes_(size) +#define _Writable_elements_(size) + +#define __bcount(size) +#define __bcount_opt(size) +#define __deref_bcount(size) +#define __deref_bcount_opt(size) +#define __deref_ecount(size) +#define __deref_ecount_opt(size) +#define __deref_in +#define __deref_in_bcount(size) +#define __deref_in_bcount_opt(size) +#define __deref_in_ecount(size) +#define __deref_in_ecount_opt(size) +#define __deref_in_opt +#define __deref_inout +#define __deref_inout_bcount(size) +#define __deref_inout_bcount_full(size) +#define __deref_inout_bcount_full_opt(size) +#define __deref_inout_bcount_opt(size) +#define __deref_inout_bcount_part(size, length) +#define __deref_inout_bcount_part_opt(size, length) +#define __deref_inout_ecount(size) +#define __deref_inout_ecount_full(size) +#define __deref_inout_ecount_full_opt(size) +#define __deref_inout_ecount_opt(size) +#define __deref_inout_ecount_part(size, length) +#define __deref_inout_ecount_part_opt(size, length) +#define __deref_inout_opt +#define __deref_opt_bcount(size) +#define __deref_opt_bcount_opt(size) +#define __deref_opt_ecount(size) +#define __deref_opt_ecount_opt(size) +#define __deref_opt_in +#define __deref_opt_in_bcount(size) +#define __deref_opt_in_bcount_opt(size) +#define __deref_opt_in_ecount(size) +#define __deref_opt_in_ecount_opt(size) +#define __deref_opt_in_opt +#define __deref_opt_inout +#define __deref_opt_inout_bcount(size) +#define __deref_opt_inout_bcount_full(size) +#define __deref_opt_inout_bcount_full_opt(size) +#define __deref_opt_inout_bcount_opt(size) +#define __deref_opt_inout_bcount_part(size, length) +#define __deref_opt_inout_bcount_part_opt(size, length) +#define __deref_opt_inout_ecount(size) +#define __deref_opt_inout_ecount_full(size) +#define __deref_opt_inout_ecount_full_opt(size) +#define __deref_opt_inout_ecount_opt(size) +#define __deref_opt_inout_ecount_part(size, length) +#define __deref_opt_inout_ecount_part_opt(size, length) +#define __deref_opt_inout_opt +#define __deref_opt_out +#define __deref_opt_out_bcount(size) +#define __deref_opt_out_bcount_full(size) +#define __deref_opt_out_bcount_full_opt(size) +#define __deref_opt_out_bcount_opt(size) +#define __deref_opt_out_bcount_part(size, length) +#define __deref_opt_out_bcount_part_opt(size, length) +#define __deref_opt_out_ecount(size) +#define __deref_opt_out_ecount_full(size) +#define __deref_opt_out_ecount_full_opt(size) +#define __deref_opt_out_ecount_opt(size) +#define __deref_opt_out_ecount_part(size, length) +#define __deref_opt_out_ecount_part_opt(size, length) +#define __deref_opt_out_opt +#define __deref_out +#define __deref_out_bcount(size) +#define __deref_out_bcount_full(size) +#define __deref_out_bcount_full_opt(size) +#define __deref_out_bcount_opt(size) +#define __deref_out_bcount_part(size, length) +#define __deref_out_bcount_part_opt(size, length) +#define __deref_out_ecount(size) +#define __deref_out_ecount_full(size) +#define __deref_out_ecount_full_opt(size) +#define __deref_out_ecount_opt(size) +#define __deref_out_ecount_part(size, length) +#define __deref_out_ecount_part_opt(size, length) +#define __deref_out_opt +#define __ecount(size) +#define __ecount_opt(size) +//#define __in /* Conflicts with libstdc++ header macros */ +#define __in_bcount(size) +#define __in_bcount_opt(size) +#define __in_ecount(size) +#define __in_ecount_opt(size) +#define __in_opt +#define __inout +#define __inout_bcount(size) +#define __inout_bcount_full(size) +#define __inout_bcount_full_opt(size) +#define __inout_bcount_opt(size) +#define __inout_bcount_part(size, length) +#define __inout_bcount_part_opt(size, length) +#define __inout_ecount(size) +#define __inout_ecount_full(size) +#define __inout_ecount_full_opt(size) +#define __inout_ecount_opt(size) +#define __inout_ecount_part(size, length) +#define __inout_ecount_part_opt(size, length) +#define __inout_opt +//#define __out /* Conflicts with libstdc++ header macros */ +#define __out_bcount(size) +#define __out_bcount_full(size) +#define __out_bcount_full_opt(size) +#define __out_bcount_opt(size) +#define __out_bcount_part(size, length) +#define __out_bcount_part_opt(size, length) +#define __out_ecount(size) +#define __out_ecount_full(size) +#define __out_ecount_full_opt(size) +#define __out_ecount_opt(size) +#define __out_ecount_part(size, length) +#define __out_ecount_part_opt(size, length) +#define __out_opt + +#define __blocksOn(resource) +#define __callback +#define __checkReturn +#define __format_string +#define __in_awcount(expr, size) +#define __nullnullterminated +#define __nullterminated +#define __out_awcount(expr, size) +#define __override +//#define __reserved /* Conflicts with header included by CarbonCore.h on OS X */ +#define __success(expr) +#define __typefix(ctype) + +#ifndef _countof +#ifndef __cplusplus +#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0])) +#else +extern "C++" +{ + template + char (*__countof_helper(_CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray]; +#define _countof(_Array) sizeof(*__countof_helper(_Array)) +} +#endif +#endif + +/** + * RTL Definitions + */ + +#define MINCHAR 0x80 +#define MAXCHAR 0x7F + +#ifndef MINSHORT +#define MINSHORT 0x8000 +#endif + +#ifndef MAXSHORT +#define MAXSHORT 0x7FFF +#endif + +#define MINLONG 0x80000000 +#define MAXLONG 0x7FFFFFFF +#define MAXBYTE 0xFF +#define MAXWORD 0xFFFF +#define MAXDWORD 0xFFFFFFFF + +#define FIELD_OFFSET(type, field) ((LONG)(LONG_PTR) & (((type*)0)->field)) + +#define RTL_FIELD_SIZE(type, field) (sizeof(((type*)0)->field)) + +#define RTL_SIZEOF_THROUGH_FIELD(type, field) \ + (FIELD_OFFSET(type, field) + RTL_FIELD_SIZE(type, field)) + +#define RTL_CONTAINS_FIELD(Struct, Size, Field) \ + ((((PCHAR)(&(Struct)->Field)) + sizeof((Struct)->Field)) <= (((PCHAR)(Struct)) + (Size))) + +#define RTL_NUMBER_OF_V1(A) (sizeof(A) / sizeof((A)[0])) +#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A) + +#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V1(A) + +#define ARRAYSIZE(A) RTL_NUMBER_OF_V2(A) +#define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A) + +#define RTL_FIELD_TYPE(type, field) (((type*)0)->field) + +#define RTL_NUMBER_OF_FIELD(type, field) (RTL_NUMBER_OF(RTL_FIELD_TYPE(type, field))) + +#define RTL_PADDING_BETWEEN_FIELDS(T, F1, F2) \ + ((FIELD_OFFSET(T, F2) > FIELD_OFFSET(T, F1)) \ + ? (FIELD_OFFSET(T, F2) - FIELD_OFFSET(T, F1) - RTL_FIELD_SIZE(T, F1)) \ + : (FIELD_OFFSET(T, F1) - FIELD_OFFSET(T, F2) - RTL_FIELD_SIZE(T, F2))) + +#if defined(__cplusplus) +#define RTL_CONST_CAST(type) const_cast +#else +#define RTL_CONST_CAST(type) (type) +#endif + +#define RTL_BITS_OF(sizeOfArg) (sizeof(sizeOfArg) * 8) + +#define RTL_BITS_OF_FIELD(type, field) (RTL_BITS_OF(RTL_FIELD_TYPE(type, field))) + +#define CONTAINING_RECORD(address, type, field) \ + ((type*)((PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field))) + +#if defined(__clang__) +WINPR_PRAGMA_DIAG_POP +#endif + +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +#ifdef __GNUC__ +#define DECLSPEC_EXPORT __attribute__((dllexport)) +#ifndef DECLSPEC_IMPORT +#define DECLSPEC_IMPORT __attribute__((dllimport)) +#endif /* DECLSPEC_IMPORT */ +#else +#define DECLSPEC_EXPORT __declspec(dllexport) +#define DECLSPEC_IMPORT __declspec(dllimport) +#endif /* __GNUC__ */ +#else +#if defined(__GNUC__) && __GNUC__ >= 4 +#define DECLSPEC_EXPORT __attribute__((visibility("default"))) +#define DECLSPEC_IMPORT +#else +#define DECLSPEC_EXPORT +#define DECLSPEC_IMPORT +#endif +#endif + +#endif /* WINPR_SPEC_H */ diff --git a/winpr/include/winpr/ssl.h b/winpr/include/winpr/ssl.h new file mode 100644 index 0000000..ff50097 --- /dev/null +++ b/winpr/include/winpr/ssl.h @@ -0,0 +1,49 @@ +/** + * WinPR: Windows Portable Runtime + * OpenSSL Library Initialization + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSL_H +#define WINPR_SSL_H + +#include +#include + +#define WINPR_SSL_INIT_DEFAULT 0x00 +#define WINPR_SSL_INIT_ALREADY_INITIALIZED 0x01 +#define WINPR_SSL_INIT_ENABLE_LOCKING 0x2 +#define WINPR_SSL_INIT_ENABLE_FIPS 0x4 + +#define WINPR_SSL_CLEANUP_GLOBAL 0x01 +#define WINPR_SSL_CLEANUP_THREAD 0x02 + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL winpr_InitializeSSL(DWORD flags); + WINPR_API BOOL winpr_CleanupSSL(DWORD flags); + + WINPR_API BOOL winpr_FIPSMode(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_SSL_H */ diff --git a/winpr/include/winpr/sspi.h b/winpr/include/winpr/sspi.h new file mode 100644 index 0000000..e565b40 --- /dev/null +++ b/winpr/include/winpr/sspi.h @@ -0,0 +1,1436 @@ +/** + * WinPR: Windows Portable Runtime + * Security Support Provider Interface (SSPI) + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_H +#define WINPR_SSPI_H + +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#include +#include + +#define SECURITY_WIN32 +#include +#include + +#endif /* _WIN32 */ + +#if !defined(_WIN32) || defined(_UWP) + +#ifndef SEC_ENTRY +#define SEC_ENTRY +#endif /* SEC_ENTRY */ + +typedef CHAR SEC_CHAR; +typedef WCHAR SEC_WCHAR; + +typedef struct +{ + UINT32 LowPart; + INT32 HighPart; +} SECURITY_INTEGER; + +typedef SECURITY_INTEGER TimeStamp; +typedef SECURITY_INTEGER* PTimeStamp; + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifndef __SECSTATUS_DEFINED__ +typedef LONG SECURITY_STATUS; +#define __SECSTATUS_DEFINED__ +#endif /* __SECSTATUS_DEFINED__ */ + +WINPR_PRAGMA_DIAG_POP + +typedef struct +{ + UINT32 fCapabilities; + UINT16 wVersion; + UINT16 wRPCID; + UINT32 cbMaxToken; + SEC_CHAR* Name; + SEC_CHAR* Comment; +} SecPkgInfoA; +typedef SecPkgInfoA* PSecPkgInfoA; + +typedef struct +{ + UINT32 fCapabilities; + UINT16 wVersion; + UINT16 wRPCID; + UINT32 cbMaxToken; + SEC_WCHAR* Name; + SEC_WCHAR* Comment; +} SecPkgInfoW; +typedef SecPkgInfoW* PSecPkgInfoW; + +#ifdef UNICODE +#define SecPkgInfo SecPkgInfoW +#define PSecPkgInfo PSecPkgInfoW +#else +#define SecPkgInfo SecPkgInfoA +#define PSecPkgInfo PSecPkgInfoA +#endif /* UNICODE */ + +#endif /* !defined(_WIN32) || defined(_UWP) */ + +#define NTLM_SSP_NAME _T("NTLM") +#define KERBEROS_SSP_NAME _T("Kerberos") +#define NEGO_SSP_NAME _T("Negotiate") + +#define SECPKG_ID_NONE 0xFFFF + +#define SECPKG_FLAG_INTEGRITY 0x00000001 +#define SECPKG_FLAG_PRIVACY 0x00000002 +#define SECPKG_FLAG_TOKEN_ONLY 0x00000004 +#define SECPKG_FLAG_DATAGRAM 0x00000008 +#define SECPKG_FLAG_CONNECTION 0x00000010 +#define SECPKG_FLAG_MULTI_REQUIRED 0x00000020 +#define SECPKG_FLAG_CLIENT_ONLY 0x00000040 +#define SECPKG_FLAG_EXTENDED_ERROR 0x00000080 +#define SECPKG_FLAG_IMPERSONATION 0x00000100 +#define SECPKG_FLAG_ACCEPT_WIN32_NAME 0x00000200 +#define SECPKG_FLAG_STREAM 0x00000400 +#define SECPKG_FLAG_NEGOTIABLE 0x00000800 +#define SECPKG_FLAG_GSS_COMPATIBLE 0x00001000 +#define SECPKG_FLAG_LOGON 0x00002000 +#define SECPKG_FLAG_ASCII_BUFFERS 0x00004000 +#define SECPKG_FLAG_FRAGMENT 0x00008000 +#define SECPKG_FLAG_MUTUAL_AUTH 0x00010000 +#define SECPKG_FLAG_DELEGATION 0x00020000 +#define SECPKG_FLAG_READONLY_WITH_CHECKSUM 0x00040000 +#define SECPKG_FLAG_RESTRICTED_TOKENS 0x00080000 +#define SECPKG_FLAG_NEGO_EXTENDER 0x00100000 +#define SECPKG_FLAG_NEGOTIABLE2 0x00200000 + +#ifndef _WINERROR_ + +#define SEC_E_OK (SECURITY_STATUS)0x00000000L +#define SEC_E_INSUFFICIENT_MEMORY (SECURITY_STATUS)0x80090300L +#define SEC_E_INVALID_HANDLE (SECURITY_STATUS)0x80090301L +#define SEC_E_UNSUPPORTED_FUNCTION (SECURITY_STATUS)0x80090302L +#define SEC_E_TARGET_UNKNOWN (SECURITY_STATUS)0x80090303L +#define SEC_E_INTERNAL_ERROR (SECURITY_STATUS)0x80090304L +#define SEC_E_SECPKG_NOT_FOUND (SECURITY_STATUS)0x80090305L +#define SEC_E_NOT_OWNER (SECURITY_STATUS)0x80090306L +#define SEC_E_CANNOT_INSTALL (SECURITY_STATUS)0x80090307L +#define SEC_E_INVALID_TOKEN (SECURITY_STATUS)0x80090308L +#define SEC_E_CANNOT_PACK (SECURITY_STATUS)0x80090309L +#define SEC_E_QOP_NOT_SUPPORTED (SECURITY_STATUS)0x8009030AL +#define SEC_E_NO_IMPERSONATION (SECURITY_STATUS)0x8009030BL +#define SEC_E_LOGON_DENIED (SECURITY_STATUS)0x8009030CL +#define SEC_E_UNKNOWN_CREDENTIALS (SECURITY_STATUS)0x8009030DL +#define SEC_E_NO_CREDENTIALS (SECURITY_STATUS)0x8009030EL +#define SEC_E_MESSAGE_ALTERED (SECURITY_STATUS)0x8009030FL +#define SEC_E_OUT_OF_SEQUENCE (SECURITY_STATUS)0x80090310L +#define SEC_E_NO_AUTHENTICATING_AUTHORITY (SECURITY_STATUS)0x80090311L +#define SEC_E_BAD_PKGID (SECURITY_STATUS)0x80090316L +#define SEC_E_CONTEXT_EXPIRED (SECURITY_STATUS)0x80090317L +#define SEC_E_INCOMPLETE_MESSAGE (SECURITY_STATUS)0x80090318L +#define SEC_E_INCOMPLETE_CREDENTIALS (SECURITY_STATUS)0x80090320L +#define SEC_E_BUFFER_TOO_SMALL (SECURITY_STATUS)0x80090321L +#define SEC_E_WRONG_PRINCIPAL (SECURITY_STATUS)0x80090322L +#define SEC_E_TIME_SKEW (SECURITY_STATUS)0x80090324L +#define SEC_E_UNTRUSTED_ROOT (SECURITY_STATUS)0x80090325L +#define SEC_E_ILLEGAL_MESSAGE (SECURITY_STATUS)0x80090326L +#define SEC_E_CERT_UNKNOWN (SECURITY_STATUS)0x80090327L +#define SEC_E_CERT_EXPIRED (SECURITY_STATUS)0x80090328L +#define SEC_E_ENCRYPT_FAILURE (SECURITY_STATUS)0x80090329L +#define SEC_E_DECRYPT_FAILURE (SECURITY_STATUS)0x80090330L +#define SEC_E_ALGORITHM_MISMATCH (SECURITY_STATUS)0x80090331L +#define SEC_E_SECURITY_QOS_FAILED (SECURITY_STATUS)0x80090332L +#define SEC_E_UNFINISHED_CONTEXT_DELETED (SECURITY_STATUS)0x80090333L +#define SEC_E_NO_TGT_REPLY (SECURITY_STATUS)0x80090334L +#define SEC_E_NO_IP_ADDRESSES (SECURITY_STATUS)0x80090335L +#define SEC_E_WRONG_CREDENTIAL_HANDLE (SECURITY_STATUS)0x80090336L +#define SEC_E_CRYPTO_SYSTEM_INVALID (SECURITY_STATUS)0x80090337L +#define SEC_E_MAX_REFERRALS_EXCEEDED (SECURITY_STATUS)0x80090338L +#define SEC_E_MUST_BE_KDC (SECURITY_STATUS)0x80090339L +#define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED (SECURITY_STATUS)0x8009033AL +#define SEC_E_TOO_MANY_PRINCIPALS (SECURITY_STATUS)0x8009033BL +#define SEC_E_NO_PA_DATA (SECURITY_STATUS)0x8009033CL +#define SEC_E_PKINIT_NAME_MISMATCH (SECURITY_STATUS)0x8009033DL +#define SEC_E_SMARTCARD_LOGON_REQUIRED (SECURITY_STATUS)0x8009033EL +#define SEC_E_SHUTDOWN_IN_PROGRESS (SECURITY_STATUS)0x8009033FL +#define SEC_E_KDC_INVALID_REQUEST (SECURITY_STATUS)0x80090340L +#define SEC_E_KDC_UNABLE_TO_REFER (SECURITY_STATUS)0x80090341L +#define SEC_E_KDC_UNKNOWN_ETYPE (SECURITY_STATUS)0x80090342L +#define SEC_E_UNSUPPORTED_PREAUTH (SECURITY_STATUS)0x80090343L +#define SEC_E_DELEGATION_REQUIRED (SECURITY_STATUS)0x80090345L +#define SEC_E_BAD_BINDINGS (SECURITY_STATUS)0x80090346L +#define SEC_E_MULTIPLE_ACCOUNTS (SECURITY_STATUS)0x80090347L +#define SEC_E_NO_KERB_KEY (SECURITY_STATUS)0x80090348L +#define SEC_E_CERT_WRONG_USAGE (SECURITY_STATUS)0x80090349L +#define SEC_E_DOWNGRADE_DETECTED (SECURITY_STATUS)0x80090350L +#define SEC_E_SMARTCARD_CERT_REVOKED (SECURITY_STATUS)0x80090351L +#define SEC_E_ISSUING_CA_UNTRUSTED (SECURITY_STATUS)0x80090352L +#define SEC_E_REVOCATION_OFFLINE_C (SECURITY_STATUS)0x80090353L +#define SEC_E_PKINIT_CLIENT_FAILURE (SECURITY_STATUS)0x80090354L +#define SEC_E_SMARTCARD_CERT_EXPIRED (SECURITY_STATUS)0x80090355L +#define SEC_E_NO_S4U_PROT_SUPPORT (SECURITY_STATUS)0x80090356L +#define SEC_E_CROSSREALM_DELEGATION_FAILURE (SECURITY_STATUS)0x80090357L +#define SEC_E_REVOCATION_OFFLINE_KDC (SECURITY_STATUS)0x80090358L +#define SEC_E_ISSUING_CA_UNTRUSTED_KDC (SECURITY_STATUS)0x80090359L +#define SEC_E_KDC_CERT_EXPIRED (SECURITY_STATUS)0x8009035AL +#define SEC_E_KDC_CERT_REVOKED (SECURITY_STATUS)0x8009035BL +#define SEC_E_INVALID_PARAMETER (SECURITY_STATUS)0x8009035DL +#define SEC_E_DELEGATION_POLICY (SECURITY_STATUS)0x8009035EL +#define SEC_E_POLICY_NLTM_ONLY (SECURITY_STATUS)0x8009035FL +#define SEC_E_NO_CONTEXT (SECURITY_STATUS)0x80090361L +#define SEC_E_PKU2U_CERT_FAILURE (SECURITY_STATUS)0x80090362L +#define SEC_E_MUTUAL_AUTH_FAILED (SECURITY_STATUS)0x80090363L + +#define SEC_I_CONTINUE_NEEDED (SECURITY_STATUS)0x00090312L +#define SEC_I_COMPLETE_NEEDED (SECURITY_STATUS)0x00090313L +#define SEC_I_COMPLETE_AND_CONTINUE (SECURITY_STATUS)0x00090314L +#define SEC_I_LOCAL_LOGON (SECURITY_STATUS)0x00090315L +#define SEC_I_CONTEXT_EXPIRED (SECURITY_STATUS)0x00090317L +#define SEC_I_INCOMPLETE_CREDENTIALS (SECURITY_STATUS)0x00090320L +#define SEC_I_RENEGOTIATE (SECURITY_STATUS)0x00090321L +#define SEC_I_NO_LSA_CONTEXT (SECURITY_STATUS)0x00090323L +#define SEC_I_SIGNATURE_NEEDED (SECURITY_STATUS)0x0009035CL +#define SEC_I_NO_RENEGOTIATION (SECURITY_STATUS)0x00090360L + +#endif /* _WINERROR_ */ + +/* ============== some definitions missing in mingw ========================*/ +#ifndef SEC_E_INVALID_PARAMETER +#define SEC_E_INVALID_PARAMETER (SECURITY_STATUS)0x8009035DL +#endif + +#ifndef SEC_E_DELEGATION_POLICY +#define SEC_E_DELEGATION_POLICY (SECURITY_STATUS)0x8009035EL +#endif + +#ifndef SEC_E_POLICY_NLTM_ONLY +#define SEC_E_POLICY_NLTM_ONLY (SECURITY_STATUS)0x8009035FL +#endif + +#ifndef SEC_E_NO_CONTEXT +#define SEC_E_NO_CONTEXT (SECURITY_STATUS)0x80090361L +#endif + +#ifndef SEC_E_PKU2U_CERT_FAILURE +#define SEC_E_PKU2U_CERT_FAILURE (SECURITY_STATUS)0x80090362L +#endif + +#ifndef SEC_E_MUTUAL_AUTH_FAILED +#define SEC_E_MUTUAL_AUTH_FAILED (SECURITY_STATUS)0x80090363L +#endif + +#ifndef SEC_I_SIGNATURE_NEEDED +#define SEC_I_SIGNATURE_NEEDED (SECURITY_STATUS)0x0009035CL +#endif + +#ifndef SEC_I_NO_RENEGOTIATION +#define SEC_I_NO_RENEGOTIATION (SECURITY_STATUS)0x00090360L +#endif + +/* ==================================================================================== */ + +#define SECURITY_NATIVE_DREP 0x00000010 +#define SECURITY_NETWORK_DREP 0x00000000 + +#define SECPKG_CRED_INBOUND 0x00000001 +#define SECPKG_CRED_OUTBOUND 0x00000002 +#define SECPKG_CRED_BOTH 0x00000003 +#define SECPKG_CRED_AUTOLOGON_RESTRICTED 0x00000010 +#define SECPKG_CRED_PROCESS_POLICY_ONLY 0x00000020 + +/* Security Context Attributes */ + +#define SECPKG_ATTR_SIZES 0 +#define SECPKG_ATTR_NAMES 1 +#define SECPKG_ATTR_LIFESPAN 2 +#define SECPKG_ATTR_DCE_INFO 3 +#define SECPKG_ATTR_STREAM_SIZES 4 +#define SECPKG_ATTR_KEY_INFO 5 +#define SECPKG_ATTR_AUTHORITY 6 +#define SECPKG_ATTR_PROTO_INFO 7 +#define SECPKG_ATTR_PASSWORD_EXPIRY 8 +#define SECPKG_ATTR_SESSION_KEY 9 +#define SECPKG_ATTR_PACKAGE_INFO 10 +#define SECPKG_ATTR_USER_FLAGS 11 +#define SECPKG_ATTR_NEGOTIATION_INFO 12 +#define SECPKG_ATTR_NATIVE_NAMES 13 +#define SECPKG_ATTR_FLAGS 14 +#define SECPKG_ATTR_USE_VALIDATED 15 +#define SECPKG_ATTR_CREDENTIAL_NAME 16 +#define SECPKG_ATTR_TARGET_INFORMATION 17 +#define SECPKG_ATTR_ACCESS_TOKEN 18 +#define SECPKG_ATTR_TARGET 19 +#define SECPKG_ATTR_AUTHENTICATION_ID 20 +#define SECPKG_ATTR_LOGOFF_TIME 21 +#define SECPKG_ATTR_NEGO_KEYS 22 +#define SECPKG_ATTR_PROMPTING_NEEDED 24 +#define SECPKG_ATTR_UNIQUE_BINDINGS 25 +#define SECPKG_ATTR_ENDPOINT_BINDINGS 26 +#define SECPKG_ATTR_CLIENT_SPECIFIED_TARGET 27 +#define SECPKG_ATTR_LAST_CLIENT_TOKEN_STATUS 30 +#define SECPKG_ATTR_NEGO_PKG_INFO 31 +#define SECPKG_ATTR_NEGO_STATUS 32 +#define SECPKG_ATTR_CONTEXT_DELETED 33 + +#if !defined(_WIN32) || defined(_UWP) + +typedef struct +{ + void* AccessToken; +} SecPkgContext_AccessToken; + +typedef struct +{ + UINT32 dwFlags; + UINT32 cbAppData; + BYTE* pbAppData; +} SecPkgContext_SessionAppData; + +typedef struct +{ + char* sAuthorityName; +} SecPkgContext_Authority; + +typedef struct +{ + char* sTargetName; +} SecPkgContext_ClientSpecifiedTarget; + +typedef UINT32 ALG_ID; + +typedef struct +{ + UINT32 dwProtocol; + ALG_ID aiCipher; + UINT32 dwCipherStrength; + ALG_ID aiHash; + UINT32 dwHashStrength; + ALG_ID aiExch; + UINT32 dwExchStrength; +} SecPkgContext_ConnectionInfo; + +typedef struct +{ + UINT32 AuthBufferLen; + BYTE* AuthBuffer; +} SecPkgContext_ClientCreds; + +typedef struct +{ + UINT32 AuthzSvc; + void* pPac; +} SecPkgContex_DceInfo; + +typedef struct +{ + UINT32 dwInitiatorAddrType; + UINT32 cbInitiatorLength; + UINT32 dwInitiatorOffset; + UINT32 dwAcceptorAddrType; + UINT32 cbAcceptorLength; + UINT32 dwAcceptorOffset; + UINT32 cbApplicationDataLength; + UINT32 dwApplicationDataOffset; +} SEC_CHANNEL_BINDINGS; + +typedef struct +{ + BYTE rgbKeys[128]; + BYTE rgbIVs[64]; +} SecPkgContext_EapKeyBlock; + +typedef struct +{ + UINT32 Flags; +} SecPkgContext_Flags; + +typedef struct +{ + char* sSignatureAlgorithmName; + char* sEncryptAlgorithmName; + UINT32 KeySize; + UINT32 SignatureAlgorithm; + UINT32 EncryptAlgorithm; +} SecPkgContext_KeyInfo; + +typedef struct +{ + TimeStamp tsStart; + TimeStamp tsExpiry; +} SecPkgContext_Lifespan; + +typedef struct +{ + char* sUserName; +} SecPkgContext_Names; + +typedef struct +{ + char* sClientName; + char* sServerName; +} SecPkgContext_NativeNames; + +typedef struct +{ + SecPkgInfo* PackageInfo; + UINT32 NegotiationState; +} SecPkgContext_NegotiationInfo; + +typedef struct +{ + SecPkgInfo* PackageInfo; +} SecPkgContext_PackageInfo; + +typedef struct +{ + TimeStamp tsPasswordExpires; +} SecPkgContext_PasswordExpiry; + +typedef struct +{ + UINT32 SessionKeyLength; + BYTE* SessionKey; +} SecPkgContext_SessionKey; + +typedef struct +{ + UINT32 dwFlags; + UINT32 cbSessionId; + BYTE rgbSessionId[32]; +} SecPkgContext_SessionInfo; + +typedef struct +{ + UINT32 cbMaxToken; + UINT32 cbMaxSignature; + UINT32 cbBlockSize; + UINT32 cbSecurityTrailer; +} SecPkgContext_Sizes; + +typedef struct +{ + UINT32 cbHeader; + UINT32 cbTrailer; + UINT32 cbMaximumMessage; + UINT32 cBuffers; + UINT32 cbBlockSize; +} SecPkgContext_StreamSizes; + +typedef struct +{ + void* AttributeInfo; +} SecPkgContext_SubjectAttributes; + +typedef struct +{ + UINT16 cSignatureAndHashAlgorithms; + UINT16* pSignatureAndHashAlgorithms; +} SecPkgContext_SupportedSignatures; + +typedef struct +{ + UINT32 MarshalledTargetInfoLength; + BYTE* MarshalledTargetInfo; +} SecPkgContext_TargetInformation; + +/* Security Credentials Attributes */ + +#define SECPKG_CRED_ATTR_NAMES 1 +#define SECPKG_CRED_ATTR_SSI_PROVIDER 2 +#define SECPKG_CRED_ATTR_CERT 4 +#define SECPKG_CRED_ATTR_PAC_BYPASS 5 + +typedef struct +{ + SEC_CHAR* sUserName; +} SecPkgCredentials_NamesA; +typedef SecPkgCredentials_NamesA* PSecPkgCredentials_NamesA; + +typedef struct +{ + SEC_WCHAR* sUserName; +} SecPkgCredentials_NamesW; +typedef SecPkgCredentials_NamesW* PSecPkgCredentials_NamesW; + +#ifdef UNICODE +#define SecPkgCredentials_Names SecPkgCredentials_NamesW +#define PSecPkgCredentials_Names PSecPkgCredentials_NamesW +#else +#define SecPkgCredentials_Names SecPkgCredentials_NamesA +#define PSecPkgCredentials_Names PSecPkgCredentials_NamesA +#endif + +typedef struct +{ + SEC_WCHAR* sProviderName; + unsigned long ProviderInfoLength; + char* ProviderInfo; +} SecPkgCredentials_SSIProviderW, *PSecPkgCredentials_SSIProviderW; + +typedef struct +{ + SEC_CHAR* sProviderName; + unsigned long ProviderInfoLength; + char* ProviderInfo; +} SecPkgCredentials_SSIProviderA, *PSecPkgCredentials_SSIProviderA; + +#ifdef UNICODE +#define SecPkgCredentials_SSIProvider SecPkgCredentials_SSIProviderW +#define PSecPkgCredentials_SSIProvider PSecPkgCredentials_SSIProviderW +#else +#define SecPkgCredentials_SSIProvider SecPkgCredentials_SSIProviderA +#define PSecPkgCredentials_SSIProvider PSecPkgCredentials_SSIProviderA +#endif + +typedef struct _SecPkgCredentials_Cert +{ + unsigned long EncodedCertSize; + unsigned char* EncodedCert; +} SecPkgCredentials_Cert, *PSecPkgCredentials_Cert; + +#endif /* !defined(_WIN32) || defined(_UWP) */ + +#if !defined(_WIN32) || defined(_UWP) || (defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 8)) + +#define SECPKG_CRED_ATTR_KDC_PROXY_SETTINGS 3 + +#define KDC_PROXY_SETTINGS_V1 1 +#define KDC_PROXY_SETTINGS_FLAGS_FORCEPROXY 0x1 + +typedef struct +{ + ULONG Version; + ULONG Flags; + USHORT ProxyServerOffset; + USHORT ProxyServerLength; + USHORT ClientTlsCredOffset; + USHORT ClientTlsCredLength; +} SecPkgCredentials_KdcProxySettingsW, *PSecPkgCredentials_KdcProxySettingsW; + +typedef struct +{ + ULONG Version; + ULONG Flags; + USHORT ProxyServerOffset; + USHORT ProxyServerLength; + USHORT ClientTlsCredOffset; + USHORT ClientTlsCredLength; +} SecPkgCredentials_KdcProxySettingsA, *PSecPkgCredentials_KdcProxySettingsA; + +#ifdef UNICODE +#define SecPkgCredentials_KdcProxySettings SecPkgCredentials_KdcProxySettingsW +#define PSecPkgCredentials_KdcProxySettings PSecPkgCredentials_KdcProxySettingsW +#else +#define SecPkgCredentials_KdcProxySettings SecPkgCredentials_KdcProxySettingsA +#define PSecPkgCredentials_KdcProxySettings SecPkgCredentials_KdcProxySettingsA +#endif + +typedef struct +{ + UINT32 BindingsLength; + SEC_CHANNEL_BINDINGS* Bindings; +} SecPkgContext_Bindings; +#endif + +/* InitializeSecurityContext Flags */ + +#define ISC_REQ_DELEGATE 0x00000001 +#define ISC_REQ_MUTUAL_AUTH 0x00000002 +#define ISC_REQ_REPLAY_DETECT 0x00000004 +#define ISC_REQ_SEQUENCE_DETECT 0x00000008 +#define ISC_REQ_CONFIDENTIALITY 0x00000010 +#define ISC_REQ_USE_SESSION_KEY 0x00000020 +#define ISC_REQ_PROMPT_FOR_CREDS 0x00000040 +#define ISC_REQ_USE_SUPPLIED_CREDS 0x00000080 +#define ISC_REQ_ALLOCATE_MEMORY 0x00000100 +#define ISC_REQ_USE_DCE_STYLE 0x00000200 +#define ISC_REQ_DATAGRAM 0x00000400 +#define ISC_REQ_CONNECTION 0x00000800 +#define ISC_REQ_CALL_LEVEL 0x00001000 +#define ISC_REQ_FRAGMENT_SUPPLIED 0x00002000 +#define ISC_REQ_EXTENDED_ERROR 0x00004000 +#define ISC_REQ_STREAM 0x00008000 +#define ISC_REQ_INTEGRITY 0x00010000 +#define ISC_REQ_IDENTIFY 0x00020000 +#define ISC_REQ_NULL_SESSION 0x00040000 +#define ISC_REQ_MANUAL_CRED_VALIDATION 0x00080000 +#define ISC_REQ_RESERVED1 0x00100000 +#define ISC_REQ_FRAGMENT_TO_FIT 0x00200000 +#define ISC_REQ_FORWARD_CREDENTIALS 0x00400000 +#define ISC_REQ_NO_INTEGRITY 0x00800000 +#define ISC_REQ_USE_HTTP_STYLE 0x01000000 + +#define ISC_RET_DELEGATE 0x00000001 +#define ISC_RET_MUTUAL_AUTH 0x00000002 +#define ISC_RET_REPLAY_DETECT 0x00000004 +#define ISC_RET_SEQUENCE_DETECT 0x00000008 +#define ISC_RET_CONFIDENTIALITY 0x00000010 +#define ISC_RET_USE_SESSION_KEY 0x00000020 +#define ISC_RET_USED_COLLECTED_CREDS 0x00000040 +#define ISC_RET_USED_SUPPLIED_CREDS 0x00000080 +#define ISC_RET_ALLOCATED_MEMORY 0x00000100 +#define ISC_RET_USED_DCE_STYLE 0x00000200 +#define ISC_RET_DATAGRAM 0x00000400 +#define ISC_RET_CONNECTION 0x00000800 +#define ISC_RET_INTERMEDIATE_RETURN 0x00001000 +#define ISC_RET_CALL_LEVEL 0x00002000 +#define ISC_RET_EXTENDED_ERROR 0x00004000 +#define ISC_RET_STREAM 0x00008000 +#define ISC_RET_INTEGRITY 0x00010000 +#define ISC_RET_IDENTIFY 0x00020000 +#define ISC_RET_NULL_SESSION 0x00040000 +#define ISC_RET_MANUAL_CRED_VALIDATION 0x00080000 +#define ISC_RET_RESERVED1 0x00100000 +#define ISC_RET_FRAGMENT_ONLY 0x00200000 +#define ISC_RET_FORWARD_CREDENTIALS 0x00400000 +#define ISC_RET_USED_HTTP_STYLE 0x01000000 + +/* AcceptSecurityContext Flags */ + +#define ASC_REQ_DELEGATE 0x00000001 +#define ASC_REQ_MUTUAL_AUTH 0x00000002 +#define ASC_REQ_REPLAY_DETECT 0x00000004 +#define ASC_REQ_SEQUENCE_DETECT 0x00000008 +#define ASC_REQ_CONFIDENTIALITY 0x00000010 +#define ASC_REQ_USE_SESSION_KEY 0x00000020 +#define ASC_REQ_ALLOCATE_MEMORY 0x00000100 +#define ASC_REQ_USE_DCE_STYLE 0x00000200 +#define ASC_REQ_DATAGRAM 0x00000400 +#define ASC_REQ_CONNECTION 0x00000800 +#define ASC_REQ_CALL_LEVEL 0x00001000 +#define ASC_REQ_EXTENDED_ERROR 0x00008000 +#define ASC_REQ_STREAM 0x00010000 +#define ASC_REQ_INTEGRITY 0x00020000 +#define ASC_REQ_LICENSING 0x00040000 +#define ASC_REQ_IDENTIFY 0x00080000 +#define ASC_REQ_ALLOW_NULL_SESSION 0x00100000 +#define ASC_REQ_ALLOW_NON_USER_LOGONS 0x00200000 +#define ASC_REQ_ALLOW_CONTEXT_REPLAY 0x00400000 +#define ASC_REQ_FRAGMENT_TO_FIT 0x00800000 +#define ASC_REQ_FRAGMENT_SUPPLIED 0x00002000 +#define ASC_REQ_NO_TOKEN 0x01000000 +#define ASC_REQ_PROXY_BINDINGS 0x04000000 +#define ASC_REQ_ALLOW_MISSING_BINDINGS 0x10000000 + +#define ASC_RET_DELEGATE 0x00000001 +#define ASC_RET_MUTUAL_AUTH 0x00000002 +#define ASC_RET_REPLAY_DETECT 0x00000004 +#define ASC_RET_SEQUENCE_DETECT 0x00000008 +#define ASC_RET_CONFIDENTIALITY 0x00000010 +#define ASC_RET_USE_SESSION_KEY 0x00000020 +#define ASC_RET_ALLOCATED_MEMORY 0x00000100 +#define ASC_RET_USED_DCE_STYLE 0x00000200 +#define ASC_RET_DATAGRAM 0x00000400 +#define ASC_RET_CONNECTION 0x00000800 +#define ASC_RET_CALL_LEVEL 0x00002000 +#define ASC_RET_THIRD_LEG_FAILED 0x00004000 +#define ASC_RET_EXTENDED_ERROR 0x00008000 +#define ASC_RET_STREAM 0x00010000 +#define ASC_RET_INTEGRITY 0x00020000 +#define ASC_RET_LICENSING 0x00040000 +#define ASC_RET_IDENTIFY 0x00080000 +#define ASC_RET_NULL_SESSION 0x00100000 +#define ASC_RET_ALLOW_NON_USER_LOGONS 0x00200000 +#define ASC_RET_FRAGMENT_ONLY 0x00800000 +#define ASC_RET_NO_TOKEN 0x01000000 +#define ASC_RET_NO_PROXY_BINDINGS 0x04000000 +#define ASC_RET_MISSING_BINDINGS 0x10000000 + +#define SEC_WINNT_AUTH_IDENTITY_ANSI 0x1 +#define SEC_WINNT_AUTH_IDENTITY_UNICODE 0x2 +#define SEC_WINNT_AUTH_IDENTITY_MARSHALLED 0x4 +#define SEC_WINNT_AUTH_IDENTITY_ONLY 0x8 +#define SEC_WINNT_AUTH_IDENTITY_EXTENDED 0x100 + +#if !defined(_WIN32) || defined(_UWP) || defined(__MINGW32__) + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifndef _AUTH_IDENTITY_DEFINED +#define _AUTH_IDENTITY_DEFINED + +typedef struct +{ + UINT16* User; + ULONG UserLength; + UINT16* Domain; + ULONG DomainLength; + UINT16* Password; + ULONG PasswordLength; + UINT32 Flags; +} SEC_WINNT_AUTH_IDENTITY_W, *PSEC_WINNT_AUTH_IDENTITY_W; + +typedef struct +{ + BYTE* User; + ULONG UserLength; + BYTE* Domain; + ULONG DomainLength; + BYTE* Password; + ULONG PasswordLength; + UINT32 Flags; +} SEC_WINNT_AUTH_IDENTITY_A, *PSEC_WINNT_AUTH_IDENTITY_A; + +// Always define SEC_WINNT_AUTH_IDENTITY to SEC_WINNT_AUTH_IDENTITY_W + +#ifdef UNICODE +#define SEC_WINNT_AUTH_IDENTITY SEC_WINNT_AUTH_IDENTITY_W +#define PSEC_WINNT_AUTH_IDENTITY PSEC_WINNT_AUTH_IDENTITY_W +#else +#define SEC_WINNT_AUTH_IDENTITY SEC_WINNT_AUTH_IDENTITY_W +#define PSEC_WINNT_AUTH_IDENTITY PSEC_WINNT_AUTH_IDENTITY_W +#endif + +#endif /* _AUTH_IDENTITY_DEFINED */ + +#ifndef SEC_WINNT_AUTH_IDENTITY_VERSION +#define SEC_WINNT_AUTH_IDENTITY_VERSION 0x200 + +typedef struct +{ + UINT32 Version; + UINT32 Length; + UINT16* User; + UINT32 UserLength; + UINT16* Domain; + UINT32 DomainLength; + UINT16* Password; + UINT32 PasswordLength; + UINT32 Flags; + UINT16* PackageList; + UINT32 PackageListLength; +} SEC_WINNT_AUTH_IDENTITY_EXW, *PSEC_WINNT_AUTH_IDENTITY_EXW; + +typedef struct +{ + UINT32 Version; + UINT32 Length; + BYTE* User; + UINT32 UserLength; + BYTE* Domain; + UINT32 DomainLength; + BYTE* Password; + UINT32 PasswordLength; + UINT32 Flags; + BYTE* PackageList; + UINT32 PackageListLength; +} SEC_WINNT_AUTH_IDENTITY_EXA, *PSEC_WINNT_AUTH_IDENTITY_EXA; + +#ifdef UNICODE +#define SEC_WINNT_AUTH_IDENTITY_EX SEC_WINNT_AUTH_IDENTITY_EXW +#define PSEC_WINNT_AUTH_IDENTITY_EX PSEC_WINNT_AUTH_IDENTITY_EXW +#else +#define SEC_WINNT_AUTH_IDENTITY_EX SEC_WINNT_AUTH_IDENTITY_EXA +#define PSEC_WINNT_AUTH_IDENTITY_EX PSEC_WINNT_AUTH_IDENTITY_EXA +#endif + +#endif /* SEC_WINNT_AUTH_IDENTITY_VERSION */ + +#ifndef SEC_WINNT_AUTH_IDENTITY_VERSION_2 +#define SEC_WINNT_AUTH_IDENTITY_VERSION_2 0x201 + +typedef struct _SEC_WINNT_AUTH_IDENTITY_EX2 +{ + UINT32 Version; + UINT16 cbHeaderLength; + UINT32 cbStructureLength; + UINT32 UserOffset; + UINT16 UserLength; + UINT32 DomainOffset; + UINT16 DomainLength; + UINT32 PackedCredentialsOffset; + UINT16 PackedCredentialsLength; + UINT32 Flags; + UINT32 PackageListOffset; + UINT16 PackageListLength; +} SEC_WINNT_AUTH_IDENTITY_EX2, *PSEC_WINNT_AUTH_IDENTITY_EX2; + +#endif /* SEC_WINNT_AUTH_IDENTITY_VERSION_2 */ + +#ifndef _AUTH_IDENTITY_INFO_DEFINED +#define _AUTH_IDENTITY_INFO_DEFINED + +// https://docs.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_info + +typedef union _SEC_WINNT_AUTH_IDENTITY_INFO +{ + SEC_WINNT_AUTH_IDENTITY_EXW AuthIdExw; + SEC_WINNT_AUTH_IDENTITY_EXA AuthIdExa; + SEC_WINNT_AUTH_IDENTITY_A AuthId_a; + SEC_WINNT_AUTH_IDENTITY_W AuthId_w; + SEC_WINNT_AUTH_IDENTITY_EX2 AuthIdEx2; +} SEC_WINNT_AUTH_IDENTITY_INFO, *PSEC_WINNT_AUTH_IDENTITY_INFO; + +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_PROCESS_ENCRYPTED 0x10 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SYSTEM_PROTECTED 0x20 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_USER_PROTECTED 0x40 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SYSTEM_ENCRYPTED 0x80 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_RESERVED 0x10000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_NULL_USER 0x20000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_NULL_DOMAIN 0x40000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_ID_PROVIDER 0x80000 + +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_USE_MASK 0xFF000000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_CREDPROV_DO_NOT_SAVE 0x80000000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_SAVE_CRED_CHECKED 0x40000000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_NO_CHECKBOX 0x20000000 +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_CREDPROV_DO_NOT_LOAD 0x10000000 + +#define SEC_WINNT_AUTH_IDENTITY_FLAGS_VALID_SSPIPFC_FLAGS \ + (SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_CREDPROV_DO_NOT_SAVE | \ + SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_SAVE_CRED_CHECKED | \ + SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_NO_CHECKBOX | \ + SEC_WINNT_AUTH_IDENTITY_FLAGS_SSPIPFC_CREDPROV_DO_NOT_LOAD) + +#endif /* _AUTH_IDENTITY_INFO_DEFINED */ + +WINPR_PRAGMA_DIAG_POP + +#if !defined(__MINGW32__) +typedef struct +{ + ULONG_PTR dwLower; + ULONG_PTR dwUpper; +} SecHandle; +typedef SecHandle* PSecHandle; + +typedef SecHandle CredHandle; +typedef CredHandle* PCredHandle; +typedef SecHandle CtxtHandle; +typedef CtxtHandle* PCtxtHandle; + +#define SecInvalidateHandle(x) \ + ((PSecHandle)(x))->dwLower = ((PSecHandle)(x))->dwUpper = ((ULONG_PTR)((INT_PTR)-1)) + +#define SecIsValidHandle(x) \ + ((((PSecHandle)(x))->dwLower != ((ULONG_PTR)((INT_PTR)-1))) && \ + (((PSecHandle)(x))->dwUpper != ((ULONG_PTR)((INT_PTR)-1)))) + +typedef struct +{ + ULONG cbBuffer; + ULONG BufferType; + void* pvBuffer; +} SecBuffer; +typedef SecBuffer* PSecBuffer; + +typedef struct +{ + ULONG ulVersion; + ULONG cBuffers; + PSecBuffer pBuffers; +} SecBufferDesc; +typedef SecBufferDesc* PSecBufferDesc; + +#endif /* __MINGW32__ */ + +#endif /* !defined(_WIN32) || defined(_UWP) || defined(__MINGW32__) */ + +typedef SECURITY_STATUS (*psSspiNtlmHashCallback)(void* client, + const SEC_WINNT_AUTH_IDENTITY* authIdentity, + const SecBuffer* ntproofvalue, + const BYTE* randkey, const BYTE* mic, + const SecBuffer* micvalue, BYTE* ntlmhash); + +typedef struct +{ + char* samFile; + psSspiNtlmHashCallback hashCallback; + void* hashCallbackArg; +} SEC_WINPR_NTLM_SETTINGS; + +typedef struct +{ + char* kdcUrl; + char* keytab; + char* cache; + char* armorCache; + char* pkinitX509Anchors; + char* pkinitX509Identity; + BOOL withPac; + INT32 startTime; + INT32 renewLifeTime; + INT32 lifeTime; + BYTE certSha1[20]; +} SEC_WINPR_KERBEROS_SETTINGS; + +typedef struct +{ + SEC_WINNT_AUTH_IDENTITY_EXW identity; + SEC_WINPR_NTLM_SETTINGS* ntlmSettings; + SEC_WINPR_KERBEROS_SETTINGS* kerberosSettings; +} SEC_WINNT_AUTH_IDENTITY_WINPR; + +#define SECBUFFER_VERSION 0 + +/* Buffer Types */ +#define SECBUFFER_EMPTY 0 +#define SECBUFFER_DATA 1 +#define SECBUFFER_TOKEN 2 +#define SECBUFFER_PKG_PARAMS 3 +#define SECBUFFER_MISSING 4 +#define SECBUFFER_EXTRA 5 +#define SECBUFFER_STREAM_TRAILER 6 +#define SECBUFFER_STREAM_HEADER 7 +#define SECBUFFER_NEGOTIATION_INFO 8 +#define SECBUFFER_PADDING 9 +#define SECBUFFER_STREAM 10 +#define SECBUFFER_MECHLIST 11 +#define SECBUFFER_MECHLIST_SIGNATURE 12 +#define SECBUFFER_TARGET 13 +#define SECBUFFER_CHANNEL_BINDINGS 14 +#define SECBUFFER_CHANGE_PASS_RESPONSE 15 +#define SECBUFFER_TARGET_HOST 16 +#define SECBUFFER_ALERT 17 + +/* Security Buffer Flags */ +#define SECBUFFER_ATTRMASK 0xF0000000 +#define SECBUFFER_READONLY 0x80000000 +#define SECBUFFER_READONLY_WITH_CHECKSUM 0x10000000 +#define SECBUFFER_RESERVED 0x60000000 + +#if !defined(_WIN32) || defined(_UWP) + +typedef void(SEC_ENTRY* SEC_GET_KEY_FN)(void* Arg, void* Principal, UINT32 KeyVer, void** Key, + SECURITY_STATUS* pStatus); + +typedef SECURITY_STATUS(SEC_ENTRY* ENUMERATE_SECURITY_PACKAGES_FN_A)(ULONG* pcPackages, + PSecPkgInfoA* ppPackageInfo); +typedef SECURITY_STATUS(SEC_ENTRY* ENUMERATE_SECURITY_PACKAGES_FN_W)(ULONG* pcPackages, + PSecPkgInfoW* ppPackageInfo); + +#ifdef UNICODE +#define EnumerateSecurityPackages EnumerateSecurityPackagesW +#define ENUMERATE_SECURITY_PACKAGES_FN ENUMERATE_SECURITY_PACKAGES_FN_W +#else +#define EnumerateSecurityPackages EnumerateSecurityPackagesA +#define ENUMERATE_SECURITY_PACKAGES_FN ENUMERATE_SECURITY_PACKAGES_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_CREDENTIALS_ATTRIBUTES_FN_A)(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer); +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_CREDENTIALS_ATTRIBUTES_FN_W)(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer); + +#ifdef UNICODE +#define QueryCredentialsAttributes QueryCredentialsAttributesW +#define QUERY_CREDENTIALS_ATTRIBUTES_FN QUERY_CREDENTIALS_ATTRIBUTES_FN_W +#else +#define QueryCredentialsAttributes QueryCredentialsAttributesA +#define QUERY_CREDENTIALS_ATTRIBUTES_FN QUERY_CREDENTIALS_ATTRIBUTES_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* ACQUIRE_CREDENTIALS_HANDLE_FN_A)( + LPSTR pszPrincipal, LPSTR pszPackage, ULONG fCredentialUse, void* pvLogonID, void* pAuthData, + SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry); +typedef SECURITY_STATUS(SEC_ENTRY* ACQUIRE_CREDENTIALS_HANDLE_FN_W)( + LPWSTR pszPrincipal, LPWSTR pszPackage, ULONG fCredentialUse, void* pvLogonID, void* pAuthData, + SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry); + +#ifdef UNICODE +#define AcquireCredentialsHandle AcquireCredentialsHandleW +#define ACQUIRE_CREDENTIALS_HANDLE_FN ACQUIRE_CREDENTIALS_HANDLE_FN_W +#else +#define AcquireCredentialsHandle AcquireCredentialsHandleA +#define ACQUIRE_CREDENTIALS_HANDLE_FN ACQUIRE_CREDENTIALS_HANDLE_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* FREE_CREDENTIALS_HANDLE_FN)(PCredHandle phCredential); + +typedef SECURITY_STATUS(SEC_ENTRY* INITIALIZE_SECURITY_CONTEXT_FN_A)( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry); +typedef SECURITY_STATUS(SEC_ENTRY* INITIALIZE_SECURITY_CONTEXT_FN_W)( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry); + +#ifdef UNICODE +#define InitializeSecurityContext InitializeSecurityContextW +#define INITIALIZE_SECURITY_CONTEXT_FN INITIALIZE_SECURITY_CONTEXT_FN_W +#else +#define InitializeSecurityContext InitializeSecurityContextA +#define INITIALIZE_SECURITY_CONTEXT_FN INITIALIZE_SECURITY_CONTEXT_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* ACCEPT_SECURITY_CONTEXT_FN)( + PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq, + ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, + PTimeStamp ptsTimeStamp); + +typedef SECURITY_STATUS(SEC_ENTRY* COMPLETE_AUTH_TOKEN_FN)(PCtxtHandle phContext, + PSecBufferDesc pToken); + +typedef SECURITY_STATUS(SEC_ENTRY* DELETE_SECURITY_CONTEXT_FN)(PCtxtHandle phContext); + +typedef SECURITY_STATUS(SEC_ENTRY* APPLY_CONTROL_TOKEN_FN)(PCtxtHandle phContext, + PSecBufferDesc pInput); + +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_CONTEXT_ATTRIBUTES_FN_A)(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer); +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_CONTEXT_ATTRIBUTES_FN_W)(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer); + +#ifdef UNICODE +#define QueryContextAttributes QueryContextAttributesW +#define QUERY_CONTEXT_ATTRIBUTES_FN QUERY_CONTEXT_ATTRIBUTES_FN_W +#else +#define QueryContextAttributes QueryContextAttributesA +#define QUERY_CONTEXT_ATTRIBUTES_FN QUERY_CONTEXT_ATTRIBUTES_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* IMPERSONATE_SECURITY_CONTEXT_FN)(PCtxtHandle phContext); + +typedef SECURITY_STATUS(SEC_ENTRY* REVERT_SECURITY_CONTEXT_FN)(PCtxtHandle phContext); + +typedef SECURITY_STATUS(SEC_ENTRY* MAKE_SIGNATURE_FN)(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo); + +typedef SECURITY_STATUS(SEC_ENTRY* VERIFY_SIGNATURE_FN)(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP); + +typedef SECURITY_STATUS(SEC_ENTRY* FREE_CONTEXT_BUFFER_FN)(void* pvContextBuffer); + +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_SECURITY_PACKAGE_INFO_FN_A)(SEC_CHAR* pszPackageName, + PSecPkgInfoA* ppPackageInfo); +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_SECURITY_PACKAGE_INFO_FN_W)(SEC_WCHAR* pszPackageName, + PSecPkgInfoW* ppPackageInfo); + +#ifdef UNICODE +#define QuerySecurityPackageInfo QuerySecurityPackageInfoW +#define QUERY_SECURITY_PACKAGE_INFO_FN QUERY_SECURITY_PACKAGE_INFO_FN_W +#else +#define QuerySecurityPackageInfo QuerySecurityPackageInfoA +#define QUERY_SECURITY_PACKAGE_INFO_FN QUERY_SECURITY_PACKAGE_INFO_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* EXPORT_SECURITY_CONTEXT_FN)(PCtxtHandle phContext, ULONG fFlags, + PSecBuffer pPackedContext, + HANDLE* pToken); + +typedef SECURITY_STATUS(SEC_ENTRY* IMPORT_SECURITY_CONTEXT_FN_A)(SEC_CHAR* pszPackage, + PSecBuffer pPackedContext, + HANDLE pToken, + PCtxtHandle phContext); +typedef SECURITY_STATUS(SEC_ENTRY* IMPORT_SECURITY_CONTEXT_FN_W)(SEC_WCHAR* pszPackage, + PSecBuffer pPackedContext, + HANDLE pToken, + PCtxtHandle phContext); + +#ifdef UNICODE +#define ImportSecurityContext ImportSecurityContextW +#define IMPORT_SECURITY_CONTEXT_FN IMPORT_SECURITY_CONTEXT_FN_W +#else +#define ImportSecurityContext ImportSecurityContextA +#define IMPORT_SECURITY_CONTEXT_FN IMPORT_SECURITY_CONTEXT_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* ADD_CREDENTIALS_FN_A)( + PCredHandle hCredentials, SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, UINT32 fCredentialUse, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PTimeStamp ptsExpiry); +typedef SECURITY_STATUS(SEC_ENTRY* ADD_CREDENTIALS_FN_W)( + PCredHandle hCredentials, SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, UINT32 fCredentialUse, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PTimeStamp ptsExpiry); + +#ifdef UNICODE +#define AddCredentials AddCredentialsW +#define ADD_CREDENTIALS_FN ADD_CREDENTIALS_FN_W +#else +#define AddCredentials AddCredentialsA +#define ADD_CREDENTIALS_FN ADD_CREDENTIALS_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* QUERY_SECURITY_CONTEXT_TOKEN_FN)(PCtxtHandle phContext, + HANDLE* phToken); + +typedef SECURITY_STATUS(SEC_ENTRY* ENCRYPT_MESSAGE_FN)(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo); + +typedef SECURITY_STATUS(SEC_ENTRY* DECRYPT_MESSAGE_FN)(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP); + +typedef SECURITY_STATUS(SEC_ENTRY* SET_CONTEXT_ATTRIBUTES_FN_A)(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer); +typedef SECURITY_STATUS(SEC_ENTRY* SET_CONTEXT_ATTRIBUTES_FN_W)(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer); + +#ifdef UNICODE +#define SetContextAttributes SetContextAttributesW +#define SET_CONTEXT_ATTRIBUTES_FN SET_CONTEXT_ATTRIBUTES_FN_W +#else +#define SetContextAttributes SetContextAttributesA +#define SET_CONTEXT_ATTRIBUTES_FN SET_CONTEXT_ATTRIBUTES_FN_A +#endif + +typedef SECURITY_STATUS(SEC_ENTRY* SET_CREDENTIALS_ATTRIBUTES_FN_A)(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer); + +typedef SECURITY_STATUS(SEC_ENTRY* SET_CREDENTIALS_ATTRIBUTES_FN_W)(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer); + +#ifdef UNICODE +#define SetCredentialsAttributes SetCredentialsAttributesW +#define SET_CREDENTIALS_ATTRIBUTES_FN SET_CREDENTIALS_ATTRIBUTES_FN_W +#else +#define SetCredentialsAttributes SetCredentialsAttributesA +#define SET_CREDENTIALS_ATTRIBUTES_FN SET_CREDENTIALS_ATTRIBUTES_FN_A +#endif + +#define SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION \ + 1 /* Interface has all routines through DecryptMessage */ +#define SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_2 \ + 2 /* Interface has all routines through SetContextAttributes */ +#define SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_3 \ + 3 /* Interface has all routines through SetCredentialsAttributes */ +#define SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_4 \ + 4 /* Interface has all routines through ChangeAccountPassword */ + +typedef struct +{ + UINT32 dwVersion; + ENUMERATE_SECURITY_PACKAGES_FN_A EnumerateSecurityPackagesA; + QUERY_CREDENTIALS_ATTRIBUTES_FN_A QueryCredentialsAttributesA; + ACQUIRE_CREDENTIALS_HANDLE_FN_A AcquireCredentialsHandleA; + FREE_CREDENTIALS_HANDLE_FN FreeCredentialsHandle; + void* Reserved2; + INITIALIZE_SECURITY_CONTEXT_FN_A InitializeSecurityContextA; + ACCEPT_SECURITY_CONTEXT_FN AcceptSecurityContext; + COMPLETE_AUTH_TOKEN_FN CompleteAuthToken; + DELETE_SECURITY_CONTEXT_FN DeleteSecurityContext; + APPLY_CONTROL_TOKEN_FN ApplyControlToken; + QUERY_CONTEXT_ATTRIBUTES_FN_A QueryContextAttributesA; + IMPERSONATE_SECURITY_CONTEXT_FN ImpersonateSecurityContext; + REVERT_SECURITY_CONTEXT_FN RevertSecurityContext; + MAKE_SIGNATURE_FN MakeSignature; + VERIFY_SIGNATURE_FN VerifySignature; + FREE_CONTEXT_BUFFER_FN FreeContextBuffer; + QUERY_SECURITY_PACKAGE_INFO_FN_A QuerySecurityPackageInfoA; + void* Reserved3; + void* Reserved4; + EXPORT_SECURITY_CONTEXT_FN ExportSecurityContext; + IMPORT_SECURITY_CONTEXT_FN_A ImportSecurityContextA; + ADD_CREDENTIALS_FN_A AddCredentialsA; + void* Reserved8; + QUERY_SECURITY_CONTEXT_TOKEN_FN QuerySecurityContextToken; + ENCRYPT_MESSAGE_FN EncryptMessage; + DECRYPT_MESSAGE_FN DecryptMessage; + SET_CONTEXT_ATTRIBUTES_FN_A SetContextAttributesA; + SET_CREDENTIALS_ATTRIBUTES_FN_A SetCredentialsAttributesA; +} SecurityFunctionTableA; +typedef SecurityFunctionTableA* PSecurityFunctionTableA; + +typedef struct +{ + UINT32 dwVersion; + ENUMERATE_SECURITY_PACKAGES_FN_W EnumerateSecurityPackagesW; + QUERY_CREDENTIALS_ATTRIBUTES_FN_W QueryCredentialsAttributesW; + ACQUIRE_CREDENTIALS_HANDLE_FN_W AcquireCredentialsHandleW; + FREE_CREDENTIALS_HANDLE_FN FreeCredentialsHandle; + void* Reserved2; + INITIALIZE_SECURITY_CONTEXT_FN_W InitializeSecurityContextW; + ACCEPT_SECURITY_CONTEXT_FN AcceptSecurityContext; + COMPLETE_AUTH_TOKEN_FN CompleteAuthToken; + DELETE_SECURITY_CONTEXT_FN DeleteSecurityContext; + APPLY_CONTROL_TOKEN_FN ApplyControlToken; + QUERY_CONTEXT_ATTRIBUTES_FN_W QueryContextAttributesW; + IMPERSONATE_SECURITY_CONTEXT_FN ImpersonateSecurityContext; + REVERT_SECURITY_CONTEXT_FN RevertSecurityContext; + MAKE_SIGNATURE_FN MakeSignature; + VERIFY_SIGNATURE_FN VerifySignature; + FREE_CONTEXT_BUFFER_FN FreeContextBuffer; + QUERY_SECURITY_PACKAGE_INFO_FN_W QuerySecurityPackageInfoW; + void* Reserved3; + void* Reserved4; + EXPORT_SECURITY_CONTEXT_FN ExportSecurityContext; + IMPORT_SECURITY_CONTEXT_FN_W ImportSecurityContextW; + ADD_CREDENTIALS_FN_W AddCredentialsW; + void* Reserved8; + QUERY_SECURITY_CONTEXT_TOKEN_FN QuerySecurityContextToken; + ENCRYPT_MESSAGE_FN EncryptMessage; + DECRYPT_MESSAGE_FN DecryptMessage; + SET_CONTEXT_ATTRIBUTES_FN_W SetContextAttributesW; + SET_CREDENTIALS_ATTRIBUTES_FN_W SetCredentialsAttributesW; +} SecurityFunctionTableW; +typedef SecurityFunctionTableW* PSecurityFunctionTableW; + +typedef PSecurityFunctionTableA(SEC_ENTRY* INIT_SECURITY_INTERFACE_A)(void); +typedef PSecurityFunctionTableW(SEC_ENTRY* INIT_SECURITY_INTERFACE_W)(void); + +#ifdef UNICODE +#define InitSecurityInterface InitSecurityInterfaceW +#define SecurityFunctionTable SecurityFunctionTableW +#define PSecurityFunctionTable PSecurityFunctionTableW +#define INIT_SECURITY_INTERFACE INIT_SECURITY_INTERFACE_W +#else +#define InitSecurityInterface InitSecurityInterfaceA +#define SecurityFunctionTable SecurityFunctionTableA +#define PSecurityFunctionTable PSecurityFunctionTableA +#define INIT_SECURITY_INTERFACE INIT_SECURITY_INTERFACE_A +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef SSPI_DLL + + /* Package Management */ + + WINPR_API SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesA(ULONG* pcPackages, + PSecPkgInfoA* ppPackageInfo); + WINPR_API SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesW(ULONG* pcPackages, + PSecPkgInfoW* ppPackageInfo); + + WINPR_API PSecurityFunctionTableA SEC_ENTRY InitSecurityInterfaceA(void); + WINPR_API PSecurityFunctionTableW SEC_ENTRY InitSecurityInterfaceW(void); + + WINPR_API SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoA(SEC_CHAR* pszPackageName, + PSecPkgInfoA* ppPackageInfo); + WINPR_API SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoW(SEC_WCHAR* pszPackageName, + PSecPkgInfoW* ppPackageInfo); + + /* Credential Management */ + + WINPR_API SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry); + WINPR_API SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry); + + WINPR_API SECURITY_STATUS SEC_ENTRY ExportSecurityContext(PCtxtHandle phContext, ULONG fFlags, + PSecBuffer pPackedContext, + HANDLE* pToken); + WINPR_API SECURITY_STATUS SEC_ENTRY FreeCredentialsHandle(PCredHandle phCredential); + + WINPR_API SECURITY_STATUS SEC_ENTRY ImportSecurityContextA(SEC_CHAR* pszPackage, + PSecBuffer pPackedContext, + HANDLE pToken, + PCtxtHandle phContext); + WINPR_API SECURITY_STATUS SEC_ENTRY ImportSecurityContextW(SEC_WCHAR* pszPackage, + PSecBuffer pPackedContext, + HANDLE pToken, + PCtxtHandle phContext); + + WINPR_API SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer); + WINPR_API SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer); + + /* Context Management */ + + WINPR_API SECURITY_STATUS SEC_ENTRY + AcceptSecurityContext(PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, + ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp); + + WINPR_API SECURITY_STATUS SEC_ENTRY ApplyControlToken(PCtxtHandle phContext, + PSecBufferDesc pInput); + WINPR_API SECURITY_STATUS SEC_ENTRY CompleteAuthToken(PCtxtHandle phContext, + PSecBufferDesc pToken); + WINPR_API SECURITY_STATUS SEC_ENTRY DeleteSecurityContext(PCtxtHandle phContext); + WINPR_API SECURITY_STATUS SEC_ENTRY FreeContextBuffer(void* pvContextBuffer); + WINPR_API SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext(PCtxtHandle phContext); + + WINPR_API SECURITY_STATUS SEC_ENTRY InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, + PTimeStamp ptsExpiry); + WINPR_API SECURITY_STATUS SEC_ENTRY InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, + ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, + ULONG Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, + PTimeStamp ptsExpiry); + + WINPR_API SECURITY_STATUS SEC_ENTRY QueryContextAttributes(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer); + WINPR_API SECURITY_STATUS SEC_ENTRY QuerySecurityContextToken(PCtxtHandle phContext, + HANDLE* phToken); + WINPR_API SECURITY_STATUS SEC_ENTRY SetContextAttributes(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer); + WINPR_API SECURITY_STATUS SEC_ENTRY RevertSecurityContext(PCtxtHandle phContext); + + /* Message Support */ + + WINPR_API SECURITY_STATUS SEC_ENTRY DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP); + WINPR_API SECURITY_STATUS SEC_ENTRY EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo); + WINPR_API SECURITY_STATUS SEC_ENTRY MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo); + WINPR_API SECURITY_STATUS SEC_ENTRY VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP); + +#endif /* SSPI_DLL */ + +#ifdef __cplusplus +} +#endif + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Custom API */ + +/* Extended SECPKG_ATTR IDs begin at 1000 */ +#define SECPKG_ATTR_AUTH_IDENTITY 1001 +#define SECPKG_ATTR_AUTH_PASSWORD 1002 +#define SECPKG_ATTR_AUTH_NTLM_HASH 1003 +#define SECPKG_ATTR_AUTH_NTLM_MESSAGE 1100 +#define SECPKG_ATTR_AUTH_NTLM_TIMESTAMP 1101 +#define SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE 1102 +#define SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE 1103 +#define SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE 1104 +#define SECPKG_ATTR_AUTH_NTLM_RANDKEY 1105 +#define SECPKG_ATTR_AUTH_NTLM_MIC 1106 +#define SECPKG_ATTR_AUTH_NTLM_MIC_VALUE 1107 + + typedef struct + { + char User[256 + 1]; + char Domain[256 + 1]; + } SecPkgContext_AuthIdentity; + + typedef struct + { + char Password[256 + 1]; + } SecPkgContext_AuthPassword; + + typedef struct + { + int Version; + BYTE NtlmHash[16]; + } SecPkgContext_AuthNtlmHash; + + typedef struct + { + BYTE Timestamp[8]; + BOOL ChallengeOrResponse; + } SecPkgContext_AuthNtlmTimestamp; + + typedef struct + { + BYTE ClientChallenge[8]; + } SecPkgContext_AuthNtlmClientChallenge; + + typedef struct + { + BYTE ServerChallenge[8]; + } SecPkgContext_AuthNtlmServerChallenge; + + typedef struct + { + UINT32 type; + UINT32 length; + BYTE* buffer; + } SecPkgContext_AuthNtlmMessage; + +#define SSPI_INTERFACE_WINPR 0x00000001 +#define SSPI_INTERFACE_NATIVE 0x00000002 + + typedef PSecurityFunctionTableA(SEC_ENTRY* INIT_SECURITY_INTERFACE_EX_A)(DWORD flags); + typedef PSecurityFunctionTableW(SEC_ENTRY* INIT_SECURITY_INTERFACE_EX_W)(DWORD flags); + + WINPR_API void sspi_GlobalInit(void); + WINPR_API void sspi_GlobalFinish(void); + + WINPR_API void* sspi_SecBufferAlloc(PSecBuffer SecBuffer, ULONG size); + WINPR_API void sspi_SecBufferFree(PSecBuffer SecBuffer); + +#define sspi_SetAuthIdentity sspi_SetAuthIdentityA + WINPR_API int sspi_SetAuthIdentityA(SEC_WINNT_AUTH_IDENTITY* identity, const char* user, + const char* domain, const char* password); + WINPR_API int sspi_SetAuthIdentityW(SEC_WINNT_AUTH_IDENTITY* identity, const WCHAR* user, + const WCHAR* domain, const WCHAR* password); + WINPR_API int sspi_SetAuthIdentityWithLengthW(SEC_WINNT_AUTH_IDENTITY* identity, + const WCHAR* user, size_t userLen, + const WCHAR* domain, size_t domainLen, + const WCHAR* password, size_t passwordLen); + WINPR_API UINT32 sspi_GetAuthIdentityVersion(const void* identity); + WINPR_API UINT32 sspi_GetAuthIdentityFlags(const void* identity); + WINPR_API BOOL sspi_GetAuthIdentityUserDomainW(const void* identity, const WCHAR** pUser, + UINT32* pUserLength, const WCHAR** pDomain, + UINT32* pDomainLength); + WINPR_API BOOL sspi_GetAuthIdentityUserDomainA(const void* identity, const char** pUser, + UINT32* pUserLength, const char** pDomain, + UINT32* pDomainLength); + WINPR_API BOOL sspi_GetAuthIdentityPasswordW(const void* identity, const WCHAR** pPassword, + UINT32* pPasswordLength); + WINPR_API BOOL sspi_GetAuthIdentityPasswordA(const void* identity, const char** pPassword, + UINT32* pPasswordLength); + WINPR_API BOOL sspi_CopyAuthIdentityFieldsA(const SEC_WINNT_AUTH_IDENTITY_INFO* identity, + char** pUser, char** pDomain, char** pPassword); + WINPR_API BOOL sspi_CopyAuthIdentityFieldsW(const SEC_WINNT_AUTH_IDENTITY_INFO* identity, + WCHAR** pUser, WCHAR** pDomain, WCHAR** pPassword); + WINPR_API BOOL sspi_CopyAuthPackageListA(const SEC_WINNT_AUTH_IDENTITY_INFO* identity, + char** pPackageList); + WINPR_API int sspi_CopyAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, + const SEC_WINNT_AUTH_IDENTITY_INFO* srcIdentity); + + WINPR_API void sspi_FreeAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity); + + WINPR_API const char* GetSecurityStatusString(SECURITY_STATUS status); + + WINPR_API SecurityFunctionTableW* SEC_ENTRY InitSecurityInterfaceExW(DWORD flags); + WINPR_API SecurityFunctionTableA* SEC_ENTRY InitSecurityInterfaceExA(DWORD flags); + +#ifdef UNICODE +#define InitSecurityInterfaceEx InitSecurityInterfaceExW +#define INIT_SECURITY_INTERFACE_EX INIT_SECURITY_INTERFACE_EX_W +#else +#define InitSecurityInterfaceEx InitSecurityInterfaceExA +#define INIT_SECURITY_INTERFACE_EX INIT_SECURITY_INTERFACE_EX_A +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_SSPI_H */ diff --git a/winpr/include/winpr/sspicli.h b/winpr/include/winpr/sspicli.h new file mode 100644 index 0000000..bea9e9a --- /dev/null +++ b/winpr/include/winpr/sspicli.h @@ -0,0 +1,147 @@ +/** + * WinPR: Windows Portable Runtime + * Security Support Provider Interface + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPICLI_H +#define WINPR_SSPICLI_H + +#include +#include +#include +#include +#include + +#ifndef _WIN32 + +#define LOGON32_LOGON_INTERACTIVE 2 +#define LOGON32_LOGON_NETWORK 3 +#define LOGON32_LOGON_BATCH 4 +#define LOGON32_LOGON_SERVICE 5 +#define LOGON32_LOGON_UNLOCK 7 +#define LOGON32_LOGON_NETWORK_CLEARTEXT 8 +#define LOGON32_LOGON_NEW_CREDENTIALS 9 + +#define LOGON32_PROVIDER_DEFAULT 0 +#define LOGON32_PROVIDER_WINNT35 1 +#define LOGON32_PROVIDER_WINNT40 2 +#define LOGON32_PROVIDER_WINNT50 3 +#define LOGON32_PROVIDER_VIRTUAL 4 + +typedef struct +{ + SIZE_T PagedPoolLimit; + SIZE_T NonPagedPoolLimit; + SIZE_T MinimumWorkingSetSize; + SIZE_T MaximumWorkingSetSize; + SIZE_T PagefileLimit; + LARGE_INTEGER TimeLimit; +} QUOTA_LIMITS, *PQUOTA_LIMITS; + +typedef enum +{ + /* An unknown name type */ + NameUnknown = 0, + + /* The fully qualified distinguished name (for example, CN=Jeff + Smith,OU=Users,DC=Engineering,DC=Microsoft,DC=Com) */ + NameFullyQualifiedDN = 1, + + /* + * A legacy account name (for example, Engineering\JSmith). + * The domain-only version includes trailing backslashes (\\) + */ + NameSamCompatible = 2, + + /* + * A "friendly" display name (for example, Jeff Smith). + * The display name is not necessarily the defining relative distinguished name (RDN) + */ + NameDisplay = 3, + + /* A GUID string that the IIDFromString function returns (for example, + {4fa050f0-f561-11cf-bdd9-00aa003a77b6}) */ + NameUniqueId = 6, + + /* + * The complete canonical name (for example, engineering.microsoft.com/software/someone). + * The domain-only version includes a trailing forward slash (/) + */ + NameCanonical = 7, + + /* The user principal name (for example, someone@example.com) */ + NameUserPrincipal = 8, + + /* + * The same as NameCanonical except that the rightmost forward slash (/) + * is replaced with a new line character (\n), even in a domain-only case + * (for example, engineering.microsoft.com/software\nJSmith) + */ + NameCanonicalEx = 9, + + /* The generalized service principal name (for example, www/www.microsoft.com@microsoft.com) */ + NameServicePrincipal = 10, + + /* The DNS domain name followed by a backward-slash and the SAM user name */ + NameDnsDomain = 12 + +} EXTENDED_NAME_FORMAT, + *PEXTENDED_NAME_FORMAT; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL LogonUserA(LPCSTR lpszUsername, LPCSTR lpszDomain, LPCSTR lpszPassword, + DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken); + + WINPR_API BOOL LogonUserW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword, + DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken); + + WINPR_API BOOL LogonUserExA(LPCSTR lpszUsername, LPCSTR lpszDomain, LPCSTR lpszPassword, + DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken, + PSID* ppLogonSid, PVOID* ppProfileBuffer, LPDWORD pdwProfileLength, + PQUOTA_LIMITS pQuotaLimits); + + WINPR_API BOOL LogonUserExW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword, + DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken, + PSID* ppLogonSid, PVOID* ppProfileBuffer, LPDWORD pdwProfileLength, + PQUOTA_LIMITS pQuotaLimits); + + WINPR_API BOOL GetUserNameExA(EXTENDED_NAME_FORMAT NameFormat, LPSTR lpNameBuffer, + PULONG nSize); + WINPR_API BOOL GetUserNameExW(EXTENDED_NAME_FORMAT NameFormat, LPWSTR lpNameBuffer, + PULONG nSize); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define LogonUser LogonUserW +#define LogonUserEx LogonUserExW +#define GetUserNameEx GetUserNameExW +#else +#define LogonUser LogonUserA +#define LogonUserEx LogonUserExA +#define GetUserNameEx GetUserNameExA +#endif + +#endif + +#endif /* WINPR_SSPICLI_H */ diff --git a/winpr/include/winpr/stream.h b/winpr/include/winpr/stream.h new file mode 100644 index 0000000..6dd5a0e --- /dev/null +++ b/winpr/include/winpr/stream.h @@ -0,0 +1,842 @@ +/* + * WinPR: Windows Portable Runtime + * Stream Utils + * + * Copyright 2011 Vic Lee + * Copyright 2012 Marc-Andre Moreau + * Copyright 2017 Armin Novak + * Copyright 2017 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_STREAM_H +#define WINPR_UTILS_STREAM_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct s_wStreamPool wStreamPool; + + typedef struct + { + BYTE* buffer; + BYTE* pointer; + size_t length; + size_t capacity; + + DWORD count; + wStreamPool* pool; + BOOL isAllocatedStream; + BOOL isOwner; + } wStream; + + static INLINE size_t Stream_Capacity(const wStream* _s); + WINPR_API size_t Stream_GetRemainingCapacity(const wStream* _s); + WINPR_API size_t Stream_GetRemainingLength(const wStream* _s); + + WINPR_API BOOL Stream_EnsureCapacity(wStream* s, size_t size); + WINPR_API BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size); + +#ifdef __cplusplus +#define WINPR_STREAM_CAST(t, val) static_cast(val) +#else +#define WINPR_STREAM_CAST(t, val) (t)(val) +#endif + +#define Stream_CheckAndLogRequiredCapacityOfSize(tag, s, nmemb, size) \ + Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, size, "%s(%s:%" PRIuz ")", \ + __func__, __FILE__, (size_t)__LINE__) +#define Stream_CheckAndLogRequiredCapacity(tag, s, len) \ + Stream_CheckAndLogRequiredCapacityOfSize((tag), (s), (len), 1) + + WINPR_API BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, + size_t nmemb, size_t size, const char* fmt, + ...); + WINPR_API BOOL Stream_CheckAndLogRequiredCapacityExVa(const char* tag, DWORD level, wStream* s, + size_t nmemb, size_t size, + const char* fmt, va_list args); + +#define Stream_CheckAndLogRequiredCapacityOfSizeWLog(log, s, nmemb, size) \ + Stream_CheckAndLogRequiredCapacityWLogEx(log, WLOG_WARN, s, nmemb, size, "%s(%s:%" PRIuz ")", \ + __func__, __FILE__, (size_t)__LINE__) + +#define Stream_CheckAndLogRequiredCapacityWLog(log, s, len) \ + Stream_CheckAndLogRequiredCapacityOfSizeWLog((log), (s), (len), 1) + + WINPR_API BOOL Stream_CheckAndLogRequiredCapacityWLogEx(wLog* log, DWORD level, wStream* s, + size_t nmemb, size_t size, + const char* fmt, ...); + WINPR_API BOOL Stream_CheckAndLogRequiredCapacityWLogExVa(wLog* log, DWORD level, wStream* s, + size_t nmemb, size_t size, + const char* fmt, va_list args); + + WINPR_API void Stream_Free(wStream* s, BOOL bFreeBuffer); + + WINPR_ATTR_MALLOC(Stream_Free, 1) + WINPR_API wStream* Stream_New(BYTE* buffer, size_t size); + WINPR_API wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size); + WINPR_API wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size); + +#define Stream_CheckAndLogRequiredLengthOfSize(tag, s, nmemb, size) \ + Stream_CheckAndLogRequiredLengthEx(tag, WLOG_WARN, s, nmemb, size, "%s(%s:%" PRIuz ")", \ + __func__, __FILE__, (size_t)__LINE__) +#define Stream_CheckAndLogRequiredLength(tag, s, len) \ + Stream_CheckAndLogRequiredLengthOfSize(tag, s, len, 1) + + WINPR_API BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, + size_t nmemb, size_t size, const char* fmt, + ...); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, + size_t nmemb, size_t size, const char* fmt, + va_list args); + +#define Stream_CheckAndLogRequiredLengthOfSizeWLog(log, s, nmemb, size) \ + Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, nmemb, size, "%s(%s:%" PRIuz ")", \ + __func__, __FILE__, (size_t)__LINE__) +#define Stream_CheckAndLogRequiredLengthWLog(log, s, len) \ + Stream_CheckAndLogRequiredLengthOfSizeWLog(log, s, len, 1) + + WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, + size_t nmemb, size_t size, + const char* fmt, ...); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, + size_t nmemb, size_t size, + const char* fmt, va_list args); + + static INLINE void Stream_Seek(wStream* s, size_t _offset) + { + WINPR_ASSERT(s); + WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= _offset); + s->pointer += (_offset); + } + + static INLINE void Stream_Rewind(wStream* s, size_t _offset) + { + size_t cur; + WINPR_ASSERT(s); + WINPR_ASSERT(s->buffer <= s->pointer); + cur = WINPR_STREAM_CAST(size_t, s->pointer - s->buffer); + WINPR_ASSERT(cur >= _offset); + if (cur >= _offset) + s->pointer -= (_offset); + else + s->pointer = s->buffer; + } + + static INLINE UINT8 stream_read_u8(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT8)); + + const UINT8 v = WINPR_STREAM_CAST(UINT8, *(_s)->pointer); + if (seek) + Stream_Seek(_s, sizeof(UINT8)); + return v; + } + + static INLINE INT8 stream_read_i8(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(INT8)); + + const INT8 v = WINPR_STREAM_CAST(INT8, *(_s)->pointer); + if (seek) + Stream_Seek(_s, sizeof(INT8)); + return v; + } + + static INLINE UINT16 stream_read_u16_le(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT16)); + + const UINT16 v = WINPR_STREAM_CAST( + UINT16, (*(_s)->pointer) + ((WINPR_STREAM_CAST(UINT16, *((_s)->pointer + 1))) << 8)); + if (seek) + Stream_Seek(_s, sizeof(UINT16)); + return v; + } + + static INLINE UINT16 stream_read_u16_be(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT16)); + + const UINT16 v = ((WINPR_STREAM_CAST(UINT16, *(_s)->pointer) << 8) + + (WINPR_STREAM_CAST(UINT16, *((_s)->pointer + 1)))); + if (seek) + Stream_Seek(_s, sizeof(UINT16)); + return v; + } + + static INLINE UINT16 stream_read_i16_le(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(INT16)); + + const INT16 v = WINPR_STREAM_CAST( + INT16, (*(_s)->pointer) + ((WINPR_STREAM_CAST(INT16, *((_s)->pointer + 1))) << 8)); + if (seek) + Stream_Seek(_s, sizeof(INT16)); + return v; + } + + static INLINE UINT16 stream_read_i16_be(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(INT16)); + + const INT16 v = ((WINPR_STREAM_CAST(INT16, *(_s)->pointer) << 8) + + (WINPR_STREAM_CAST(INT16, *((_s)->pointer + 1)))); + if (seek) + Stream_Seek(_s, sizeof(INT16)); + return v; + } + + static INLINE UINT32 stream_read_u32_le(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT32)); + + const UINT32 v = (WINPR_STREAM_CAST(UINT32, *(_s)->pointer) << 0) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 1))) << 8) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 2))) << 16) + + (((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 3))) << 24)); + if (seek) + Stream_Seek(_s, sizeof(UINT32)); + return v; + } + + static INLINE UINT32 stream_read_u32_be(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT32)); + + const UINT32 v = (WINPR_STREAM_CAST(UINT32, *(_s)->pointer) << 24) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 1))) << 16) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 2))) << 8) + + (((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 3))) << 0)); + if (seek) + Stream_Seek(_s, sizeof(UINT32)); + return v; + } + + static INLINE INT32 stream_read_i32_le(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT32)); + + const INT32 v = + WINPR_STREAM_CAST(INT32, (WINPR_STREAM_CAST(UINT32, *(_s)->pointer) << 0) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 1))) << 8) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 2))) << 16) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 3))) << 24)); + if (seek) + Stream_Seek(_s, sizeof(UINT32)); + return v; + } + + static INLINE INT32 stream_read_i32_be(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT32)); + + const INT32 v = WINPR_STREAM_CAST( + INT32, (WINPR_STREAM_CAST(UINT32, *(_s)->pointer) << 24) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 1))) << 16) + + ((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 2))) << 8) + + (((WINPR_STREAM_CAST(UINT32, *((_s)->pointer + 3))) << 0))); + if (seek) + Stream_Seek(_s, sizeof(UINT32)); + return v; + } + + static INLINE UINT64 stream_read_u64_le(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT64)); + + const UINT64 v = (WINPR_STREAM_CAST(UINT64, *(_s)->pointer) << 0) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 1))) << 8) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 2))) << 16) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 3))) << 24) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 4))) << 32) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 5))) << 40) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 6))) << 48) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 7))) << 56); + + if (seek) + Stream_Seek(_s, sizeof(UINT64)); + return v; + } + + static INLINE UINT64 stream_read_u64_be(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(UINT64)); + + const UINT64 v = (WINPR_STREAM_CAST(UINT64, *(_s)->pointer) << 56) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 1))) << 48) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 2))) << 40) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 3))) << 32) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 4))) << 24) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 5))) << 16) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 6))) << 8) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 7))) << 0); + + if (seek) + Stream_Seek(_s, sizeof(UINT64)); + return v; + } + + static INLINE INT64 stream_read_i64_le(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(INT64)); + + const INT64 v = + WINPR_STREAM_CAST(INT64, ((WINPR_STREAM_CAST(UINT64, *(_s)->pointer) << 0) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 1))) << 8) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 2))) << 16) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 3))) << 24) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 4))) << 32) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 5))) << 40) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 6))) << 48) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 7))) << 56))); + + if (seek) + Stream_Seek(_s, sizeof(UINT64)); + return v; + } + + static INLINE INT64 stream_read_i64_be(wStream* _s, BOOL seek) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingLength(_s) >= sizeof(INT64)); + + const INT64 v = + WINPR_STREAM_CAST(INT64, ((WINPR_STREAM_CAST(UINT64, *(_s)->pointer) << 56) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 1))) << 48) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 2))) << 40) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 3))) << 32) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 4))) << 24) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 5))) << 16) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 6))) << 8) + + ((WINPR_STREAM_CAST(UINT64, *((_s)->pointer + 7))) << 0))); + + if (seek) + Stream_Seek(_s, sizeof(UINT64)); + return v; + } + +#define Stream_Read_UINT8(_s, _v) \ + do \ + { \ + _v = stream_read_u8(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT8(_s, _v) \ + do \ + { \ + _v = stream_read_i8(_s, TRUE); \ + } while (0) + +#define Stream_Read_UINT16(_s, _v) \ + do \ + { \ + _v = stream_read_u16_le(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT16(_s, _v) \ + do \ + { \ + _v = stream_read_i16_le(_s, TRUE); \ + } while (0) + +#define Stream_Read_UINT16_BE(_s, _v) \ + do \ + { \ + _v = stream_read_u16_be(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT16_BE(_s, _v) \ + do \ + { \ + _v = stream_read_i16_be(_s, TRUE); \ + } while (0) + +#define Stream_Read_UINT32(_s, _v) \ + do \ + { \ + _v = stream_read_u32_le(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT32(_s, _v) \ + do \ + { \ + _v = stream_read_i32_le(_s, TRUE); \ + } while (0) + +#define Stream_Read_UINT32_BE(_s, _v) \ + do \ + { \ + _v = stream_read_u32_be(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT32_BE(_s, _v) \ + do \ + { \ + _v = stream_read_i32_be(_s, TRUE); \ + } while (0) + +#define Stream_Read_UINT64(_s, _v) \ + do \ + { \ + _v = stream_read_u64_le(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT64(_s, _v) \ + do \ + { \ + _v = stream_read_i64_le(_s, TRUE); \ + } while (0) + +#define Stream_Read_UINT64_BE(_s, _v) \ + do \ + { \ + _v = stream_read_u64_be(_s, TRUE); \ + } while (0) + +#define Stream_Read_INT64_BE(_s, _v) \ + do \ + { \ + _v = stream_read_i64_be(_s, TRUE); \ + } while (0) + + static INLINE void Stream_Read(wStream* _s, void* _b, size_t _n) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_b || (_n == 0)); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= _n); + memcpy(_b, (_s->pointer), (_n)); + Stream_Seek(_s, _n); + } + +#define Stream_Peek_UINT8(_s, _v) \ + do \ + { \ + _v = stream_read_u8(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT8(_s, _v) \ + do \ + { \ + _v = stream_read_i8(_s, FALSE); \ + } while (0) + +#define Stream_Peek_UINT16(_s, _v) \ + do \ + { \ + _v = stream_read_u16_le(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT16(_s, _v) \ + do \ + { \ + _v = stream_read_i16_le(_s, FALSE); \ + } while (0) + +#define Stream_Peek_UINT16_BE(_s, _v) \ + do \ + { \ + _v = stream_read_u16_be(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT16_BE(_s, _v) \ + do \ + { \ + _v = stream_read_i16_be(_s, FALSE); \ + } while (0) + +#define Stream_Peek_UINT32(_s, _v) \ + do \ + { \ + _v = stream_read_u32_le(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT32(_s, _v) \ + do \ + { \ + _v = stream_read_i32_le(_s, FALSE); \ + } while (0) + +#define Stream_Peek_UINT32_BE(_s, _v) \ + do \ + { \ + _v = stream_read_u32_be(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT32_BE(_s, _v) \ + do \ + { \ + _v = stream_read_i32_be(_s, FALSE); \ + } while (0) + +#define Stream_Peek_UINT64(_s, _v) \ + do \ + { \ + _v = stream_read_u64_le(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT64(_s, _v) \ + do \ + { \ + _v = stream_read_i64_le(_s, FALSE); \ + } while (0) + +#define Stream_Peek_UINT64_BE(_s, _v) \ + do \ + { \ + _v = stream_read_u64_be(_s, FALSE); \ + } while (0) + +#define Stream_Peek_INT64_BE(_s, _v) \ + do \ + { \ + _v = stream_read_i64_be(_s, FALSE); \ + } while (0) + + static INLINE void Stream_Peek(const wStream* _s, void* _b, size_t _n) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_b || (_n == 0)); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= _n); + memcpy(_b, (_s->pointer), (_n)); + } + + static INLINE void Stream_Write_UINT8(wStream* _s, UINT8 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 1); + *_s->pointer++ = (_v); + } + + static INLINE void Stream_Write_INT16(wStream* _s, INT16 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 2); + *_s->pointer++ = (_v)&0xFF; + *_s->pointer++ = ((_v) >> 8) & 0xFF; + } + + static INLINE void Stream_Write_UINT16(wStream* _s, UINT16 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 2); + *_s->pointer++ = (_v)&0xFF; + *_s->pointer++ = ((_v) >> 8) & 0xFF; + } + + static INLINE void Stream_Write_UINT16_BE(wStream* _s, UINT16 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 2); + *_s->pointer++ = ((_v) >> 8) & 0xFF; + *_s->pointer++ = (_v)&0xFF; + } + + static INLINE void Stream_Write_UINT24_BE(wStream* _s, UINT32 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(_v <= 0x00FFFFFF); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 3); + *_s->pointer++ = ((_v) >> 16) & 0xFF; + *_s->pointer++ = ((_v) >> 8) & 0xFF; + *_s->pointer++ = (_v)&0xFF; + } + + static INLINE void Stream_Write_INT32(wStream* _s, INT32 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 4); + *_s->pointer++ = (_v)&0xFF; + *_s->pointer++ = ((_v) >> 8) & 0xFF; + *_s->pointer++ = ((_v) >> 16) & 0xFF; + *_s->pointer++ = ((_v) >> 24) & 0xFF; + } + + static INLINE void Stream_Write_UINT32(wStream* _s, UINT32 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 4); + *_s->pointer++ = (_v)&0xFF; + *_s->pointer++ = ((_v) >> 8) & 0xFF; + *_s->pointer++ = ((_v) >> 16) & 0xFF; + *_s->pointer++ = ((_v) >> 24) & 0xFF; + } + + static INLINE void Stream_Write_UINT32_BE(wStream* _s, UINT32 _v) + { + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 4); + Stream_Write_UINT16_BE(_s, ((_v) >> 16 & 0xFFFF)); + Stream_Write_UINT16_BE(_s, ((_v)&0xFFFF)); + } + + static INLINE void Stream_Write_UINT64(wStream* _s, UINT64 _v) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->pointer); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= 8); + *_s->pointer++ = (_v)&0xFF; + *_s->pointer++ = (_v >> 8) & 0xFF; + *_s->pointer++ = (_v >> 16) & 0xFF; + *_s->pointer++ = (_v >> 24) & 0xFF; + *_s->pointer++ = (_v >> 32) & 0xFF; + *_s->pointer++ = (_v >> 40) & 0xFF; + *_s->pointer++ = (_v >> 48) & 0xFF; + *_s->pointer++ = (_v >> 56) & 0xFF; + } + static INLINE void Stream_Write(wStream* _s, const void* _b, size_t _n) + { + if (_n > 0) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_b); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= _n); + memcpy(_s->pointer, (_b), (_n)); + Stream_Seek(_s, _n); + } + } + + static INLINE void Stream_Seek_UINT8(wStream* _s) + { + Stream_Seek(_s, sizeof(UINT8)); + } + static INLINE void Stream_Seek_UINT16(wStream* _s) + { + Stream_Seek(_s, sizeof(UINT16)); + } + static INLINE void Stream_Seek_UINT32(wStream* _s) + { + Stream_Seek(_s, sizeof(UINT32)); + } + static INLINE void Stream_Seek_UINT64(wStream* _s) + { + Stream_Seek(_s, sizeof(UINT64)); + } + + static INLINE void Stream_Rewind_UINT8(wStream* _s) + { + Stream_Rewind(_s, sizeof(UINT8)); + } + static INLINE void Stream_Rewind_UINT16(wStream* _s) + { + Stream_Rewind(_s, sizeof(UINT16)); + } + static INLINE void Stream_Rewind_UINT32(wStream* _s) + { + Stream_Rewind(_s, sizeof(UINT32)); + } + static INLINE void Stream_Rewind_UINT64(wStream* _s) + { + Stream_Rewind(_s, sizeof(UINT64)); + } + + static INLINE void Stream_Zero(wStream* _s, size_t _n) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= (_n)); + memset(_s->pointer, '\0', (_n)); + Stream_Seek(_s, _n); + } + + static INLINE void Stream_Fill(wStream* _s, int _v, size_t _n) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(Stream_GetRemainingCapacity(_s) >= (_n)); + memset(_s->pointer, _v, (_n)); + Stream_Seek(_s, _n); + } + + static INLINE void Stream_Copy(wStream* _src, wStream* _dst, size_t _n) + { + WINPR_ASSERT(_src); + WINPR_ASSERT(_dst); + WINPR_ASSERT(Stream_GetRemainingCapacity(_src) >= (_n)); + WINPR_ASSERT(Stream_GetRemainingCapacity(_dst) >= (_n)); + + memcpy(_dst->pointer, _src->pointer, _n); + Stream_Seek(_dst, _n); + Stream_Seek(_src, _n); + } + + static INLINE BYTE* Stream_Buffer(wStream* _s) + { + WINPR_ASSERT(_s); + return _s->buffer; + } + + static INLINE const BYTE* Stream_ConstBuffer(const wStream* _s) + { + WINPR_ASSERT(_s); + return _s->buffer; + } + +#define Stream_GetBuffer(_s, _b) _b = Stream_Buffer(_s) +#define Stream_PointerAs(s, type) (type*)Stream_Pointer(s) + + static INLINE void* Stream_Pointer(wStream* _s) + { + WINPR_ASSERT(_s); + return _s->pointer; + } + + static INLINE const void* Stream_ConstPointer(const wStream* _s) + { + WINPR_ASSERT(_s); + return _s->pointer; + } + +#define Stream_GetPointer(_s, _p) _p = Stream_Pointer(_s) + +#if defined(WITH_WINPR_DEPRECATED) + WINPR_API WINPR_DEPRECATED_VAR("Use Stream_SetPosition instead", + BOOL Stream_SetPointer(wStream* _s, BYTE* _p)); + WINPR_API WINPR_DEPRECATED_VAR("Use Stream_New(buffer, capacity) instead", + BOOL Stream_SetBuffer(wStream* _s, BYTE* _b)); + WINPR_API WINPR_DEPRECATED_VAR("Use Stream_New(buffer, capacity) instead", + void Stream_SetCapacity(wStream* _s, size_t capacity)); +#endif + + static INLINE size_t Stream_Length(const wStream* _s) + { + WINPR_ASSERT(_s); + return _s->length; + } + +#define Stream_GetLength(_s, _l) _l = Stream_Length(_s) + WINPR_API BOOL Stream_SetLength(wStream* _s, size_t _l); + + static INLINE size_t Stream_Capacity(const wStream* _s) + { + WINPR_ASSERT(_s); + return _s->capacity; + } + +#define Stream_GetCapacity(_s, _c) _c = Stream_Capacity(_s); + + static INLINE size_t Stream_GetPosition(const wStream* _s) + { + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + return WINPR_STREAM_CAST(size_t, (_s->pointer - _s->buffer)); + } + + WINPR_API BOOL Stream_SetPosition(wStream* _s, size_t _p); + + WINPR_API void Stream_SealLength(wStream* _s); + + static INLINE void Stream_Clear(wStream* _s) + { + WINPR_ASSERT(_s); + memset(_s->buffer, 0, _s->capacity); + } + +#define Stream_SafeSeek(s, size) Stream_SafeSeekEx(s, size, __FILE__, __LINE__, __func__) + WINPR_API BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, + const char* fkt); + + WINPR_API BOOL Stream_Read_UTF16_String(wStream* s, WCHAR* dst, size_t charLength); + WINPR_API BOOL Stream_Write_UTF16_String(wStream* s, const WCHAR* src, size_t charLength); + + /** \brief Reads a WCHAR string from a stream and converts it to UTF-8 and returns a newly + * allocated string + * + * \param s The stream to read data from + * \param wcharLength The number of WCHAR characters to read (NOT the size in bytes!) + * \param pUtfCharLength Ignored if \b NULL, otherwise will be set to the number of + * characters in the resulting UTF-8 string + * \return A '\0' terminated UTF-8 encoded string or NULL for any failure. + */ + WINPR_API char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t wcharLength, + size_t* pUtfCharLength); + + /** \brief Reads a WCHAR string from a stream and converts it to UTF-8 and + * writes it to the supplied buffer + * + * \param s The stream to read data from + * \param wcharLength The number of WCHAR characters to read (NOT the size in bytes!) + * \param utfBuffer A pointer to a buffer holding the result string + * \param utfBufferCharLength The size of the result buffer + * \return The char length (strlen) of the result string or -1 for failure + */ + WINPR_API SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, + char* utfBuffer, + size_t utfBufferCharLength); + + /** \brief Writes a UTF-8 string UTF16 encoded to the stream. If the UTF-8 + * string is short, the remainig characters are filled up with '\0' + * + * \param s The stream to write to + * \param wcharLength the length (in WCHAR characters) to write + * \param src The source data buffer with the UTF-8 data + * \param length The length in bytes of the UTF-8 buffer + * \param fill If \b TRUE fill the unused parts of the wcharLength with 0 + * + * \b return number of used characters for success, /b -1 for failure + */ + WINPR_API SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t wcharLength, + const char* src, size_t length, + BOOL fill); + + /* StreamPool */ + + WINPR_API void StreamPool_Return(wStreamPool* pool, wStream* s); + + WINPR_API wStream* StreamPool_Take(wStreamPool* pool, size_t size); + + WINPR_API void Stream_AddRef(wStream* s); + WINPR_API void Stream_Release(wStream* s); + + WINPR_API wStream* StreamPool_Find(wStreamPool* pool, BYTE* ptr); + + WINPR_API void StreamPool_Clear(wStreamPool* pool); + + WINPR_API void StreamPool_Free(wStreamPool* pool); + + WINPR_ATTR_MALLOC(StreamPool_Free, 1) + WINPR_API wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize); + + WINPR_API char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_UTILS_STREAM_H */ diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h new file mode 100644 index 0000000..09b253c --- /dev/null +++ b/winpr/include/winpr/string.h @@ -0,0 +1,434 @@ +/** + * WinPR: Windows Portable Runtime + * String Manipulation (CRT) + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CRT_STRING_H +#define WINPR_CRT_STRING_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API char* winpr_str_url_encode(const char* str, size_t len); + WINPR_API char* winpr_str_url_decode(const char* str, size_t len); + + WINPR_API BOOL winpr_str_append(const char* what, char* buffer, size_t size, + const char* separator); + + WINPR_API int winpr_asprintf(char** s, size_t* slen, const char* templ, ...); + WINPR_API int winpr_vasprintf(char** s, size_t* slen, const char* templ, va_list ap); + +#ifndef _WIN32 + +#define CSTR_LESS_THAN 1 +#define CSTR_EQUAL 2 +#define CSTR_GREATER_THAN 3 + +#define CP_ACP 0 +#define CP_OEMCP 1 +#define CP_MACCP 2 +#define CP_THREAD_ACP 3 +#define CP_SYMBOL 42 +#define CP_UTF7 65000 +#define CP_UTF8 65001 + +#define MB_PRECOMPOSED 0x00000001 +#define MB_COMPOSITE 0x00000002 +#define MB_USEGLYPHCHARS 0x00000004 +#define MB_ERR_INVALID_CHARS 0x00000008 + + WINPR_API char* _strdup(const char* strSource); + WINPR_API WCHAR* _wcsdup(const WCHAR* strSource); + + WINPR_API int _stricmp(const char* string1, const char* string2); + WINPR_API int _strnicmp(const char* string1, const char* string2, size_t count); + + WINPR_API int _wcscmp(const WCHAR* string1, const WCHAR* string2); + WINPR_API int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count); + + WINPR_API size_t _wcslen(const WCHAR* str); + WINPR_API size_t _wcsnlen(const WCHAR* str, size_t maxNumberOfElements); + + WINPR_API WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch); + + WINPR_API WCHAR* _wcschr(const WCHAR* str, WCHAR c); + WINPR_API WCHAR* _wcsrchr(const WCHAR* str, WCHAR c); + + WINPR_API char* strtok_s(char* strToken, const char* strDelimit, char** context); + WINPR_API WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context); + + WINPR_API WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz); +#else + +#define _wcscmp wcscmp +#define _wcsncmp wcsncmp +#define _wcslen wcslen +#define _wcsnlen wcsnlen +#define _wcsstr wcsstr +#define _wcschr wcschr +#define _wcsrchr wcsrchr +#define _wcsncat wcsncat + +#endif /* _WIN32 */ + +#if !defined(_WIN32) || defined(_UWP) + + WINPR_API LPSTR CharUpperA(LPSTR lpsz); + WINPR_API LPWSTR CharUpperW(LPWSTR lpsz); + +#ifdef UNICODE +#define CharUpper CharUpperW +#else +#define CharUpper CharUpperA +#endif + + WINPR_API DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength); + WINPR_API DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength); + +#ifdef UNICODE +#define CharUpperBuff CharUpperBuffW +#else +#define CharUpperBuff CharUpperBuffA +#endif + + WINPR_API LPSTR CharLowerA(LPSTR lpsz); + WINPR_API LPWSTR CharLowerW(LPWSTR lpsz); + +#ifdef UNICODE +#define CharLower CharLowerW +#else +#define CharLower CharLowerA +#endif + + WINPR_API DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength); + WINPR_API DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength); + +#ifdef UNICODE +#define CharLowerBuff CharLowerBuffW +#else +#define CharLowerBuff CharLowerBuffA +#endif + + WINPR_API BOOL IsCharAlphaA(CHAR ch); + WINPR_API BOOL IsCharAlphaW(WCHAR ch); + +#ifdef UNICODE +#define IsCharAlpha IsCharAlphaW +#else +#define IsCharAlpha IsCharAlphaA +#endif + + WINPR_API BOOL IsCharAlphaNumericA(CHAR ch); + WINPR_API BOOL IsCharAlphaNumericW(WCHAR ch); + +#ifdef UNICODE +#define IsCharAlphaNumeric IsCharAlphaNumericW +#else +#define IsCharAlphaNumeric IsCharAlphaNumericA +#endif + + WINPR_API BOOL IsCharUpperA(CHAR ch); + WINPR_API BOOL IsCharUpperW(WCHAR ch); + +#ifdef UNICODE +#define IsCharUpper IsCharUpperW +#else +#define IsCharUpper IsCharUpperA +#endif + + WINPR_API BOOL IsCharLowerA(CHAR ch); + WINPR_API BOOL IsCharLowerW(WCHAR ch); + +#ifdef UNICODE +#define IsCharLower IsCharLowerW +#else +#define IsCharLower IsCharLowerA +#endif + +#endif + +#ifndef _WIN32 + +#define sprintf_s snprintf +#define _snprintf snprintf +#define _scprintf(...) snprintf(NULL, 0, __VA_ARGS__) + +#define _scprintf(...) snprintf(NULL, 0, __VA_ARGS__) + + /* Unicode Conversion */ + +#if defined(WITH_WINPR_DEPRECATED) + WINPR_API WINPR_DEPRECATED_VAR("Use ConvertUtf8ToWChar instead", + int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, + LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar)); + + WINPR_API WINPR_DEPRECATED_VAR("Use ConvertWCharToUtf8 instead", + int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, + LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, + LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar)); +#endif + +#endif + + /* Extended API */ + /** \brief Converts form UTF-16 to UTF-8 + * + * The function does string conversions of any '\0' terminated input string + * + * Supplying len = 0 will return the required size of the buffer in characters. + * + * \warning Supplying a buffer length smaller than required will result in + * platform dependent (=undefined) behaviour! + * + * \param wstr A '\0' terminated WCHAR string, may be NULL + * \param str A pointer to the result string + * \param len The length in characters of the result buffer + * + * \return the size of the converted string in char (strlen), or -1 for failure + */ + WINPR_API SSIZE_T ConvertWCharToUtf8(const WCHAR* wstr, char* str, size_t len); + + /** \brief Converts form UTF-16 to UTF-8 + * + * The function does string conversions of any input string of wlen (or less) + * characters until it reaches the first '\0'. + * + * Supplying len = 0 will return the required size of the buffer in characters. + * + * \warning Supplying a buffer length smaller than required will result in + * platform dependent (=undefined) behaviour! + * + * \param wstr A WCHAR string of \b wlen length + * \param wlen The (buffer) length in characters of \b wstr + * \param str A pointer to the result string + * \param len The length in characters of the result buffer + * + * \return the size of the converted string in char (strlen), or -1 for failure + */ + WINPR_API SSIZE_T ConvertWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len); + + /** \brief Converts multistrings form UTF-16 to UTF-8 + * + * The function does string conversions of any input string of wlen characters. + * Any character in the buffer (incuding any '\0') is converted. + * + * Supplying len = 0 will return the required size of the buffer in characters. + * + * \warning Supplying a buffer length smaller than required will result in + * platform dependent (=undefined) behaviour! + * + * \param wstr A WCHAR string of \b wlen length + * \param wlen The (buffer) length in characters of \b wstr + * \param str A pointer to the result string + * \param len The length in characters of the result buffer + * + * \return the size of the converted string in CHAR characters (including any '\0'), or -1 for + * failure + */ + WINPR_API SSIZE_T ConvertMszWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len); + + /** \brief Converts form UTF-8 to UTF-16 + * + * The function does string conversions of any '\0' terminated input string + * + * Supplying len = 0 will return the required size of the buffer in characters. + * + * \warning Supplying a buffer length smaller than required will result in + * platform dependent (=undefined) behaviour! + * + * \param str A '\0' terminated CHAR string, may be NULL + * \param wstr A pointer to the result WCHAR string + * \param wlen The length in WCHAR characters of the result buffer + * + * \return the size of the converted string in WCHAR characters (wcslen), or -1 for failure + */ + WINPR_API SSIZE_T ConvertUtf8ToWChar(const char* str, WCHAR* wstr, size_t wlen); + + /** \brief Converts form UTF-8 to UTF-16 + * + * The function does string conversions of any input string of len (or less) + * characters until it reaches the first '\0'. + * + * Supplying len = 0 will return the required size of the buffer in characters. + * + * \warning Supplying a buffer length smaller than required will result in + * platform dependent (=undefined) behaviour! + * + * \param str A CHAR string of \b len length + * \param len The (buffer) length in characters of \b str + * \param wstr A pointer to the result WCHAR string + * \param wlen The length in WCHAR characters of the result buffer + * + * \return the size of the converted string in WCHAR characters (wcslen), or -1 for failure + */ + WINPR_API SSIZE_T ConvertUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen); + + /** \brief Converts multistrings form UTF-8 to UTF-16 + * + * The function does string conversions of any input string of len characters. + * Any character in the buffer (incuding any '\0') is converted. + * + * Supplying len = 0 will return the required size of the buffer in characters. + * + * \warning Supplying a buffer length smaller than required will result in + * platform dependent (=undefined) behaviour! + * + * \param str A CHAR string of \b len length + * \param len The (buffer) length in characters of \b str + * \param wstr A pointer to the result WCHAR string + * \param wlen The length in WCHAR characters of the result buffer + * + * \return the size of the converted string in WCHAR characters (including any '\0'), or -1 for + * failure + */ + WINPR_API SSIZE_T ConvertMszUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen); + + /** \brief Converts form UTF-16 to UTF-8, returns an allocated string + * + * The function does string conversions of any '\0' terminated input string + * + * \param wstr A '\0' terminated WCHAR string, may be NULL + * \param pSize Ignored if NULL, otherwise receives the length of the result string in + * characters (strlen) + * + * \return An allocated zero terminated UTF-8 string or NULL in case of failure. + */ + WINPR_API char* ConvertWCharToUtf8Alloc(const WCHAR* wstr, size_t* pSize); + + /** \brief Converts form UTF-16 to UTF-8, returns an allocated string + * + * The function does string conversions of any input string of wlen (or less) + * characters until it reaches the first '\0'. + * + * \param wstr A WCHAR string of \b wlen length + * \param wlen The (buffer) length in characters of \b wstr + * \param pSize Ignored if NULL, otherwise receives the length of the result string in + * characters (strlen) + * + * \return An allocated zero terminated UTF-8 string or NULL in case of failure. + */ + WINPR_API char* ConvertWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pSize); + + /** \brief Converts multistring form UTF-16 to UTF-8, returns an allocated string + * + * The function does string conversions of any input string of len characters. + * Any character in the buffer (incuding any '\0') is converted. + * + * \param wstr A WCHAR string of \b len character length + * \param wlen The (buffer) length in characters of \b str + * \param pSize Ignored if NULL, otherwise receives the length of the result string in + * characters (including any '\0' character) + * + * \return An allocated double zero terminated UTF-8 string or NULL in case of failure. + */ + WINPR_API char* ConvertMszWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pSize); + + /** \brief Converts form UTF-8 to UTF-16, returns an allocated string + * + * The function does string conversions of any '\0' terminated input string + * + * \param str A '\0' terminated CHAR string, may be NULL + * \param pSize Ignored if NULL, otherwise receives the length of the result string in + * characters (wcslen) + * + * \return An allocated zero terminated UTF-16 string or NULL in case of failure. + */ + WINPR_API WCHAR* ConvertUtf8ToWCharAlloc(const char* str, size_t* pSize); + + /** \brief Converts form UTF-8 to UTF-16, returns an allocated string + * + * The function does string conversions of any input string of len (or less) + * characters until it reaches the first '\0'. + * + * \param str A CHAR string of \b len length + * \param len The (buffer) length in characters of \b str + * \param pSize Ignored if NULL, otherwise receives the length of the result string in + * characters (wcslen) + * + * \return An allocated zero terminated UTF-16 string or NULL in case of failure. + */ + WINPR_API WCHAR* ConvertUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize); + + /** \brief Converts multistring form UTF-8 to UTF-16, returns an allocated string + * + * The function does string conversions of any input string of len characters. + * Any character in the buffer (incuding any '\0') is converted. + * + * \param str A CHAR string of \b len byte length + * \param len The (buffer) length in characters of \b str + * \param pSize Ignored if NULL, otherwise receives the length of the result string in + * characters (including any '\0' character) + * + * \return An allocated double zero terminated UTF-16 string or NULL in case of failure. + */ + WINPR_API WCHAR* ConvertMszUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize); + + /** \brief Helper function to initialize const WCHAR pointer from a Utf8 string + * + * \param str The Utf8 string to use for initialization + * \param buffer The WCHAR buffer used to store the converted data + * \param len The size of the buffer in number of WCHAR + * + * \return The WCHAR string (a pointer to buffer) + */ + WINPR_API const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len); + +#if defined(WITH_WINPR_DEPRECATED) + WINPR_API WINPR_DEPRECATED_VAR("Use ConvertUtf8ToWChar functions instead", + int ConvertToUnicode(UINT CodePage, DWORD dwFlags, + LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR* lpWideCharStr, int cchWideChar)); + + WINPR_API WINPR_DEPRECATED_VAR("Use ConvertWCharToUtf8 functions instead", + int ConvertFromUnicode(UINT CodePage, DWORD dwFlags, + LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR* lpMultiByteStr, int cbMultiByte, + LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar)); +#endif + + WINPR_API const WCHAR* ByteSwapUnicode(WCHAR* wstr, size_t length); + + WINPR_API size_t ConvertLineEndingToLF(char* str, size_t size); + WINPR_API char* ConvertLineEndingToCRLF(const char* str, size_t* size); + + WINPR_API char* StrSep(char** stringp, const char* delim); + + WINPR_API INT64 GetLine(char** lineptr, size_t* size, FILE* stream); + +#if !defined(WINPR_HAVE_STRNDUP) + WINPR_API char* strndup(const char* s, size_t n); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_CRT_STRING_H */ diff --git a/winpr/include/winpr/strlst.h b/winpr/include/winpr/strlst.h new file mode 100644 index 0000000..346946d --- /dev/null +++ b/winpr/include/winpr/strlst.h @@ -0,0 +1,41 @@ +/** + * String list Manipulation (UTILS) + * + * Copyright 2018 Pascal Bourguignon + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_STRLST_H +#define WINPR_UTILS_STRLST_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API void string_list_free(char** string_list); + WINPR_API int string_list_length(const char* const* string_list); + WINPR_API char** string_list_copy(const char* const* string_list); + WINPR_API void string_list_print(FILE* out, const char* const* string_list); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_UTILS_STRLST_H */ diff --git a/winpr/include/winpr/synch.h b/winpr/include/winpr/synch.h new file mode 100644 index 0000000..b310a3b --- /dev/null +++ b/winpr/include/winpr/synch.h @@ -0,0 +1,423 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SYNCH_H +#define WINPR_SYNCH_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _WIN32 + +/* Mutex */ +#define CREATE_MUTEX_INITIAL_OWNER 0x00000001 + + WINPR_API HANDLE CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, + LPCSTR lpName); + WINPR_API HANDLE CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, + LPCWSTR lpName); + + WINPR_API HANDLE CreateMutexExA(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCSTR lpName, + DWORD dwFlags, DWORD dwDesiredAccess); + WINPR_API HANDLE CreateMutexExW(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCWSTR lpName, + DWORD dwFlags, DWORD dwDesiredAccess); + + WINPR_API HANDLE OpenMutexA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName); + WINPR_API HANDLE OpenMutexW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName); + + WINPR_API BOOL ReleaseMutex(HANDLE hMutex); + +#ifdef UNICODE +#define CreateMutex CreateMutexW +#define CreateMutexEx CreateMutexExW +#define OpenMutex OpenMutexW +#else +#define CreateMutex CreateMutexA +#define CreateMutexEx CreateMutexExA +#define OpenMutex OpenMutexA +#endif + + /* Semaphore */ + + WINPR_API HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, + LONG lInitialCount, LONG lMaximumCount, LPCSTR lpName); + WINPR_API HANDLE CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, + LONG lInitialCount, LONG lMaximumCount, LPCWSTR lpName); + + WINPR_API HANDLE OpenSemaphoreA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName); + WINPR_API HANDLE OpenSemaphoreW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName); + +#ifdef UNICODE +#define CreateSemaphore CreateSemaphoreW +#define OpenSemaphore OpenSemaphoreW +#else +#define CreateSemaphore CreateSemaphoreA +#define OpenSemaphore OpenSemaphoreA +#endif + + WINPR_API BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount); + +/* Event */ +#define CREATE_EVENT_MANUAL_RESET 0x00000001 +#define CREATE_EVENT_INITIAL_SET 0x00000002 + + WINPR_API HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, LPCSTR lpName); + WINPR_API HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, LPCWSTR lpName); + + WINPR_API HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, + DWORD dwFlags, DWORD dwDesiredAccess); + WINPR_API HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, + DWORD dwFlags, DWORD dwDesiredAccess); + + WINPR_API HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName); + WINPR_API HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName); + + WINPR_API BOOL SetEvent(HANDLE hEvent); + WINPR_API BOOL ResetEvent(HANDLE hEvent); + +#if defined(WITH_DEBUG_EVENTS) +#define DumpEventHandles() DumpEventHandles_(__func__, __FILE__, __LINE__) + WINPR_API void DumpEventHandles_(const char* fkt, const char* file, size_t line); +#endif +#ifdef UNICODE +#define CreateEvent CreateEventW +#define CreateEventEx CreateEventExW +#define OpenEvent OpenEventW +#else +#define CreateEvent CreateEventA +#define CreateEventEx CreateEventExA +#define OpenEvent OpenEventA +#endif + + /* Condition Variable */ + + typedef PVOID RTL_CONDITION_VARIABLE; + typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE; + + /* Critical Section */ + + typedef struct + { + PVOID DebugInfo; + LONG LockCount; + LONG RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; + ULONG_PTR SpinCount; + } RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION; + + typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; + typedef PRTL_CRITICAL_SECTION PCRITICAL_SECTION; + typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION; + + WINPR_API VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); + WINPR_API BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, + DWORD dwSpinCount, DWORD Flags); + WINPR_API BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, + DWORD dwSpinCount); + + WINPR_API DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection, + DWORD dwSpinCount); + + WINPR_API VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); + WINPR_API BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); + + WINPR_API VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); + + WINPR_API VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); + + /* Sleep */ + + WINPR_API VOID Sleep(DWORD dwMilliseconds); + WINPR_API DWORD SleepEx(DWORD dwMilliseconds, BOOL bAlertable); + + /* Address */ + + WINPR_API VOID WakeByAddressAll(PVOID Address); + WINPR_API VOID WakeByAddressSingle(PVOID Address); + + WINPR_API BOOL WaitOnAddress(VOID volatile* Address, PVOID CompareAddress, SIZE_T AddressSize, + DWORD dwMilliseconds); + + /* Wait */ + +#define INFINITE 0xFFFFFFFFUL + +#define WAIT_OBJECT_0 0x00000000UL +#define WAIT_ABANDONED 0x00000080UL +#define WAIT_IO_COMPLETION 0x000000C0UL + +#ifndef WAIT_TIMEOUT +#define WAIT_TIMEOUT 0x00000102UL +#endif + +#define WAIT_FAILED 0xFFFFFFFFUL + +#define MAXIMUM_WAIT_OBJECTS 64 + + WINPR_API DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); + WINPR_API DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable); + WINPR_API DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, + DWORD dwMilliseconds); + WINPR_API DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, + DWORD dwMilliseconds, BOOL bAlertable); + + WINPR_API DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, + DWORD dwMilliseconds, BOOL bAlertable); + + /* Waitable Timer */ + +#define CREATE_WAITABLE_TIMER_MANUAL_RESET 0x00000001 + + typedef struct + { + ULONG Version; + DWORD Flags; + + union + { + struct + { + HMODULE LocalizedReasonModule; + ULONG LocalizedReasonId; + ULONG ReasonStringCount; + LPWSTR* ReasonStrings; + } Detailed; + + LPWSTR SimpleReasonString; + } Reason; + } REASON_CONTEXT, *PREASON_CONTEXT; + + typedef VOID (*PTIMERAPCROUTINE)(LPVOID lpArgToCompletionRoutine, DWORD dwTimerLowValue, + DWORD dwTimerHighValue); + + WINPR_API HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, + BOOL bManualReset, LPCSTR lpTimerName); + WINPR_API HANDLE CreateWaitableTimerW(LPSECURITY_ATTRIBUTES lpTimerAttributes, + BOOL bManualReset, LPCWSTR lpTimerName); + + WINPR_API HANDLE CreateWaitableTimerExA(LPSECURITY_ATTRIBUTES lpTimerAttributes, + LPCSTR lpTimerName, DWORD dwFlags, + DWORD dwDesiredAccess); + WINPR_API HANDLE CreateWaitableTimerExW(LPSECURITY_ATTRIBUTES lpTimerAttributes, + LPCWSTR lpTimerName, DWORD dwFlags, + DWORD dwDesiredAccess); + + WINPR_API BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod, + PTIMERAPCROUTINE pfnCompletionRoutine, + LPVOID lpArgToCompletionRoutine, BOOL fResume); + + WINPR_API BOOL SetWaitableTimerEx(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod, + PTIMERAPCROUTINE pfnCompletionRoutine, + LPVOID lpArgToCompletionRoutine, PREASON_CONTEXT WakeContext, + ULONG TolerableDelay); + + WINPR_API HANDLE OpenWaitableTimerA(DWORD dwDesiredAccess, BOOL bInheritHandle, + LPCSTR lpTimerName); + WINPR_API HANDLE OpenWaitableTimerW(DWORD dwDesiredAccess, BOOL bInheritHandle, + LPCWSTR lpTimerName); + + WINPR_API BOOL CancelWaitableTimer(HANDLE hTimer); + +#ifdef UNICODE +#define CreateWaitableTimer CreateWaitableTimerW +#define CreateWaitableTimerEx CreateWaitableTimerExW +#define OpenWaitableTimer OpenWaitableTimerW +#else +#define CreateWaitableTimer CreateWaitableTimerA +#define CreateWaitableTimerEx CreateWaitableTimerExA +#define OpenWaitableTimer OpenWaitableTimerA +#endif + + WINPR_API int GetTimerFileDescriptor(HANDLE hEvent); + + /** + * Timer-Queue Timer + */ + +#define WT_EXECUTEDEFAULT 0x00000000 +#define WT_EXECUTEINIOTHREAD 0x00000001 +#define WT_EXECUTEINUITHREAD 0x00000002 +#define WT_EXECUTEINWAITTHREAD 0x00000004 +#define WT_EXECUTEONLYONCE 0x00000008 +#define WT_EXECUTELONGFUNCTION 0x00000010 +#define WT_EXECUTEINTIMERTHREAD 0x00000020 +#define WT_EXECUTEINPERSISTENTIOTHREAD 0x00000040 +#define WT_EXECUTEINPERSISTENTTHREAD 0x00000080 +#define WT_TRANSFER_IMPERSONATION 0x00000100 + + typedef VOID (*WAITORTIMERCALLBACK)(PVOID lpParameter, BOOLEAN TimerOrWaitFired); + + WINPR_API HANDLE CreateTimerQueue(void); + WINPR_API BOOL DeleteTimerQueue(HANDLE TimerQueue); + WINPR_API BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent); + + WINPR_API BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, + WAITORTIMERCALLBACK Callback, PVOID Parameter, + DWORD DueTime, DWORD Period, ULONG Flags); + WINPR_API BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, + ULONG Period); + WINPR_API BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent); + +#endif + +#if (defined(_WIN32) && (_WIN32_WINNT < 0x0600)) +#define InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, Flags) \ + InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount) +#endif + + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifndef _RTL_RUN_ONCE_DEF +#define _RTL_RUN_ONCE_DEF + + WINPR_PRAGMA_DIAG_POP + +#define RTL_RUN_ONCE_INIT \ + { \ + 0 \ + } + +#define RTL_RUN_ONCE_CHECK_ONLY 0x00000001 +#define RTL_RUN_ONCE_ASYNC 0x00000002 +#define RTL_RUN_ONCE_INIT_FAILED 0x00000004 + +#define RTL_RUN_ONCE_CTX_RESERVED_BITS 2 + + typedef struct + { + PVOID Ptr; + } RTL_RUN_ONCE, *PRTL_RUN_ONCE; + + typedef ULONG CALLBACK RTL_RUN_ONCE_INIT_FN(PRTL_RUN_ONCE RunOnce, PVOID Parameter, + PVOID* Context); + typedef RTL_RUN_ONCE_INIT_FN* PRTL_RUN_ONCE_INIT_FN; + +#endif + +#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600)) + + /* One-Time Initialization */ + +#define INIT_ONCE_STATIC_INIT RTL_RUN_ONCE_INIT + + typedef RTL_RUN_ONCE INIT_ONCE; + typedef PRTL_RUN_ONCE PINIT_ONCE; + typedef PRTL_RUN_ONCE LPINIT_ONCE; + typedef BOOL(CALLBACK* PINIT_ONCE_FN)(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context); + + WINPR_API BOOL winpr_InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, + PBOOL fPending, LPVOID* lpContext); + WINPR_API BOOL winpr_InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext); + WINPR_API BOOL winpr_InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, + PVOID Parameter, LPVOID* Context); + WINPR_API VOID winpr_InitOnceInitialize(PINIT_ONCE InitOnce); + +#define InitOnceBeginInitialize winpr_InitOnceBeginInitialize +#define InitOnceComplete winpr_InitOnceComplete +#define InitOnceExecuteOnce winpr_InitOnceExecuteOnce +#define InitOnceInitialize winpr_InitOnceInitialize +#endif + + /* Synchronization Barrier */ + +#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0602) && !defined(_SYNCHAPI_H_)) +#define WINPR_SYNCHRONIZATION_BARRIER 1 +#endif + +#ifdef WINPR_SYNCHRONIZATION_BARRIER + + typedef struct + { + DWORD Reserved1; + DWORD Reserved2; + ULONG_PTR Reserved3[2]; + DWORD Reserved4; + DWORD Reserved5; + } RTL_BARRIER, *PRTL_BARRIER; + + typedef RTL_BARRIER SYNCHRONIZATION_BARRIER; + typedef PRTL_BARRIER PSYNCHRONIZATION_BARRIER; + typedef PRTL_BARRIER LPSYNCHRONIZATION_BARRIER; + +#define SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY 0x01 +#define SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY 0x02 +#define SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE 0x04 + + WINPR_API BOOL WINAPI winpr_InitializeSynchronizationBarrier( + LPSYNCHRONIZATION_BARRIER lpBarrier, LONG lTotalThreads, LONG lSpinCount); + WINPR_API BOOL WINAPI winpr_EnterSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier, + DWORD dwFlags); + WINPR_API BOOL WINAPI winpr_DeleteSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier); + +#define InitializeSynchronizationBarrier winpr_InitializeSynchronizationBarrier +#define EnterSynchronizationBarrier winpr_EnterSynchronizationBarrier +#define DeleteSynchronizationBarrier winpr_DeleteSynchronizationBarrier + +#endif + + /* Extended API */ + + WINPR_API VOID USleep(DWORD dwMicroseconds); + + WINPR_API HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, + BOOL bManualReset, BOOL bInitialState, + int FileDescriptor, ULONG mode); + WINPR_API HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, + BOOL bManualReset, BOOL bInitialState, + int FileDescriptor, ULONG mode); + + WINPR_API HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, + BOOL bManualReset, BOOL bInitialState, void* pObject); + +#ifdef UNICODE +#define CreateFileDescriptorEvent CreateFileDescriptorEventW +#else +#define CreateFileDescriptorEvent CreateFileDescriptorEventA +#endif + + WINPR_API int GetEventFileDescriptor(HANDLE hEvent); + WINPR_API int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode); + + WINPR_API void* GetEventWaitObject(HANDLE hEvent); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_SYNCH_H */ diff --git a/winpr/include/winpr/sysinfo.h b/winpr/include/winpr/sysinfo.h new file mode 100644 index 0000000..d7d6dd8 --- /dev/null +++ b/winpr/include/winpr/sysinfo.h @@ -0,0 +1,358 @@ +/** + * WinPR: Windows Portable Runtime + * System Information + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SYSINFO_H +#define WINPR_SYSINFO_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _WIN32 + +#define PROCESSOR_ARCHITECTURE_INTEL 0 +#define PROCESSOR_ARCHITECTURE_MIPS 1 +#define PROCESSOR_ARCHITECTURE_ALPHA 2 +#define PROCESSOR_ARCHITECTURE_PPC 3 +#define PROCESSOR_ARCHITECTURE_SHX 4 +#define PROCESSOR_ARCHITECTURE_ARM 5 +#define PROCESSOR_ARCHITECTURE_IA64 6 +#define PROCESSOR_ARCHITECTURE_ALPHA64 7 +#define PROCESSOR_ARCHITECTURE_MSIL 8 +#define PROCESSOR_ARCHITECTURE_AMD64 9 +#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 +#define PROCESSOR_ARCHITECTURE_NEUTRAL 11 +#define PROCESSOR_ARCHITECTURE_ARM64 12 +#define PROCESSOR_ARCHITECTURE_MIPS64 13 +#define PROCESSOR_ARCHITECTURE_E2K 14 +#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xFFFF + +#define PROCESSOR_INTEL_386 386 +#define PROCESSOR_INTEL_486 486 +#define PROCESSOR_INTEL_PENTIUM 586 +#define PROCESSOR_INTEL_IA64 2200 +#define PROCESSOR_AMD_X8664 8664 +#define PROCESSOR_MIPS_R4000 4000 +#define PROCESSOR_ALPHA_21064 21064 +#define PROCESSOR_PPC_601 601 +#define PROCESSOR_PPC_603 603 +#define PROCESSOR_PPC_604 604 +#define PROCESSOR_PPC_620 620 +#define PROCESSOR_HITACHI_SH3 10003 +#define PROCESSOR_HITACHI_SH3E 10004 +#define PROCESSOR_HITACHI_SH4 10005 +#define PROCESSOR_MOTOROLA_821 821 +#define PROCESSOR_SHx_SH3 103 +#define PROCESSOR_SHx_SH4 104 +#define PROCESSOR_STRONGARM 2577 +#define PROCESSOR_ARM720 1824 +#define PROCESSOR_ARM820 2080 +#define PROCESSOR_ARM920 2336 +#define PROCESSOR_ARM_7TDMI 70001 +#define PROCESSOR_OPTIL 0x494F + + typedef struct + { + union + { + DWORD dwOemId; + + struct + { + WORD wProcessorArchitecture; + WORD wReserved; + }; + }; + + DWORD dwPageSize; + LPVOID lpMinimumApplicationAddress; + LPVOID lpMaximumApplicationAddress; + DWORD_PTR dwActiveProcessorMask; + DWORD dwNumberOfProcessors; + DWORD dwProcessorType; + DWORD dwAllocationGranularity; + WORD wProcessorLevel; + WORD wProcessorRevision; + } SYSTEM_INFO, *LPSYSTEM_INFO; + + WINPR_API void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo); + WINPR_API void GetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo); + +#if defined(WITH_WINPR_DEPRECATED) + typedef struct + { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + CHAR szCSDVersion[128]; + } OSVERSIONINFOA, *POSVERSIONINFOA, *LPOSVERSIONINFOA; + + typedef struct + { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR szCSDVersion[128]; + } OSVERSIONINFOW, *POSVERSIONINFOW, *LPOSVERSIONINFOW; + + typedef struct + { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + CHAR szCSDVersion[128]; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; + } OSVERSIONINFOEXA, *POSVERSIONINFOEXA, *LPOSVERSIONINFOEXA; + + typedef struct + { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR szCSDVersion[128]; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; + } OSVERSIONINFOEXW, *POSVERSIONINFOEXW, *LPOSVERSIONINFOEXW; + +#ifdef UNICODE +#define OSVERSIONINFO OSVERSIONINFOW +#define OSVERSIONINFOEX OSVERSIONINFOEXW +#define POSVERSIONINFO POSVERSIONINFOW +#define POSVERSIONINFOEX POSVERSIONINFOEXW +#define LPOSVERSIONINFO LPOSVERSIONINFOW +#define LPOSVERSIONINFOEX LPOSVERSIONINFOEXW +#else +#define OSVERSIONINFO OSVERSIONINFOA +#define OSVERSIONINFOEX OSVERSIONINFOEXA +#define POSVERSIONINFO POSVERSIONINFOA +#define POSVERSIONINFOEX POSVERSIONINFOEXA +#define LPOSVERSIONINFO LPOSVERSIONINFOA +#define LPOSVERSIONINFOEX LPOSVERSIONINFOEXA +#endif + +#define VER_PLATFORM_WIN32_NT 0x00000002 + +#define VER_SUITE_BACKOFFICE 0x00000004 +#define VER_SUITE_BLADE 0x00000400 +#define VER_SUITE_COMPUTE_SERVER 0x00004000 +#define VER_SUITE_DATACENTER 0x00000080 +#define VER_SUITE_ENTERPRISE 0x00000002 +#define VER_SUITE_EMBEDDEDNT 0x00000040 +#define VER_SUITE_PERSONAL 0x00000200 +#define VER_SUITE_SINGLEUSERTS 0x00000100 +#define VER_SUITE_SMALLBUSINESS 0x00000001 +#define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x00000020 +#define VER_SUITE_STORAGE_SERVER 0x00002000 +#define VER_SUITE_TERMINAL 0x00000010 +#define VER_SUITE_WH_SERVER 0x00008000 +#endif + +#define VER_NT_DOMAIN_CONTROLLER 0x0000002 +#define VER_NT_SERVER 0x0000003 +#define VER_NT_WORKSTATION 0x0000001 + + WINPR_API void GetSystemTime(LPSYSTEMTIME lpSystemTime); + WINPR_API BOOL SetSystemTime(CONST SYSTEMTIME* lpSystemTime); + WINPR_API VOID GetLocalTime(LPSYSTEMTIME lpSystemTime); + WINPR_API BOOL SetLocalTime(CONST SYSTEMTIME* lpSystemTime); + + WINPR_API VOID GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime); + WINPR_API BOOL GetSystemTimeAdjustment(PDWORD lpTimeAdjustment, PDWORD lpTimeIncrement, + PBOOL lpTimeAdjustmentDisabled); + + WINPR_API BOOL IsProcessorFeaturePresent(DWORD ProcessorFeature); + +#define PF_FLOATING_POINT_PRECISION_ERRATA 0 +#define PF_FLOATING_POINT_EMULATED 1 +#define PF_COMPARE_EXCHANGE_DOUBLE 2 +#define PF_MMX_INSTRUCTIONS_AVAILABLE 3 +#define PF_PPC_MOVEMEM_64BIT_OK 4 +#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6 /* SSE */ +#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7 +#define PF_RDTSC_INSTRUCTION_AVAILABLE 8 +#define PF_PAE_ENABLED 9 +#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10 /* SSE2 */ +#define PF_SSE_DAZ_MODE_AVAILABLE 11 +#define PF_NX_ENABLED 12 +#define PF_SSE3_INSTRUCTIONS_AVAILABLE 13 +#define PF_COMPARE_EXCHANGE128 14 +#define PF_COMPARE64_EXCHANGE128 15 +#define PF_CHANNELS_ENABLED 16 +#define PF_XSAVE_ENABLED 17 +#define PF_ARM_VFP_32_REGISTERS_AVAILABLE 18 +#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19 +#define PF_SECOND_LEVEL_ADDRESS_TRANSLATION 20 +#define PF_VIRT_FIRMWARE_ENABLED 21 +#define PF_RDWRFSGSBASE_AVAILABLE 22 +#define PF_FASTFAIL_AVAILABLE 23 +#define PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE 24 +#define PF_ARM_64BIT_LOADSTORE_ATOMIC 25 +#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 26 +#define PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE 27 + +#define PF_ARM_V4 0x80000001 +#define PF_ARM_V5 0x80000002 +#define PF_ARM_V6 0x80000003 +#define PF_ARM_V7 0x80000004 +#define PF_ARM_THUMB 0x80000005 +#define PF_ARM_JAZELLE 0x80000006 +#define PF_ARM_DSP 0x80000007 +#define PF_ARM_MOVE_CP 0x80000008 +#define PF_ARM_VFP10 0x80000009 +#define PF_ARM_MPU 0x8000000A +#define PF_ARM_WRITE_BUFFER 0x8000000B +#define PF_ARM_MBX 0x8000000C +#define PF_ARM_L2CACHE 0x8000000D +#define PF_ARM_PHYSICALLY_TAGGED_CACHE 0x8000000E +#define PF_ARM_VFP_SINGLE_PRECISION 0x8000000F +#define PF_ARM_VFP_DOUBLE_PRECISION 0x80000010 +#define PF_ARM_ITCM 0x80000011 +#define PF_ARM_DTCM 0x80000012 +#define PF_ARM_UNIFIED_CACHE 0x80000013 +#define PF_ARM_WRITE_BACK_CACHE 0x80000014 +#define PF_ARM_CACHE_CAN_BE_LOCKED_DOWN 0x80000015 +#define PF_ARM_L2CACHE_MEMORY_MAPPED 0x80000016 +#define PF_ARM_L2CACHE_COPROC 0x80000017 +#define PF_ARM_THUMB2 0x80000018 +#define PF_ARM_T2EE 0x80000019 +#define PF_ARM_VFP3 0x8000001A +#define PF_ARM_NEON 0x8000001B +#define PF_ARM_UNALIGNED_ACCESS 0x8000001C + +#define PF_ARM_INTEL_XSCALE 0x80010001 +#define PF_ARM_INTEL_PMU 0x80010002 +#define PF_ARM_INTEL_WMMX 0x80010003 + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +#if defined(WITH_WINPR_DEPRECATED) + WINPR_API BOOL GetVersionExA(LPOSVERSIONINFOA lpVersionInformation); + WINPR_API BOOL GetVersionExW(LPOSVERSIONINFOW lpVersionInformation); + +#ifdef UNICODE +#define GetVersionEx GetVersionExW +#else +#define GetVersionEx GetVersionExA +#endif + +#endif +#endif + +#if !defined(_WIN32) || defined(_UWP) + + WINPR_API DWORD GetTickCount(void); + + typedef enum + { + ComputerNameNetBIOS, + ComputerNameDnsHostname, + ComputerNameDnsDomain, + ComputerNameDnsFullyQualified, + ComputerNamePhysicalNetBIOS, + ComputerNamePhysicalDnsHostname, + ComputerNamePhysicalDnsDomain, + ComputerNamePhysicalDnsFullyQualified, + ComputerNameMax + } COMPUTER_NAME_FORMAT; + +#define MAX_COMPUTERNAME_LENGTH 31 + + WINPR_API BOOL GetComputerNameA(LPSTR lpBuffer, LPDWORD lpnSize); + WINPR_API BOOL GetComputerNameW(LPWSTR lpBuffer, LPDWORD lpnSize); + + WINPR_API BOOL GetComputerNameExA(COMPUTER_NAME_FORMAT NameType, LPSTR lpBuffer, + LPDWORD lpnSize); + WINPR_API BOOL GetComputerNameExW(COMPUTER_NAME_FORMAT NameType, LPWSTR lpBuffer, + LPDWORD lpnSize); + +#ifdef UNICODE +#define GetComputerName GetComputerNameW +#define GetComputerNameEx GetComputerNameExW +#else +#define GetComputerName GetComputerNameA +#define GetComputerNameEx GetComputerNameExA +#endif + +#endif + +#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600)) + + WINPR_API ULONGLONG winpr_GetTickCount64(void); +#define GetTickCount64 winpr_GetTickCount64 + +#endif + + WINPR_API DWORD GetTickCountPrecise(void); + + WINPR_API BOOL IsProcessorFeaturePresentEx(DWORD ProcessorFeature); + +/* extended flags */ +#define PF_EX_LZCNT 1 +#define PF_EX_3DNOW_PREFETCH 2 +#define PF_EX_SSSE3 3 +#define PF_EX_SSE41 4 +#define PF_EX_SSE42 5 +#define PF_EX_AVX 6 +#define PF_EX_FMA 7 +#define PF_EX_AVX_AES 8 +#define PF_EX_AVX2 9 +#define PF_EX_ARM_VFP1 10 +#define PF_EX_ARM_VFP3D16 11 +#define PF_EX_ARM_VFP4 12 +#define PF_EX_ARM_IDIVA 13 +#define PF_EX_ARM_IDIVT 14 +#define PF_EX_AVX_PCLMULQDQ 15 +#define PF_EX_AVX512F 16 + +/* + * some "aliases" for the standard defines + * to be more clear + */ +#define PF_SSE_INSTRUCTIONS_AVAILABLE PF_XMMI_INSTRUCTIONS_AVAILABLE +#define PF_SSE2_INSTRUCTIONS_AVAILABLE PF_XMMI64_INSTRUCTIONS_AVAILABLE + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_SYSINFO_H */ diff --git a/winpr/include/winpr/tchar.h b/winpr/include/winpr/tchar.h new file mode 100644 index 0000000..5128352 --- /dev/null +++ b/winpr/include/winpr/tchar.h @@ -0,0 +1,70 @@ +/** + * WinPR: Windows Portable Runtime + * TCHAR + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_TCHAR_H +#define WINPR_TCHAR_H + +#include +#include + +#ifdef _WIN32 + +#include + +#else + +#ifdef UNICODE +typedef WCHAR TCHAR; +#else +typedef CHAR TCHAR; +#endif + +#ifdef UNICODE +#define _tprintf wprintf +#define _sntprintf snwprintf +#define _tcslen _wcslen +#define _tcsdup _wcsdup +#define _tcscmp wcscmp +#define _tcsncmp wcsncmp +#define _tcscpy wcscpy +#define _tcscat wcscat +#define _tcschr wcschr +#define _tcsrchr wcsrchr +#define _tcsstr wcsstr +#define _stprintf_s swprintf_s +#define _tcsnccmp wcsncmp +#else +#define _tprintf printf +#define _sntprintf snprintf +#define _tcslen strlen +#define _tcsdup _strdup +#define _tcscmp strcmp +#define _tcsncmp strncmp +#define _tcscpy strcpy +#define _tcscat strcat +#define _tcschr strchr +#define _tcsrchr strrchr +#define _tcsstr strstr +#define _stprintf_s sprintf_s +#define _tcsnccmp strncmp +#endif + +#endif + +#endif /* WINPR_TCHAR_H */ diff --git a/winpr/include/winpr/thread.h b/winpr/include/winpr/thread.h new file mode 100644 index 0000000..2f17603 --- /dev/null +++ b/winpr/include/winpr/thread.h @@ -0,0 +1,257 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_THREAD_H +#define WINPR_THREAD_H + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _WIN32 + + typedef struct + { + DWORD cb; + LPSTR lpReserved; + LPSTR lpDesktop; + LPSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + LPBYTE lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + } STARTUPINFOA, *LPSTARTUPINFOA; + + typedef struct + { + DWORD cb; + LPWSTR lpReserved; + LPWSTR lpDesktop; + LPWSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + LPBYTE lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + } STARTUPINFOW, *LPSTARTUPINFOW; + +#ifdef UNICODE + typedef STARTUPINFOW STARTUPINFO; + typedef LPSTARTUPINFOW LPSTARTUPINFO; +#else + typedef STARTUPINFOA STARTUPINFO; + typedef LPSTARTUPINFOA LPSTARTUPINFO; +#endif + +#define STARTF_USESHOWWINDOW 0x00000001 +#define STARTF_USESIZE 0x00000002 +#define STARTF_USEPOSITION 0x00000004 +#define STARTF_USECOUNTCHARS 0x00000008 +#define STARTF_USEFILLATTRIBUTE 0x00000010 +#define STARTF_RUNFULLSCREEN 0x00000020 +#define STARTF_FORCEONFEEDBACK 0x00000040 +#define STARTF_FORCEOFFFEEDBACK 0x00000080 +#define STARTF_USESTDHANDLES 0x00000100 +#define STARTF_USEHOTKEY 0x00000200 +#define STARTF_TITLEISLINKNAME 0x00000800 +#define STARTF_TITLEISAPPID 0x00001000 +#define STARTF_PREVENTPINNING 0x00002000 + + /* Process */ + +#define LOGON_WITH_PROFILE 0x00000001 +#define LOGON_NETCREDENTIALS_ONLY 0x00000002 +#define LOGON_ZERO_PASSWORD_BUFFER 0x80000000 + + WINPR_API BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, + LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessAsUserW(HANDLE hToken, LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessWithLogonA(LPCSTR lpUsername, LPCSTR lpDomain, LPCSTR lpPassword, + DWORD dwLogonFlags, LPCSTR lpApplicationName, + LPSTR lpCommandLine, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessWithLogonW(LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword, + DWORD dwLogonFlags, LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessWithTokenA(HANDLE hToken, DWORD dwLogonFlags, + LPCSTR lpApplicationName, LPSTR lpCommandLine, + DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + + WINPR_API BOOL CreateProcessWithTokenW(HANDLE hToken, DWORD dwLogonFlags, + LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + +#ifdef UNICODE +#define CreateProcess CreateProcessW +#define CreateProcessAsUser CreateProcessAsUserW +#define CreateProcessWithLogon CreateProcessWithLogonW +#define CreateProcessWithToken CreateProcessWithTokenW +#else +#define CreateProcess CreateProcessA +#define CreateProcessAsUser CreateProcessAsUserA +#define CreateProcessWithLogon CreateProcessWithLogonA +#define CreateProcessWithToken CreateProcessWithTokenA +#endif + + DECLSPEC_NORETURN WINPR_API VOID ExitProcess(UINT uExitCode); + WINPR_API BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode); + + WINPR_API HANDLE _GetCurrentProcess(void); + WINPR_API DWORD GetCurrentProcessId(void); + + WINPR_API BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode); + + /* Process Argument Vector Parsing */ + + WINPR_API LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs); + +#ifdef UNICODE +#define CommandLineToArgv CommandLineToArgvW +#else +#define CommandLineToArgv CommandLineToArgvA +#endif + + /* Thread */ + +#define CREATE_SUSPENDED 0x00000004 +#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 + + WINPR_API HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, + DWORD dwCreationFlags, LPDWORD lpThreadId); + + WINPR_API HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, + SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId); + + WINPR_API VOID ExitThread(DWORD dwExitCode); + WINPR_API BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode); + + WINPR_API HANDLE _GetCurrentThread(void); + WINPR_API DWORD GetCurrentThreadId(void); + + typedef void (*PAPCFUNC)(ULONG_PTR Parameter); + WINPR_API DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData); + + WINPR_API DWORD ResumeThread(HANDLE hThread); + WINPR_API DWORD SuspendThread(HANDLE hThread); + WINPR_API BOOL SwitchToThread(void); + + WINPR_API BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode); + + /* Processor */ + + WINPR_API DWORD GetCurrentProcessorNumber(void); + + /* Thread-Local Storage */ + +#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) + + WINPR_API DWORD TlsAlloc(void); + WINPR_API LPVOID TlsGetValue(DWORD dwTlsIndex); + WINPR_API BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue); + WINPR_API BOOL TlsFree(DWORD dwTlsIndex); + +#else + +/* + * GetCurrentProcess / GetCurrentThread cause a conflict on Mac OS X + */ +#define _GetCurrentProcess GetCurrentProcess +#define _GetCurrentThread GetCurrentThread + +#endif + + /* CommandLineToArgvA is not present in the original Windows API, WinPR always exports it */ + + WINPR_API LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs); + WINPR_API VOID DumpThreadHandles(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_THREAD_H */ diff --git a/winpr/include/winpr/timezone.h b/winpr/include/winpr/timezone.h new file mode 100644 index 0000000..d1c2f3e --- /dev/null +++ b/winpr/include/winpr/timezone.h @@ -0,0 +1,115 @@ +/** + * WinPR: Windows Portable Runtime + * Time Zone + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_TIMEZONE_H +#define WINPR_TIMEZONE_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _WIN32 + + typedef struct + { + LONG Bias; + WCHAR StandardName[32]; + SYSTEMTIME StandardDate; + LONG StandardBias; + WCHAR DaylightName[32]; + SYSTEMTIME DaylightDate; + LONG DaylightBias; + } TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION, *LPTIME_ZONE_INFORMATION; + + typedef struct + { + LONG Bias; + WCHAR StandardName[32]; + SYSTEMTIME StandardDate; + LONG StandardBias; + WCHAR DaylightName[32]; + SYSTEMTIME DaylightDate; + LONG DaylightBias; + WCHAR TimeZoneKeyName[128]; + BOOLEAN DynamicDaylightTimeDisabled; + } DYNAMIC_TIME_ZONE_INFORMATION, *PDYNAMIC_TIME_ZONE_INFORMATION, + *LPDYNAMIC_TIME_ZONE_INFORMATION; + + WINPR_API DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation); + WINPR_API BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation); + WINPR_API BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime); + WINPR_API BOOL FileTimeToSystemTime(const FILETIME* lpFileTime, LPSYSTEMTIME lpSystemTime); + WINPR_API BOOL SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTimeZone, + LPSYSTEMTIME lpUniversalTime, + LPSYSTEMTIME lpLocalTime); + WINPR_API BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation, + LPSYSTEMTIME lpLocalTime, + LPSYSTEMTIME lpUniversalTime); + +#endif + +/* + * GetDynamicTimeZoneInformation is provided by the SDK if _WIN32_WINNT >= 0x0600 in SDKs above 7.1A + * and incorrectly if _WIN32_WINNT >= 0x0501 in older SDKs + */ +#if !defined(_WIN32) || \ + (defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \ + !defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */ + + WINPR_API DWORD + GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation); + WINPR_API BOOL + SetDynamicTimeZoneInformation(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation); + WINPR_API BOOL GetTimeZoneInformationForYear(USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, + LPTIME_ZONE_INFORMATION ptzi); + +#endif + +#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0601)) /* Windows 7 */ + + WINPR_API BOOL + SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, + const SYSTEMTIME* lpUniversalTime, LPSYSTEMTIME lpLocalTime); + WINPR_API BOOL + TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, + const SYSTEMTIME* lpLocalTime, LPSYSTEMTIME lpUniversalTime); + +#endif + +#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0602)) /* Windows 8 */ + + WINPR_API DWORD EnumDynamicTimeZoneInformation( + const DWORD dwIndex, PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation); + WINPR_API DWORD GetDynamicTimeZoneInformationEffectiveYears( + const PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, LPDWORD FirstYear, + LPDWORD LastYear); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_TIMEZONE_H */ diff --git a/winpr/include/winpr/tools/makecert.h b/winpr/include/winpr/tools/makecert.h new file mode 100644 index 0000000..8a6d30f --- /dev/null +++ b/winpr/include/winpr/tools/makecert.h @@ -0,0 +1,50 @@ +/** + * WinPR: Windows Portable Runtime + * makecert replacement + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MAKECERT_TOOL_H +#define MAKECERT_TOOL_H + +#include +#include +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct S_MAKECERT_CONTEXT MAKECERT_CONTEXT; + + WINPR_API int makecert_context_process(MAKECERT_CONTEXT* context, int argc, char** argv); + + WINPR_API int makecert_context_set_output_file_name(MAKECERT_CONTEXT* context, + const char* name); + WINPR_API int makecert_context_output_certificate_file(MAKECERT_CONTEXT* context, + const char* path); + WINPR_API int makecert_context_output_private_key_file(MAKECERT_CONTEXT* context, + const char* path); + + WINPR_API void makecert_context_free(MAKECERT_CONTEXT* context); + + WINPR_ATTR_MALLOC(makecert_context_free, 1) + WINPR_API MAKECERT_CONTEXT* makecert_context_new(void); + +#ifdef __cplusplus +} +#endif + +#endif /* MAKECERT_TOOL_H */ diff --git a/winpr/include/winpr/user.h b/winpr/include/winpr/user.h new file mode 100644 index 0000000..d819f43 --- /dev/null +++ b/winpr/include/winpr/user.h @@ -0,0 +1,296 @@ +/** + * WinPR: Windows Portable Runtime + * User Environment + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2015 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_USER_H +#define WINPR_USER_H + +#include + +/** + * Standard Clipboard Formats + */ + +#ifndef _WIN32 + +#define MB_OK 0x00000000L +#define MB_OKCANCEL 0x00000001L +#define MB_ABORTRETRYIGNORE 0x00000002L +#define MB_YESNOCANCEL 0x00000003L +#define MB_YESNO 0x00000004L +#define MB_RETRYCANCEL 0x00000005L +#define MB_CANCELTRYCONTINUE 0x00000006L + +#define IDOK 1 +#define IDCANCEL 2 +#define IDABORT 3 +#define IDRETRY 4 +#define IDIGNORE 5 +#define IDYES 6 +#define IDNO 7 +#define IDTRYAGAIN 10 +#define IDCONTINUE 11 +#define IDTIMEOUT 32000 +#define IDASYNC 32001 + +#define CF_RAW 0 +#define CF_TEXT 1 +#define CF_BITMAP 2 +#define CF_METAFILEPICT 3 +#define CF_SYLK 4 +#define CF_DIF 5 +#define CF_TIFF 6 +#define CF_OEMTEXT 7 +#define CF_DIB 8 +#define CF_PALETTE 9 +#define CF_PENDATA 10 +#define CF_RIFF 11 +#define CF_WAVE 12 +#define CF_UNICODETEXT 13 +#define CF_ENHMETAFILE 14 +#define CF_HDROP 15 +#define CF_LOCALE 16 +#define CF_DIBV5 17 +#define CF_MAX 18 + +#define CF_OWNERDISPLAY 0x0080 +#define CF_DSPTEXT 0x0081 +#define CF_DSPBITMAP 0x0082 +#define CF_DSPMETAFILEPICT 0x0083 +#define CF_DSPENHMETAFILE 0x008E + +#define CF_PRIVATEFIRST 0x0200 +#define CF_PRIVATELAST 0x02FF + +#define CF_GDIOBJFIRST 0x0300 +#define CF_GDIOBJLAST 0x03FF + +/* Windows Metafile Picture Format */ + +#define MM_TEXT 1 +#define MM_LOMETRIC 2 +#define MM_HIMETRIC 3 +#define MM_LOENGLISH 4 +#define MM_HIENGLISH 5 +#define MM_TWIPS 6 +#define MM_ISOTROPIC 7 +#define MM_ANISOTROPIC 8 + +#define MM_MIN MM_TEXT +#define MM_MAX MM_ANISOTROPIC +#define MM_MAX_FIXEDSCALE MM_TWIPS + +#endif + +/** + * Bitmap Definitions + */ + +#if !defined(_WIN32) + +#pragma pack(push, 1) + +typedef LONG FXPT16DOT16, FAR *LPFXPT16DOT16; +typedef LONG FXPT2DOT30, FAR *LPFXPT2DOT30; + +typedef struct tagCIEXYZ +{ + FXPT2DOT30 ciexyzX; + FXPT2DOT30 ciexyzY; + FXPT2DOT30 ciexyzZ; +} CIEXYZ; + +typedef CIEXYZ FAR* LPCIEXYZ; + +typedef struct tagICEXYZTRIPLE +{ + CIEXYZ ciexyzRed; + CIEXYZ ciexyzGreen; + CIEXYZ ciexyzBlue; +} CIEXYZTRIPLE; + +typedef CIEXYZTRIPLE FAR* LPCIEXYZTRIPLE; + +typedef struct tagBITMAP +{ + LONG bmType; + LONG bmWidth; + LONG bmHeight; + LONG bmWidthBytes; + WORD bmPlanes; + WORD bmBitsPixel; + LPVOID bmBits; +} BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP; + +typedef struct tagRGBTRIPLE +{ + BYTE rgbtBlue; + BYTE rgbtGreen; + BYTE rgbtRed; +} RGBTRIPLE, *PRGBTRIPLE, NEAR *NPRGBTRIPLE, FAR *LPRGBTRIPLE; + +typedef struct tagRGBQUAD +{ + BYTE rgbBlue; + BYTE rgbGreen; + BYTE rgbRed; + BYTE rgbReserved; +} RGBQUAD; + +typedef RGBQUAD FAR* LPRGBQUAD; + +#define BI_RGB 0 +#define BI_RLE8 1 +#define BI_RLE4 2 +#define BI_BITFIELDS 3 +#define BI_JPEG 4 +#define BI_PNG 5 + +#define PROFILE_LINKED 'LINK' +#define PROFILE_EMBEDDED 'MBED' + +typedef struct tagBITMAPINFOHEADER +{ + DWORD biSize; + LONG biWidth; + LONG biHeight; + WORD biPlanes; + WORD biBitCount; + DWORD biCompression; + DWORD biSizeImage; + LONG biXPelsPerMeter; + LONG biYPelsPerMeter; + DWORD biClrUsed; + DWORD biClrImportant; +} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER; + +typedef struct +{ + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[1]; +} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO; + +typedef enum +{ + ORIENTATION_PREFERENCE_NONE = 0x0, + ORIENTATION_PREFERENCE_LANDSCAPE = 0x1, + + ORIENTATION_PREFERENCE_PORTRAIT = 0x2, + ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4, + ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8 +} ORIENTATION_PREFERENCE; + +#pragma pack(pop) + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +#pragma pack(push, 1) + +typedef struct tagBITMAPCOREHEADER +{ + DWORD bcSize; + WORD bcWidth; + WORD bcHeight; + WORD bcPlanes; + WORD bcBitCount; +} BITMAPCOREHEADER, FAR *LPBITMAPCOREHEADER, *PBITMAPCOREHEADER; + +typedef struct +{ + DWORD bV4Size; + LONG bV4Width; + LONG bV4Height; + WORD bV4Planes; + WORD bV4BitCount; + DWORD bV4V4Compression; + DWORD bV4SizeImage; + LONG bV4XPelsPerMeter; + LONG bV4YPelsPerMeter; + DWORD bV4ClrUsed; + DWORD bV4ClrImportant; + DWORD bV4RedMask; + DWORD bV4GreenMask; + DWORD bV4BlueMask; + DWORD bV4AlphaMask; + DWORD bV4CSType; + CIEXYZTRIPLE bV4Endpoints; + DWORD bV4GammaRed; + DWORD bV4GammaGreen; + DWORD bV4GammaBlue; +} BITMAPV4HEADER, FAR *LPBITMAPV4HEADER, *PBITMAPV4HEADER; + +typedef struct +{ + DWORD bV5Size; + LONG bV5Width; + LONG bV5Height; + WORD bV5Planes; + WORD bV5BitCount; + DWORD bV5Compression; + DWORD bV5SizeImage; + LONG bV5XPelsPerMeter; + LONG bV5YPelsPerMeter; + DWORD bV5ClrUsed; + DWORD bV5ClrImportant; + DWORD bV5RedMask; + DWORD bV5GreenMask; + DWORD bV5BlueMask; + DWORD bV5AlphaMask; + DWORD bV5CSType; + CIEXYZTRIPLE bV5Endpoints; + DWORD bV5GammaRed; + DWORD bV5GammaGreen; + DWORD bV5GammaBlue; + DWORD bV5Intent; + DWORD bV5ProfileData; + DWORD bV5ProfileSize; + DWORD bV5Reserved; +} BITMAPV5HEADER, FAR *LPBITMAPV5HEADER, *PBITMAPV5HEADER; + +typedef struct tagBITMAPCOREINFO +{ + BITMAPCOREHEADER bmciHeader; + RGBTRIPLE bmciColors[1]; +} BITMAPCOREINFO, FAR *LPBITMAPCOREINFO, *PBITMAPCOREINFO; + +typedef struct tagBITMAPFILEHEADER +{ + WORD bfType; + DWORD bfSize; + WORD bfReserved1; + WORD bfReserved2; + DWORD bfOffBits; +} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER; + +#pragma pack(pop) + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_USER_H */ diff --git a/winpr/include/winpr/wincrypt.h b/winpr/include/winpr/wincrypt.h new file mode 100644 index 0000000..cda3131 --- /dev/null +++ b/winpr/include/winpr/wincrypt.h @@ -0,0 +1,738 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WINCRYPT_H +#define WINPR_WINCRYPT_H + +#include +#include + +#include + +#ifdef _WIN32 + +#include + +#endif + +#ifndef ALG_TYPE_RESERVED7 +#define ALG_TYPE_RESERVED7 (7 << 9) +#endif + +#if !defined(NTDDI_VERSION) || (NTDDI_VERSION <= 0x05010200) +#define ALG_SID_SHA_256 12 +#define ALG_SID_SHA_384 13 +#define ALG_SID_SHA_512 14 +#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) +#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) +#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) +#endif + +#ifndef _WIN32 + +/* ncrypt.h */ + +typedef ULONG_PTR NCRYPT_HANDLE; +typedef ULONG_PTR NCRYPT_PROV_HANDLE; +typedef ULONG_PTR NCRYPT_KEY_HANDLE; +typedef ULONG_PTR NCRYPT_HASH_HANDLE; +typedef ULONG_PTR NCRYPT_SECRET_HANDLE; + +/* wincrypt.h */ + +#define GET_ALG_CLASS(x) (x & (7 << 13)) +#define GET_ALG_TYPE(x) (x & (15 << 9)) +#define GET_ALG_SID(x) (x & (511)) + +#define ALG_CLASS_ANY (0) +#define ALG_CLASS_SIGNATURE (1 << 13) +#define ALG_CLASS_MSG_ENCRYPT (2 << 13) +#define ALG_CLASS_DATA_ENCRYPT (3 << 13) +#define ALG_CLASS_HASH (4 << 13) +#define ALG_CLASS_KEY_EXCHANGE (5 << 13) +#define ALG_CLASS_ALL (7 << 13) + +#define ALG_TYPE_ANY (0) +#define ALG_TYPE_DSS (1 << 9) +#define ALG_TYPE_RSA (2 << 9) +#define ALG_TYPE_BLOCK (3 << 9) +#define ALG_TYPE_STREAM (4 << 9) +#define ALG_TYPE_DH (5 << 9) +#define ALG_TYPE_SECURECHANNEL (6 << 9) + +#define ALG_SID_ANY (0) + +#define ALG_SID_RSA_ANY 0 +#define ALG_SID_RSA_PKCS 1 +#define ALG_SID_RSA_MSATWORK 2 +#define ALG_SID_RSA_ENTRUST 3 +#define ALG_SID_RSA_PGP 4 + +#define ALG_SID_DSS_ANY 0 +#define ALG_SID_DSS_PKCS 1 +#define ALG_SID_DSS_DMS 2 + +#define ALG_SID_DES 1 +#define ALG_SID_3DES 3 +#define ALG_SID_DESX 4 +#define ALG_SID_IDEA 5 +#define ALG_SID_CAST 6 +#define ALG_SID_SAFERSK64 7 +#define ALG_SID_SAFERSK128 8 +#define ALG_SID_3DES_112 9 +#define ALG_SID_CYLINK_MEK 12 +#define ALG_SID_RC5 13 + +#define ALG_SID_AES_128 14 +#define ALG_SID_AES_192 15 +#define ALG_SID_AES_256 16 +#define ALG_SID_AES 17 + +#define ALG_SID_SKIPJACK 10 +#define ALG_SID_TEK 11 + +#define CRYPT_MODE_CBCI 6 +#define CRYPT_MODE_CFBP 7 +#define CRYPT_MODE_OFBP 8 +#define CRYPT_MODE_CBCOFM 9 +#define CRYPT_MODE_CBCOFMI 10 + +#define ALG_SID_RC2 2 + +#define ALG_SID_RC4 1 +#define ALG_SID_SEAL 2 + +#define ALG_SID_DH_SANDF 1 +#define ALG_SID_DH_EPHEM 2 +#define ALG_SID_AGREED_KEY_ANY 3 +#define ALG_SID_KEA 4 + +#define ALG_SID_ECDH 5 + +#define ALG_SID_MD2 1 +#define ALG_SID_MD4 2 +#define ALG_SID_MD5 3 +#define ALG_SID_SHA 4 +#define ALG_SID_SHA1 4 +#define ALG_SID_MAC 5 +#define ALG_SID_RIPEMD 6 +#define ALG_SID_RIPEMD160 7 +#define ALG_SID_SSL3SHAMD5 8 +#define ALG_SID_HMAC 9 +#define ALG_SID_TLS1PRF 10 + +#define ALG_SID_HASH_REPLACE_OWF 11 + +#define ALG_SID_SHA_256 12 +#define ALG_SID_SHA_384 13 +#define ALG_SID_SHA_512 14 + +#define ALG_SID_SSL3_MASTER 1 +#define ALG_SID_SCHANNEL_MASTER_HASH 2 +#define ALG_SID_SCHANNEL_MAC_KEY 3 +#define ALG_SID_PCT1_MASTER 4 +#define ALG_SID_SSL2_MASTER 5 +#define ALG_SID_TLS1_MASTER 6 +#define ALG_SID_SCHANNEL_ENC_KEY 7 + +#define ALG_SID_ECMQV 1 + +#define CALG_MD2 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2) +#define CALG_MD4 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4) +#define CALG_MD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5) +#define CALG_SHA (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA) +#define CALG_SHA1 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1) +#define CALG_MAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MAC) +#define CALG_RSA_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) +#define CALG_DSS_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY) + +#define CALG_NO_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_ANY | ALG_SID_ANY) + +#define CALG_RSA_KEYX (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) +#define CALG_DES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DES) +#define CALG_3DES_112 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES_112) +#define CALG_3DES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES) +#define CALG_DESX (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DESX) +#define CALG_RC2 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC2) +#define CALG_RC4 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_RC4) +#define CALG_SEAL (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_SEAL) +#define CALG_DH_SF (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_DH_SANDF) +#define CALG_DH_EPHEM (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_DH_EPHEM) +#define CALG_AGREEDKEY_ANY (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_AGREED_KEY_ANY) +#define CALG_KEA_KEYX (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_KEA) +#define CALG_HUGHES_MD5 (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_MD5) +#define CALG_SKIPJACK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_SKIPJACK) +#define CALG_TEK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_TEK) +#define CALG_CYLINK_MEK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_CYLINK_MEK) +#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) +#define CALG_SSL3_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SSL3_MASTER) +#define CALG_SCHANNEL_MASTER_HASH \ + (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_MASTER_HASH) +#define CALG_SCHANNEL_MAC_KEY \ + (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_MAC_KEY) +#define CALG_SCHANNEL_ENC_KEY \ + (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_ENC_KEY) +#define CALG_PCT1_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_PCT1_MASTER) +#define CALG_SSL2_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SSL2_MASTER) +#define CALG_TLS1_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_TLS1_MASTER) +#define CALG_RC5 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC5) +#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) +#define CALG_TLS1PRF (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF) + +#define CALG_HASH_REPLACE_OWF (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HASH_REPLACE_OWF) +#define CALG_AES_128 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_128) +#define CALG_AES_192 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_192) +#define CALG_AES_256 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256) +#define CALG_AES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES) + +#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) +#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) +#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) + +#define CALG_ECDH (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_ECDH) +#define CALG_ECMQV (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_ECMQV) + +typedef struct +{ + DWORD cbData; + BYTE* pbData; +} CRYPT_INTEGER_BLOB, *PCRYPT_INTEGER_BLOB, CRYPT_UINT_BLOB, *PCRYPT_UINT_BLOB, CRYPT_OBJID_BLOB, + *PCRYPT_OBJID_BLOB, CERT_NAME_BLOB, *PCERT_NAME_BLOB, CERT_RDN_VALUE_BLOB, + *PCERT_RDN_VALUE_BLOB, CERT_BLOB, *PCERT_BLOB, CRL_BLOB, *PCRL_BLOB, DATA_BLOB, *PDATA_BLOB, + CRYPT_DATA_BLOB, *PCRYPT_DATA_BLOB, CRYPT_HASH_BLOB, *PCRYPT_HASH_BLOB, CRYPT_DIGEST_BLOB, + *PCRYPT_DIGEST_BLOB, CRYPT_DER_BLOB, *PCRYPT_DER_BLOB, CRYPT_ATTR_BLOB, *PCRYPT_ATTR_BLOB; + +typedef struct +{ + LPSTR pszObjId; + CRYPT_OBJID_BLOB Parameters; +} CRYPT_ALGORITHM_IDENTIFIER, *PCRYPT_ALGORITHM_IDENTIFIER; + +typedef struct +{ + DWORD cbData; + BYTE* pbData; + DWORD cUnusedBits; +} CRYPT_BIT_BLOB, *PCRYPT_BIT_BLOB; + +typedef struct +{ + CRYPT_ALGORITHM_IDENTIFIER Algorithm; + CRYPT_BIT_BLOB PublicKey; +} CERT_PUBLIC_KEY_INFO, *PCERT_PUBLIC_KEY_INFO; + +typedef struct +{ + LPSTR pszObjId; + BOOL fCritical; + CRYPT_OBJID_BLOB Value; +} CERT_EXTENSION, *PCERT_EXTENSION; +typedef const CERT_EXTENSION* PCCERT_EXTENSION; + +typedef struct +{ + DWORD dwVersion; + CRYPT_INTEGER_BLOB SerialNumber; + CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + CERT_NAME_BLOB Issuer; + FILETIME NotBefore; + FILETIME NotAfter; + CERT_NAME_BLOB Subject; + CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo; + CRYPT_BIT_BLOB IssuerUniqueId; + CRYPT_BIT_BLOB SubjectUniqueId; + DWORD cExtension; + PCERT_EXTENSION rgExtension; +} CERT_INFO, *PCERT_INFO; + +typedef void* HCERTSTORE; +typedef ULONG_PTR HCRYPTPROV; +typedef ULONG_PTR HCRYPTPROV_LEGACY; + +typedef struct +{ + DWORD dwCertEncodingType; + BYTE* pbCertEncoded; + DWORD cbCertEncoded; + PCERT_INFO pCertInfo; + HCERTSTORE hCertStore; +} CERT_CONTEXT, *PCERT_CONTEXT; +typedef const CERT_CONTEXT* PCCERT_CONTEXT; + +#if !defined(AT_KEYEXCHANGE) +#define AT_KEYEXCHANGE (1) +#endif +#if !defined(AT_SIGNATURE) +#define AT_SIGNATURE (2) +#endif +#if !defined(AT_AUTHENTICATE) +#define AT_AUTHENTICATE (3) +#endif + +#define CERT_ENCODING_TYPE_MASK 0x0000FFFF +#define CMSG_ENCODING_TYPE_MASK 0xFFFF0000 +#define GET_CERT_ENCODING_TYPE(x) (x & CERT_ENCODING_TYPE_MASK) +#define GET_CMSG_ENCODING_TYPE(x) (x & CMSG_ENCODING_TYPE_MASK) + +#define CRYPT_ASN_ENCODING 0x00000001 +#define CRYPT_NDR_ENCODING 0x00000002 +#define X509_ASN_ENCODING 0x00000001 +#define X509_NDR_ENCODING 0x00000002 +#define PKCS_7_ASN_ENCODING 0x00010000 +#define PKCS_7_NDR_ENCODING 0x00020000 + +#define CERT_KEY_PROV_HANDLE_PROP_ID 1 +#define CERT_KEY_PROV_INFO_PROP_ID 2 +#define CERT_SHA1_HASH_PROP_ID 3 +#define CERT_MD5_HASH_PROP_ID 4 +#define CERT_HASH_PROP_ID CERT_SHA1_HASH_PROP_ID +#define CERT_KEY_CONTEXT_PROP_ID 5 +#define CERT_KEY_SPEC_PROP_ID 6 +#define CERT_IE30_RESERVED_PROP_ID 7 +#define CERT_PUBKEY_HASH_RESERVED_PROP_ID 8 +#define CERT_ENHKEY_USAGE_PROP_ID 9 +#define CERT_CTL_USAGE_PROP_ID CERT_ENHKEY_USAGE_PROP_ID +#define CERT_NEXT_UPDATE_LOCATION_PROP_ID 10 +#define CERT_FRIENDLY_NAME_PROP_ID 11 +#define CERT_PVK_FILE_PROP_ID 12 +#define CERT_DESCRIPTION_PROP_ID 13 +#define CERT_ACCESS_STATE_PROP_ID 14 +#define CERT_SIGNATURE_HASH_PROP_ID 15 +#define CERT_SMART_CARD_DATA_PROP_ID 16 +#define CERT_EFS_PROP_ID 17 +#define CERT_FORTEZZA_DATA_PROP_ID 18 +#define CERT_ARCHIVED_PROP_ID 19 +#define CERT_KEY_IDENTIFIER_PROP_ID 20 +#define CERT_AUTO_ENROLL_PROP_ID 21 +#define CERT_PUBKEY_ALG_PARA_PROP_ID 22 +#define CERT_CROSS_CERT_DIST_POINTS_PROP_ID 23 +#define CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID 24 +#define CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID 25 +#define CERT_ENROLLMENT_PROP_ID 26 +#define CERT_DATE_STAMP_PROP_ID 27 +#define CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID 28 +#define CERT_SUBJECT_NAME_MD5_HASH_PROP_ID 29 +#define CERT_EXTENDED_ERROR_INFO_PROP_ID 30 +#define CERT_RENEWAL_PROP_ID 64 +#define CERT_ARCHIVED_KEY_HASH_PROP_ID 65 +#define CERT_AUTO_ENROLL_RETRY_PROP_ID 66 +#define CERT_AIA_URL_RETRIEVED_PROP_ID 67 +#define CERT_AUTHORITY_INFO_ACCESS_PROP_ID 68 +#define CERT_BACKED_UP_PROP_ID 69 +#define CERT_OCSP_RESPONSE_PROP_ID 70 +#define CERT_REQUEST_ORIGINATOR_PROP_ID 71 +#define CERT_SOURCE_LOCATION_PROP_ID 72 +#define CERT_SOURCE_URL_PROP_ID 73 +#define CERT_NEW_KEY_PROP_ID 74 +#define CERT_OCSP_CACHE_PREFIX_PROP_ID 75 +#define CERT_SMART_CARD_ROOT_INFO_PROP_ID 76 +#define CERT_NO_AUTO_EXPIRE_CHECK_PROP_ID 77 +#define CERT_NCRYPT_KEY_HANDLE_PROP_ID 78 +#define CERT_HCRYPTPROV_OR_NCRYPT_KEY_HANDLE_PROP_ID 79 +#define CERT_SUBJECT_INFO_ACCESS_PROP_ID 80 +#define CERT_CA_OCSP_AUTHORITY_INFO_ACCESS_PROP_ID 81 +#define CERT_CA_DISABLE_CRL_PROP_ID 82 +#define CERT_ROOT_PROGRAM_CERT_POLICIES_PROP_ID 83 +#define CERT_ROOT_PROGRAM_NAME_CONSTRAINTS_PROP_ID 84 +#define CERT_SUBJECT_OCSP_AUTHORITY_INFO_ACCESS_PROP_ID 85 +#define CERT_SUBJECT_DISABLE_CRL_PROP_ID 86 +#define CERT_CEP_PROP_ID 87 +#define CERT_SIGN_HASH_CNG_ALG_PROP_ID 89 +#define CERT_SCARD_PIN_ID_PROP_ID 90 +#define CERT_SCARD_PIN_INFO_PROP_ID 91 +#define CERT_SUBJECT_PUB_KEY_BIT_LENGTH_PROP_ID 92 +#define CERT_PUB_KEY_CNG_ALG_BIT_LENGTH_PROP_ID 93 +#define CERT_ISSUER_PUB_KEY_BIT_LENGTH_PROP_ID 94 +#define CERT_ISSUER_CHAIN_SIGN_HASH_CNG_ALG_PROP_ID 95 +#define CERT_ISSUER_CHAIN_PUB_KEY_CNG_ALG_BIT_LENGTH_PROP_ID 96 +#define CERT_NO_EXPIRE_NOTIFICATION_PROP_ID 97 +#define CERT_AUTH_ROOT_SHA256_HASH_PROP_ID 98 +#define CERT_NCRYPT_KEY_HANDLE_TRANSFER_PROP_ID 99 +#define CERT_HCRYPTPROV_TRANSFER_PROP_ID 100 +#define CERT_SMART_CARD_READER_PROP_ID 101 +#define CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID 102 +#define CERT_KEY_REPAIR_ATTEMPTED_PROP_ID 103 +#define CERT_DISALLOWED_FILETIME_PROP_ID 104 +#define CERT_ROOT_PROGRAM_CHAIN_POLICIES_PROP_ID 105 +#define CERT_SMART_CARD_READER_NON_REMOVABLE_PROP_ID 106 +#define CERT_SHA256_HASH_PROP_ID 107 +#define CERT_SCEP_SERVER_CERTS_PROP_ID 108 +#define CERT_SCEP_RA_SIGNATURE_CERT_PROP_ID 109 +#define CERT_SCEP_RA_ENCRYPTION_CERT_PROP_ID 110 +#define CERT_SCEP_CA_CERT_PROP_ID 111 +#define CERT_SCEP_SIGNER_CERT_PROP_ID 112 +#define CERT_SCEP_NONCE_PROP_ID 113 +#define CERT_SCEP_ENCRYPT_HASH_CNG_ALG_PROP_ID 114 +#define CERT_SCEP_FLAGS_PROP_ID 115 +#define CERT_SCEP_GUID_PROP_ID 116 +#define CERT_SERIALIZABLE_KEY_CONTEXT_PROP_ID 117 +#define CERT_ISOLATED_KEY_PROP_ID 118 +#define CERT_SERIAL_CHAIN_PROP_ID 119 +#define CERT_KEY_CLASSIFICATION_PROP_ID 120 +#define CERT_OCSP_MUST_STAPLE_PROP_ID 121 +#define CERT_DISALLOWED_ENHKEY_USAGE_PROP_ID 122 +#define CERT_NONCOMPLIANT_ROOT_URL_PROP_ID 123 +#define CERT_PIN_SHA256_HASH_PROP_ID 124 +#define CERT_CLR_DELETE_KEY_PROP_ID 125 +#define CERT_NOT_BEFORE_FILETIME_PROP_ID 126 +#define CERT_NOT_BEFORE_ENHKEY_USAGE_PROP_ID 127 + +#define CERT_FIRST_RESERVED_PROP_ID 107 +#define CERT_LAST_RESERVED_PROP_ID 0x00007fff +#define CERT_FIRST_USER_PROP_ID 0x8000 +#define CERT_LAST_USER_PROP_ID 0x0000ffff + +#define CERT_COMPARE_MASK 0xFFFF +#define CERT_COMPARE_SHIFT 16 +#define CERT_COMPARE_ANY 0 +#define CERT_COMPARE_SHA1_HASH 1 +#define CERT_COMPARE_NAME 2 +#define CERT_COMPARE_ATTR 3 +#define CERT_COMPARE_MD5_HASH 4 +#define CERT_COMPARE_PROPERTY 5 +#define CERT_COMPARE_PUBLIC_KEY 6 +#define CERT_COMPARE_HASH CERT_COMPARE_SHA1_HASH +#define CERT_COMPARE_NAME_STR_A 7 +#define CERT_COMPARE_NAME_STR_W 8 +#define CERT_COMPARE_KEY_SPEC 9 +#define CERT_COMPARE_ENHKEY_USAGE 10 +#define CERT_COMPARE_CTL_USAGE CERT_COMPARE_ENHKEY_USAGE +#define CERT_COMPARE_SUBJECT_CERT 11 +#define CERT_COMPARE_ISSUER_OF 12 +#define CERT_COMPARE_EXISTING 13 +#define CERT_COMPARE_SIGNATURE_HASH 14 +#define CERT_COMPARE_KEY_IDENTIFIER 15 +#define CERT_COMPARE_CERT_ID 16 +#define CERT_COMPARE_CROSS_CERT_DIST_POINTS 17 +#define CERT_COMPARE_PUBKEY_MD5_HASH 18 +#define CERT_COMPARE_SUBJECT_INFO_ACCESS 19 +#define CERT_COMPARE_HASH_STR 20 +#define CERT_COMPARE_HAS_PRIVATE_KEY 21 + +#define CERT_FIND_ANY (CERT_COMPARE_ANY << CERT_COMPARE_SHIFT) +#define CERT_FIND_SHA1_HASH (CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_MD5_HASH (CERT_COMPARE_MD5_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_SIGNATURE_HASH (CERT_COMPARE_SIGNATURE_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_KEY_IDENTIFIER (CERT_COMPARE_KEY_IDENTIFIER << CERT_COMPARE_SHIFT) +#define CERT_FIND_HASH CERT_FIND_SHA1_HASH +#define CERT_FIND_PROPERTY (CERT_COMPARE_PROPERTY << CERT_COMPARE_SHIFT) +#define CERT_FIND_PUBLIC_KEY (CERT_COMPARE_PUBLIC_KEY << CERT_COMPARE_SHIFT) +#define CERT_FIND_SUBJECT_NAME (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_SUBJECT_ATTR (CERT_COMPARE_ATTR << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_ISSUER_NAME (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_ISSUER_ATTR (CERT_COMPARE_ATTR << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_SUBJECT_STR_A \ + (CERT_COMPARE_NAME_STR_A << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_SUBJECT_STR_W \ + (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_SUBJECT_STR CERT_FIND_SUBJECT_STR_W +#define CERT_FIND_ISSUER_STR_A \ + (CERT_COMPARE_NAME_STR_A << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_ISSUER_STR_W \ + (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_ISSUER_STR CERT_FIND_ISSUER_STR_W +#define CERT_FIND_KEY_SPEC (CERT_COMPARE_KEY_SPEC << CERT_COMPARE_SHIFT) +#define CERT_FIND_ENHKEY_USAGE (CERT_COMPARE_ENHKEY_USAGE << CERT_COMPARE_SHIFT) +#define CERT_FIND_CTL_USAGE CERT_FIND_ENHKEY_USAGE +#define CERT_FIND_SUBJECT_CERT (CERT_COMPARE_SUBJECT_CERT << CERT_COMPARE_SHIFT) +#define CERT_FIND_ISSUER_OF (CERT_COMPARE_ISSUER_OF << CERT_COMPARE_SHIFT) +#define CERT_FIND_EXISTING (CERT_COMPARE_EXISTING << CERT_COMPARE_SHIFT) +#define CERT_FIND_CERT_ID (CERT_COMPARE_CERT_ID << CERT_COMPARE_SHIFT) +#define CERT_FIND_CROSS_CERT_DIST_POINTS (CERT_COMPARE_CROSS_CERT_DIST_POINTS << CERT_COMPARE_SHIFT) +#define CERT_FIND_PUBKEY_MD5_HASH (CERT_COMPARE_PUBKEY_MD5_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_SUBJECT_INFO_ACCESS (CERT_COMPARE_SUBJECT_INFO_ACCESS << CERT_COMPARE_SHIFT) +#define CERT_FIND_HASH_STR (CERT_COMPARE_HASH_STR << CERT_COMPARE_SHIFT) +#define CERT_FIND_HAS_PRIVATE_KEY (CERT_COMPARE_HAS_PRIVATE_KEY << CERT_COMPARE_SHIFT) + +#define CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG 0x1 +#define CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG 0x2 +#define CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG 0x4 +#define CERT_FIND_NO_ENHKEY_USAGE_FLAG 0x8 +#define CERT_FIND_OR_ENHKEY_USAGE_FLAG 0x10 +#define CERT_FIND_VALID_ENHKEY_USAGE_FLAG 0x20 +#define CERT_FIND_OPTIONAL_CTL_USAGE_FLAG CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG +#define CERT_FIND_EXT_ONLY_CTL_USAGE_FLAG CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG +#define CERT_FIND_PROP_ONLY_CTL_USAGE_FLAG CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG +#define CERT_FIND_NO_CTL_USAGE_FLAG CERT_FIND_NO_ENHKEY_USAGE_FLAG +#define CERT_FIND_OR_CTL_USAGE_FLAG CERT_FIND_OR_ENHKEY_USAGE_FLAG +#define CERT_FIND_VALID_CTL_USAGE_FLAG CERT_FIND_VALID_ENHKEY_USAGE_FLAG + +#define CERT_NAME_EMAIL_TYPE 1 +#define CERT_NAME_RDN_TYPE 2 +#define CERT_NAME_ATTR_TYPE 3 +#define CERT_NAME_SIMPLE_DISPLAY_TYPE 4 +#define CERT_NAME_FRIENDLY_DISPLAY_TYPE 5 +#define CERT_NAME_DNS_TYPE 6 +#define CERT_NAME_URL_TYPE 7 +#define CERT_NAME_UPN_TYPE 8 + +#define CERT_NAME_ISSUER_FLAG 0x1 +#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000 + +#define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 + +#define CERT_STORE_PROV_MSG ((LPCSTR)1) +#define CERT_STORE_PROV_MEMORY ((LPCSTR)2) +#define CERT_STORE_PROV_FILE ((LPCSTR)3) +#define CERT_STORE_PROV_REG ((LPCSTR)4) +#define CERT_STORE_PROV_PKCS7 ((LPCSTR)5) +#define CERT_STORE_PROV_SERIALIZED ((LPCSTR)6) +#define CERT_STORE_PROV_FILENAME_A ((LPCSTR)7) +#define CERT_STORE_PROV_FILENAME_W ((LPCSTR)8) +#define CERT_STORE_PROV_FILENAME CERT_STORE_PROV_FILENAME_W +#define CERT_STORE_PROV_SYSTEM_A ((LPCSTR)9) +#define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)10) +#define CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W +#define CERT_STORE_PROV_COLLECTION ((LPCSTR)11) +#define CERT_STORE_PROV_SYSTEM_REGISTRY_A ((LPCSTR)12) +#define CERT_STORE_PROV_SYSTEM_REGISTRY_W ((LPCSTR)13) +#define CERT_STORE_PROV_SYSTEM_REGISTRY CERT_STORE_PROV_SYSTEM_REGISTRY_W +#define CERT_STORE_PROV_PHYSICAL_W ((LPCSTR)14) +#define CERT_STORE_PROV_PHYSICAL CERT_STORE_PROV_PHYSICAL_W +#define CERT_STORE_PROV_SMART_CARD_W ((LPCSTR)15) +#define CERT_STORE_PROV_SMART_CARD CERT_STORE_PROV_SMART_CARD_W +#define CERT_STORE_PROV_LDAP_W ((LPCSTR)16) +#define CERT_STORE_PROV_LDAP CERT_STORE_PROV_LDAP_W +#define CERT_STORE_PROV_PKCS12 ((LPCSTR)17) +#define sz_CERT_STORE_PROV_MEMORY "Memory" +#define sz_CERT_STORE_PROV_FILENAME_W "File" +#define sz_CERT_STORE_PROV_FILENAME sz_CERT_STORE_PROV_FILENAME_W +#define sz_CERT_STORE_PROV_SYSTEM_W "System" +#define sz_CERT_STORE_PROV_SYSTEM sz_CERT_STORE_PROV_SYSTEM_W +#define sz_CERT_STORE_PROV_PKCS7 "PKCS7" +#define sz_CERT_STORE_PROV_PKCS12 "PKCS12" +#define sz_CERT_STORE_PROV_SERIALIZED "Serialized" +#define sz_CERT_STORE_PROV_COLLECTION "Collection" +#define sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W "SystemRegistry" +#define sz_CERT_STORE_PROV_SYSTEM_REGISTRY sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W +#define sz_CERT_STORE_PROV_PHYSICAL_W "Physical" +#define sz_CERT_STORE_PROV_PHYSICAL sz_CERT_STORE_PROV_PHYSICAL_W +#define sz_CERT_STORE_PROV_SMART_CARD_W "SmartCard" +#define sz_CERT_STORE_PROV_SMART_CARD sz_CERT_STORE_PROV_SMART_CARD_W +#define sz_CERT_STORE_PROV_LDAP_W "Ldap" +#define sz_CERT_STORE_PROV_LDAP sz_CERT_STORE_PROV_LDAP_W + +#define CERT_STORE_SIGNATURE_FLAG 0x00000001 +#define CERT_STORE_TIME_VALIDITY_FLAG 0x00000002 +#define CERT_STORE_REVOCATION_FLAG 0x00000004 +#define CERT_STORE_NO_CRL_FLAG 0x00010000 +#define CERT_STORE_NO_ISSUER_FLAG 0x00020000 +#define CERT_STORE_BASE_CRL_FLAG 0x00000100 +#define CERT_STORE_DELTA_CRL_FLAG 0x00000200 + +#define CERT_STORE_NO_CRYPT_RELEASE_FLAG 0x00000001 +#define CERT_STORE_SET_LOCALIZED_NAME_FLAG 0x00000002 +#define CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG 0x00000004 +#define CERT_STORE_DELETE_FLAG 0x00000010 +#define CERT_STORE_UNSAFE_PHYSICAL_FLAG 0x00000020 +#define CERT_STORE_SHARE_STORE_FLAG 0x00000040 +#define CERT_STORE_SHARE_CONTEXT_FLAG 0x00000080 +#define CERT_STORE_MANIFOLD_FLAG 0x00000100 +#define CERT_STORE_ENUM_ARCHIVED_FLAG 0x00000200 +#define CERT_STORE_UPDATE_KEYID_FLAG 0x00000400 +#define CERT_STORE_BACKUP_RESTORE_FLAG 0x00000800 +#define CERT_STORE_READONLY_FLAG 0x00008000 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 +#define CERT_STORE_CREATE_NEW_FLAG 0x00002000 +#define CERT_STORE_MAXIMUM_ALLOWED_FLAG 0x00001000 + +#define CERT_SYSTEM_STORE_MASK 0xFFFF0000 +#define CERT_SYSTEM_STORE_RELOCATE_FLAG 0x80000000 +#define CERT_SYSTEM_STORE_UNPROTECTED_FLAG 0x40000000 +#define CERT_SYSTEM_STORE_DEFER_READ_FLAG 0x20000000 +#define CERT_SYSTEM_STORE_LOCATION_MASK 0x00FF0000 +#define CERT_SYSTEM_STORE_LOCATION_SHIFT 16 +#define CERT_SYSTEM_STORE_CURRENT_USER_ID 1 +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ID 2 +#define CERT_SYSTEM_STORE_CURRENT_SERVICE_ID 4 +#define CERT_SYSTEM_STORE_SERVICES_ID 5 +#define CERT_SYSTEM_STORE_USERS_ID 6 +#define CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID 7 +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID 8 +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID 9 + +#define CERT_SYSTEM_STORE_CURRENT_USER \ + (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_LOCAL_MACHINE \ + (CERT_SYSTEM_STORE_LOCAL_MACHINE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_CURRENT_SERVICE \ + (CERT_SYSTEM_STORE_CURRENT_SERVICE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_SERVICES \ + (CERT_SYSTEM_STORE_SERVICES_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_USERS (CERT_SYSTEM_STORE_USERS_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY \ + (CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY \ + (CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE \ + (CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) + +WINPR_API HCERTSTORE CertOpenStore(LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType, + HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const void* pvPara); + +WINPR_API HCERTSTORE CertOpenSystemStoreW(HCRYPTPROV_LEGACY hProv, LPCWSTR szSubsystemProtocol); +WINPR_API HCERTSTORE CertOpenSystemStoreA(HCRYPTPROV_LEGACY hProv, LPCSTR szSubsystemProtocol); + +WINPR_API BOOL CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags); + +#ifdef UNICODE +#define CertOpenSystemStore CertOpenSystemStoreW +#else +#define CertOpenSystemStore CertOpenSystemStoreA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API PCCERT_CONTEXT CertFindCertificateInStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, DWORD dwFindFlags, + DWORD dwFindType, const void* pvFindPara, + PCCERT_CONTEXT pPrevCertContext); + + WINPR_API PCCERT_CONTEXT CertEnumCertificatesInStore(HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrevCertContext); + + WINPR_API DWORD CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, + void* pvTypePara, LPWSTR pszNameString, DWORD cchNameString); + WINPR_API DWORD CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, + void* pvTypePara, LPSTR pszNameString, DWORD cchNameString); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define CertGetNameString CertGetNameStringW +#else +#define CertGetNameString CertGetNameStringA +#endif + +/** + * Data Protection API (DPAPI) + */ + +#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 + +#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00000000 +#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x00000001 +#define CRYPTPROTECTMEMORY_SAME_LOGON 0x00000002 + +#define CRYPTPROTECT_PROMPT_ON_UNPROTECT 0x00000001 +#define CRYPTPROTECT_PROMPT_ON_PROTECT 0x00000002 +#define CRYPTPROTECT_PROMPT_RESERVED 0x00000004 +#define CRYPTPROTECT_PROMPT_STRONG 0x00000008 +#define CRYPTPROTECT_PROMPT_REQUIRE_STRONG 0x00000010 + +#define CRYPTPROTECT_UI_FORBIDDEN 0x1 +#define CRYPTPROTECT_LOCAL_MACHINE 0x4 +#define CRYPTPROTECT_CRED_SYNC 0x8 +#define CRYPTPROTECT_AUDIT 0x10 +#define CRYPTPROTECT_NO_RECOVERY 0x20 +#define CRYPTPROTECT_VERIFY_PROTECTION 0x40 +#define CRYPTPROTECT_CRED_REGENERATE 0x80 + +#define CRYPTPROTECT_FIRST_RESERVED_FLAGVAL 0x0FFFFFFF +#define CRYPTPROTECT_LAST_RESERVED_FLAGVAL 0xFFFFFFFF + +typedef struct +{ + DWORD cbSize; + DWORD dwPromptFlags; + HWND hwndApp; + LPCWSTR szPrompt; +} CRYPTPROTECT_PROMPTSTRUCT, *PCRYPTPROTECT_PROMPTSTRUCT; + +#define CRYPTPROTECT_DEFAULT_PROVIDER \ + { \ + 0xdf9d8cd0, 0x1501, 0x11d1, \ + { \ + 0x8c, 0x7a, 0x00, 0xc0, 0x4f, 0xc2, 0x97, 0xeb \ + } \ + } + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); + WINPR_API BOOL CryptUnprotectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); + + WINPR_API BOOL CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, + DATA_BLOB* pOptionalEntropy, PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, + DATA_BLOB* pDataOut); + WINPR_API BOOL CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR* ppszDataDescr, + DATA_BLOB* pOptionalEntropy, PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, + DATA_BLOB* pDataOut); + +#ifdef __cplusplus +} +#endif + +#define CRYPT_STRING_BASE64HEADER 0x00000000 +#define CRYPT_STRING_BASE64 0x00000001 +#define CRYPT_STRING_BINARY 0x00000002 +#define CRYPT_STRING_BASE64REQUESTHEADER 0x00000003 +#define CRYPT_STRING_HEX 0x00000004 +#define CRYPT_STRING_HEXASCII 0x00000005 +#define CRYPT_STRING_BASE64_ANY 0x00000006 +#define CRYPT_STRING_ANY 0x00000007 +#define CRYPT_STRING_HEX_ANY 0x00000008 +#define CRYPT_STRING_BASE64X509CRLHEADER 0x00000009 +#define CRYPT_STRING_HEXADDR 0x0000000A +#define CRYPT_STRING_HEXASCIIADDR 0x0000000B +#define CRYPT_STRING_HEXRAW 0x0000000C + +#define CRYPT_STRING_HASHDATA 0x10000000 +#define CRYPT_STRING_STRICT 0x20000000 +#define CRYPT_STRING_NOCRLF 0x40000000 +#define CRYPT_STRING_NOCR 0x80000000 + +WINPR_API BOOL CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, + BYTE* pbBinary, DWORD* pcbBinary, DWORD* pdwSkip, + DWORD* pdwFlags); +WINPR_API BOOL CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, + BYTE* pbBinary, DWORD* pcbBinary, DWORD* pdwSkip, + DWORD* pdwFlags); + +WINPR_API BOOL CryptBinaryToStringW(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, + LPWSTR pszString, DWORD* pcchString); +WINPR_API BOOL CryptBinaryToStringA(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, + LPSTR pszString, DWORD* pcchString); + +#ifdef UNICODE +#define CryptStringToBinary CryptStringToBinaryW +#define CryptBinaryToString CryptBinaryToStringW +#else +#define CryptStringToBinary CryptStringToBinaryA +#define CryptBinaryToString CryptBinaryToStringA +#endif + +#endif + +#ifndef ALG_SID_ECSDA +#define ALG_SID_ECDSA 3 +#define CALG_ECDSA (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_ECDSA) +#endif + +#endif /* WINPR_WINCRYPT_H */ diff --git a/winpr/include/winpr/windows.h b/winpr/include/winpr/windows.h new file mode 100644 index 0000000..5bc6722 --- /dev/null +++ b/winpr/include/winpr/windows.h @@ -0,0 +1,130 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Header Include Wrapper + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WINDOWS_H +#define WINPR_WINDOWS_H + +/* Windows header include order is important, use this instead of including windows.h directly */ + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include + +#else + +/* Client System Parameters Update PDU + * defined in winuser.h + */ +typedef enum +{ + SPI_SETDRAGFULLWINDOWS = 0x00000025, + SPI_SETKEYBOARDCUES = 0x0000100B, + SPI_SETKEYBOARDPREF = 0x00000045, + SPI_SETWORKAREA = 0x0000002f, + RAIL_SPI_DISPLAYCHANGE = 0x0000F001, + SPI_SETMOUSEBUTTONSWAP = 0x00000021, + RAIL_SPI_TASKBARPOS = 0x0000F000, + SPI_SETHIGHCONTRAST = 0x00000043, + SPI_SETCARETWIDTH = 0x00002007, + SPI_SETSTICKYKEYS = 0x0000003B, + SPI_SETTOGGLEKEYS = 0x00000035, + SPI_SETFILTERKEYS = 0x00000033, + RAIL_SPI_DISPLAY_ANIMATIONS_ENABLED = 0x0000F002, + RAIL_SPI_DISPLAY_ADVANCED_EFFECTS_ENABLED = 0x0000F003, + RAIL_SPI_DISPLAY_AUTO_HIDE_SCROLLBARS = 0x0000F004, + RAIL_SPI_DISPLAY_MESSAGE_DURATION = 0x0000F005, + RAIL_SPI_CLOSED_CAPTION_FONT_COLOR = 0x0000F006, + RAIL_SPI_CLOSED_CAPTION_FONT_OPACITY = 0x0000F007, + RAIL_SPI_CLOSED_CAPTION_FONT_SIZE = 0x0000F008, + RAIL_SPI_CLOSED_CAPTION_FONT_STYLE = 0x0000F009, + RAIL_SPI_CLOSED_CAPTION_FONT_EDGE_EFFECT = 0x0000F00A, + RAIL_SPI_CLOSED_CAPTION_BACKGROUND_COLOR = 0x0000F00B, + RAIL_SPI_CLOSED_CAPTION_BACKGROUND_OPACITY = 0x0000F00C, + RAIL_SPI_CLOSED_CAPTION_REGION_COLOR = 0x0000F00D, + RAIL_SPI_CLOSED_CAPTION_REGION_OPACITY = 0x0000F00E +} SystemParam; + +/* Server System Parameters Update PDU */ +#define SPI_SETSCREENSAVEACTIVE 0x00000011 + +/* HIGHCONTRAST flags values */ +#define HCF_HIGHCONTRASTON 0x00000001 +#define HCF_AVAILABLE 0x00000002 +#define HCF_HOTKEYACTIVE 0x00000004 +#define HCF_CONFIRMHOTKEY 0x00000008 +#define HCF_HOTKEYSOUND 0x00000010 +#define HCF_INDICATOR 0x00000020 +#define HCF_HOTKEYAVAILABLE 0x00000040 + +/* TS_FILTERKEYS */ +#define FKF_FILTERKEYSON 0x00000001 +#define FKF_AVAILABLE 0x00000002 +#define FKF_HOTKEYACTIVE 0x00000004 +#define FKF_CONFIRMHOTKEY 0x00000008 +#define FKF_HOTKEYSOUND 0x00000010 +#define FKF_INDICATOR 0x00000020 +#define FKF_CLICKON 0x00000040 + +/* TS_TOGGLEKEYS */ +#define TKF_TOGGLEKEYSON 0x00000001 +#define TKF_AVAILABLE 0x00000002 +#define TKF_HOTKEYACTIVE 0x00000004 +#define TKF_CONFIRMHOTKEY 0x00000008 +#define TKF_HOTKEYSOUND 0x00000010 + +/* TS_STICKYKEYS */ +#define SKF_STICKYKEYSON 0x00000001 +#define SKF_AVAILABLE 0x00000002 +#define SKF_HOTKEYACTIVE 0x00000004 +#define SKF_CONFIRMHOTKEY 0x00000008 +#define SKF_HOTKEYSOUND 0x00000010 +#define SKF_INDICATOR 0x00000020 +#define SKF_AUDIBLEFEEDBACK 0x00000040 +#define SKF_TRISTATE 0x00000080 +#define SKF_TWOKEYSOFF 0x00000100 +#define SKF_LSHIFTLOCKED 0x00010000 +#define SKF_RSHIFTLOCKED 0x00020000 +#define SKF_LCTLLOCKED 0x00040000 +#define SKF_RCTLLOCKED 0x00080000 +#define SKF_LALTLOCKED 0x00100000 +#define SKF_RALTLOCKED 0x00200000 +#define SKF_LWINLOCKED 0x00400000 +#define SKF_RWINLOCKED 0x00800000 +#define SKF_LSHIFTLATCHED 0x01000000 +#define SKF_RSHIFTLATCHED 0x02000000 +#define SKF_LCTLLATCHED 0x04000000 +#define SKF_RCTLLATCHED 0x08000000 +#define SKF_LALTLATCHED 0x10000000 +#define SKF_RALTLATCHED 0x20000000 +#define SKF_LWINLATCHED 0x40000000 +#define SKF_RWINLATCHED 0x80000000 + +#endif + +#ifndef SPI_SETSCREENSAVESECURE +#define SPI_SETSCREENSAVESECURE 0x00000077 +#endif + +#endif /* WINPR_WINDOWS_H */ diff --git a/winpr/include/winpr/winpr.h b/winpr/include/winpr/winpr.h new file mode 100644 index 0000000..04311d8 --- /dev/null +++ b/winpr/include/winpr/winpr.h @@ -0,0 +1,129 @@ +/** + * WinPR: Windows Portable Runtime + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_H +#define WINPR_H + +#include + +#ifdef WINPR_DLL +#if defined _WIN32 || defined __CYGWIN__ +#ifdef WINPR_EXPORTS +#ifdef __GNUC__ +#define WINPR_API __attribute__((dllexport)) +#else +#define WINPR_API __declspec(dllexport) +#endif +#else +#ifdef __GNUC__ +#define WINPR_API __attribute__((dllimport)) +#else +#define WINPR_API __declspec(dllimport) +#endif +#endif +#else +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define WINPR_API __attribute__((visibility("default"))) +#else +#define WINPR_API +#endif +#endif +#else /* WINPR_DLL */ +#define WINPR_API +#endif + +#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ <= 10) +#define WINPR_ATTR_MALLOC(deallocator, ptrindex) __attribute__((malloc, warn_unused_result)) +#elif defined(__GNUC__) +#define WINPR_ATTR_MALLOC(deallocator, ptrindex) \ + __attribute__((malloc(deallocator, ptrindex), warn_unused_result)) +#else +#define WINPR_ATTR_MALLOC(deallocator, ptrindex) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define WINPR_ATTR_FORMAT_ARG(pos, args) __attribute__((__format__(__printf__, pos, args))) +#define WINPR_FORMAT_ARG /**/ +#else +#define WINPR_ATTR_FORMAT_ARG(pos, args) +#define WINPR_FORMAT_ARG _Printf_format_string_ +#endif + +#if defined(__STDC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) +#define WINPR_DEPRECATED(obj) [[deprecated]] obj +#define WINPR_DEPRECATED_VAR(text, obj) [[deprecated(text)]] obj +#define WINPR_NORETURN(obj) [[noreturn]] obj +#elif defined(WIN32) && !defined(__CYGWIN__) +#define WINPR_DEPRECATED(obj) __declspec(deprecated) obj +#define WINPR_DEPRECATED_VAR(text, obj) __declspec(deprecated(text)) obj +#define WINPR_NORETURN(obj) __declspec(noreturn) obj +#elif defined(__GNUC__) +#define WINPR_DEPRECATED(obj) obj __attribute__((deprecated)) +#define WINPR_DEPRECATED_VAR(text, obj) obj __attribute__((deprecated(text))) +#define WINPR_NORETURN(obj) __attribute__((__noreturn__)) obj +#else +#define WINPR_DEPRECATED(obj) obj +#define WINPR_DEPRECATED_VAR(text, obj) obj +#define WINPR_NORETURN(obj) obj +#endif + +#if defined(EXPORT_ALL_SYMBOLS) +#define WINPR_LOCAL WINPR_API +#else +#if defined _WIN32 || defined __CYGWIN__ +#define WINPR_LOCAL +#else +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define WINPR_LOCAL __attribute__((visibility("hidden"))) +#else +#define WINPR_LOCAL +#endif +#endif +#endif + +// WARNING: *do not* use thread-local storage for new code because it is not portable +// It is only used for VirtualChannelInit, and all FreeRDP channels use VirtualChannelInitEx +// The old virtual channel API is only realistically used on Windows where TLS is available +#if defined _WIN32 || defined __CYGWIN__ +#ifdef __GNUC__ +#define WINPR_TLS __thread +#else +#define WINPR_TLS __declspec(thread) +#endif +#elif !defined(__IOS__) +#define WINPR_TLS __thread +#else +// thread-local storage is not supported on iOS +// don't warn because it isn't actually used on iOS +#define WINPR_TLS +#endif + +#ifdef _WIN32 +#define INLINE __inline +#else +#define INLINE inline +#endif + +WINPR_API void winpr_get_version(int* major, int* minor, int* revision); +WINPR_API const char* winpr_get_version_string(void); +WINPR_API const char* winpr_get_build_revision(void); +WINPR_API const char* winpr_get_build_config(void); + +#define WINPR_UNUSED(x) (void)(x) + +#endif /* WINPR_H */ diff --git a/winpr/include/winpr/winsock.h b/winpr/include/winpr/winsock.h new file mode 100644 index 0000000..73dc9ae --- /dev/null +++ b/winpr/include/winpr/winsock.h @@ -0,0 +1,366 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Sockets (Winsock) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WINSOCK_H +#define WINPR_WINSOCK_H + +#include +#include +#include +#include + +#ifdef _WIN32 + +#define _accept accept +#define _bind bind +#define _connect connect +#define _ioctlsocket ioctlsocket +#define _getpeername getpeername +#define _getsockname getsockname +#define _getsockopt getsockopt +#define _htonl htonl +#define _htons htons +#define _inet_addr inet_addr +#define _inet_ntoa inet_ntoa +#define _listen listen +#define _ntohl ntohl +#define _ntohs ntohs +#define _recv recv +#define _recvfrom recvfrom +#define _select select +#define _send send +#define _sendto sendto +#define _setsockopt setsockopt +#define _shutdown shutdown +#define _socket socket +#define _gethostbyaddr gethostbyaddr +#define _gethostbyname gethostbyname +#define _gethostname gethostname +#define _getservbyport getservbyport +#define _getservbyname getservbyname +#define _getprotobynumber getprotobynumber +#define _getprotobyname getprotobyname + +#define _IFF_UP IFF_UP +#define _IFF_BROADCAST IFF_BROADCAST +#define _IFF_LOOPBACK IFF_LOOPBACK +#define _IFF_POINTTOPOINT IFF_POINTTOPOINT +#define _IFF_MULTICAST IFF_MULTICAST + +#if (_WIN32_WINNT < 0x0600) + +WINPR_API PCSTR winpr_inet_ntop(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize); +WINPR_API INT winpr_inet_pton(INT Family, PCSTR pszAddrString, PVOID pAddrBuf); + +#define inet_ntop winpr_inet_ntop +#define inet_pton winpr_inet_pton + +#endif /* (_WIN32_WINNT < 0x0600) */ + +#else /* _WIN32 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WSAEVENT HANDLE +#define LPWSAEVENT LPHANDLE +#define WSAOVERLAPPED OVERLAPPED +typedef OVERLAPPED* LPWSAOVERLAPPED; + +typedef UINT_PTR SOCKET; +typedef struct sockaddr_storage SOCKADDR_STORAGE; + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (SOCKET)(~0) +#endif + +#define WSADESCRIPTION_LEN 256 +#define WSASYS_STATUS_LEN 128 + +#define FD_READ_BIT 0 +#define FD_READ (1 << FD_READ_BIT) + +#define FD_WRITE_BIT 1 +#define FD_WRITE (1 << FD_WRITE_BIT) + +#define FD_OOB_BIT 2 +#define FD_OOB (1 << FD_OOB_BIT) + +#define FD_ACCEPT_BIT 3 +#define FD_ACCEPT (1 << FD_ACCEPT_BIT) + +#define FD_CONNECT_BIT 4 +#define FD_CONNECT (1 << FD_CONNECT_BIT) + +#define FD_CLOSE_BIT 5 +#define FD_CLOSE (1 << FD_CLOSE_BIT) + +#define FD_QOS_BIT 6 +#define FD_QOS (1 << FD_QOS_BIT) + +#define FD_GROUP_QOS_BIT 7 +#define FD_GROUP_QOS (1 << FD_GROUP_QOS_BIT) + +#define FD_ROUTING_INTERFACE_CHANGE_BIT 8 +#define FD_ROUTING_INTERFACE_CHANGE (1 << FD_ROUTING_INTERFACE_CHANGE_BIT) + +#define FD_ADDRESS_LIST_CHANGE_BIT 9 +#define FD_ADDRESS_LIST_CHANGE (1 << FD_ADDRESS_LIST_CHANGE_BIT) + +#define FD_MAX_EVENTS 10 +#define FD_ALL_EVENTS ((1 << FD_MAX_EVENTS) - 1) + +#define SD_RECEIVE 0 +#define SD_SEND 1 +#define SD_BOTH 2 + +#define SOCKET_ERROR (-1) + +typedef struct WSAData +{ + WORD wVersion; + WORD wHighVersion; +#ifdef _M_AMD64 + unsigned short iMaxSockets; + unsigned short iMaxUdpDg; + char* lpVendorInfo; + char szDescription[WSADESCRIPTION_LEN + 1]; + char szSystemStatus[WSASYS_STATUS_LEN + 1]; +#else + char szDescription[WSADESCRIPTION_LEN + 1]; + char szSystemStatus[WSASYS_STATUS_LEN + 1]; + unsigned short iMaxSockets; + unsigned short iMaxUdpDg; + char* lpVendorInfo; +#endif +} WSADATA, *LPWSADATA; + +#ifndef MAKEWORD +#define MAKEWORD(a, b) \ + ((WORD)(((BYTE)((DWORD_PTR)(a)&0xFF)) | (((WORD)((BYTE)((DWORD_PTR)(b)&0xFF))) << 8))) +#endif + +typedef struct in6_addr IN6_ADDR; +typedef struct in6_addr* PIN6_ADDR; +typedef struct in6_addr* LPIN6_ADDR; + +struct sockaddr_in6_old +{ + SHORT sin6_family; + USHORT sin6_port; + ULONG sin6_flowinfo; + IN6_ADDR sin6_addr; +}; + +typedef union sockaddr_gen +{ + struct sockaddr Address; + struct sockaddr_in AddressIn; + struct sockaddr_in6_old AddressIn6; +} sockaddr_gen; + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#define _IFF_UP 0x00000001 +#define _IFF_BROADCAST 0x00000002 +#define _IFF_LOOPBACK 0x00000004 +#define _IFF_POINTTOPOINT 0x00000008 +#define _IFF_MULTICAST 0x00000010 + +WINPR_PRAGMA_DIAG_POP + +typedef struct +{ + ULONG iiFlags; + sockaddr_gen iiAddress; + sockaddr_gen iiBroadcastAddress; + sockaddr_gen iiNetmask; +} INTERFACE_INFO; +typedef INTERFACE_INFO* LPINTERFACE_INFO; + +#define MAX_PROTOCOL_CHAIN 7 +#define WSAPROTOCOL_LEN 255 + +typedef struct +{ + int ChainLen; + DWORD ChainEntries[MAX_PROTOCOL_CHAIN]; +} WSAPROTOCOLCHAIN, *LPWSAPROTOCOLCHAIN; + +typedef struct +{ + DWORD dwServiceFlags1; + DWORD dwServiceFlags2; + DWORD dwServiceFlags3; + DWORD dwServiceFlags4; + DWORD dwProviderFlags; + GUID ProviderId; + DWORD dwCatalogEntryId; + WSAPROTOCOLCHAIN ProtocolChain; + int iVersion; + int iAddressFamily; + int iMaxSockAddr; + int iMinSockAddr; + int iSocketType; + int iProtocol; + int iProtocolMaxOffset; + int iNetworkByteOrder; + int iSecurityScheme; + DWORD dwMessageSize; + DWORD dwProviderReserved; + CHAR szProtocol[WSAPROTOCOL_LEN + 1]; +} WSAPROTOCOL_INFOA, *LPWSAPROTOCOL_INFOA; + +typedef struct +{ + DWORD dwServiceFlags1; + DWORD dwServiceFlags2; + DWORD dwServiceFlags3; + DWORD dwServiceFlags4; + DWORD dwProviderFlags; + GUID ProviderId; + DWORD dwCatalogEntryId; + WSAPROTOCOLCHAIN ProtocolChain; + int iVersion; + int iAddressFamily; + int iMaxSockAddr; + int iMinSockAddr; + int iSocketType; + int iProtocol; + int iProtocolMaxOffset; + int iNetworkByteOrder; + int iSecurityScheme; + DWORD dwMessageSize; + DWORD dwProviderReserved; + WCHAR szProtocol[WSAPROTOCOL_LEN + 1]; +} WSAPROTOCOL_INFOW, *LPWSAPROTOCOL_INFOW; + +typedef void(CALLBACK* LPWSAOVERLAPPED_COMPLETION_ROUTINE)(DWORD dwError, DWORD cbTransferred, + LPWSAOVERLAPPED lpOverlapped, + DWORD dwFlags); + +typedef UINT32 GROUP; +#define SG_UNCONSTRAINED_GROUP 0x01 +#define SG_CONSTRAINED_GROUP 0x02 + +#define SIO_GET_INTERFACE_LIST _IOR('t', 127, ULONG) +#define SIO_GET_INTERFACE_LIST_EX _IOR('t', 126, ULONG) +#define SIO_SET_MULTICAST_FILTER _IOW('t', 125, ULONG) +#define SIO_GET_MULTICAST_FILTER _IOW('t', 124 | IOC_IN, ULONG) +#define SIOCSIPMSFILTER SIO_SET_MULTICAST_FILTER +#define SIOCGIPMSFILTER SIO_GET_MULTICAST_FILTER + +#ifdef UNICODE +#define WSAPROTOCOL_INFO WSAPROTOCOL_INFOW +#define LPWSAPROTOCOL_INFO LPWSAPROTOCOL_INFOW +#else +#define WSAPROTOCOL_INFO WSAPROTOCOL_INFOA +#define LPWSAPROTOCOL_INFO LPWSAPROTOCOL_INFOA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData); + WINPR_API int WSACleanup(void); + + WINPR_API void WSASetLastError(int iError); + WINPR_API int WSAGetLastError(void); + + WINPR_API HANDLE WSACreateEvent(void); + WINPR_API BOOL WSASetEvent(HANDLE hEvent); + WINPR_API BOOL WSAResetEvent(HANDLE hEvent); + WINPR_API BOOL WSACloseEvent(HANDLE hEvent); + + WINPR_API int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, LONG lNetworkEvents); + + WINPR_API DWORD WSAWaitForMultipleEvents(DWORD cEvents, const HANDLE* lphEvents, BOOL fWaitAll, + DWORD dwTimeout, BOOL fAlertable); + + WINPR_API SOCKET WSASocketA(int af, int type, int protocol, LPWSAPROTOCOL_INFOA lpProtocolInfo, + GROUP g, DWORD dwFlags); + WINPR_API SOCKET WSASocketW(int af, int type, int protocol, LPWSAPROTOCOL_INFOW lpProtocolInfo, + GROUP g, DWORD dwFlags); + + WINPR_API int WSAIoctl(SOCKET s, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + + WINPR_API SOCKET _accept(SOCKET s, struct sockaddr* addr, int* addrlen); + WINPR_API int _bind(SOCKET s, const struct sockaddr* addr, int namelen); + WINPR_API int closesocket(SOCKET s); + WINPR_API int _connect(SOCKET s, const struct sockaddr* name, int namelen); + WINPR_API int _ioctlsocket(SOCKET s, long cmd, u_long* argp); + WINPR_API int _getpeername(SOCKET s, struct sockaddr* name, int* namelen); + WINPR_API int _getsockname(SOCKET s, struct sockaddr* name, int* namelen); + WINPR_API int _getsockopt(SOCKET s, int level, int optname, char* optval, int* optlen); + WINPR_API u_long _htonl(u_long hostlong); + WINPR_API u_short _htons(u_short hostshort); + WINPR_API unsigned long _inet_addr(const char* cp); + WINPR_API char* _inet_ntoa(struct in_addr in); + WINPR_API int _listen(SOCKET s, int backlog); + WINPR_API u_long _ntohl(u_long netlong); + WINPR_API u_short _ntohs(u_short netshort); + WINPR_API int _recv(SOCKET s, char* buf, int len, int flags); + WINPR_API int _recvfrom(SOCKET s, char* buf, int len, int flags, struct sockaddr* from, + int* fromlen); + WINPR_API int _select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, + const struct timeval* timeout); + WINPR_API int _send(SOCKET s, const char* buf, int len, int flags); + WINPR_API int _sendto(SOCKET s, const char* buf, int len, int flags, const struct sockaddr* to, + int tolen); + WINPR_API int _setsockopt(SOCKET s, int level, int optname, const char* optval, int optlen); + WINPR_API int _shutdown(SOCKET s, int how); + WINPR_API SOCKET _socket(int af, int type, int protocol); + WINPR_API struct hostent* _gethostbyaddr(const char* addr, int len, int type); + WINPR_API struct hostent* _gethostbyname(const char* name); + WINPR_API int _gethostname(char* name, int namelen); + WINPR_API struct servent* _getservbyport(int port, const char* proto); + WINPR_API struct servent* _getservbyname(const char* name, const char* proto); + WINPR_API struct protoent* _getprotobynumber(int number); + WINPR_API struct protoent* _getprotobyname(const char* name); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define WSASocket WSASocketW +#else +#define WSASocket WSASocketA +#endif + +#endif /* _WIN32 */ + +#endif /* WINPR_WINSOCK_H */ diff --git a/winpr/include/winpr/wlog.h b/winpr/include/winpr/wlog.h new file mode 100644 index 0000000..c3918f5 --- /dev/null +++ b/winpr/include/winpr/wlog.h @@ -0,0 +1,246 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 Bernhard Miklautz + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_LOG_H +#define WINPR_LOG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include +#include + +/** + * Log Levels + */ +#define WLOG_TRACE 0 +#define WLOG_DEBUG 1 +#define WLOG_INFO 2 +#define WLOG_WARN 3 +#define WLOG_ERROR 4 +#define WLOG_FATAL 5 +#define WLOG_OFF 6 +#define WLOG_LEVEL_INHERIT 0xFFFF + +/** + * Log Message + */ +#define WLOG_MESSAGE_TEXT 0 +#define WLOG_MESSAGE_DATA 1 +#define WLOG_MESSAGE_IMAGE 2 +#define WLOG_MESSAGE_PACKET 3 + +/** + * Log Appenders + */ +#define WLOG_APPENDER_CONSOLE 0 +#define WLOG_APPENDER_FILE 1 +#define WLOG_APPENDER_BINARY 2 +#define WLOG_APPENDER_CALLBACK 3 +#define WLOG_APPENDER_SYSLOG 4 +#define WLOG_APPENDER_JOURNALD 5 +#define WLOG_APPENDER_UDP 6 + + typedef struct + { + DWORD Type; + + DWORD Level; + + LPSTR PrefixString; + + LPCSTR FormatString; + LPCSTR TextString; + + size_t LineNumber; /* __LINE__ */ + LPCSTR FileName; /* __FILE__ */ + LPCSTR FunctionName; /* __func__ */ + + /* Data Message */ + + void* Data; + size_t Length; + + /* Image Message */ + + void* ImageData; + size_t ImageWidth; + size_t ImageHeight; + size_t ImageBpp; + + /* Packet Message */ + + void* PacketData; + size_t PacketLength; + DWORD PacketFlags; + } wLogMessage; + typedef struct s_wLogLayout wLogLayout; + typedef struct s_wLogAppender wLogAppender; + typedef struct s_wLog wLog; + +#define WLOG_PACKET_INBOUND 1 +#define WLOG_PACKET_OUTBOUND 2 + + WINPR_API BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level, size_t line, + const char* file, const char* function, ...); + WINPR_API BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level, size_t line, + const char* file, const char* function, va_list args); + + WINPR_API wLog* WLog_GetRoot(void); + WINPR_API wLog* WLog_Get(LPCSTR name); + WINPR_API DWORD WLog_GetLogLevel(wLog* log); + WINPR_API BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level); + + /** @brief Set a custom context for a dynamic logger. + * This can be used to print a customized prefix, e.g. some session id for a specific context + * + * @param log The logger to ste the context for. Must not be \b NULL + * @param fkt A function pointer that is called to get the custimized string. + * @param context A context \b fkt is called with. Caller must ensure it is still allocated + * when \b log is used + * + * @return \b TRUE for success, \b FALSE otherwise. + */ + WINPR_API BOOL WLog_SetContext(wLog* log, const char* (*fkt)(void*), void* context); + +#define WLog_Print_unchecked(_log, _log_level, ...) \ + do \ + { \ + WLog_PrintMessage(_log, WLOG_MESSAGE_TEXT, _log_level, __LINE__, __FILE__, __func__, \ + __VA_ARGS__); \ + } while (0) + +#define WLog_Print(_log, _log_level, ...) \ + do \ + { \ + if (WLog_IsLevelActive(_log, _log_level)) \ + { \ + WLog_Print_unchecked(_log, _log_level, ##__VA_ARGS__); \ + } \ + } while (0) + +#define WLog_Print_tag(_tag, _log_level, ...) \ + do \ + { \ + static wLog* _log_cached_ptr = NULL; \ + if (!_log_cached_ptr) \ + _log_cached_ptr = WLog_Get(_tag); \ + WLog_Print(_log_cached_ptr, _log_level, __VA_ARGS__); \ + } while (0) + +#define WLog_PrintVA_unchecked(_log, _log_level, _args) \ + do \ + { \ + WLog_PrintMessageVA(_log, WLOG_MESSAGE_TEXT, _log_level, __LINE__, __FILE__, __func__, \ + _args); \ + } while (0) + +#define WLog_PrintVA(_log, _log_level, _args) \ + do \ + { \ + if (WLog_IsLevelActive(_log, _log_level)) \ + { \ + WLog_PrintVA_unchecked(_log, _log_level, _args); \ + } \ + } while (0) + +#define WLog_Data(_log, _log_level, ...) \ + do \ + { \ + if (WLog_IsLevelActive(_log, _log_level)) \ + { \ + WLog_PrintMessage(_log, WLOG_MESSAGE_DATA, _log_level, __LINE__, __FILE__, __func__, \ + __VA_ARGS__); \ + } \ + } while (0) + +#define WLog_Image(_log, _log_level, ...) \ + do \ + { \ + if (WLog_IsLevelActive(_log, _log_level)) \ + { \ + WLog_PrintMessage(_log, WLOG_MESSAGE_DATA, _log_level, __LINE__, __FILE__, __func__, \ + __VA_ARGS__); \ + } \ + } while (0) + +#define WLog_Packet(_log, _log_level, ...) \ + do \ + { \ + if (WLog_IsLevelActive(_log, _log_level)) \ + { \ + WLog_PrintMessage(_log, WLOG_MESSAGE_PACKET, _log_level, __LINE__, __FILE__, __func__, \ + __VA_ARGS__); \ + } \ + } while (0) + +#define WLog_LVL(tag, lvl, ...) WLog_Print_tag(tag, lvl, __VA_ARGS__) +#define WLog_VRB(tag, ...) WLog_Print_tag(tag, WLOG_TRACE, __VA_ARGS__) +#define WLog_DBG(tag, ...) WLog_Print_tag(tag, WLOG_DEBUG, __VA_ARGS__) +#define WLog_INFO(tag, ...) WLog_Print_tag(tag, WLOG_INFO, __VA_ARGS__) +#define WLog_WARN(tag, ...) WLog_Print_tag(tag, WLOG_WARN, __VA_ARGS__) +#define WLog_ERR(tag, ...) WLog_Print_tag(tag, WLOG_ERROR, __VA_ARGS__) +#define WLog_FATAL(tag, ...) WLog_Print_tag(tag, WLOG_FATAL, __VA_ARGS__) + + WINPR_API BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel); + WINPR_API BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level); + WINPR_API BOOL WLog_AddStringLogFilters(LPCSTR filter); + + WINPR_API BOOL WLog_SetLogAppenderType(wLog* log, DWORD logAppenderType); + WINPR_API wLogAppender* WLog_GetLogAppender(wLog* log); + WINPR_API BOOL WLog_OpenAppender(wLog* log); + WINPR_API BOOL WLog_CloseAppender(wLog* log); + WINPR_API BOOL WLog_ConfigureAppender(wLogAppender* appender, const char* setting, void* value); + + WINPR_API wLogLayout* WLog_GetLogLayout(wLog* log); + WINPR_API BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format); + +#if defined(WITH_WINPR_DEPRECATED) + /** Deprecated */ + WINPR_API WINPR_DEPRECATED(BOOL WLog_Init(void)); + /** Deprecated */ + WINPR_API WINPR_DEPRECATED(BOOL WLog_Uninit(void)); +#endif + + typedef BOOL (*wLogCallbackMessage_t)(const wLogMessage* msg); + typedef BOOL (*wLogCallbackData_t)(const wLogMessage* msg); + typedef BOOL (*wLogCallbackImage_t)(const wLogMessage* msg); + typedef BOOL (*wLogCallbackPackage_t)(const wLogMessage* msg); + + typedef struct + { + wLogCallbackData_t data; + wLogCallbackImage_t image; + wLogCallbackMessage_t message; + wLogCallbackPackage_t package; + } wLogCallbacks; + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_WLOG_H */ diff --git a/winpr/include/winpr/wtsapi.h b/winpr/include/winpr/wtsapi.h new file mode 100644 index 0000000..bd5616f --- /dev/null +++ b/winpr/include/winpr/wtsapi.h @@ -0,0 +1,1512 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Terminal Services API + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2015 Copyright 2015 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WTSAPI_H +#define WINPR_WTSAPI_H + +#include +#include + +#include + +#define CHANNEL_CHUNK_MAX_LENGTH 16256 + +#ifdef _WIN32 + +#define CurrentTime _CurrentTime /* Workaround for X11 "CurrentTime" header conflict */ + +#endif + +#if defined(_WIN32) && !defined(_UWP) + +#include + +#else + +/** + * Virtual Channel Protocol (pchannel.h) + */ + +#define CHANNEL_CHUNK_LENGTH 1600 + +#define CHANNEL_PDU_LENGTH (CHANNEL_CHUNK_LENGTH + sizeof(CHANNEL_PDU_HEADER)) + +#define CHANNEL_FLAG_FIRST 0x01 +#define CHANNEL_FLAG_LAST 0x02 +#define CHANNEL_FLAG_ONLY (CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST) +#define CHANNEL_FLAG_MIDDLE 0 +#define CHANNEL_FLAG_FAIL 0x100 + +#define CHANNEL_OPTION_INITIALIZED 0x80000000 + +#define CHANNEL_OPTION_ENCRYPT_RDP 0x40000000 +#define CHANNEL_OPTION_ENCRYPT_SC 0x20000000 +#define CHANNEL_OPTION_ENCRYPT_CS 0x10000000 +#define CHANNEL_OPTION_PRI_HIGH 0x08000000 +#define CHANNEL_OPTION_PRI_MED 0x04000000 +#define CHANNEL_OPTION_PRI_LOW 0x02000000 +#define CHANNEL_OPTION_COMPRESS_RDP 0x00800000 +#define CHANNEL_OPTION_COMPRESS 0x00400000 +#define CHANNEL_OPTION_SHOW_PROTOCOL 0x00200000 +#define CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT 0x00100000 + +#define CHANNEL_MAX_COUNT 31 +#define CHANNEL_NAME_LEN 7 + +typedef struct tagCHANNEL_DEF +{ + char name[CHANNEL_NAME_LEN + 1]; + ULONG options; +} CHANNEL_DEF; +typedef CHANNEL_DEF* PCHANNEL_DEF; +typedef PCHANNEL_DEF* PPCHANNEL_DEF; + +typedef struct tagCHANNEL_PDU_HEADER +{ + UINT32 length; + UINT32 flags; +} CHANNEL_PDU_HEADER, *PCHANNEL_PDU_HEADER; + +#endif /* _WIN32 */ + +/** + * These channel flags are defined in some versions of pchannel.h only + */ + +#ifndef CHANNEL_FLAG_SHOW_PROTOCOL +#define CHANNEL_FLAG_SHOW_PROTOCOL 0x10 +#endif +#ifndef CHANNEL_FLAG_SUSPEND +#define CHANNEL_FLAG_SUSPEND 0x20 +#endif +#ifndef CHANNEL_FLAG_RESUME +#define CHANNEL_FLAG_RESUME 0x40 +#endif +#ifndef CHANNEL_FLAG_SHADOW_PERSISTENT +#define CHANNEL_FLAG_SHADOW_PERSISTENT 0x80 +#endif + +#if !defined(_WIN32) || !defined(H_CCHANNEL) + +/** + * Virtual Channel Client API (cchannel.h) + */ + +#ifdef _WIN32 +#define VCAPITYPE __stdcall +#define VCEXPORT +#else +#define VCAPITYPE CALLBACK +#define VCEXPORT __export +#endif + +typedef VOID VCAPITYPE CHANNEL_INIT_EVENT_FN(LPVOID pInitHandle, UINT event, LPVOID pData, + UINT dataLength); + +typedef CHANNEL_INIT_EVENT_FN* PCHANNEL_INIT_EVENT_FN; + +typedef VOID VCAPITYPE CHANNEL_INIT_EVENT_EX_FN(LPVOID lpUserParam, LPVOID pInitHandle, UINT event, + LPVOID pData, UINT dataLength); + +typedef CHANNEL_INIT_EVENT_EX_FN* PCHANNEL_INIT_EVENT_EX_FN; + +#define CHANNEL_EVENT_INITIALIZED 0 +#define CHANNEL_EVENT_CONNECTED 1 +#define CHANNEL_EVENT_V1_CONNECTED 2 +#define CHANNEL_EVENT_DISCONNECTED 3 +#define CHANNEL_EVENT_TERMINATED 4 +#define CHANNEL_EVENT_REMOTE_CONTROL_START 5 +#define CHANNEL_EVENT_REMOTE_CONTROL_STOP 6 +#define CHANNEL_EVENT_ATTACHED 7 +#define CHANNEL_EVENT_DETACHED 8 +#define CHANNEL_EVENT_DATA_RECEIVED 10 +#define CHANNEL_EVENT_WRITE_COMPLETE 11 +#define CHANNEL_EVENT_WRITE_CANCELLED 12 + +typedef VOID VCAPITYPE CHANNEL_OPEN_EVENT_FN(DWORD openHandle, UINT event, LPVOID pData, + UINT32 dataLength, UINT32 totalLength, + UINT32 dataFlags); + +typedef CHANNEL_OPEN_EVENT_FN* PCHANNEL_OPEN_EVENT_FN; + +typedef VOID VCAPITYPE CHANNEL_OPEN_EVENT_EX_FN(LPVOID lpUserParam, DWORD openHandle, UINT event, + LPVOID pData, UINT32 dataLength, UINT32 totalLength, + UINT32 dataFlags); + +typedef CHANNEL_OPEN_EVENT_EX_FN* PCHANNEL_OPEN_EVENT_EX_FN; + +#define CHANNEL_RC_OK 0 +#define CHANNEL_RC_ALREADY_INITIALIZED 1 +#define CHANNEL_RC_NOT_INITIALIZED 2 +#define CHANNEL_RC_ALREADY_CONNECTED 3 +#define CHANNEL_RC_NOT_CONNECTED 4 +#define CHANNEL_RC_TOO_MANY_CHANNELS 5 +#define CHANNEL_RC_BAD_CHANNEL 6 +#define CHANNEL_RC_BAD_CHANNEL_HANDLE 7 +#define CHANNEL_RC_NO_BUFFER 8 +#define CHANNEL_RC_BAD_INIT_HANDLE 9 +#define CHANNEL_RC_NOT_OPEN 10 +#define CHANNEL_RC_BAD_PROC 11 +#define CHANNEL_RC_NO_MEMORY 12 +#define CHANNEL_RC_UNKNOWN_CHANNEL_NAME 13 +#define CHANNEL_RC_ALREADY_OPEN 14 +#define CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY 15 +#define CHANNEL_RC_NULL_DATA 16 +#define CHANNEL_RC_ZERO_LENGTH 17 +#define CHANNEL_RC_INVALID_INSTANCE 18 +#define CHANNEL_RC_UNSUPPORTED_VERSION 19 +#define CHANNEL_RC_INITIALIZATION_ERROR 20 + +#define VIRTUAL_CHANNEL_VERSION_WIN2000 1 + +typedef UINT VCAPITYPE VIRTUALCHANNELINIT(LPVOID* ppInitHandle, PCHANNEL_DEF pChannel, + INT channelCount, ULONG versionRequested, + PCHANNEL_INIT_EVENT_FN pChannelInitEventProc); + +typedef VIRTUALCHANNELINIT* PVIRTUALCHANNELINIT; + +typedef UINT VCAPITYPE VIRTUALCHANNELINITEX(LPVOID lpUserParam, LPVOID clientContext, + LPVOID pInitHandle, PCHANNEL_DEF pChannel, + INT channelCount, ULONG versionRequested, + PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx); + +typedef VIRTUALCHANNELINITEX* PVIRTUALCHANNELINITEX; + +typedef UINT VCAPITYPE VIRTUALCHANNELOPEN(LPVOID pInitHandle, LPDWORD pOpenHandle, + PCHAR pChannelName, + PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc); + +typedef VIRTUALCHANNELOPEN* PVIRTUALCHANNELOPEN; + +typedef UINT VCAPITYPE VIRTUALCHANNELOPENEX(LPVOID pInitHandle, LPDWORD pOpenHandle, + PCHAR pChannelName, + PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx); + +typedef VIRTUALCHANNELOPENEX* PVIRTUALCHANNELOPENEX; + +typedef UINT VCAPITYPE VIRTUALCHANNELCLOSE(DWORD openHandle); + +typedef VIRTUALCHANNELCLOSE* PVIRTUALCHANNELCLOSE; + +typedef UINT VCAPITYPE VIRTUALCHANNELCLOSEEX(LPVOID pInitHandle, DWORD openHandle); + +typedef VIRTUALCHANNELCLOSEEX* PVIRTUALCHANNELCLOSEEX; + +typedef UINT VCAPITYPE VIRTUALCHANNELWRITE(DWORD openHandle, LPVOID pData, ULONG dataLength, + LPVOID pUserData); + +typedef VIRTUALCHANNELWRITE* PVIRTUALCHANNELWRITE; + +typedef UINT VCAPITYPE VIRTUALCHANNELWRITEEX(LPVOID pInitHandle, DWORD openHandle, LPVOID pData, + ULONG dataLength, LPVOID pUserData); + +typedef VIRTUALCHANNELWRITEEX* PVIRTUALCHANNELWRITEEX; + +typedef struct tagCHANNEL_ENTRY_POINTS +{ + DWORD cbSize; + DWORD protocolVersion; + PVIRTUALCHANNELINIT pVirtualChannelInit; + PVIRTUALCHANNELOPEN pVirtualChannelOpen; + PVIRTUALCHANNELCLOSE pVirtualChannelClose; + PVIRTUALCHANNELWRITE pVirtualChannelWrite; +} CHANNEL_ENTRY_POINTS, *PCHANNEL_ENTRY_POINTS; + +typedef struct tagCHANNEL_ENTRY_POINTS_EX +{ + DWORD cbSize; + DWORD protocolVersion; + PVIRTUALCHANNELINITEX pVirtualChannelInitEx; + PVIRTUALCHANNELOPENEX pVirtualChannelOpenEx; + PVIRTUALCHANNELCLOSEEX pVirtualChannelCloseEx; + PVIRTUALCHANNELWRITEEX pVirtualChannelWriteEx; +} CHANNEL_ENTRY_POINTS_EX, *PCHANNEL_ENTRY_POINTS_EX; + +typedef BOOL VCAPITYPE VIRTUALCHANNELENTRY(PCHANNEL_ENTRY_POINTS pEntryPoints); + +typedef VIRTUALCHANNELENTRY* PVIRTUALCHANNELENTRY; + +typedef BOOL VCAPITYPE VIRTUALCHANNELENTRYEX(PCHANNEL_ENTRY_POINTS_EX pEntryPointsEx, + PVOID pInitHandle); + +typedef VIRTUALCHANNELENTRYEX* PVIRTUALCHANNELENTRYEX; + +typedef HRESULT(VCAPITYPE* PFNVCAPIGETINSTANCE)(REFIID refiid, PULONG pNumObjs, PVOID* ppObjArray); + +#endif + +#if !defined(_WIN32) || !defined(_INC_WTSAPI) + +/** + * Windows Terminal Services API (wtsapi32.h) + */ + +#define WTS_CURRENT_SERVER ((HANDLE)NULL) +#define WTS_CURRENT_SERVER_HANDLE ((HANDLE)NULL) +#define WTS_CURRENT_SERVER_NAME (NULL) + +#define WTS_CURRENT_SESSION ((DWORD)-1) + +#define WTS_ANY_SESSION ((DWORD)-2) + +#define IDTIMEOUT 32000 +#define IDASYNC 32001 + +#define USERNAME_LENGTH 20 +#define CLIENTNAME_LENGTH 20 +#define CLIENTADDRESS_LENGTH 30 + +#define WTS_WSD_LOGOFF 0x00000001 +#define WTS_WSD_SHUTDOWN 0x00000002 +#define WTS_WSD_REBOOT 0x00000004 +#define WTS_WSD_POWEROFF 0x00000008 +#define WTS_WSD_FASTREBOOT 0x00000010 + +#define MAX_ELAPSED_TIME_LENGTH 15 +#define MAX_DATE_TIME_LENGTH 56 +#define WINSTATIONNAME_LENGTH 32 +#define DOMAIN_LENGTH 17 + +#define WTS_DRIVE_LENGTH 3 +#define WTS_LISTENER_NAME_LENGTH 32 +#define WTS_COMMENT_LENGTH 60 + +#define WTS_LISTENER_CREATE 0x00000001 +#define WTS_LISTENER_UPDATE 0x00000010 + +#define WTS_SECURITY_QUERY_INFORMATION 0x00000001 +#define WTS_SECURITY_SET_INFORMATION 0x00000002 +#define WTS_SECURITY_RESET 0x00000004 +#define WTS_SECURITY_VIRTUAL_CHANNELS 0x00000008 +#define WTS_SECURITY_REMOTE_CONTROL 0x00000010 +#define WTS_SECURITY_LOGON 0x00000020 +#define WTS_SECURITY_LOGOFF 0x00000040 +#define WTS_SECURITY_MESSAGE 0x00000080 +#define WTS_SECURITY_CONNECT 0x00000100 +#define WTS_SECURITY_DISCONNECT 0x00000200 + +#define WTS_SECURITY_GUEST_ACCESS (WTS_SECURITY_LOGON) + +#define WTS_SECURITY_CURRENT_GUEST_ACCESS (WTS_SECURITY_VIRTUAL_CHANNELS | WTS_SECURITY_LOGOFF) + +#define WTS_SECURITY_USER_ACCESS \ + (WTS_SECURITY_CURRENT_GUEST_ACCESS | WTS_SECURITY_QUERY_INFORMATION | WTS_SECURITY_CONNECT) + +#define WTS_SECURITY_CURRENT_USER_ACCESS \ + (WTS_SECURITY_SET_INFORMATION | WTS_SECURITY_RESET WTS_SECURITY_VIRTUAL_CHANNELS | \ + WTS_SECURITY_LOGOFF WTS_SECURITY_DISCONNECT) + +#define WTS_SECURITY_ALL_ACCESS \ + (STANDARD_RIGHTS_REQUIRED | WTS_SECURITY_QUERY_INFORMATION | WTS_SECURITY_SET_INFORMATION | \ + WTS_SECURITY_RESET | WTS_SECURITY_VIRTUAL_CHANNELS | WTS_SECURITY_REMOTE_CONTROL | \ + WTS_SECURITY_LOGON | WTS_SECURITY_MESSAGE | WTS_SECURITY_CONNECT | WTS_SECURITY_DISCONNECT) + +typedef enum +{ + WTSActive, + WTSConnected, + WTSConnectQuery, + WTSShadow, + WTSDisconnected, + WTSIdle, + WTSListen, + WTSReset, + WTSDown, + WTSInit +} WTS_CONNECTSTATE_CLASS; + +typedef struct +{ + LPWSTR pServerName; +} WTS_SERVER_INFOW, *PWTS_SERVER_INFOW; + +typedef struct +{ + LPSTR pServerName; +} WTS_SERVER_INFOA, *PWTS_SERVER_INFOA; + +typedef struct +{ + DWORD SessionId; + LPWSTR pWinStationName; + WTS_CONNECTSTATE_CLASS State; +} WTS_SESSION_INFOW, *PWTS_SESSION_INFOW; + +typedef struct +{ + DWORD SessionId; + LPSTR pWinStationName; + WTS_CONNECTSTATE_CLASS State; +} WTS_SESSION_INFOA, *PWTS_SESSION_INFOA; + +typedef struct +{ + DWORD ExecEnvId; + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + LPWSTR pSessionName; + LPWSTR pHostName; + LPWSTR pUserName; + LPWSTR pDomainName; + LPWSTR pFarmName; +} WTS_SESSION_INFO_1W, *PWTS_SESSION_INFO_1W; + +typedef struct +{ + DWORD ExecEnvId; + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + LPSTR pSessionName; + LPSTR pHostName; + LPSTR pUserName; + LPSTR pDomainName; + LPSTR pFarmName; +} WTS_SESSION_INFO_1A, *PWTS_SESSION_INFO_1A; + +typedef struct +{ + DWORD SessionId; + DWORD ProcessId; + LPWSTR pProcessName; + PSID pUserSid; +} WTS_PROCESS_INFOW, *PWTS_PROCESS_INFOW; + +typedef struct +{ + DWORD SessionId; + DWORD ProcessId; + LPSTR pProcessName; + PSID pUserSid; +} WTS_PROCESS_INFOA, *PWTS_PROCESS_INFOA; + +#define WTS_PROTOCOL_TYPE_CONSOLE 0 +#define WTS_PROTOCOL_TYPE_ICA 1 +#define WTS_PROTOCOL_TYPE_RDP 2 + +typedef enum +{ + WTSInitialProgram, + WTSApplicationName, + WTSWorkingDirectory, + WTSOEMId, + WTSSessionId, + WTSUserName, + WTSWinStationName, + WTSDomainName, + WTSConnectState, + WTSClientBuildNumber, + WTSClientName, + WTSClientDirectory, + WTSClientProductId, + WTSClientHardwareId, + WTSClientAddress, + WTSClientDisplay, + WTSClientProtocolType, + WTSIdleTime, + WTSLogonTime, + WTSIncomingBytes, + WTSOutgoingBytes, + WTSIncomingFrames, + WTSOutgoingFrames, + WTSClientInfo, + WTSSessionInfo, + WTSSessionInfoEx, + WTSConfigInfo, + WTSValidationInfo, + WTSSessionAddressV4, + WTSIsRemoteSession +} WTS_INFO_CLASS; + +typedef struct +{ + ULONG version; + ULONG fConnectClientDrivesAtLogon; + ULONG fConnectPrinterAtLogon; + ULONG fDisablePrinterRedirection; + ULONG fDisableDefaultMainClientPrinter; + ULONG ShadowSettings; + WCHAR LogonUserName[USERNAME_LENGTH + 1]; + WCHAR LogonDomain[DOMAIN_LENGTH + 1]; + WCHAR WorkDirectory[MAX_PATH + 1]; + WCHAR InitialProgram[MAX_PATH + 1]; + WCHAR ApplicationName[MAX_PATH + 1]; +} WTSCONFIGINFOW, *PWTSCONFIGINFOW; + +typedef struct +{ + ULONG version; + ULONG fConnectClientDrivesAtLogon; + ULONG fConnectPrinterAtLogon; + ULONG fDisablePrinterRedirection; + ULONG fDisableDefaultMainClientPrinter; + ULONG ShadowSettings; + CHAR LogonUserName[USERNAME_LENGTH + 1]; + CHAR LogonDomain[DOMAIN_LENGTH + 1]; + CHAR WorkDirectory[MAX_PATH + 1]; + CHAR InitialProgram[MAX_PATH + 1]; + CHAR ApplicationName[MAX_PATH + 1]; +} WTSCONFIGINFOA, *PWTSCONFIGINFOA; + +typedef struct +{ + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + DWORD IncomingBytes; + DWORD OutgoingBytes; + DWORD IncomingFrames; + DWORD OutgoingFrames; + DWORD IncomingCompressedBytes; + DWORD OutgoingCompressedBytes; + WCHAR WinStationName[WINSTATIONNAME_LENGTH]; + WCHAR Domain[DOMAIN_LENGTH]; + WCHAR UserName[USERNAME_LENGTH + 1]; + LARGE_INTEGER ConnectTime; + LARGE_INTEGER DisconnectTime; + LARGE_INTEGER LastInputTime; + LARGE_INTEGER LogonTime; + LARGE_INTEGER _CurrentTime; /* Conflicts with X11 headers */ +} WTSINFOW, *PWTSINFOW; + +typedef struct +{ + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + DWORD IncomingBytes; + DWORD OutgoingBytes; + DWORD IncomingFrames; + DWORD OutgoingFrames; + DWORD IncomingCompressedBytes; + DWORD OutgoingCompressedBy; + CHAR WinStationName[WINSTATIONNAME_LENGTH]; + CHAR Domain[DOMAIN_LENGTH]; + CHAR UserName[USERNAME_LENGTH + 1]; + LARGE_INTEGER ConnectTime; + LARGE_INTEGER DisconnectTime; + LARGE_INTEGER LastInputTime; + LARGE_INTEGER LogonTime; + LARGE_INTEGER _CurrentTime; /* Conflicts with X11 headers */ +} WTSINFOA, *PWTSINFOA; + +#define WTS_SESSIONSTATE_UNKNOWN 0xFFFFFFFF +#define WTS_SESSIONSTATE_LOCK 0x00000000 +#define WTS_SESSIONSTATE_UNLOCK 0x00000001 + +typedef struct +{ + ULONG SessionId; + WTS_CONNECTSTATE_CLASS SessionState; + LONG SessionFlags; + WCHAR WinStationName[WINSTATIONNAME_LENGTH + 1]; + WCHAR UserName[USERNAME_LENGTH + 1]; + WCHAR DomainName[DOMAIN_LENGTH + 1]; + LARGE_INTEGER LogonTime; + LARGE_INTEGER ConnectTime; + LARGE_INTEGER DisconnectTime; + LARGE_INTEGER LastInputTime; + LARGE_INTEGER _CurrentTime; /* Conflicts with X11 headers */ + DWORD IncomingBytes; + DWORD OutgoingBytes; + DWORD IncomingFrames; + DWORD OutgoingFrames; + DWORD IncomingCompressedBytes; + DWORD OutgoingCompressedBytes; +} WTSINFOEX_LEVEL1_W, *PWTSINFOEX_LEVEL1_W; + +typedef struct +{ + ULONG SessionId; + WTS_CONNECTSTATE_CLASS SessionState; + LONG SessionFlags; + CHAR WinStationName[WINSTATIONNAME_LENGTH + 1]; + CHAR UserName[USERNAME_LENGTH + 1]; + CHAR DomainName[DOMAIN_LENGTH + 1]; + LARGE_INTEGER LogonTime; + LARGE_INTEGER ConnectTime; + LARGE_INTEGER DisconnectTime; + LARGE_INTEGER LastInputTime; + LARGE_INTEGER _CurrentTime; /* Conflicts with X11 headers */ + DWORD IncomingBytes; + DWORD OutgoingBytes; + DWORD IncomingFrames; + DWORD OutgoingFrames; + DWORD IncomingCompressedBytes; + DWORD OutgoingCompressedBytes; +} WTSINFOEX_LEVEL1_A, *PWTSINFOEX_LEVEL1_A; + +typedef union +{ + WTSINFOEX_LEVEL1_W WTSInfoExLevel1; +} WTSINFOEX_LEVEL_W, *PWTSINFOEX_LEVEL_W; + +typedef union +{ + WTSINFOEX_LEVEL1_A WTSInfoExLevel1; +} WTSINFOEX_LEVEL_A, *PWTSINFOEX_LEVEL_A; + +typedef struct +{ + DWORD Level; + WTSINFOEX_LEVEL_W Data; +} WTSINFOEXW, *PWTSINFOEXW; + +typedef struct +{ + DWORD Level; + WTSINFOEX_LEVEL_A Data; +} WTSINFOEXA, *PWTSINFOEXA; + +typedef struct +{ + WCHAR ClientName[CLIENTNAME_LENGTH + 1]; + WCHAR Domain[DOMAIN_LENGTH + 1]; + WCHAR UserName[USERNAME_LENGTH + 1]; + WCHAR WorkDirectory[MAX_PATH + 1]; + WCHAR InitialProgram[MAX_PATH + 1]; + BYTE EncryptionLevel; + ULONG ClientAddressFamily; + USHORT ClientAddress[CLIENTADDRESS_LENGTH + 1]; + USHORT HRes; + USHORT VRes; + USHORT ColorDepth; + WCHAR ClientDirectory[MAX_PATH + 1]; + ULONG ClientBuildNumber; + ULONG ClientHardwareId; + USHORT ClientProductId; + USHORT OutBufCountHost; + USHORT OutBufCountClient; + USHORT OutBufLength; + WCHAR DeviceId[MAX_PATH + 1]; +} WTSCLIENTW, *PWTSCLIENTW; + +typedef struct +{ + CHAR ClientName[CLIENTNAME_LENGTH + 1]; + CHAR Domain[DOMAIN_LENGTH + 1]; + CHAR UserName[USERNAME_LENGTH + 1]; + CHAR WorkDirectory[MAX_PATH + 1]; + CHAR InitialProgram[MAX_PATH + 1]; + BYTE EncryptionLevel; + ULONG ClientAddressFamily; + USHORT ClientAddress[CLIENTADDRESS_LENGTH + 1]; + USHORT HRes; + USHORT VRes; + USHORT ColorDepth; + CHAR ClientDirectory[MAX_PATH + 1]; + ULONG ClientBuildNumber; + ULONG ClientHardwareId; + USHORT ClientProductId; + USHORT OutBufCountHost; + USHORT OutBufCountClient; + USHORT OutBufLength; + CHAR DeviceId[MAX_PATH + 1]; +} WTSCLIENTA, *PWTSCLIENTA; + +#define PRODUCTINFO_COMPANYNAME_LENGTH 256 +#define PRODUCTINFO_PRODUCTID_LENGTH 4 + +typedef struct +{ + CHAR CompanyName[PRODUCTINFO_COMPANYNAME_LENGTH]; + CHAR ProductID[PRODUCTINFO_PRODUCTID_LENGTH]; +} PRODUCT_INFOA; + +typedef struct +{ + WCHAR CompanyName[PRODUCTINFO_COMPANYNAME_LENGTH]; + WCHAR ProductID[PRODUCTINFO_PRODUCTID_LENGTH]; +} PRODUCT_INFOW; + +#define VALIDATIONINFORMATION_LICENSE_LENGTH 16384 +#define VALIDATIONINFORMATION_HARDWAREID_LENGTH 20 + +typedef struct +{ + PRODUCT_INFOA ProductInfo; + BYTE License[VALIDATIONINFORMATION_LICENSE_LENGTH]; + DWORD LicenseLength; + BYTE HardwareID[VALIDATIONINFORMATION_HARDWAREID_LENGTH]; + DWORD HardwareIDLength; +} WTS_VALIDATION_INFORMATIONA, *PWTS_VALIDATION_INFORMATIONA; + +typedef struct +{ + PRODUCT_INFOW ProductInfo; + BYTE License[VALIDATIONINFORMATION_LICENSE_LENGTH]; + DWORD LicenseLength; + BYTE HardwareID[VALIDATIONINFORMATION_HARDWAREID_LENGTH]; + DWORD HardwareIDLength; +} WTS_VALIDATION_INFORMATIONW, *PWTS_VALIDATION_INFORMATIONW; + +typedef struct +{ + DWORD AddressFamily; + BYTE Address[20]; +} WTS_CLIENT_ADDRESS, *PWTS_CLIENT_ADDRESS; + +typedef struct +{ + DWORD HorizontalResolution; + DWORD VerticalResolution; + DWORD ColorDepth; +} WTS_CLIENT_DISPLAY, *PWTS_CLIENT_DISPLAY; + +typedef enum +{ + WTSUserConfigInitialProgram, + WTSUserConfigWorkingDirectory, + WTSUserConfigfInheritInitialProgram, + WTSUserConfigfAllowLogonTerminalServer, + WTSUserConfigTimeoutSettingsConnections, + WTSUserConfigTimeoutSettingsDisconnections, + WTSUserConfigTimeoutSettingsIdle, + WTSUserConfigfDeviceClientDrives, + WTSUserConfigfDeviceClientPrinters, + WTSUserConfigfDeviceClientDefaultPrinter, + WTSUserConfigBrokenTimeoutSettings, + WTSUserConfigReconnectSettings, + WTSUserConfigModemCallbackSettings, + WTSUserConfigModemCallbackPhoneNumber, + WTSUserConfigShadowingSettings, + WTSUserConfigTerminalServerProfilePath, + WTSUserConfigTerminalServerHomeDir, + WTSUserConfigTerminalServerHomeDirDrive, + WTSUserConfigfTerminalServerRemoteHomeDir, + WTSUserConfigUser +} WTS_CONFIG_CLASS; + +typedef enum +{ + WTSUserConfigSourceSAM +} WTS_CONFIG_SOURCE; + +typedef struct +{ + DWORD Source; + DWORD InheritInitialProgram; + DWORD AllowLogonTerminalServer; + DWORD TimeoutSettingsConnections; + DWORD TimeoutSettingsDisconnections; + DWORD TimeoutSettingsIdle; + DWORD DeviceClientDrives; + DWORD DeviceClientPrinters; + DWORD ClientDefaultPrinter; + DWORD BrokenTimeoutSettings; + DWORD ReconnectSettings; + DWORD ShadowingSettings; + DWORD TerminalServerRemoteHomeDir; + CHAR InitialProgram[MAX_PATH + 1]; + CHAR WorkDirectory[MAX_PATH + 1]; + CHAR TerminalServerProfilePath[MAX_PATH + 1]; + CHAR TerminalServerHomeDir[MAX_PATH + 1]; + CHAR TerminalServerHomeDirDrive[WTS_DRIVE_LENGTH + 1]; +} WTSUSERCONFIGA, *PWTSUSERCONFIGA; + +typedef struct +{ + DWORD Source; + DWORD InheritInitialProgram; + DWORD AllowLogonTerminalServer; + DWORD TimeoutSettingsConnections; + DWORD TimeoutSettingsDisconnections; + DWORD TimeoutSettingsIdle; + DWORD DeviceClientDrives; + DWORD DeviceClientPrinters; + DWORD ClientDefaultPrinter; + DWORD BrokenTimeoutSettings; + DWORD ReconnectSettings; + DWORD ShadowingSettings; + DWORD TerminalServerRemoteHomeDir; + WCHAR InitialProgram[MAX_PATH + 1]; + WCHAR WorkDirectory[MAX_PATH + 1]; + WCHAR TerminalServerProfilePath[MAX_PATH + 1]; + WCHAR TerminalServerHomeDir[MAX_PATH + 1]; + WCHAR TerminalServerHomeDirDrive[WTS_DRIVE_LENGTH + 1]; +} WTSUSERCONFIGW, *PWTSUSERCONFIGW; + +#define WTS_EVENT_NONE 0x00000000 +#define WTS_EVENT_CREATE 0x00000001 +#define WTS_EVENT_DELETE 0x00000002 +#define WTS_EVENT_RENAME 0x00000004 +#define WTS_EVENT_CONNECT 0x00000008 +#define WTS_EVENT_DISCONNECT 0x00000010 +#define WTS_EVENT_LOGON 0x00000020 +#define WTS_EVENT_LOGOFF 0x00000040 +#define WTS_EVENT_STATECHANGE 0x00000080 +#define WTS_EVENT_LICENSE 0x00000100 +#define WTS_EVENT_ALL 0x7FFFFFFF +#define WTS_EVENT_FLUSH 0x80000000 + +#define REMOTECONTROL_KBDSHIFT_HOTKEY 0x1 +#define REMOTECONTROL_KBDCTRL_HOTKEY 0x2 +#define REMOTECONTROL_KBDALT_HOTKEY 0x4 + +typedef enum +{ + WTSVirtualClientData, + WTSVirtualFileHandle, + WTSVirtualEventHandle, /* Extended */ + WTSVirtualChannelReady, /* Extended */ + WTSVirtualChannelOpenStatus /* Extended */ +} WTS_VIRTUAL_CLASS; + +typedef struct +{ + DWORD AddressFamily; + BYTE Address[20]; +} WTS_SESSION_ADDRESS, *PWTS_SESSION_ADDRESS; + +#define WTS_CHANNEL_OPTION_DYNAMIC 0x00000001 +#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_LOW 0x00000000 +#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_MED 0x00000002 +#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_HIGH 0x00000004 +#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_REAL 0x00000006 +#define WTS_CHANNEL_OPTION_DYNAMIC_NO_COMPRESS 0x00000008 + +#define NOTIFY_FOR_ALL_SESSIONS 1 +#define NOTIFY_FOR_THIS_SESSION 0 + +#define WTS_PROCESS_INFO_LEVEL_0 0 +#define WTS_PROCESS_INFO_LEVEL_1 1 + +typedef struct +{ + DWORD SessionId; + DWORD ProcessId; + LPWSTR pProcessName; + PSID pUserSid; + DWORD NumberOfThreads; + DWORD HandleCount; + DWORD PagefileUsage; + DWORD PeakPagefileUsage; + DWORD WorkingSetSize; + DWORD PeakWorkingSetSize; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; +} WTS_PROCESS_INFO_EXW, *PWTS_PROCESS_INFO_EXW; + +typedef struct +{ + DWORD SessionId; + DWORD ProcessId; + LPSTR pProcessName; + PSID pUserSid; + DWORD NumberOfThreads; + DWORD HandleCount; + DWORD PagefileUsage; + DWORD PeakPagefileUsage; + DWORD WorkingSetSize; + DWORD PeakWorkingSetSize; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; +} WTS_PROCESS_INFO_EXA, *PWTS_PROCESS_INFO_EXA; + +typedef enum +{ + WTSTypeProcessInfoLevel0, + WTSTypeProcessInfoLevel1, + WTSTypeSessionInfoLevel1 +} WTS_TYPE_CLASS; + +typedef WCHAR WTSLISTENERNAMEW[WTS_LISTENER_NAME_LENGTH + 1]; +typedef WTSLISTENERNAMEW* PWTSLISTENERNAMEW; +typedef CHAR WTSLISTENERNAMEA[WTS_LISTENER_NAME_LENGTH + 1]; +typedef WTSLISTENERNAMEA* PWTSLISTENERNAMEA; + +typedef struct +{ + ULONG version; + ULONG fEnableListener; + ULONG MaxConnectionCount; + ULONG fPromptForPassword; + ULONG fInheritColorDepth; + ULONG ColorDepth; + ULONG fInheritBrokenTimeoutSettings; + ULONG BrokenTimeoutSettings; + ULONG fDisablePrinterRedirection; + ULONG fDisableDriveRedirection; + ULONG fDisableComPortRedirection; + ULONG fDisableLPTPortRedirection; + ULONG fDisableClipboardRedirection; + ULONG fDisableAudioRedirection; + ULONG fDisablePNPRedirection; + ULONG fDisableDefaultMainClientPrinter; + ULONG LanAdapter; + ULONG PortNumber; + ULONG fInheritShadowSettings; + ULONG ShadowSettings; + ULONG TimeoutSettingsConnection; + ULONG TimeoutSettingsDisconnection; + ULONG TimeoutSettingsIdle; + ULONG SecurityLayer; + ULONG MinEncryptionLevel; + ULONG UserAuthentication; + WCHAR Comment[WTS_COMMENT_LENGTH + 1]; + WCHAR LogonUserName[USERNAME_LENGTH + 1]; + WCHAR LogonDomain[DOMAIN_LENGTH + 1]; + WCHAR WorkDirectory[MAX_PATH + 1]; + WCHAR InitialProgram[MAX_PATH + 1]; +} WTSLISTENERCONFIGW, *PWTSLISTENERCONFIGW; + +typedef struct +{ + ULONG version; + ULONG fEnableListener; + ULONG MaxConnectionCount; + ULONG fPromptForPassword; + ULONG fInheritColorDepth; + ULONG ColorDepth; + ULONG fInheritBrokenTimeoutSettings; + ULONG BrokenTimeoutSettings; + ULONG fDisablePrinterRedirection; + ULONG fDisableDriveRedirection; + ULONG fDisableComPortRedirection; + ULONG fDisableLPTPortRedirection; + ULONG fDisableClipboardRedirection; + ULONG fDisableAudioRedirection; + ULONG fDisablePNPRedirection; + ULONG fDisableDefaultMainClientPrinter; + ULONG LanAdapter; + ULONG PortNumber; + ULONG fInheritShadowSettings; + ULONG ShadowSettings; + ULONG TimeoutSettingsConnection; + ULONG TimeoutSettingsDisconnection; + ULONG TimeoutSettingsIdle; + ULONG SecurityLayer; + ULONG MinEncryptionLevel; + ULONG UserAuthentication; + CHAR Comment[WTS_COMMENT_LENGTH + 1]; + CHAR LogonUserName[USERNAME_LENGTH + 1]; + CHAR LogonDomain[DOMAIN_LENGTH + 1]; + CHAR WorkDirectory[MAX_PATH + 1]; + CHAR InitialProgram[MAX_PATH + 1]; +} WTSLISTENERCONFIGA, *PWTSLISTENERCONFIGA; + +#ifdef UNICODE +#define WTS_SERVER_INFO WTS_SERVER_INFOW +#define PWTS_SERVER_INFO PWTS_SERVER_INFOW +#define WTS_SESSION_INFO WTS_SESSION_INFOW +#define PWTS_SESSION_INFO PWTS_SESSION_INFOW +#define WTS_SESSION_INFO_1 WTS_SESSION_INFO_1W +#define PWTS_SESSION_INFO_1 PWTS_SESSION_INFO_1W +#define WTS_PROCESS_INFO WTS_PROCESS_INFOW +#define PWTS_PROCESS_INFO PWTS_PROCESS_INFOW +#define WTSCONFIGINFO WTSCONFIGINFOW +#define PWTSCONFIGINFO PWTSCONFIGINFOW +#define WTSINFO WTSINFOW +#define PWTSINFO PWTSINFOW +#define WTSINFOEX WTSINFOEXW +#define PWTSINFOEX PWTSINFOEXW +#define WTSINFOEX_LEVEL WTSINFOEX_LEVEL_W +#define PWTSINFOEX_LEVEL PWTSINFOEX_LEVEL_W +#define WTSINFOEX_LEVEL1 WTSINFOEX_LEVEL1_W +#define PWTSINFOEX_LEVEL1 PWTSINFOEX_LEVEL1_W +#define WTSCLIENT WTSCLIENTW +#define PWTSCLIENT PWTSCLIENTW +#define PRODUCT_INFO PRODUCT_INFOW +#define WTS_VALIDATION_INFORMATION WTS_VALIDATION_INFORMATIONW +#define PWTS_VALIDATION_INFORMATION PWTS_VALIDATION_INFORMATIONW +#define WTSUSERCONFIG WTSUSERCONFIGW +#define PWTSUSERCONFIG PWTSUSERCONFIGW +#define WTS_PROCESS_INFO_EX WTS_PROCESS_INFO_EXW +#define PWTS_PROCESS_INFO_EX PWTS_PROCESS_INFO_EXW +#define WTSLISTENERNAME WTSLISTENERNAMEW +#define PWTSLISTENERNAME PWTSLISTENERNAMEW +#define WTSLISTENERCONFIG WTSLISTENERCONFIGW +#define PWTSLISTENERCONFIG PWTSLISTENERCONFIGW +#else +#define WTS_SERVER_INFO WTS_SERVER_INFOA +#define PWTS_SERVER_INFO PWTS_SERVER_INFOA +#define WTS_SESSION_INFO WTS_SESSION_INFOA +#define PWTS_SESSION_INFO PWTS_SESSION_INFOA +#define WTS_SESSION_INFO_1 WTS_SESSION_INFO_1A +#define PWTS_SESSION_INFO_1 PWTS_SESSION_INFO_1A +#define WTS_PROCESS_INFO WTS_PROCESS_INFOA +#define PWTS_PROCESS_INFO PWTS_PROCESS_INFOA +#define WTSCONFIGINFO WTSCONFIGINFOA +#define PWTSCONFIGINFO PWTSCONFIGINFOA +#define WTSINFO WTSINFOA +#define PWTSINFO PWTSINFOA +#define WTSINFOEX WTSINFOEXA +#define PWTSINFOEX PWTSINFOEXA +#define WTSINFOEX_LEVEL WTSINFOEX_LEVEL_A +#define PWTSINFOEX_LEVEL PWTSINFOEX_LEVEL_A +#define WTSINFOEX_LEVEL1 WTSINFOEX_LEVEL1_A +#define PWTSINFOEX_LEVEL1 PWTSINFOEX_LEVEL1_A +#define WTSCLIENT WTSCLIENTA +#define PWTSCLIENT PWTSCLIENTA +#define PRODUCT_INFO PRODUCT_INFOA +#define WTS_VALIDATION_INFORMATION WTS_VALIDATION_INFORMATIONA +#define PWTS_VALIDATION_INFORMATION PWTS_VALIDATION_INFORMATIONA +#define WTSUSERCONFIG WTSUSERCONFIGA +#define PWTSUSERCONFIG PWTSUSERCONFIGA +#define WTS_PROCESS_INFO_EX WTS_PROCESS_INFO_EXA +#define PWTS_PROCESS_INFO_EX PWTS_PROCESS_INFO_EXA +#define WTSLISTENERNAME WTSLISTENERNAMEA +#define PWTSLISTENERNAME PWTSLISTENERNAMEA +#define WTSLISTENERCONFIG WTSLISTENERCONFIGA +#define PWTSLISTENERCONFIG PWTSLISTENERCONFIGA +#endif + +#define REMOTECONTROL_FLAG_DISABLE_KEYBOARD 0x00000001 +#define REMOTECONTROL_FLAG_DISABLE_MOUSE 0x00000002 +#define REMOTECONTROL_FLAG_DISABLE_INPUT \ + REMOTECONTROL_FLAG_DISABLE_KEYBOARD | REMOTECONTROL_FLAG_DISABLE_MOUSE + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL WINAPI WTSStopRemoteControlSession(ULONG LogonId); + + WINPR_API BOOL WINAPI WTSStartRemoteControlSessionW(LPWSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers); + WINPR_API BOOL WINAPI WTSStartRemoteControlSessionA(LPSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers); + + WINPR_API BOOL WINAPI WTSStartRemoteControlSessionExW(LPWSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers, DWORD flags); + WINPR_API BOOL WINAPI WTSStartRemoteControlSessionExA(LPSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers, DWORD flags); + + WINPR_API BOOL WINAPI WTSConnectSessionW(ULONG LogonId, ULONG TargetLogonId, PWSTR pPassword, + BOOL bWait); + WINPR_API BOOL WINAPI WTSConnectSessionA(ULONG LogonId, ULONG TargetLogonId, PSTR pPassword, + BOOL bWait); + + WINPR_API BOOL WINAPI WTSEnumerateServersW(LPWSTR pDomainName, DWORD Reserved, DWORD Version, + PWTS_SERVER_INFOW* ppServerInfo, DWORD* pCount); + WINPR_API BOOL WINAPI WTSEnumerateServersA(LPSTR pDomainName, DWORD Reserved, DWORD Version, + PWTS_SERVER_INFOA* ppServerInfo, DWORD* pCount); + + WINPR_API HANDLE WINAPI WTSOpenServerW(LPWSTR pServerName); + WINPR_API HANDLE WINAPI WTSOpenServerA(LPSTR pServerName); + + WINPR_API HANDLE WINAPI WTSOpenServerExW(LPWSTR pServerName); + WINPR_API HANDLE WINAPI WTSOpenServerExA(LPSTR pServerName); + + WINPR_API VOID WINAPI WTSCloseServer(HANDLE hServer); + + WINPR_API BOOL WINAPI WTSEnumerateSessionsW(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_SESSION_INFOW* ppSessionInfo, DWORD* pCount); + WINPR_API BOOL WINAPI WTSEnumerateSessionsA(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_SESSION_INFOA* ppSessionInfo, DWORD* pCount); + + WINPR_API BOOL WINAPI WTSEnumerateSessionsExW(HANDLE hServer, DWORD* pLevel, DWORD Filter, + PWTS_SESSION_INFO_1W* ppSessionInfo, + DWORD* pCount); + WINPR_API BOOL WINAPI WTSEnumerateSessionsExA(HANDLE hServer, DWORD* pLevel, DWORD Filter, + PWTS_SESSION_INFO_1A* ppSessionInfo, + DWORD* pCount); + + WINPR_API BOOL WINAPI WTSEnumerateProcessesW(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_PROCESS_INFOW* ppProcessInfo, DWORD* pCount); + WINPR_API BOOL WINAPI WTSEnumerateProcessesA(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_PROCESS_INFOA* ppProcessInfo, DWORD* pCount); + + WINPR_API BOOL WINAPI WTSTerminateProcess(HANDLE hServer, DWORD ProcessId, DWORD ExitCode); + + WINPR_API BOOL WINAPI WTSQuerySessionInformationW(HANDLE hServer, DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, LPWSTR* ppBuffer, + DWORD* pBytesReturned); + WINPR_API BOOL WINAPI WTSQuerySessionInformationA(HANDLE hServer, DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, LPSTR* ppBuffer, + DWORD* pBytesReturned); + + WINPR_API BOOL WINAPI WTSQueryUserConfigW(LPWSTR pServerName, LPWSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPWSTR* ppBuffer, + DWORD* pBytesReturned); + WINPR_API BOOL WINAPI WTSQueryUserConfigA(LPSTR pServerName, LPSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPSTR* ppBuffer, + DWORD* pBytesReturned); + + WINPR_API BOOL WINAPI WTSSetUserConfigW(LPWSTR pServerName, LPWSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPWSTR pBuffer, + DWORD DataLength); + WINPR_API BOOL WINAPI WTSSetUserConfigA(LPSTR pServerName, LPSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPSTR pBuffer, + DWORD DataLength); + + WINPR_API BOOL WINAPI WTSSendMessageW(HANDLE hServer, DWORD SessionId, LPWSTR pTitle, + DWORD TitleLength, LPWSTR pMessage, DWORD MessageLength, + DWORD Style, DWORD Timeout, DWORD* pResponse, BOOL bWait); + WINPR_API BOOL WINAPI WTSSendMessageA(HANDLE hServer, DWORD SessionId, LPSTR pTitle, + DWORD TitleLength, LPSTR pMessage, DWORD MessageLength, + DWORD Style, DWORD Timeout, DWORD* pResponse, BOOL bWait); + + WINPR_API BOOL WINAPI WTSDisconnectSession(HANDLE hServer, DWORD SessionId, BOOL bWait); + + WINPR_API BOOL WINAPI WTSLogoffSession(HANDLE hServer, DWORD SessionId, BOOL bWait); + + WINPR_API BOOL WINAPI WTSShutdownSystem(HANDLE hServer, DWORD ShutdownFlag); + + WINPR_API BOOL WINAPI WTSWaitSystemEvent(HANDLE hServer, DWORD EventMask, DWORD* pEventFlags); + + WINPR_API HANDLE WINAPI WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, + LPSTR pVirtualName); + + WINPR_API HANDLE WINAPI WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, + DWORD flags); + + WINPR_API BOOL WINAPI WTSVirtualChannelClose(HANDLE hChannelHandle); + + WINPR_API BOOL WINAPI WTSVirtualChannelRead(HANDLE hChannelHandle, ULONG TimeOut, PCHAR Buffer, + ULONG BufferSize, PULONG pBytesRead); + + WINPR_API BOOL WINAPI WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, ULONG Length, + PULONG pBytesWritten); + + WINPR_API BOOL WINAPI WTSVirtualChannelPurgeInput(HANDLE hChannelHandle); + + WINPR_API BOOL WINAPI WTSVirtualChannelPurgeOutput(HANDLE hChannelHandle); + + WINPR_API BOOL WINAPI WTSVirtualChannelQuery(HANDLE hChannelHandle, + WTS_VIRTUAL_CLASS WtsVirtualClass, PVOID* ppBuffer, + DWORD* pBytesReturned); + + WINPR_API VOID WINAPI WTSFreeMemory(PVOID pMemory); + + WINPR_API BOOL WINAPI WTSRegisterSessionNotification(HWND hWnd, DWORD dwFlags); + + WINPR_API BOOL WINAPI WTSUnRegisterSessionNotification(HWND hWnd); + + WINPR_API BOOL WINAPI WTSRegisterSessionNotificationEx(HANDLE hServer, HWND hWnd, + DWORD dwFlags); + + WINPR_API BOOL WINAPI WTSUnRegisterSessionNotificationEx(HANDLE hServer, HWND hWnd); + + WINPR_API BOOL WINAPI WTSQueryUserToken(ULONG SessionId, PHANDLE phToken); + + WINPR_API BOOL WINAPI WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, + ULONG NumberOfEntries); + WINPR_API BOOL WINAPI WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, + ULONG NumberOfEntries); + + WINPR_API BOOL WINAPI WTSEnumerateProcessesExW(HANDLE hServer, DWORD* pLevel, DWORD SessionId, + LPWSTR* ppProcessInfo, DWORD* pCount); + WINPR_API BOOL WINAPI WTSEnumerateProcessesExA(HANDLE hServer, DWORD* pLevel, DWORD SessionId, + LPSTR* ppProcessInfo, DWORD* pCount); + + WINPR_API BOOL WINAPI WTSEnumerateListenersW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + PWTSLISTENERNAMEW pListeners, DWORD* pCount); + WINPR_API BOOL WINAPI WTSEnumerateListenersA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + PWTSLISTENERNAMEA pListeners, DWORD* pCount); + + WINPR_API BOOL WINAPI WTSQueryListenerConfigW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, + PWTSLISTENERCONFIGW pBuffer); + WINPR_API BOOL WINAPI WTSQueryListenerConfigA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, PWTSLISTENERCONFIGA pBuffer); + + WINPR_API BOOL WINAPI WTSCreateListenerW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, PWTSLISTENERCONFIGW pBuffer, + DWORD flag); + WINPR_API BOOL WINAPI WTSCreateListenerA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, PWTSLISTENERCONFIGA pBuffer, + DWORD flag); + + WINPR_API BOOL WINAPI WTSSetListenerSecurityW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor); + WINPR_API BOOL WINAPI WTSSetListenerSecurityA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor); + + WINPR_API BOOL WINAPI WTSGetListenerSecurityW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD nLength, LPDWORD lpnLengthNeeded); + WINPR_API BOOL WINAPI WTSGetListenerSecurityA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD nLength, LPDWORD lpnLengthNeeded); + + /** + * WTSEnableChildSessions, WTSIsChildSessionsEnabled and WTSGetChildSessionId + * are not explicitly declared as WINAPI like other WTSAPI functions. + * Since they are declared as extern "C", we explicitly declare them as CDECL. + */ + + WINPR_API BOOL CDECL WTSEnableChildSessions(BOOL bEnable); + + WINPR_API BOOL CDECL WTSIsChildSessionsEnabled(PBOOL pbEnabled); + + WINPR_API BOOL CDECL WTSGetChildSessionId(PULONG pSessionId); + + WINPR_API BOOL CDECL WTSLogonUser(HANDLE hServer, LPCSTR username, LPCSTR password, + LPCSTR domain); + + WINPR_API BOOL CDECL WTSLogoffUser(HANDLE hServer); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define WTSStartRemoteControlSession WTSStartRemoteControlSessionW +#define WTSStartRemoteControlSessionEx WTSStartRemoteControlSessionExW +#define WTSConnectSession WTSConnectSessionW +#define WTSEnumerateServers WTSEnumerateServersW +#define WTSOpenServer WTSOpenServerW +#define WTSOpenServerEx WTSOpenServerExW +#define WTSEnumerateSessions WTSEnumerateSessionsW +#define WTSEnumerateSessionsEx WTSEnumerateSessionsExW +#define WTSEnumerateProcesses WTSEnumerateProcessesW +#define WTSQuerySessionInformation WTSQuerySessionInformationW +#define WTSQueryUserConfig WTSQueryUserConfigW +#define WTSSetUserConfig WTSSetUserConfigW +#define WTSSendMessage WTSSendMessageW +#define WTSFreeMemoryEx WTSFreeMemoryExW +#define WTSEnumerateProcessesEx WTSEnumerateProcessesExW +#define WTSEnumerateListeners WTSEnumerateListenersW +#define WTSQueryListenerConfig WTSQueryListenerConfigW +#define WTSCreateListener WTSCreateListenerW +#define WTSSetListenerSecurity WTSSetListenerSecurityW +#define WTSGetListenerSecurity WTSGetListenerSecurityW +#else +#define WTSStartRemoteControlSession WTSStartRemoteControlSessionA +#define WTSStartRemoteControlSessionEx WTSStartRemoteControlSessionExA +#define WTSConnectSession WTSConnectSessionA +#define WTSEnumerateServers WTSEnumerateServersA +#define WTSOpenServer WTSOpenServerA +#define WTSOpenServerEx WTSOpenServerExA +#define WTSEnumerateSessions WTSEnumerateSessionsA +#define WTSEnumerateSessionsEx WTSEnumerateSessionsExA +#define WTSEnumerateProcesses WTSEnumerateProcessesA +#define WTSQuerySessionInformation WTSQuerySessionInformationA +#define WTSQueryUserConfig WTSQueryUserConfigA +#define WTSSetUserConfig WTSSetUserConfigA +#define WTSSendMessage WTSSendMessageA +#define WTSFreeMemoryEx WTSFreeMemoryExA +#define WTSEnumerateProcessesEx WTSEnumerateProcessesExA +#define WTSEnumerateListeners WTSEnumerateListenersA +#define WTSQueryListenerConfig WTSQueryListenerConfigA +#define WTSCreateListener WTSCreateListenerA +#define WTSSetListenerSecurity WTSSetListenerSecurityA +#define WTSGetListenerSecurity WTSGetListenerSecurityA +#endif + +#endif + +#ifndef _WIN32 + +/** + * WTSGetActiveConsoleSessionId is declared in WinBase.h + * and exported by kernel32.dll, so we have to treat it separately. + */ + +WINPR_API DWORD WINAPI WTSGetActiveConsoleSessionId(void); + +#endif + +typedef BOOL(WINAPI* WTS_STOP_REMOTE_CONTROL_SESSION_FN)(ULONG LogonId); + +typedef BOOL(WINAPI* WTS_START_REMOTE_CONTROL_SESSION_FN_W)(LPWSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers); +typedef BOOL(WINAPI* WTS_START_REMOTE_CONTROL_SESSION_FN_A)(LPSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers); + +typedef BOOL(WINAPI* WTS_START_REMOTE_CONTROL_SESSION_EX_FN_W)(LPWSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers, DWORD flags); +typedef BOOL(WINAPI* WTS_START_REMOTE_CONTROL_SESSION_EX_FN_A)(LPSTR pTargetServerName, + ULONG TargetLogonId, BYTE HotkeyVk, + USHORT HotkeyModifiers, DWORD flags); + +typedef BOOL(WINAPI* WTS_CONNECT_SESSION_FN_W)(ULONG LogonId, ULONG TargetLogonId, PWSTR pPassword, + BOOL bWait); +typedef BOOL(WINAPI* WTS_CONNECT_SESSION_FN_A)(ULONG LogonId, ULONG TargetLogonId, PSTR pPassword, + BOOL bWait); + +typedef BOOL(WINAPI* WTS_ENUMERATE_SERVERS_FN_W)(LPWSTR pDomainName, DWORD Reserved, DWORD Version, + PWTS_SERVER_INFOW* ppServerInfo, DWORD* pCount); +typedef BOOL(WINAPI* WTS_ENUMERATE_SERVERS_FN_A)(LPSTR pDomainName, DWORD Reserved, DWORD Version, + PWTS_SERVER_INFOA* ppServerInfo, DWORD* pCount); + +typedef HANDLE(WINAPI* WTS_OPEN_SERVER_FN_W)(LPWSTR pServerName); +typedef HANDLE(WINAPI* WTS_OPEN_SERVER_FN_A)(LPSTR pServerName); + +typedef HANDLE(WINAPI* WTS_OPEN_SERVER_EX_FN_W)(LPWSTR pServerName); +typedef HANDLE(WINAPI* WTS_OPEN_SERVER_EX_FN_A)(LPSTR pServerName); + +typedef VOID(WINAPI* WTS_CLOSE_SERVER_FN)(HANDLE hServer); + +typedef BOOL(WINAPI* WTS_ENUMERATE_SESSIONS_FN_W)(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_SESSION_INFOW* ppSessionInfo, DWORD* pCount); +typedef BOOL(WINAPI* WTS_ENUMERATE_SESSIONS_FN_A)(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_SESSION_INFOA* ppSessionInfo, DWORD* pCount); + +typedef BOOL(WINAPI* WTS_ENUMERATE_SESSIONS_EX_FN_W)(HANDLE hServer, DWORD* pLevel, DWORD Filter, + PWTS_SESSION_INFO_1W* ppSessionInfo, + DWORD* pCount); +typedef BOOL(WINAPI* WTS_ENUMERATE_SESSIONS_EX_FN_A)(HANDLE hServer, DWORD* pLevel, DWORD Filter, + PWTS_SESSION_INFO_1A* ppSessionInfo, + DWORD* pCount); + +typedef BOOL(WINAPI* WTS_ENUMERATE_PROCESSES_FN_W)(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_PROCESS_INFOW* ppProcessInfo, + DWORD* pCount); +typedef BOOL(WINAPI* WTS_ENUMERATE_PROCESSES_FN_A)(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_PROCESS_INFOA* ppProcessInfo, + DWORD* pCount); + +typedef BOOL(WINAPI* WTS_TERMINATE_PROCESS_FN)(HANDLE hServer, DWORD ProcessId, DWORD ExitCode); + +typedef BOOL(WINAPI* WTS_QUERY_SESSION_INFORMATION_FN_W)(HANDLE hServer, DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, + LPWSTR* ppBuffer, DWORD* pBytesReturned); +typedef BOOL(WINAPI* WTS_QUERY_SESSION_INFORMATION_FN_A)(HANDLE hServer, DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, + LPSTR* ppBuffer, DWORD* pBytesReturned); + +typedef BOOL(WINAPI* WTS_QUERY_USER_CONFIG_FN_W)(LPWSTR pServerName, LPWSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPWSTR* ppBuffer, + DWORD* pBytesReturned); +typedef BOOL(WINAPI* WTS_QUERY_USER_CONFIG_FN_A)(LPSTR pServerName, LPSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPSTR* ppBuffer, + DWORD* pBytesReturned); + +typedef BOOL(WINAPI* WTS_SET_USER_CONFIG_FN_W)(LPWSTR pServerName, LPWSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPWSTR pBuffer, + DWORD DataLength); +typedef BOOL(WINAPI* WTS_SET_USER_CONFIG_FN_A)(LPSTR pServerName, LPSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPSTR pBuffer, + DWORD DataLength); + +typedef BOOL(WINAPI* WTS_SEND_MESSAGE_FN_W)(HANDLE hServer, DWORD SessionId, LPWSTR pTitle, + DWORD TitleLength, LPWSTR pMessage, DWORD MessageLength, + DWORD Style, DWORD Timeout, DWORD* pResponse, + BOOL bWait); +typedef BOOL(WINAPI* WTS_SEND_MESSAGE_FN_A)(HANDLE hServer, DWORD SessionId, LPSTR pTitle, + DWORD TitleLength, LPSTR pMessage, DWORD MessageLength, + DWORD Style, DWORD Timeout, DWORD* pResponse, + BOOL bWait); + +typedef BOOL(WINAPI* WTS_DISCONNECT_SESSION_FN)(HANDLE hServer, DWORD SessionId, BOOL bWait); + +typedef BOOL(WINAPI* WTS_LOGOFF_SESSION_FN)(HANDLE hServer, DWORD SessionId, BOOL bWait); + +typedef BOOL(WINAPI* WTS_SHUTDOWN_SYSTEM_FN)(HANDLE hServer, DWORD ShutdownFlag); + +typedef BOOL(WINAPI* WTS_WAIT_SYSTEM_EVENT_FN)(HANDLE hServer, DWORD EventMask, DWORD* pEventFlags); + +typedef HANDLE(WINAPI* WTS_VIRTUAL_CHANNEL_OPEN_FN)(HANDLE hServer, DWORD SessionId, + LPSTR pVirtualName); + +typedef HANDLE(WINAPI* WTS_VIRTUAL_CHANNEL_OPEN_EX_FN)(DWORD SessionId, LPSTR pVirtualName, + DWORD flags); + +typedef BOOL(WINAPI* WTS_VIRTUAL_CHANNEL_CLOSE_FN)(HANDLE hChannelHandle); + +typedef BOOL(WINAPI* WTS_VIRTUAL_CHANNEL_READ_FN)(HANDLE hChannelHandle, ULONG TimeOut, + PCHAR Buffer, ULONG BufferSize, + PULONG pBytesRead); + +typedef BOOL(WINAPI* WTS_VIRTUAL_CHANNEL_WRITE_FN)(HANDLE hChannelHandle, PCHAR Buffer, + ULONG Length, PULONG pBytesWritten); + +typedef BOOL(WINAPI* WTS_VIRTUAL_CHANNEL_PURGE_INPUT_FN)(HANDLE hChannelHandle); + +typedef BOOL(WINAPI* WTS_VIRTUAL_CHANNEL_PURGE_OUTPUT_FN)(HANDLE hChannelHandle); + +typedef BOOL(WINAPI* WTS_VIRTUAL_CHANNEL_QUERY_FN)(HANDLE hChannelHandle, + WTS_VIRTUAL_CLASS WtsVirtualClass, + PVOID* ppBuffer, DWORD* pBytesReturned); + +typedef VOID(WINAPI* WTS_FREE_MEMORY_FN)(PVOID pMemory); + +typedef BOOL(WINAPI* WTS_REGISTER_SESSION_NOTIFICATION_FN)(HWND hWnd, DWORD dwFlags); + +typedef BOOL(WINAPI* WTS_UNREGISTER_SESSION_NOTIFICATION_FN)(HWND hWnd); + +typedef BOOL(WINAPI* WTS_REGISTER_SESSION_NOTIFICATION_EX_FN)(HANDLE hServer, HWND hWnd, + DWORD dwFlags); + +typedef BOOL(WINAPI* WTS_UNREGISTER_SESSION_NOTIFICATION_EX_FN)(HANDLE hServer, HWND hWnd); + +typedef BOOL(WINAPI* WTS_QUERY_USER_TOKEN_FN)(ULONG SessionId, PHANDLE phToken); + +typedef BOOL(WINAPI* WTS_FREE_MEMORY_EX_FN_W)(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, + ULONG NumberOfEntries); +typedef BOOL(WINAPI* WTS_FREE_MEMORY_EX_FN_A)(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, + ULONG NumberOfEntries); + +typedef BOOL(WINAPI* WTS_ENUMERATE_PROCESSES_EX_FN_W)(HANDLE hServer, DWORD* pLevel, + DWORD SessionId, LPWSTR* ppProcessInfo, + DWORD* pCount); +typedef BOOL(WINAPI* WTS_ENUMERATE_PROCESSES_EX_FN_A)(HANDLE hServer, DWORD* pLevel, + DWORD SessionId, LPSTR* ppProcessInfo, + DWORD* pCount); + +typedef BOOL(WINAPI* WTS_ENUMERATE_LISTENERS_FN_W)(HANDLE hServer, PVOID pReserved, DWORD Reserved, + PWTSLISTENERNAMEW pListeners, DWORD* pCount); +typedef BOOL(WINAPI* WTS_ENUMERATE_LISTENERS_FN_A)(HANDLE hServer, PVOID pReserved, DWORD Reserved, + PWTSLISTENERNAMEA pListeners, DWORD* pCount); + +typedef BOOL(WINAPI* WTS_QUERY_LISTENER_CONFIG_FN_W)(HANDLE hServer, PVOID pReserved, + DWORD Reserved, LPWSTR pListenerName, + PWTSLISTENERCONFIGW pBuffer); +typedef BOOL(WINAPI* WTS_QUERY_LISTENER_CONFIG_FN_A)(HANDLE hServer, PVOID pReserved, + DWORD Reserved, LPSTR pListenerName, + PWTSLISTENERCONFIGA pBuffer); + +typedef BOOL(WINAPI* WTS_CREATE_LISTENER_FN_W)(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, PWTSLISTENERCONFIGW pBuffer, + DWORD flag); +typedef BOOL(WINAPI* WTS_CREATE_LISTENER_FN_A)(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, PWTSLISTENERCONFIGA pBuffer, + DWORD flag); + +typedef BOOL(WINAPI* WTS_SET_LISTENER_SECURITY_FN_W)(HANDLE hServer, PVOID pReserved, + DWORD Reserved, LPWSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor); +typedef BOOL(WINAPI* WTS_SET_LISTENER_SECURITY_FN_A)(HANDLE hServer, PVOID pReserved, + DWORD Reserved, LPSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor); + +typedef BOOL(WINAPI* WTS_GET_LISTENER_SECURITY_FN_W)(HANDLE hServer, PVOID pReserved, + DWORD Reserved, LPWSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD nLength, LPDWORD lpnLengthNeeded); +typedef BOOL(WINAPI* WTS_GET_LISTENER_SECURITY_FN_A)(HANDLE hServer, PVOID pReserved, + DWORD Reserved, LPSTR pListenerName, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD nLength, LPDWORD lpnLengthNeeded); + +typedef BOOL(CDECL* WTS_ENABLE_CHILD_SESSIONS_FN)(BOOL bEnable); + +typedef BOOL(CDECL* WTS_IS_CHILD_SESSIONS_ENABLED_FN)(PBOOL pbEnabled); + +typedef BOOL(CDECL* WTS_GET_CHILD_SESSION_ID_FN)(PULONG pSessionId); + +typedef DWORD(WINAPI* WTS_GET_ACTIVE_CONSOLE_SESSION_ID_FN)(void); + +typedef BOOL(WINAPI* WTS_LOGON_USER_FN)(HANDLE hServer, LPCSTR username, LPCSTR password, + LPCSTR domain); + +typedef BOOL(WINAPI* WTS_LOGOFF_USER_FN)(HANDLE hServer); + +typedef struct +{ + DWORD dwVersion; + DWORD dwFlags; + + WTS_STOP_REMOTE_CONTROL_SESSION_FN pStopRemoteControlSession; + WTS_START_REMOTE_CONTROL_SESSION_FN_W pStartRemoteControlSessionW; + WTS_START_REMOTE_CONTROL_SESSION_FN_A pStartRemoteControlSessionA; + WTS_CONNECT_SESSION_FN_W pConnectSessionW; + WTS_CONNECT_SESSION_FN_A pConnectSessionA; + WTS_ENUMERATE_SERVERS_FN_W pEnumerateServersW; + WTS_ENUMERATE_SERVERS_FN_A pEnumerateServersA; + WTS_OPEN_SERVER_FN_W pOpenServerW; + WTS_OPEN_SERVER_FN_A pOpenServerA; + WTS_OPEN_SERVER_EX_FN_W pOpenServerExW; + WTS_OPEN_SERVER_EX_FN_A pOpenServerExA; + WTS_CLOSE_SERVER_FN pCloseServer; + WTS_ENUMERATE_SESSIONS_FN_W pEnumerateSessionsW; + WTS_ENUMERATE_SESSIONS_FN_A pEnumerateSessionsA; + WTS_ENUMERATE_SESSIONS_EX_FN_W pEnumerateSessionsExW; + WTS_ENUMERATE_SESSIONS_EX_FN_A pEnumerateSessionsExA; + WTS_ENUMERATE_PROCESSES_FN_W pEnumerateProcessesW; + WTS_ENUMERATE_PROCESSES_FN_A pEnumerateProcessesA; + WTS_TERMINATE_PROCESS_FN pTerminateProcess; + WTS_QUERY_SESSION_INFORMATION_FN_W pQuerySessionInformationW; + WTS_QUERY_SESSION_INFORMATION_FN_A pQuerySessionInformationA; + WTS_QUERY_USER_CONFIG_FN_W pQueryUserConfigW; + WTS_QUERY_USER_CONFIG_FN_A pQueryUserConfigA; + WTS_SET_USER_CONFIG_FN_W pSetUserConfigW; + WTS_SET_USER_CONFIG_FN_A pSetUserConfigA; + WTS_SEND_MESSAGE_FN_W pSendMessageW; + WTS_SEND_MESSAGE_FN_A pSendMessageA; + WTS_DISCONNECT_SESSION_FN pDisconnectSession; + WTS_LOGOFF_SESSION_FN pLogoffSession; + WTS_SHUTDOWN_SYSTEM_FN pShutdownSystem; + WTS_WAIT_SYSTEM_EVENT_FN pWaitSystemEvent; + WTS_VIRTUAL_CHANNEL_OPEN_FN pVirtualChannelOpen; + WTS_VIRTUAL_CHANNEL_OPEN_EX_FN pVirtualChannelOpenEx; + WTS_VIRTUAL_CHANNEL_CLOSE_FN pVirtualChannelClose; + WTS_VIRTUAL_CHANNEL_READ_FN pVirtualChannelRead; + WTS_VIRTUAL_CHANNEL_WRITE_FN pVirtualChannelWrite; + WTS_VIRTUAL_CHANNEL_PURGE_INPUT_FN pVirtualChannelPurgeInput; + WTS_VIRTUAL_CHANNEL_PURGE_OUTPUT_FN pVirtualChannelPurgeOutput; + WTS_VIRTUAL_CHANNEL_QUERY_FN pVirtualChannelQuery; + WTS_FREE_MEMORY_FN pFreeMemory; + WTS_REGISTER_SESSION_NOTIFICATION_FN pRegisterSessionNotification; + WTS_UNREGISTER_SESSION_NOTIFICATION_FN pUnRegisterSessionNotification; + WTS_REGISTER_SESSION_NOTIFICATION_EX_FN pRegisterSessionNotificationEx; + WTS_UNREGISTER_SESSION_NOTIFICATION_EX_FN pUnRegisterSessionNotificationEx; + WTS_QUERY_USER_TOKEN_FN pQueryUserToken; + WTS_FREE_MEMORY_EX_FN_W pFreeMemoryExW; + WTS_FREE_MEMORY_EX_FN_A pFreeMemoryExA; + WTS_ENUMERATE_PROCESSES_EX_FN_W pEnumerateProcessesExW; + WTS_ENUMERATE_PROCESSES_EX_FN_A pEnumerateProcessesExA; + WTS_ENUMERATE_LISTENERS_FN_W pEnumerateListenersW; + WTS_ENUMERATE_LISTENERS_FN_A pEnumerateListenersA; + WTS_QUERY_LISTENER_CONFIG_FN_W pQueryListenerConfigW; + WTS_QUERY_LISTENER_CONFIG_FN_A pQueryListenerConfigA; + WTS_CREATE_LISTENER_FN_W pCreateListenerW; + WTS_CREATE_LISTENER_FN_A pCreateListenerA; + WTS_SET_LISTENER_SECURITY_FN_W pSetListenerSecurityW; + WTS_SET_LISTENER_SECURITY_FN_A pSetListenerSecurityA; + WTS_GET_LISTENER_SECURITY_FN_W pGetListenerSecurityW; + WTS_GET_LISTENER_SECURITY_FN_A pGetListenerSecurityA; + WTS_ENABLE_CHILD_SESSIONS_FN pEnableChildSessions; + WTS_IS_CHILD_SESSIONS_ENABLED_FN pIsChildSessionsEnabled; + WTS_GET_CHILD_SESSION_ID_FN pGetChildSessionId; + WTS_GET_ACTIVE_CONSOLE_SESSION_ID_FN pGetActiveConsoleSessionId; + WTS_LOGON_USER_FN pLogonUser; + WTS_LOGOFF_USER_FN pLogoffUser; + WTS_START_REMOTE_CONTROL_SESSION_EX_FN_W pStartRemoteControlSessionExW; + WTS_START_REMOTE_CONTROL_SESSION_EX_FN_A pStartRemoteControlSessionExA; +} WtsApiFunctionTable; +typedef WtsApiFunctionTable* PWtsApiFunctionTable; + +typedef const WtsApiFunctionTable*(CDECL* INIT_WTSAPI_FN)(void); + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL WTSRegisterWtsApiFunctionTable(const WtsApiFunctionTable* table); + WINPR_API const CHAR* WTSErrorToString(UINT error); + WINPR_API const CHAR* WTSSessionStateToString(WTS_CONNECTSTATE_CLASS state); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_WTSAPI_H */ diff --git a/winpr/libwinpr/CMakeLists.txt b/winpr/libwinpr/CMakeLists.txt new file mode 100644 index 0000000..4be2015 --- /dev/null +++ b/winpr/libwinpr/CMakeLists.txt @@ -0,0 +1,213 @@ +# WinPR: Windows Portable Runtime +# winpr cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(CheckFunctionExists) + +set(WINPR_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(WINPR_SRCS "") +set(WINPR_LIBS_PRIVATE "") +set(WINPR_LIBS_PUBLIC "") +set(WINPR_INCLUDES "") +set(WINPR_DEFINITIONS "") +set(WINPR_COMPILE_OPTIONS "") +set(WINPR_LINK_OPTIONS "") +set(WINPR_LINK_DIRS "") + +macro (winpr_module_add) + file (RELATIVE_PATH _relPath "${WINPR_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") + foreach (_src ${ARGN}) + if (_relPath) + list (APPEND WINPR_SRCS "${_relPath}/${_src}") + else() + list (APPEND WINPR_SRCS "${_src}") + endif() + endforeach() + if (_relPath) + set (WINPR_SRCS ${WINPR_SRCS} PARENT_SCOPE) + endif() +endmacro() + +macro (winpr_include_directory_add) + file (RELATIVE_PATH _relPath "${WINPR_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") + foreach (_inc ${ARGN}) + if (IS_ABSOLUTE ${_inc}) + list (APPEND WINPR_INCLUDES "${_inc}") + else() + if (_relPath) + list (APPEND WINPR_INCLUDES "${_relPath}/${_inc}") + else() + list (APPEND WINPR_INCLUDES "${_inc}") + endif() + endif() + endforeach() + if (_relPath) + set (WINPR_INCLUDES ${WINPR_INCLUDES} PARENT_SCOPE) + endif() +endmacro() + +macro (winpr_library_add_private) + foreach (_lib ${ARGN}) + list (APPEND WINPR_LIBS_PRIVATE "${_lib}") + endforeach() + set (WINPR_LIBS_PRIVATE ${WINPR_LIBS_PRIVATE} PARENT_SCOPE) +endmacro() + +macro (winpr_library_add_public) + foreach (_lib ${ARGN}) + list (APPEND WINPR_LIBS_PUBLIC "${_lib}") + endforeach() + set (WINPR_LIBS_PUBLIC ${WINPR_LIBS_PUBLIC} PARENT_SCOPE) +endmacro() + +macro (winpr_definition_add) + foreach (_define ${ARGN}) + list (APPEND WINPR_DEFINITIONS "${_define}") + endforeach() + set (WINPR_DEFINITIONS ${WINPR_DEFINITIONS} PARENT_SCOPE) +endmacro() + +macro (winpr_library_add_compile_options) + foreach (_define ${ARGN}) + list (APPEND WINPR_COMPILE_OPTIONS "${_define}") + endforeach() + set (WINPR_COMPILE_OPTIONS ${WINPR_COMPILE_OPTIONS} PARENT_SCOPE) +endmacro() + +macro (winpr_library_add_link_options) + foreach (_define ${ARGN}) + list (APPEND WINPR_LINK_OPTIONS "${_define}") + endforeach() + set (WINPR_LINK_OPTIONS ${WINPR_LINK_OPTIONS} PARENT_SCOPE) +endmacro() + +macro (winpr_library_add_link_directory) + foreach (_define ${ARGN}) + list (APPEND WINPR_LINK_DIRS "${_define}") + endforeach() + set (WINPR_LINK_DIRS ${WINPR_LINK_DIRS} PARENT_SCOPE) +endmacro() + +set(CMAKE_REQUIRED_LIBRARIES rt) + +find_package(uriparser) +option(WITH_URIPARSER "use uriparser library to handle URIs" ${uriparser_FOUND}) +if (WITH_URIPARSER) + find_package(uriparser CONFIG COMPONENTS char) + if (uriparser_FOUND) + winpr_library_add_private(uriparser::uriparser) + else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(uriparser REQUIRED liburiparser) + winpr_include_directory_add(${uriparser_INCLUDEDIR}) + winpr_include_directory_add(${uriparser_INCLUDE_DIRS}) + winpr_library_add_private(${uriparser_LIBRARIES}) + endif() + add_definitions("-DWITH_URIPARSER") +endif() + +if(NOT IOS) + check_function_exists(timer_create TIMER_CREATE) + check_function_exists(timer_delete TIMER_DELETE) + check_function_exists(timer_settime TIMER_SETTIME) + check_function_exists(timer_gettime TIMER_GETTIME) + if (TIMER_CREATE AND TIMER_DELETE AND TIMER_SETTIME AND TIMER_GETTIME) + add_definitions(-DWITH_POSIX_TIMER) + winpr_library_add_private(rt) + endif() +endif() + +if (ANDROID) + winpr_library_add_private(log) +endif() + +# Level "1" API as defined for MinCore.lib +set(WINPR_CORE synch library file comm pipe interlocked security + environment crypto registry path io memory ncrypt input shell + utils error timezone sysinfo pool handle thread) + +foreach(DIR ${WINPR_CORE}) + add_subdirectory(${DIR}) + source_group("${DIR}" REGULAR_EXPRESSION "${DIR}/.*\\.[ch]") +endforeach() + +set(WINPR_LEVEL2 winsock sspi sspicli crt bcrypt rpc + wtsapi dsparse smartcard nt clipboard) + +foreach(DIR ${WINPR_LEVEL2}) + add_subdirectory(${DIR}) + source_group("${DIR}" REGULAR_EXPRESSION "${DIR}/.*\\.[ch]") +endforeach() + +set(MODULE_NAME winpr) +list(REMOVE_DUPLICATES WINPR_DEFINITIONS) +list(REMOVE_DUPLICATES WINPR_COMPILE_OPTIONS) +list(REMOVE_DUPLICATES WINPR_LINK_OPTIONS) +list(REMOVE_DUPLICATES WINPR_LINK_DIRS) +list(REMOVE_DUPLICATES WINPR_LIBS_PRIVATE) +list(REMOVE_DUPLICATES WINPR_LIBS_PUBLIC) +list(REMOVE_DUPLICATES WINPR_INCLUDES) + +# On windows create dll version information. +# Vendor, product and year are already set in top level CMakeLists.txt +if (WIN32) + set (RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR}) + set (RC_VERSION_MINOR ${WINPR_VERSION_MINOR}) + set (RC_VERSION_BUILD ${WINPR_VERSION_REVISION}) + set (RC_VERSION_FILE "${CMAKE_SHARED_LIBRARY_PREFIX}${MODULE_NAME}${WINPR_API_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}" ) + + configure_file( + ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set (WINPR_SRCS ${WINPR_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + winpr_library_add_public("shlwapi") +endif() + +add_library(${MODULE_NAME} ${WINPR_SRCS}) +if (APPLE) + set_target_properties(${MODULE_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION FALSE) +endif() +set_target_properties(${MODULE_NAME} PROPERTIES LINKER_LANGUAGE C) +set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${WINPR_API_VERSION}) +if (WITH_LIBRARY_VERSIONING) + set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION} SOVERSION ${WINPR_API_VERSION}) +endif() + +if (NOT BUILD_SHARED_LIBS) + set(LINK_OPTS_MODE PUBLIC) +else() + set(LINK_OPTS_MODE PRIVATE) +endif() +target_link_options(${MODULE_NAME} ${LINK_OPTS_MODE} ${WINPR_LINK_OPTIONS}) +target_include_directories(${MODULE_NAME} PRIVATE ${WINPR_INCLUDES}) +target_include_directories(${MODULE_NAME} INTERFACE $) +target_link_directories(${MODULE_NAME} PRIVATE ${WINPR_LINK_DIRS}) +target_compile_options(${MODULE_NAME} PRIVATE ${WINPR_COMPILE_OPTIONS}) +target_compile_definitions(${MODULE_NAME} PRIVATE ${WINPR_DEFINITIONS}) + +target_link_libraries(${MODULE_NAME} PRIVATE ${WINPR_LIBS_PRIVATE} PUBLIC ${WINPR_LIBS_PUBLIC}) +install(TARGETS ${MODULE_NAME} COMPONENT libraries EXPORT WinPRTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS) + get_target_property(OUTPUT_FILENAME ${MODULE_NAME} OUTPUT_NAME) + install(FILES ${CMAKE_PDB_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols) +endif() +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/libwinpr") diff --git a/winpr/libwinpr/bcrypt/CMakeLists.txt b/winpr/libwinpr/bcrypt/CMakeLists.txt new file mode 100644 index 0000000..48046b8 --- /dev/null +++ b/winpr/libwinpr/bcrypt/CMakeLists.txt @@ -0,0 +1,19 @@ +# WinPR: Windows Portable Runtime +# libwinpr-bcrypt cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(bcrypt.c) + diff --git a/winpr/libwinpr/bcrypt/ModuleOptions.cmake b/winpr/libwinpr/bcrypt/ModuleOptions.cmake new file mode 100644 index 0000000..c1dc8f3 --- /dev/null +++ b/winpr/libwinpr/bcrypt/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "bcrypt") +set(MINWIN_LONG_NAME "Cryptography API: Next Generation (CNG)") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/bcrypt/bcrypt.c b/winpr/libwinpr/bcrypt/bcrypt.c new file mode 100644 index 0000000..0ec86ac --- /dev/null +++ b/winpr/libwinpr/bcrypt/bcrypt.c @@ -0,0 +1,114 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API: Next Generation + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef _WIN32 +#include + +/** + * Cryptography API: Next Generation: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa376210/ + */ + +NTSTATUS BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE* phAlgorithm, LPCWSTR pszAlgId, + LPCWSTR pszImplementation, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE hAlgorithm, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptGetProperty(BCRYPT_HANDLE hObject, LPCWSTR pszProperty, PUCHAR pbOutput, + ULONG cbOutput, ULONG* pcbResult, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptCreateHash(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_HASH_HANDLE* phHash, + PUCHAR pbHashObject, ULONG cbHashObject, PUCHAR pbSecret, ULONG cbSecret, + ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptDestroyHash(BCRYPT_HASH_HANDLE hHash) +{ + return 0; +} + +NTSTATUS BCryptHashData(BCRYPT_HASH_HANDLE hHash, PUCHAR pbInput, ULONG cbInput, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptFinishHash(BCRYPT_HASH_HANDLE hHash, PUCHAR pbOutput, ULONG cbOutput, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptGenRandom(BCRYPT_ALG_HANDLE hAlgorithm, PUCHAR pbBuffer, ULONG cbBuffer, + ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptGenerateSymmetricKey(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE* phKey, + PUCHAR pbKeyObject, ULONG cbKeyObject, PUCHAR pbSecret, + ULONG cbSecret, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptGenerateKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE* phKey, + ULONG dwLength, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptImportKey(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, + LPCWSTR pszBlobType, BCRYPT_KEY_HANDLE* phKey, PUCHAR pbKeyObject, + ULONG cbKeyObject, PUCHAR pbInput, ULONG cbInput, ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptDestroyKey(BCRYPT_KEY_HANDLE hKey) +{ + return 0; +} + +NTSTATUS BCryptEncrypt(BCRYPT_KEY_HANDLE hKey, PUCHAR pbInput, ULONG cbInput, VOID* pPaddingInfo, + PUCHAR pbIV, ULONG cbIV, PUCHAR pbOutput, ULONG cbOutput, ULONG* pcbResult, + ULONG dwFlags) +{ + return 0; +} + +NTSTATUS BCryptDecrypt(BCRYPT_KEY_HANDLE hKey, PUCHAR pbInput, ULONG cbInput, VOID* pPaddingInfo, + PUCHAR pbIV, ULONG cbIV, PUCHAR pbOutput, ULONG cbOutput, ULONG* pcbResult, + ULONG dwFlags) +{ + return 0; +} + +#endif /* _WIN32 */ diff --git a/winpr/libwinpr/clipboard/CMakeLists.txt b/winpr/libwinpr/clipboard/CMakeLists.txt new file mode 100644 index 0000000..898067d --- /dev/null +++ b/winpr/libwinpr/clipboard/CMakeLists.txt @@ -0,0 +1,28 @@ +# WinPR: Windows Portable Runtime +# libwinpr-clipboard cmake build script +# +# Copyright 2014 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + synthetic.c + clipboard.c + clipboard.h + synthetic_file.h + synthetic_file.c + ) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/clipboard/ModuleOptions.cmake b/winpr/libwinpr/clipboard/ModuleOptions.cmake new file mode 100644 index 0000000..f8ebeaa --- /dev/null +++ b/winpr/libwinpr/clipboard/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "clipboard") +set(MINWIN_LONG_NAME "Clipboard Functions") +set(MODULE_LIBRARY_NAME "clipboard") + diff --git a/winpr/libwinpr/clipboard/clipboard.c b/winpr/libwinpr/clipboard/clipboard.c new file mode 100644 index 0000000..9d17fbc --- /dev/null +++ b/winpr/libwinpr/clipboard/clipboard.c @@ -0,0 +1,738 @@ +/** + * WinPR: Windows Portable Runtime + * Clipboard Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +#include "clipboard.h" + +#include "synthetic_file.h" + +#include "../log.h" +#define TAG WINPR_TAG("clipboard") + +const char* mime_text_plain = "text/plain"; + +/** + * Clipboard (Windows): + * msdn.microsoft.com/en-us/library/windows/desktop/ms648709/ + * + * W3C Clipboard API and events: + * http://www.w3.org/TR/clipboard-apis/ + */ + +static const char* CF_STANDARD_STRINGS[] = { + "CF_RAW", /* 0 */ + "CF_TEXT", /* 1 */ + "CF_BITMAP", /* 2 */ + "CF_METAFILEPICT", /* 3 */ + "CF_SYLK", /* 4 */ + "CF_DIF", /* 5 */ + "CF_TIFF", /* 6 */ + "CF_OEMTEXT", /* 7 */ + "CF_DIB", /* 8 */ + "CF_PALETTE", /* 9 */ + "CF_PENDATA", /* 10 */ + "CF_RIFF", /* 11 */ + "CF_WAVE", /* 12 */ + "CF_UNICODETEXT", /* 13 */ + "CF_ENHMETAFILE", /* 14 */ + "CF_HDROP", /* 15 */ + "CF_LOCALE", /* 16 */ + "CF_DIBV5" /* 17 */ +}; + +const char* ClipboardGetFormatIdString(UINT32 formatId) +{ + if (formatId < ARRAYSIZE(CF_STANDARD_STRINGS)) + return CF_STANDARD_STRINGS[formatId]; + return "CF_REGISTERED_FORMAT"; +} + +static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId, + const char* name) +{ + wClipboardFormat* format = NULL; + + if (!clipboard) + return NULL; + + if (formatId) + { + for (UINT32 index = 0; index < clipboard->numFormats; index++) + { + wClipboardFormat* cformat = &clipboard->formats[index]; + if (formatId == cformat->formatId) + { + format = cformat; + break; + } + } + } + else if (name) + { + for (UINT32 index = 0; index < clipboard->numFormats; index++) + { + wClipboardFormat* cformat = &clipboard->formats[index]; + if (!cformat->formatName) + continue; + + if (strcmp(name, cformat->formatName) == 0) + { + format = cformat; + break; + } + } + } + else + { + /* special "CF_RAW" case */ + if (clipboard->numFormats > 0) + { + format = &clipboard->formats[0]; + + if (format->formatId) + return NULL; + + if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0)) + return format; + } + } + + return format; +} + +static wClipboardSynthesizer* ClipboardFindSynthesizer(wClipboardFormat* format, UINT32 formatId) +{ + if (!format) + return NULL; + + for (UINT32 index = 0; index < format->numSynthesizers; index++) + { + wClipboardSynthesizer* synthesizer = &(format->synthesizers[index]); + + if (formatId == synthesizer->syntheticId) + return synthesizer; + } + + return NULL; +} + +void ClipboardLock(wClipboard* clipboard) +{ + if (!clipboard) + return; + + EnterCriticalSection(&(clipboard->lock)); +} + +void ClipboardUnlock(wClipboard* clipboard) +{ + if (!clipboard) + return; + + LeaveCriticalSection(&(clipboard->lock)); +} + +BOOL ClipboardEmpty(wClipboard* clipboard) +{ + if (!clipboard) + return FALSE; + + if (clipboard->data) + { + free((void*)clipboard->data); + clipboard->data = NULL; + } + + clipboard->size = 0; + clipboard->formatId = 0; + clipboard->sequenceNumber++; + return TRUE; +} + +UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard) +{ + if (!clipboard) + return 0; + + return clipboard->numFormats; +} + +UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds) +{ + UINT32* pFormatIds = NULL; + wClipboardFormat* format = NULL; + + if (!clipboard) + return 0; + + if (!ppFormatIds) + return 0; + + pFormatIds = *ppFormatIds; + + if (!pFormatIds) + { + pFormatIds = calloc(clipboard->numFormats, sizeof(UINT32)); + + if (!pFormatIds) + return 0; + + *ppFormatIds = pFormatIds; + } + + for (UINT32 index = 0; index < clipboard->numFormats; index++) + { + format = &(clipboard->formats[index]); + pFormatIds[index] = format->formatId; + } + + return clipboard->numFormats; +} + +UINT32 ClipboardRegisterFormat(wClipboard* clipboard, const char* name) +{ + wClipboardFormat* format = NULL; + + if (!clipboard) + return 0; + + format = ClipboardFindFormat(clipboard, 0, name); + + if (format) + return format->formatId; + + if ((clipboard->numFormats + 1) >= clipboard->maxFormats) + { + UINT32 numFormats = clipboard->maxFormats * 2; + wClipboardFormat* tmpFormat = NULL; + tmpFormat = + (wClipboardFormat*)realloc(clipboard->formats, numFormats * sizeof(wClipboardFormat)); + + if (!tmpFormat) + return 0; + + clipboard->formats = tmpFormat; + clipboard->maxFormats = numFormats; + } + + format = &(clipboard->formats[clipboard->numFormats]); + ZeroMemory(format, sizeof(wClipboardFormat)); + + if (name) + { + format->formatName = _strdup(name); + + if (!format->formatName) + return 0; + } + + format->formatId = clipboard->nextFormatId++; + clipboard->numFormats++; + return format->formatId; +} + +BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId, + CLIPBOARD_SYNTHESIZE_FN pfnSynthesize) +{ + UINT32 index = 0; + wClipboardFormat* format = NULL; + wClipboardSynthesizer* synthesizer = NULL; + + if (!clipboard) + return FALSE; + + format = ClipboardFindFormat(clipboard, formatId, NULL); + + if (!format) + return FALSE; + + if (format->formatId == syntheticId) + return FALSE; + + synthesizer = ClipboardFindSynthesizer(format, formatId); + + if (!synthesizer) + { + wClipboardSynthesizer* tmpSynthesizer = NULL; + UINT32 numSynthesizers = format->numSynthesizers + 1; + tmpSynthesizer = (wClipboardSynthesizer*)realloc( + format->synthesizers, numSynthesizers * sizeof(wClipboardSynthesizer)); + + if (!tmpSynthesizer) + return FALSE; + + format->synthesizers = tmpSynthesizer; + format->numSynthesizers = numSynthesizers; + index = numSynthesizers - 1; + synthesizer = &(format->synthesizers[index]); + } + + synthesizer->syntheticId = syntheticId; + synthesizer->pfnSynthesize = pfnSynthesize; + return TRUE; +} + +UINT32 ClipboardCountFormats(wClipboard* clipboard) +{ + UINT32 count = 0; + wClipboardFormat* format = NULL; + + if (!clipboard) + return 0; + + format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL); + + if (!format) + return 0; + + count = 1 + format->numSynthesizers; + return count; +} + +UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds) +{ + UINT32 count = 0; + UINT32* pFormatIds = NULL; + wClipboardFormat* format = NULL; + wClipboardSynthesizer* synthesizer = NULL; + + if (!clipboard) + return 0; + + format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL); + + if (!format) + return 0; + + count = 1 + format->numSynthesizers; + + if (!ppFormatIds) + return 0; + + pFormatIds = *ppFormatIds; + + if (!pFormatIds) + { + pFormatIds = calloc(count, sizeof(UINT32)); + + if (!pFormatIds) + return 0; + + *ppFormatIds = pFormatIds; + } + + pFormatIds[0] = format->formatId; + + for (UINT32 index = 1; index < count; index++) + { + synthesizer = &(format->synthesizers[index - 1]); + pFormatIds[index] = synthesizer->syntheticId; + } + + return count; +} + +static void ClipboardUninitFormats(wClipboard* clipboard) +{ + WINPR_ASSERT(clipboard); + for (UINT32 formatId = 0; formatId < clipboard->numFormats; formatId++) + { + wClipboardFormat* format = &clipboard->formats[formatId]; + free(format->formatName); + free(format->synthesizers); + format->formatName = NULL; + format->synthesizers = NULL; + } +} + +static BOOL ClipboardInitFormats(wClipboard* clipboard) +{ + UINT32 formatId = 0; + wClipboardFormat* format = NULL; + + if (!clipboard) + return FALSE; + + for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++) + { + format = &(clipboard->formats[clipboard->numFormats]); + ZeroMemory(format, sizeof(wClipboardFormat)); + format->formatId = formatId; + format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]); + + if (!format->formatName) + goto error; + } + + if (!ClipboardInitSynthesizers(clipboard)) + goto error; + + return TRUE; +error: + + ClipboardUninitFormats(clipboard); + return FALSE; +} + +UINT32 ClipboardGetFormatId(wClipboard* clipboard, const char* name) +{ + wClipboardFormat* format = NULL; + + if (!clipboard) + return 0; + + format = ClipboardFindFormat(clipboard, 0, name); + + if (!format) + return 0; + + return format->formatId; +} + +const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId) +{ + wClipboardFormat* format = NULL; + + if (!clipboard) + return NULL; + + format = ClipboardFindFormat(clipboard, formatId, NULL); + + if (!format) + return NULL; + + return format->formatName; +} + +void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize) +{ + UINT32 SrcSize = 0; + UINT32 DstSize = 0; + void* pSrcData = NULL; + void* pDstData = NULL; + wClipboardFormat* format = NULL; + wClipboardSynthesizer* synthesizer = NULL; + + if (!clipboard) + return NULL; + + if (!pSize) + return NULL; + + *pSize = 0; + format = ClipboardFindFormat(clipboard, clipboard->formatId, NULL); + + if (!format) + return NULL; + + SrcSize = clipboard->size; + pSrcData = (void*)clipboard->data; + + if (formatId == format->formatId) + { + DstSize = SrcSize; + pDstData = malloc(DstSize); + + if (!pDstData) + return NULL; + + CopyMemory(pDstData, pSrcData, SrcSize); + *pSize = DstSize; + } + else + { + synthesizer = ClipboardFindSynthesizer(format, formatId); + + if (!synthesizer || !synthesizer->pfnSynthesize) + return NULL; + + DstSize = SrcSize; + pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize); + if (pDstData) + *pSize = DstSize; + } + + return pDstData; +} + +BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32 size) +{ + wClipboardFormat* format = NULL; + + if (!clipboard) + return FALSE; + + format = ClipboardFindFormat(clipboard, formatId, NULL); + + if (!format) + return FALSE; + + free((void*)clipboard->data); + clipboard->data = malloc(size); + + if (!clipboard->data) + return FALSE; + + memcpy(clipboard->data, data, size); + clipboard->size = size; + clipboard->formatId = formatId; + clipboard->sequenceNumber++; + return TRUE; +} + +UINT64 ClipboardGetOwner(wClipboard* clipboard) +{ + if (!clipboard) + return 0; + + return clipboard->ownerId; +} + +void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId) +{ + if (!clipboard) + return; + + clipboard->ownerId = ownerId; +} + +wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard) +{ + if (!clipboard) + return NULL; + + return &clipboard->delegate; +} + +static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard) +{ + /* + * There can be only one local file subsystem active. + * Return as soon as initialization succeeds. + */ + if (ClipboardInitSyntheticFileSubsystem(clipboard)) + { + WLog_DBG(TAG, "initialized synthetic local file subsystem"); + return; + } + else + { + WLog_WARN(TAG, "failed to initialize synthetic local file subsystem"); + } + + WLog_INFO(TAG, "failed to initialize local file subsystem, file transfer not available"); +} + +wClipboard* ClipboardCreate(void) +{ + wClipboard* clipboard = (wClipboard*)calloc(1, sizeof(wClipboard)); + + if (!clipboard) + return NULL; + + clipboard->nextFormatId = 0xC000; + clipboard->sequenceNumber = 0; + + if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000)) + goto fail; + + clipboard->numFormats = 0; + clipboard->maxFormats = 64; + clipboard->formats = (wClipboardFormat*)calloc(clipboard->maxFormats, sizeof(wClipboardFormat)); + + if (!clipboard->formats) + goto fail; + + if (!ClipboardInitFormats(clipboard)) + goto fail; + + clipboard->delegate.clipboard = clipboard; + ClipboardInitLocalFileSubsystem(clipboard); + return clipboard; +fail: + ClipboardDestroy(clipboard); + return NULL; +} + +void ClipboardDestroy(wClipboard* clipboard) +{ + if (!clipboard) + return; + + ArrayList_Free(clipboard->localFiles); + clipboard->localFiles = NULL; + + ClipboardUninitFormats(clipboard); + + free((void*)clipboard->data); + clipboard->data = NULL; + clipboard->size = 0; + clipboard->numFormats = 0; + free(clipboard->formats); + DeleteCriticalSection(&(clipboard->lock)); + free(clipboard); +} + +static BOOL is_dos_drive(const char* path, size_t len) +{ + if (len < 2) + return FALSE; + + WINPR_ASSERT(path); + if (path[1] == ':' || path[1] == '|') + { + if (((path[0] >= 'A') && (path[0] <= 'Z')) || ((path[0] >= 'a') && (path[0] <= 'z'))) + return TRUE; + } + return FALSE; +} + +char* parse_uri_to_local_file(const char* uri, size_t uri_len) +{ + // URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089 + const char prefix[] = "file:"; + const char prefixTraditional[] = "file://"; + const char* localName = NULL; + size_t localLen = 0; + char* buffer = NULL; + const size_t prefixLen = strnlen(prefix, sizeof(prefix)); + const size_t prefixTraditionalLen = strnlen(prefixTraditional, sizeof(prefixTraditional)); + + WINPR_ASSERT(uri || (uri_len == 0)); + + WLog_VRB(TAG, "processing URI: %.*s", uri_len, uri); + + if ((uri_len <= prefixLen) || strncmp(uri, prefix, prefixLen)) + { + WLog_ERR(TAG, "non-'file:' URI schemes are not supported"); + return NULL; + } + + do + { + /* https://datatracker.ietf.org/doc/html/rfc8089#appendix-F + * - The minimal representation of a local file in a DOS- or Windows- + * based environment with no authority field and an absolute path + * that begins with a drive letter. + * + * "file:c:/path/to/file" + * + * - Regular DOS or Windows file URIs with vertical line characters in + * the drive letter construct. + * + * "file:c|/path/to/file" + * + */ + if (uri[prefixLen] != '/') + { + + if (is_dos_drive(&uri[prefixLen], uri_len - prefixLen)) + { + // Dos and Windows file URI + localName = &uri[prefixLen]; + localLen = uri_len - prefixLen; + break; + } + else + { + WLog_ERR(TAG, "URI format are not supported: %s", uri); + return NULL; + } + } + + /* + * - The minimal representation of a local file with no authority field + * and an absolute path that begins with a slash "/". For example: + * + * "file:/path/to/file" + * + */ + else if ((uri_len > prefixLen + 1) && (uri[prefixLen + 1] != '/')) + { + if (is_dos_drive(&uri[prefixLen + 1], uri_len - prefixLen - 1)) + { + // Dos and Windows file URI + localName = (const char*)(uri + prefixLen + 1); + localLen = uri_len - prefixLen - 1; + } + else + { + localName = &uri[prefixLen]; + localLen = uri_len - prefixLen; + } + break; + } + + /* + * - A traditional file URI for a local file with an empty authority. + * + * "file:///path/to/file" + */ + if ((uri_len < prefixTraditionalLen) || + strncmp(uri, prefixTraditional, prefixTraditionalLen)) + { + WLog_ERR(TAG, "non-'file:' URI schemes are not supported"); + return NULL; + } + + localName = &uri[prefixTraditionalLen]; + localLen = uri_len - prefixTraditionalLen; + + if (localLen < 1) + { + WLog_ERR(TAG, "empty 'file:' URI schemes are not supported"); + return NULL; + } + + /* + * "file:///c:/path/to/file" + * "file:///c|/path/to/file" + */ + if (localName[0] != '/') + { + WLog_ERR(TAG, "URI format are not supported: %s", uri); + return NULL; + } + + if (is_dos_drive(&localName[1], localLen - 1)) + { + localName++; + localLen--; + } + + } while (0); + + buffer = winpr_str_url_decode(localName, localLen); + if (buffer) + { + if (buffer[1] == '|' && + ((buffer[0] >= 'A' && buffer[0] <= 'Z') || (buffer[0] >= 'a' && buffer[0] <= 'z'))) + buffer[1] = ':'; + return buffer; + } + + return NULL; +} diff --git a/winpr/libwinpr/clipboard/clipboard.h b/winpr/libwinpr/clipboard/clipboard.h new file mode 100644 index 0000000..b3dc4d0 --- /dev/null +++ b/winpr/libwinpr/clipboard/clipboard.h @@ -0,0 +1,77 @@ +/** + * WinPR: Windows Portable Runtime + * Clipboard Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CLIPBOARD_PRIVATE_H +#define WINPR_CLIPBOARD_PRIVATE_H + +#include +#include + +#include + +typedef struct +{ + UINT32 syntheticId; + CLIPBOARD_SYNTHESIZE_FN pfnSynthesize; +} wClipboardSynthesizer; + +typedef struct +{ + UINT32 formatId; + char* formatName; + + UINT32 numSynthesizers; + wClipboardSynthesizer* synthesizers; +} wClipboardFormat; + +struct s_wClipboard +{ + UINT64 ownerId; + + /* clipboard formats */ + + UINT32 numFormats; + UINT32 maxFormats; + UINT32 nextFormatId; + wClipboardFormat* formats; + + /* clipboard data */ + + UINT32 size; + void* data; + UINT32 formatId; + UINT32 sequenceNumber; + + /* clipboard file handling */ + + wArrayList* localFiles; + UINT32 fileListSequenceNumber; + + wClipboardDelegate delegate; + + CRITICAL_SECTION lock; +}; + +WINPR_LOCAL BOOL ClipboardInitSynthesizers(wClipboard* clipboard); + +WINPR_LOCAL char* parse_uri_to_local_file(const char* uri, size_t uri_len); + +extern const char* mime_text_plain; + +#endif /* WINPR_CLIPBOARD_PRIVATE_H */ diff --git a/winpr/libwinpr/clipboard/synthetic.c b/winpr/libwinpr/clipboard/synthetic.c new file mode 100644 index 0000000..db8af71 --- /dev/null +++ b/winpr/libwinpr/clipboard/synthetic.c @@ -0,0 +1,826 @@ +/** + * WinPR: Windows Portable Runtime + * Clipboard Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include "clipboard.h" + +static const char* mime_bitmap[] = { "image/bmp", "image/x-bmp", "image/x-MS-bmp", + "image/x-win-bitmap" }; + +#if defined(WINPR_UTILS_IMAGE_WEBP) +static const char mime_webp[] = "image/webp"; +#endif +#if defined(WINPR_UTILS_IMAGE_PNG) +static const char mime_png[] = "image/png"; +#endif +#if defined(WINPR_UTILS_IMAGE_JPEG) +static const char mime_jpeg[] = "image/jpeg"; +#endif +/** + * Standard Clipboard Formats: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ff729168/ + */ + +/** + * "CF_TEXT": + * + * Null-terminated ANSI text with CR/LF line endings. + */ + +static void* clipboard_synthesize_cf_text(wClipboard* clipboard, UINT32 formatId, const void* data, + UINT32* pSize) +{ + size_t size = 0; + char* pDstData = NULL; + + if (formatId == CF_UNICODETEXT) + { + char* str = ConvertWCharNToUtf8Alloc(data, *pSize / sizeof(WCHAR), &size); + + if (!str) + return NULL; + + pDstData = ConvertLineEndingToCRLF(str, &size); + free(str); + *pSize = size; + return pDstData; + } + else if ((formatId == CF_TEXT) || (formatId == CF_OEMTEXT) || + (formatId == ClipboardGetFormatId(clipboard, mime_text_plain))) + { + size = *pSize; + pDstData = ConvertLineEndingToCRLF(data, &size); + + if (!pDstData) + return NULL; + + *pSize = size; + return pDstData; + } + + return NULL; +} + +/** + * "CF_OEMTEXT": + * + * Null-terminated OEM text with CR/LF line endings. + */ + +static void* clipboard_synthesize_cf_oemtext(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_cf_text(clipboard, formatId, data, pSize); +} + +/** + * "CF_LOCALE": + * + * System locale identifier associated with CF_TEXT + */ + +static void* clipboard_synthesize_cf_locale(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + UINT32* pDstData = NULL; + pDstData = (UINT32*)malloc(sizeof(UINT32)); + + if (!pDstData) + return NULL; + + *pDstData = 0x0409; /* English - United States */ + return (void*)pDstData; +} + +/** + * "CF_UNICODETEXT": + * + * Null-terminated UTF-16 text with CR/LF line endings. + */ + +static void* clipboard_synthesize_cf_unicodetext(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + size_t size = 0; + char* crlfStr = NULL; + WCHAR* pDstData = NULL; + + if ((formatId == CF_TEXT) || (formatId == CF_OEMTEXT) || + (formatId == ClipboardGetFormatId(clipboard, mime_text_plain))) + { + size_t len = 0; + if (!pSize || (*pSize > INT32_MAX)) + return NULL; + + size = *pSize; + crlfStr = ConvertLineEndingToCRLF((const char*)data, &size); + + if (!crlfStr) + return NULL; + + pDstData = ConvertUtf8NToWCharAlloc(crlfStr, size, &len); + free(crlfStr); + + if ((len < 1) || (len > UINT32_MAX / sizeof(WCHAR))) + { + free(pDstData); + return NULL; + } + + *pSize = (len + 1) * sizeof(WCHAR); + } + + return (void*)pDstData; +} + +/** + * mime_utf8_string: + * + * Null-terminated UTF-8 string with LF line endings. + */ + +static void* clipboard_synthesize_utf8_string(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + size_t size = 0; + char* pDstData = NULL; + + if (formatId == CF_UNICODETEXT) + { + pDstData = ConvertWCharNToUtf8Alloc(data, *pSize / sizeof(WCHAR), &size); + + if (!pDstData) + return NULL; + + size = ConvertLineEndingToLF(pDstData, size); + *pSize = (UINT32)size; + return pDstData; + } + else if ((formatId == CF_TEXT) || (formatId == CF_OEMTEXT) || + (formatId == ClipboardGetFormatId(clipboard, mime_text_plain))) + { + int rc = 0; + size = *pSize; + pDstData = (char*)malloc(size); + + if (!pDstData) + return NULL; + + CopyMemory(pDstData, data, size); + rc = ConvertLineEndingToLF(pDstData, size); + if (rc < 0) + { + free(pDstData); + return NULL; + } + *pSize = (UINT32)rc; + return pDstData; + } + + return NULL; +} + +static BOOL is_format_bitmap(wClipboard* clipboard, UINT32 formatId) +{ + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardGetFormatId(clipboard, mime); + if (altFormatId == formatId) + return TRUE; + } + + return FALSE; +} + +/** + * "CF_DIB": + * + * BITMAPINFO structure followed by the bitmap bits. + */ + +static void* clipboard_synthesize_cf_dib(wClipboard* clipboard, UINT32 formatId, const void* data, + UINT32* pSize) +{ + UINT32 SrcSize = 0; + UINT32 DstSize = 0; + BYTE* pDstData = NULL; + SrcSize = *pSize; + + if (formatId == CF_DIBV5) + { + } + else if (is_format_bitmap(clipboard, formatId)) + { + const BITMAPFILEHEADER* pFileHeader = NULL; + + if (SrcSize < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) + return NULL; + + pFileHeader = (const BITMAPFILEHEADER*)data; + + if (pFileHeader->bfType != 0x4D42) + return NULL; + + DstSize = SrcSize - sizeof(BITMAPFILEHEADER); + pDstData = (BYTE*)malloc(DstSize); + + if (!pDstData) + return NULL; + + data = (const void*)&((const BYTE*)data)[sizeof(BITMAPFILEHEADER)]; + CopyMemory(pDstData, data, DstSize); + *pSize = DstSize; + return pDstData; + } + + return NULL; +} + +/** + * "CF_DIBV5": + * + * BITMAPV5HEADER structure followed by the bitmap color space information and the bitmap bits. + */ + +static void* clipboard_synthesize_cf_dibv5(wClipboard* clipboard, UINT32 formatId, const void* data, + UINT32* pSize) +{ + if (formatId == CF_DIB) + { + } + else if (is_format_bitmap(clipboard, formatId)) + { + } + + return NULL; +} + +static void* clipboard_prepend_bmp_header(const BITMAPINFOHEADER* pInfoHeader, const void* data, + size_t size, UINT32* pSize) +{ + WINPR_ASSERT(pInfoHeader); + WINPR_ASSERT(pSize); + + *pSize = 0; + if ((pInfoHeader->biBitCount < 1) || (pInfoHeader->biBitCount > 32)) + return NULL; + + const size_t DstSize = sizeof(BITMAPFILEHEADER) + size; + BYTE* pDstData = (BYTE*)malloc(DstSize); + + if (!pDstData) + return NULL; + + BITMAPFILEHEADER* pFileHeader = (BITMAPFILEHEADER*)pDstData; + pFileHeader->bfType = 0x4D42; + pFileHeader->bfSize = DstSize; + pFileHeader->bfReserved1 = 0; + pFileHeader->bfReserved2 = 0; + pFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + unsigned char* pDst = &pDstData[sizeof(BITMAPFILEHEADER)]; + CopyMemory(pDst, data, size); + *pSize = DstSize; + return pDstData; +} + +/** + * "image/bmp": + * + * Bitmap file format. + */ + +static void* clipboard_synthesize_image_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + UINT32 SrcSize = *pSize; + + if (formatId == CF_DIB) + { + if (SrcSize < sizeof(BITMAPINFOHEADER)) + return NULL; + + const BITMAPINFOHEADER* pInfoHeader = (const BITMAPINFOHEADER*)data; + return clipboard_prepend_bmp_header(pInfoHeader, data, SrcSize, pSize); + } + else if (formatId == CF_DIBV5) + { + } + + return NULL; +} + +static void* clipboard_synthesize_image_bmp_to_format(wClipboard* clipboard, UINT32 formatId, + UINT32 bmpFormat, const void* data, + UINT32* pSize) +{ + WINPR_ASSERT(clipboard); + WINPR_ASSERT(data); + WINPR_ASSERT(pSize); + + size_t dsize = 0; + void* result = NULL; + + wImage* img = winpr_image_new(); + void* bmp = clipboard_synthesize_image_bmp(clipboard, formatId, data, pSize); + const UINT32 SrcSize = *pSize; + *pSize = 0; + + if (!bmp || !img) + goto fail; + + if (winpr_image_read_buffer(img, bmp, SrcSize) <= 0) + goto fail; + + result = winpr_image_write_buffer(img, bmpFormat, &dsize); + if (result) + *pSize = dsize; + +fail: + free(bmp); + winpr_image_free(img, TRUE); + return result; +} + +#if defined(WINPR_UTILS_IMAGE_PNG) +static void* clipboard_synthesize_image_bmp_to_png(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_PNG, data, + pSize); +} + +static void* clipboard_synthesize_image_format_to_bmp(wClipboard* clipboard, UINT32 srcFormatId, + const void* data, UINT32* pSize) +{ + WINPR_ASSERT(clipboard); + WINPR_ASSERT(data); + WINPR_ASSERT(pSize); + + void* dst = NULL; + const UINT32 SrcSize = *pSize; + size_t size = 0; + wImage* image = winpr_image_new(); + if (!image) + goto fail; + + const int res = winpr_image_read_buffer(image, data, SrcSize); + if (res <= 0) + goto fail; + + dst = winpr_image_write_buffer(image, WINPR_IMAGE_BITMAP, &size); + if ((size < sizeof(WINPR_BITMAP_FILE_HEADER)) || (size > UINT32_MAX)) + { + free(dst); + dst = NULL; + goto fail; + } + *pSize = (UINT32)size; + +fail: + winpr_image_free(image, TRUE); + + if (dst) + memmove(dst, &dst[sizeof(WINPR_BITMAP_FILE_HEADER)], + size - sizeof(WINPR_BITMAP_FILE_HEADER)); + return dst; +} + +static void* clipboard_synthesize_image_png_to_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize); +} +#endif + +#if defined(WINPR_UTILS_IMAGE_WEBP) +static void* clipboard_synthesize_image_bmp_to_webp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_WEBP, data, + pSize); +} + +static void* clipboard_synthesize_image_webp_to_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize); +} +#endif + +#if defined(WINPR_UTILS_IMAGE_JPEG) +static void* clipboard_synthesize_image_bmp_to_jpeg(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_JPEG, data, + pSize); +} + +static void* clipboard_synthesize_image_jpeg_to_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize); +} +#endif + +/** + * "HTML Format": + * + * HTML clipboard format: msdn.microsoft.com/en-us/library/windows/desktop/ms649015/ + */ + +static void* clipboard_synthesize_html_format(wClipboard* clipboard, UINT32 formatId, + const void* pData, UINT32* pSize) +{ + union + { + const void* cpv; + const char* cpc; + const BYTE* cpb; + WCHAR* pv; + } pSrcData; + char* pDstData = NULL; + + pSrcData.cpv = NULL; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(pSize); + + if (formatId == ClipboardGetFormatId(clipboard, "text/html")) + { + const INT64 SrcSize = (INT64)*pSize; + const size_t DstSize = SrcSize + 200; + char* body = NULL; + char num[20] = { 0 }; + + /* Create a copy, we modify the input data */ + pSrcData.pv = calloc(1, SrcSize + 1); + if (!pSrcData.pv) + goto fail; + memcpy(pSrcData.pv, pData, SrcSize); + + if (SrcSize > 2) + { + if (SrcSize > INT_MAX) + goto fail; + + /* Check the BOM (Byte Order Mark) */ + if ((pSrcData.cpb[0] == 0xFE) && (pSrcData.cpb[1] == 0xFF)) + ByteSwapUnicode(pSrcData.pv, (SrcSize / 2)); + + /* Check if we have WCHAR, convert to UTF-8 */ + if ((pSrcData.cpb[0] == 0xFF) && (pSrcData.cpb[1] == 0xFE)) + { + char* utfString = + ConvertWCharNToUtf8Alloc(&pSrcData.pv[1], SrcSize / sizeof(WCHAR), NULL); + free(pSrcData.pv); + pSrcData.cpc = utfString; + if (!utfString) + goto fail; + } + } + + pDstData = (char*)calloc(1, DstSize); + + if (!pDstData) + goto fail; + + sprintf_s(pDstData, DstSize, + "Version:0.9\r\n" + "StartHTML:0000000000\r\n" + "EndHTML:0000000000\r\n" + "StartFragment:0000000000\r\n" + "EndFragment:0000000000\r\n"); + body = strstr(pSrcData.cpc, "", pDstData, DstSize, NULL)) + goto fail; + } + + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + + /* StartFragment */ + sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); + CopyMemory(&pDstData[69], num, 10); + + if (!winpr_str_append(pSrcData.cpc, pDstData, DstSize, NULL)) + goto fail; + + /* EndFragment */ + sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); + CopyMemory(&pDstData[93], num, 10); + + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + + if (!body) + { + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + } + + /* EndHTML */ + sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize)); + CopyMemory(&pDstData[43], num, 10); + *pSize = (UINT32)strnlen(pDstData, DstSize) + 1; + } +fail: + free(pSrcData.pv); + return pDstData; +} + +/** + * "text/html": + * + * HTML text format. + */ + +static void* clipboard_synthesize_text_html(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + long beg = 0; + long end = 0; + const char* str = NULL; + char* begStr = NULL; + char* endStr = NULL; + long DstSize = -1; + BYTE* pDstData = NULL; + + if (formatId == ClipboardGetFormatId(clipboard, "HTML Format")) + { + INT64 SrcSize = 0; + str = (const char*)data; + SrcSize = (INT64)*pSize; + begStr = strstr(str, "StartHTML:"); + endStr = strstr(str, "EndHTML:"); + + if (!begStr || !endStr) + return NULL; + + errno = 0; + beg = strtol(&begStr[10], NULL, 10); + + if (errno != 0) + return NULL; + + end = strtol(&endStr[8], NULL, 10); + + if (beg < 0 || end < 0 || (beg > SrcSize) || (end > SrcSize) || (beg >= end) || + (errno != 0)) + return NULL; + + DstSize = end - beg; + pDstData = (BYTE*)malloc((size_t)(SrcSize - beg + 1)); + + if (!pDstData) + return NULL; + + CopyMemory(pDstData, &str[beg], DstSize); + DstSize = ConvertLineEndingToLF((char*)pDstData, DstSize); + *pSize = (UINT32)DstSize; + } + + return (void*)pDstData; +} + +BOOL ClipboardInitSynthesizers(wClipboard* clipboard) +{ + /** + * CF_TEXT + */ + { + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_LOCALE, clipboard_synthesize_cf_locale); + + UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, altFormatId, + clipboard_synthesize_utf8_string); + } + /** + * CF_OEMTEXT + */ + { + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_TEXT, clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_LOCALE, + clipboard_synthesize_cf_locale); + UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, altFormatId, + clipboard_synthesize_utf8_string); + } + /** + * CF_UNICODETEXT + */ + { + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_TEXT, + clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_LOCALE, + clipboard_synthesize_cf_locale); + UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, altFormatId, + clipboard_synthesize_utf8_string); + } + /** + * UTF8_STRING + */ + { + UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + + if (formatId) + { + ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, + clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE, + clipboard_synthesize_cf_locale); + } + } + /** + * text/plain + */ + { + UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + + if (formatId) + { + ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, + clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE, + clipboard_synthesize_cf_locale); + } + } + /** + * CF_DIB + */ + { + ClipboardRegisterSynthesizer(clipboard, CF_DIB, CF_DIBV5, clipboard_synthesize_cf_dibv5); + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime); + if (altFormatId == 0) + continue; + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp); + } + } + + /** + * CF_DIBV5 + */ + + if (0) + { + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, CF_DIB, clipboard_synthesize_cf_dib); + + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime); + if (altFormatId == 0) + continue; + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_bmp); + } + } + + /** + * image/bmp + */ + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime); + if (altFormatId == 0) + continue; + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, clipboard_synthesize_cf_dib); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_cf_dibv5); + } + + /** + * image/png + */ +#if defined(WINPR_UTILS_IMAGE_PNG) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_png); + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp_to_png); + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_bmp_to_png); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, + clipboard_synthesize_image_png_to_bmp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_image_png_to_bmp); + } +#endif + + /** + * image/webp + */ +#if defined(WINPR_UTILS_IMAGE_WEBP) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_webp); + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp_to_webp); + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_webp_to_bmp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, + clipboard_synthesize_image_bmp_to_webp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_image_webp_to_bmp); + } +#endif + + /** + * image/jpeg + */ +#if defined(WINPR_UTILS_IMAGE_JPEG) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_jpeg); + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp_to_jpeg); + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_jpeg_to_bmp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, + clipboard_synthesize_image_bmp_to_jpeg); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_image_jpeg_to_bmp); + } +#endif + + /** + * HTML Format + */ + { + UINT32 formatId = ClipboardRegisterFormat(clipboard, "HTML Format"); + + if (formatId) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, "text/html"); + ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId, + clipboard_synthesize_text_html); + } + } + + /** + * text/html + */ + { + UINT32 formatId = ClipboardRegisterFormat(clipboard, "text/html"); + + if (formatId) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, "HTML Format"); + ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId, + clipboard_synthesize_html_format); + } + } + + return TRUE; +} diff --git a/winpr/libwinpr/clipboard/synthetic_file.c b/winpr/libwinpr/clipboard/synthetic_file.c new file mode 100644 index 0000000..1421980 --- /dev/null +++ b/winpr/libwinpr/clipboard/synthetic_file.c @@ -0,0 +1,1262 @@ +/** + * WinPR: Windows Portable Runtime + * Clipboard Functions: POSIX file handling + * + * Copyright 2017 Alexei Lozovsky + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#define _FILE_OFFSET_BITS 64 + +WINPR_PRAGMA_DIAG_POP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clipboard.h" +#include "synthetic_file.h" + +#include "../log.h" +#define TAG WINPR_TAG("clipboard.synthetic.file") + +static const char* mime_uri_list = "text/uri-list"; +static const char* mime_FileGroupDescriptorW = "FileGroupDescriptorW"; +static const char* mime_gnome_copied_files = "x-special/gnome-copied-files"; +static const char* mime_mate_copied_files = "x-special/mate-copied-files"; + +struct synthetic_file +{ + WCHAR* local_name; + WCHAR* remote_name; + + HANDLE fd; + INT64 offset; + + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +}; + +void free_synthetic_file(struct synthetic_file* file); + +static struct synthetic_file* make_synthetic_file(const WCHAR* local_name, const WCHAR* remote_name) +{ + struct synthetic_file* file = NULL; + WIN32_FIND_DATAW fd = { 0 }; + HANDLE hFind = NULL; + + WINPR_ASSERT(local_name); + WINPR_ASSERT(remote_name); + + hFind = FindFirstFileW(local_name, &fd); + if (INVALID_HANDLE_VALUE == hFind) + { + WLog_ERR(TAG, "FindFirstFile failed (%" PRIu32 ")", GetLastError()); + return NULL; + } + FindClose(hFind); + + file = calloc(1, sizeof(*file)); + if (!file) + return NULL; + + file->fd = INVALID_HANDLE_VALUE; + file->offset = 0; + file->local_name = _wcsdup(local_name); + if (!file->local_name) + goto fail; + + file->remote_name = _wcsdup(remote_name); + if (!file->remote_name) + goto fail; + + const size_t len = _wcslen(file->remote_name); + PathCchConvertStyleW(file->remote_name, len, PATH_STYLE_WINDOWS); + + file->dwFileAttributes = fd.dwFileAttributes; + file->ftCreationTime = fd.ftCreationTime; + file->ftLastWriteTime = fd.ftLastWriteTime; + file->ftLastAccessTime = fd.ftLastAccessTime; + file->nFileSizeHigh = fd.nFileSizeHigh; + file->nFileSizeLow = fd.nFileSizeLow; + + return file; +fail: + free_synthetic_file(file); + return NULL; +} + +static UINT synthetic_file_read_close(struct synthetic_file* file, BOOL force); + +void free_synthetic_file(struct synthetic_file* file) +{ + if (!file) + return; + + synthetic_file_read_close(file, TRUE); + + free(file->local_name); + free(file->remote_name); + free(file); +} + +/* + * Note that the function converts a single file name component, + * it does not take care of component separators. + */ +static WCHAR* convert_local_name_component_to_remote(wClipboard* clipboard, const WCHAR* local_name) +{ + wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard); + WCHAR* remote_name = NULL; + + WINPR_ASSERT(delegate); + + remote_name = _wcsdup(local_name); + + /* + * Some file names are not valid on Windows. Check for these now + * so that we won't get ourselves into a trouble later as such names + * are known to crash some Windows shells when pasted via clipboard. + * + * The IsFileNameComponentValid callback can be overridden by the API + * user, if it is known, that the connected peer is not on the + * Windows platform. + */ + if (!delegate->IsFileNameComponentValid(remote_name)) + { + WLog_ERR(TAG, "invalid file name component: %s", local_name); + goto error; + } + + return remote_name; +error: + free(remote_name); + return NULL; +} + +static WCHAR* concat_file_name(const WCHAR* dir, const WCHAR* file) +{ + size_t len_dir = 0; + size_t len_file = 0; + const WCHAR slash = '/'; + WCHAR* buffer = NULL; + + WINPR_ASSERT(dir); + WINPR_ASSERT(file); + + len_dir = _wcslen(dir); + len_file = _wcslen(file); + buffer = calloc(len_dir + 1 + len_file + 2, sizeof(WCHAR)); + + if (!buffer) + return NULL; + + memcpy(buffer, dir, len_dir * sizeof(WCHAR)); + buffer[len_dir] = slash; + memcpy(buffer + len_dir + 1, file, len_file * sizeof(WCHAR)); + return buffer; +} + +static BOOL add_file_to_list(wClipboard* clipboard, const WCHAR* local_name, + const WCHAR* remote_name, wArrayList* files); + +static BOOL add_directory_entry_to_list(wClipboard* clipboard, const WCHAR* local_dir_name, + const WCHAR* remote_dir_name, + const LPWIN32_FIND_DATAW pFileData, wArrayList* files) +{ + BOOL result = FALSE; + WCHAR* local_name = NULL; + WCHAR* remote_name = NULL; + WCHAR* remote_base_name = NULL; + + WCHAR dotbuffer[6] = { 0 }; + WCHAR dotdotbuffer[6] = { 0 }; + const WCHAR* dot = InitializeConstWCharFromUtf8(".", dotbuffer, ARRAYSIZE(dotbuffer)); + const WCHAR* dotdot = InitializeConstWCharFromUtf8("..", dotdotbuffer, ARRAYSIZE(dotdotbuffer)); + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(local_dir_name); + WINPR_ASSERT(remote_dir_name); + WINPR_ASSERT(pFileData); + WINPR_ASSERT(files); + + /* Skip special directory entries. */ + + if ((_wcscmp(pFileData->cFileName, dot) == 0) || (_wcscmp(pFileData->cFileName, dotdot) == 0)) + return TRUE; + + remote_base_name = convert_local_name_component_to_remote(clipboard, pFileData->cFileName); + + if (!remote_base_name) + return FALSE; + + local_name = concat_file_name(local_dir_name, pFileData->cFileName); + remote_name = concat_file_name(remote_dir_name, remote_base_name); + + if (local_name && remote_name) + result = add_file_to_list(clipboard, local_name, remote_name, files); + + free(remote_base_name); + free(remote_name); + free(local_name); + return result; +} + +static BOOL do_add_directory_contents_to_list(wClipboard* clipboard, const WCHAR* local_name, + const WCHAR* remote_name, WCHAR* namebuf, + wArrayList* files) +{ + WINPR_ASSERT(clipboard); + WINPR_ASSERT(local_name); + WINPR_ASSERT(remote_name); + WINPR_ASSERT(files); + WINPR_ASSERT(namebuf); + + WIN32_FIND_DATAW FindData = { 0 }; + HANDLE hFind = FindFirstFileW(namebuf, &FindData); + if (INVALID_HANDLE_VALUE == hFind) + { + WLog_ERR(TAG, "FindFirstFile failed (%" PRIu32 ")", GetLastError()); + return FALSE; + } + while (TRUE) + { + if (!add_directory_entry_to_list(clipboard, local_name, remote_name, &FindData, files)) + { + FindClose(hFind); + return FALSE; + } + + BOOL bRet = FindNextFileW(hFind, &FindData); + if (!bRet) + { + FindClose(hFind); + if (ERROR_NO_MORE_FILES == GetLastError()) + return TRUE; + WLog_WARN(TAG, "FindNextFile failed (%" PRIu32 ")", GetLastError()); + return FALSE; + } + } + + return TRUE; +} + +static BOOL add_directory_contents_to_list(wClipboard* clipboard, const WCHAR* local_name, + const WCHAR* remote_name, wArrayList* files) +{ + BOOL result = FALSE; + const WCHAR* wildcard = "/\0*\0\0\0"; + const size_t wildcardLen = 3; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(local_name); + WINPR_ASSERT(remote_name); + WINPR_ASSERT(files); + + size_t len = _wcslen(local_name); + WCHAR* namebuf = calloc(len + wildcardLen, sizeof(WCHAR)); + if (!namebuf) + return FALSE; + + _wcsncat(namebuf, local_name, len); + _wcsncat(namebuf, wildcard, wildcardLen); + + result = do_add_directory_contents_to_list(clipboard, local_name, remote_name, namebuf, files); + + free(namebuf); + return result; +} + +static BOOL add_file_to_list(wClipboard* clipboard, const WCHAR* local_name, + const WCHAR* remote_name, wArrayList* files) +{ + struct synthetic_file* file = NULL; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(local_name); + WINPR_ASSERT(remote_name); + WINPR_ASSERT(files); + + file = make_synthetic_file(local_name, remote_name); + + if (!file) + return FALSE; + + if (!ArrayList_Append(files, file)) + { + free_synthetic_file(file); + return FALSE; + } + + if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + /* + * This is effectively a recursive call, but we do not track + * recursion depth, thus filesystem loops can cause a crash. + */ + if (!add_directory_contents_to_list(clipboard, local_name, remote_name, files)) + return FALSE; + } + + return TRUE; +} + +static const WCHAR* get_basename(const WCHAR* name) +{ + const WCHAR* c = name; + const WCHAR* last_name = name; + const WCHAR slash = '/'; + + WINPR_ASSERT(name); + + while (*c++) + { + if (*c == slash) + last_name = c + 1; + } + + return last_name; +} + +static BOOL process_file_name(wClipboard* clipboard, const WCHAR* local_name, wArrayList* files) +{ + BOOL result = FALSE; + const WCHAR* base_name = NULL; + WCHAR* remote_name = NULL; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(local_name); + WINPR_ASSERT(files); + + /* + * Start with the base name of the file. text/uri-list contains the + * exact files selected by the user, and we want the remote files + * to have names relative to that selection. + */ + base_name = get_basename(local_name); + remote_name = convert_local_name_component_to_remote(clipboard, base_name); + + if (!remote_name) + return FALSE; + + result = add_file_to_list(clipboard, local_name, remote_name, files); + free(remote_name); + return result; +} + +static BOOL process_uri(wClipboard* clipboard, const char* uri, size_t uri_len) +{ + // URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089 + BOOL result = FALSE; + char* name = NULL; + + WINPR_ASSERT(clipboard); + + name = parse_uri_to_local_file(uri, uri_len); + if (name) + { + WCHAR* wname = NULL; + /* + * Note that local file names are not actually guaranteed to be + * encoded in UTF-8. Filesystems and users can use whatever they + * want. The OS does not care, aside from special treatment of + * '\0' and '/' bytes. But we need to make some decision here. + * Assuming UTF-8 is currently the most sane thing. + */ + wname = ConvertUtf8ToWCharAlloc(name, NULL); + if (wname) + result = process_file_name(clipboard, wname, clipboard->localFiles); + + free(name); + free(wname); + } + + return result; +} + +static BOOL process_uri_list(wClipboard* clipboard, const char* data, size_t length) +{ + const char* cur = data; + const char* lim = data + length; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(data); + + WLog_VRB(TAG, "processing URI list:\n%.*s", length, data); + ArrayList_Clear(clipboard->localFiles); + + /* + * The "text/uri-list" Internet Media Type is specified by RFC 2483. + * + * While the RFCs 2046 and 2483 require the lines of text/... formats + * to be terminated by CRLF sequence, be prepared for those who don't + * read the spec, use plain LFs, and don't leave the trailing CRLF. + */ + + while (cur < lim) + { + BOOL comment = (*cur == '#'); + const char* start = cur; + const char* stop = cur; + + for (; stop < lim; stop++) + { + if (*stop == '\r') + { + if ((stop + 1 < lim) && (*(stop + 1) == '\n')) + cur = stop + 2; + else + cur = stop + 1; + + break; + } + + if (*stop == '\n') + { + cur = stop + 1; + break; + } + } + + if (stop == lim) + { + if (strnlen(start, stop - start) < 1) + return TRUE; + cur = lim; + } + + if (comment) + continue; + + if (!process_uri(clipboard, start, stop - start)) + return FALSE; + } + + return TRUE; +} + +static BOOL convert_local_file_to_filedescriptor(const struct synthetic_file* file, + FILEDESCRIPTORW* descriptor) +{ + size_t remote_len = 0; + + WINPR_ASSERT(file); + WINPR_ASSERT(descriptor); + + descriptor->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI; + + if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + descriptor->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + descriptor->nFileSizeLow = 0; + descriptor->nFileSizeHigh = 0; + } + else + { + descriptor->dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + descriptor->nFileSizeLow = file->nFileSizeLow; + descriptor->nFileSizeHigh = file->nFileSizeHigh; + } + + descriptor->ftLastWriteTime = file->ftLastWriteTime; + + remote_len = _wcsnlen(file->remote_name, ARRAYSIZE(descriptor->cFileName)); + + if (remote_len >= ARRAYSIZE(descriptor->cFileName)) + { + WLog_ERR(TAG, "file name too long (%" PRIuz " characters)", remote_len); + return FALSE; + } + + memcpy(descriptor->cFileName, file->remote_name, remote_len * sizeof(WCHAR)); + return TRUE; +} + +static FILEDESCRIPTORW* convert_local_file_list_to_filedescriptors(wArrayList* files) +{ + size_t count = 0; + FILEDESCRIPTORW* descriptors = NULL; + + count = ArrayList_Count(files); + + descriptors = calloc(count, sizeof(FILEDESCRIPTORW)); + + if (!descriptors) + goto error; + + for (size_t i = 0; i < count; i++) + { + const struct synthetic_file* file = ArrayList_GetItem(files, i); + + if (!convert_local_file_to_filedescriptor(file, &descriptors[i])) + goto error; + } + + return descriptors; +error: + free(descriptors); + return NULL; +} + +static void* convert_any_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 formatId, + UINT32* pSize) +{ + FILEDESCRIPTORW* descriptors = NULL; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(pSize); + + descriptors = convert_local_file_list_to_filedescriptors(clipboard->localFiles); + *pSize = 0; + if (!descriptors) + return NULL; + + *pSize = (UINT32)ArrayList_Count(clipboard->localFiles) * sizeof(FILEDESCRIPTORW); + clipboard->fileListSequenceNumber = clipboard->sequenceNumber; + return descriptors; +} + +static void* convert_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + const UINT32 expected = ClipboardGetFormatId(clipboard, mime_uri_list); + if (formatId != expected) + return NULL; + if (!process_uri_list(clipboard, (const char*)data, *pSize)) + return NULL; + return convert_any_uri_list_to_filedescriptors(clipboard, formatId, pSize); +} + +static BOOL process_files(wClipboard* clipboard, const char* data, UINT32 pSize, const char* prefix) +{ + WINPR_ASSERT(prefix); + + const size_t prefix_len = strlen(prefix); + + WINPR_ASSERT(clipboard); + + ArrayList_Clear(clipboard->localFiles); + + if (!data || (pSize < prefix_len)) + return FALSE; + if (strncmp(data, prefix, prefix_len) != 0) + return FALSE; + data += prefix_len; + pSize -= prefix_len; + + BOOL rc = FALSE; + char* copy = strndup(data, pSize); + if (!copy) + goto fail; + + char* endptr = NULL; + char* tok = strtok_s(copy, "\n", &endptr); + while (tok) + { + size_t tok_len = strnlen(tok, pSize); + if (!process_uri(clipboard, tok, tok_len)) + goto fail; + pSize -= tok_len; + tok = strtok_s(NULL, "\n", &endptr); + } + rc = TRUE; + +fail: + free(copy); + return rc; +} + +static BOOL process_gnome_copied_files(wClipboard* clipboard, const char* data, UINT32 pSize) +{ + return process_files(clipboard, data, pSize, "copy\n"); +} + +static BOOL process_mate_copied_files(wClipboard* clipboard, const char* data, UINT32 pSize) +{ + return process_files(clipboard, data, pSize, "copy\n"); +} + +static BOOL process_nautilus_clipboard(wClipboard* clipboard, const char* data, UINT32 pSize) +{ + return process_files(clipboard, data, pSize, "x-special/nautilus-clipboard\ncopy\n"); +} + +static void* convert_nautilus_clipboard_to_filedescriptors(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + const UINT32 expected = ClipboardGetFormatId(clipboard, mime_gnome_copied_files); + if (formatId != expected) + return NULL; + if (!process_nautilus_clipboard(clipboard, (const char*)data, *pSize)) + return NULL; + return convert_any_uri_list_to_filedescriptors(clipboard, formatId, pSize); +} + +static void* convert_gnome_copied_files_to_filedescriptors(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + const UINT32 expected = ClipboardGetFormatId(clipboard, mime_gnome_copied_files); + if (formatId != expected) + return NULL; + if (!process_gnome_copied_files(clipboard, (const char*)data, *pSize)) + return NULL; + return convert_any_uri_list_to_filedescriptors(clipboard, formatId, pSize); +} + +static void* convert_mate_copied_files_to_filedescriptors(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + const UINT32 expected = ClipboardGetFormatId(clipboard, mime_mate_copied_files); + if (formatId != expected) + return NULL; + + if (!process_mate_copied_files(clipboard, (const char*)data, *pSize)) + return NULL; + + return convert_any_uri_list_to_filedescriptors(clipboard, formatId, pSize); +} + +static size_t count_special_chars(const WCHAR* str) +{ + size_t count = 0; + const WCHAR* start = str; + + WINPR_ASSERT(str); + while (*start) + { + const WCHAR sharp = '#'; + const WCHAR questionmark = '?'; + const WCHAR star = '*'; + const WCHAR exclamationmark = '!'; + const WCHAR percent = '%'; + + if ((*start == sharp) || (*start == questionmark) || (*start == star) || + (*start == exclamationmark) || (*start == percent)) + { + count++; + } + start++; + } + return count; +} + +static const char* stop_at_special_chars(const char* str) +{ + const char* start = str; + WINPR_ASSERT(str); + + while (*start) + { + if (*start == '#' || *start == '?' || *start == '*' || *start == '!' || *start == '%') + { + return start; + } + start++; + } + return NULL; +} + +/* The universal converter from filedescriptors to different file lists */ +static void* convert_filedescriptors_to_file_list(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize, + const char* header, const char* lineprefix, + const char* lineending, BOOL skip_last_lineending) +{ + union + { + char c[2]; + WCHAR w; + } backslash; + backslash.c[0] = '\\'; + backslash.c[1] = '\0'; + + const FILEDESCRIPTORW* descriptors = NULL; + UINT32 nrDescriptors = 0; + size_t count = 0; + size_t alloc = 0; + size_t pos = 0; + size_t baseLength = 0; + char* dst = NULL; + size_t header_len = strlen(header); + size_t lineprefix_len = strlen(lineprefix); + size_t lineending_len = strlen(lineending); + size_t decoration_len = 0; + + if (!clipboard || !data || !pSize) + return NULL; + + if (*pSize < sizeof(UINT32)) + return NULL; + + if (clipboard->delegate.basePath) + baseLength = strnlen(clipboard->delegate.basePath, MAX_PATH); + + if (baseLength < 1) + return NULL; + + wStream sbuffer = { 0 }; + wStream* s = Stream_StaticConstInit(&sbuffer, data, *pSize); + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return NULL; + + Stream_Read_UINT32(s, nrDescriptors); + + count = (*pSize - 4) / sizeof(FILEDESCRIPTORW); + + if ((count < 1) || (count != nrDescriptors)) + return NULL; + + descriptors = Stream_ConstPointer(s); + + if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW)) + return NULL; + + /* Plus 1 for '/' between basepath and filename*/ + decoration_len = lineprefix_len + lineending_len + baseLength + 1; + alloc = header_len; + + /* Get total size of file/folder names under first level folder only */ + for (size_t x = 0; x < count; x++) + { + const FILEDESCRIPTORW* dsc = &descriptors[x]; + + if (_wcschr(dsc->cFileName, backslash.w) == NULL) + { + alloc += ARRAYSIZE(dsc->cFileName) * + 8; /* Overallocate, just take the biggest value the result path can have */ + /* # (1 char) -> %23 (3 chars) , the first char is replaced inplace */ + alloc += count_special_chars(dsc->cFileName) * 2; + alloc += decoration_len; + } + } + + /* Append a prefix file:// and postfix \n for each file */ + /* We need to keep last \n since snprintf is null terminated!! */ + alloc++; + dst = calloc(alloc, sizeof(char)); + + if (!dst) + return NULL; + + _snprintf(&dst[0], alloc, "%s", header); + + pos = header_len; + + for (size_t x = 0; x < count; x++) + { + const FILEDESCRIPTORW* dsc = &descriptors[x]; + BOOL fail = TRUE; + if (_wcschr(dsc->cFileName, backslash.w) != NULL) + { + continue; + } + int rc = -1; + char curName[520] = { 0 }; + const char* stop_at = NULL; + const char* previous_at = NULL; + + if (ConvertWCharNToUtf8(dsc->cFileName, ARRAYSIZE(dsc->cFileName), curName, + ARRAYSIZE(curName)) < 0) + goto loop_fail; + + rc = _snprintf(&dst[pos], alloc - pos, "%s%s/", lineprefix, clipboard->delegate.basePath); + + if (rc < 0) + goto loop_fail; + + pos += (size_t)rc; + + previous_at = curName; + while ((stop_at = stop_at_special_chars(previous_at)) != NULL) + { + char* tmp = strndup(previous_at, stop_at - previous_at); + if (!tmp) + goto loop_fail; + + rc = _snprintf(&dst[pos], stop_at - previous_at + 1, "%s", tmp); + free(tmp); + if (rc < 0) + goto loop_fail; + + pos += (size_t)rc; + rc = _snprintf(&dst[pos], 4, "%%%x", *stop_at); + if (rc < 0) + goto loop_fail; + + pos += (size_t)rc; + previous_at = stop_at + 1; + } + + rc = _snprintf(&dst[pos], alloc - pos, "%s%s", previous_at, lineending); + + fail = FALSE; + loop_fail: + if ((rc < 0) || fail) + { + free(dst); + return NULL; + } + + pos += (size_t)rc; + } + + if (skip_last_lineending) + { + const size_t endlen = strlen(lineending); + if (alloc > endlen) + { + const size_t len = strnlen(dst, alloc); + if (len < endlen) + { + free(dst); + return NULL; + } + + if (memcmp(&dst[len - endlen], lineending, endlen) == 0) + { + memset(&dst[len - endlen], 0, endlen); + alloc -= endlen; + } + } + } + + alloc = strnlen(dst, alloc) + 1; + *pSize = (UINT32)alloc; + clipboard->fileListSequenceNumber = clipboard->sequenceNumber; + return dst; +} + +/* Prepend header of kde dolphin format to file list + * See: + * GTK: https://docs.gtk.org/glib/struct.Uri.html + * uri syntax: https://www.rfc-editor.org/rfc/rfc3986#section-3 + * uri-lists format: https://www.rfc-editor.org/rfc/rfc2483#section-5 + */ +static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return convert_filedescriptors_to_file_list(clipboard, formatId, data, pSize, "", "file://", + "\r\n", FALSE); +} + +/* Prepend header of common gnome format to file list*/ +static void* convert_filedescriptors_to_gnome_copied_files(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return convert_filedescriptors_to_file_list(clipboard, formatId, data, pSize, "copy\n", + "file://", "\n", TRUE); +} + +/* Prepend header of nautilus based filemanager's format to file list*/ +static void* convert_filedescriptors_to_nautilus_clipboard(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + /* Here Nemo (and Caja) have different behavior. They encounter error with the last \n . but + nautilus needs it. So user have to skip Nemo's error dialog to continue. Caja has different + TARGET , so it's easy to fix. see convert_filedescriptors_to_mate_copied_files + + The text based "x-special/nautilus-clipboard" type was introduced with GNOME 3.30 and + was necessary for the desktop icons extension, as gnome-shell at that time only + supported text based mime types for gnome extensions. With GNOME 3.38, gnome-shell got + support for non-text based mime types for gnome extensions. With GNOME 40, nautilus reverted + the mime type change to "x-special/gnome-copied-files" and removed support for the text based + mime type. So, in the near future, change this behaviour in favor for Nemo and Caja. + */ + /* see nautilus/src/nautilus-clipboard.c:convert_selection_data_to_str_list + see nemo/libnemo-private/nemo-clipboard.c:nemo_clipboard_get_uri_list_from_selection_data + */ + + return convert_filedescriptors_to_file_list(clipboard, formatId, data, pSize, + "x-special/nautilus-clipboard\ncopy\n", "file://", + "\n", FALSE); +} + +static void* convert_filedescriptors_to_mate_copied_files(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + + char* pDstData = convert_filedescriptors_to_file_list(clipboard, formatId, data, pSize, + "copy\n", "file://", "\n", TRUE); + if (!pDstData) + { + return pDstData; + } + /* Replace last \n with \0 + see + mate-desktop/caja/libcaja-private/caja-clipboard.c:caja_clipboard_get_uri_list_from_selection_data + */ + + pDstData[*pSize - 1] = '\0'; + *pSize = *pSize - 1; + return pDstData; +} + +static void array_free_synthetic_file(void* the_file) +{ + struct synthetic_file* file = the_file; + free_synthetic_file(file); +} + +static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard) +{ + wObject* obj = NULL; + + /* + 1. Gnome Nautilus based file manager (Nautilus only with version >= 3.30 AND < 40): + TARGET: UTF8_STRING + format: x-special/nautilus-clipboard\copy\n\file://path\n\0 + 2. Kde Dolpin and Qt: + TARGET: text/uri-list + format: file:path\r\n\0 + See: + GTK: https://docs.gtk.org/glib/struct.Uri.html + uri syntax: https://www.rfc-editor.org/rfc/rfc3986#section-3 + uri-lists fomat: https://www.rfc-editor.org/rfc/rfc2483#section-5 + 3. Gnome and others (Unity/XFCE/Nautilus < 3.30/Nautilus >= 40): + TARGET: x-special/gnome-copied-files + format: copy\nfile://path\n\0 + 4. Mate Caja: + TARGET: x-special/mate-copied-files + format: copy\nfile://path\n + + TODO: other file managers do not use previous targets and formats. + */ + + const UINT32 local_gnome_file_format_id = + ClipboardRegisterFormat(clipboard, mime_gnome_copied_files); + const UINT32 local_mate_file_format_id = + ClipboardRegisterFormat(clipboard, mime_mate_copied_files); + const UINT32 file_group_format_id = + ClipboardRegisterFormat(clipboard, mime_FileGroupDescriptorW); + const UINT32 local_file_format_id = ClipboardRegisterFormat(clipboard, mime_uri_list); + + if (!file_group_format_id || !local_file_format_id || !local_gnome_file_format_id || + !local_mate_file_format_id) + goto error; + + clipboard->localFiles = ArrayList_New(FALSE); + + if (!clipboard->localFiles) + goto error; + + obj = ArrayList_Object(clipboard->localFiles); + obj->fnObjectFree = array_free_synthetic_file; + + if (!ClipboardRegisterSynthesizer(clipboard, local_file_format_id, file_group_format_id, + convert_uri_list_to_filedescriptors)) + goto error_free_local_files; + + if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id, local_file_format_id, + convert_filedescriptors_to_uri_list)) + goto error_free_local_files; + + if (!ClipboardRegisterSynthesizer(clipboard, local_gnome_file_format_id, file_group_format_id, + convert_gnome_copied_files_to_filedescriptors)) + goto error_free_local_files; + + if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id, local_gnome_file_format_id, + convert_filedescriptors_to_gnome_copied_files)) + goto error_free_local_files; + + if (!ClipboardRegisterSynthesizer(clipboard, local_mate_file_format_id, file_group_format_id, + convert_mate_copied_files_to_filedescriptors)) + goto error_free_local_files; + + if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id, local_mate_file_format_id, + convert_filedescriptors_to_mate_copied_files)) + goto error_free_local_files; + + return TRUE; +error_free_local_files: + ArrayList_Free(clipboard->localFiles); + clipboard->localFiles = NULL; +error: + return FALSE; +} + +static UINT file_get_size(const struct synthetic_file* file, UINT64* size) +{ + UINT64 s = 0; + + if (!file || !size) + return E_INVALIDARG; + + s = file->nFileSizeHigh; + s <<= 32; + s |= file->nFileSizeLow; + *size = s; + return NO_ERROR; +} + +static UINT delegate_file_request_size(wClipboardDelegate* delegate, + const wClipboardFileSizeRequest* request) +{ + UINT error = NO_ERROR; + UINT64 size = 0; + struct synthetic_file* file = NULL; + + if (!delegate || !delegate->clipboard || !request) + return ERROR_BAD_ARGUMENTS; + + if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber) + return ERROR_INVALID_STATE; + + file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex); + + if (!file) + return ERROR_INDEX_ABSENT; + + error = file_get_size(file, &size); + + if (error) + error = delegate->ClipboardFileSizeFailure(delegate, request, error); + else + error = delegate->ClipboardFileSizeSuccess(delegate, request, size); + + if (error) + WLog_WARN(TAG, "failed to report file size result: 0x%08X", error); + + return NO_ERROR; +} + +UINT synthetic_file_read_close(struct synthetic_file* file, BOOL force) +{ + if (!file || INVALID_HANDLE_VALUE == file->fd) + return NO_ERROR; + + /* Always force close the file. Clipboard might open hundreds of files + * so avoid caching to prevent running out of available file descriptors */ + UINT64 size = 0; + file_get_size(file, &size); + if ((file->offset < 0) || ((UINT64)file->offset >= size) || force) + { + WLog_VRB(TAG, "close file %d", file->fd); + if (!CloseHandle(file->fd)) + { + WLog_WARN(TAG, "failed to close fd %d: %" PRIu32, file->fd, GetLastError()); + } + + file->fd = INVALID_HANDLE_VALUE; + } + + return NO_ERROR; +} + +static UINT file_get_range(struct synthetic_file* file, UINT64 offset, UINT32 size, + BYTE** actual_data, UINT32* actual_size) +{ + UINT error = NO_ERROR; + DWORD dwLow = 0; + DWORD dwHigh = 0; + BYTE* buffer = NULL; + + WINPR_ASSERT(file); + WINPR_ASSERT(actual_data); + WINPR_ASSERT(actual_size); + + if (INVALID_HANDLE_VALUE == file->fd) + { + BY_HANDLE_FILE_INFORMATION FileInfo = { 0 }; + + file->fd = CreateFileW(file->local_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == file->fd) + { + error = GetLastError(); + WLog_ERR(TAG, "failed to open file %s: 0x%08" PRIx32, file->local_name, error); + return error; + } + + if (!GetFileInformationByHandle(file->fd, &FileInfo)) + { + file->fd = INVALID_HANDLE_VALUE; + CloseHandle(file->fd); + error = GetLastError(); + WLog_ERR(TAG, "Get file [%s] information fail: 0x%08" PRIx32, file->local_name, error); + return error; + } + + file->offset = 0; + file->nFileSizeHigh = FileInfo.nFileSizeHigh; + file->nFileSizeLow = FileInfo.nFileSizeLow; + + /* + { + UINT64 s = 0; + file_get_size(file, &s); + WLog_DBG(TAG, "open file %d -> %s", file->fd, file->local_name); + WLog_DBG(TAG, "file %d size: %" PRIu64 " bytes", file->fd, s); + } //*/ + } + + do + { + /* + * We should avoid seeking when possible as some filesystems (e.g., + * an FTP server mapped via FUSE) may not support seeking. We keep + * an accurate account of the current file offset and do not call + * lseek() if the client requests file content sequentially. + */ + if (offset > INT64_MAX) + { + WLog_ERR(TAG, "offset [%" PRIu64 "] > INT64_MAX", offset); + error = ERROR_SEEK; + break; + } + + if (file->offset != (INT64)offset) + { + WLog_DBG(TAG, "file %d force seeking to %" PRIu64 ", current %" PRIu64, file->fd, + offset, file->offset); + + dwHigh = offset >> 32; + dwLow = offset & 0xFFFFFFFF; + if (INVALID_SET_FILE_POINTER == + SetFilePointer(file->fd, dwLow, (PLONG)&dwHigh, FILE_BEGIN)) + { + error = GetLastError(); + break; + } + } + + buffer = malloc(size); + if (!buffer) + { + error = ERROR_NOT_ENOUGH_MEMORY; + break; + } + if (!ReadFile(file->fd, buffer, size, (LPDWORD)actual_size, NULL)) + { + error = GetLastError(); + break; + } + + *actual_data = buffer; + file->offset += *actual_size; + WLog_VRB(TAG, "file %d actual read %" PRIu32 " bytes (offset %" PRIu64 ")", file->fd, + *actual_size, file->offset); + } while (0); + + if (NO_ERROR != error) + { + free(buffer); + } + synthetic_file_read_close(file, TRUE /* (error != NO_ERROR) && (size > 0) */); + return error; +} + +static UINT delegate_file_request_range(wClipboardDelegate* delegate, + const wClipboardFileRangeRequest* request) +{ + UINT error = 0; + BYTE* data = NULL; + UINT32 size = 0; + UINT64 offset = 0; + struct synthetic_file* file = NULL; + + if (!delegate || !delegate->clipboard || !request) + return ERROR_BAD_ARGUMENTS; + + if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber) + return ERROR_INVALID_STATE; + + file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex); + + if (!file) + return ERROR_INDEX_ABSENT; + + offset = (((UINT64)request->nPositionHigh) << 32) | ((UINT64)request->nPositionLow); + error = file_get_range(file, offset, request->cbRequested, &data, &size); + + if (error) + error = delegate->ClipboardFileRangeFailure(delegate, request, error); + else + error = delegate->ClipboardFileRangeSuccess(delegate, request, data, size); + + if (error) + WLog_WARN(TAG, "failed to report file range result: 0x%08X", error); + + free(data); + return NO_ERROR; +} + +static UINT dummy_file_size_success(wClipboardDelegate* delegate, + const wClipboardFileSizeRequest* request, UINT64 fileSize) +{ + return ERROR_NOT_SUPPORTED; +} + +static UINT dummy_file_size_failure(wClipboardDelegate* delegate, + const wClipboardFileSizeRequest* request, UINT errorCode) +{ + return ERROR_NOT_SUPPORTED; +} + +static UINT dummy_file_range_success(wClipboardDelegate* delegate, + const wClipboardFileRangeRequest* request, const BYTE* data, + UINT32 size) +{ + return ERROR_NOT_SUPPORTED; +} + +static UINT dummy_file_range_failure(wClipboardDelegate* delegate, + const wClipboardFileRangeRequest* request, UINT errorCode) +{ + return ERROR_NOT_SUPPORTED; +} + +static void setup_delegate(wClipboardDelegate* delegate) +{ + WINPR_ASSERT(delegate); + + delegate->ClientRequestFileSize = delegate_file_request_size; + delegate->ClipboardFileSizeSuccess = dummy_file_size_success; + delegate->ClipboardFileSizeFailure = dummy_file_size_failure; + delegate->ClientRequestFileRange = delegate_file_request_range; + delegate->ClipboardFileRangeSuccess = dummy_file_range_success; + delegate->ClipboardFileRangeFailure = dummy_file_range_failure; + delegate->IsFileNameComponentValid = ValidFileNameComponent; +} + +BOOL ClipboardInitSyntheticFileSubsystem(wClipboard* clipboard) +{ + if (!clipboard) + return FALSE; + + if (!register_file_formats_and_synthesizers(clipboard)) + return FALSE; + + setup_delegate(&clipboard->delegate); + return TRUE; +} diff --git a/winpr/libwinpr/clipboard/synthetic_file.h b/winpr/libwinpr/clipboard/synthetic_file.h new file mode 100644 index 0000000..a92a5da --- /dev/null +++ b/winpr/libwinpr/clipboard/synthetic_file.h @@ -0,0 +1,27 @@ +/** + * WinPR: Windows Portable Runtime + * Clipboard Functions: POSIX file handling + * + * Copyright 2017 Alexei Lozovsky + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CLIPBOARD_POSIX_H +#define WINPR_CLIPBOARD_POSIX_H + +#include + +BOOL ClipboardInitSyntheticFileSubsystem(wClipboard* clipboard); + +#endif /* WINPR_CLIPBOARD_POSIX_H */ diff --git a/winpr/libwinpr/clipboard/test/CMakeLists.txt b/winpr/libwinpr/clipboard/test/CMakeLists.txt new file mode 100644 index 0000000..90996c0 --- /dev/null +++ b/winpr/libwinpr/clipboard/test/CMakeLists.txt @@ -0,0 +1,27 @@ + +set(MODULE_NAME "TestClipboard") +set(MODULE_PREFIX "TEST_CLIPBOARD") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestUri.c + TestClipboardFormats.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/clipboard/test/TestClipboardFormats.c b/winpr/libwinpr/clipboard/test/TestClipboardFormats.c new file mode 100644 index 0000000..615f8a0 --- /dev/null +++ b/winpr/libwinpr/clipboard/test/TestClipboardFormats.c @@ -0,0 +1,82 @@ + +#include +#include +#include + +int TestClipboardFormats(int argc, char* argv[]) +{ + UINT32 count = 0; + UINT32* pFormatIds = NULL; + const char* formatName = NULL; + wClipboard* clipboard = NULL; + UINT32 utf8StringFormatId = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + clipboard = ClipboardCreate(); + if (!clipboard) + return -1; + + ClipboardRegisterFormat(clipboard, "text/html"); + ClipboardRegisterFormat(clipboard, "image/bmp"); + ClipboardRegisterFormat(clipboard, "image/png"); + + utf8StringFormatId = ClipboardRegisterFormat(clipboard, "UTF8_STRING"); + pFormatIds = NULL; + count = ClipboardGetRegisteredFormatIds(clipboard, &pFormatIds); + + for (UINT32 index = 0; index < count; index++) + { + UINT32 formatId = pFormatIds[index]; + formatName = ClipboardGetFormatName(clipboard, formatId); + fprintf(stderr, "Format: 0x%08" PRIX32 " %s\n", formatId, formatName); + } + + free(pFormatIds); + + if (1) + { + BOOL bSuccess = 0; + UINT32 SrcSize = 0; + UINT32 DstSize = 0; + const char pSrcData[] = "this is a test string"; + char* pDstData = NULL; + + SrcSize = (UINT32)(strnlen(pSrcData, ARRAYSIZE(pSrcData)) + 1); + bSuccess = ClipboardSetData(clipboard, utf8StringFormatId, pSrcData, SrcSize); + fprintf(stderr, "ClipboardSetData: %" PRId32 "\n", bSuccess); + DstSize = 0; + pDstData = (char*)ClipboardGetData(clipboard, utf8StringFormatId, &DstSize); + fprintf(stderr, "ClipboardGetData: %s\n", pDstData); + free(pDstData); + } + + if (1) + { + UINT32 DstSize = 0; + char* pSrcData = NULL; + WCHAR* pDstData = NULL; + DstSize = 0; + pDstData = (WCHAR*)ClipboardGetData(clipboard, CF_UNICODETEXT, &DstSize); + pSrcData = ConvertWCharNToUtf8Alloc(pDstData, DstSize / sizeof(WCHAR), NULL); + + fprintf(stderr, "ClipboardGetData (synthetic): %s\n", pSrcData); + free(pDstData); + free(pSrcData); + } + + pFormatIds = NULL; + count = ClipboardGetFormatIds(clipboard, &pFormatIds); + + for (UINT32 index = 0; index < count; index++) + { + UINT32 formatId = pFormatIds[index]; + formatName = ClipboardGetFormatName(clipboard, formatId); + fprintf(stderr, "Format: 0x%08" PRIX32 " %s\n", formatId, formatName); + } + + free(pFormatIds); + ClipboardDestroy(clipboard); + return 0; +} diff --git a/winpr/libwinpr/clipboard/test/TestUri.c b/winpr/libwinpr/clipboard/test/TestUri.c new file mode 100644 index 0000000..c1cccd3 --- /dev/null +++ b/winpr/libwinpr/clipboard/test/TestUri.c @@ -0,0 +1,69 @@ + +#include +#include +#include +#include +#include +#include "winpr/wlog.h" + +#include "../clipboard.h" + +#define WINPR_TAG(tag) "com.winpr." tag +#define TAG WINPR_TAG("clipboard.posix") + +int TestUri(int argc, char* argv[]) +{ + int nRet = 0; + const char* input[] = { /*uri, file or NULL*/ + "file://root/a.txt", + NULL, + "file:a.txt", + NULL, + "file:///c:/windows/a.txt", + "c:/windows/a.txt", + "file:c:/windows/a.txt", + "c:/windows/a.txt", + "file:c|/windows/a.txt", + "c:/windows/a.txt", + "file:///root/a.txt", + "/root/a.txt", + "file:/root/a.txt", + "/root/a.txt" + }; + + const size_t nLen = ARRAYSIZE(input); + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + printf("input length:%" PRIuz "\n", nLen / 2); + + for (size_t i = 0; i < nLen; i += 2) + { + const char* in = input[i]; + const char* cmp = input[i + 1]; + int bTest = 0; + char* name = parse_uri_to_local_file(in, strlen(in)); + if (name) + { + bTest = !strcmp(name, cmp); + if (!bTest) + { + printf("Test error: input: %s; Expected value: %s; output: %s\n", in, cmp, name); + nRet++; + } + free(name); + } + else + { + if (cmp) + { + printf("Test error: input: %s; Expected value: %s; output: %s\n", in, cmp, name); + nRet++; + } + } + } + + printf("TestUri return value: %d\n", nRet); + return nRet; +} diff --git a/winpr/libwinpr/comm/CMakeLists.txt b/winpr/libwinpr/comm/CMakeLists.txt new file mode 100644 index 0000000..cc73741 --- /dev/null +++ b/winpr/libwinpr/comm/CMakeLists.txt @@ -0,0 +1,40 @@ +# WinPR: Windows Portable Runtime +# libwinpr-comm cmake build script +# +# Copyright 2014 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "winpr-comm") +set(MODULE_PREFIX "WINPR_COMM") + +if(UNIX AND NOT WIN32 AND NOT APPLE) + set(${MODULE_PREFIX}_SRCS + comm.c + comm.h + comm_io.c + comm_ioctl.c + comm_ioctl.h + comm_serial_sys.c + comm_serial_sys.h + comm_sercx_sys.c + comm_sercx_sys.h + comm_sercx2_sys.c + comm_sercx2_sys.h) + + winpr_module_add(${${MODULE_PREFIX}_SRCS}) + + if(BUILD_TESTING AND BUILD_COMM_TESTS) + add_subdirectory(test) + endif() +endif() diff --git a/winpr/libwinpr/comm/ModuleOptions.cmake b/winpr/libwinpr/comm/ModuleOptions.cmake new file mode 100644 index 0000000..4095589 --- /dev/null +++ b/winpr/libwinpr/comm/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "comm") +set(MINWIN_LONG_NAME "Serial Communication API") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/comm/comm.c b/winpr/libwinpr/comm/comm.c new file mode 100644 index 0000000..c4e828e --- /dev/null +++ b/winpr/libwinpr/comm/comm.c @@ -0,0 +1,1426 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if defined __linux__ && !defined ANDROID + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "comm_ioctl.h" + +/** + * Communication Resources: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363196/ + */ + +#include "comm.h" + +static wLog* _Log = NULL; + +struct comm_device +{ + LPTSTR name; + LPTSTR path; +}; + +typedef struct comm_device COMM_DEVICE; + +/* FIXME: get a clever data structure, see also io.h functions */ +/* _CommDevices is a NULL-terminated array with a maximun of COMM_DEVICE_MAX COMM_DEVICE */ +#define COMM_DEVICE_MAX 128 +static COMM_DEVICE** _CommDevices = NULL; +static CRITICAL_SECTION _CommDevicesLock; + +static HANDLE_CREATOR _CommHandleCreator; + +static pthread_once_t _CommInitialized = PTHREAD_ONCE_INIT; + +static int CommGetFd(HANDLE handle) +{ + WINPR_COMM* comm = (WINPR_COMM*)handle; + + if (!CommIsHandled(handle)) + return -1; + + return comm->fd; +} + +HANDLE_CREATOR* GetCommHandleCreator(void) +{ + _CommHandleCreator.IsHandled = IsCommDevice; + _CommHandleCreator.CreateFileA = CommCreateFileA; + return &_CommHandleCreator; +} + +static void _CommInit(void) +{ + /* NB: error management to be done outside of this function */ + WINPR_ASSERT(_Log == NULL); + WINPR_ASSERT(_CommDevices == NULL); + _CommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*)); + + if (!_CommDevices) + return; + + if (!InitializeCriticalSectionEx(&_CommDevicesLock, 0, 0)) + { + free(_CommDevices); + _CommDevices = NULL; + return; + } + + _Log = WLog_Get("com.winpr.comm"); + WINPR_ASSERT(_Log != NULL); +} + +/** + * Returns TRUE when the comm module is correctly intialized, FALSE otherwise + * with ERROR_DLL_INIT_FAILED set as the last error. + */ +static BOOL CommInitialized(void) +{ + if (pthread_once(&_CommInitialized, _CommInit) != 0) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + return TRUE; +} + +void CommLog_Print(DWORD level, ...) +{ + if (!CommInitialized()) + return; + + va_list ap; + va_start(ap, level); + WLog_PrintVA(_Log, level, ap); + va_end(ap); +} + +BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hCommDev; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hCommDev; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +/** + * ERRORS: + * ERROR_DLL_INIT_FAILED + * ERROR_INVALID_HANDLE + */ +BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + DWORD bytesReturned = 0; + + if (!CommIsHandleValid(hFile)) + return FALSE; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp, + sizeof(COMMPROP), &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "GetCommProperties failure."); + return FALSE; + } + + return TRUE; +} + +/** + * + * + * ERRORS: + * ERROR_INVALID_HANDLE + * ERROR_INVALID_DATA + * ERROR_IO_DEVICE + * ERROR_OUTOFMEMORY + */ +BOOL GetCommState(HANDLE hFile, LPDCB lpDCB) +{ + DCB* lpLocalDcb = NULL; + struct termios currentState; + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + DWORD bytesReturned = 0; + + if (!CommIsHandleValid(hFile)) + return FALSE; + + if (!lpDCB) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + if (lpDCB->DCBlength < sizeof(DCB)) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + if (tcgetattr(pComm->fd, ¤tState) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength); + + if (lpLocalDcb == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + /* error_handle */ + lpLocalDcb->DCBlength = lpDCB->DCBlength; + SERIAL_BAUD_RATE baudRate; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate, + sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate."); + goto error_handle; + } + + lpLocalDcb->BaudRate = baudRate.BaudRate; + lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0; + + if (!lpLocalDcb->fBinary) + { + CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag."); + } + + lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0; + SERIAL_HANDFLOW handflow; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow, + sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings."); + goto error_handle; + } + + lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0; + lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0; + + if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) + { + lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE; + } + else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL) + { + lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE; + } + else + { + lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE; + } + + lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0; + lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0; + lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0; + lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0; + lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0; + lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0; + + if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) + { + lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE; + } + else if (handflow.FlowReplace & SERIAL_RTS_CONTROL) + { + lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE; + } + else + { + lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE; + } + + // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow + // Control Enabled bit in its Modem Control Register (MCR) + lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0; + /* lpLocalDcb->fDummy2 not used */ + lpLocalDcb->wReserved = 0; /* must be zero */ + lpLocalDcb->XonLim = handflow.XonLimit; + lpLocalDcb->XoffLim = handflow.XoffLimit; + SERIAL_LINE_CONTROL lineControl; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl, + sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings."); + goto error_handle; + } + + lpLocalDcb->ByteSize = lineControl.WordLength; + lpLocalDcb->Parity = lineControl.Parity; + lpLocalDcb->StopBits = lineControl.StopBits; + SERIAL_CHARS serialChars; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, + sizeof(SERIAL_CHARS), &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars."); + goto error_handle; + } + + lpLocalDcb->XonChar = serialChars.XonChar; + lpLocalDcb->XoffChar = serialChars.XoffChar; + lpLocalDcb->ErrorChar = serialChars.ErrorChar; + lpLocalDcb->EofChar = serialChars.EofChar; + lpLocalDcb->EvtChar = serialChars.EventChar; + memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength); + free(lpLocalDcb); + return TRUE; +error_handle: + free(lpLocalDcb); + return FALSE; +} + +/** + * @return TRUE on success, FALSE otherwise. + * + * As of today, SetCommState() can fail half-way with some settings + * applied and some others not. SetCommState() returns on the first + * failure met. FIXME: or is it correct? + * + * ERRORS: + * ERROR_INVALID_HANDLE + * ERROR_IO_DEVICE + */ +BOOL SetCommState(HANDLE hFile, LPDCB lpDCB) +{ + struct termios upcomingTermios = { 0 }; + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + DWORD bytesReturned = 0; + + /* FIXME: validate changes according GetCommProperties? */ + + if (!CommIsHandleValid(hFile)) + return FALSE; + + if (!lpDCB) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + /* NB: did the choice to call ioctls first when available and + then to setup upcomingTermios. Don't mix both stages. */ + /** ioctl calls stage **/ + SERIAL_BAUD_RATE baudRate; + baudRate.BaudRate = lpDCB->BaudRate; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE), + NULL, 0, &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate."); + return FALSE; + } + + SERIAL_CHARS serialChars; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, + sizeof(SERIAL_CHARS), &bytesReturned, + NULL)) /* as of today, required for BreakChar */ + { + CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars."); + return FALSE; + } + + serialChars.XonChar = lpDCB->XonChar; + serialChars.XoffChar = lpDCB->XoffChar; + serialChars.ErrorChar = lpDCB->ErrorChar; + serialChars.EofChar = lpDCB->EofChar; + serialChars.EventChar = lpDCB->EvtChar; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS), + NULL, 0, &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars."); + return FALSE; + } + + SERIAL_LINE_CONTROL lineControl; + lineControl.StopBits = lpDCB->StopBits; + lineControl.Parity = lpDCB->Parity; + lineControl.WordLength = lpDCB->ByteSize; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl, + sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings."); + return FALSE; + } + + SERIAL_HANDFLOW handflow = { 0 }; + + if (lpDCB->fOutxCtsFlow) + { + handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE; + } + + if (lpDCB->fOutxDsrFlow) + { + handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE; + } + + switch (lpDCB->fDtrControl) + { + case SERIAL_DTR_HANDSHAKE: + handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE; + break; + + case SERIAL_DTR_CONTROL: + handflow.ControlHandShake |= DTR_CONTROL_ENABLE; + break; + + case DTR_CONTROL_DISABLE: + /* do nothing since handflow is init-zeroed */ + break; + + default: + CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRIu32 "\n", + lpDCB->fDtrControl); + return FALSE; + } + + if (lpDCB->fDsrSensitivity) + { + handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY; + } + + if (lpDCB->fTXContinueOnXoff) + { + handflow.FlowReplace |= SERIAL_XOFF_CONTINUE; + } + + if (lpDCB->fOutX) + { + handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT; + } + + if (lpDCB->fInX) + { + handflow.FlowReplace |= SERIAL_AUTO_RECEIVE; + } + + if (lpDCB->fErrorChar) + { + handflow.FlowReplace |= SERIAL_ERROR_CHAR; + } + + if (lpDCB->fNull) + { + handflow.FlowReplace |= SERIAL_NULL_STRIPPING; + } + + switch (lpDCB->fRtsControl) + { + case RTS_CONTROL_TOGGLE: + CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature"); + // FIXME: see also GetCommState() + return FALSE; + + case RTS_CONTROL_HANDSHAKE: + handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE; + break; + + case RTS_CONTROL_ENABLE: + handflow.FlowReplace |= SERIAL_RTS_CONTROL; + break; + + case RTS_CONTROL_DISABLE: + /* do nothing since handflow is init-zeroed */ + break; + + default: + CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRIu32 "\n", + lpDCB->fRtsControl); + return FALSE; + } + + if (lpDCB->fAbortOnError) + { + handflow.ControlHandShake |= SERIAL_ERROR_ABORT; + } + + /* lpDCB->fDummy2 not used */ + /* lpLocalDcb->wReserved ignored */ + handflow.XonLimit = lpDCB->XonLim; + handflow.XoffLimit = lpDCB->XoffLim; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW), + NULL, 0, &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings."); + return FALSE; + } + + /** upcomingTermios stage **/ + + if (tcgetattr(pComm->fd, &upcomingTermios) < + 0) /* NB: preserves current settings not directly handled by the Communication Functions */ + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + if (lpDCB->fBinary) + { + upcomingTermios.c_lflag &= ~ICANON; + } + else + { + upcomingTermios.c_lflag |= ICANON; + CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag."); + } + + if (lpDCB->fParity) + { + upcomingTermios.c_iflag |= INPCK; + } + else + { + upcomingTermios.c_iflag &= ~INPCK; + } + + /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx + * + * The SetCommState function reconfigures the communications + * resource, but it does not affect the internal output and + * input buffers of the specified driver. The buffers are not + * flushed, and pending read and write operations are not + * terminated prematurely. + * + * TCSANOW matches the best this definition + */ + + if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +/** + * ERRORS: + * ERROR_INVALID_HANDLE + */ +BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + DWORD bytesReturned = 0; + + if (!CommIsHandleValid(hFile)) + return FALSE; + + /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */ + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts, + sizeof(COMMTIMEOUTS), &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "GetCommTimeouts failure."); + return FALSE; + } + + return TRUE; +} + +/** + * ERRORS: + * ERROR_INVALID_HANDLE + */ +BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + DWORD bytesReturned = 0; + + if (!CommIsHandleValid(hFile)) + return FALSE; + + /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */ + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS), + NULL, 0, &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "SetCommTimeouts failure."); + return FALSE; + } + + return TRUE; +} + +BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize) +{ + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetCommBreak(HANDLE hFile) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL ClearCommBreak(HANDLE hFile) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL PurgeComm(HANDLE hFile, DWORD dwFlags) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + DWORD bytesReturned = 0; + + if (!CommIsHandleValid(hFile)) + return FALSE; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0, + &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "PurgeComm failure."); + return FALSE; + } + + return TRUE; +} + +BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + SERIAL_QUEUE_SIZE queueSize; + DWORD bytesReturned = 0; + + if (!CommIsHandleValid(hFile)) + return FALSE; + + queueSize.InSize = dwInQueue; + queueSize.OutSize = dwOutQueue; + + if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize, + sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "SetCommTimeouts failure."); + return FALSE; + } + + return TRUE; +} + +BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL TransmitCommChar(HANDLE hFile, char cChar) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hFile; + + if (!CommInitialized()) + return FALSE; + + /* TODO: not implemented */ + + if (!pComm) + return FALSE; + + CommLog_Print(WLOG_ERROR, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +/** + * Returns TRUE on success, FALSE otherwise. To get extended error + * information, call GetLastError. + * + * ERRORS: + * ERROR_DLL_INIT_FAILED + * ERROR_OUTOFMEMORY was not possible to get mappings. + * ERROR_INVALID_DATA was not possible to add the device. + */ +BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath) +{ + LPTSTR storedDeviceName = NULL; + LPTSTR storedTargetPath = NULL; + + if (!CommInitialized()) + return FALSE; + + EnterCriticalSection(&_CommDevicesLock); + + if (_CommDevices == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + goto error_handle; + } + + storedDeviceName = _tcsdup(lpDeviceName); + + if (storedDeviceName == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + goto error_handle; + } + + storedTargetPath = _tcsdup(lpTargetPath); + + if (storedTargetPath == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + goto error_handle; + } + + int i = 0; + for (; i < COMM_DEVICE_MAX; i++) + { + if (_CommDevices[i] != NULL) + { + if (_tcscmp(_CommDevices[i]->name, storedDeviceName) == 0) + { + /* take over the emplacement */ + free(_CommDevices[i]->name); + free(_CommDevices[i]->path); + _CommDevices[i]->name = storedDeviceName; + _CommDevices[i]->path = storedTargetPath; + break; + } + } + else + { + /* new emplacement */ + _CommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE)); + + if (_CommDevices[i] == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + goto error_handle; + } + + _CommDevices[i]->name = storedDeviceName; + _CommDevices[i]->path = storedTargetPath; + break; + } + } + + if (i == COMM_DEVICE_MAX) + { + SetLastError(ERROR_OUTOFMEMORY); + goto error_handle; + } + + LeaveCriticalSection(&_CommDevicesLock); + return TRUE; +error_handle: + free(storedDeviceName); + free(storedTargetPath); + LeaveCriticalSection(&_CommDevicesLock); + return FALSE; +} + +/** + * Returns the number of target paths in the buffer pointed to by + * lpTargetPath. + * + * The current implementation returns in any case 0 and 1 target + * path. A NULL lpDeviceName is not supported yet to get all the + * paths. + * + * ERRORS: + * ERROR_SUCCESS + * ERROR_DLL_INIT_FAILED + * ERROR_OUTOFMEMORY was not possible to get mappings. + * ERROR_NOT_SUPPORTED equivalent QueryDosDevice feature not supported. + * ERROR_INVALID_DATA was not possible to retrieve any device information. + * ERROR_INSUFFICIENT_BUFFER too small lpTargetPath + */ +DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax) +{ + LPTSTR storedTargetPath = NULL; + SetLastError(ERROR_SUCCESS); + + if (!CommInitialized()) + return 0; + + if (_CommDevices == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return 0; + } + + if (lpDeviceName == NULL || lpTargetPath == NULL) + { + SetLastError(ERROR_NOT_SUPPORTED); + return 0; + } + + EnterCriticalSection(&_CommDevicesLock); + storedTargetPath = NULL; + + for (int i = 0; i < COMM_DEVICE_MAX; i++) + { + if (_CommDevices[i] != NULL) + { + if (_tcscmp(_CommDevices[i]->name, lpDeviceName) == 0) + { + storedTargetPath = _CommDevices[i]->path; + break; + } + + continue; + } + + break; + } + + LeaveCriticalSection(&_CommDevicesLock); + + if (storedTargetPath == NULL) + { + SetLastError(ERROR_INVALID_DATA); + return 0; + } + + if (_tcslen(storedTargetPath) + 2 > ucchMax) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + _tcscpy(lpTargetPath, storedTargetPath); + lpTargetPath[_tcslen(storedTargetPath) + 1] = '\0'; /* 2nd final '\0' */ + return _tcslen(lpTargetPath) + 2; +} + +/** + * Checks whether lpDeviceName is a valid and registered Communication device. + */ +BOOL IsCommDevice(LPCTSTR lpDeviceName) +{ + TCHAR lpTargetPath[MAX_PATH]; + + if (!CommInitialized()) + return FALSE; + + if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0) + { + return TRUE; + } + + return FALSE; +} + +/** + * Sets + */ +void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_COMM* pComm = NULL; + + if (!CommInitialized()) + return; + + if (!winpr_Handle_GetInfo(hComm, &Type, &Object)) + { + CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure"); + return; + } + + pComm = (WINPR_COMM*)Object; + pComm->serverSerialDriverId = driverId; +} + +static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle, + CommGetFd, NULL, /* CleanupHandle */ + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL }; + +/** + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363198%28v=vs.85%29.aspx + * + * @param lpDeviceName e.g. COM1, ... + * + * @param dwDesiredAccess expects GENERIC_READ | GENERIC_WRITE, a + * warning message is printed otherwise. TODO: better support. + * + * @param dwShareMode must be zero, INVALID_HANDLE_VALUE is returned + * otherwise and GetLastError() should return ERROR_SHARING_VIOLATION. + * + * @param lpSecurityAttributes NULL expected, a warning message is printed + * otherwise. TODO: better support. + * + * @param dwCreationDisposition must be OPEN_EXISTING. If the + * communication device doesn't exist INVALID_HANDLE_VALUE is returned + * and GetLastError() returns ERROR_FILE_NOT_FOUND. + * + * @param dwFlagsAndAttributes zero expected, a warning message is + * printed otherwise. + * + * @param hTemplateFile must be NULL. + * + * @return INVALID_HANDLE_VALUE on error. + */ +HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + CHAR devicePath[MAX_PATH]; + struct stat deviceStat; + WINPR_COMM* pComm = NULL; + struct termios upcomingTermios; + + if (!CommInitialized()) + return INVALID_HANDLE_VALUE; + + if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE)) + { + CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "", + dwDesiredAccess); + } + + if (dwShareMode != 0) + { + SetLastError(ERROR_SHARING_VIOLATION); + return INVALID_HANDLE_VALUE; + } + + /* TODO: Prevents other processes from opening a file or + * device if they request delete, read, or write access. */ + + if (lpSecurityAttributes != NULL) + { + CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "", + lpSecurityAttributes->nLength); + } + + if (dwCreationDisposition != OPEN_EXISTING) + { + SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */ + return INVALID_HANDLE_VALUE; + } + + if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0) + { + /* SetLastError(GetLastError()); */ + return INVALID_HANDLE_VALUE; + } + + if (stat(devicePath, &deviceStat) < 0) + { + CommLog_Print(WLOG_WARN, "device not found %s", devicePath); + SetLastError(ERROR_FILE_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + + if (!S_ISCHR(deviceStat.st_mode)) + { + CommLog_Print(WLOG_WARN, "bad device %s", devicePath); + SetLastError(ERROR_BAD_DEVICE); + return INVALID_HANDLE_VALUE; + } + + if (dwFlagsAndAttributes != 0) + { + CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "", + dwFlagsAndAttributes); + } + + if (hTemplateFile != NULL) + { + SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */ + return INVALID_HANDLE_VALUE; + } + + pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM)); + + if (pComm == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + return INVALID_HANDLE_VALUE; + } + + WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ); + pComm->common.ops = &ops; + /* error_handle */ + pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (pComm->fd < 0) + { + CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath); + SetLastError(ERROR_BAD_DEVICE); + goto error_handle; + } + + pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK); + + if (pComm->fd_read < 0) + { + CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath); + SetLastError(ERROR_BAD_DEVICE); + goto error_handle; + } + + pComm->fd_read_event = eventfd( + 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */ + + if (pComm->fd_read_event < 0) + { + CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath); + SetLastError(ERROR_BAD_DEVICE); + goto error_handle; + } + + InitializeCriticalSection(&pComm->ReadLock); + pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK); + + if (pComm->fd_write < 0) + { + CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath); + SetLastError(ERROR_BAD_DEVICE); + goto error_handle; + } + + pComm->fd_write_event = eventfd( + 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */ + + if (pComm->fd_write_event < 0) + { + CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath); + SetLastError(ERROR_BAD_DEVICE); + goto error_handle; + } + + InitializeCriticalSection(&pComm->WriteLock); + /* can also be setup later on with _comm_setServerSerialDriver() */ + pComm->serverSerialDriverId = SerialDriverUnknown; + InitializeCriticalSection(&pComm->EventsLock); + + if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + CommLog_Print(WLOG_WARN, "could not read counters."); + /* could not initialize counters but keep on. + * + * Not all drivers, especially for USB to serial + * adapters (e.g. those based on pl2303), does support + * this call. + */ + ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct)); + } + + /* The binary/raw mode is required for the redirection but + * only flags that are not handle somewhere-else, except + * ICANON, are forced here. */ + ZeroMemory(&upcomingTermios, sizeof(struct termios)); + + if (tcgetattr(pComm->fd, &upcomingTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + goto error_handle; + } + + upcomingTermios.c_iflag &= + ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/); + upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */ + upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */ + /* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */ + /* upcomingTermios.c_cflag |= CS8; */ + /* About missing flags recommended by termios(3): + * + * IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW + * CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL + */ + /* a few more settings required for the redirection */ + upcomingTermios.c_cflag |= CLOCAL | CREAD; + + if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + goto error_handle; + } + + return (HANDLE)pComm; +error_handle: + CloseHandle(pComm); + return INVALID_HANDLE_VALUE; +} + +BOOL CommIsHandled(HANDLE handle) +{ + if (!CommInitialized()) + return FALSE; + + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE); +} + +BOOL CommIsHandleValid(HANDLE handle) +{ + WINPR_COMM* pComm = (WINPR_COMM*)handle; + if (!CommIsHandled(handle)) + return FALSE; + if (pComm->fd <= 0) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + return TRUE; +} + +BOOL CommCloseHandle(HANDLE handle) +{ + WINPR_COMM* pComm = (WINPR_COMM*)handle; + + if (!CommIsHandled(handle)) + return FALSE; + + if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) + { + ULONG WaitMask = 0; + DWORD BytesReturned = 0; + + /* ensures to gracefully stop the WAIT_ON_MASK's loop */ + if (!CommDeviceIoControl(handle, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof(ULONG), NULL, + 0, &BytesReturned, NULL)) + { + CommLog_Print(WLOG_WARN, "failure to WAIT_ON_MASK's loop!"); + } + } + + DeleteCriticalSection(&pComm->ReadLock); + DeleteCriticalSection(&pComm->WriteLock); + DeleteCriticalSection(&pComm->EventsLock); + + if (pComm->fd > 0) + close(pComm->fd); + + if (pComm->fd_write > 0) + close(pComm->fd_write); + + if (pComm->fd_write_event > 0) + close(pComm->fd_write_event); + + if (pComm->fd_read > 0) + close(pComm->fd_read); + + if (pComm->fd_read_event > 0) + close(pComm->fd_read_event); + + free(pComm); + return TRUE; +} + +#ifndef WITH_EVENTFD_READ_WRITE +int eventfd_read(int fd, eventfd_t* value) +{ + return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1; +} + +int eventfd_write(int fd, eventfd_t value) +{ + return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1; +} +#endif + +#endif /* __linux__ */ diff --git a/winpr/libwinpr/comm/comm.h b/winpr/libwinpr/comm/comm.h new file mode 100644 index 0000000..c7884cb --- /dev/null +++ b/winpr/libwinpr/comm/comm.h @@ -0,0 +1,111 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_COMM_PRIVATE_H +#define WINPR_COMM_PRIVATE_H + +#if defined __linux__ && !defined ANDROID + +#include +#include + +#include + +#include "../handle/handle.h" +#include + +struct winpr_comm +{ + WINPR_HANDLE common; + + int fd; + + int fd_read; + int fd_read_event; /* as of today, only used by _purge() */ + CRITICAL_SECTION ReadLock; + + int fd_write; + int fd_write_event; /* as of today, only used by _purge() */ + CRITICAL_SECTION WriteLock; + + /* permissive mode on errors. If TRUE (default is FALSE) + * CommDeviceIoControl always return TRUE. + * + * Not all features are supported yet and an error is then returned when + * an application turns them on (e.g: i/o buffers > 4096). It appeared + * though that devices and applications can be still functional on such + * errors. + * + * see also: comm_ioctl.c + * + * FIXME: getting rid of this flag once all features supported. + */ + BOOL permissive; + + SERIAL_DRIVER_ID serverSerialDriverId; + + COMMTIMEOUTS timeouts; + + CRITICAL_SECTION + EventsLock; /* protects counters, WaitEventMask and PendingEvents */ + struct serial_icounter_struct counters; + ULONG WaitEventMask; + ULONG PendingEvents; + + char eventChar; + /* NB: CloseHandle() has to free resources */ +}; + +typedef struct winpr_comm WINPR_COMM; + +#define SERIAL_EV_RXCHAR 0x0001 +#define SERIAL_EV_RXFLAG 0x0002 +#define SERIAL_EV_TXEMPTY 0x0004 +#define SERIAL_EV_CTS 0x0008 +#define SERIAL_EV_DSR 0x0010 +#define SERIAL_EV_RLSD 0x0020 +#define SERIAL_EV_BREAK 0x0040 +#define SERIAL_EV_ERR 0x0080 +#define SERIAL_EV_RING 0x0100 +#define SERIAL_EV_PERR 0x0200 +#define SERIAL_EV_RX80FULL 0x0400 +#define SERIAL_EV_EVENT1 0x0800 +#define SERIAL_EV_EVENT2 0x1000 +#define SERIAL_EV_WINPR_WAITING 0x4000 /* bit today unused by other SERIAL_EV_* */ +#define SERIAL_EV_WINPR_STOP 0x8000 /* bit today unused by other SERIAL_EV_* */ + +#define WINPR_PURGE_TXABORT 0x00000001 /* abort pending transmission */ +#define WINPR_PURGE_RXABORT 0x00000002 /* abort pending reception */ + +void CommLog_Print(DWORD wlog_level, ...); + +BOOL CommIsHandled(HANDLE handle); +BOOL CommIsHandleValid(HANDLE handle); +BOOL CommCloseHandle(HANDLE handle); +HANDLE_CREATOR* GetCommHandleCreator(void); + +#ifndef WITH_EVENTFD_READ_WRITE +int eventfd_read(int fd, eventfd_t* value); +int eventfd_write(int fd, eventfd_t value); +#endif + +#endif /* __linux__ */ + +#endif /* WINPR_COMM_PRIVATE_H */ diff --git a/winpr/libwinpr/comm/comm_io.c b/winpr/libwinpr/comm/comm_io.c new file mode 100644 index 0000000..9904eab --- /dev/null +++ b/winpr/libwinpr/comm/comm_io.c @@ -0,0 +1,549 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if defined __linux__ && !defined ANDROID + +#include +#include +#include +#include + +#include +#include +#include + +#include "comm.h" + +BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hDevice; + + if (!CommIsHandled(hDevice)) + return FALSE; + + pComm->permissive = permissive; + return TRUE; +} + +/* Computes VTIME in deciseconds from Ti in milliseconds */ +static UCHAR _vtime(ULONG Ti) +{ + /* FIXME: look for an equivalent math function otherwise let + * do the compiler do the optimization */ + if (Ti == 0) + return 0; + else if (Ti < 100) + return 1; + else if (Ti > 25500) + return 255; /* 0xFF */ + else + return Ti / 100; +} + +/** + * ERRORS: + * ERROR_INVALID_HANDLE + * ERROR_NOT_SUPPORTED + * ERROR_INVALID_PARAMETER + * ERROR_TIMEOUT + * ERROR_IO_DEVICE + * ERROR_BAD_DEVICE + */ +BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hDevice; + int biggestFd = -1; + fd_set read_set; + int nbFds = 0; + COMMTIMEOUTS* pTimeouts = NULL; + UCHAR vmin = 0; + UCHAR vtime = 0; + ULONGLONG Tmax = 0; + struct timeval tmaxTimeout; + struct timeval* pTmaxTimeout = NULL; + struct termios currentTermios; + EnterCriticalSection(&pComm->ReadLock); /* KISSer by the function's beginning */ + + if (!CommIsHandled(hDevice)) + goto return_false; + + if (lpOverlapped != NULL) + { + SetLastError(ERROR_NOT_SUPPORTED); + goto return_false; + } + + if (lpNumberOfBytesRead == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */ + goto return_false; + } + + *lpNumberOfBytesRead = 0; /* will be ajusted if required ... */ + + if (nNumberOfBytesToRead <= 0) /* N */ + { + goto return_true; /* FIXME: or FALSE? */ + } + + if (tcgetattr(pComm->fd, ¤tTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + goto return_false; + } + + if (currentTermios.c_lflag & ICANON) + { + CommLog_Print(WLOG_WARN, "Canonical mode not supported"); /* the timeout could not be set */ + SetLastError(ERROR_NOT_SUPPORTED); + goto return_false; + } + + /* http://msdn.microsoft.com/en-us/library/hh439614%28v=vs.85%29.aspx + * http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx + * + * ReadIntervalTimeout | ReadTotalTimeoutMultiplier | ReadTotalTimeoutConstant | VMIN | VTIME | + * TMAX | 0 | 0 | 0 | N | 0 | + * INDEF | Blocks for N bytes available. 0< Ti fd_read_event, O_NONBLOCK) doesn't conflict with + * above use cases */ + pTimeouts = &(pComm->timeouts); + + if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && + (pTimeouts->ReadTotalTimeoutConstant == MAXULONG)) + { + CommLog_Print( + WLOG_WARN, + "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG"); + SetLastError(ERROR_INVALID_PARAMETER); + goto return_false; + } + + /* VMIN */ + + if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && + (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0)) + { + vmin = 0; + } + else + { + /* N */ + /* vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255;*/ /* 0xFF */ + /* NB: we might wait endlessly with vmin=N, prefer to + * force vmin=1 and return with bytes + * available. FIXME: is a feature disarded here? */ + vmin = 1; + } + + /* VTIME */ + + if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG)) + { + /* Ti */ + vtime = _vtime(pTimeouts->ReadIntervalTimeout); + } + + /* TMAX */ + pTmaxTimeout = &tmaxTimeout; + + if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && + (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG)) + { + /* Tc */ + Tmax = pTimeouts->ReadTotalTimeoutConstant; + } + else + { + /* Tmax */ + Tmax = nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier + + pTimeouts->ReadTotalTimeoutConstant; + + /* INDEFinitely */ + if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) && + (pTimeouts->ReadTotalTimeoutMultiplier == 0)) + pTmaxTimeout = NULL; + } + + if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime)) + { + currentTermios.c_cc[VMIN] = vmin; + currentTermios.c_cc[VTIME] = vtime; + + if (tcsetattr(pComm->fd, TCSANOW, ¤tTermios) < 0) + { + CommLog_Print(WLOG_WARN, + "CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8 + ", VTIME=%" PRIu8 "", + vmin, vtime); + SetLastError(ERROR_IO_DEVICE); + goto return_false; + } + } + + /* wait indefinitely if pTmaxTimeout is NULL */ + + if (pTmaxTimeout != NULL) + { + ZeroMemory(pTmaxTimeout, sizeof(struct timeval)); + + if (Tmax > 0) /* return immdiately if Tmax == 0 */ + { + pTmaxTimeout->tv_sec = Tmax / 1000; /* s */ + pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */ + } + } + + /* FIXME: had expected eventfd_write() to return EAGAIN when + * there is no eventfd_read() but this not the case. */ + /* discard a possible and no more relevant event */ + eventfd_read(pComm->fd_read_event, NULL); + biggestFd = pComm->fd_read; + + if (pComm->fd_read_event > biggestFd) + biggestFd = pComm->fd_read_event; + + FD_ZERO(&read_set); + WINPR_ASSERT(pComm->fd_read_event < FD_SETSIZE); + WINPR_ASSERT(pComm->fd_read < FD_SETSIZE); + FD_SET(pComm->fd_read_event, &read_set); + FD_SET(pComm->fd_read, &read_set); + nbFds = select(biggestFd + 1, &read_set, NULL, NULL, pTmaxTimeout); + + if (nbFds < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + goto return_false; + } + + if (nbFds == 0) + { + /* timeout */ + SetLastError(ERROR_TIMEOUT); + goto return_false; + } + + /* read_set */ + + if (FD_ISSET(pComm->fd_read_event, &read_set)) + { + eventfd_t event = 0; + + if (eventfd_read(pComm->fd_read_event, &event) < 0) + { + if (errno == EAGAIN) + { + WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */ + /* keep on */ + } + else + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, + "unexpected error on reading fd_read_event, errno=[%d] %s\n", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + /* FIXME: goto return_false ? */ + } + + WINPR_ASSERT(errno == EAGAIN); + } + + if (event == WINPR_PURGE_RXABORT) + { + SetLastError(ERROR_CANCELLED); + goto return_false; + } + + WINPR_ASSERT(event == WINPR_PURGE_RXABORT); /* no other expected event so far */ + } + + if (FD_ISSET(pComm->fd_read, &read_set)) + { + ssize_t nbRead = 0; + nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead); + + if (nbRead < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, + "CommReadFile failed, ReadIntervalTimeout=%" PRIu32 + ", ReadTotalTimeoutMultiplier=%" PRIu32 + ", ReadTotalTimeoutConstant=%" PRIu32 " VMIN=%u, VTIME=%u", + pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier, + pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN], + currentTermios.c_cc[VTIME]); + CommLog_Print( + WLOG_WARN, "CommReadFile failed, nNumberOfBytesToRead=%" PRIu32 ", errno=[%d] %s", + nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + + if (errno == EAGAIN) + { + /* keep on */ + goto return_true; /* expect a read-loop to be implemented on the server side */ + } + else if (errno == EBADF) + { + SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */ + goto return_false; + } + else + { + WINPR_ASSERT(FALSE); + SetLastError(ERROR_IO_DEVICE); + goto return_false; + } + } + + if (nbRead == 0) + { + /* termios timeout */ + SetLastError(ERROR_TIMEOUT); + goto return_false; + } + + *lpNumberOfBytesRead = nbRead; + + EnterCriticalSection(&pComm->EventsLock); + if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) + { + if (pComm->eventChar != '\0' && memchr(lpBuffer, pComm->eventChar, nbRead)) + pComm->PendingEvents |= SERIAL_EV_RXCHAR; + } + LeaveCriticalSection(&pComm->EventsLock); + goto return_true; + } + + WINPR_ASSERT(FALSE); + *lpNumberOfBytesRead = 0; +return_false: + LeaveCriticalSection(&pComm->ReadLock); + return FALSE; +return_true: + LeaveCriticalSection(&pComm->ReadLock); + return TRUE; +} + +/** + * ERRORS: + * ERROR_INVALID_HANDLE + * ERROR_NOT_SUPPORTED + * ERROR_INVALID_PARAMETER + * ERROR_BAD_DEVICE + */ +BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hDevice; + struct timeval tmaxTimeout; + struct timeval* pTmaxTimeout = NULL; + EnterCriticalSection(&pComm->WriteLock); /* KISSer by the function's beginning */ + + if (!CommIsHandled(hDevice)) + goto return_false; + + if (lpOverlapped != NULL) + { + SetLastError(ERROR_NOT_SUPPORTED); + goto return_false; + } + + if (lpNumberOfBytesWritten == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */ + goto return_false; + } + + *lpNumberOfBytesWritten = 0; /* will be ajusted if required ... */ + + if (nNumberOfBytesToWrite <= 0) + { + goto return_true; /* FIXME: or FALSE? */ + } + + /* FIXME: had expected eventfd_write() to return EAGAIN when + * there is no eventfd_read() but this not the case. */ + /* discard a possible and no more relevant event */ + eventfd_read(pComm->fd_write_event, NULL); + /* ms */ + ULONGLONG Tmax = nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier + + pComm->timeouts.WriteTotalTimeoutConstant; + /* NB: select() may update the timeout argument to indicate + * how much time was left. Keep the timeout variable out of + * the while() */ + pTmaxTimeout = &tmaxTimeout; + ZeroMemory(pTmaxTimeout, sizeof(struct timeval)); + + if (Tmax > 0) + { + pTmaxTimeout->tv_sec = Tmax / 1000; /* s */ + pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */ + } + else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) && + (pComm->timeouts.WriteTotalTimeoutConstant == 0)) + { + pTmaxTimeout = NULL; + } + + /* else return immdiately */ + + while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite) + { + int biggestFd = -1; + fd_set event_set; + fd_set write_set; + int nbFds = 0; + biggestFd = pComm->fd_write; + + if (pComm->fd_write_event > biggestFd) + biggestFd = pComm->fd_write_event; + + FD_ZERO(&event_set); + FD_ZERO(&write_set); + WINPR_ASSERT(pComm->fd_write_event < FD_SETSIZE); + WINPR_ASSERT(pComm->fd_write < FD_SETSIZE); + FD_SET(pComm->fd_write_event, &event_set); + FD_SET(pComm->fd_write, &write_set); + nbFds = select(biggestFd + 1, &event_set, &write_set, NULL, pTmaxTimeout); + + if (nbFds < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + goto return_false; + } + + if (nbFds == 0) + { + /* timeout */ + SetLastError(ERROR_TIMEOUT); + goto return_false; + } + + /* event_set */ + + if (FD_ISSET(pComm->fd_write_event, &event_set)) + { + eventfd_t event = 0; + + if (eventfd_read(pComm->fd_write_event, &event) < 0) + { + if (errno == EAGAIN) + { + WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */ + /* keep on */ + } + else + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, + "unexpected error on reading fd_write_event, errno=[%d] %s\n", + errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + /* FIXME: goto return_false ? */ + } + + WINPR_ASSERT(errno == EAGAIN); + } + + if (event == WINPR_PURGE_TXABORT) + { + SetLastError(ERROR_CANCELLED); + goto return_false; + } + + WINPR_ASSERT(event == WINPR_PURGE_TXABORT); /* no other expected event so far */ + } + + /* write_set */ + + if (FD_ISSET(pComm->fd_write, &write_set)) + { + ssize_t nbWritten = 0; + nbWritten = write(pComm->fd_write, ((const BYTE*)lpBuffer) + (*lpNumberOfBytesWritten), + nNumberOfBytesToWrite - (*lpNumberOfBytesWritten)); + + if (nbWritten < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, + "CommWriteFile failed after %" PRIu32 + " bytes written, errno=[%d] %s\n", + *lpNumberOfBytesWritten, errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + + if (errno == EAGAIN) + { + /* keep on */ + continue; + } + else if (errno == EBADF) + { + SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */ + goto return_false; + } + else + { + WINPR_ASSERT(FALSE); + SetLastError(ERROR_IO_DEVICE); + goto return_false; + } + } + + *lpNumberOfBytesWritten += nbWritten; + } + } /* while */ + + /* FIXME: this call to tcdrain() doesn't look correct and + * might hide a bug but was required while testing a serial + * printer. Its driver was expecting the modem line status + * SERIAL_MSR_DSR true after the sending which was never + * happenning otherwise. A purge was also done before each + * Write operation. The serial port was opened with: + * DesiredAccess=0x0012019F. The printer worked fine with + * mstsc. */ + tcdrain(pComm->fd_write); + +return_true: + LeaveCriticalSection(&pComm->WriteLock); + return TRUE; + +return_false: + LeaveCriticalSection(&pComm->WriteLock); + return FALSE; +} + +#endif /* __linux__ */ diff --git a/winpr/libwinpr/comm/comm_ioctl.c b/winpr/libwinpr/comm/comm_ioctl.c new file mode 100644 index 0000000..4f208ef --- /dev/null +++ b/winpr/libwinpr/comm/comm_ioctl.c @@ -0,0 +1,731 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if defined __linux__ && !defined ANDROID + +#include +#include + +#include + +#include "comm.h" +#include "comm_ioctl.h" +#include "comm_serial_sys.h" +#include "comm_sercx_sys.h" +#include "comm_sercx2_sys.h" + +/* NB: MS-RDPESP's recommendation: + * + * <2> Section 3.2.5.1.6: Windows Implementations use IOCTL constants + * for IoControlCode values. The content and values of the IOCTLs are + * opaque to the protocol. On the server side, the data contained in + * an IOCTL is simply packaged and sent to the client side. For + * maximum compatibility between the different versions of the Windows + * operating system, the client implementation only singles out + * critical IOCTLs and invokes the applicable Win32 port API. The + * other IOCTLS are passed directly to the client-side driver, and the + * processing of this value depends on the drivers installed on the + * client side. The values and parameters for these IOCTLS can be + * found in [MSFT-W2KDDK] Volume 2, Part 2—Serial and Parallel + * Drivers, and in [MSDN-PORTS]. + */ + +const char* _comm_serial_ioctl_name(ULONG number) +{ + for (int i = 0; _SERIAL_IOCTL_NAMES[i].number != 0; i++) + { + if (_SERIAL_IOCTL_NAMES[i].number == number) + { + return _SERIAL_IOCTL_NAMES[i].name; + } + } + + return "(unknown ioctl name)"; +} + +static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, + LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hDevice; + SERIAL_DRIVER* pServerSerialDriver = NULL; + + if (!CommIsHandleValid(hDevice)) + return FALSE; + + if (lpOverlapped) + { + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + if (lpBytesReturned == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */ + return FALSE; + } + + /* clear any previous last error */ + SetLastError(ERROR_SUCCESS); + + *lpBytesReturned = 0; /* will be ajusted if required ... */ + + CommLog_Print(WLOG_DEBUG, "CommDeviceIoControl: IoControlCode: 0x%0.8x", dwIoControlCode); + + /* remoteSerialDriver to be use ... + * + * FIXME: might prefer to use an automatic rather than static structure + */ + switch (pComm->serverSerialDriverId) + { + case SerialDriverSerialSys: + pServerSerialDriver = SerialSys_s(); + break; + + case SerialDriverSerCxSys: + pServerSerialDriver = SerCxSys_s(); + break; + + case SerialDriverSerCx2Sys: + pServerSerialDriver = SerCx2Sys_s(); + break; + + case SerialDriverUnknown: + default: + CommLog_Print(WLOG_DEBUG, "Unknown remote serial driver (%d), using SerCx2.sys", + pComm->serverSerialDriverId); + pServerSerialDriver = SerCx2Sys_s(); + break; + } + + WINPR_ASSERT(pServerSerialDriver != NULL); + + switch (dwIoControlCode) + { + case IOCTL_USBPRINT_GET_1284_ID: + { + /* FIXME: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */ + *lpBytesReturned = nOutBufferSize; /* an empty OutputBuffer will be returned */ + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; + } + case IOCTL_SERIAL_SET_BAUD_RATE: + { + if (pServerSerialDriver->set_baud_rate) + { + SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_BAUD_RATE)); + if (nInBufferSize < sizeof(SERIAL_BAUD_RATE)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_baud_rate(pComm, pBaudRate); + } + break; + } + case IOCTL_SERIAL_GET_BAUD_RATE: + { + if (pServerSerialDriver->get_baud_rate) + { + SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_BAUD_RATE)); + if (nOutBufferSize < sizeof(SERIAL_BAUD_RATE)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_baud_rate(pComm, pBaudRate)) + return FALSE; + + *lpBytesReturned = sizeof(SERIAL_BAUD_RATE); + return TRUE; + } + break; + } + case IOCTL_SERIAL_GET_PROPERTIES: + { + if (pServerSerialDriver->get_properties) + { + COMMPROP* pProperties = (COMMPROP*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(COMMPROP)); + if (nOutBufferSize < sizeof(COMMPROP)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_properties(pComm, pProperties)) + return FALSE; + + *lpBytesReturned = sizeof(COMMPROP); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_CHARS: + { + if (pServerSerialDriver->set_serial_chars) + { + SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_CHARS)); + if (nInBufferSize < sizeof(SERIAL_CHARS)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_serial_chars(pComm, pSerialChars); + } + break; + } + case IOCTL_SERIAL_GET_CHARS: + { + if (pServerSerialDriver->get_serial_chars) + { + SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_CHARS)); + if (nOutBufferSize < sizeof(SERIAL_CHARS)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_serial_chars(pComm, pSerialChars)) + return FALSE; + + *lpBytesReturned = sizeof(SERIAL_CHARS); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_LINE_CONTROL: + { + if (pServerSerialDriver->set_line_control) + { + SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL)); + if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_line_control(pComm, pLineControl); + } + break; + } + case IOCTL_SERIAL_GET_LINE_CONTROL: + { + if (pServerSerialDriver->get_line_control) + { + SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_LINE_CONTROL)); + if (nOutBufferSize < sizeof(SERIAL_LINE_CONTROL)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_line_control(pComm, pLineControl)) + return FALSE; + + *lpBytesReturned = sizeof(SERIAL_LINE_CONTROL); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_HANDFLOW: + { + if (pServerSerialDriver->set_handflow) + { + SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_HANDFLOW)); + if (nInBufferSize < sizeof(SERIAL_HANDFLOW)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_handflow(pComm, pHandflow); + } + break; + } + case IOCTL_SERIAL_GET_HANDFLOW: + { + if (pServerSerialDriver->get_handflow) + { + SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_HANDFLOW)); + if (nOutBufferSize < sizeof(SERIAL_HANDFLOW)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_handflow(pComm, pHandflow)) + return FALSE; + + *lpBytesReturned = sizeof(SERIAL_HANDFLOW); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_TIMEOUTS: + { + if (pServerSerialDriver->set_timeouts) + { + SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_TIMEOUTS)); + if (nInBufferSize < sizeof(SERIAL_TIMEOUTS)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_timeouts(pComm, pHandflow); + } + break; + } + case IOCTL_SERIAL_GET_TIMEOUTS: + { + if (pServerSerialDriver->get_timeouts) + { + SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_TIMEOUTS)); + if (nOutBufferSize < sizeof(SERIAL_TIMEOUTS)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_timeouts(pComm, pHandflow)) + return FALSE; + + *lpBytesReturned = sizeof(SERIAL_TIMEOUTS); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_DTR: + { + if (pServerSerialDriver->set_dtr) + { + return pServerSerialDriver->set_dtr(pComm); + } + break; + } + case IOCTL_SERIAL_CLR_DTR: + { + if (pServerSerialDriver->clear_dtr) + { + return pServerSerialDriver->clear_dtr(pComm); + } + break; + } + case IOCTL_SERIAL_SET_RTS: + { + if (pServerSerialDriver->set_rts) + { + return pServerSerialDriver->set_rts(pComm); + } + break; + } + case IOCTL_SERIAL_CLR_RTS: + { + if (pServerSerialDriver->clear_rts) + { + return pServerSerialDriver->clear_rts(pComm); + } + break; + } + case IOCTL_SERIAL_GET_MODEMSTATUS: + { + if (pServerSerialDriver->get_modemstatus) + { + ULONG* pRegister = (ULONG*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_modemstatus(pComm, pRegister)) + return FALSE; + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_WAIT_MASK: + { + if (pServerSerialDriver->set_wait_mask) + { + ULONG* pWaitMask = (ULONG*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(ULONG)); + if (nInBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_wait_mask(pComm, pWaitMask); + } + break; + } + case IOCTL_SERIAL_GET_WAIT_MASK: + { + if (pServerSerialDriver->get_wait_mask) + { + ULONG* pWaitMask = (ULONG*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_wait_mask(pComm, pWaitMask)) + return FALSE; + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } + case IOCTL_SERIAL_WAIT_ON_MASK: + { + if (pServerSerialDriver->wait_on_mask) + { + ULONG* pOutputMask = (ULONG*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->wait_on_mask(pComm, pOutputMask)) + { + *lpBytesReturned = sizeof(ULONG); + return FALSE; + } + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_QUEUE_SIZE: + { + if (pServerSerialDriver->set_queue_size) + { + SERIAL_QUEUE_SIZE* pQueueSize = (SERIAL_QUEUE_SIZE*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_QUEUE_SIZE)); + if (nInBufferSize < sizeof(SERIAL_QUEUE_SIZE)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->set_queue_size(pComm, pQueueSize); + } + break; + } + case IOCTL_SERIAL_PURGE: + { + if (pServerSerialDriver->purge) + { + ULONG* pPurgeMask = (ULONG*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(ULONG)); + if (nInBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->purge(pComm, pPurgeMask); + } + break; + } + case IOCTL_SERIAL_GET_COMMSTATUS: + { + if (pServerSerialDriver->get_commstatus) + { + SERIAL_STATUS* pCommstatus = (SERIAL_STATUS*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_STATUS)); + if (nOutBufferSize < sizeof(SERIAL_STATUS)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_commstatus(pComm, pCommstatus)) + return FALSE; + + *lpBytesReturned = sizeof(SERIAL_STATUS); + return TRUE; + } + break; + } + case IOCTL_SERIAL_SET_BREAK_ON: + { + if (pServerSerialDriver->set_break_on) + { + return pServerSerialDriver->set_break_on(pComm); + } + break; + } + case IOCTL_SERIAL_SET_BREAK_OFF: + { + if (pServerSerialDriver->set_break_off) + { + return pServerSerialDriver->set_break_off(pComm); + } + break; + } + case IOCTL_SERIAL_SET_XOFF: + { + if (pServerSerialDriver->set_xoff) + { + return pServerSerialDriver->set_xoff(pComm); + } + break; + } + case IOCTL_SERIAL_SET_XON: + { + if (pServerSerialDriver->set_xon) + { + return pServerSerialDriver->set_xon(pComm); + } + break; + } + case IOCTL_SERIAL_GET_DTRRTS: + { + if (pServerSerialDriver->get_dtrrts) + { + ULONG* pMask = (ULONG*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->get_dtrrts(pComm, pMask)) + return FALSE; + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } + case IOCTL_SERIAL_CONFIG_SIZE: + { + if (pServerSerialDriver->config_size) + { + ULONG* pSize = (ULONG*)lpOutBuffer; + + WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pServerSerialDriver->config_size(pComm, pSize)) + return FALSE; + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } + case IOCTL_SERIAL_IMMEDIATE_CHAR: + { + if (pServerSerialDriver->immediate_char) + { + UCHAR* pChar = (UCHAR*)lpInBuffer; + + WINPR_ASSERT(nInBufferSize >= sizeof(UCHAR)); + if (nInBufferSize < sizeof(UCHAR)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pServerSerialDriver->immediate_char(pComm, pChar); + } + break; + } + case IOCTL_SERIAL_RESET_DEVICE: + { + if (pServerSerialDriver->reset_device) + { + return pServerSerialDriver->reset_device(pComm); + } + break; + } + } + + CommLog_Print( + WLOG_WARN, _T("unsupported IoControlCode=[0x%08" PRIX32 "] %s (remote serial driver: %s)"), + dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), pServerSerialDriver->name); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); /* => STATUS_NOT_IMPLEMENTED */ + return FALSE; +} + +/** + * FIXME: to be used through winpr-io's DeviceIoControl + * + * Any previous error as returned by GetLastError is cleared. + * + * ERRORS: + * ERROR_INVALID_HANDLE + * ERROR_INVALID_PARAMETER + * ERROR_NOT_SUPPORTED lpOverlapped is not supported + * ERROR_INSUFFICIENT_BUFFER + * ERROR_CALL_NOT_IMPLEMENTED unimplemented ioctl + */ +BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, + DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, + LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) +{ + WINPR_COMM* pComm = (WINPR_COMM*)hDevice; + BOOL result = 0; + + if (hDevice == INVALID_HANDLE_VALUE) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!CommIsHandled(hDevice)) + return FALSE; + + if (!pComm->fd) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + result = s_CommDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, + nOutBufferSize, lpBytesReturned, lpOverlapped); + + if (lpBytesReturned && *lpBytesReturned != nOutBufferSize) + { + /* This might be a hint for a bug, especially when result==TRUE */ + CommLog_Print(WLOG_WARN, + "lpBytesReturned=%" PRIu32 " and nOutBufferSize=%" PRIu32 " are different!", + *lpBytesReturned, nOutBufferSize); + } + + if (pComm->permissive) + { + if (!result) + { + CommLog_Print( + WLOG_WARN, + "[permissive]: whereas it failed, made to succeed IoControlCode=[0x%08" PRIX32 + "] %s, last-error: 0x%08" PRIX32 "", + dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), GetLastError()); + } + + return TRUE; /* always! */ + } + + return result; +} + +int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p) +{ + int result = 0; + struct termios currentState = { 0 }; + + if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0) + { + CommLog_Print(WLOG_WARN, "tcsetattr failure, errno: %d", errno); + return result; + } + + /* NB: tcsetattr() can succeed even if not all changes have been applied. */ + if ((result = tcgetattr(fd, ¤tState)) < 0) + { + CommLog_Print(WLOG_WARN, "tcgetattr failure, errno: %d", errno); + return result; + } + + if (memcmp(¤tState, termios_p, sizeof(struct termios)) != 0) + { + CommLog_Print(WLOG_DEBUG, + "all termios parameters are not set yet, doing a second attempt..."); + if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0) + { + CommLog_Print(WLOG_WARN, "2nd tcsetattr failure, errno: %d", errno); + return result; + } + + ZeroMemory(¤tState, sizeof(struct termios)); + if ((result = tcgetattr(fd, ¤tState)) < 0) + { + CommLog_Print(WLOG_WARN, "tcgetattr failure, errno: %d", errno); + return result; + } + + if (memcmp(¤tState, termios_p, sizeof(struct termios)) != 0) + { + CommLog_Print(WLOG_WARN, + "Failure: all termios parameters are still not set on a second attempt"); + return -1; + } + } + + return 0; +} + +#endif /* __linux__ */ diff --git a/winpr/libwinpr/comm/comm_ioctl.h b/winpr/libwinpr/comm/comm_ioctl.h new file mode 100644 index 0000000..6467a43 --- /dev/null +++ b/winpr/libwinpr/comm/comm_ioctl.h @@ -0,0 +1,236 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_COMM_IOCTL_H_ +#define WINPR_COMM_IOCTL_H_ + +#if defined __linux__ && !defined ANDROID + +#include + +#include +#include +#include + +#include "comm.h" + +/* Serial I/O Request Interface: http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx + * Ntddser.h http://msdn.microsoft.com/en-us/cc308432.aspx + * Ntddpar.h http://msdn.microsoft.com/en-us/cc308431.aspx + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* TODO: defines and types below are very similar to those in comm.h, keep only + * those that differ more than the names */ + +#define STOP_BIT_1 0 +#define STOP_BITS_1_5 1 +#define STOP_BITS_2 2 + +#define NO_PARITY 0 +#define ODD_PARITY 1 +#define EVEN_PARITY 2 +#define MARK_PARITY 3 +#define SPACE_PARITY 4 + + typedef struct + { + ULONG BaudRate; + } SERIAL_BAUD_RATE, *PSERIAL_BAUD_RATE; + + typedef struct + { + UCHAR EofChar; + UCHAR ErrorChar; + UCHAR BreakChar; + UCHAR EventChar; + UCHAR XonChar; + UCHAR XoffChar; + } SERIAL_CHARS, *PSERIAL_CHARS; + + typedef struct + { + UCHAR StopBits; + UCHAR Parity; + UCHAR WordLength; + } SERIAL_LINE_CONTROL, *PSERIAL_LINE_CONTROL; + + typedef struct + { + ULONG ControlHandShake; + ULONG FlowReplace; + LONG XonLimit; + LONG XoffLimit; + } SERIAL_HANDFLOW, *PSERIAL_HANDFLOW; + +#define SERIAL_DTR_MASK ((ULONG)0x03) +#define SERIAL_DTR_CONTROL ((ULONG)0x01) +#define SERIAL_DTR_HANDSHAKE ((ULONG)0x02) +#define SERIAL_CTS_HANDSHAKE ((ULONG)0x08) +#define SERIAL_DSR_HANDSHAKE ((ULONG)0x10) +#define SERIAL_DCD_HANDSHAKE ((ULONG)0x20) +#define SERIAL_OUT_HANDSHAKEMASK ((ULONG)0x38) +#define SERIAL_DSR_SENSITIVITY ((ULONG)0x40) +#define SERIAL_ERROR_ABORT ((ULONG)0x80000000) +#define SERIAL_CONTROL_INVALID ((ULONG)0x7fffff84) +#define SERIAL_AUTO_TRANSMIT ((ULONG)0x01) +#define SERIAL_AUTO_RECEIVE ((ULONG)0x02) +#define SERIAL_ERROR_CHAR ((ULONG)0x04) +#define SERIAL_NULL_STRIPPING ((ULONG)0x08) +#define SERIAL_BREAK_CHAR ((ULONG)0x10) +#define SERIAL_RTS_MASK ((ULONG)0xc0) +#define SERIAL_RTS_CONTROL ((ULONG)0x40) +#define SERIAL_RTS_HANDSHAKE ((ULONG)0x80) +#define SERIAL_TRANSMIT_TOGGLE ((ULONG)0xc0) +#define SERIAL_XOFF_CONTINUE ((ULONG)0x80000000) +#define SERIAL_FLOW_INVALID ((ULONG)0x7fffff20) + +#define SERIAL_SP_SERIALCOMM ((ULONG)0x00000001) + +#define SERIAL_SP_UNSPECIFIED ((ULONG)0x00000000) +#define SERIAL_SP_RS232 ((ULONG)0x00000001) +#define SERIAL_SP_PARALLEL ((ULONG)0x00000002) +#define SERIAL_SP_RS422 ((ULONG)0x00000003) +#define SERIAL_SP_RS423 ((ULONG)0x00000004) +#define SERIAL_SP_RS449 ((ULONG)0x00000005) +#define SERIAL_SP_MODEM ((ULONG)0x00000006) +#define SERIAL_SP_FAX ((ULONG)0x00000021) +#define SERIAL_SP_SCANNER ((ULONG)0x00000022) +#define SERIAL_SP_BRIDGE ((ULONG)0x00000100) +#define SERIAL_SP_LAT ((ULONG)0x00000101) +#define SERIAL_SP_TELNET ((ULONG)0x00000102) +#define SERIAL_SP_X25 ((ULONG)0x00000103) + + typedef struct + { + ULONG ReadIntervalTimeout; + ULONG ReadTotalTimeoutMultiplier; + ULONG ReadTotalTimeoutConstant; + ULONG WriteTotalTimeoutMultiplier; + ULONG WriteTotalTimeoutConstant; + } SERIAL_TIMEOUTS, *PSERIAL_TIMEOUTS; + +#define SERIAL_MSR_DCTS 0x01 +#define SERIAL_MSR_DDSR 0x02 +#define SERIAL_MSR_TERI 0x04 +#define SERIAL_MSR_DDCD 0x08 +#define SERIAL_MSR_CTS 0x10 +#define SERIAL_MSR_DSR 0x20 +#define SERIAL_MSR_RI 0x40 +#define SERIAL_MSR_DCD 0x80 + + typedef struct + { + ULONG InSize; + ULONG OutSize; + } SERIAL_QUEUE_SIZE, *PSERIAL_QUEUE_SIZE; + +#define SERIAL_PURGE_TXABORT 0x00000001 +#define SERIAL_PURGE_RXABORT 0x00000002 +#define SERIAL_PURGE_TXCLEAR 0x00000004 +#define SERIAL_PURGE_RXCLEAR 0x00000008 + + typedef struct + { + ULONG Errors; + ULONG HoldReasons; + ULONG AmountInInQueue; + ULONG AmountInOutQueue; + BOOLEAN EofReceived; + BOOLEAN WaitForImmediate; + } SERIAL_STATUS, *PSERIAL_STATUS; + +#define SERIAL_TX_WAITING_FOR_CTS ((ULONG)0x00000001) +#define SERIAL_TX_WAITING_FOR_DSR ((ULONG)0x00000002) +#define SERIAL_TX_WAITING_FOR_DCD ((ULONG)0x00000004) +#define SERIAL_TX_WAITING_FOR_XON ((ULONG)0x00000008) +#define SERIAL_TX_WAITING_XOFF_SENT ((ULONG)0x00000010) +#define SERIAL_TX_WAITING_ON_BREAK ((ULONG)0x00000020) +#define SERIAL_RX_WAITING_FOR_DSR ((ULONG)0x00000040) + +#define SERIAL_ERROR_BREAK ((ULONG)0x00000001) +#define SERIAL_ERROR_FRAMING ((ULONG)0x00000002) +#define SERIAL_ERROR_OVERRUN ((ULONG)0x00000004) +#define SERIAL_ERROR_QUEUEOVERRUN ((ULONG)0x00000008) +#define SERIAL_ERROR_PARITY ((ULONG)0x00000010) + +#define SERIAL_DTR_STATE ((ULONG)0x00000001) +#define SERIAL_RTS_STATE ((ULONG)0x00000002) +#define SERIAL_CTS_STATE ((ULONG)0x00000010) +#define SERIAL_DSR_STATE ((ULONG)0x00000020) +#define SERIAL_RI_STATE ((ULONG)0x00000040) +#define SERIAL_DCD_STATE ((ULONG)0x00000080) + + /** + * A function might be NULL if not supported by the underlying driver. + * + * FIXME: better have to use input and output buffers for all functions? + */ + typedef struct + { + SERIAL_DRIVER_ID id; + TCHAR* name; + BOOL (*set_baud_rate)(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate); + BOOL (*get_baud_rate)(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate); + BOOL (*get_properties)(WINPR_COMM* pComm, COMMPROP* pProperties); + BOOL (*set_serial_chars)(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars); + BOOL (*get_serial_chars)(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars); + BOOL (*set_line_control)(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl); + BOOL (*get_line_control)(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl); + BOOL (*set_handflow)(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow); + BOOL (*get_handflow)(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow); + BOOL (*set_timeouts)(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts); + BOOL (*get_timeouts)(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts); + BOOL (*set_dtr)(WINPR_COMM* pComm); + BOOL (*clear_dtr)(WINPR_COMM* pComm); + BOOL (*set_rts)(WINPR_COMM* pComm); + BOOL (*clear_rts)(WINPR_COMM* pComm); + BOOL (*get_modemstatus)(WINPR_COMM* pComm, ULONG* pRegister); + BOOL (*set_wait_mask)(WINPR_COMM* pComm, const ULONG* pWaitMask); + BOOL (*get_wait_mask)(WINPR_COMM* pComm, ULONG* pWaitMask); + BOOL (*wait_on_mask)(WINPR_COMM* pComm, ULONG* pOutputMask); + BOOL (*set_queue_size)(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize); + BOOL (*purge)(WINPR_COMM* pComm, const ULONG* pPurgeMask); + BOOL (*get_commstatus)(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus); + BOOL (*set_break_on)(WINPR_COMM* pComm); + BOOL (*set_break_off)(WINPR_COMM* pComm); + BOOL (*set_xoff)(WINPR_COMM* pComm); + BOOL (*set_xon)(WINPR_COMM* pComm); + BOOL (*get_dtrrts)(WINPR_COMM* pComm, ULONG* pMask); + BOOL (*config_size)(WINPR_COMM* pComm, ULONG* pSize); + BOOL (*immediate_char)(WINPR_COMM* pComm, const UCHAR* pChar); + BOOL (*reset_device)(WINPR_COMM* pComm); + + } SERIAL_DRIVER; + + int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p); + +#ifdef __cplusplus +} +#endif + +#endif /* __linux__ */ + +#endif /* WINPR_COMM_IOCTL_H_ */ diff --git a/winpr/libwinpr/comm/comm_sercx2_sys.c b/winpr/libwinpr/comm/comm_sercx2_sys.c new file mode 100644 index 0000000..d636124 --- /dev/null +++ b/winpr/libwinpr/comm/comm_sercx2_sys.c @@ -0,0 +1,200 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined __linux__ && !defined ANDROID + +#include + +#include "comm_serial_sys.h" +#include "comm_sercx_sys.h" + +#include "comm_sercx2_sys.h" + +/* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx + * + * SerCx2 does not support special characters. SerCx2 always completes + * an IOCTL_SERIAL_SET_CHARS request with a STATUS_SUCCESS status + * code, but does not set any special characters or perform any other + * operation in response to this request. For an + * IOCTL_SERIAL_GET_CHARS request, SerCx2 sets all the character + * values in the SERIAL_CHARS structure to null, and completes the + * request with a STATUS_SUCCESS status code. + */ + +static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars) +{ + return TRUE; +} + +static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars) +{ + WINPR_ASSERT(pSerialChars); + ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS)); + return TRUE; +} + +/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */ +/* FIXME: only using the Serial.sys' events, complete the support of the remaining events */ +static const ULONG _SERCX2_SYS_SUPPORTED_EV_MASK = + SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR | + SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING | + /* SERIAL_EV_PERR | */ + SERIAL_EV_RX80FULL /*| + SERIAL_EV_EVENT1 | + SERIAL_EV_EVENT2*/ + ; + +/* use Serial.sys for basis (not SerCx.sys) */ +static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask) +{ + ULONG possibleMask = 0; + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + possibleMask = *pWaitMask & _SERCX2_SYS_SUPPORTED_EV_MASK; + + if (possibleMask != *pWaitMask) + { + CommLog_Print(WLOG_WARN, + "Not all wait events supported (SerCx2.sys), requested events= 0x%08" PRIX32 + ", possible events= 0x%08" PRIX32 "", + *pWaitMask, possibleMask); + + /* FIXME: shall we really set the possibleMask and return FALSE? */ + pComm->WaitEventMask = possibleMask; + return FALSE; + } + + /* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/ + return pSerialSys->set_wait_mask(pComm, pWaitMask); +} + +static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask) +{ + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + /* http://msdn.microsoft.com/en-us/library/windows/hardware/ff546655%28v=vs.85%29.aspx */ + + if ((*pPurgeMask & SERIAL_PURGE_RXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_RXABORT)) + { + CommLog_Print(WLOG_WARN, + "Expecting SERIAL_PURGE_RXABORT since SERIAL_PURGE_RXCLEAR is set"); + SetLastError(ERROR_INVALID_DEVICE_OBJECT_PARAMETER); + return FALSE; + } + + if ((*pPurgeMask & SERIAL_PURGE_TXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_TXABORT)) + { + CommLog_Print(WLOG_WARN, + "Expecting SERIAL_PURGE_TXABORT since SERIAL_PURGE_TXCLEAR is set"); + SetLastError(ERROR_INVALID_DEVICE_OBJECT_PARAMETER); + return FALSE; + } + + return pSerialSys->purge(pComm, pPurgeMask); +} + +/* specific functions only */ +static SERIAL_DRIVER _SerCx2Sys = { + .id = SerialDriverSerCx2Sys, + .name = _T("SerCx2.sys"), + .set_baud_rate = NULL, + .get_baud_rate = NULL, + .get_properties = NULL, + .set_serial_chars = _set_serial_chars, + .get_serial_chars = _get_serial_chars, + .set_line_control = NULL, + .get_line_control = NULL, + .set_handflow = NULL, + .get_handflow = NULL, + .set_timeouts = NULL, + .get_timeouts = NULL, + .set_dtr = NULL, + .clear_dtr = NULL, + .set_rts = NULL, + .clear_rts = NULL, + .get_modemstatus = NULL, + .set_wait_mask = _set_wait_mask, + .get_wait_mask = NULL, + .wait_on_mask = NULL, + .set_queue_size = NULL, + .purge = _purge, + .get_commstatus = NULL, + .set_break_on = NULL, + .set_break_off = NULL, + .set_xoff = NULL, /* not supported by SerCx2.sys */ + .set_xon = NULL, /* not supported by SerCx2.sys */ + .get_dtrrts = NULL, + .config_size = NULL, /* not supported by SerCx2.sys */ + .immediate_char = NULL, /* not supported by SerCx2.sys */ + .reset_device = NULL, /* not supported by SerCx2.sys */ +}; + +SERIAL_DRIVER* SerCx2Sys_s(void) +{ + /* _SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */ + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + SERIAL_DRIVER* pSerCxSys = SerCxSys_s(); + if (!pSerialSys || !pSerCxSys) + return NULL; + + _SerCx2Sys.set_baud_rate = pSerialSys->set_baud_rate; + _SerCx2Sys.get_baud_rate = pSerialSys->get_baud_rate; + + _SerCx2Sys.get_properties = pSerialSys->get_properties; + + _SerCx2Sys.set_line_control = pSerCxSys->set_line_control; + _SerCx2Sys.get_line_control = pSerCxSys->get_line_control; + + /* Only SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL and SERIAL_RTS_HANDSHAKE flags are really + * required by SerCx2.sys http://msdn.microsoft.com/en-us/library/jj680685%28v=vs.85%29.aspx + */ + _SerCx2Sys.set_handflow = pSerialSys->set_handflow; + _SerCx2Sys.get_handflow = pSerialSys->get_handflow; + + _SerCx2Sys.set_timeouts = pSerialSys->set_timeouts; + _SerCx2Sys.get_timeouts = pSerialSys->get_timeouts; + + _SerCx2Sys.set_dtr = pSerialSys->set_dtr; + _SerCx2Sys.clear_dtr = pSerialSys->clear_dtr; + + _SerCx2Sys.set_rts = pSerialSys->set_rts; + _SerCx2Sys.clear_rts = pSerialSys->clear_rts; + + _SerCx2Sys.get_modemstatus = pSerialSys->get_modemstatus; + + _SerCx2Sys.set_wait_mask = pSerialSys->set_wait_mask; + _SerCx2Sys.get_wait_mask = pSerialSys->get_wait_mask; + _SerCx2Sys.wait_on_mask = pSerialSys->wait_on_mask; + + _SerCx2Sys.set_queue_size = pSerialSys->set_queue_size; + + _SerCx2Sys.get_commstatus = pSerialSys->get_commstatus; + + _SerCx2Sys.set_break_on = pSerialSys->set_break_on; + _SerCx2Sys.set_break_off = pSerialSys->set_break_off; + + _SerCx2Sys.get_dtrrts = pSerialSys->get_dtrrts; + + return &_SerCx2Sys; +} + +#endif /* __linux__ */ diff --git a/winpr/libwinpr/comm/comm_sercx2_sys.h b/winpr/libwinpr/comm/comm_sercx2_sys.h new file mode 100644 index 0000000..a290653 --- /dev/null +++ b/winpr/libwinpr/comm/comm_sercx2_sys.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMM_SERCX2_SYS_H +#define COMM_SERCX2_SYS_H + +#if defined __linux__ && !defined ANDROID + +#include "comm_ioctl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERIAL_DRIVER* SerCx2Sys_s(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __linux__ */ + +#endif /* COMM_SERCX2_SYS_H */ diff --git a/winpr/libwinpr/comm/comm_sercx_sys.c b/winpr/libwinpr/comm/comm_sercx_sys.c new file mode 100644 index 0000000..ece456f --- /dev/null +++ b/winpr/libwinpr/comm/comm_sercx_sys.c @@ -0,0 +1,266 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined __linux__ && !defined ANDROID + +#include +#include + +#include + +#include "comm_serial_sys.h" +#include "comm_sercx_sys.h" + +static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow) +{ + SERIAL_HANDFLOW SerCxHandflow; + BOOL result = TRUE; + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + memcpy(&SerCxHandflow, pHandflow, sizeof(SERIAL_HANDFLOW)); + + /* filter out unsupported bits by SerCx.sys + * + * http://msdn.microsoft.com/en-us/library/windows/hardware/jj680685%28v=vs.85%29.aspx + */ + + SerCxHandflow.ControlHandShake = + pHandflow->ControlHandShake & + (SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE | SERIAL_CTS_HANDSHAKE | SERIAL_DSR_HANDSHAKE); + SerCxHandflow.FlowReplace = + pHandflow->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE); + + if (SerCxHandflow.ControlHandShake != pHandflow->ControlHandShake) + { + if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE) + { + CommLog_Print(WLOG_WARN, + "SERIAL_DCD_HANDSHAKE not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY) + { + CommLog_Print(WLOG_WARN, + "SERIAL_DSR_SENSITIVITY not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT) + { + CommLog_Print(WLOG_WARN, + "SERIAL_ERROR_ABORT not supposed to be implemented by SerCx.sys"); + } + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + result = FALSE; + } + + if (SerCxHandflow.FlowReplace != pHandflow->FlowReplace) + { + if (pHandflow->ControlHandShake & SERIAL_AUTO_TRANSMIT) + { + CommLog_Print(WLOG_WARN, + "SERIAL_AUTO_TRANSMIT not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_AUTO_RECEIVE) + { + CommLog_Print(WLOG_WARN, + "SERIAL_AUTO_RECEIVE not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_ERROR_CHAR) + { + CommLog_Print(WLOG_WARN, + "SERIAL_ERROR_CHAR not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_NULL_STRIPPING) + { + CommLog_Print(WLOG_WARN, + "SERIAL_NULL_STRIPPING not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_BREAK_CHAR) + { + CommLog_Print(WLOG_WARN, + "SERIAL_BREAK_CHAR not supposed to be implemented by SerCx.sys"); + } + + if (pHandflow->ControlHandShake & SERIAL_XOFF_CONTINUE) + { + CommLog_Print(WLOG_WARN, + "SERIAL_XOFF_CONTINUE not supposed to be implemented by SerCx.sys"); + } + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + result = FALSE; + } + + if (!pSerialSys->set_handflow(pComm, &SerCxHandflow)) + return FALSE; + + return result; +} + +static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow) +{ + BOOL result = 0; + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + result = pSerialSys->get_handflow(pComm, pHandflow); + + /* filter out unsupported bits by SerCx.sys + * + * http://msdn.microsoft.com/en-us/library/windows/hardware/jj680685%28v=vs.85%29.aspx + */ + + pHandflow->ControlHandShake = + pHandflow->ControlHandShake & + (SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE | SERIAL_CTS_HANDSHAKE | SERIAL_DSR_HANDSHAKE); + pHandflow->FlowReplace = pHandflow->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE); + + return result; +} + +/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */ +static const ULONG _SERCX_SYS_SUPPORTED_EV_MASK = SERIAL_EV_RXCHAR | + /* SERIAL_EV_RXFLAG | */ + SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | + SERIAL_EV_DSR | SERIAL_EV_RLSD | SERIAL_EV_BREAK | + SERIAL_EV_ERR | SERIAL_EV_RING /* | + SERIAL_EV_PERR | + SERIAL_EV_RX80FULL | + SERIAL_EV_EVENT1 | + SERIAL_EV_EVENT2*/ + ; + +static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask) +{ + ULONG possibleMask = 0; + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + possibleMask = *pWaitMask & _SERCX_SYS_SUPPORTED_EV_MASK; + + if (possibleMask != *pWaitMask) + { + CommLog_Print(WLOG_WARN, + "Not all wait events supported (SerCx.sys), requested events= 0x%08" PRIX32 + ", possible events= 0x%08" PRIX32 "", + *pWaitMask, possibleMask); + + /* FIXME: shall we really set the possibleMask and return FALSE? */ + pComm->WaitEventMask = possibleMask; + return FALSE; + } + + /* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/ + return pSerialSys->set_wait_mask(pComm, pWaitMask); +} + +/* specific functions only */ +static SERIAL_DRIVER _SerCxSys = { + .id = SerialDriverSerCxSys, + .name = _T("SerCx.sys"), + .set_baud_rate = NULL, + .get_baud_rate = NULL, + .get_properties = NULL, + .set_serial_chars = NULL, + .get_serial_chars = NULL, + .set_line_control = NULL, + .get_line_control = NULL, + .set_handflow = _set_handflow, + .get_handflow = _get_handflow, + .set_timeouts = NULL, + .get_timeouts = NULL, + .set_dtr = NULL, + .clear_dtr = NULL, + .set_rts = NULL, + .clear_rts = NULL, + .get_modemstatus = NULL, + .set_wait_mask = _set_wait_mask, + .get_wait_mask = NULL, + .wait_on_mask = NULL, + .set_queue_size = NULL, + .purge = NULL, + .get_commstatus = NULL, + .set_break_on = NULL, + .set_break_off = NULL, + .set_xoff = NULL, + .set_xon = NULL, + .get_dtrrts = NULL, + .config_size = NULL, /* not supported by SerCx.sys */ + .immediate_char = NULL, + .reset_device = NULL, /* not supported by SerCx.sys */ +}; + +SERIAL_DRIVER* SerCxSys_s(void) +{ + /* _SerCxSys completed with inherited functions from SerialSys */ + SERIAL_DRIVER* pSerialSys = SerialSys_s(); + if (!pSerialSys) + return NULL; + + _SerCxSys.set_baud_rate = pSerialSys->set_baud_rate; + _SerCxSys.get_baud_rate = pSerialSys->get_baud_rate; + + _SerCxSys.get_properties = pSerialSys->get_properties; + + _SerCxSys.set_serial_chars = pSerialSys->set_serial_chars; + _SerCxSys.get_serial_chars = pSerialSys->get_serial_chars; + _SerCxSys.set_line_control = pSerialSys->set_line_control; + _SerCxSys.get_line_control = pSerialSys->get_line_control; + + _SerCxSys.set_timeouts = pSerialSys->set_timeouts; + _SerCxSys.get_timeouts = pSerialSys->get_timeouts; + + _SerCxSys.set_dtr = pSerialSys->set_dtr; + _SerCxSys.clear_dtr = pSerialSys->clear_dtr; + + _SerCxSys.set_rts = pSerialSys->set_rts; + _SerCxSys.clear_rts = pSerialSys->clear_rts; + + _SerCxSys.get_modemstatus = pSerialSys->get_modemstatus; + + _SerCxSys.set_wait_mask = pSerialSys->set_wait_mask; + _SerCxSys.get_wait_mask = pSerialSys->get_wait_mask; + _SerCxSys.wait_on_mask = pSerialSys->wait_on_mask; + + _SerCxSys.set_queue_size = pSerialSys->set_queue_size; + + _SerCxSys.purge = pSerialSys->purge; + + _SerCxSys.get_commstatus = pSerialSys->get_commstatus; + + _SerCxSys.set_break_on = pSerialSys->set_break_on; + _SerCxSys.set_break_off = pSerialSys->set_break_off; + + _SerCxSys.set_xoff = pSerialSys->set_xoff; + _SerCxSys.set_xon = pSerialSys->set_xon; + + _SerCxSys.get_dtrrts = pSerialSys->get_dtrrts; + + _SerCxSys.immediate_char = pSerialSys->immediate_char; + + return &_SerCxSys; +} + +#endif /* __linux__ */ diff --git a/winpr/libwinpr/comm/comm_sercx_sys.h b/winpr/libwinpr/comm/comm_sercx_sys.h new file mode 100644 index 0000000..2268c0c --- /dev/null +++ b/winpr/libwinpr/comm/comm_sercx_sys.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMM_SERCX_SYS_H +#define COMM_SERCX_SYS_H + +#if defined __linux__ && !defined ANDROID + +#include "comm_ioctl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERIAL_DRIVER* SerCxSys_s(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __linux__ */ + +#endif /* COMM_SERCX_SYS_H */ diff --git a/winpr/libwinpr/comm/comm_serial_sys.c b/winpr/libwinpr/comm/comm_serial_sys.c new file mode 100644 index 0000000..cae653c --- /dev/null +++ b/winpr/libwinpr/comm/comm_serial_sys.c @@ -0,0 +1,1637 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Eduardo Fiss Beloni + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined __linux__ && !defined ANDROID + +#include +#include +#include +#include +#include +#include + +#include "comm_serial_sys.h" +#ifdef __UCLIBC__ +#include "comm.h" +#endif + +#include +#include + +/* Undocumented flag, not supported everywhere. + * Provide a sensible fallback to avoid compilation problems. */ +#ifndef CMSPAR +#define CMSPAR 010000000000 +#endif + +/* hard-coded in N_TTY */ +#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ +#define TTY_THRESHOLD_UNTHROTTLE 128 +#define N_TTY_BUF_SIZE 4096 + +#define BAUD_TABLE_END 0010020 /* __MAX_BAUD + 1 */ + +/* 0: B* (Linux termios) + * 1: CBR_* or actual baud rate + * 2: BAUD_* (identical to SERIAL_BAUD_*) + */ +static const speed_t _BAUD_TABLE[][3] = { +#ifdef B0 + { B0, 0, 0 }, /* hang up */ +#endif +#ifdef B50 + { B50, 50, 0 }, +#endif +#ifdef B75 + { B75, 75, BAUD_075 }, +#endif +#ifdef B110 + { B110, CBR_110, BAUD_110 }, +#endif +#ifdef B134 + { B134, 134, 0 /*BAUD_134_5*/ }, +#endif +#ifdef B150 + { B150, 150, BAUD_150 }, +#endif +#ifdef B200 + { B200, 200, 0 }, +#endif +#ifdef B300 + { B300, CBR_300, BAUD_300 }, +#endif +#ifdef B600 + { B600, CBR_600, BAUD_600 }, +#endif +#ifdef B1200 + { B1200, CBR_1200, BAUD_1200 }, +#endif +#ifdef B1800 + { B1800, 1800, BAUD_1800 }, +#endif +#ifdef B2400 + { B2400, CBR_2400, BAUD_2400 }, +#endif +#ifdef B4800 + { B4800, CBR_4800, BAUD_4800 }, +#endif +/* {, ,BAUD_7200} */ +#ifdef B9600 + { B9600, CBR_9600, BAUD_9600 }, +#endif +/* {, CBR_14400, BAUD_14400}, /\* unsupported on Linux *\/ */ +#ifdef B19200 + { B19200, CBR_19200, BAUD_19200 }, +#endif +#ifdef B38400 + { B38400, CBR_38400, BAUD_38400 }, +#endif +/* {, CBR_56000, BAUD_56K}, /\* unsupported on Linux *\/ */ +#ifdef B57600 + { B57600, CBR_57600, BAUD_57600 }, +#endif +#ifdef B115200 + { B115200, CBR_115200, BAUD_115200 }, +#endif +/* {, CBR_128000, BAUD_128K}, /\* unsupported on Linux *\/ */ +/* {, CBR_256000, BAUD_USER}, /\* unsupported on Linux *\/ */ +#ifdef B230400 + { B230400, 230400, BAUD_USER }, +#endif +#ifdef B460800 + { B460800, 460800, BAUD_USER }, +#endif +#ifdef B500000 + { B500000, 500000, BAUD_USER }, +#endif +#ifdef B576000 + { B576000, 576000, BAUD_USER }, +#endif +#ifdef B921600 + { B921600, 921600, BAUD_USER }, +#endif +#ifdef B1000000 + { B1000000, 1000000, BAUD_USER }, +#endif +#ifdef B1152000 + { B1152000, 1152000, BAUD_USER }, +#endif +#ifdef B1500000 + { B1500000, 1500000, BAUD_USER }, +#endif +#ifdef B2000000 + { B2000000, 2000000, BAUD_USER }, +#endif +#ifdef B2500000 + { B2500000, 2500000, BAUD_USER }, +#endif +#ifdef B3000000 + { B3000000, 3000000, BAUD_USER }, +#endif +#ifdef B3500000 + { B3500000, 3500000, BAUD_USER }, +#endif +#ifdef B4000000 + { B4000000, 4000000, BAUD_USER }, /* __MAX_BAUD */ +#endif + { BAUD_TABLE_END, 0, 0 } +}; + +static BOOL _get_properties(WINPR_COMM* pComm, COMMPROP* pProperties) +{ + /* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx + */ + + /* FIXME: properties should be better probed. The current + * implementation just relies on the Linux' implementation. + */ + WINPR_ASSERT(pProperties); + if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED) + { + ZeroMemory(pProperties, sizeof(COMMPROP)); + pProperties->wPacketLength = sizeof(COMMPROP); + } + + pProperties->wPacketVersion = 2; + + pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM; + + /* pProperties->Reserved1; not used */ + + /* FIXME: could be implemented on top of N_TTY */ + pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE; + pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE; + + /* FIXME: to be probe on the device? */ + pProperties->dwMaxBaud = BAUD_USER; + + /* FIXME: what about PST_RS232? see also: serial_struct */ + pProperties->dwProvSubType = PST_UNSPECIFIED; + + /* TODO: to be finalized */ + pProperties->dwProvCapabilities = + /*PCF_16BITMODE |*/ PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK | /*PCF_RLSD |*/ + PCF_RTSCTS | PCF_SETXCHAR | /*PCF_SPECIALCHARS |*/ PCF_TOTALTIMEOUTS | PCF_XONXOFF; + + /* TODO: double check SP_RLSD */ + pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY | + SP_PARITY_CHECK | /*SP_RLSD |*/ SP_STOPBITS; + + pProperties->dwSettableBaud = 0; + for (int i = 0; _BAUD_TABLE[i][0] < BAUD_TABLE_END; i++) + { + pProperties->dwSettableBaud |= _BAUD_TABLE[i][2]; + } + + pProperties->wSettableData = + DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 /*| DATABITS_16 | DATABITS_16X*/; + + pProperties->wSettableStopParity = STOPBITS_10 | /*STOPBITS_15 |*/ STOPBITS_20 | PARITY_NONE | + PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE; + + /* FIXME: additional input and output buffers could be implemented on top of N_TTY */ + pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE; + pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE; + + /* pProperties->ProvSpec1; see above */ + /* pProperties->ProvSpec2; ignored */ + /* pProperties->ProvChar[1]; ignored */ + + return TRUE; +} + +static BOOL _set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate) +{ + speed_t newSpeed = 0; + struct termios futureState; + + ZeroMemory(&futureState, sizeof(struct termios)); + if (tcgetattr(pComm->fd, &futureState) < + 0) /* NB: preserves current settings not directly handled by the Communication Functions */ + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + for (int i = 0; _BAUD_TABLE[i][0] < BAUD_TABLE_END; i++) + { + if (_BAUD_TABLE[i][1] == pBaudRate->BaudRate) + { + newSpeed = _BAUD_TABLE[i][0]; + if (cfsetspeed(&futureState, newSpeed) < 0) + { + CommLog_Print(WLOG_WARN, "failed to set speed 0x%x (%" PRIu32 ")", newSpeed, + pBaudRate->BaudRate); + return FALSE; + } + + WINPR_ASSERT(cfgetispeed(&futureState) == newSpeed); + + if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0) + { + CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "", + GetLastError()); + return FALSE; + } + + return TRUE; + } + } + + CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %" PRIu32 "", + pBaudRate->BaudRate); + SetLastError(ERROR_INVALID_DATA); + return FALSE; +} + +static BOOL _get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate) +{ + speed_t currentSpeed = 0; + struct termios currentState; + + ZeroMemory(¤tState, sizeof(struct termios)); + if (tcgetattr(pComm->fd, ¤tState) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + currentSpeed = cfgetispeed(¤tState); + + for (int i = 0; _BAUD_TABLE[i][0] < BAUD_TABLE_END; i++) + { + if (_BAUD_TABLE[i][0] == currentSpeed) + { + pBaudRate->BaudRate = _BAUD_TABLE[i][1]; + return TRUE; + } + } + + CommLog_Print(WLOG_WARN, "could not find a matching baud rate for the speed 0x%x", + currentSpeed); + SetLastError(ERROR_INVALID_DATA); + return FALSE; +} + +/** + * NOTE: Only XonChar and XoffChar are plenty supported with the Linux + * N_TTY line discipline. + * + * ERRORS: + * ERROR_IO_DEVICE + * ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same; + * ERROR_NOT_SUPPORTED + */ +static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars) +{ + BOOL result = TRUE; + struct termios upcomingTermios; + + ZeroMemory(&upcomingTermios, sizeof(struct termios)); + if (tcgetattr(pComm->fd, &upcomingTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + if (pSerialChars->XonChar == pSerialChars->XoffChar) + { + /* https://msdn.microsoft.com/en-us/library/windows/hardware/ff546688?v=vs.85.aspx */ + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + /* termios(3): (..) above symbolic subscript values are all + * different, except that VTIME, VMIN may have the same value + * as VEOL, VEOF, respectively. In noncanonical mode the + * special character meaning is replaced by the timeout + * meaning. + * + * EofChar and c_cc[VEOF] are not quite the same, prefer to + * don't use c_cc[VEOF] at all. + * + * FIXME: might be implemented during read/write I/O + */ + if (pSerialChars->EofChar != '\0') + { + CommLog_Print(WLOG_WARN, "EofChar %02" PRIX8 " cannot be set\n", pSerialChars->EofChar); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + /* According the Linux's n_tty discipline, charaters with a + * parity error can only be let unchanged, replaced by \0 or + * get the prefix the prefix \377 \0 + */ + + /* FIXME: see also: _set_handflow() */ + if (pSerialChars->ErrorChar != '\0') + { + CommLog_Print(WLOG_WARN, "ErrorChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n", + pSerialChars->ErrorChar, (char)pSerialChars->ErrorChar); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + /* FIXME: see also: _set_handflow() */ + if (pSerialChars->BreakChar != '\0') + { + CommLog_Print(WLOG_WARN, "BreakChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n", + pSerialChars->BreakChar, (char)pSerialChars->BreakChar); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + if (pSerialChars->EventChar != '\0') + { + pComm->eventChar = pSerialChars->EventChar; + } + + upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar; + + upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar; + + if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) + { + CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "", + GetLastError()); + return FALSE; + } + + return result; +} + +static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars) +{ + struct termios currentTermios; + + ZeroMemory(¤tTermios, sizeof(struct termios)); + if (tcgetattr(pComm->fd, ¤tTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS)); + + /* EofChar unsupported */ + + /* ErrorChar unsupported */ + + /* BreakChar unsupported */ + + /* FIXME: see also: _set_serial_chars() */ + /* EventChar */ + + pSerialChars->XonChar = currentTermios.c_cc[VSTART]; + + pSerialChars->XoffChar = currentTermios.c_cc[VSTOP]; + + return TRUE; +} + +static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl) +{ + BOOL result = TRUE; + struct termios upcomingTermios; + + /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx + * + * The use of 5 data bits with 2 stop bits is an invalid + * combination, as is 6, 7, or 8 data bits with 1.5 stop bits. + * + * FIXME: prefered to let the underlying driver to deal with + * this issue. At least produce a warning message? + */ + + ZeroMemory(&upcomingTermios, sizeof(struct termios)); + if (tcgetattr(pComm->fd, &upcomingTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + /* FIXME: use of a COMMPROP to validate new settings? */ + + switch (pLineControl->StopBits) + { + case STOP_BIT_1: + upcomingTermios.c_cflag &= ~CSTOPB; + break; + + case STOP_BITS_1_5: + CommLog_Print(WLOG_WARN, "Unsupported one and a half stop bits."); + break; + + case STOP_BITS_2: + upcomingTermios.c_cflag |= CSTOPB; + break; + + default: + CommLog_Print(WLOG_WARN, "unexpected number of stop bits: %" PRIu8 "\n", + pLineControl->StopBits); + result = FALSE; /* but keep on */ + break; + } + + switch (pLineControl->Parity) + { + case NO_PARITY: + upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR); + break; + + case ODD_PARITY: + upcomingTermios.c_cflag &= ~CMSPAR; + upcomingTermios.c_cflag |= PARENB | PARODD; + break; + + case EVEN_PARITY: + upcomingTermios.c_cflag &= ~(PARODD | CMSPAR); + upcomingTermios.c_cflag |= PARENB; + break; + + case MARK_PARITY: + upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR; + break; + + case SPACE_PARITY: + upcomingTermios.c_cflag &= ~PARODD; + upcomingTermios.c_cflag |= PARENB | CMSPAR; + break; + + default: + CommLog_Print(WLOG_WARN, "unexpected type of parity: %" PRIu8 "\n", + pLineControl->Parity); + result = FALSE; /* but keep on */ + break; + } + + switch (pLineControl->WordLength) + { + case 5: + upcomingTermios.c_cflag &= ~CSIZE; + upcomingTermios.c_cflag |= CS5; + break; + + case 6: + upcomingTermios.c_cflag &= ~CSIZE; + upcomingTermios.c_cflag |= CS6; + break; + + case 7: + upcomingTermios.c_cflag &= ~CSIZE; + upcomingTermios.c_cflag |= CS7; + break; + + case 8: + upcomingTermios.c_cflag &= ~CSIZE; + upcomingTermios.c_cflag |= CS8; + break; + + default: + CommLog_Print(WLOG_WARN, "unexpected number od data bits per character: %" PRIu8 "\n", + pLineControl->WordLength); + result = FALSE; /* but keep on */ + break; + } + + if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) + { + CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "", + GetLastError()); + return FALSE; + } + + return result; +} + +static BOOL _get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl) +{ + struct termios currentTermios; + + ZeroMemory(¤tTermios, sizeof(struct termios)); + if (tcgetattr(pComm->fd, ¤tTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1; + + if (!(currentTermios.c_cflag & PARENB)) + { + pLineControl->Parity = NO_PARITY; + } + else if (currentTermios.c_cflag & CMSPAR) + { + pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY; + } + else + { + /* PARENB is set */ + pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY; + } + + switch (currentTermios.c_cflag & CSIZE) + { + case CS5: + pLineControl->WordLength = 5; + break; + case CS6: + pLineControl->WordLength = 6; + break; + case CS7: + pLineControl->WordLength = 7; + break; + default: + pLineControl->WordLength = 8; + break; + } + + return TRUE; +} + +static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow) +{ + BOOL result = TRUE; + struct termios upcomingTermios; + + ZeroMemory(&upcomingTermios, sizeof(struct termios)); + if (tcgetattr(pComm->fd, &upcomingTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + /* HUPCL */ + + /* logical XOR */ + if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) && + (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) || + ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) && + !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL))) + { + CommLog_Print(WLOG_WARN, + "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL " + "will be set since it is claimed for one of the both lines.", + (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ? "ON" : "OFF", + (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ? "ON" : "OFF"); + } + + if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) || + (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) + { + upcomingTermios.c_cflag |= HUPCL; + } + else + { + upcomingTermios.c_cflag &= ~HUPCL; + + /* FIXME: is the DTR line also needs to be forced to a disable state according + * SERIAL_DTR_CONTROL? */ + /* FIXME: is the RTS line also needs to be forced to a disable state according + * SERIAL_RTS_CONTROL? */ + } + + /* CRTSCTS */ + + /* logical XOR */ + if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) && + (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) || + ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) && + !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))) + { + CommLog_Print(WLOG_WARN, + "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, " + "CRTSCTS will be set since it is claimed for one of the both lines.", + (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ? "ON" : "OFF", + (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ? "ON" : "OFF"); + } + + if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) || + (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) + { + upcomingTermios.c_cflag |= CRTSCTS; + } + else + { + upcomingTermios.c_cflag &= ~CRTSCTS; + } + + /* ControlHandShake */ + + if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE) + { + /* DTR/DSR flow control not supported on Linux */ + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE) + { + /* DTR/DSR flow control not supported on Linux */ + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE) + { + /* DCD flow control not supported on Linux */ + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + // FIXME: could be implemented during read/write I/O + if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY) + { + /* DSR line control not supported on Linux */ + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + // FIXME: could be implemented during read/write I/O + if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT) + { + /* Aborting operations on error not supported on Linux */ + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_ERROR_ABORT feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + /* FlowReplace */ + + if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT) + { + upcomingTermios.c_iflag |= IXON; + } + else + { + upcomingTermios.c_iflag &= ~IXON; + } + + if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE) + { + upcomingTermios.c_iflag |= IXOFF; + } + else + { + upcomingTermios.c_iflag &= ~IXOFF; + } + + // FIXME: could be implemented during read/write I/O, as of today ErrorChar is necessary '\0' + if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR) + { + /* errors will be replaced by the character '\0'. */ + upcomingTermios.c_iflag &= ~IGNPAR; + } + else + { + upcomingTermios.c_iflag |= IGNPAR; + } + + if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING) + { + upcomingTermios.c_iflag |= IGNBRK; + } + else + { + upcomingTermios.c_iflag &= ~IGNBRK; + } + + // FIXME: could be implemented during read/write I/O + if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR) + { + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_BREAK_CHAR feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + // FIXME: could be implemented during read/write I/O + if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE) + { + /* not supported on Linux */ + CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature."); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + /* XonLimit */ + + // FIXME: could be implemented during read/write I/O + if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE) + { + CommLog_Print(WLOG_WARN, "Attempt to set XonLimit with an unsupported value: %" PRId32 "", + pHandflow->XonLimit); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + /* XoffChar */ + + // FIXME: could be implemented during read/write I/O + if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE) + { + CommLog_Print(WLOG_WARN, "Attempt to set XoffLimit with an unsupported value: %" PRId32 "", + pHandflow->XoffLimit); + SetLastError(ERROR_NOT_SUPPORTED); + result = FALSE; /* but keep on */ + } + + if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0) + { + CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "", + GetLastError()); + return FALSE; + } + + return result; +} + +static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow) +{ + struct termios currentTermios; + + ZeroMemory(¤tTermios, sizeof(struct termios)); + if (tcgetattr(pComm->fd, ¤tTermios) < 0) + { + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + /* ControlHandShake */ + + pHandflow->ControlHandShake = 0; + + if (currentTermios.c_cflag & HUPCL) + pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL; + + /* SERIAL_DTR_HANDSHAKE unsupported */ + + if (currentTermios.c_cflag & CRTSCTS) + pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE; + + /* SERIAL_DSR_HANDSHAKE unsupported */ + + /* SERIAL_DCD_HANDSHAKE unsupported */ + + /* SERIAL_DSR_SENSITIVITY unsupported */ + + /* SERIAL_ERROR_ABORT unsupported */ + + /* FlowReplace */ + + pHandflow->FlowReplace = 0; + + if (currentTermios.c_iflag & IXON) + pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT; + + if (currentTermios.c_iflag & IXOFF) + pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE; + + if (!(currentTermios.c_iflag & IGNPAR)) + pHandflow->FlowReplace |= SERIAL_ERROR_CHAR; + + if (currentTermios.c_iflag & IGNBRK) + pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING; + + /* SERIAL_BREAK_CHAR unsupported */ + + if (currentTermios.c_cflag & HUPCL) + pHandflow->FlowReplace |= SERIAL_RTS_CONTROL; + + if (currentTermios.c_cflag & CRTSCTS) + pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE; + + /* SERIAL_XOFF_CONTINUE unsupported */ + + /* XonLimit */ + + pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE; + + /* XoffLimit */ + + pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE; + + return TRUE; +} + +static BOOL _set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts) +{ + /* NB: timeouts are applied on system during read/write I/O */ + + /* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */ + if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && + (pTimeouts->ReadTotalTimeoutConstant == MAXULONG)) + { + CommLog_Print( + WLOG_WARN, + "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout; + pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier; + pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant; + pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier; + pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant; + + CommLog_Print(WLOG_DEBUG, "ReadIntervalTimeout %" PRIu32 "", + pComm->timeouts.ReadIntervalTimeout); + CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutMultiplier %" PRIu32 "", + pComm->timeouts.ReadTotalTimeoutMultiplier); + CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutConstant %" PRIu32 "", + pComm->timeouts.ReadTotalTimeoutConstant); + CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutMultiplier %" PRIu32 "", + pComm->timeouts.WriteTotalTimeoutMultiplier); + CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutConstant %" PRIu32 "", + pComm->timeouts.WriteTotalTimeoutConstant); + + return TRUE; +} + +static BOOL _get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts) +{ + pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout; + pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier; + pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant; + pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier; + pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant; + + return TRUE; +} + +static BOOL _set_lines(WINPR_COMM* pComm, UINT32 lines) +{ + if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCMBIS ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines, + errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +static BOOL _clear_lines(WINPR_COMM* pComm, UINT32 lines) +{ + if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCMBIC ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines, + errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +static BOOL _set_dtr(WINPR_COMM* pComm) +{ + SERIAL_HANDFLOW handflow; + if (!_get_handflow(pComm, &handflow)) + return FALSE; + + /* SERIAL_DTR_HANDSHAKE not supported as of today */ + WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0); + + if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return _set_lines(pComm, TIOCM_DTR); +} + +static BOOL _clear_dtr(WINPR_COMM* pComm) +{ + SERIAL_HANDFLOW handflow; + if (!_get_handflow(pComm, &handflow)) + return FALSE; + + /* SERIAL_DTR_HANDSHAKE not supported as of today */ + WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0); + + if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return _clear_lines(pComm, TIOCM_DTR); +} + +static BOOL _set_rts(WINPR_COMM* pComm) +{ + SERIAL_HANDFLOW handflow; + if (!_get_handflow(pComm, &handflow)) + return FALSE; + + if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return _set_lines(pComm, TIOCM_RTS); +} + +static BOOL _clear_rts(WINPR_COMM* pComm) +{ + SERIAL_HANDFLOW handflow; + if (!_get_handflow(pComm, &handflow)) + return FALSE; + + if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return _clear_lines(pComm, TIOCM_RTS); +} + +static BOOL _get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister) +{ + UINT32 lines = 0; + if (ioctl(pComm->fd, TIOCMGET, &lines) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + ZeroMemory(pRegister, sizeof(ULONG)); + + /* FIXME: Is the last read of the MSR register available or + * cached somewhere? Not quite sure we need to return the 4 + * LSBits anyway. A direct access to the register -- which + * would reset the register -- is likely not expected from + * this function. + */ + + /* #define SERIAL_MSR_DCTS 0x01 */ + /* #define SERIAL_MSR_DDSR 0x02 */ + /* #define SERIAL_MSR_TERI 0x04 */ + /* #define SERIAL_MSR_DDCD 0x08 */ + + if (lines & TIOCM_CTS) + *pRegister |= SERIAL_MSR_CTS; + if (lines & TIOCM_DSR) + *pRegister |= SERIAL_MSR_DSR; + if (lines & TIOCM_RI) + *pRegister |= SERIAL_MSR_RI; + if (lines & TIOCM_CD) + *pRegister |= SERIAL_MSR_DCD; + + return TRUE; +} + +/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */ +static const ULONG _SERIAL_SYS_SUPPORTED_EV_MASK = + SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR | + SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING | + /* SERIAL_EV_PERR | */ + SERIAL_EV_RX80FULL /*| + SERIAL_EV_EVENT1 | + SERIAL_EV_EVENT2*/ + ; + +static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask) +{ + ULONG possibleMask = 0; + + /* Stops pending IOCTL_SERIAL_WAIT_ON_MASK + * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx + */ + + if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) + { + /* FIXME: any doubt on reading PendingEvents out of a critical section? */ + + EnterCriticalSection(&pComm->EventsLock); + pComm->PendingEvents |= SERIAL_EV_WINPR_STOP; + LeaveCriticalSection(&pComm->EventsLock); + + /* waiting the end of the pending _wait_on_mask() */ + while (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) + Sleep(10); /* 10ms */ + } + + /* NB: ensure to leave the critical section before to return */ + EnterCriticalSection(&pComm->EventsLock); + + if (*pWaitMask == 0) + { + /* clearing pending events */ + + if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + + if (pComm->permissive) + { + /* counters could not be reset but keep on */ + ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct)); + } + else + { + SetLastError(ERROR_IO_DEVICE); + LeaveCriticalSection(&pComm->EventsLock); + return FALSE; + } + } + + pComm->PendingEvents = 0; + } + + possibleMask = *pWaitMask & _SERIAL_SYS_SUPPORTED_EV_MASK; + + if (possibleMask != *pWaitMask) + { + CommLog_Print(WLOG_WARN, + "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32 + ", possible events= 0x%08" PRIX32 "", + *pWaitMask, possibleMask); + + /* FIXME: shall we really set the possibleMask and return FALSE? */ + pComm->WaitEventMask = possibleMask; + + LeaveCriticalSection(&pComm->EventsLock); + return FALSE; + } + + pComm->WaitEventMask = possibleMask; + + LeaveCriticalSection(&pComm->EventsLock); + return TRUE; +} + +static BOOL _get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask) +{ + *pWaitMask = pComm->WaitEventMask; + return TRUE; +} + +static BOOL _set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize) +{ + if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE)) + return TRUE; /* nothing to do */ + + /* FIXME: could be implemented on top of N_TTY */ + + if (pQueueSize->InSize > N_TTY_BUF_SIZE) + CommLog_Print(WLOG_WARN, + "Requested an incompatible input buffer size: %" PRIu32 + ", keeping on with a %" PRIu32 " bytes buffer.", + pQueueSize->InSize, N_TTY_BUF_SIZE); + + if (pQueueSize->OutSize > N_TTY_BUF_SIZE) + CommLog_Print(WLOG_WARN, + "Requested an incompatible output buffer size: %" PRIu32 + ", keeping on with a %" PRIu32 " bytes buffer.", + pQueueSize->OutSize, N_TTY_BUF_SIZE); + + SetLastError(ERROR_CANCELLED); + return FALSE; +} + +static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask) +{ + if ((*pPurgeMask & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR | + SERIAL_PURGE_RXCLEAR)) > 0) + { + CommLog_Print(WLOG_WARN, "Invalid purge mask: 0x%" PRIX32 "\n", *pPurgeMask); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + /* FIXME: currently relying too much on the fact the server + * sends a single IRP_MJ_WRITE or IRP_MJ_READ at a time + * (taking care though that one IRP_MJ_WRITE and one + * IRP_MJ_READ can be sent simultaneously) */ + + if (*pPurgeMask & SERIAL_PURGE_TXABORT) + { + /* Purges all write (IRP_MJ_WRITE) requests. */ + + if (eventfd_write(pComm->fd_write_event, WINPR_PURGE_TXABORT) < 0) + { + if (errno != EAGAIN) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + + WINPR_ASSERT(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_WRITE */ + } + } + + if (*pPurgeMask & SERIAL_PURGE_RXABORT) + { + /* Purges all read (IRP_MJ_READ) requests. */ + + if (eventfd_write(pComm->fd_read_event, WINPR_PURGE_RXABORT) < 0) + { + if (errno != EAGAIN) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + + WINPR_ASSERT(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_READ */ + } + } + + if (*pPurgeMask & SERIAL_PURGE_TXCLEAR) + { + /* Purges the transmit buffer, if one exists. */ + + if (tcflush(pComm->fd, TCOFLUSH) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "tcflush(TCOFLUSH) failure, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_CANCELLED); + return FALSE; + } + } + + if (*pPurgeMask & SERIAL_PURGE_RXCLEAR) + { + /* Purges the receive buffer, if one exists. */ + + if (tcflush(pComm->fd, TCIFLUSH) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "tcflush(TCIFLUSH) failure, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_CANCELLED); + return FALSE; + } + } + + return TRUE; +} + +/* NB: _get_commstatus also produces most of the events consumed by _wait_on_mask(). Exceptions: + * - SERIAL_EV_RXFLAG: FIXME: once EventChar supported + * + */ +static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus) +{ + /* http://msdn.microsoft.com/en-us/library/jj673022%28v=vs.85%29.aspx */ + + struct serial_icounter_struct currentCounters; + + /* NB: ensure to leave the critical section before to return */ + EnterCriticalSection(&pComm->EventsLock); + + ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS)); + + ZeroMemory(¤tCounters, sizeof(struct serial_icounter_struct)); + if (ioctl(pComm->fd, TIOCGICOUNT, ¤tCounters) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + CommLog_Print(WLOG_WARN, " could not read counters."); + + if (pComm->permissive) + { + /* Errors and events based on counters could not be + * detected but keep on. + */ + ZeroMemory(¤tCounters, sizeof(struct serial_icounter_struct)); + } + else + { + SetLastError(ERROR_IO_DEVICE); + LeaveCriticalSection(&pComm->EventsLock); + return FALSE; + } + } + + /* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* > + * pComm->counters.*) thinking the counters can loop */ + + /* Errors */ + + if (currentCounters.buf_overrun != pComm->counters.buf_overrun) + { + pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN; + } + + if (currentCounters.overrun != pComm->counters.overrun) + { + pCommstatus->Errors |= SERIAL_ERROR_OVERRUN; + pComm->PendingEvents |= SERIAL_EV_ERR; + } + + if (currentCounters.brk != pComm->counters.brk) + { + pCommstatus->Errors |= SERIAL_ERROR_BREAK; + pComm->PendingEvents |= SERIAL_EV_BREAK; + } + + if (currentCounters.parity != pComm->counters.parity) + { + pCommstatus->Errors |= SERIAL_ERROR_PARITY; + pComm->PendingEvents |= SERIAL_EV_ERR; + } + + if (currentCounters.frame != pComm->counters.frame) + { + pCommstatus->Errors |= SERIAL_ERROR_FRAMING; + pComm->PendingEvents |= SERIAL_EV_ERR; + } + + /* HoldReasons */ + + /* TODO: SERIAL_TX_WAITING_FOR_CTS */ + + /* TODO: SERIAL_TX_WAITING_FOR_DSR */ + + /* TODO: SERIAL_TX_WAITING_FOR_DCD */ + + /* TODO: SERIAL_TX_WAITING_FOR_XON */ + + /* TODO: SERIAL_TX_WAITING_ON_BREAK, see LCR's bit 6 */ + + /* TODO: SERIAL_TX_WAITING_XOFF_SENT */ + + /* AmountInInQueue */ + + if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCINQ ioctl failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + + LeaveCriticalSection(&pComm->EventsLock); + return FALSE; + } + + /* AmountInOutQueue */ + + if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCOUTQ ioctl failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + + LeaveCriticalSection(&pComm->EventsLock); + return FALSE; + } + + /* BOOLEAN EofReceived; FIXME: once EofChar supported */ + + /* BOOLEAN WaitForImmediate; TODO: once IOCTL_SERIAL_IMMEDIATE_CHAR fully supported */ + + /* other events based on counters */ + + if (currentCounters.rx != pComm->counters.rx) + { + pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR; + } + + if ((currentCounters.tx != pComm->counters.tx) && /* at least a transmission occurred AND ...*/ + (pCommstatus->AmountInOutQueue == 0)) /* output bufer is now empty */ + { + pComm->PendingEvents |= SERIAL_EV_TXEMPTY; + } + else + { + /* FIXME: "now empty" from the specs is ambiguous, need to track previous completed + * transmission? */ + pComm->PendingEvents &= ~SERIAL_EV_TXEMPTY; + } + + if (currentCounters.cts != pComm->counters.cts) + { + pComm->PendingEvents |= SERIAL_EV_CTS; + } + + if (currentCounters.dsr != pComm->counters.dsr) + { + pComm->PendingEvents |= SERIAL_EV_DSR; + } + + if (currentCounters.dcd != pComm->counters.dcd) + { + pComm->PendingEvents |= SERIAL_EV_RLSD; + } + + if (currentCounters.rng != pComm->counters.rng) + { + pComm->PendingEvents |= SERIAL_EV_RING; + } + + if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE)) + { + pComm->PendingEvents |= SERIAL_EV_RX80FULL; + } + else + { + /* FIXME: "is 80 percent full" from the specs is ambiguous, need to track when it previously + * * occurred? */ + pComm->PendingEvents &= ~SERIAL_EV_RX80FULL; + } + + pComm->counters = currentCounters; + + LeaveCriticalSection(&pComm->EventsLock); + return TRUE; +} + +static BOOL _refresh_PendingEvents(WINPR_COMM* pComm) +{ + SERIAL_STATUS serialStatus; + + /* NB: also ensures PendingEvents to be up to date */ + ZeroMemory(&serialStatus, sizeof(SERIAL_STATUS)); + if (!_get_commstatus(pComm, &serialStatus)) + { + return FALSE; + } + + return TRUE; +} + +static void _consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event) +{ + if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event)) + { + pComm->PendingEvents &= ~event; /* consumed */ + *pOutputMask |= event; + } +} + +/* + * NB: see also: _set_wait_mask() + */ +static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask) +{ + WINPR_ASSERT(*pOutputMask == 0); + + EnterCriticalSection(&pComm->EventsLock); + pComm->PendingEvents |= SERIAL_EV_WINPR_WAITING; + LeaveCriticalSection(&pComm->EventsLock); + + while (TRUE) + { + /* NB: EventsLock also used by _refresh_PendingEvents() */ + if (!_refresh_PendingEvents(pComm)) + { + EnterCriticalSection(&pComm->EventsLock); + pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING; + LeaveCriticalSection(&pComm->EventsLock); + return FALSE; + } + + /* NB: ensure to leave the critical section before to return */ + EnterCriticalSection(&pComm->EventsLock); + + if (pComm->PendingEvents & SERIAL_EV_WINPR_STOP) + { + pComm->PendingEvents &= ~SERIAL_EV_WINPR_STOP; + + /* pOutputMask must remain empty but should + * not have been modified. + * + * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx + */ + WINPR_ASSERT(*pOutputMask == 0); + + pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING; + LeaveCriticalSection(&pComm->EventsLock); + return TRUE; + } + + _consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR); + _consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG); + _consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY); + _consume_event(pComm, pOutputMask, SERIAL_EV_CTS); + _consume_event(pComm, pOutputMask, SERIAL_EV_DSR); + _consume_event(pComm, pOutputMask, SERIAL_EV_RLSD); + _consume_event(pComm, pOutputMask, SERIAL_EV_BREAK); + _consume_event(pComm, pOutputMask, SERIAL_EV_ERR); + _consume_event(pComm, pOutputMask, SERIAL_EV_RING); + _consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL); + + LeaveCriticalSection(&pComm->EventsLock); + + /* NOTE: PendingEvents can be modified from now on but + * not pOutputMask */ + + if (*pOutputMask != 0) + { + /* at least an event occurred */ + + EnterCriticalSection(&pComm->EventsLock); + pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING; + LeaveCriticalSection(&pComm->EventsLock); + return TRUE; + } + + /* waiting for a modification of PendingEvents. + * + * NOTE: previously used a semaphore but used + * sem_timedwait() anyway. Finally preferred a simpler + * solution with Sleep() without the burden of the + * semaphore initialization and destroying. + */ + + Sleep(100); /* 100 ms */ + } +} + +static BOOL _set_break_on(WINPR_COMM* pComm) +{ + if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +static BOOL _set_break_off(WINPR_COMM* pComm) +{ + if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +static BOOL _set_xoff(WINPR_COMM* pComm) +{ + if (tcflow(pComm->fd, TCIOFF) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TCIOFF failure, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +static BOOL _set_xon(WINPR_COMM* pComm) +{ + if (tcflow(pComm->fd, TCION) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TCION failure, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + return TRUE; +} + +static BOOL _get_dtrrts(WINPR_COMM* pComm, ULONG* pMask) +{ + UINT32 lines = 0; + if (ioctl(pComm->fd, TIOCMGET, &lines) < 0) + { + char ebuffer[256] = { 0 }; + CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + *pMask = 0; + + if (!(lines & TIOCM_DTR)) + *pMask |= SERIAL_DTR_STATE; + if (!(lines & TIOCM_RTS)) + *pMask |= SERIAL_RTS_STATE; + + return TRUE; +} + +static BOOL _config_size(WINPR_COMM* pComm, ULONG* pSize) +{ + /* http://msdn.microsoft.com/en-us/library/ff546548%28v=vs.85%29.aspx */ + if (!pSize) + return FALSE; + + *pSize = 0; + return TRUE; +} + +static BOOL _immediate_char(WINPR_COMM* pComm, const UCHAR* pChar) +{ + BOOL result = 0; + DWORD nbBytesWritten = -1; + + /* FIXME: CommWriteFile uses a critical section, shall it be + * interrupted? + * + * FIXME: see also _get_commstatus()'s WaitForImmediate boolean + */ + + result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL); + + WINPR_ASSERT(nbBytesWritten == 1); + + return result; +} + +static BOOL _reset_device(WINPR_COMM* pComm) +{ + /* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */ + return TRUE; +} + +static SERIAL_DRIVER _SerialSys = { + .id = SerialDriverSerialSys, + .name = _T("Serial.sys"), + .set_baud_rate = _set_baud_rate, + .get_baud_rate = _get_baud_rate, + .get_properties = _get_properties, + .set_serial_chars = _set_serial_chars, + .get_serial_chars = _get_serial_chars, + .set_line_control = _set_line_control, + .get_line_control = _get_line_control, + .set_handflow = _set_handflow, + .get_handflow = _get_handflow, + .set_timeouts = _set_timeouts, + .get_timeouts = _get_timeouts, + .set_dtr = _set_dtr, + .clear_dtr = _clear_dtr, + .set_rts = _set_rts, + .clear_rts = _clear_rts, + .get_modemstatus = _get_modemstatus, + .set_wait_mask = _set_wait_mask, + .get_wait_mask = _get_wait_mask, + .wait_on_mask = _wait_on_mask, + .set_queue_size = _set_queue_size, + .purge = _purge, + .get_commstatus = _get_commstatus, + .set_break_on = _set_break_on, + .set_break_off = _set_break_off, + .set_xoff = _set_xoff, + .set_xon = _set_xon, + .get_dtrrts = _get_dtrrts, + .config_size = _config_size, + .immediate_char = _immediate_char, + .reset_device = _reset_device, +}; + +SERIAL_DRIVER* SerialSys_s(void) +{ + return &_SerialSys; +} + +#endif /* __linux__ */ diff --git a/winpr/libwinpr/comm/comm_serial_sys.h b/winpr/libwinpr/comm/comm_serial_sys.h new file mode 100644 index 0000000..abb3f01 --- /dev/null +++ b/winpr/libwinpr/comm/comm_serial_sys.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMM_SERIAL_SYS_H +#define COMM_SERIAL_SYS_H + +#if defined __linux__ && !defined ANDROID + +#include "comm_ioctl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERIAL_DRIVER* SerialSys_s(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __linux__ */ + +#endif /* COMM_SERIAL_SYS_H */ diff --git a/winpr/libwinpr/comm/test/CMakeLists.txt b/winpr/libwinpr/comm/test/CMakeLists.txt new file mode 100644 index 0000000..f5ae406 --- /dev/null +++ b/winpr/libwinpr/comm/test/CMakeLists.txt @@ -0,0 +1,35 @@ + +set(MODULE_NAME "TestComm") +set(MODULE_PREFIX "TEST_COMM") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestCommDevice.c + TestCommConfig.c + TestGetCommState.c + TestSetCommState.c + TestSerialChars.c + TestControlSettings.c + TestHandflow.c + TestTimeouts.c + TestCommMonitor.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) + set_tests_properties(${TestName} PROPERTIES LABELS "comm" ) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/comm/test/TestCommConfig.c b/winpr/libwinpr/comm/test/TestCommConfig.c new file mode 100644 index 0000000..c405f14 --- /dev/null +++ b/winpr/libwinpr/comm/test/TestCommConfig.c @@ -0,0 +1,148 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +int TestCommConfig(int argc, char* argv[]) +{ + DCB dcb = { 0 }; + HANDLE hComm; + BOOL success; + LPCSTR lpFileName = "\\\\.\\COM1"; + COMMPROP commProp = { 0 }; + struct stat statbuf = { 0 }; + + hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (hComm && (hComm != INVALID_HANDLE_VALUE)) + { + fprintf(stderr, + "CreateFileA failure: could create a handle on a not yet defined device: %s\n", + lpFileName); + return EXIT_FAILURE; + } + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + success = DefineCommDevice(lpFileName, "/dev/ttyS0"); + if (!success) + { + fprintf(stderr, "DefineCommDevice failure: %s\n", lpFileName); + return EXIT_FAILURE; + } + + hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, /* invalid parmaeter */ + NULL, CREATE_NEW, /* invalid parameter */ + 0, (HANDLE)1234); /* invalid parmaeter */ + if (hComm != INVALID_HANDLE_VALUE) + { + fprintf(stderr, + "CreateFileA failure: could create a handle with some invalid parameters %s\n", + lpFileName); + return EXIT_FAILURE; + } + + hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (!hComm || (hComm == INVALID_HANDLE_VALUE)) + { + fprintf(stderr, "CreateFileA failure: %s GetLastError() = 0x%08x\n", lpFileName, + GetLastError()); + return EXIT_FAILURE; + } + + /* TODO: a second call to CreateFileA should failed and + * GetLastError should return ERROR_SHARING_VIOLATION */ + + dcb.DCBlength = sizeof(DCB); + success = GetCommState(hComm, &dcb); + if (!success) + { + fprintf(stderr, "GetCommState failure: GetLastError() = Ox%x\n", GetLastError()); + return EXIT_FAILURE; + } + + fprintf(stderr, + "BaudRate: %" PRIu32 " ByteSize: %" PRIu8 " Parity: %" PRIu8 " StopBits: %" PRIu8 "\n", + dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.StopBits); + + if (!GetCommProperties(hComm, &commProp)) + { + fprintf(stderr, "GetCommProperties failure: GetLastError(): 0x%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + if ((commProp.dwSettableBaud & BAUD_57600) <= 0) + { + fprintf(stderr, "BAUD_57600 unsupported!\n"); + return EXIT_FAILURE; + } + + if ((commProp.dwSettableBaud & BAUD_14400) > 0) + { + fprintf(stderr, "BAUD_14400 supported!\n"); + return EXIT_FAILURE; + } + + dcb.BaudRate = CBR_57600; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + + success = SetCommState(hComm, &dcb); + + if (!success) + { + fprintf(stderr, "SetCommState failure: GetLastError() = 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + success = GetCommState(hComm, &dcb); + + if (!success) + { + fprintf(stderr, "GetCommState failure: GetLastError() = 0x%x\n", GetLastError()); + return 0; + } + + if ((dcb.BaudRate != CBR_57600) || (dcb.ByteSize != 8) || (dcb.Parity != NOPARITY) || + (dcb.StopBits != ONESTOPBIT)) + { + fprintf(stderr, + "Got an unexpeted value among: BaudRate: %" PRIu32 " ByteSize: %" PRIu8 + " Parity: %" PRIu8 " StopBits: %" PRIu8 "\n", + dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.StopBits); + } + + CloseHandle(hComm); + + return 0; +} diff --git a/winpr/libwinpr/comm/test/TestCommDevice.c b/winpr/libwinpr/comm/test/TestCommDevice.c new file mode 100644 index 0000000..09eb1c2 --- /dev/null +++ b/winpr/libwinpr/comm/test/TestCommDevice.c @@ -0,0 +1,115 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +static int test_CommDevice(LPCTSTR lpDeviceName, BOOL expectedResult) +{ + BOOL result; + TCHAR lpTargetPath[MAX_PATH]; + size_t tcslen; + + result = DefineCommDevice(lpDeviceName, _T("/dev/test")); + if ((!expectedResult && result) || (expectedResult && !result)) /* logical XOR */ + { + _tprintf(_T("DefineCommDevice failure: device name: %s, expected result: %s, result: %s\n"), + lpDeviceName, (expectedResult ? "TRUE" : "FALSE"), (result ? "TRUE" : "FALSE")); + + return FALSE; + } + + result = IsCommDevice(lpDeviceName); + if ((!expectedResult && result) || (expectedResult && !result)) /* logical XOR */ + { + _tprintf(_T("IsCommDevice failure: device name: %s, expected result: %s, result: %s\n"), + lpDeviceName, (expectedResult ? "TRUE" : "FALSE"), (result ? "TRUE" : "FALSE")); + + return FALSE; + } + + tcslen = (size_t)QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH); + if (expectedResult) + { + if (tcslen <= _tcslen(lpTargetPath)) /* at least 2 more TCHAR are expected */ + { + _tprintf(_T("QueryCommDevice failure: didn't find the device name: %s\n"), + lpDeviceName); + return FALSE; + } + + if (_tcscmp(_T("/dev/test"), lpTargetPath) != 0) + { + _tprintf( + _T("QueryCommDevice failure: device name: %s, expected result: %s, result: %s\n"), + lpDeviceName, _T("/dev/test"), lpTargetPath); + + return FALSE; + } + + if (lpTargetPath[_tcslen(lpTargetPath) + 1] != 0) + { + _tprintf(_T("QueryCommDevice failure: device name: %s, the second NULL character is ") + _T("missing at the end of the buffer\n"), + lpDeviceName); + return FALSE; + } + } + else + { + if (tcslen > 0) + { + _tprintf(_T("QueryCommDevice failure: device name: %s, expected result: , ") + _T("result: %") _T(PRIuz) _T(" %s\n"), + lpDeviceName, tcslen, lpTargetPath); + + return FALSE; + } + } + + return TRUE; +} + +int TestCommDevice(int argc, char* argv[]) +{ + if (!test_CommDevice(_T("COM0"), FALSE)) + return EXIT_FAILURE; + + if (!test_CommDevice(_T("COM1"), TRUE)) + return EXIT_FAILURE; + + if (!test_CommDevice(_T("COM1"), TRUE)) + return EXIT_FAILURE; + + if (!test_CommDevice(_T("COM10"), FALSE)) + return EXIT_FAILURE; + + if (!test_CommDevice(_T("\\\\.\\COM5"), TRUE)) + return EXIT_FAILURE; + + if (!test_CommDevice(_T("\\\\.\\COM10"), TRUE)) + return EXIT_FAILURE; + + if (!test_CommDevice(_T("\\\\.COM10"), FALSE)) + return EXIT_FAILURE; + + return 0; +} diff --git a/winpr/libwinpr/comm/test/TestCommMonitor.c b/winpr/libwinpr/comm/test/TestCommMonitor.c new file mode 100644 index 0000000..fe28a86 --- /dev/null +++ b/winpr/libwinpr/comm/test/TestCommMonitor.c @@ -0,0 +1,70 @@ + +#include +#include +#include +#include +#include + +int TestCommMonitor(int argc, char* argv[]) +{ + HANDLE hComm; + DWORD dwError; + BOOL fSuccess; + DWORD dwEvtMask; + OVERLAPPED overlapped = { 0 }; + LPCSTR lpFileName = "\\\\.\\COM1"; + + hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + + if (!hComm || (hComm == INVALID_HANDLE_VALUE)) + { + printf("CreateFileA failure: %s\n", lpFileName); + return -1; + } + + fSuccess = SetCommMask(hComm, EV_CTS | EV_DSR); + + if (!fSuccess) + { + printf("SetCommMask failure: GetLastError() = %" PRIu32 "\n", GetLastError()); + return -1; + } + + if (!(overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("CreateEvent failed: GetLastError() = %" PRIu32 "\n", GetLastError()); + return -1; + } + + if (WaitCommEvent(hComm, &dwEvtMask, &overlapped)) + { + if (dwEvtMask & EV_DSR) + { + printf("EV_DSR\n"); + } + + if (dwEvtMask & EV_CTS) + { + printf("EV_CTS\n"); + } + } + else + { + dwError = GetLastError(); + + if (dwError == ERROR_IO_PENDING) + { + printf("ERROR_IO_PENDING\n"); + } + else + { + printf("WaitCommEvent failure: GetLastError() = %" PRIu32 "\n", dwError); + return -1; + } + } + + CloseHandle(hComm); + + return 0; +} diff --git a/winpr/libwinpr/comm/test/TestControlSettings.c b/winpr/libwinpr/comm/test/TestControlSettings.c new file mode 100644 index 0000000..7611dbb --- /dev/null +++ b/winpr/libwinpr/comm/test/TestControlSettings.c @@ -0,0 +1,123 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include + +#include "../comm.h" + +int TestControlSettings(int argc, char* argv[]) +{ + struct stat statbuf; + BOOL result; + HANDLE hComm; + DCB dcb; + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + result = DefineCommDevice("COM1", "/dev/ttyS0"); + if (!result) + { + fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComm == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + ZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!GetCommState(hComm, &dcb)) + { + fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError()); + return FALSE; + } + + /* Test 1 */ + + dcb.ByteSize = 5; + dcb.StopBits = ONESTOPBIT; + dcb.Parity = MARKPARITY; + + if (!SetCommState(hComm, &dcb)) + { + fprintf(stderr, "SetCommState failure; GetLastError(): %08x\n", GetLastError()); + return FALSE; + } + + ZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!GetCommState(hComm, &dcb)) + { + fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError()); + return FALSE; + } + + if ((dcb.ByteSize != 5) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != MARKPARITY)) + { + fprintf(stderr, "test1 failed.\n"); + return FALSE; + } + + /* Test 2 */ + + dcb.ByteSize = 8; + dcb.StopBits = ONESTOPBIT; + dcb.Parity = NOPARITY; + + if (!SetCommState(hComm, &dcb)) + { + fprintf(stderr, "SetCommState failure; GetLastError(): %08x\n", GetLastError()); + return FALSE; + } + + ZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!GetCommState(hComm, &dcb)) + { + fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError()); + return FALSE; + } + + if ((dcb.ByteSize != 8) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != NOPARITY)) + { + fprintf(stderr, "test2 failed.\n"); + return FALSE; + } + + if (!CloseHandle(hComm)) + { + fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/winpr/libwinpr/comm/test/TestGetCommState.c b/winpr/libwinpr/comm/test/TestGetCommState.c new file mode 100644 index 0000000..909e61a --- /dev/null +++ b/winpr/libwinpr/comm/test/TestGetCommState.c @@ -0,0 +1,138 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include "../comm.h" + +static BOOL test_generic(HANDLE hComm) +{ + DCB dcb, *pDcb; + BOOL result; + + ZeroMemory(&dcb, sizeof(DCB)); + result = GetCommState(hComm, &dcb); + if (result) + { + printf("GetCommState failure, should have returned false because dcb.DCBlength has been " + "let uninitialized\n"); + return FALSE; + } + + ZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB) / 2; /* improper value */ + result = GetCommState(hComm, &dcb); + if (result) + { + printf("GetCommState failure, should have return false because dcb.DCBlength was not " + "correctly initialized\n"); + return FALSE; + } + + ZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + result = GetCommState(hComm, &dcb); + if (!result) + { + printf("GetCommState failure: Ox%x, with adjusted DCBlength\n", GetLastError()); + return FALSE; + } + + pDcb = (DCB*)calloc(2, sizeof(DCB)); + if (!pDcb) + return FALSE; + pDcb->DCBlength = sizeof(DCB) * 2; + result = GetCommState(hComm, pDcb); + result = result && (pDcb->DCBlength == sizeof(DCB) * 2); + free(pDcb); + if (!result) + { + printf("GetCommState failure: 0x%x, with bigger DCBlength\n", GetLastError()); + return FALSE; + } + + return TRUE; +} + +int TestGetCommState(int argc, char* argv[]) +{ + struct stat statbuf; + BOOL result; + HANDLE hComm; + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + result = DefineCommDevice("COM1", "/dev/ttyS0"); + if (!result) + { + printf("DefineCommDevice failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + hComm = CreateFileA("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (hComm == INVALID_HANDLE_VALUE) + { + printf("CreateFileA failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + if (!test_generic(hComm)) + { + printf("test_generic failure (SerialDriverUnknown)\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerialSys); + if (!test_generic(hComm)) + { + printf("test_generic failure (SerialDriverSerialSys)\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); + if (!test_generic(hComm)) + { + printf("test_generic failure (SerialDriverSerCxSys)\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); + if (!test_generic(hComm)) + { + printf("test_generic failure (SerialDriverSerCx2Sys)\n"); + return EXIT_FAILURE; + } + + if (!CloseHandle(hComm)) + { + fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/winpr/libwinpr/comm/test/TestHandflow.c b/winpr/libwinpr/comm/test/TestHandflow.c new file mode 100644 index 0000000..ad7fe3c --- /dev/null +++ b/winpr/libwinpr/comm/test/TestHandflow.c @@ -0,0 +1,92 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include +#include + +#include "../comm.h" + +static BOOL test_SerialSys(HANDLE hComm) +{ + // TMP: TODO: + return TRUE; +} + +int TestHandflow(int argc, char* argv[]) +{ + struct stat statbuf; + BOOL result; + HANDLE hComm; + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + result = DefineCommDevice("COM1", "/dev/ttyS0"); + if (!result) + { + fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComm == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerialSys); + if (!test_SerialSys(hComm)) + { + fprintf(stderr, "test_SerCxSys failure\n"); + return EXIT_FAILURE; + } + + /* _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); */ + /* if (!test_SerCxSys(hComm)) */ + /* { */ + /* fprintf(stderr, "test_SerCxSys failure\n"); */ + /* return EXIT_FAILURE; */ + /* } */ + + /* _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); */ + /* if (!test_SerCx2Sys(hComm)) */ + /* { */ + /* fprintf(stderr, "test_SerCxSys failure\n"); */ + /* return EXIT_FAILURE; */ + /* } */ + + if (!CloseHandle(hComm)) + { + fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/winpr/libwinpr/comm/test/TestSerialChars.c b/winpr/libwinpr/comm/test/TestSerialChars.c new file mode 100644 index 0000000..b235321 --- /dev/null +++ b/winpr/libwinpr/comm/test/TestSerialChars.c @@ -0,0 +1,178 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include +#include + +#include "../comm.h" + +static BOOL test_SerCxSys(HANDLE hComm) +{ + DCB dcb = { 0 }; + UCHAR XonChar, XoffChar; + + struct termios currentTermios = { 0 }; + + if (tcgetattr(((WINPR_COMM*)hComm)->fd, ¤tTermios) < 0) + { + fprintf(stderr, "tcgetattr failure.\n"); + return FALSE; + } + + dcb.DCBlength = sizeof(DCB); + if (!GetCommState(hComm, &dcb)) + { + fprintf(stderr, "GetCommState failure, GetLastError(): 0x%08x\n", GetLastError()); + return FALSE; + } + + if ((dcb.XonChar == '\0') || (dcb.XoffChar == '\0')) + { + fprintf(stderr, "test_SerCxSys failure, expected XonChar and XoffChar to be set\n"); + return FALSE; + } + + /* retrieve Xon/Xoff chars */ + if ((dcb.XonChar != currentTermios.c_cc[VSTART]) || + (dcb.XoffChar != currentTermios.c_cc[VSTOP])) + { + fprintf(stderr, "test_SerCxSys failure, could not retrieve XonChar and XoffChar\n"); + return FALSE; + } + + /* swap XonChar/XoffChar */ + + XonChar = dcb.XonChar; + XoffChar = dcb.XoffChar; + dcb.XonChar = XoffChar; + dcb.XoffChar = XonChar; + if (!SetCommState(hComm, &dcb)) + { + fprintf(stderr, "SetCommState failure, GetLastError(): 0x%08x\n", GetLastError()); + return FALSE; + } + + ZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!GetCommState(hComm, &dcb)) + { + fprintf(stderr, "GetCommState failure, GetLastError(): 0x%08x\n", GetLastError()); + return FALSE; + } + + if ((dcb.XonChar != XoffChar) || (dcb.XoffChar != XonChar)) + { + fprintf(stderr, "test_SerCxSys, expected XonChar and XoffChar to be swapped\n"); + return FALSE; + } + + /* same XonChar / XoffChar */ + dcb.XonChar = dcb.XoffChar; + if (SetCommState(hComm, &dcb)) + { + fprintf(stderr, "test_SerCxSys failure, SetCommState() was supposed to failed because " + "XonChar and XoffChar are the same\n"); + return FALSE; + } + if (GetLastError() != ERROR_INVALID_PARAMETER) + { + fprintf(stderr, "test_SerCxSys failure, SetCommState() was supposed to failed with " + "GetLastError()=ERROR_INVALID_PARAMETER\n"); + return FALSE; + } + + return TRUE; +} + +static BOOL test_SerCx2Sys(HANDLE hComm) +{ + DCB dcb = { 0 }; + + dcb.DCBlength = sizeof(DCB); + if (!GetCommState(hComm, &dcb)) + { + fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError()); + return FALSE; + } + + if ((dcb.ErrorChar != '\0') || (dcb.EofChar != '\0') || (dcb.EvtChar != '\0') || + (dcb.XonChar != '\0') || (dcb.XoffChar != '\0')) + { + fprintf(stderr, "test_SerCx2Sys failure, expected all characters to be: '\\0'\n"); + return FALSE; + } + + return TRUE; +} + +int TestSerialChars(int argc, char* argv[]) +{ + struct stat statbuf; + BOOL result; + HANDLE hComm; + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + result = DefineCommDevice("COM1", "/dev/ttyS0"); + if (!result) + { + fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComm == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); + if (!test_SerCxSys(hComm)) + { + fprintf(stderr, "test_SerCxSys failure\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); + if (!test_SerCx2Sys(hComm)) + { + fprintf(stderr, "test_SerCxSys failure\n"); + return EXIT_FAILURE; + } + + if (!CloseHandle(hComm)) + { + fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/winpr/libwinpr/comm/test/TestSetCommState.c b/winpr/libwinpr/comm/test/TestSetCommState.c new file mode 100644 index 0000000..0204058 --- /dev/null +++ b/winpr/libwinpr/comm/test/TestSetCommState.c @@ -0,0 +1,332 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include "../comm.h" + +static void init_empty_dcb(DCB* pDcb) +{ + WINPR_ASSERT(pDcb); + + ZeroMemory(pDcb, sizeof(DCB)); + pDcb->DCBlength = sizeof(DCB); + pDcb->XonChar = 1; + pDcb->XoffChar = 2; +} + +static BOOL test_fParity(HANDLE hComm) +{ + DCB dcb; + BOOL result; + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + /* test 1 */ + dcb.fParity = TRUE; + result = SetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "SetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + if (!dcb.fParity) + { + fprintf(stderr, "unexpected fParity: %" PRIu32 " instead of TRUE\n", dcb.fParity); + return FALSE; + } + + /* test 2 */ + dcb.fParity = FALSE; + result = SetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "SetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + if (dcb.fParity) + { + fprintf(stderr, "unexpected fParity: %" PRIu32 " instead of FALSE\n", dcb.fParity); + return FALSE; + } + + /* test 3 (redo test 1) */ + dcb.fParity = TRUE; + result = SetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "SetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError()); + return FALSE; + } + + if (!dcb.fParity) + { + fprintf(stderr, "unexpected fParity: %" PRIu32 " instead of TRUE\n", dcb.fParity); + return FALSE; + } + + return TRUE; +} + +static BOOL test_SerialSys(HANDLE hComm) +{ + DCB dcb; + BOOL result; + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError()); + return FALSE; + } + + /* Test 1 */ + dcb.BaudRate = CBR_115200; + result = SetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "SetCommState failure: 0x%08x\n", GetLastError()); + return FALSE; + } + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError()); + return FALSE; + } + if (dcb.BaudRate != CBR_115200) + { + fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (CBR_115200)\n", + CBR_115200); + return FALSE; + } + + /* Test 2 using a defferent baud rate */ + + dcb.BaudRate = CBR_57600; + result = SetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError()); + return FALSE; + } + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError()); + return FALSE; + } + if (dcb.BaudRate != CBR_57600) + { + fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (CBR_57600)\n", CBR_57600); + return FALSE; + } + + /* Test 3 using an unsupported baud rate on Linux */ + dcb.BaudRate = CBR_128000; + result = SetCommState(hComm, &dcb); + if (result) + { + fprintf(stderr, "SetCommState failure: unexpected support of BaudRate=%d (CBR_128000)\n", + CBR_128000); + return FALSE; + } + + return TRUE; +} + +static BOOL test_SerCxSys(HANDLE hComm) +{ + /* as of today there is no difference */ + return test_SerialSys(hComm); +} + +static BOOL test_SerCx2Sys(HANDLE hComm) +{ + /* as of today there is no difference */ + return test_SerialSys(hComm); +} + +static BOOL test_generic(HANDLE hComm) +{ + DCB dcb, dcb2; + BOOL result; + + init_empty_dcb(&dcb); + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError()); + return FALSE; + } + + /* Checks whether we get the same information before and after SetCommState */ + memcpy(&dcb2, &dcb, sizeof(DCB)); + result = SetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "SetCommState failure: 0x%08x\n", GetLastError()); + return FALSE; + } + + result = GetCommState(hComm, &dcb); + if (!result) + { + fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError()); + return FALSE; + } + + if (memcmp(&dcb, &dcb2, sizeof(DCB)) != 0) + { + fprintf(stderr, + "DCB is different after SetCommState() whereas it should have not changed\n"); + return FALSE; + } + + // TODO: a more complete and generic test using GetCommProperties() + + /* TMP: TODO: fBinary tests */ + + /* fParity tests */ + if (!test_fParity(hComm)) + { + fprintf(stderr, "test_fParity failure\n"); + return FALSE; + } + + return TRUE; +} + +int TestSetCommState(int argc, char* argv[]) +{ + struct stat statbuf; + BOOL result; + HANDLE hComm; + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + result = DefineCommDevice("COM1", "/dev/ttyS0"); + if (!result) + { + fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComm == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + if (!test_generic(hComm)) + { + fprintf(stderr, "test_generic failure (SerialDriverUnknown)\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerialSys); + if (!test_generic(hComm)) + { + fprintf(stderr, "test_generic failure (SerialDriverSerialSys)\n"); + return EXIT_FAILURE; + } + if (!test_SerialSys(hComm)) + { + fprintf(stderr, "test_SerialSys failure\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); + if (!test_generic(hComm)) + { + fprintf(stderr, "test_generic failure (SerialDriverSerCxSys)\n"); + return EXIT_FAILURE; + } + if (!test_SerCxSys(hComm)) + { + fprintf(stderr, "test_SerCxSys failure\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); + if (!test_generic(hComm)) + { + fprintf(stderr, "test_generic failure (SerialDriverSerCx2Sys)\n"); + return EXIT_FAILURE; + } + if (!test_SerCx2Sys(hComm)) + { + fprintf(stderr, "test_SerCx2Sys failure\n"); + return EXIT_FAILURE; + } + + if (!CloseHandle(hComm)) + { + fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/winpr/libwinpr/comm/test/TestTimeouts.c b/winpr/libwinpr/comm/test/TestTimeouts.c new file mode 100644 index 0000000..c2d3be8 --- /dev/null +++ b/winpr/libwinpr/comm/test/TestTimeouts.c @@ -0,0 +1,138 @@ +/** + * WinPR: Windows Portable Runtime + * Serial Communication API + * + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include +#include + +#include "../comm.h" + +static BOOL test_generic(HANDLE hComm) +{ + COMMTIMEOUTS timeouts = { 0 }, timeouts2 = { 0 }; + + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutMultiplier = 2; + timeouts.ReadTotalTimeoutConstant = 3; + timeouts.WriteTotalTimeoutMultiplier = 4; + timeouts.WriteTotalTimeoutConstant = 5; + + if (!SetCommTimeouts(hComm, &timeouts)) + { + fprintf(stderr, "SetCommTimeouts failure, GetLastError: 0x%08x\n", GetLastError()); + return FALSE; + } + + if (!GetCommTimeouts(hComm, &timeouts2)) + { + fprintf(stderr, "GetCommTimeouts failure, GetLastError: 0x%08x\n", GetLastError()); + return FALSE; + } + + if (memcmp(&timeouts, &timeouts2, sizeof(COMMTIMEOUTS)) != 0) + { + fprintf(stderr, "TestTimeouts failure, didn't get back the same timeouts.\n"); + return FALSE; + } + + /* not supported combination */ + timeouts.ReadIntervalTimeout = MAXULONG; + timeouts.ReadTotalTimeoutConstant = MAXULONG; + if (SetCommTimeouts(hComm, &timeouts)) + { + fprintf(stderr, + "SetCommTimeouts succeeded with ReadIntervalTimeout and ReadTotalTimeoutConstant " + "set to MAXULONG. GetLastError: 0x%08x\n", + GetLastError()); + return FALSE; + } + + if (GetLastError() != ERROR_INVALID_PARAMETER) + { + fprintf(stderr, + "SetCommTimeouts failure, expected GetLastError to return ERROR_INVALID_PARAMETER " + "and got: 0x%08x\n", + GetLastError()); + return FALSE; + } + + return TRUE; +} + +int TestTimeouts(int argc, char* argv[]) +{ + struct stat statbuf; + BOOL result; + HANDLE hComm; + + if (stat("/dev/ttyS0", &statbuf) < 0) + { + fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n"); + return EXIT_SUCCESS; + } + + result = DefineCommDevice("COM1", "/dev/ttyS0"); + if (!result) + { + fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hComm == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError()); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerialSys); + if (!test_generic(hComm)) + { + fprintf(stderr, "test_SerialSys failure\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); + if (!test_generic(hComm)) + { + fprintf(stderr, "test_SerCxSys failure\n"); + return EXIT_FAILURE; + } + + _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); + if (!test_generic(hComm)) + { + fprintf(stderr, "test_SerCx2Sys failure\n"); + return EXIT_FAILURE; + } + + if (!CloseHandle(hComm)) + { + fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt new file mode 100644 index 0000000..d82e64f --- /dev/null +++ b/winpr/libwinpr/crt/CMakeLists.txt @@ -0,0 +1,46 @@ +# WinPR: Windows Portable Runtime +# libwinpr-crt cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set (CRT_FILES alignment.c + conversion.c + buffer.c + memory.c + unicode.c + string.c) + +if(WITH_UNICODE_BUILTIN) + list(APPEND CRT_FILES unicode_builtin.c) +else() + IF (ANDROID) + list(APPEND CRT_FILES unicode_android.c) + elseif (NOT APPLE AND NOT WIN32) + find_package(ICU REQUIRED i18n uc io data) + list(APPEND CRT_FILES unicode_icu.c) + winpr_include_directory_add(${ICU_INCLUDE_DIRS}) + winpr_library_add_private(${ICU_LIBRARIES}) + elseif(APPLE) + list(APPEND CRT_FILES unicode_apple.m) + find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED) + winpr_library_add_private(${FOUNDATION_FRAMEWORK}) + endif() +endif() + +winpr_module_add(${CRT_FILES}) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/crt/ModuleOptions.cmake b/winpr/libwinpr/crt/ModuleOptions.cmake new file mode 100644 index 0000000..4530a74 --- /dev/null +++ b/winpr/libwinpr/crt/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "crt") +set(MINWIN_LONG_NAME "Microsoft C Run-Time") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/crt/alignment.c b/winpr/libwinpr/crt/alignment.c new file mode 100644 index 0000000..e313c2d --- /dev/null +++ b/winpr/libwinpr/crt/alignment.c @@ -0,0 +1,268 @@ +/** + * WinPR: Windows Portable Runtime + * Data Alignment + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +/* Data Alignment: http://msdn.microsoft.com/en-us/library/fs9stz4e/ */ + +#if !defined(_WIN32) || (defined(__MINGW32__) && !defined(_UCRT)) + +#include +#include + +#define WINPR_ALIGNED_MEM_SIGNATURE 0x0BA0BAB + +#define WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(_memptr) \ + (WINPR_ALIGNED_MEM*)(((size_t)(((BYTE*)_memptr) - sizeof(WINPR_ALIGNED_MEM)))) + +#include + +#if defined(__APPLE__) +#include +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("crt") + +struct winpr_aligned_mem +{ + UINT32 sig; + size_t size; + void* base_addr; +}; +typedef struct winpr_aligned_mem WINPR_ALIGNED_MEM; + +void* winpr_aligned_malloc(size_t size, size_t alignment) +{ + return winpr_aligned_offset_malloc(size, alignment, 0); +} + +void* winpr_aligned_calloc(size_t count, size_t size, size_t alignment) +{ + return winpr_aligned_recalloc(NULL, count, size, alignment); +} + +void* winpr_aligned_realloc(void* memblock, size_t size, size_t alignment) +{ + return winpr_aligned_offset_realloc(memblock, size, alignment, 0); +} + +void* winpr_aligned_recalloc(void* memblock, size_t num, size_t size, size_t alignment) +{ + return winpr_aligned_offset_recalloc(memblock, num, size, alignment, 0); +} + +void* winpr_aligned_offset_malloc(size_t size, size_t alignment, size_t offset) +{ + size_t header = 0; + size_t alignsize = 0; + uintptr_t basesize = 0; + void* base = NULL; + void* memblock = NULL; + WINPR_ALIGNED_MEM* pMem = NULL; + + /* alignment must be a power of 2 */ + if (alignment % 2 == 1) + return NULL; + + /* offset must be less than size */ + if (offset >= size) + return NULL; + + /* minimum alignment is pointer size */ + if (alignment < sizeof(void*)) + alignment = sizeof(void*); + + if (alignment > SIZE_MAX - sizeof(WINPR_ALIGNED_MEM)) + return NULL; + + header = sizeof(WINPR_ALIGNED_MEM) + alignment; + + if (size > SIZE_MAX - header) + return NULL; + + alignsize = size + header; + /* malloc size + alignment to make sure we can align afterwards */ +#if defined(_ISOC11_SOURCE) + base = aligned_alloc(alignment, alignsize); +#elif _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 + if (posix_memalign(&base, alignment, alignsize) != 0) + return NULL; +#else + base = malloc(alignsize); +#endif + if (!base) + return NULL; + + basesize = (uintptr_t)base; + + if ((offset > UINTPTR_MAX) || (header > UINTPTR_MAX - offset) || + (basesize > UINTPTR_MAX - header - offset)) + { + free(base); + return NULL; + } + + memblock = (void*)(((basesize + header + offset) & ~(alignment - 1)) - offset); + pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock); + pMem->sig = WINPR_ALIGNED_MEM_SIGNATURE; + pMem->base_addr = base; + pMem->size = size; + return memblock; +} + +void* winpr_aligned_offset_realloc(void* memblock, size_t size, size_t alignment, size_t offset) +{ + size_t copySize = 0; + void* newMemblock = NULL; + WINPR_ALIGNED_MEM* pMem = NULL; + WINPR_ALIGNED_MEM* pNewMem = NULL; + + if (!memblock) + return winpr_aligned_offset_malloc(size, alignment, offset); + + pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock); + + if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE) + { + WLog_ERR(TAG, + "_aligned_offset_realloc: memory block was not allocated by _aligned_malloc!"); + return NULL; + } + + if (size == 0) + { + winpr_aligned_free(memblock); + return NULL; + } + + newMemblock = winpr_aligned_offset_malloc(size, alignment, offset); + + if (!newMemblock) + return NULL; + + pNewMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(newMemblock); + copySize = (pNewMem->size < pMem->size) ? pNewMem->size : pMem->size; + CopyMemory(newMemblock, memblock, copySize); + winpr_aligned_free(memblock); + return newMemblock; +} + +static INLINE size_t cMIN(size_t a, size_t b) +{ + if (a > b) + return b; + return a; +} + +void* winpr_aligned_offset_recalloc(void* memblock, size_t num, size_t size, size_t alignment, + size_t offset) +{ + char* newMemblock = NULL; + WINPR_ALIGNED_MEM* pMem = NULL; + WINPR_ALIGNED_MEM* pNewMem = NULL; + + if (!memblock) + { + newMemblock = winpr_aligned_offset_malloc(size * num, alignment, offset); + + if (newMemblock) + { + pNewMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(newMemblock); + ZeroMemory(newMemblock, pNewMem->size); + } + + return newMemblock; + } + + pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock); + + if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE) + { + WLog_ERR(TAG, + "_aligned_offset_recalloc: memory block was not allocated by _aligned_malloc!"); + goto fail; + } + + if ((num == 0) || (size == 0)) + goto fail; + + if (pMem->size > (1ull * num * size) + alignment) + return memblock; + + newMemblock = winpr_aligned_offset_malloc(size * num, alignment, offset); + + if (!newMemblock) + goto fail; + + pNewMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(newMemblock); + { + const size_t csize = cMIN(pMem->size, pNewMem->size); + memcpy(newMemblock, memblock, csize); + ZeroMemory(newMemblock + csize, pNewMem->size - csize); + } +fail: + winpr_aligned_free(memblock); + return newMemblock; +} + +size_t winpr_aligned_msize(void* memblock, size_t alignment, size_t offset) +{ + WINPR_ALIGNED_MEM* pMem = NULL; + + if (!memblock) + return 0; + + pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock); + + if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE) + { + WLog_ERR(TAG, "_aligned_msize: memory block was not allocated by _aligned_malloc!"); + return 0; + } + + return pMem->size; +} + +void winpr_aligned_free(void* memblock) +{ + WINPR_ALIGNED_MEM* pMem = NULL; + + if (!memblock) + return; + + pMem = WINPR_ALIGNED_MEM_STRUCT_FROM_PTR(memblock); + + if (pMem->sig != WINPR_ALIGNED_MEM_SIGNATURE) + { + WLog_ERR(TAG, "_aligned_free: memory block was not allocated by _aligned_malloc!"); + return; + } + + free(pMem->base_addr); +} + +#endif /* _WIN32 */ diff --git a/winpr/libwinpr/crt/buffer.c b/winpr/libwinpr/crt/buffer.c new file mode 100644 index 0000000..fc914c9 --- /dev/null +++ b/winpr/libwinpr/crt/buffer.c @@ -0,0 +1,50 @@ +/** + * WinPR: Windows Portable Runtime + * Buffer Manipulation + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/* Buffer Manipulation: http://msdn.microsoft.com/en-us/library/b3893xdy/ */ + +#ifndef _WIN32 + +#include + +errno_t memmove_s(void* dest, size_t numberOfElements, const void* src, size_t count) +{ + if (count > numberOfElements) + return -1; + + memmove(dest, src, count); + + return 0; +} + +errno_t wmemmove_s(WCHAR* dest, size_t numberOfElements, const WCHAR* src, size_t count) +{ + if (count * 2 > numberOfElements) + return -1; + + memmove(dest, src, count * 2); + + return 0; +} + +#endif diff --git a/winpr/libwinpr/crt/casing.c b/winpr/libwinpr/crt/casing.c new file mode 100644 index 0000000..36485ab --- /dev/null +++ b/winpr/libwinpr/crt/casing.c @@ -0,0 +1,726 @@ +/** + * Unicode case mappings + * + * This code is generated by wine's make_unicode script + * which downloads data from unicode.org and produces + * readily usable conversion tables. + * + * After asking permission from Alexandre Julliard in May 2011, + * it was clarified that no copyright was claimed by the wine + * project on the script's generated output. + */ + +#define WINPR_TOLOWERW(_wch) \ + (_wch + winpr_casemap_lower[winpr_casemap_lower[_wch >> 8] + (_wch & 0xFF)]) + +#define WINPR_TOUPPERW(_wch) \ + (_wch + winpr_casemap_upper[winpr_casemap_upper[_wch >> 8] + (_wch & 0xFF)]) + +static const WCHAR winpr_casemap_lower[3807] = { + /* index */ + 0x01bf, 0x02bf, 0x03bf, 0x044f, 0x054f, 0x064f, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x06af, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x07af, 0x08ae, 0x0100, 0x09ab, 0x0100, 0x0100, + 0x0a2f, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0b2f, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0c22, 0x0d00, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0ddf, + /* defaults */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x0041 .. 0x00ff */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x0100 .. 0x01ff */ + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0xff39, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0xff87, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x00d2, 0x0001, 0x0000, + 0x0001, 0x0000, 0x00ce, 0x0001, 0x0000, 0x00cd, 0x00cd, 0x0001, 0x0000, 0x0000, 0x004f, 0x00ca, + 0x00cb, 0x0001, 0x0000, 0x00cd, 0x00cf, 0x0000, 0x00d3, 0x00d1, 0x0001, 0x0000, 0x0000, 0x0000, + 0x00d3, 0x00d5, 0x0000, 0x00d6, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x00da, 0x0001, + 0x0000, 0x00da, 0x0000, 0x0000, 0x0001, 0x0000, 0x00da, 0x0001, 0x0000, 0x00d9, 0x00d9, 0x0001, + 0x0000, 0x0001, 0x0000, 0x00db, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0xff9f, 0xffc8, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, + /* 0x0200 .. 0x02ff */ + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xff7e, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2a2b, 0x0001, + 0x0000, 0xff5d, 0x2a28, 0x0000, 0x0000, 0x0001, 0x0000, 0xff3d, 0x0045, 0x0047, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x0370 .. 0x03ff */ + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0026, 0x0000, + 0x0025, 0x0025, 0x0025, 0x0000, 0x0040, 0x0000, 0x003f, 0x003f, 0x0000, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc4, 0x0000, 0x0000, 0x0001, 0x0000, 0xfff9, 0x0001, 0x0000, 0x0000, 0xff7e, 0xff7e, 0xff7e, + /* 0x0400 .. 0x04ff */ + 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, + 0x0050, 0x0050, 0x0050, 0x0050, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x000f, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, + /* 0x0500 .. 0x05ff */ + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x10a0 .. 0x10ff */ + 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, + 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, + 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, 0x1c60, + 0x1c60, 0x1c60, 0x0000, 0x1c60, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1c60, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x1e00 .. 0x1eff */ + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xe241, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, + /* 0x1f01 .. 0x1fff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, + 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xffb6, 0xffb6, 0xfff7, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffaa, 0xffaa, 0xffaa, 0xffaa, 0xfff7, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, + 0xfff8, 0xff9c, 0xff9c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xff90, 0xff90, 0xfff9, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xff80, 0xff82, 0xff82, 0xfff7, + 0x0000, 0x0000, 0x0000, + /* 0x2103 .. 0x21ff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xe2a3, + 0x0000, 0x0000, 0x0000, 0xdf41, 0xdfba, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001c, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, + /* 0x247c .. 0x24ff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001a, 0x001a, + 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, + 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x2c00 .. 0x2cff */ + 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0xd609, 0xf11a, 0xd619, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0xd5e4, 0xd603, 0xd5e1, 0xd5e2, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xd5c1, 0xd5c1, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0xa60d .. 0xa6ff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + /* 0xa722 .. 0xa7ff */ + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x75fc, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x5ad8, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x5abc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0xff21 .. 0xffff */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; +static const WCHAR winpr_casemap_upper[3994] = { + /* index */ + 0x019f, 0x029f, 0x039f, 0x045a, 0x0556, 0x0656, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x06dd, 0x07dc, 0x08dc, 0x0100, 0x09d0, 0x0100, 0x0100, + 0x0a55, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0b3f, 0x0c3f, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0cfe, 0x0ddb, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0e9a, + /* defaults */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x0061 .. 0x00ff */ + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x02e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0x0079, + /* 0x0100 .. 0x01ff */ + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xff18, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xfed4, 0x00c3, 0x0000, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0061, 0x0000, 0x0000, 0x0000, 0xffff, 0x00a3, 0x0000, + 0x0000, 0x0000, 0x0082, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, + 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0038, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, + 0xfffe, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xffb1, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, + /* 0x0200 .. 0x02ff */ + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffff, 0x0000, 0x0000, 0x2a3f, 0x2a3f, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x2a1f, 0x2a1c, 0x2a1e, 0xff2e, + 0xff32, 0x0000, 0xff33, 0xff33, 0x0000, 0xff36, 0x0000, 0xff35, 0x0000, 0x0000, 0x0000, 0x0000, + 0xff33, 0x0000, 0x0000, 0xff31, 0x0000, 0xa528, 0xa544, 0x0000, 0xff2f, 0xff2d, 0x0000, 0x29f7, + 0x0000, 0x0000, 0x0000, 0xff2d, 0x0000, 0x29fd, 0xff2b, 0x0000, 0x0000, 0xff2a, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x29e7, 0x0000, 0x0000, 0xff26, 0x0000, 0x0000, 0xff26, + 0x0000, 0x0000, 0x0000, 0x0000, 0xff26, 0xffbb, 0xff27, 0xff27, 0xffb9, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xff25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x0345 .. 0x03ff */ + 0x0054, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0082, 0x0082, 0x0082, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffda, 0xffdb, 0xffdb, 0xffdb, 0x0000, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe1, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffc0, 0xffc1, 0xffc1, 0x0000, 0xffc2, 0xffc7, 0x0000, 0x0000, 0x0000, + 0xffd1, 0xffca, 0xfff8, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0xffaa, 0xffb0, 0x0007, 0x0000, 0x0000, 0xffa0, 0x0000, 0x0000, 0xffff, + 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x0404 .. 0x04ff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, + 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xfff1, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + /* 0x0500 .. 0x05ff */ + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0xffd0, 0xffd0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x1d79 .. 0x1dff */ + 0x8a04, 0x0000, 0x0000, 0x0000, 0x0ee6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + /* 0x1e01 .. 0x1eff */ + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffc5, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, + /* 0x1f00 .. 0x1fff */ + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x0008, + 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x004a, 0x004a, 0x0056, 0x0056, 0x0056, 0x0056, 0x0064, 0x0064, + 0x0080, 0x0080, 0x0070, 0x0070, 0x007e, 0x007e, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0009, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xe3db, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, + 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x210c .. 0x21ff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, + 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x247b .. 0x24ff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, + 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, + 0xffe6, 0xffe6, 0xffe6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, + /* 0x2c16 .. 0x2cff */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, + 0xffd0, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xd5d5, 0xd5d8, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, + 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, + 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0x2d00 .. 0x2dff */ + 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, + 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, + 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, 0xe3a0, + 0xe3a0, 0xe3a0, 0x0000, 0xe3a0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xe3a0, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + /* 0xa641 .. 0xa6ff */ + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0xa723 .. 0xa7ff */ + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, + 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, + 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 0xff41 .. 0xffff */ + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, + 0xffe0, 0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; diff --git a/winpr/libwinpr/crt/conversion.c b/winpr/libwinpr/crt/conversion.c new file mode 100644 index 0000000..0a3d1e1 --- /dev/null +++ b/winpr/libwinpr/crt/conversion.c @@ -0,0 +1,46 @@ +/** + * WinPR: Windows Portable Runtime + * Data Conversion + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +/* Data Conversion: http://msdn.microsoft.com/en-us/library/0heszx3w/ */ + +#ifndef _WIN32 + +errno_t _itoa_s(int value, char* buffer, size_t sizeInCharacters, int radix) +{ + int length = 0; + + length = sprintf_s(NULL, 0, "%d", value); + + if (length < 0) + return -1; + + if (sizeInCharacters < (size_t)length) + return -1; + + sprintf_s(buffer, length + 1, "%d", value); + + return 0; +} + +#endif diff --git a/winpr/libwinpr/crt/memory.c b/winpr/libwinpr/crt/memory.c new file mode 100644 index 0000000..40c6c85 --- /dev/null +++ b/winpr/libwinpr/crt/memory.c @@ -0,0 +1,42 @@ +/** + * WinPR: Windows Portable Runtime + * Memory Allocation + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/* Memory Allocation: http://msdn.microsoft.com/en-us/library/hk1k7x6x.aspx */ +/* Memory Management Functions: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366781/ */ + +#ifndef _WIN32 + +PVOID SecureZeroMemory(PVOID ptr, SIZE_T cnt) +{ + volatile BYTE* p = ptr; + + while (cnt--) + { + *p = 0; + p++; + } + + return ptr; +} + +#endif diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c new file mode 100644 index 0000000..93721a6 --- /dev/null +++ b/winpr/libwinpr/crt/string.c @@ -0,0 +1,832 @@ +/** + * WinPR: Windows Portable Runtime + * String Manipulation (CRT) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(WITH_URIPARSER) +#include +#endif + +/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */ + +#include "../log.h" +#define TAG WINPR_TAG("crt") + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#if defined(WITH_URIPARSER) +char* winpr_str_url_decode(const char* str, size_t len) +{ + char* dst = strndup(str, len); + if (!dst) + return NULL; + + if (!uriUnescapeInPlaceExA(dst, URI_FALSE, URI_FALSE)) + { + free(dst); + return NULL; + } + + return dst; +} + +char* winpr_str_url_encode(const char* str, size_t len) +{ + char* dst = calloc(len + 1, sizeof(char) * 3); + if (!dst) + return NULL; + + if (!uriEscapeA(str, dst, URI_FALSE, URI_FALSE)) + { + free(dst); + return NULL; + } + return dst; +} + +#else +static const char rfc3986[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static char hex2bin(char what) +{ + if (what >= 'a') + what -= 'a' - 'A'; + if (what >= 'A') + what -= ('A' - 10); + else + what -= '0'; + return what; +} + +static char unescape(const char* what, size_t* px) +{ + if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2]))) + { + *px += 2; + return 16 * hex2bin(what[1]) + hex2bin(what[2]); + } + + return *what; +} + +char* winpr_str_url_decode(const char* str, size_t len) +{ + char* dst = calloc(len + 1, sizeof(char)); + if (!dst) + return NULL; + + size_t pos = 0; + for (size_t x = 0; x < strnlen(str, len); x++) + { + const char* cur = &str[x]; + dst[pos++] = unescape(cur, &x); + } + return dst; +} + +static char* escape(char* dst, char what) +{ + if (rfc3986[what & 0xff]) + { + *dst = what; + return dst + 1; + } + + sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff)); + return dst + 3; +} + +char* winpr_str_url_encode(const char* str, size_t len) +{ + char* dst = calloc(len + 1, sizeof(char) * 3); + if (!dst) + return NULL; + + char* ptr = dst; + for (size_t x = 0; x < strnlen(str, len); x++) + { + const char cur = str[x]; + ptr = escape(ptr, cur); + } + return dst; +} +#endif + +BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator) +{ + const size_t used = strnlen(buffer, size); + const size_t add = strnlen(what, size); + const size_t sep_len = separator ? strnlen(separator, size) : 0; + const size_t sep = (used > 0) ? sep_len : 0; + + if (used + add + sep >= size) + return FALSE; + + if ((used > 0) && (sep_len > 0)) + strncat(buffer, separator, sep_len); + + strncat(buffer, what, add); + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(3, 4) +int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...) +{ + va_list ap; + + va_start(ap, templ); + int rc = winpr_vasprintf(s, slen, templ, ap); + va_end(ap); + return rc; +} + +WINPR_ATTR_FORMAT_ARG(3, 0) +int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap) +{ + va_list ap; + + *s = NULL; + *slen = 0; + + va_copy(ap, oap); + const int length = vsnprintf(NULL, 0, templ, ap); + va_end(ap); + if (length < 0) + return length; + + char* str = calloc((size_t)length + 1ul, sizeof(char)); + if (!str) + return -1; + + va_copy(ap, oap); + const int plen = vsprintf(str, templ, ap); + va_end(ap); + + WINPR_ASSERT(length == plen); + *s = str; + *slen = (size_t)length; + return length; +} + +#ifndef _WIN32 + +char* _strdup(const char* strSource) +{ + if (strSource == NULL) + return NULL; + + char* strDestination = strdup(strSource); + + if (strDestination == NULL) + WLog_ERR(TAG, "strdup"); + + return strDestination; +} + +WCHAR* _wcsdup(const WCHAR* strSource) +{ + if (!strSource) + return NULL; + + size_t len = _wcslen(strSource); + WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR)); + + if (strDestination != NULL) + memcpy(strDestination, strSource, len * sizeof(WCHAR)); + + if (strDestination == NULL) + WLog_ERR(TAG, "wcsdup"); + + return strDestination; +} + +WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz) +{ + WINPR_ASSERT(dst); + WINPR_ASSERT(src || (sz == 0)); + + const size_t dlen = _wcslen(dst); + const size_t slen = _wcsnlen(src, sz); + for (size_t x = 0; x < slen; x++) + dst[dlen + x] = src[x]; + dst[dlen + slen] = '\0'; + return dst; +} + +int _stricmp(const char* string1, const char* string2) +{ + return strcasecmp(string1, string2); +} + +int _strnicmp(const char* string1, const char* string2, size_t count) +{ + return strncasecmp(string1, string2, count); +} + +/* _wcscmp -> wcscmp */ + +int _wcscmp(const WCHAR* string1, const WCHAR* string2) +{ + WINPR_ASSERT(string1); + WINPR_ASSERT(string2); + + while (TRUE) + { + const WCHAR w1 = *string1++; + const WCHAR w2 = *string2++; + + if (w1 != w2) + return (int)w1 - w2; + else if ((w1 == '\0') || (w2 == '\0')) + return (int)w1 - w2; + } +} + +int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count) +{ + WINPR_ASSERT(string1); + WINPR_ASSERT(string2); + + for (size_t x = 0; x < count; x++) + { + const WCHAR a = string1[x]; + const WCHAR b = string2[x]; + + if (a != b) + return (int)a - b; + else if ((a == '\0') || (b == '\0')) + return (int)a - b; + } + return 0; +} + +/* _wcslen -> wcslen */ + +size_t _wcslen(const WCHAR* str) +{ + const WCHAR* p = (const WCHAR*)str; + + WINPR_ASSERT(p); + + while (*p) + p++; + + return (size_t)(p - str); +} + +/* _wcsnlen -> wcsnlen */ + +size_t _wcsnlen(const WCHAR* str, size_t max) +{ + WINPR_ASSERT(str); + + size_t x = 0; + for (; x < max; x++) + { + if (str[x] == 0) + return x; + } + + return x; +} + +/* _wcsstr -> wcsstr */ + +WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch) +{ + WINPR_ASSERT(str); + WINPR_ASSERT(strSearch); + + if (strSearch[0] == '\0') + return (WCHAR*)str; + + const size_t searchLen = _wcslen(strSearch); + while (*str) + { + if (_wcsncmp(str, strSearch, searchLen) == 0) + return (WCHAR*)str; + str++; + } + return NULL; +} + +/* _wcschr -> wcschr */ + +WCHAR* _wcschr(const WCHAR* str, WCHAR value) +{ + union + { + const WCHAR* cc; + WCHAR* c; + } cnv; + const WCHAR* p = (const WCHAR*)str; + + while (*p && (*p != value)) + p++; + + cnv.cc = (*p == value) ? p : NULL; + return cnv.c; +} + +/* _wcsrchr -> wcsrchr */ + +WCHAR* _wcsrchr(const WCHAR* str, WCHAR c) +{ + union + { + const WCHAR* cc; + WCHAR* c; + } cnv; + const WCHAR* p = NULL; + + if (!str) + return NULL; + + for (; *str != '\0'; str++) + { + const WCHAR ch = *str; + if (ch == c) + p = str; + } + + cnv.cc = p; + return cnv.c; +} + +char* strtok_s(char* strToken, const char* strDelimit, char** context) +{ + return strtok_r(strToken, strDelimit, context); +} + +WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context) +{ + WCHAR* nextToken = NULL; + WCHAR value = 0; + + if (!strToken) + strToken = *context; + + value = *strToken; + + while (*strToken && _wcschr(strDelimit, value)) + { + strToken++; + value = *strToken; + } + + if (!*strToken) + return NULL; + + nextToken = strToken++; + value = *strToken; + + while (*strToken && !(_wcschr(strDelimit, value))) + { + strToken++; + value = *strToken; + } + + if (*strToken) + *strToken++ = 0; + + *context = strToken; + return nextToken; +} + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll + * http://msdn.microsoft.com/en-us/library/hh802935/ + */ + +#include "casing.c" + +LPSTR CharUpperA(LPSTR lpsz) +{ + size_t length = 0; + + if (!lpsz) + return NULL; + + length = strlen(lpsz); + + if (length < 1) + return (LPSTR)NULL; + + if (length == 1) + { + char c = *lpsz; + + if ((c >= 'a') && (c <= 'z')) + c = c - 'a' + 'A'; + + *lpsz = c; + return lpsz; + } + + for (size_t i = 0; i < length; i++) + { + if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z')) + lpsz[i] = lpsz[i] - 'a' + 'A'; + } + + return lpsz; +} + +LPWSTR CharUpperW(LPWSTR lpsz) +{ + size_t length = 0; + + if (!lpsz) + return NULL; + + length = _wcslen(lpsz); + + if (length < 1) + return (LPWSTR)NULL; + + if (length == 1) + { + WCHAR c = *lpsz; + + if ((c >= L'a') && (c <= L'z')) + c = c - L'a' + L'A'; + + *lpsz = c; + return lpsz; + } + + for (size_t i = 0; i < length; i++) + { + if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z')) + lpsz[i] = lpsz[i] - L'a' + L'A'; + } + + return lpsz; +} + +DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength) +{ + if (cchLength < 1) + return 0; + + for (DWORD i = 0; i < cchLength; i++) + { + if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z')) + lpsz[i] = lpsz[i] - 'a' + 'A'; + } + + return cchLength; +} + +DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength) +{ + WCHAR value = 0; + + for (DWORD i = 0; i < cchLength; i++) + { + Data_Read_UINT16(&lpsz[i], value); + value = WINPR_TOUPPERW(value); + Data_Write_UINT16(&lpsz[i], value); + } + + return cchLength; +} + +LPSTR CharLowerA(LPSTR lpsz) +{ + size_t length = 0; + + if (!lpsz) + return (LPSTR)NULL; + + length = strlen(lpsz); + + if (length < 1) + return (LPSTR)NULL; + + if (length == 1) + { + char c = *lpsz; + + if ((c >= 'A') && (c <= 'Z')) + c = c - 'A' + 'a'; + + *lpsz = c; + return lpsz; + } + + for (size_t i = 0; i < length; i++) + { + if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z')) + lpsz[i] = lpsz[i] - 'A' + 'a'; + } + + return lpsz; +} + +LPWSTR CharLowerW(LPWSTR lpsz) +{ + CharLowerBuffW(lpsz, _wcslen(lpsz)); + return lpsz; +} + +DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength) +{ + if (cchLength < 1) + return 0; + + for (DWORD i = 0; i < cchLength; i++) + { + if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z')) + lpsz[i] = lpsz[i] - 'A' + 'a'; + } + + return cchLength; +} + +DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength) +{ + WCHAR value = 0; + + for (DWORD i = 0; i < cchLength; i++) + { + Data_Read_UINT16(&lpsz[i], value); + value = WINPR_TOLOWERW(value); + Data_Write_UINT16(&lpsz[i], value); + } + + return cchLength; +} + +BOOL IsCharAlphaA(CHAR ch) +{ + if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) + return 1; + else + return 0; +} + +BOOL IsCharAlphaW(WCHAR ch) +{ + if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z'))) + return 1; + else + return 0; +} + +BOOL IsCharAlphaNumericA(CHAR ch) +{ + if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || + ((ch >= '0') && (ch <= '9'))) + return 1; + else + return 0; +} + +BOOL IsCharAlphaNumericW(WCHAR ch) +{ + if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) || + ((ch >= L'0') && (ch <= L'9'))) + return 1; + else + return 0; +} + +BOOL IsCharUpperA(CHAR ch) +{ + if ((ch >= 'A') && (ch <= 'Z')) + return 1; + else + return 0; +} + +BOOL IsCharUpperW(WCHAR ch) +{ + if ((ch >= L'A') && (ch <= L'Z')) + return 1; + else + return 0; +} + +BOOL IsCharLowerA(CHAR ch) +{ + if ((ch >= 'a') && (ch <= 'z')) + return 1; + else + return 0; +} + +BOOL IsCharLowerW(WCHAR ch) +{ + if ((ch >= L'a') && (ch <= L'z')) + return 1; + else + return 0; +} + +#endif + +size_t ConvertLineEndingToLF(char* str, size_t size) +{ + size_t skip = 0; + + WINPR_ASSERT(str || (size == 0)); + for (size_t x = 0; x < size; x++) + { + char c = str[x]; + switch (c) + { + case '\r': + str[x - skip] = '\n'; + if ((x + 1 < size) && (str[x + 1] == '\n')) + skip++; + break; + default: + str[x - skip] = c; + break; + } + } + return size - skip; +} + +char* ConvertLineEndingToCRLF(const char* str, size_t* size) +{ + WINPR_ASSERT(size); + const size_t s = *size; + WINPR_ASSERT(str || (s == 0)); + + *size = 0; + if (s == 0) + return NULL; + + size_t linebreaks = 0; + for (size_t x = 0; x < s - 1; x++) + { + char c = str[x]; + switch (c) + { + case '\r': + case '\n': + linebreaks++; + break; + default: + break; + } + } + char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char)); + if (!cnv) + return NULL; + + size_t pos = 0; + for (size_t x = 0; x < s; x++) + { + const char c = str[x]; + switch (c) + { + case '\r': + cnv[pos++] = '\r'; + cnv[pos++] = '\n'; + break; + case '\n': + /* Do not duplicate existing \r\n sequences */ + if ((x > 0) && (str[x - 1] != '\r')) + { + cnv[pos++] = '\r'; + cnv[pos++] = '\n'; + } + break; + default: + cnv[pos++] = c; + break; + } + } + *size = pos; + return cnv; +} + +char* StrSep(char** stringp, const char* delim) +{ + char* start = *stringp; + char* p = NULL; + p = (start != NULL) ? strpbrk(start, delim) : NULL; + + if (!p) + *stringp = NULL; + else + { + *p = '\0'; + *stringp = p + 1; + } + + return start; +} + +INT64 GetLine(char** lineptr, size_t* size, FILE* stream) +{ +#if defined(_WIN32) + char c; + char* n; + size_t step = 32; + size_t used = 0; + + if (!lineptr || !size) + { + errno = EINVAL; + return -1; + } + + do + { + if (used + 2 >= *size) + { + *size += step; + n = realloc(*lineptr, *size); + + if (!n) + { + return -1; + } + + *lineptr = n; + } + + c = fgetc(stream); + + if (c != EOF) + (*lineptr)[used++] = c; + } while ((c != '\n') && (c != '\r') && (c != EOF)); + + (*lineptr)[used] = '\0'; + return used; +#elif !defined(ANDROID) && !defined(IOS) + return getline(lineptr, size, stream); +#else + return -1; +#endif +} + +#if !defined(WINPR_HAVE_STRNDUP) +char* strndup(const char* src, size_t n) +{ + char* dst = calloc(n + 1, sizeof(char)); + if (dst) + strncpy(dst, src, n); + return dst; +} +#endif + +const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len) +{ + WINPR_ASSERT(str); + WINPR_ASSERT(buffer || (len == 0)); + ConvertUtf8ToWChar(str, buffer, len); + return buffer; +} diff --git a/winpr/libwinpr/crt/test/CMakeLists.txt b/winpr/libwinpr/crt/test/CMakeLists.txt new file mode 100644 index 0000000..5f5b013 --- /dev/null +++ b/winpr/libwinpr/crt/test/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(MODULE_NAME "TestCrt") +set(MODULE_PREFIX "TEST_CRT") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestTypes.c + TestFormatSpecifiers.c + TestAlignment.c + TestString.c + TestUnicodeConversion.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/crt/test/TestAlignment.c b/winpr/libwinpr/crt/test/TestAlignment.c new file mode 100644 index 0000000..07bac7f --- /dev/null +++ b/winpr/libwinpr/crt/test/TestAlignment.c @@ -0,0 +1,87 @@ + +#include +#include +#include + +int TestAlignment(int argc, char* argv[]) +{ + void* ptr = NULL; + size_t alignment = 0; + size_t offset = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Alignment should be 2^N where N is a positive integer */ + + alignment = 16; + offset = 8; + + /* _aligned_malloc */ + + ptr = winpr_aligned_malloc(100, alignment); + + if (ptr == NULL) + { + printf("Error allocating aligned memory.\n"); + return -1; + } + + if (((size_t)ptr % alignment) != 0) + { + printf("This pointer, %p, is not aligned on %" PRIuz "\n", ptr, alignment); + return -1; + } + + /* _aligned_realloc */ + + ptr = winpr_aligned_realloc(ptr, 200, alignment); + + if (((size_t)ptr % alignment) != 0) + { + printf("This pointer, %p, is not aligned on %" PRIuz "\n", ptr, alignment); + return -1; + } + + winpr_aligned_free(ptr); + + /* _aligned_offset_malloc */ + + ptr = winpr_aligned_offset_malloc(200, alignment, offset); + + if (ptr == NULL) + { + printf("Error reallocating aligned offset memory."); + return -1; + } + + if (((((size_t)ptr) + offset) % alignment) != 0) + { + printf("This pointer, %p, does not satisfy offset %" PRIuz " and alignment %" PRIuz "\n", + ptr, offset, alignment); + return -1; + } + + /* _aligned_offset_realloc */ + + ptr = winpr_aligned_offset_realloc(ptr, 200, alignment, offset); + + if (ptr == NULL) + { + printf("Error reallocating aligned offset memory."); + return -1; + } + + if (((((size_t)ptr) + offset) % alignment) != 0) + { + printf("This pointer, %p, does not satisfy offset %" PRIuz " and alignment %" PRIuz "\n", + ptr, offset, alignment); + return -1; + } + + /* _aligned_free works for both _aligned_malloc and _aligned_offset_malloc. free() should not be + * used. */ + winpr_aligned_free(ptr); + + return 0; +} diff --git a/winpr/libwinpr/crt/test/TestFormatSpecifiers.c b/winpr/libwinpr/crt/test/TestFormatSpecifiers.c new file mode 100644 index 0000000..5ae6a40 --- /dev/null +++ b/winpr/libwinpr/crt/test/TestFormatSpecifiers.c @@ -0,0 +1,164 @@ +#include +#include +#include + +int TestFormatSpecifiers(int argc, char* argv[]) +{ + unsigned errors = 0; + + char fmt[4096]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* size_t */ + { + size_t arg = 0xabcd; + const char* chk = "uz:43981 oz:125715 xz:abcd Xz:ABCD"; + + sprintf_s(fmt, sizeof(fmt), "uz:%" PRIuz " oz:%" PRIoz " xz:%" PRIxz " Xz:%" PRIXz "", arg, + arg, arg, arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed size_t test: got [%s] instead of [%s]\n", __func__, fmt, + chk); + errors++; + } + } + + /* INT8 */ + { + INT8 arg = -16; + const char* chk = "d8:-16 x8:f0 X8:F0"; + + sprintf_s(fmt, sizeof(fmt), "d8:%" PRId8 " x8:%" PRIx8 " X8:%" PRIX8 "", arg, (UINT8)arg, + (UINT8)arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed INT8 test: got [%s] instead of [%s]\n", __func__, fmt, chk); + errors++; + } + } + + /* UINT8 */ + { + UINT8 arg = 0xFE; + const char* chk = "u8:254 o8:376 x8:fe X8:FE"; + + sprintf_s(fmt, sizeof(fmt), "u8:%" PRIu8 " o8:%" PRIo8 " x8:%" PRIx8 " X8:%" PRIX8 "", arg, + arg, arg, arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed UINT8 test: got [%s] instead of [%s]\n", __func__, fmt, chk); + errors++; + } + } + + /* INT16 */ + { + INT16 arg = -16; + const char* chk = "d16:-16 x16:fff0 X16:FFF0"; + + sprintf_s(fmt, sizeof(fmt), "d16:%" PRId16 " x16:%" PRIx16 " X16:%" PRIX16 "", arg, + (UINT16)arg, (UINT16)arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed INT16 test: got [%s] instead of [%s]\n", __func__, fmt, chk); + errors++; + } + } + + /* UINT16 */ + { + UINT16 arg = 0xFFFE; + const char* chk = "u16:65534 o16:177776 x16:fffe X16:FFFE"; + + sprintf_s(fmt, sizeof(fmt), + "u16:%" PRIu16 " o16:%" PRIo16 " x16:%" PRIx16 " X16:%" PRIX16 "", arg, arg, arg, + arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed UINT16 test: got [%s] instead of [%s]\n", __func__, fmt, + chk); + errors++; + } + } + + /* INT32 */ + { + INT32 arg = -16; + const char* chk = "d32:-16 x32:fffffff0 X32:FFFFFFF0"; + + sprintf_s(fmt, sizeof(fmt), "d32:%" PRId32 " x32:%" PRIx32 " X32:%" PRIX32 "", arg, + (UINT32)arg, (UINT32)arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed INT32 test: got [%s] instead of [%s]\n", __func__, fmt, chk); + errors++; + } + } + + /* UINT32 */ + { + UINT32 arg = 0xFFFFFFFE; + const char* chk = "u32:4294967294 o32:37777777776 x32:fffffffe X32:FFFFFFFE"; + + sprintf_s(fmt, sizeof(fmt), + "u32:%" PRIu32 " o32:%" PRIo32 " x32:%" PRIx32 " X32:%" PRIX32 "", arg, arg, arg, + arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed UINT16 test: got [%s] instead of [%s]\n", __func__, fmt, + chk); + errors++; + } + } + + /* INT64 */ + { + INT64 arg = -16; + const char* chk = "d64:-16 x64:fffffffffffffff0 X64:FFFFFFFFFFFFFFF0"; + + sprintf_s(fmt, sizeof(fmt), "d64:%" PRId64 " x64:%" PRIx64 " X64:%" PRIX64 "", arg, + (UINT64)arg, (UINT64)arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed INT64 test: got [%s] instead of [%s]\n", __func__, fmt, chk); + errors++; + } + } + + /* UINT64 */ + { + UINT64 arg = 0xFFFFFFFFFFFFFFFE; + const char* chk = "u64:18446744073709551614 o64:1777777777777777777776 " + "x64:fffffffffffffffe X64:FFFFFFFFFFFFFFFE"; + + sprintf_s(fmt, sizeof(fmt), + "u64:%" PRIu64 " o64:%" PRIo64 " x64:%016" PRIx64 " X64:%016" PRIX64 "", arg, arg, + arg, arg); + + if (strcmp(fmt, chk)) + { + fprintf(stderr, "%s failed UINT64 test: got [%s] instead of [%s]\n", __func__, fmt, + chk); + errors++; + } + } + + if (errors) + { + fprintf(stderr, "%s produced %u errors\n", __func__, errors); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/crt/test/TestString.c b/winpr/libwinpr/crt/test/TestString.c new file mode 100644 index 0000000..cb7d0fb --- /dev/null +++ b/winpr/libwinpr/crt/test/TestString.c @@ -0,0 +1,188 @@ + +#include +#include +#include + +static const CHAR testStringA[] = { 'T', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', + 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u', + 'm', 'p', 's', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h', + 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g', '\0' }; + +#define testStringA_Length ((sizeof(testStringA) / sizeof(CHAR)) - 1) + +static const CHAR testToken1A[] = { 'q', 'u', 'i', 'c', 'k', '\0' }; +static const CHAR testToken2A[] = { 'b', 'r', 'o', 'w', 'n', '\0' }; +static const CHAR testToken3A[] = { 'f', 'o', 'x', '\0' }; + +#define testToken1A_Length ((sizeof(testToken1A) / sizeof(CHAR)) - 1) +#define testToken2A_Length ((sizeof(testToken2A) / sizeof(CHAR)) - 1) +#define testToken3A_Length ((sizeof(testToken3A) / sizeof(CHAR)) - 1) + +static const CHAR testTokensA[] = { 'q', 'u', 'i', 'c', 'k', '\r', '\n', 'b', 'r', 'o', + 'w', 'n', '\r', '\n', 'f', 'o', 'x', '\r', '\n', '\0' }; + +#define testTokensA_Length ((sizeof(testTokensA) / sizeof(CHAR)) - 1) + +static const CHAR testDelimiterA[] = { '\r', '\n', '\0' }; + +#define testDelimiterA_Length ((sizeof(testDelimiter) / sizeof(CHAR)) - 1) + +struct url_test_pair +{ + const char* what; + const char* escaped; +}; + +static const struct url_test_pair url_tests[] = { + { "xxx%bar gaee#%%#%{h}g{f{e%d|c\\b^a~p[q]r`s;t/u?v:w@x=y&z$xxx", + "xxx%25bar%20ga%3Cka%3Eee%23%25%25%23%25%7Bh%7Dg%7Bf%7Be%25d%7Cc%5Cb%5Ea~p%5Bq%5Dr%60s%3Bt%" + "2Fu%3Fv%3Aw%40x%3Dy%26z%24xxx" }, + { "äöúëü", "%C3%A4%C3%B6%C3%BA%C3%AB%C3%BC" }, + { "🎅🏄🤘😈", "%F0%9F%8E%85%F0%9F%8F%84%F0%9F%A4%98%F0%9F%98%88" }, + { "foo$.%.^.&.\\.txt+", "foo%24.%25.%5E.%26.%5C.txt%2B" } +}; + +static BOOL test_url_escape(void) +{ + for (size_t x = 0; x < ARRAYSIZE(url_tests); x++) + { + const struct url_test_pair* cur = &url_tests[x]; + + char* escaped = winpr_str_url_encode(cur->what, strlen(cur->what) + 1); + char* what = winpr_str_url_decode(cur->escaped, strlen(cur->escaped) + 1); + + const size_t elen = strlen(escaped); + const size_t wlen = strlen(what); + const size_t pelen = strlen(cur->escaped); + const size_t pwlen = strlen(cur->what); + BOOL rc = TRUE; + if (!escaped || (elen != pelen) || (strcmp(escaped, cur->escaped) != 0)) + { + printf("expected: [%" PRIuz "] %s\n", pelen, cur->escaped); + printf("got : [%" PRIuz "] %s\n", elen, escaped); + rc = FALSE; + } + else if (!what || (wlen != pwlen) || (strcmp(what, cur->what) != 0)) + { + printf("expected: [%" PRIuz "] %s\n", pwlen, cur->what); + printf("got : [%" PRIuz "] %s\n", wlen, what); + rc = FALSE; + } + + free(escaped); + free(what); + if (!rc) + return FALSE; + } + + return TRUE; +} + +int TestString(int argc, char* argv[]) +{ + const WCHAR* p = NULL; + size_t pos = 0; + size_t length = 0; + WCHAR* context = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_url_escape()) + return -1; + + /* _wcslen */ + WCHAR testStringW[ARRAYSIZE(testStringA)] = { 0 }; + ConvertUtf8NToWChar(testStringA, ARRAYSIZE(testStringA), testStringW, ARRAYSIZE(testStringW)); + const size_t testStringW_Length = testStringA_Length; + length = _wcslen(testStringW); + + if (length != testStringW_Length) + { + printf("_wcslen error: length mismatch: Actual: %" PRIuz ", Expected: %" PRIuz "\n", length, + testStringW_Length); + return -1; + } + + /* _wcschr */ + union + { + char c[2]; + WCHAR w; + } search; + search.c[0] = 'r'; + search.c[1] = '\0'; + + p = _wcschr(testStringW, search.w); + pos = (p - testStringW); + + if (pos != 11) + { + printf("_wcschr error: position mismatch: Actual: %" PRIuz ", Expected: 11\n", pos); + return -1; + } + + p = _wcschr(&testStringW[pos + 1], search.w); + pos = (p - testStringW); + + if (pos != 29) + { + printf("_wcschr error: position mismatch: Actual: %" PRIuz ", Expected: 29\n", pos); + return -1; + } + + p = _wcschr(&testStringW[pos + 1], search.w); + + if (p != NULL) + { + printf("_wcschr error: return value mismatch: Actual: %p, Expected: NULL\n", + (const void*)p); + return -1; + } + + /* wcstok_s */ + WCHAR testDelimiterW[ARRAYSIZE(testDelimiterA)] = { 0 }; + WCHAR testTokensW[ARRAYSIZE(testTokensA)] = { 0 }; + ConvertUtf8NToWChar(testTokensA, ARRAYSIZE(testTokensA), testTokensW, ARRAYSIZE(testTokensW)); + ConvertUtf8NToWChar(testDelimiterA, ARRAYSIZE(testDelimiterA), testDelimiterW, + ARRAYSIZE(testDelimiterW)); + p = wcstok_s(testTokensW, testDelimiterW, &context); + + WCHAR testToken1W[ARRAYSIZE(testToken1A)] = { 0 }; + ConvertUtf8NToWChar(testToken1A, ARRAYSIZE(testToken1A), testToken1W, ARRAYSIZE(testToken1W)); + if (memcmp(p, testToken1W, sizeof(testToken1W)) != 0) + { + printf("wcstok_s error: token #1 mismatch\n"); + return -1; + } + + p = wcstok_s(NULL, testDelimiterW, &context); + + WCHAR testToken2W[ARRAYSIZE(testToken2A)] = { 0 }; + ConvertUtf8NToWChar(testToken2A, ARRAYSIZE(testToken2A), testToken2W, ARRAYSIZE(testToken2W)); + if (memcmp(p, testToken2W, sizeof(testToken2W)) != 0) + { + printf("wcstok_s error: token #2 mismatch\n"); + return -1; + } + + p = wcstok_s(NULL, testDelimiterW, &context); + + WCHAR testToken3W[ARRAYSIZE(testToken3A)] = { 0 }; + ConvertUtf8NToWChar(testToken3A, ARRAYSIZE(testToken3A), testToken3W, ARRAYSIZE(testToken3W)); + if (memcmp(p, testToken3W, sizeof(testToken3W)) != 0) + { + printf("wcstok_s error: token #3 mismatch\n"); + return -1; + } + + p = wcstok_s(NULL, testDelimiterW, &context); + + if (p != NULL) + { + printf("wcstok_s error: return value is not NULL\n"); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/crt/test/TestTypes.c b/winpr/libwinpr/crt/test/TestTypes.c new file mode 100644 index 0000000..734da06 --- /dev/null +++ b/winpr/libwinpr/crt/test/TestTypes.c @@ -0,0 +1,115 @@ + +#include +#include +#include + +#define EXPECTED_SIZEOF_BYTE 1 +#define EXPECTED_SIZEOF_BOOLEAN 1 +#define EXPECTED_SIZEOF_CHAR 1 +#define EXPECTED_SIZEOF_UCHAR 1 +#define EXPECTED_SIZEOF_INT8 1 +#define EXPECTED_SIZEOF_UINT8 1 +#define EXPECTED_SIZEOF_INT16 2 +#define EXPECTED_SIZEOF_UINT16 2 +#define EXPECTED_SIZEOF_WORD 2 +#define EXPECTED_SIZEOF_WCHAR 2 +#define EXPECTED_SIZEOF_SHORT 2 +#define EXPECTED_SIZEOF_USHORT 2 +#define EXPECTED_SIZEOF_BOOL 4 +#define EXPECTED_SIZEOF_INT 4 +#define EXPECTED_SIZEOF_UINT 4 +#define EXPECTED_SIZEOF_INT32 4 +#define EXPECTED_SIZEOF_UINT32 4 +#define EXPECTED_SIZEOF_DWORD 4 +#define EXPECTED_SIZEOF_DWORD32 4 +#define EXPECTED_SIZEOF_LONG 4 +#define EXPECTED_SIZEOF_LONG32 4 +#define EXPECTED_SIZEOF_INT64 8 +#define EXPECTED_SIZEOF_UINT64 8 +#define EXPECTED_SIZEOF_DWORD64 8 +#define EXPECTED_SIZEOF_DWORDLONG 8 +#define EXPECTED_SIZEOF_LONG64 8 +#define EXPECTED_SIZEOF_ULONGLONG 8 +#define EXPECTED_SIZEOF_LUID 8 +#define EXPECTED_SIZEOF_FILETIME 8 +#define EXPECTED_SIZEOF_LARGE_INTEGER 8 +#define EXPECTED_SIZEOF_ULARGE_INTEGER 8 +#define EXPECTED_SIZEOF_GUID 16 +#define EXPECTED_SIZEOF_SYSTEMTIME 16 +#define EXPECTED_SIZEOF_SIZE_T sizeof(void*) +#define EXPECTED_SIZEOF_INT_PTR sizeof(void*) +#define EXPECTED_SIZEOF_UINT_PTR sizeof(void*) +#define EXPECTED_SIZEOF_DWORD_PTR sizeof(void*) +#define EXPECTED_SIZEOF_LONG_PTR sizeof(void*) +#define EXPECTED_SIZEOF_ULONG_PTR sizeof(void*) + +#define TEST_SIZEOF_TYPE(_name) \ + if (sizeof(_name) != EXPECTED_SIZEOF_##_name) \ + { \ + fprintf(stderr, "sizeof(%s) mismatch: Actual: %" PRIuz ", Expected: %" PRIuz "\n", #_name, \ + sizeof(_name), (size_t)EXPECTED_SIZEOF_##_name); \ + status = -1; \ + } + +int TestTypes(int argc, char* argv[]) +{ + int status = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + TEST_SIZEOF_TYPE(INT8) + TEST_SIZEOF_TYPE(UINT8) + + TEST_SIZEOF_TYPE(BYTE) + TEST_SIZEOF_TYPE(BOOLEAN) + TEST_SIZEOF_TYPE(CHAR) + TEST_SIZEOF_TYPE(UCHAR) + + TEST_SIZEOF_TYPE(INT16) + TEST_SIZEOF_TYPE(UINT16) + + TEST_SIZEOF_TYPE(WORD) + TEST_SIZEOF_TYPE(WCHAR) + TEST_SIZEOF_TYPE(SHORT) + TEST_SIZEOF_TYPE(USHORT) + + /* fails on OS X */ + // TEST_SIZEOF_TYPE(BOOL) + + TEST_SIZEOF_TYPE(INT) + TEST_SIZEOF_TYPE(UINT) + TEST_SIZEOF_TYPE(DWORD) + TEST_SIZEOF_TYPE(DWORD32) + TEST_SIZEOF_TYPE(LONG) + TEST_SIZEOF_TYPE(LONG32) + + TEST_SIZEOF_TYPE(INT32) + TEST_SIZEOF_TYPE(UINT32) + + TEST_SIZEOF_TYPE(INT64) + TEST_SIZEOF_TYPE(UINT64) + + TEST_SIZEOF_TYPE(DWORD64) + TEST_SIZEOF_TYPE(DWORDLONG) + + TEST_SIZEOF_TYPE(LONG64) + TEST_SIZEOF_TYPE(ULONGLONG) + + TEST_SIZEOF_TYPE(LUID) + TEST_SIZEOF_TYPE(FILETIME) + TEST_SIZEOF_TYPE(LARGE_INTEGER) + TEST_SIZEOF_TYPE(ULARGE_INTEGER) + + TEST_SIZEOF_TYPE(GUID) + TEST_SIZEOF_TYPE(SYSTEMTIME) + + TEST_SIZEOF_TYPE(SIZE_T) + TEST_SIZEOF_TYPE(INT_PTR) + TEST_SIZEOF_TYPE(UINT_PTR) + TEST_SIZEOF_TYPE(DWORD_PTR) + TEST_SIZEOF_TYPE(LONG_PTR) + TEST_SIZEOF_TYPE(ULONG_PTR) + + return status; +} diff --git a/winpr/libwinpr/crt/test/TestUnicodeConversion.c b/winpr/libwinpr/crt/test/TestUnicodeConversion.c new file mode 100644 index 0000000..a5c4c75 --- /dev/null +++ b/winpr/libwinpr/crt/test/TestUnicodeConversion.c @@ -0,0 +1,1289 @@ + +#include +#include +#include +#include +#include +#include +#include + +#define TESTCASE_BUFFER_SIZE 8192 + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +typedef struct +{ + char* utf8; + size_t utf8len; + WCHAR* utf16; + size_t utf16len; +} testcase_t; + +// TODO: The unit tests do not check for valid code points, so always end the test +// strings with a simple ASCII symbol for now. +static const testcase_t unit_testcases[] = { + { "foo", 3, "f\x00o\x00o\x00\x00\x00", 3 }, + { "foo", 4, "f\x00o\x00o\x00\x00\x00", 4 }, + { "✊🎅ęʥ꣸𑗊a", 19, + "\x0a\x27\x3c\xd8\x85\xdf\x19\x01\xa5\x02\xf8\xa8\x05\xd8\xca\xdd\x61\x00\x00\x00", 9 } +}; + +static void create_prefix(char* prefix, size_t prefixlen, size_t buffersize, SSIZE_T rc, + SSIZE_T inputlen, const testcase_t* test, const char* fkt, size_t line) +{ + _snprintf(prefix, prefixlen, + "[%s:%" PRIuz "] '%s' [utf8: %" PRIuz ", utf16: %" PRIuz "] buffersize: %" PRIuz + ", rc: %" PRIdz ", inputlen: %" PRIdz ":: ", + fkt, line, test->utf8, test->utf8len, test->utf16len, buffersize, rc, inputlen); +} + +static BOOL check_short_buffer(const char* prefix, int rc, size_t buffersize, + const testcase_t* test, BOOL utf8) +{ + if ((rc > 0) && ((size_t)rc <= buffersize)) + return TRUE; + + size_t len = test->utf8len; + if (!utf8) + len = test->utf16len; + + if (buffersize > len) + { + fprintf(stderr, + "%s length does not match buffersize: %" PRId32 " != %" PRIuz + ",but is large enough to hold result\n", + prefix, rc, buffersize); + return FALSE; + } + const DWORD err = GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) + { + + fprintf(stderr, + "%s length does not match buffersize: %" PRId32 " != %" PRIuz + ", unexpected GetLastError() 0x08%" PRIx32 "\n", + prefix, rc, buffersize, err); + return FALSE; + } + else + return TRUE; +} + +#define compare_utf16(what, buffersize, rc, inputlen, test) \ + compare_utf16_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__) +static BOOL compare_utf16_int(const WCHAR* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen, + const testcase_t* test, const char* fkt, size_t line) +{ + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line); + + WINPR_ASSERT(what || (buffersize == 0)); + WINPR_ASSERT(test); + + const size_t welen = _wcsnlen(test->utf16, test->utf16len); + if (buffersize > welen) + { + if ((rc < 0) || ((size_t)rc != welen)) + { + fprintf(stderr, "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n", + prefix, rc, welen); + return FALSE; + } + } + else + { + if (!check_short_buffer(prefix, rc, buffersize, test, FALSE)) + return FALSE; + } + + if ((rc > 0) && (buffersize > (size_t)rc)) + { + const size_t wlen = _wcsnlen(what, buffersize); + if ((rc < 0) || (wlen > (size_t)rc)) + { + fprintf(stderr, "%s length does not match wcslen: %" PRIdz " < %" PRIuz "\n", prefix, + rc, wlen); + return FALSE; + } + } + + if (rc >= 0) + { + if (memcmp(test->utf16, what, rc * sizeof(WCHAR)) != 0) + { + fprintf(stderr, "%s contents does not match expectations: TODO '%s' != '%s'\n", prefix, + test->utf8, test->utf8); + return FALSE; + } + } + + printf("%s success\n", prefix); + + return TRUE; +} + +#define compare_utf8(what, buffersize, rc, inputlen, test) \ + compare_utf8_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__) +static BOOL compare_utf8_int(const char* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen, + const testcase_t* test, const char* fkt, size_t line) +{ + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line); + + WINPR_ASSERT(what || (buffersize == 0)); + WINPR_ASSERT(test); + + const size_t slen = strnlen(test->utf8, test->utf8len); + if (buffersize > slen) + { + if ((rc < 0) || ((size_t)rc != slen)) + { + fprintf(stderr, "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n", + prefix, rc, slen); + return FALSE; + } + } + else + { + if (!check_short_buffer(prefix, rc, buffersize, test, TRUE)) + return FALSE; + } + + if ((rc > 0) && (buffersize > (size_t)rc)) + { + const size_t wlen = strnlen(what, buffersize); + if (wlen != (size_t)rc) + { + fprintf(stderr, "%s length does not match strnlen: %" PRIdz " != %" PRIuz "\n", prefix, + rc, wlen); + return FALSE; + } + } + + if (rc >= 0) + { + if (memcmp(test->utf8, what, rc) != 0) + { + fprintf(stderr, "%s contents does not match expectations: '%s' != '%s'\n", prefix, what, + test->utf8); + return FALSE; + } + } + + printf("%s success\n", prefix); + + return TRUE; +} + +static BOOL test_convert_to_utf16(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1, + test->utf16len - 1 }; + const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const SSIZE_T rc2 = ConvertUtf8ToWChar(test->utf8, NULL, 0); + const size_t wlen = _wcsnlen(test->utf16, test->utf16len); + if ((rc2 < 0) || ((size_t)rc2 != wlen)) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__); + fprintf(stderr, "%s ConvertUtf8ToWChar(%s, NULL, 0) expected %" PRIuz ", got %" PRIdz "\n", + prefix, test->utf8, wlen, rc2); + return FALSE; + } + for (size_t x = 0; x < max; x++) + { + WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + const SSIZE_T rc = ConvertUtf8ToWChar(test->utf8, buffer, len[x]); + if (!compare_utf16(buffer, len[x], rc, -1, test)) + return FALSE; + } + + return TRUE; +} + +static BOOL test_convert_to_utf16_n(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1, + test->utf16len - 1 }; + const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const SSIZE_T rc2 = ConvertUtf8NToWChar(test->utf8, test->utf8len, NULL, 0); + const size_t wlen = _wcsnlen(test->utf16, test->utf16len); + if ((rc2 < 0) || ((size_t)rc2 != wlen)) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf8len, test, __func__, __LINE__); + fprintf(stderr, + "%s ConvertUtf8NToWChar(%s, %" PRIuz ", NULL, 0) expected %" PRIuz ", got %" PRIdz + "\n", + prefix, test->utf8, test->utf8len, wlen, rc2); + return FALSE; + } + + for (size_t x = 0; x < max; x++) + { + const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1, + test->utf8len - 1 }; + const size_t imax = test->utf8len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1; + + for (size_t y = 0; y < imax; y++) + { + WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + SSIZE_T rc = ConvertUtf8NToWChar(test->utf8, ilen[x], buffer, len[x]); + if (!compare_utf16(buffer, len[x], rc, ilen[x], test)) + return FALSE; + } + } + return TRUE; +} + +static BOOL test_convert_to_utf8(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1, + test->utf8len - 1 }; + const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const SSIZE_T rc2 = ConvertWCharToUtf8(test->utf16, NULL, 0); + const size_t wlen = strnlen(test->utf8, test->utf8len); + if ((rc2 < 0) || ((size_t)rc2 != wlen)) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__); + fprintf(stderr, "%s ConvertWCharToUtf8(%s, NULL, 0) expected %" PRIuz ", got %" PRIdz "\n", + prefix, test->utf8, wlen, rc2); + return FALSE; + } + + for (size_t x = 0; x < max; x++) + { + char buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + SSIZE_T rc = ConvertWCharToUtf8(test->utf16, buffer, len[x]); + if (!compare_utf8(buffer, len[x], rc, -1, test)) + return FALSE; + } + + return TRUE; +} + +static BOOL test_convert_to_utf8_n(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1, + test->utf8len - 1 }; + const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const SSIZE_T rc2 = ConvertWCharNToUtf8(test->utf16, test->utf16len, NULL, 0); + const size_t wlen = strnlen(test->utf8, test->utf8len); + if ((rc2 < 0) || ((size_t)rc2 != wlen)) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf16len, test, __func__, __LINE__); + fprintf(stderr, + "%s ConvertWCharNToUtf8(%s, %" PRIuz ", NULL, 0) expected %" PRIuz ", got %" PRIdz + "\n", + prefix, test->utf8, test->utf16len, wlen, rc2); + return FALSE; + } + + for (size_t x = 0; x < max; x++) + { + const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1, + test->utf16len - 1 }; + const size_t imax = test->utf16len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1; + + for (size_t y = 0; y < imax; y++) + { + char buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + SSIZE_T rc = ConvertWCharNToUtf8(test->utf16, ilen[x], buffer, len[x]); + if (!compare_utf8(buffer, len[x], rc, ilen[x], test)) + return FALSE; + } + } + + return TRUE; +} + +static BOOL test_conversion(const testcase_t* testcases, size_t count) +{ + WINPR_ASSERT(testcases || (count == 0)); + for (size_t x = 0; x < count; x++) + { + const testcase_t* test = &testcases[x]; + + printf("Running test case %" PRIuz " [%s]\n", x, test->utf8); + if (!test_convert_to_utf16(test)) + return FALSE; + if (!test_convert_to_utf16_n(test)) + return FALSE; + if (!test_convert_to_utf8(test)) + return FALSE; + if (!test_convert_to_utf8_n(test)) + return FALSE; + } + return TRUE; +} + +#if defined(WITH_WINPR_DEPRECATED) + +#define compare_win_utf16(what, buffersize, rc, inputlen, test) \ + compare_win_utf16_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__) +static BOOL compare_win_utf16_int(const WCHAR* what, size_t buffersize, int rc, int inputlen, + const testcase_t* test, const char* fkt, size_t line) +{ + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line); + + WINPR_ASSERT(what || (buffersize == 0)); + WINPR_ASSERT(test); + + BOOL isNullTerminated = TRUE; + if (inputlen > 0) + isNullTerminated = strnlen(test->utf8, inputlen) < inputlen; + size_t welen = _wcsnlen(test->utf16, buffersize); + if (isNullTerminated) + welen++; + + if (buffersize >= welen) + { + if ((inputlen >= 0) && (rc > buffersize)) + { + fprintf(stderr, "%s length does not match expectation: %d > %" PRIuz "\n", prefix, rc, + buffersize); + return FALSE; + } + else if ((inputlen < 0) && (rc != welen)) + { + fprintf(stderr, "%s length does not match expectation: %d != %" PRIuz "\n", prefix, rc, + welen); + return FALSE; + } + } + else + { + if (!check_short_buffer(prefix, rc, buffersize, test, FALSE)) + return FALSE; + } + + if ((rc > 0) && (buffersize > rc)) + { + size_t wlen = _wcsnlen(what, buffersize); + if (isNullTerminated) + wlen++; + if ((inputlen >= 0) && (buffersize < rc)) + { + fprintf(stderr, "%s length does not match wcslen: %d > %" PRIuz "\n", prefix, rc, + buffersize); + return FALSE; + } + else if ((inputlen < 0) && (welen > rc)) + { + fprintf(stderr, "%s length does not match wcslen: %d < %" PRIuz "\n", prefix, rc, wlen); + return FALSE; + } + } + + const size_t cmp_size = MIN(rc, test->utf16len) * sizeof(WCHAR); + if (memcmp(test->utf16, what, cmp_size) != 0) + { + fprintf(stderr, "%s contents does not match expectations: TODO '%s' != '%s'\n", prefix, + test->utf8, test->utf8); + return FALSE; + } + + printf("%s success\n", prefix); + + return TRUE; +} + +#define compare_win_utf8(what, buffersize, rc, inputlen, test) \ + compare_win_utf8_int((what), (buffersize), (rc), (inputlen), (test), __func__, __LINE__) +static BOOL compare_win_utf8_int(const char* what, size_t buffersize, SSIZE_T rc, SSIZE_T inputlen, + const testcase_t* test, const char* fkt, size_t line) +{ + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), buffersize, rc, inputlen, test, fkt, line); + + WINPR_ASSERT(what || (buffersize == 0)); + WINPR_ASSERT(test); + + BOOL isNullTerminated = TRUE; + if (inputlen > 0) + isNullTerminated = _wcsnlen(test->utf16, inputlen) < inputlen; + + size_t slen = strnlen(test->utf8, test->utf8len); + if (isNullTerminated) + slen++; + + if (buffersize > slen) + { + if ((inputlen >= 0) && (rc > buffersize)) + { + fprintf(stderr, "%s length does not match expectation: %" PRIdz " > %" PRIuz "\n", + prefix, rc, buffersize); + return FALSE; + } + else if ((inputlen < 0) && (rc != slen)) + { + fprintf(stderr, "%s length does not match expectation: %" PRIdz " != %" PRIuz "\n", + prefix, rc, slen); + return FALSE; + } + } + else + { + if (!check_short_buffer(prefix, rc, buffersize, test, TRUE)) + return FALSE; + } + + if ((rc > 0) && (buffersize > rc)) + { + size_t wlen = strnlen(what, buffersize); + if (isNullTerminated) + wlen++; + + if (wlen > rc) + { + fprintf(stderr, "%s length does not match wcslen: %" PRIdz " < %" PRIuz "\n", prefix, + rc, wlen); + return FALSE; + } + } + + const size_t cmp_size = MIN(test->utf8len, rc); + if (memcmp(test->utf8, what, cmp_size) != 0) + { + fprintf(stderr, "%s contents does not match expectations: '%s' != '%s'\n", prefix, what, + test->utf8); + return FALSE; + } + printf("%s success\n", prefix); + + return TRUE; +} +#endif + +#if defined(WITH_WINPR_DEPRECATED) +static BOOL test_win_convert_to_utf16(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1, + test->utf16len - 1 }; + const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const int rc2 = MultiByteToWideChar(CP_UTF8, 0, test->utf8, -1, NULL, 0); + const size_t wlen = _wcsnlen(test->utf16, test->utf16len); + if (rc2 != wlen + 1) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__); + fprintf(stderr, + "%s MultiByteToWideChar(CP_UTF8, 0, %s, [-1], NULL, 0) expected %" PRIuz + ", got %d\n", + prefix, test->utf8, wlen + 1, rc2); + return FALSE; + } + for (size_t x = 0; x < max; x++) + { + WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + const int rc = MultiByteToWideChar(CP_UTF8, 0, test->utf8, -1, buffer, len[x]); + if (!compare_win_utf16(buffer, len[x], rc, -1, test)) + return FALSE; + } + + return TRUE; +} + +static BOOL test_win_convert_to_utf16_n(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1, + test->utf16len - 1 }; + const size_t max = test->utf16len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + BOOL isNullTerminated = strnlen(test->utf8, test->utf8len) < test->utf8len; + const int rc2 = MultiByteToWideChar(CP_UTF8, 0, test->utf8, test->utf8len, NULL, 0); + size_t wlen = _wcsnlen(test->utf16, test->utf16len); + if (isNullTerminated) + wlen++; + + if (rc2 != wlen) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf8len, test, __func__, __LINE__); + fprintf(stderr, + "%s MultiByteToWideChar(CP_UTF8, 0, %s, %" PRIuz ", NULL, 0) expected %" PRIuz + ", got %d\n", + prefix, test->utf8, test->utf8len, wlen, rc2); + return FALSE; + } + + for (size_t x = 0; x < max; x++) + { + const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1, + test->utf8len - 1 }; + const size_t imax = test->utf8len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1; + + for (size_t y = 0; y < imax; y++) + { + char mbuffer[TESTCASE_BUFFER_SIZE] = { 0 }; + WCHAR buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + strncpy(mbuffer, test->utf8, test->utf8len); + const int rc = MultiByteToWideChar(CP_UTF8, 0, mbuffer, ilen[x], buffer, len[x]); + if (!compare_win_utf16(buffer, len[x], rc, ilen[x], test)) + return FALSE; + } + } + return TRUE; +} +#endif + +#if defined(WITH_WINPR_DEPRECATED) +static BOOL test_win_convert_to_utf8(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1, + test->utf8len - 1 }; + const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const int rc2 = WideCharToMultiByte(CP_UTF8, 0, test->utf16, -1, NULL, 0, NULL, NULL); + const size_t wlen = strnlen(test->utf8, test->utf8len) + 1; + if (rc2 != wlen) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, -1, test, __func__, __LINE__); + fprintf(stderr, + "%s WideCharToMultiByte(CP_UTF8, 0, %s, -1, NULL, 0, NULL, NULL) expected %" PRIuz + ", got %d\n", + prefix, test->utf8, wlen, rc2); + return FALSE; + } + + for (size_t x = 0; x < max; x++) + { + char buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + int rc = WideCharToMultiByte(CP_UTF8, 0, test->utf16, -1, buffer, len[x], NULL, NULL); + if (!compare_win_utf8(buffer, len[x], rc, -1, test)) + return FALSE; + } + + return TRUE; +} + +static BOOL test_win_convert_to_utf8_n(const testcase_t* test) +{ + const size_t len[] = { TESTCASE_BUFFER_SIZE, test->utf8len, test->utf8len + 1, + test->utf8len - 1 }; + const size_t max = test->utf8len > 0 ? ARRAYSIZE(len) : ARRAYSIZE(len) - 1; + + const BOOL isNullTerminated = _wcsnlen(test->utf16, test->utf16len) < test->utf16len; + const int rc2 = + WideCharToMultiByte(CP_UTF8, 0, test->utf16, test->utf16len, NULL, 0, NULL, NULL); + size_t wlen = strnlen(test->utf8, test->utf8len); + if (isNullTerminated) + wlen++; + + if (rc2 != wlen) + { + char prefix[8192] = { 0 }; + create_prefix(prefix, ARRAYSIZE(prefix), 0, rc2, test->utf16len, test, __func__, __LINE__); + fprintf(stderr, + "%s WideCharToMultiByte(CP_UTF8, 0, %s, %" PRIuz + ", NULL, 0, NULL, NULL) expected %" PRIuz ", got %d\n", + prefix, test->utf8, test->utf16len, wlen, rc2); + return FALSE; + } + + for (size_t x = 0; x < max; x++) + { + const size_t ilen[] = { TESTCASE_BUFFER_SIZE, test->utf16len, test->utf16len + 1, + test->utf16len - 1 }; + const size_t imax = test->utf16len > 0 ? ARRAYSIZE(ilen) : ARRAYSIZE(ilen) - 1; + + for (size_t y = 0; y < imax; y++) + { + WCHAR wbuffer[TESTCASE_BUFFER_SIZE] = { 0 }; + char buffer[TESTCASE_BUFFER_SIZE] = { 0 }; + memcpy(wbuffer, test->utf16, test->utf16len * sizeof(WCHAR)); + const int rc = + WideCharToMultiByte(CP_UTF8, 0, wbuffer, ilen[x], buffer, len[x], NULL, NULL); + if (!compare_win_utf8(buffer, len[x], rc, ilen[x], test)) + return FALSE; + } + } + + return TRUE; +} + +static BOOL test_win_conversion(const testcase_t* testcases, size_t count) +{ + WINPR_ASSERT(testcases || (count == 0)); + for (size_t x = 0; x < count; x++) + { + const testcase_t* test = &testcases[x]; + + printf("Running test case %" PRIuz " [%s]\n", x, test->utf8); + if (!test_win_convert_to_utf16(test)) + return FALSE; + if (!test_win_convert_to_utf16_n(test)) + return FALSE; + if (!test_win_convert_to_utf8(test)) + return FALSE; + if (!test_win_convert_to_utf8_n(test)) + return FALSE; + } + return TRUE; +} +#endif + +#if defined(WITH_WINPR_DEPRECATED) +/* Letters */ + +static BYTE c_cedilla_UTF8[] = "\xC3\xA7\x00"; +static BYTE c_cedilla_UTF16[] = "\xE7\x00\x00\x00"; +static int c_cedilla_cchWideChar = 2; +static int c_cedilla_cbMultiByte = 3; + +/* English */ + +static BYTE en_Hello_UTF8[] = "Hello\0"; +static BYTE en_Hello_UTF16[] = "\x48\x00\x65\x00\x6C\x00\x6C\x00\x6F\x00\x00\x00"; +static int en_Hello_cchWideChar = 6; +static int en_Hello_cbMultiByte = 6; + +static BYTE en_HowAreYou_UTF8[] = "How are you?\0"; +static BYTE en_HowAreYou_UTF16[] = + "\x48\x00\x6F\x00\x77\x00\x20\x00\x61\x00\x72\x00\x65\x00\x20\x00" + "\x79\x00\x6F\x00\x75\x00\x3F\x00\x00\x00"; +static int en_HowAreYou_cchWideChar = 13; +static int en_HowAreYou_cbMultiByte = 13; + +/* French */ + +static BYTE fr_Hello_UTF8[] = "Allo\0"; +static BYTE fr_Hello_UTF16[] = "\x41\x00\x6C\x00\x6C\x00\x6F\x00\x00\x00"; +static int fr_Hello_cchWideChar = 5; +static int fr_Hello_cbMultiByte = 5; + +static BYTE fr_HowAreYou_UTF8[] = + "\x43\x6F\x6D\x6D\x65\x6E\x74\x20\xC3\xA7\x61\x20\x76\x61\x3F\x00"; +static BYTE fr_HowAreYou_UTF16[] = + "\x43\x00\x6F\x00\x6D\x00\x6D\x00\x65\x00\x6E\x00\x74\x00\x20\x00" + "\xE7\x00\x61\x00\x20\x00\x76\x00\x61\x00\x3F\x00\x00\x00"; +static int fr_HowAreYou_cchWideChar = 15; +static int fr_HowAreYou_cbMultiByte = 16; + +/* Russian */ + +static BYTE ru_Hello_UTF8[] = "\xD0\x97\xD0\xB4\xD0\xBE\xD1\x80\xD0\xBE\xD0\xB2\xD0\xBE\x00"; +static BYTE ru_Hello_UTF16[] = "\x17\x04\x34\x04\x3E\x04\x40\x04\x3E\x04\x32\x04\x3E\x04\x00\x00"; +static int ru_Hello_cchWideChar = 8; +static int ru_Hello_cbMultiByte = 15; + +static BYTE ru_HowAreYou_UTF8[] = + "\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB4\xD0\xB5\xD0\xBB\xD0\xB0\x3F\x00"; +static BYTE ru_HowAreYou_UTF16[] = + "\x1A\x04\x30\x04\x3A\x04\x20\x00\x34\x04\x35\x04\x3B\x04\x30\x04" + "\x3F\x00\x00\x00"; +static int ru_HowAreYou_cchWideChar = 10; +static int ru_HowAreYou_cbMultiByte = 17; + +/* Arabic */ + +static BYTE ar_Hello_UTF8[] = "\xD8\xA7\xD9\x84\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\x20\xD8\xB9\xD9" + "\x84\xD9\x8A\xD9\x83\xD9\x85\x00"; +static BYTE ar_Hello_UTF16[] = "\x27\x06\x44\x06\x33\x06\x44\x06\x27\x06\x45\x06\x20\x00\x39\x06" + "\x44\x06\x4A\x06\x43\x06\x45\x06\x00\x00"; +static int ar_Hello_cchWideChar = 13; +static int ar_Hello_cbMultiByte = 24; + +static BYTE ar_HowAreYou_UTF8[] = "\xD9\x83\xD9\x8A\xD9\x81\x20\xD8\xAD\xD8\xA7\xD9\x84\xD9\x83\xD8" + "\x9F\x00"; +static BYTE ar_HowAreYou_UTF16[] = + "\x43\x06\x4A\x06\x41\x06\x20\x00\x2D\x06\x27\x06\x44\x06\x43\x06" + "\x1F\x06\x00\x00"; +static int ar_HowAreYou_cchWideChar = 10; +static int ar_HowAreYou_cbMultiByte = 18; + +/* Chinese */ + +static BYTE ch_Hello_UTF8[] = "\xE4\xBD\xA0\xE5\xA5\xBD\x00"; +static BYTE ch_Hello_UTF16[] = "\x60\x4F\x7D\x59\x00\x00"; +static int ch_Hello_cchWideChar = 3; +static int ch_Hello_cbMultiByte = 7; + +static BYTE ch_HowAreYou_UTF8[] = "\xE4\xBD\xA0\xE5\xA5\xBD\xE5\x90\x97\x00"; +static BYTE ch_HowAreYou_UTF16[] = "\x60\x4F\x7D\x59\x17\x54\x00\x00"; +static int ch_HowAreYou_cchWideChar = 4; +static int ch_HowAreYou_cbMultiByte = 10; + +/* Uppercasing */ + +static BYTE ru_Administrator_lower[] = "\xd0\x90\xd0\xb4\xd0\xbc\xd0\xb8\xd0\xbd\xd0\xb8\xd1\x81" + "\xd1\x82\xd1\x80\xd0\xb0\xd1\x82\xd0\xbe\xd1\x80\x00"; + +static BYTE ru_Administrator_upper[] = "\xd0\x90\xd0\x94\xd0\x9c\xd0\x98\xd0\x9d\xd0\x98\xd0\xa1" + "\xd0\xa2\xd0\xa0\xd0\x90\xd0\xa2\xd0\x9e\xd0\xa0\x00"; + +static void string_hexdump(const BYTE* data, size_t length) +{ + size_t offset = 0; + + char* str = winpr_BinToHexString(data, length, TRUE); + if (!str) + return; + + while (offset < length) + { + const size_t diff = (length - offset) * 3; + WINPR_ASSERT(diff <= INT_MAX); + printf("%04" PRIxz " %.*s\n", offset, (int)diff, &str[offset]); + offset += 16; + } + + free(str); +} + +static int convert_utf8_to_utf16(BYTE* lpMultiByteStr, BYTE* expected_lpWideCharStr, + int expected_cchWideChar) +{ + int rc = -1; + int length = 0; + size_t cbMultiByte = 0; + int cchWideChar = 0; + LPWSTR lpWideCharStr = NULL; + + cbMultiByte = strlen((char*)lpMultiByteStr); + cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpMultiByteStr, -1, NULL, 0); + + printf("MultiByteToWideChar Input UTF8 String:\n"); + string_hexdump(lpMultiByteStr, cbMultiByte + 1); + + printf("MultiByteToWideChar required cchWideChar: %d\n", cchWideChar); + + if (cchWideChar != expected_cchWideChar) + { + printf("MultiByteToWideChar unexpected cchWideChar: actual: %d expected: %d\n", cchWideChar, + expected_cchWideChar); + goto fail; + } + + lpWideCharStr = (LPWSTR)calloc((size_t)cchWideChar, sizeof(WCHAR)); + if (!lpWideCharStr) + { + printf("MultiByteToWideChar: unable to allocate memory for test\n"); + goto fail; + } + lpWideCharStr[cchWideChar - 1] = + 0xFFFF; /* should be overwritten if null terminator is inserted properly */ + length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpMultiByteStr, cbMultiByte + 1, lpWideCharStr, + cchWideChar); + + printf("MultiByteToWideChar converted length (WCHAR): %d\n", length); + + if (!length) + { + DWORD error = GetLastError(); + printf("MultiByteToWideChar error: 0x%08" PRIX32 "\n", error); + goto fail; + } + + if (length != expected_cchWideChar) + { + printf("MultiByteToWideChar unexpected converted length (WCHAR): actual: %d expected: %d\n", + length, expected_cchWideChar); + goto fail; + } + + if (_wcscmp(lpWideCharStr, (WCHAR*)expected_lpWideCharStr) != 0) + { + printf("MultiByteToWideChar unexpected string:\n"); + + printf("UTF8 String:\n"); + string_hexdump(lpMultiByteStr, cbMultiByte + 1); + + printf("UTF16 String (actual):\n"); + string_hexdump((BYTE*)lpWideCharStr, length * sizeof(WCHAR)); + + printf("UTF16 String (expected):\n"); + string_hexdump((BYTE*)expected_lpWideCharStr, expected_cchWideChar * sizeof(WCHAR)); + + goto fail; + } + + printf("MultiByteToWideChar Output UTF16 String:\n"); + string_hexdump((BYTE*)lpWideCharStr, length * sizeof(WCHAR)); + printf("\n"); + + rc = length; +fail: + free(lpWideCharStr); + + return rc; +} +#endif + +#if defined(WITH_WINPR_DEPRECATED) +static int convert_utf16_to_utf8(BYTE* lpWideCharStr, BYTE* expected_lpMultiByteStr, + int expected_cbMultiByte) +{ + int rc = -1; + int length = 0; + int cchWideChar = 0; + int cbMultiByte = 0; + LPSTR lpMultiByteStr = NULL; + + cchWideChar = _wcslen((WCHAR*)lpWideCharStr); + cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpWideCharStr, -1, NULL, 0, NULL, NULL); + + printf("WideCharToMultiByte Input UTF16 String:\n"); + string_hexdump(lpWideCharStr, (cchWideChar + 1) * sizeof(WCHAR)); + + printf("WideCharToMultiByte required cbMultiByte: %d\n", cbMultiByte); + + if (cbMultiByte != expected_cbMultiByte) + { + printf("WideCharToMultiByte unexpected cbMultiByte: actual: %d expected: %d\n", cbMultiByte, + expected_cbMultiByte); + goto fail; + } + + lpMultiByteStr = (LPSTR)malloc(cbMultiByte); + if (!lpMultiByteStr) + { + printf("WideCharToMultiByte: unable to allocate memory for test\n"); + goto fail; + } + lpMultiByteStr[cbMultiByte - 1] = + (CHAR)0xFF; /* should be overwritten if null terminator is inserted properly */ + length = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)lpWideCharStr, cchWideChar + 1, + lpMultiByteStr, cbMultiByte, NULL, NULL); + + printf("WideCharToMultiByte converted length (BYTE): %d\n", length); + + if (!length) + { + DWORD error = GetLastError(); + printf("WideCharToMultiByte error: 0x%08" PRIX32 "\n", error); + goto fail; + } + + if (length != expected_cbMultiByte) + { + printf("WideCharToMultiByte unexpected converted length (BYTE): actual: %d expected: %d\n", + length, expected_cbMultiByte); + goto fail; + } + + if (strcmp(lpMultiByteStr, (char*)expected_lpMultiByteStr) != 0) + { + printf("WideCharToMultiByte unexpected string:\n"); + + printf("UTF16 String:\n"); + string_hexdump((BYTE*)lpWideCharStr, (cchWideChar + 1) * sizeof(WCHAR)); + + printf("UTF8 String (actual):\n"); + string_hexdump((BYTE*)lpMultiByteStr, cbMultiByte); + + printf("UTF8 String (expected):\n"); + string_hexdump((BYTE*)expected_lpMultiByteStr, expected_cbMultiByte); + + goto fail; + } + + printf("WideCharToMultiByte Output UTF8 String:\n"); + string_hexdump((BYTE*)lpMultiByteStr, cbMultiByte); + printf("\n"); + + rc = length; +fail: + free(lpMultiByteStr); + + return rc; +} +#endif + +#if defined(WITH_WINPR_DEPRECATED) +static BOOL test_unicode_uppercasing(BYTE* lower, BYTE* upper) +{ + WCHAR* lowerW = NULL; + int lowerLength = 0; + WCHAR* upperW = NULL; + int upperLength = 0; + + lowerLength = ConvertToUnicode(CP_UTF8, 0, (LPSTR)lower, -1, &lowerW, 0); + upperLength = ConvertToUnicode(CP_UTF8, 0, (LPSTR)upper, -1, &upperW, 0); + + CharUpperBuffW(lowerW, lowerLength); + + if (_wcscmp(lowerW, upperW) != 0) + { + printf("Lowercase String:\n"); + string_hexdump((BYTE*)lowerW, lowerLength * 2); + + printf("Uppercase String:\n"); + string_hexdump((BYTE*)upperW, upperLength * 2); + + return FALSE; + } + + free(lowerW); + free(upperW); + + printf("success\n\n"); + return TRUE; +} +#endif + +#if defined(WITH_WINPR_DEPRECATED) +static BOOL test_ConvertFromUnicode_wrapper(void) +{ + const BYTE src1[] = + "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00\x20\x00\x46\x00" + "\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x40\x00\x40\x00\x40\x00"; + const BYTE src2[] = "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00" + "\x20\x00\x46\x00\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x00\x00"; + /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 */ + const CHAR cmp0[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T', + ' ', 'F', 'O', 'R', 'M', 'A', 'T', 0 }; + CHAR* dst = NULL; + int i = 0; + + /* Test unterminated unicode string: + * ConvertFromUnicode must always null-terminate, even if the src string isn't + */ + + printf("Input UTF16 String:\n"); + string_hexdump((const BYTE*)src1, 19 * sizeof(WCHAR)); + + i = ConvertFromUnicode(CP_UTF8, 0, (const WCHAR*)src1, 16, &dst, 0, NULL, NULL); + if (i != 16) + { + fprintf(stderr, "ConvertFromUnicode failure A1: unexpectedly returned %d instead of 16\n", + i); + goto fail; + } + if (dst == NULL) + { + fprintf(stderr, "ConvertFromUnicode failure A2: destination ist NULL\n"); + goto fail; + } + if ((i = strlen(dst)) != 16) + { + fprintf(stderr, "ConvertFromUnicode failure A3: dst length is %d instead of 16\n", i); + goto fail; + } + if (strcmp(dst, cmp0)) + { + fprintf(stderr, "ConvertFromUnicode failure A4: data mismatch\n"); + goto fail; + } + printf("Output UTF8 String:\n"); + string_hexdump((BYTE*)dst, i + 1); + + free(dst); + dst = NULL; + + /* Test null-terminated string */ + + printf("Input UTF16 String:\n"); + string_hexdump((const BYTE*)src2, (_wcslen((const WCHAR*)src2) + 1) * sizeof(WCHAR)); + + i = ConvertFromUnicode(CP_UTF8, 0, (const WCHAR*)src2, -1, &dst, 0, NULL, NULL); + if (i != 17) + { + fprintf(stderr, "ConvertFromUnicode failure B1: unexpectedly returned %d instead of 17\n", + i); + goto fail; + } + if (dst == NULL) + { + fprintf(stderr, "ConvertFromUnicode failure B2: destination ist NULL\n"); + goto fail; + } + if ((i = strlen(dst)) != 16) + { + fprintf(stderr, "ConvertFromUnicode failure B3: dst length is %d instead of 16\n", i); + goto fail; + } + if (strcmp(dst, cmp0)) + { + fprintf(stderr, "ConvertFromUnicode failure B: data mismatch\n"); + goto fail; + } + printf("Output UTF8 String:\n"); + string_hexdump((BYTE*)dst, i + 1); + + free(dst); + dst = NULL; + + printf("success\n\n"); + + return TRUE; + +fail: + free(dst); + return FALSE; +} + +static BOOL test_ConvertToUnicode_wrapper(void) +{ + /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 */ + const CHAR src1[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T', ' ', + 'F', 'O', 'R', 'M', 'A', 'T', '@', '@', '@' }; + const CHAR src2[] = { 'R', 'I', 'C', 'H', ' ', 'T', 'E', 'X', 'T', + ' ', 'F', 'O', 'R', 'M', 'A', 'T', 0 }; + const BYTE cmp0[] = "\x52\x00\x49\x00\x43\x00\x48\x00\x20\x00\x54\x00\x45\x00\x58\x00\x54\x00" + "\x20\x00\x46\x00\x4f\x00\x52\x00\x4d\x00\x41\x00\x54\x00\x00\x00"; + WCHAR* dst = NULL; + int ii = 0; + size_t i = 0; + + /* Test static string buffers of differing sizes */ + { + char name[] = "someteststring"; + const BYTE cmp[] = { 's', 0, 'o', 0, 'm', 0, 'e', 0, 't', 0, 'e', 0, 's', 0, 't', 0, + 's', 0, 't', 0, 'r', 0, 'i', 0, 'n', 0, 'g', 0, 0, 0 }; + WCHAR xname[128] = { 0 }; + LPWSTR aname = NULL; + LPWSTR wname = &xname[0]; + const size_t len = strnlen(name, ARRAYSIZE(name) - 1); + ii = ConvertToUnicode(CP_UTF8, 0, name, len, &wname, ARRAYSIZE(xname)); + if (ii != (SSIZE_T)len) + goto fail; + + if (memcmp(wname, cmp, sizeof(cmp)) != 0) + goto fail; + + ii = ConvertToUnicode(CP_UTF8, 0, name, len, &aname, 0); + if (ii != (SSIZE_T)len) + goto fail; + ii = memcmp(aname, cmp, sizeof(cmp)); + free(aname); + if (ii != 0) + goto fail; + } + + /* Test unterminated unicode string: + * ConvertToUnicode must always null-terminate, even if the src string isn't + */ + + printf("Input UTF8 String:\n"); + string_hexdump((const BYTE*)src1, 19); + + ii = ConvertToUnicode(CP_UTF8, 0, src1, 16, &dst, 0); + if (ii != 16) + { + fprintf(stderr, "ConvertToUnicode failure A1: unexpectedly returned %d instead of 16\n", + ii); + goto fail; + } + i = (size_t)ii; + if (dst == NULL) + { + fprintf(stderr, "ConvertToUnicode failure A2: destination ist NULL\n"); + goto fail; + } + if ((i = _wcslen(dst)) != 16) + { + fprintf(stderr, "ConvertToUnicode failure A3: dst length is %" PRIuz " instead of 16\n", i); + goto fail; + } + if (_wcscmp(dst, (const WCHAR*)cmp0)) + { + fprintf(stderr, "ConvertToUnicode failure A4: data mismatch\n"); + goto fail; + } + printf("Output UTF16 String:\n"); + string_hexdump((const BYTE*)dst, (i + 1) * sizeof(WCHAR)); + + free(dst); + dst = NULL; + + /* Test null-terminated string */ + + printf("Input UTF8 String:\n"); + string_hexdump((const BYTE*)src2, strlen(src2) + 1); + + i = ConvertToUnicode(CP_UTF8, 0, src2, -1, &dst, 0); + if (i != 17) + { + fprintf(stderr, + "ConvertToUnicode failure B1: unexpectedly returned %" PRIuz " instead of 17\n", i); + goto fail; + } + if (dst == NULL) + { + fprintf(stderr, "ConvertToUnicode failure B2: destination ist NULL\n"); + goto fail; + } + if ((i = _wcslen(dst)) != 16) + { + fprintf(stderr, "ConvertToUnicode failure B3: dst length is %" PRIuz " instead of 16\n", i); + goto fail; + } + if (_wcscmp(dst, (const WCHAR*)cmp0)) + { + fprintf(stderr, "ConvertToUnicode failure B: data mismatch\n"); + goto fail; + } + printf("Output UTF16 String:\n"); + string_hexdump((BYTE*)dst, (i + 1) * 2); + + free(dst); + dst = NULL; + + printf("success\n\n"); + + return TRUE; + +fail: + free(dst); + return FALSE; +} +#endif + +int TestUnicodeConversion(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_conversion(unit_testcases, ARRAYSIZE(unit_testcases))) + return -1; + +#if defined(WITH_WINPR_DEPRECATED) + if (!test_win_conversion(unit_testcases, ARRAYSIZE(unit_testcases))) + return -1; + + /* Letters */ + + printf("Letters\n"); + + if (convert_utf8_to_utf16(c_cedilla_UTF8, c_cedilla_UTF16, c_cedilla_cchWideChar) < 1) + return -1; + + if (convert_utf16_to_utf8(c_cedilla_UTF16, c_cedilla_UTF8, c_cedilla_cbMultiByte) < 1) + return -1; + + /* English */ + + printf("English\n"); + + if (convert_utf8_to_utf16(en_Hello_UTF8, en_Hello_UTF16, en_Hello_cchWideChar) < 1) + return -1; + if (convert_utf8_to_utf16(en_HowAreYou_UTF8, en_HowAreYou_UTF16, en_HowAreYou_cchWideChar) < 1) + return -1; + + if (convert_utf16_to_utf8(en_Hello_UTF16, en_Hello_UTF8, en_Hello_cbMultiByte) < 1) + return -1; + if (convert_utf16_to_utf8(en_HowAreYou_UTF16, en_HowAreYou_UTF8, en_HowAreYou_cbMultiByte) < 1) + return -1; + + /* French */ + + printf("French\n"); + + if (convert_utf8_to_utf16(fr_Hello_UTF8, fr_Hello_UTF16, fr_Hello_cchWideChar) < 1) + return -1; + if (convert_utf8_to_utf16(fr_HowAreYou_UTF8, fr_HowAreYou_UTF16, fr_HowAreYou_cchWideChar) < 1) + return -1; + + if (convert_utf16_to_utf8(fr_Hello_UTF16, fr_Hello_UTF8, fr_Hello_cbMultiByte) < 1) + return -1; + if (convert_utf16_to_utf8(fr_HowAreYou_UTF16, fr_HowAreYou_UTF8, fr_HowAreYou_cbMultiByte) < 1) + return -1; + + /* Russian */ + + printf("Russian\n"); + + if (convert_utf8_to_utf16(ru_Hello_UTF8, ru_Hello_UTF16, ru_Hello_cchWideChar) < 1) + return -1; + if (convert_utf8_to_utf16(ru_HowAreYou_UTF8, ru_HowAreYou_UTF16, ru_HowAreYou_cchWideChar) < 1) + return -1; + + if (convert_utf16_to_utf8(ru_Hello_UTF16, ru_Hello_UTF8, ru_Hello_cbMultiByte) < 1) + return -1; + if (convert_utf16_to_utf8(ru_HowAreYou_UTF16, ru_HowAreYou_UTF8, ru_HowAreYou_cbMultiByte) < 1) + return -1; + + /* Arabic */ + + printf("Arabic\n"); + + if (convert_utf8_to_utf16(ar_Hello_UTF8, ar_Hello_UTF16, ar_Hello_cchWideChar) < 1) + return -1; + if (convert_utf8_to_utf16(ar_HowAreYou_UTF8, ar_HowAreYou_UTF16, ar_HowAreYou_cchWideChar) < 1) + return -1; + + if (convert_utf16_to_utf8(ar_Hello_UTF16, ar_Hello_UTF8, ar_Hello_cbMultiByte) < 1) + return -1; + if (convert_utf16_to_utf8(ar_HowAreYou_UTF16, ar_HowAreYou_UTF8, ar_HowAreYou_cbMultiByte) < 1) + return -1; + + /* Chinese */ + + printf("Chinese\n"); + + if (convert_utf8_to_utf16(ch_Hello_UTF8, ch_Hello_UTF16, ch_Hello_cchWideChar) < 1) + return -1; + if (convert_utf8_to_utf16(ch_HowAreYou_UTF8, ch_HowAreYou_UTF16, ch_HowAreYou_cchWideChar) < 1) + return -1; + + if (convert_utf16_to_utf8(ch_Hello_UTF16, ch_Hello_UTF8, ch_Hello_cbMultiByte) < 1) + return -1; + if (convert_utf16_to_utf8(ch_HowAreYou_UTF16, ch_HowAreYou_UTF8, ch_HowAreYou_cbMultiByte) < 1) + return -1; + +#endif + + /* Uppercasing */ +#if defined(WITH_WINPR_DEPRECATED) + printf("Uppercasing\n"); + + if (!test_unicode_uppercasing(ru_Administrator_lower, ru_Administrator_upper)) + return -1; +#endif + + /* ConvertFromUnicode */ +#if defined(WITH_WINPR_DEPRECATED) + printf("ConvertFromUnicode\n"); + + if (!test_ConvertFromUnicode_wrapper()) + return -1; + + /* ConvertToUnicode */ + + printf("ConvertToUnicode\n"); + + if (!test_ConvertToUnicode_wrapper()) + return -1; +#endif + /* + + printf("----------------------------------------------------------\n\n"); + + if (0) + { + BYTE src[] = { 'R',0,'I',0,'C',0,'H',0,' ',0, 'T',0,'E',0,'X',0,'T',0,' + ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 }; + //BYTE src[] = { 'R',0,'I',0,'C',0,'H',0,' ',0, 0,0, 'T',0,'E',0,'X',0,'T',0,' + ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 }; + //BYTE src[] = { 0,0,'R',0,'I',0,'C',0,'H',0,' ',0, 'T',0,'E',0,'X',0,'T',0,' + ',0,'F',0,'O',0,'R',0,'M',0,'A',0,'T',0,'@',0,'@',0 }; char* dst = NULL; int num; num = + ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) src, 16, &dst, 0, NULL, NULL); + printf("ConvertFromUnicode returned %d dst=[%s]\n", num, dst); + string_hexdump((BYTE*)dst, num+1); + } + if (1) + { + char src[] = "RICH TEXT FORMAT@@@@@@"; + WCHAR *dst = NULL; + int num; + num = ConvertToUnicode(CP_UTF8, 0, src, 16, &dst, 0); + printf("ConvertToUnicode returned %d dst=%p\n", num, (void*) dst); + string_hexdump((BYTE*)dst, num * 2 + 2); + + } + */ + + return 0; +} diff --git a/winpr/libwinpr/crt/unicode.c b/winpr/libwinpr/crt/unicode.c new file mode 100644 index 0000000..123a488 --- /dev/null +++ b/winpr/libwinpr/crt/unicode.c @@ -0,0 +1,657 @@ +/** + * WinPR: Windows Portable Runtime + * Unicode Conversion (CRT) + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +#ifndef _WIN32 + +#include "unicode.h" + +#include "../log.h" +#define TAG WINPR_TAG("unicode") + +/** + * Notes on cross-platform Unicode portability: + * + * Unicode has many possible Unicode Transformation Format (UTF) encodings, + * where some of the most commonly used are UTF-8, UTF-16 and sometimes UTF-32. + * + * The number in the UTF encoding name (8, 16, 32) refers to the number of bits + * per code unit. A code unit is the minimal bit combination that can represent + * a unit of encoded text in the given encoding. For instance, UTF-8 encodes + * the English alphabet using 8 bits (or one byte) each, just like in ASCII. + * + * However, the total number of code points (values in the Unicode codespace) + * only fits completely within 32 bits. This means that for UTF-8 and UTF-16, + * more than one code unit may be required to fully encode a specific value. + * UTF-8 and UTF-16 are variable-width encodings, while UTF-32 is fixed-width. + * + * UTF-8 has the advantage of being backwards compatible with ASCII, and is + * one of the most commonly used Unicode encoding. + * + * UTF-16 is used everywhere in the Windows API. The strategy employed by + * Microsoft to provide backwards compatibility in their API was to create + * an ANSI and a Unicode version of the same function, ending with A (ANSI) + * and W (Wide character, or UTF-16 Unicode). In headers, the original + * function name is replaced by a macro that defines to either the ANSI + * or Unicode version based on the definition of the _UNICODE macro. + * + * UTF-32 has the advantage of being fixed width, but wastes a lot of space + * for English text (4x more than UTF-8, 2x more than UTF-16). + * + * In C, wide character strings are often defined with the wchar_t type. + * Many functions are provided to deal with those wide character strings, + * such as wcslen (strlen equivalent) or wprintf (printf equivalent). + * + * This may lead to some confusion, since many of these functions exist + * on both Windows and Linux, but they are *not* the same! + * + * This sample hello world is a good example: + * + * #include + * + * wchar_t hello[] = L"Hello, World!\n"; + * + * int main(int argc, char** argv) + * { + * wprintf(hello); + * wprintf(L"sizeof(wchar_t): %d\n", sizeof(wchar_t)); + * return 0; + * } + * + * There is a reason why the sample prints the size of the wchar_t type: + * On Windows, wchar_t is two bytes (UTF-16), while on most other systems + * it is 4 bytes (UTF-32). This means that if you write code on Windows, + * use L"" to define a string which is meant to be UTF-16 and not UTF-32, + * you will have a little surprise when trying to port your code to Linux. + * + * Since the Windows API uses UTF-16, not UTF-32, WinPR defines the WCHAR + * type to always be 2-bytes long and uses it instead of wchar_t. Do not + * ever use wchar_t with WinPR unless you know what you are doing. + * + * As for L"", it is unfortunately unusable in a portable way, unless a + * special option is passed to GCC to define wchar_t as being two bytes. + * For string constants that must be UTF-16, it is a pain, but they can + * be defined in a portable way like this: + * + * WCHAR hello[] = { 'H','e','l','l','o','\0' }; + * + * Such strings cannot be passed to native functions like wcslen(), which + * may expect a different wchar_t size. For this reason, WinPR provides + * _wcslen, which expects UTF-16 WCHAR strings on all platforms. + * + */ + +/** \deprecated We no longer export this function, see ConvertUtf8ToWChar family of functions for a + * replacement + * + * Conversion to Unicode (UTF-16) + * MultiByteToWideChar: http://msdn.microsoft.com/en-us/library/windows/desktop/dd319072/ + * + * cbMultiByte is an input size in bytes (BYTE) + * cchWideChar is an output size in wide characters (WCHAR) + * + * Null-terminated UTF-8 strings: + * + * cchWideChar *cannot* be assumed to be cbMultiByte since UTF-8 is variable-width! + * + * Instead, obtain the required cchWideChar output size like this: + * cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) lpMultiByteStr, -1, NULL, 0); + * + * A value of -1 for cbMultiByte indicates that the input string is null-terminated, + * and the null terminator *will* be processed. The size returned by MultiByteToWideChar + * will therefore include the null terminator. Equivalent behavior can be obtained by + * computing the length in bytes of the input buffer, including the null terminator: + * + * cbMultiByte = strlen((char*) lpMultiByteStr) + 1; + * + * An output buffer of the proper size can then be allocated: + * + * lpWideCharStr = (LPWSTR) malloc(cchWideChar * sizeof(WCHAR)); + * + * Since cchWideChar is an output size in wide characters, the actual buffer size is: + * (cchWideChar * sizeof(WCHAR)) or (cchWideChar * 2) + * + * Finally, perform the conversion: + * + * cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) lpMultiByteStr, -1, lpWideCharStr, + * cchWideChar); + * + * The value returned by MultiByteToWideChar corresponds to the number of wide characters written + * to the output buffer, and should match the value obtained on the first call to + * MultiByteToWideChar. + * + */ + +#if !defined(WITH_WINPR_DEPRECATED) +static +#endif + int + MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) +{ + return int_MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, + cchWideChar); +} + +/** \deprecated We no longer export this function, see ConvertWCharToUtf8 family of functions for a + * replacement + * + * Conversion from Unicode (UTF-16) + * WideCharToMultiByte: http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130/ + * + * cchWideChar is an input size in wide characters (WCHAR) + * cbMultiByte is an output size in bytes (BYTE) + * + * Null-terminated UTF-16 strings: + * + * cbMultiByte *cannot* be assumed to be cchWideChar since UTF-8 is variable-width! + * + * Instead, obtain the required cbMultiByte output size like this: + * cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) lpWideCharStr, -1, NULL, 0, NULL, NULL); + * + * A value of -1 for cbMultiByte indicates that the input string is null-terminated, + * and the null terminator *will* be processed. The size returned by WideCharToMultiByte + * will therefore include the null terminator. Equivalent behavior can be obtained by + * computing the length in bytes of the input buffer, including the null terminator: + * + * cchWideChar = _wcslen((WCHAR*) lpWideCharStr) + 1; + * + * An output buffer of the proper size can then be allocated: + * lpMultiByteStr = (LPSTR) malloc(cbMultiByte); + * + * Since cbMultiByte is an output size in bytes, it is the same as the buffer size + * + * Finally, perform the conversion: + * + * cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) lpWideCharStr, -1, lpMultiByteStr, + * cbMultiByte, NULL, NULL); + * + * The value returned by WideCharToMultiByte corresponds to the number of bytes written + * to the output buffer, and should match the value obtained on the first call to + * WideCharToMultiByte. + * + */ + +#if !defined(WITH_WINPR_DEPRECATED) +static +#endif + int + WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + return int_WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, + cbMultiByte, lpDefaultChar, lpUsedDefaultChar); +} + +#endif + +/** + * ConvertToUnicode is a convenience wrapper for MultiByteToWideChar: + * + * If the lpWideCharStr parameter for the converted string points to NULL + * or if the cchWideChar parameter is set to 0 this function will automatically + * allocate the required memory which is guaranteed to be null-terminated + * after the conversion, even if the source c string isn't. + * + * If the cbMultiByte parameter is set to -1 the passed lpMultiByteStr must + * be null-terminated and the required length for the converted string will be + * calculated accordingly. + */ +#if defined(WITH_WINPR_DEPRECATED) +int ConvertToUnicode(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR* lpWideCharStr, int cchWideChar) +{ + int status = 0; + BOOL allocate = FALSE; + + if (!lpMultiByteStr) + return 0; + + if (!lpWideCharStr) + return 0; + + if (cbMultiByte == -1) + { + size_t len = strnlen(lpMultiByteStr, INT_MAX); + if (len >= INT_MAX) + return 0; + cbMultiByte = (int)(len + 1); + } + + if (cchWideChar == 0) + { + cchWideChar = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + allocate = TRUE; + } + else if (!(*lpWideCharStr)) + allocate = TRUE; + + if (cchWideChar < 1) + return 0; + + if (allocate) + { + *lpWideCharStr = (LPWSTR)calloc(cchWideChar + 1, sizeof(WCHAR)); + + if (!(*lpWideCharStr)) + { + // SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + } + + status = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, *lpWideCharStr, + cchWideChar); + + if (status != cchWideChar) + { + if (allocate) + { + free(*lpWideCharStr); + *lpWideCharStr = NULL; + status = 0; + } + } + + return status; +} +#endif + +/** + * ConvertFromUnicode is a convenience wrapper for WideCharToMultiByte: + * + * If the lpMultiByteStr parameter for the converted string points to NULL + * or if the cbMultiByte parameter is set to 0 this function will automatically + * allocate the required memory which is guaranteed to be null-terminated + * after the conversion, even if the source unicode string isn't. + * + * If the cchWideChar parameter is set to -1 the passed lpWideCharStr must + * be null-terminated and the required length for the converted string will be + * calculated accordingly. + */ +#if defined(WITH_WINPR_DEPRECATED) +int ConvertFromUnicode(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR* lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + int status = 0; + BOOL allocate = FALSE; + + if (!lpWideCharStr) + return 0; + + if (!lpMultiByteStr) + return 0; + + if (cchWideChar == -1) + cchWideChar = (int)(_wcslen(lpWideCharStr) + 1); + + if (cbMultiByte == 0) + { + cbMultiByte = + WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, NULL, NULL); + allocate = TRUE; + } + else if (!(*lpMultiByteStr)) + allocate = TRUE; + + if (cbMultiByte < 1) + return 0; + + if (allocate) + { + *lpMultiByteStr = (LPSTR)calloc(1, cbMultiByte + 1); + + if (!(*lpMultiByteStr)) + { + // SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + } + + status = WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, *lpMultiByteStr, + cbMultiByte, lpDefaultChar, lpUsedDefaultChar); + + if ((status != cbMultiByte) && allocate) + { + status = 0; + } + + if ((status <= 0) && allocate) + { + free(*lpMultiByteStr); + *lpMultiByteStr = NULL; + } + + return status; +} +#endif + +/** + * Swap Unicode byte order (UTF16LE <-> UTF16BE) + */ + +const WCHAR* ByteSwapUnicode(WCHAR* wstr, size_t length) +{ + WINPR_ASSERT(wstr || (length == 0)); + + for (size_t x = 0; x < length; x++) + wstr[x] = _byteswap_ushort(wstr[x]); + return wstr; +} + +SSIZE_T ConvertWCharToUtf8(const WCHAR* wstr, char* str, size_t len) +{ + if (!wstr) + { + if (str && len) + str[0] = 0; + return 0; + } + + const size_t wlen = _wcslen(wstr); + return ConvertWCharNToUtf8(wstr, wlen + 1, str, len); +} + +SSIZE_T ConvertWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len) +{ + BOOL isNullTerminated = FALSE; + if (wlen == 0) + return 0; + + WINPR_ASSERT(wstr); + size_t iwlen = _wcsnlen(wstr, wlen); + + if (wlen > INT32_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + if (iwlen < wlen) + { + isNullTerminated = TRUE; + iwlen++; + } + const int rc = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)iwlen, str, (int)MIN(INT32_MAX, len), + NULL, NULL); + if ((rc <= 0) || ((len > 0) && ((size_t)rc > len))) + return -1; + else if (!isNullTerminated) + { + if (str && ((size_t)rc < len)) + str[rc] = '\0'; + return rc; + } + else if ((size_t)rc == len) + { + if (str && (str[rc - 1] != '\0')) + return rc; + } + return rc - 1; +} + +SSIZE_T ConvertMszWCharNToUtf8(const WCHAR* wstr, size_t wlen, char* str, size_t len) +{ + if (wlen == 0) + return 0; + + WINPR_ASSERT(wstr); + + if (wlen > INT32_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + const int iwlen = MIN(INT32_MAX, len); + const int rc = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)wlen, str, (int)iwlen, NULL, NULL); + if ((rc <= 0) || ((len > 0) && (rc > iwlen))) + return -1; + + return rc; +} + +SSIZE_T ConvertUtf8ToWChar(const char* str, WCHAR* wstr, size_t wlen) +{ + if (!str) + { + if (wstr && wlen) + wstr[0] = 0; + return 0; + } + + const size_t len = strlen(str); + return ConvertUtf8NToWChar(str, len + 1, wstr, wlen); +} + +SSIZE_T ConvertUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen) +{ + size_t ilen = strnlen(str, len); + BOOL isNullTerminated = FALSE; + if (len == 0) + return 0; + + WINPR_ASSERT(str); + + if (len > INT32_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + if (ilen < len) + { + isNullTerminated = TRUE; + ilen++; + } + + const int iwlen = MIN(INT32_MAX, wlen); + const int rc = MultiByteToWideChar(CP_UTF8, 0, str, (int)ilen, wstr, (int)iwlen); + if ((rc <= 0) || ((wlen > 0) && (rc > iwlen))) + return -1; + if (!isNullTerminated) + { + if (wstr && (rc < iwlen)) + wstr[rc] = '\0'; + return rc; + } + else if (rc == iwlen) + { + if (wstr && (wstr[rc - 1] != '\0')) + return rc; + } + return rc - 1; +} + +SSIZE_T ConvertMszUtf8NToWChar(const char* str, size_t len, WCHAR* wstr, size_t wlen) +{ + if (len == 0) + return 0; + + WINPR_ASSERT(str); + + if (len > INT32_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + const int iwlen = MIN(INT32_MAX, wlen); + const int rc = MultiByteToWideChar(CP_UTF8, 0, str, (int)len, wstr, (int)iwlen); + if ((rc <= 0) || ((wlen > 0) && (rc > iwlen))) + return -1; + + return rc; +} + +char* ConvertWCharToUtf8Alloc(const WCHAR* wstr, size_t* pUtfCharLength) +{ + char* tmp = NULL; + const SSIZE_T rc = ConvertWCharToUtf8(wstr, NULL, 0); + if (pUtfCharLength) + *pUtfCharLength = 0; + if (rc < 0) + return NULL; + tmp = calloc((size_t)rc + 1ull, sizeof(char)); + if (!tmp) + return NULL; + const SSIZE_T rc2 = ConvertWCharToUtf8(wstr, tmp, (size_t)rc + 1ull); + if (rc2 < 0) + { + free(tmp); + return NULL; + } + WINPR_ASSERT(rc == rc2); + if (pUtfCharLength) + *pUtfCharLength = (size_t)rc2; + return tmp; +} + +char* ConvertWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pUtfCharLength) +{ + char* tmp = NULL; + const SSIZE_T rc = ConvertWCharNToUtf8(wstr, wlen, NULL, 0); + + if (pUtfCharLength) + *pUtfCharLength = 0; + if (rc < 0) + return NULL; + tmp = calloc((size_t)rc + 1ull, sizeof(char)); + if (!tmp) + return NULL; + const SSIZE_T rc2 = ConvertWCharNToUtf8(wstr, wlen, tmp, (size_t)rc + 1ull); + if (rc2 < 0) + { + free(tmp); + return NULL; + } + WINPR_ASSERT(rc == rc2); + if (pUtfCharLength) + *pUtfCharLength = (size_t)rc2; + return tmp; +} + +char* ConvertMszWCharNToUtf8Alloc(const WCHAR* wstr, size_t wlen, size_t* pUtfCharLength) +{ + char* tmp = NULL; + const SSIZE_T rc = ConvertMszWCharNToUtf8(wstr, wlen, NULL, 0); + + if (pUtfCharLength) + *pUtfCharLength = 0; + if (rc < 0) + return NULL; + tmp = calloc((size_t)rc + 1ull, sizeof(char)); + if (!tmp) + return NULL; + const SSIZE_T rc2 = ConvertMszWCharNToUtf8(wstr, wlen, tmp, (size_t)rc + 1ull); + if (rc2 < 0) + { + free(tmp); + return NULL; + } + WINPR_ASSERT(rc == rc2); + if (pUtfCharLength) + *pUtfCharLength = (size_t)rc2; + return tmp; +} + +WCHAR* ConvertUtf8ToWCharAlloc(const char* str, size_t* pSize) +{ + WCHAR* tmp = NULL; + const SSIZE_T rc = ConvertUtf8ToWChar(str, NULL, 0); + if (pSize) + *pSize = 0; + if (rc < 0) + return NULL; + tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR)); + if (!tmp) + return NULL; + const SSIZE_T rc2 = ConvertUtf8ToWChar(str, tmp, (size_t)rc + 1ull); + if (rc2 < 0) + { + free(tmp); + return NULL; + } + WINPR_ASSERT(rc == rc2); + if (pSize) + *pSize = (size_t)rc2; + return tmp; +} + +WCHAR* ConvertUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize) +{ + WCHAR* tmp = NULL; + const SSIZE_T rc = ConvertUtf8NToWChar(str, len, NULL, 0); + if (pSize) + *pSize = 0; + if (rc < 0) + return NULL; + tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR)); + if (!tmp) + return NULL; + const SSIZE_T rc2 = ConvertUtf8NToWChar(str, len, tmp, (size_t)rc + 1ull); + if (rc2 < 0) + { + free(tmp); + return NULL; + } + WINPR_ASSERT(rc == rc2); + if (pSize) + *pSize = (size_t)rc2; + return tmp; +} + +WCHAR* ConvertMszUtf8NToWCharAlloc(const char* str, size_t len, size_t* pSize) +{ + WCHAR* tmp = NULL; + const SSIZE_T rc = ConvertMszUtf8NToWChar(str, len, NULL, 0); + if (pSize) + *pSize = 0; + if (rc < 0) + return NULL; + tmp = calloc((size_t)rc + 1ull, sizeof(WCHAR)); + if (!tmp) + return NULL; + const SSIZE_T rc2 = ConvertMszUtf8NToWChar(str, len, tmp, (size_t)rc + 1ull); + if (rc2 < 0) + { + free(tmp); + return NULL; + } + WINPR_ASSERT(rc == rc2); + if (pSize) + *pSize = (size_t)rc2; + return tmp; +} diff --git a/winpr/libwinpr/crt/unicode.h b/winpr/libwinpr/crt/unicode.h new file mode 100644 index 0000000..9305f2c --- /dev/null +++ b/winpr/libwinpr/crt/unicode.h @@ -0,0 +1,32 @@ +/** + * WinPR: Windows Portable Runtime + * Unicode Conversion (CRT) + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CRT_UNICODE_INTERNAL +#define WINPR_CRT_UNICODE_INTERNAL + +#include + +int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar); + +int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar); +#endif diff --git a/winpr/libwinpr/crt/unicode_android.c b/winpr/libwinpr/crt/unicode_android.c new file mode 100644 index 0000000..2e9bac5 --- /dev/null +++ b/winpr/libwinpr/crt/unicode_android.c @@ -0,0 +1,183 @@ +/** + * WinPR: Windows Portable Runtime + * Unicode Conversion (CRT) + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "../utils/android.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +#include "../log.h" +#define TAG WINPR_TAG("unicode") + +static int convert_int(JNIEnv* env, const void* data, size_t size, void* buffer, size_t buffersize, + BOOL toUTF16) +{ + WINPR_ASSERT(env); + WINPR_ASSERT(data || (size == 0)); + WINPR_ASSERT(buffer || (buffersize == 0)); + + jstring utf8 = (*env)->NewStringUTF(env, "UTF-8"); + jstring utf16 = (*env)->NewStringUTF(env, "UTF-16LE"); + jclass stringClass = (*env)->FindClass(env, "java/lang/String"); + + if (!utf8 || !utf16 || !stringClass) + { + WLog_ERR(TAG, "utf8-%p, utf16=%p, stringClass=%p", utf8, utf16, stringClass); + return -1; + } + + jmethodID constructorID = + (*env)->GetMethodID(env, stringClass, "", "([BLjava/lang/String;)V"); + jmethodID getBytesID = + (*env)->GetMethodID(env, stringClass, "getBytes", "(Ljava/lang/String;)[B"); + if (!constructorID || !getBytesID) + { + WLog_ERR(TAG, "constructorID=%p, getBytesID=%p", constructorID, getBytesID); + return -2; + } + + jbyteArray ret = (*env)->NewByteArray(env, size); + if (!ret) + { + WLog_ERR(TAG, "NewByteArray(%" PRIuz ") failed", size); + return -3; + } + + (*env)->SetByteArrayRegion(env, ret, 0, size, data); + + jobject obj = (*env)->NewObject(env, stringClass, constructorID, ret, toUTF16 ? utf8 : utf16); + if (!obj) + { + WLog_ERR(TAG, "NewObject(String, byteArray, UTF-%d) failed", toUTF16 ? 16 : 8); + return -4; + } + + jbyteArray res = (*env)->CallObjectMethod(env, obj, getBytesID, toUTF16 ? utf16 : utf8); + if (!res) + { + WLog_ERR(TAG, "CallObjectMethod(String, getBytes, UTF-%d) failed", toUTF16 ? 16 : 8); + return -4; + } + + jsize rlen = (*env)->GetArrayLength(env, res); + if (buffersize > 0) + { + if (rlen > buffersize) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + rlen = MIN(rlen, buffersize); + (*env)->GetByteArrayRegion(env, res, 0, rlen, buffer); + } + + if (toUTF16) + rlen /= sizeof(WCHAR); + + return rlen; +} + +static int convert(const void* data, size_t size, void* buffer, size_t buffersize, BOOL toUTF16) +{ + int rc; + JNIEnv* env = NULL; + jboolean attached = winpr_jni_attach_thread(&env); + rc = convert_int(env, data, size, buffer, buffersize, toUTF16); + if (attached) + winpr_jni_detach_thread(); + return rc; +} + +int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) +{ + size_t cbCharLen = (size_t)cbMultiByte; + + WINPR_UNUSED(dwFlags); + + /* If cbMultiByte is 0, the function fails */ + if ((cbMultiByte == 0) || (cbMultiByte < -1)) + return 0; + + if (cchWideChar < 0) + return -1; + + if (cbMultiByte < 0) + { + const size_t len = strlen(lpMultiByteStr); + if (len >= INT32_MAX) + return 0; + cbCharLen = (int)len + 1; + } + else + cbCharLen = cbMultiByte; + + WINPR_ASSERT(lpMultiByteStr); + switch (CodePage) + { + case CP_ACP: + case CP_UTF8: + break; + + default: + WLog_ERR(TAG, "Unsupported encoding %u", CodePage); + return 0; + } + + return convert(lpMultiByteStr, cbCharLen, lpWideCharStr, cchWideChar * sizeof(WCHAR), TRUE); +} + +int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + size_t cbCharLen = (size_t)cchWideChar; + + WINPR_UNUSED(dwFlags); + /* If cchWideChar is 0, the function fails */ + if ((cchWideChar == 0) || (cchWideChar < -1)) + return 0; + + if (cbMultiByte < 0) + return -1; + + WINPR_ASSERT(lpWideCharStr); + /* If cchWideChar is -1, the string is null-terminated */ + if (cchWideChar == -1) + { + const size_t len = _wcslen(lpWideCharStr); + if (len >= INT32_MAX) + return 0; + cbCharLen = (int)len + 1; + } + else + cbCharLen = cchWideChar; + + /* + * if cbMultiByte is 0, the function returns the required buffer size + * in bytes for lpMultiByteStr and makes no use of the output parameter itself. + */ + return convert(lpWideCharStr, cbCharLen * sizeof(WCHAR), lpMultiByteStr, cbMultiByte, FALSE); +} diff --git a/winpr/libwinpr/crt/unicode_apple.m b/winpr/libwinpr/crt/unicode_apple.m new file mode 100644 index 0000000..159252b --- /dev/null +++ b/winpr/libwinpr/crt/unicode_apple.m @@ -0,0 +1,146 @@ +/** + * WinPR: Windows Portable Runtime + * Unicode Conversion (CRT) + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#include +#include + +#include +#include + +#include +#include +#include + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +#include "../log.h" +#define TAG WINPR_TAG("unicode") + +int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) +{ + const BOOL isNullTerminated = cbMultiByte < 0; + + /* If cbMultiByte is 0, the function fails */ + if ((cbMultiByte == 0) || (cbMultiByte < -1)) + return 0; + + /* If cbMultiByte is -1, the string is null-terminated */ + if (isNullTerminated) + { + size_t len = strnlen(lpMultiByteStr, INT32_MAX); + if (len >= INT32_MAX) + return 0; + cbMultiByte = (int)len + 1; + } + + NSString *utf = [[NSString alloc] initWithBytes:lpMultiByteStr + length:cbMultiByte + encoding:NSUTF8StringEncoding]; + if (!utf) + { + WLog_WARN(TAG, "[NSString alloc] NSUTF8StringEncoding failed [%d] '%s'", cbMultiByte, + lpMultiByteStr); + return -1; + } + + const WCHAR *utf16 = + (const WCHAR *)[utf cStringUsingEncoding:NSUTF16LittleEndianStringEncoding]; + const size_t utf16ByteLen = [utf lengthOfBytesUsingEncoding:NSUTF16LittleEndianStringEncoding]; + const size_t utf16CharLen = utf16ByteLen / sizeof(WCHAR); + if (!utf16) + { + WLog_WARN(TAG, "[utf cStringUsingEncoding:NSUTF16LittleEndianStringEncoding] failed"); + return -1; + } + + if (cchWideChar == 0) + return utf16CharLen; + else if (cchWideChar < utf16CharLen) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + else + { + const size_t mlen = MIN((size_t)utf16CharLen, cchWideChar); + const size_t len = _wcsnlen(utf16, mlen); + memcpy(lpWideCharStr, utf16, len * sizeof(WCHAR)); + if ((len < (size_t)cchWideChar) && (len > 0) && (lpWideCharStr[len - 1] != '\0')) + lpWideCharStr[len] = '\0'; + return utf16CharLen; + } +} + +int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + const BOOL isNullTerminated = cchWideChar < 0; + + /* If cchWideChar is 0, the function fails */ + if ((cchWideChar == 0) || (cchWideChar < -1)) + return 0; + + /* If cchWideChar is -1, the string is null-terminated */ + if (isNullTerminated) + { + size_t len = _wcslen(lpWideCharStr); + if (len >= INT32_MAX) + return 0; + cchWideChar = (int)len + 1; + } + + NSString *utf = [[NSString alloc] initWithCharacters:lpWideCharStr length:cchWideChar]; + if (!utf) + { + WLog_WARN(TAG, "[NSString alloc] initWithCharacters failed [%d] 'XXX'", cchWideChar); + return -1; + } + + const char *utf8 = [utf cStringUsingEncoding:NSUTF8StringEncoding]; + const size_t utf8Len = [utf lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + if (!utf8) + { + WLog_WARN(TAG, "[utf cStringUsingEncoding:NSUTF8StringEncoding] failed"); + return -1; + } + + if (cbMultiByte == 0) + return utf8Len; + else if (cbMultiByte < utf8Len) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + else + { + const size_t mlen = MIN((size_t)cbMultiByte, utf8Len); + const size_t len = strnlen(utf8, mlen); + memcpy(lpMultiByteStr, utf8, len * sizeof(char)); + if ((len < (size_t)cbMultiByte) && (len > 0) && (lpMultiByteStr[len - 1] != '\0')) + lpMultiByteStr[len] = '\0'; + return utf8Len; + } +} diff --git a/winpr/libwinpr/crt/unicode_builtin.c b/winpr/libwinpr/crt/unicode_builtin.c new file mode 100644 index 0000000..e56ddd7 --- /dev/null +++ b/winpr/libwinpr/crt/unicode_builtin.c @@ -0,0 +1,695 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + +Conversions between UTF32, UTF-16, and UTF-8. Source code file. +Author: Mark E. Davis, 1994. +Rev History: Rick McGowan, fixes & updates May 2001. +Sept 2001: fixed const & error conditions per +mods suggested by S. Parent & A. Lillich. +June 2002: Tim Dodd added detection and handling of incomplete +source sequences, enhanced error detection, added casts +to eliminate compiler warnings. +July 2003: slight mods to back out aggressive FFFE detection. +Jan 2004: updated switches in from-UTF8 conversions. +Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + +See the header file "utf.h" for complete documentation. + +------------------------------------------------------------------------ */ + +#include +#include +#include + +#include "unicode.h" + +#include "../log.h" +#define TAG WINPR_TAG("unicode") + +/* + * Character Types: + * + * UTF8: uint8_t 8 bits + * UTF16: uint16_t 16 bits + * UTF32: uint32_t 32 bits + */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (uint32_t)0x0000FFFD +#define UNI_MAX_BMP (uint32_t)0x0000FFFF +#define UNI_MAX_UTF16 (uint32_t)0x0010FFFF +#define UNI_MAX_UTF32 (uint32_t)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (uint32_t)0x0010FFFF + +typedef enum +{ + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum +{ + strictConversion = 0, + lenientConversion +} ConversionFlags; + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const uint32_t halfBase = 0x0010000UL; +static const uint32_t halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (uint32_t)0xD800 +#define UNI_SUR_HIGH_END (uint32_t)0xDBFF +#define UNI_SUR_LOW_START (uint32_t)0xDC00 +#define UNI_SUR_LOW_END (uint32_t)0xDFFF + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const uint32_t offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const uint8_t firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +static ConversionResult winpr_ConvertUTF16toUTF8_Internal(const uint16_t** sourceStart, + const uint16_t* sourceEnd, + uint8_t** targetStart, uint8_t* targetEnd, + ConversionFlags flags) +{ + bool computeLength = (!targetEnd) ? true : false; + const uint16_t* source = *sourceStart; + uint8_t* target = *targetStart; + ConversionResult result = conversionOK; + + while (source < sourceEnd) + { + uint32_t ch = 0; + unsigned short bytesToWrite = 0; + const uint32_t byteMask = 0xBF; + const uint32_t byteMark = 0x80; + const uint16_t* oldSource = + source; /* In case we have to back up because of target overflow. */ + + ch = *source++; + + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) + { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) + { + uint32_t ch2 = *source; + + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) + { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + + halfBase; + ++source; + } + else if (flags == strictConversion) + { + /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + else + { + /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } + else if (flags == strictConversion) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) + { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + + /* Figure out how many bytes the result will require */ + if (ch < (uint32_t)0x80) + { + bytesToWrite = 1; + } + else if (ch < (uint32_t)0x800) + { + bytesToWrite = 2; + } + else if (ch < (uint32_t)0x10000) + { + bytesToWrite = 3; + } + else if (ch < (uint32_t)0x110000) + { + bytesToWrite = 4; + } + else + { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + + if ((target > targetEnd) && (!computeLength)) + { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + + if (!computeLength) + { + switch (bytesToWrite) + { + /* note: everything falls through. */ + case 4: + *--target = (uint8_t)((ch | byteMark) & byteMask); + ch >>= 6; + /* fallthrough */ + WINPR_FALLTHROUGH + case 3: + *--target = (uint8_t)((ch | byteMark) & byteMask); + ch >>= 6; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 2: + *--target = (uint8_t)((ch | byteMark) & byteMask); + ch >>= 6; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 1: + *--target = (uint8_t)(ch | firstByteMark[bytesToWrite]); + } + } + else + { + switch (bytesToWrite) + { + /* note: everything falls through. */ + case 4: + --target; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 3: + --target; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 2: + --target; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 1: + --target; + } + } + + target += bytesToWrite; + } + + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static bool isLegalUTF8(const uint8_t* source, int length) +{ + uint8_t a = 0; + const uint8_t* srcptr = source + length; + + switch (length) + { + default: + return false; + + /* Everything else falls through when "true"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 2: + if ((a = (*--srcptr)) > 0xBF) + return false; + + switch (*source) + { + /* no fall-through in this inner switch */ + case 0xE0: + if (a < 0xA0) + return false; + + break; + + case 0xED: + if (a > 0x9F) + return false; + + break; + + case 0xF0: + if (a < 0x90) + return false; + + break; + + case 0xF4: + if (a > 0x8F) + return false; + + break; + + default: + if (a < 0x80) + return false; + break; + } + /* fallthrough */ + WINPR_FALLTHROUGH + + case 1: + if (*source >= 0x80 && *source < 0xC2) + return false; + } + + if (*source > 0xF4) + return false; + + return true; +} + +/* --------------------------------------------------------------------- */ + +static ConversionResult winpr_ConvertUTF8toUTF16_Internal(const uint8_t** sourceStart, + const uint8_t* sourceEnd, + uint16_t** targetStart, + uint16_t* targetEnd, + ConversionFlags flags) +{ + bool computeLength = (!targetEnd) ? true : false; + ConversionResult result = conversionOK; + const uint8_t* source = *sourceStart; + uint16_t* target = *targetStart; + + while (source < sourceEnd) + { + uint32_t ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + + if ((source + extraBytesToRead) >= sourceEnd) + { + result = sourceExhausted; + break; + } + + /* Do this check whether lenient or strict */ + if (!isLegalUTF8(source, extraBytesToRead + 1)) + { + result = sourceIllegal; + break; + } + + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) + { + case 5: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + /* fallthrough */ + WINPR_FALLTHROUGH + + case 4: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + /* fallthrough */ + WINPR_FALLTHROUGH + + case 3: + ch += *source++; + ch <<= 6; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 2: + ch += *source++; + ch <<= 6; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 1: + ch += *source++; + ch <<= 6; + /* fallthrough */ + WINPR_FALLTHROUGH + + case 0: + ch += *source++; + } + + ch -= offsetsFromUTF8[extraBytesToRead]; + + if ((target >= targetEnd) && (!computeLength)) + { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + + if (ch <= UNI_MAX_BMP) + { + /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + { + if (flags == strictConversion) + { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + else + { + if (!computeLength) + *target++ = UNI_REPLACEMENT_CHAR; + else + target++; + } + } + else + { + if (!computeLength) + *target++ = (uint16_t)ch; /* normal case */ + else + target++; + } + } + else if (ch > UNI_MAX_UTF16) + { + if (flags == strictConversion) + { + result = sourceIllegal; + source -= (extraBytesToRead + 1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } + else + { + if (!computeLength) + *target++ = UNI_REPLACEMENT_CHAR; + else + target++; + } + } + else + { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if ((target + 1 >= targetEnd) && (!computeLength)) + { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + + ch -= halfBase; + + if (!computeLength) + { + *target++ = (uint16_t)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (uint16_t)((ch & halfMask) + UNI_SUR_LOW_START); + } + else + { + target++; + target++; + } + } + } + + *sourceStart = source; + *targetStart = target; + return result; +} + +/** + * WinPR built-in Unicode API + */ + +static int winpr_ConvertUTF8toUTF16(const uint8_t* src, int cchSrc, uint16_t* dst, int cchDst) +{ + size_t length = 0; + uint16_t* dstBeg = NULL; + uint16_t* dstEnd = NULL; + const uint8_t* srcBeg = NULL; + const uint8_t* srcEnd = NULL; + ConversionResult result = sourceIllegal; + + if (cchSrc == -1) + cchSrc = strlen((char*)src) + 1; + + srcBeg = src; + srcEnd = &src[cchSrc]; + + if (cchDst == 0) + { + result = + winpr_ConvertUTF8toUTF16_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion); + + length = dstBeg - (uint16_t*)NULL; + } + else + { + dstBeg = dst; + dstEnd = &dst[cchDst]; + + result = + winpr_ConvertUTF8toUTF16_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion); + + length = dstBeg - dst; + } + + if (result == targetExhausted) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + return (result == conversionOK) ? length : 0; +} + +static int winpr_ConvertUTF16toUTF8(const uint16_t* src, int cchSrc, uint8_t* dst, int cchDst) +{ + size_t length = 0; + uint8_t* dstBeg = NULL; + uint8_t* dstEnd = NULL; + const uint16_t* srcBeg = NULL; + const uint16_t* srcEnd = NULL; + ConversionResult result = sourceIllegal; + + if (cchSrc == -1) + cchSrc = _wcslen((uint16_t*)src) + 1; + + srcBeg = src; + srcEnd = &src[cchSrc]; + + if (cchDst == 0) + { + result = + winpr_ConvertUTF16toUTF8_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion); + + length = dstBeg - ((uint8_t*)NULL); + } + else + { + dstBeg = dst; + dstEnd = &dst[cchDst]; + + result = + winpr_ConvertUTF16toUTF8_Internal(&srcBeg, srcEnd, &dstBeg, dstEnd, strictConversion); + + length = dstBeg - dst; + } + + if (result == targetExhausted) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + return (result == conversionOK) ? length : 0; +} + +/* --------------------------------------------------------------------- */ + +int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) +{ + size_t cbCharLen = (size_t)cbMultiByte; + + WINPR_UNUSED(dwFlags); + + /* If cbMultiByte is 0, the function fails */ + if ((cbMultiByte == 0) || (cbMultiByte < -1)) + return 0; + + if (cchWideChar < 0) + return -1; + + if (cbMultiByte < 0) + { + const size_t len = strlen(lpMultiByteStr); + if (len >= INT32_MAX) + return 0; + cbCharLen = (int)len + 1; + } + else + cbCharLen = cbMultiByte; + + WINPR_ASSERT(lpMultiByteStr); + switch (CodePage) + { + case CP_ACP: + case CP_UTF8: + break; + + default: + WLog_ERR(TAG, "Unsupported encoding %u", CodePage); + return 0; + } + + return winpr_ConvertUTF8toUTF16((const uint8_t*)lpMultiByteStr, cbCharLen, + (uint16_t*)lpWideCharStr, cchWideChar); +} + +int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + size_t cbCharLen = (size_t)cchWideChar; + + WINPR_UNUSED(dwFlags); + /* If cchWideChar is 0, the function fails */ + if ((cchWideChar == 0) || (cchWideChar < -1)) + return 0; + + if (cbMultiByte < 0) + return -1; + + WINPR_ASSERT(lpWideCharStr); + /* If cchWideChar is -1, the string is null-terminated */ + if (cchWideChar == -1) + { + const size_t len = _wcslen(lpWideCharStr); + if (len >= INT32_MAX) + return 0; + cbCharLen = (int)len + 1; + } + else + cbCharLen = cchWideChar; + + /* + * if cbMultiByte is 0, the function returns the required buffer size + * in bytes for lpMultiByteStr and makes no use of the output parameter itself. + */ + + return winpr_ConvertUTF16toUTF8((const uint16_t*)lpWideCharStr, cbCharLen, + (uint8_t*)lpMultiByteStr, cbMultiByte); +} diff --git a/winpr/libwinpr/crt/unicode_icu.c b/winpr/libwinpr/crt/unicode_icu.c new file mode 100644 index 0000000..1ebc558 --- /dev/null +++ b/winpr/libwinpr/crt/unicode_icu.c @@ -0,0 +1,237 @@ +/** + * WinPR: Windows Portable Runtime + * Unicode Conversion (CRT) + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +#include +#include + +#include "unicode.h" + +#include "../log.h" +#define TAG WINPR_TAG("unicode") + +#define UCNV_CONVERT 1 + +int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) +{ + const BOOL isNullTerminated = cbMultiByte < 0; + + WINPR_UNUSED(dwFlags); + + /* If cbMultiByte is 0, the function fails */ + + if ((cbMultiByte == 0) || (cbMultiByte < -1)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + size_t len = 0; + if (isNullTerminated) + len = strlen(lpMultiByteStr) + 1; + else + len = cbMultiByte; + + if (len >= INT_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + cbMultiByte = (int)len; + + /* + * if cchWideChar is 0, the function returns the required buffer size + * in characters for lpWideCharStr and makes no use of the output parameter itself. + */ + { + UErrorCode error = U_ZERO_ERROR; + int32_t targetLength = -1; + + switch (CodePage) + { + case CP_ACP: + case CP_UTF8: + break; + + default: + WLog_ERR(TAG, "Unsupported encoding %u", CodePage); + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + const int32_t targetCapacity = cchWideChar; +#if defined(UCNV_CONVERT) + char* targetStart = (char*)lpWideCharStr; + targetLength = + ucnv_convert("UTF-16LE", "UTF-8", targetStart, targetCapacity * (int32_t)sizeof(WCHAR), + lpMultiByteStr, cbMultiByte, &error); + if (targetLength > 0) + targetLength /= sizeof(WCHAR); +#else + WCHAR* targetStart = lpWideCharStr; + u_strFromUTF8(targetStart, targetCapacity, &targetLength, lpMultiByteStr, cbMultiByte, + &error); +#endif + + switch (error) + { + case U_BUFFER_OVERFLOW_ERROR: + if (targetCapacity > 0) + { + cchWideChar = 0; + WLog_ERR(TAG, "insufficient buffer supplied, got %d, required %d", + targetCapacity, targetLength); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + } + else + cchWideChar = targetLength; + break; + case U_STRING_NOT_TERMINATED_WARNING: + cchWideChar = targetLength; + break; + case U_ZERO_ERROR: + cchWideChar = targetLength; + break; + default: + WLog_WARN(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "]", u_errorName(error), + error); + if (U_FAILURE(error)) + { + WLog_ERR(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "] is fatal", + u_errorName(error), error); + cchWideChar = 0; + SetLastError(ERROR_NO_UNICODE_TRANSLATION); + } + else + cchWideChar = targetLength; + break; + } + } + + return cchWideChar; +} + +int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + /* If cchWideChar is 0, the function fails */ + + if ((cchWideChar == 0) || (cchWideChar < -1)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + /* If cchWideChar is -1, the string is null-terminated */ + + size_t len = 0; + if (cchWideChar == -1) + len = _wcslen(lpWideCharStr) + 1; + else + len = cchWideChar; + + if (len >= INT32_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + cchWideChar = (int)len; + + /* + * if cbMultiByte is 0, the function returns the required buffer size + * in bytes for lpMultiByteStr and makes no use of the output parameter itself. + */ + { + UErrorCode error = U_ZERO_ERROR; + int32_t targetLength = -1; + + switch (CodePage) + { + case CP_ACP: + case CP_UTF8: + break; + + default: + WLog_ERR(TAG, "Unsupported encoding %u", CodePage); + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + char* targetStart = lpMultiByteStr; + const int32_t targetCapacity = cbMultiByte; +#if defined(UCNV_CONVERT) + const char* str = (const char*)lpWideCharStr; + targetLength = ucnv_convert("UTF-8", "UTF-16LE", targetStart, targetCapacity, str, + cchWideChar * (int32_t)sizeof(WCHAR), &error); +#else + u_strToUTF8(targetStart, targetCapacity, &targetLength, lpWideCharStr, cchWideChar, &error); +#endif + switch (error) + { + case U_BUFFER_OVERFLOW_ERROR: + if (targetCapacity > 0) + { + WLog_ERR(TAG, "insufficient buffer supplied, got %d, required %d", + targetCapacity, targetLength); + cbMultiByte = 0; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + } + else + cbMultiByte = targetLength; + break; + case U_STRING_NOT_TERMINATED_WARNING: + cbMultiByte = targetLength; + break; + case U_ZERO_ERROR: + cbMultiByte = targetLength; + break; + default: + WLog_WARN(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "]", u_errorName(error), + error); + if (U_FAILURE(error)) + { + WLog_ERR(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "] is fatal", + u_errorName(error), error); + cbMultiByte = 0; + SetLastError(ERROR_NO_UNICODE_TRANSLATION); + } + else + cbMultiByte = targetLength; + break; + } + } + return cbMultiByte; +} diff --git a/winpr/libwinpr/crypto/CMakeLists.txt b/winpr/libwinpr/crypto/CMakeLists.txt new file mode 100644 index 0000000..af8da8a --- /dev/null +++ b/winpr/libwinpr/crypto/CMakeLists.txt @@ -0,0 +1,59 @@ +# WinPR: Windows Portable Runtime +# libwinpr-crypto cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(SRCS + hash.c + rand.c + cipher.c + cert.c + crypto.c + crypto.h +) +if (WITH_INTERNAL_RC4) + list(APPEND SRCS rc4.c rc4.h) +endif() + +if (WITH_INTERNAL_MD4) + list(APPEND SRCS md4.c md4.h) +endif() + +if (WITH_INTERNAL_MD5) + list(APPEND SRCS md5.c md5.h) + list(APPEND SRCS hmac_md5.c hmac_md5.h) +endif() + +winpr_module_add( + ${SRCS} +) + +if(OPENSSL_FOUND) + winpr_include_directory_add(${OPENSSL_INCLUDE_DIR}) + winpr_library_add_private(${OPENSSL_LIBRARIES}) +endif() + +if(MBEDTLS_FOUND) + winpr_include_directory_add(${MBEDTLS_INCLUDE_DIR}) + winpr_library_add_private(${MBEDTLS_LIBRARIES}) +endif() + +if(WIN32) + winpr_library_add_public(crypt32) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/crypto/ModuleOptions.cmake b/winpr/libwinpr/crypto/ModuleOptions.cmake new file mode 100644 index 0000000..39cbfe1 --- /dev/null +++ b/winpr/libwinpr/crypto/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "crypto") +set(MINWIN_LONG_NAME "Cryptography API (CryptoAPI)") +set(MODULE_LIBRARY_NAME "crypt32") + diff --git a/winpr/libwinpr/crypto/cert.c b/winpr/libwinpr/crypto/cert.c new file mode 100644 index 0000000..83b7213 --- /dev/null +++ b/winpr/libwinpr/crypto/cert.c @@ -0,0 +1,223 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012-2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/** + * CertOpenStore + * CertCloseStore + * CertControlStore + * CertDuplicateStore + * CertSaveStore + * CertRegisterPhysicalStore + * CertRegisterSystemStore + * CertAddStoreToCollection + * CertRemoveStoreFromCollection + * CertOpenSystemStoreA + * CertOpenSystemStoreW + * CertEnumPhysicalStore + * CertEnumSystemStore + * CertEnumSystemStoreLocation + * CertSetStoreProperty + * CertUnregisterPhysicalStore + * CertUnregisterSystemStore + * + * CertAddCertificateContextToStore + * CertAddCertificateLinkToStore + * CertAddCRLContextToStore + * CertAddCRLLinkToStore + * CertAddCTLContextToStore + * CertAddCTLLinkToStore + * CertAddEncodedCertificateToStore + * CertAddEncodedCertificateToSystemStoreA + * CertAddEncodedCertificateToSystemStoreW + * CertAddEncodedCRLToStore + * CertAddEncodedCTLToStore + * CertAddSerializedElementToStore + * CertDeleteCertificateFromStore + * CertDeleteCRLFromStore + * CertDeleteCTLFromStore + * CertGetCRLFromStore + * CertEnumCertificatesInStore + * CertEnumCRLsInStore + * CertEnumCTLsInStore + * CertFindCertificateInStore + * CertFindChainInStore + * CertFindCRLInStore + * CertFindCTLInStore + * CertGetIssuerCertificateFromStore + * CertGetStoreProperty + * CertGetSubjectCertificateFromStore + * CertSerializeCertificateStoreElement + * CertSerializeCRLStoreElement + * CertSerializeCTLStoreElement + * + * CertAddEnhancedKeyUsageIdentifier + * CertAddRefServerOcspResponse + * CertAddRefServerOcspResponseContext + * CertAlgIdToOID + * CertCloseServerOcspResponse + * CertCompareCertificate + * CertCompareCertificateName + * CertCompareIntegerBlob + * CertComparePublicKeyInfo + * CertCreateCertificateChainEngine + * CertCreateCertificateContext + * CertCreateContext + * CertCreateCRLContext + * CertCreateCTLContext + * CertCreateCTLEntryFromCertificateContextProperties + * CertCreateSelfSignCertificate + * CertDuplicateCertificateChain + * CertDuplicateCertificateContext + * CertDuplicateCRLContext + * CertDuplicateCTLContext + * CertEnumCertificateContextProperties + * CertEnumCRLContextProperties + * CertEnumCTLContextProperties + * CertEnumSubjectInSortedCTL + * CertFindAttribute + * CertFindCertificateInCRL + * CertFindExtension + * CertFindRDNAttr + * CertFindSubjectInCTL + * CertFindSubjectInSortedCTL + * CertFreeCertificateChain + * CertFreeCertificateChainEngine + * CertFreeCertificateChainList + * CertFreeCertificateContext + * CertFreeCRLContext + * CertFreeCTLContext + * CertFreeServerOcspResponseContext + * CertGetCertificateChain + * CertGetCertificateContextProperty + * CertGetCRLContextProperty + * CertGetCTLContextProperty + * CertGetEnhancedKeyUsage + * CertGetIntendedKeyUsage + * CertGetNameStringA + * CertGetNameStringW + * CertGetPublicKeyLength + * CertGetServerOcspResponseContext + * CertGetValidUsages + * CertIsRDNAttrsInCertificateName + * CertIsStrongHashToSign + * CertIsValidCRLForCertificate + * CertNameToStrA + * CertNameToStrW + * CertOIDToAlgId + * CertOpenServerOcspResponse + * CertRDNValueToStrA + * CertRDNValueToStrW + * CertRemoveEnhancedKeyUsageIdentifier + * CertResyncCertificateChainEngine + * CertRetrieveLogoOrBiometricInfo + * CertSelectCertificateChains + * CertSetCertificateContextPropertiesFromCTLEntry + * CertSetCertificateContextProperty + * CertSetCRLContextProperty + * CertSetCTLContextProperty + * CertSetEnhancedKeyUsage + * CertStrToNameA + * CertStrToNameW + * CertVerifyCertificateChainPolicy + * CertVerifyCRLRevocation + * CertVerifyCRLTimeValidity + * CertVerifyCTLUsage + * CertVerifyRevocation + * CertVerifySubjectCertificateContext + * CertVerifyTimeValidity + * CertVerifyValidityNesting + */ + +#include +#include + +#ifndef _WIN32 + +#include "crypto.h" + +HCERTSTORE CertOpenStore(LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType, + HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const void* pvPara) +{ + WINPR_CERTSTORE* certstore = NULL; + + certstore = (WINPR_CERTSTORE*)calloc(1, sizeof(WINPR_CERTSTORE)); + + if (certstore) + { + certstore->lpszStoreProvider = lpszStoreProvider; + certstore->dwMsgAndCertEncodingType = dwMsgAndCertEncodingType; + } + + return (HCERTSTORE)certstore; +} + +HCERTSTORE CertOpenSystemStoreW(HCRYPTPROV_LEGACY hProv, LPCWSTR szSubsystemProtocol) +{ + HCERTSTORE hCertStore = NULL; + + hCertStore = CertOpenStore(CERT_STORE_PROV_FILE, X509_ASN_ENCODING, hProv, 0, NULL); + + return hCertStore; +} + +HCERTSTORE CertOpenSystemStoreA(HCRYPTPROV_LEGACY hProv, LPCSTR szSubsystemProtocol) +{ + return CertOpenSystemStoreW(hProv, NULL); +} + +BOOL CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) +{ + WINPR_CERTSTORE* certstore = NULL; + + certstore = (WINPR_CERTSTORE*)hCertStore; + + free(certstore); + + return TRUE; +} + +PCCERT_CONTEXT CertFindCertificateInStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, + DWORD dwFindFlags, DWORD dwFindType, + const void* pvFindPara, PCCERT_CONTEXT pPrevCertContext) +{ + return (PCCERT_CONTEXT)1; +} + +PCCERT_CONTEXT CertEnumCertificatesInStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext) +{ + return (PCCERT_CONTEXT)NULL; +} + +DWORD CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, void* pvTypePara, + LPWSTR pszNameString, DWORD cchNameString) +{ + return 0; +} + +DWORD CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, void* pvTypePara, + LPSTR pszNameString, DWORD cchNameString) +{ + return 0; +} + +#endif diff --git a/winpr/libwinpr/crypto/cipher.c b/winpr/libwinpr/crypto/cipher.c new file mode 100644 index 0000000..45eca79 --- /dev/null +++ b/winpr/libwinpr/crypto/cipher.c @@ -0,0 +1,747 @@ +/** + * WinPR: Windows Portable Runtime + * + * Copyright 2015 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "../log.h" +#define TAG WINPR_TAG("crypto.cipher") + +#if defined(WITH_INTERNAL_RC4) +#include "rc4.h" +#endif + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#endif + +#ifdef WITH_MBEDTLS +#include +#include +#include +#include +#if MBEDTLS_VERSION_MAJOR < 3 +#define mbedtls_cipher_info_get_iv_size(_info) (_info->iv_size) +#define mbedtls_cipher_info_get_key_bitlen(_info) (_info->key_bitlen) +#endif +#endif + +/** + * RC4 + */ + +struct winpr_rc4_ctx_private_st +{ +#if defined(WITH_INTERNAL_RC4) + winpr_int_RC4_CTX* ictx; +#else +#if defined(WITH_OPENSSL) + EVP_CIPHER_CTX* ctx; +#endif +#endif +}; + +static WINPR_RC4_CTX* winpr_RC4_New_Internal(const BYTE* key, size_t keylen, BOOL override_fips) +{ + if (!key || (keylen == 0)) + return NULL; + + WINPR_RC4_CTX* ctx = (WINPR_RC4_CTX*)calloc(1, sizeof(WINPR_RC4_CTX)); + if (!ctx) + return NULL; + +#if defined(WITH_INTERNAL_RC4) + WINPR_UNUSED(override_fips); + ctx->ictx = winpr_int_rc4_new(key, keylen); + if (!ctx->ictx) + goto fail; +#elif defined(WITH_OPENSSL) + const EVP_CIPHER* evp = NULL; + + if (keylen > INT_MAX) + goto fail; + + ctx->ctx = EVP_CIPHER_CTX_new(); + if (!ctx->ctx) + goto fail; + + evp = EVP_rc4(); + + if (!evp) + goto fail; + + EVP_CIPHER_CTX_reset(ctx->ctx); + if (EVP_EncryptInit_ex(ctx->ctx, evp, NULL, NULL, NULL) != 1) + goto fail; + + /* EVP_CIPH_FLAG_NON_FIPS_ALLOW does not exist before openssl 1.0.1 */ +#if !(OPENSSL_VERSION_NUMBER < 0x10001000L) + + if (override_fips == TRUE) + EVP_CIPHER_CTX_set_flags(ctx->ctx, EVP_CIPH_FLAG_NON_FIPS_ALLOW); + +#endif + EVP_CIPHER_CTX_set_key_length(ctx->ctx, (int)keylen); + if (EVP_EncryptInit_ex(ctx->ctx, NULL, NULL, key, NULL) != 1) + goto fail; +#endif + return ctx; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + + winpr_RC4_Free(ctx); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +WINPR_RC4_CTX* winpr_RC4_New_Allow_FIPS(const void* key, size_t keylen) +{ + return winpr_RC4_New_Internal(key, keylen, TRUE); +} + +WINPR_RC4_CTX* winpr_RC4_New(const void* key, size_t keylen) +{ + return winpr_RC4_New_Internal(key, keylen, FALSE); +} + +BOOL winpr_RC4_Update(WINPR_RC4_CTX* ctx, size_t length, const void* input, void* output) +{ + WINPR_ASSERT(ctx); + +#if defined(WITH_INTERNAL_RC4) + return winpr_int_rc4_update(ctx->ictx, length, input, output); +#elif defined(WITH_OPENSSL) + WINPR_ASSERT(ctx->ctx); + int outputLength = 0; + if (length > INT_MAX) + return FALSE; + + WINPR_ASSERT(ctx); + if (EVP_CipherUpdate(ctx->ctx, output, &outputLength, input, (int)length) != 1) + return FALSE; + return TRUE; +#endif + return FALSE; +} + +void winpr_RC4_Free(WINPR_RC4_CTX* ctx) +{ + if (!ctx) + return; + +#if defined(WITH_INTERNAL_RC4) + winpr_int_rc4_free(ctx->ictx); +#elif defined(WITH_OPENSSL) + EVP_CIPHER_CTX_free(ctx->ctx); +#endif + free(ctx); +} + +/** + * Generic Cipher API + */ + +#ifdef WITH_OPENSSL +extern const EVP_MD* winpr_openssl_get_evp_md(WINPR_MD_TYPE md); +#endif + +#ifdef WITH_MBEDTLS +extern mbedtls_md_type_t winpr_mbedtls_get_md_type(int md); +#endif + +#if defined(WITH_OPENSSL) +static const EVP_CIPHER* winpr_openssl_get_evp_cipher(int cipher) +{ + const EVP_CIPHER* evp = NULL; + + switch (cipher) + { + case WINPR_CIPHER_NULL: + evp = EVP_enc_null(); + break; + + case WINPR_CIPHER_AES_128_ECB: + evp = EVP_get_cipherbyname("aes-128-ecb"); + break; + + case WINPR_CIPHER_AES_192_ECB: + evp = EVP_get_cipherbyname("aes-192-ecb"); + break; + + case WINPR_CIPHER_AES_256_ECB: + evp = EVP_get_cipherbyname("aes-256-ecb"); + break; + + case WINPR_CIPHER_AES_128_CBC: + evp = EVP_get_cipherbyname("aes-128-cbc"); + break; + + case WINPR_CIPHER_AES_192_CBC: + evp = EVP_get_cipherbyname("aes-192-cbc"); + break; + + case WINPR_CIPHER_AES_256_CBC: + evp = EVP_get_cipherbyname("aes-256-cbc"); + break; + + case WINPR_CIPHER_AES_128_CFB128: + evp = EVP_get_cipherbyname("aes-128-cfb128"); + break; + + case WINPR_CIPHER_AES_192_CFB128: + evp = EVP_get_cipherbyname("aes-192-cfb128"); + break; + + case WINPR_CIPHER_AES_256_CFB128: + evp = EVP_get_cipherbyname("aes-256-cfb128"); + break; + + case WINPR_CIPHER_AES_128_CTR: + evp = EVP_get_cipherbyname("aes-128-ctr"); + break; + + case WINPR_CIPHER_AES_192_CTR: + evp = EVP_get_cipherbyname("aes-192-ctr"); + break; + + case WINPR_CIPHER_AES_256_CTR: + evp = EVP_get_cipherbyname("aes-256-ctr"); + break; + + case WINPR_CIPHER_AES_128_GCM: + evp = EVP_get_cipherbyname("aes-128-gcm"); + break; + + case WINPR_CIPHER_AES_192_GCM: + evp = EVP_get_cipherbyname("aes-192-gcm"); + break; + + case WINPR_CIPHER_AES_256_GCM: + evp = EVP_get_cipherbyname("aes-256-gcm"); + break; + + case WINPR_CIPHER_AES_128_CCM: + evp = EVP_get_cipherbyname("aes-128-ccm"); + break; + + case WINPR_CIPHER_AES_192_CCM: + evp = EVP_get_cipherbyname("aes-192-ccm"); + break; + + case WINPR_CIPHER_AES_256_CCM: + evp = EVP_get_cipherbyname("aes-256-ccm"); + break; + + case WINPR_CIPHER_CAMELLIA_128_ECB: + evp = EVP_get_cipherbyname("camellia-128-ecb"); + break; + + case WINPR_CIPHER_CAMELLIA_192_ECB: + evp = EVP_get_cipherbyname("camellia-192-ecb"); + break; + + case WINPR_CIPHER_CAMELLIA_256_ECB: + evp = EVP_get_cipherbyname("camellia-256-ecb"); + break; + + case WINPR_CIPHER_CAMELLIA_128_CBC: + evp = EVP_get_cipherbyname("camellia-128-cbc"); + break; + + case WINPR_CIPHER_CAMELLIA_192_CBC: + evp = EVP_get_cipherbyname("camellia-192-cbc"); + break; + + case WINPR_CIPHER_CAMELLIA_256_CBC: + evp = EVP_get_cipherbyname("camellia-256-cbc"); + break; + + case WINPR_CIPHER_CAMELLIA_128_CFB128: + evp = EVP_get_cipherbyname("camellia-128-cfb128"); + break; + + case WINPR_CIPHER_CAMELLIA_192_CFB128: + evp = EVP_get_cipherbyname("camellia-192-cfb128"); + break; + + case WINPR_CIPHER_CAMELLIA_256_CFB128: + evp = EVP_get_cipherbyname("camellia-256-cfb128"); + break; + + case WINPR_CIPHER_CAMELLIA_128_CTR: + evp = EVP_get_cipherbyname("camellia-128-ctr"); + break; + + case WINPR_CIPHER_CAMELLIA_192_CTR: + evp = EVP_get_cipherbyname("camellia-192-ctr"); + break; + + case WINPR_CIPHER_CAMELLIA_256_CTR: + evp = EVP_get_cipherbyname("camellia-256-ctr"); + break; + + case WINPR_CIPHER_CAMELLIA_128_GCM: + evp = EVP_get_cipherbyname("camellia-128-gcm"); + break; + + case WINPR_CIPHER_CAMELLIA_192_GCM: + evp = EVP_get_cipherbyname("camellia-192-gcm"); + break; + + case WINPR_CIPHER_CAMELLIA_256_GCM: + evp = EVP_get_cipherbyname("camellia-256-gcm"); + break; + + case WINPR_CIPHER_CAMELLIA_128_CCM: + evp = EVP_get_cipherbyname("camellia-128-ccm"); + break; + + case WINPR_CIPHER_CAMELLIA_192_CCM: + evp = EVP_get_cipherbyname("camellia-192-gcm"); + break; + + case WINPR_CIPHER_CAMELLIA_256_CCM: + evp = EVP_get_cipherbyname("camellia-256-gcm"); + break; + + case WINPR_CIPHER_DES_ECB: + evp = EVP_get_cipherbyname("des-ecb"); + break; + + case WINPR_CIPHER_DES_CBC: + evp = EVP_get_cipherbyname("des-cbc"); + break; + + case WINPR_CIPHER_DES_EDE_ECB: + evp = EVP_get_cipherbyname("des-ede-ecb"); + break; + + case WINPR_CIPHER_DES_EDE_CBC: + evp = EVP_get_cipherbyname("des-ede-cbc"); + break; + + case WINPR_CIPHER_DES_EDE3_ECB: + evp = EVP_get_cipherbyname("des-ede3-ecb"); + break; + + case WINPR_CIPHER_DES_EDE3_CBC: + evp = EVP_get_cipherbyname("des-ede3-cbc"); + break; + + case WINPR_CIPHER_ARC4_128: + evp = EVP_get_cipherbyname("rc4"); + break; + + case WINPR_CIPHER_BLOWFISH_ECB: + evp = EVP_get_cipherbyname("blowfish-ecb"); + break; + + case WINPR_CIPHER_BLOWFISH_CBC: + evp = EVP_get_cipherbyname("blowfish-cbc"); + break; + + case WINPR_CIPHER_BLOWFISH_CFB64: + evp = EVP_get_cipherbyname("blowfish-cfb64"); + break; + + case WINPR_CIPHER_BLOWFISH_CTR: + evp = EVP_get_cipherbyname("blowfish-ctr"); + break; + } + + return evp; +} + +#elif defined(WITH_MBEDTLS) +mbedtls_cipher_type_t winpr_mbedtls_get_cipher_type(int cipher) +{ + mbedtls_cipher_type_t type = MBEDTLS_CIPHER_NONE; + + switch (cipher) + { + case WINPR_CIPHER_NONE: + type = MBEDTLS_CIPHER_NONE; + break; + + case WINPR_CIPHER_NULL: + type = MBEDTLS_CIPHER_NULL; + break; + + case WINPR_CIPHER_AES_128_ECB: + type = MBEDTLS_CIPHER_AES_128_ECB; + break; + + case WINPR_CIPHER_AES_192_ECB: + type = MBEDTLS_CIPHER_AES_192_ECB; + break; + + case WINPR_CIPHER_AES_256_ECB: + type = MBEDTLS_CIPHER_AES_256_ECB; + break; + + case WINPR_CIPHER_AES_128_CBC: + type = MBEDTLS_CIPHER_AES_128_CBC; + break; + + case WINPR_CIPHER_AES_192_CBC: + type = MBEDTLS_CIPHER_AES_192_CBC; + break; + + case WINPR_CIPHER_AES_256_CBC: + type = MBEDTLS_CIPHER_AES_256_CBC; + break; + + case WINPR_CIPHER_AES_128_CFB128: + type = MBEDTLS_CIPHER_AES_128_CFB128; + break; + + case WINPR_CIPHER_AES_192_CFB128: + type = MBEDTLS_CIPHER_AES_192_CFB128; + break; + + case WINPR_CIPHER_AES_256_CFB128: + type = MBEDTLS_CIPHER_AES_256_CFB128; + break; + + case WINPR_CIPHER_AES_128_CTR: + type = MBEDTLS_CIPHER_AES_128_CTR; + break; + + case WINPR_CIPHER_AES_192_CTR: + type = MBEDTLS_CIPHER_AES_192_CTR; + break; + + case WINPR_CIPHER_AES_256_CTR: + type = MBEDTLS_CIPHER_AES_256_CTR; + break; + + case WINPR_CIPHER_AES_128_GCM: + type = MBEDTLS_CIPHER_AES_128_GCM; + break; + + case WINPR_CIPHER_AES_192_GCM: + type = MBEDTLS_CIPHER_AES_192_GCM; + break; + + case WINPR_CIPHER_AES_256_GCM: + type = MBEDTLS_CIPHER_AES_256_GCM; + break; + + case WINPR_CIPHER_AES_128_CCM: + type = MBEDTLS_CIPHER_AES_128_CCM; + break; + + case WINPR_CIPHER_AES_192_CCM: + type = MBEDTLS_CIPHER_AES_192_CCM; + break; + + case WINPR_CIPHER_AES_256_CCM: + type = MBEDTLS_CIPHER_AES_256_CCM; + break; + } + + return type; +} +#endif + +WINPR_CIPHER_CTX* winpr_Cipher_New(int cipher, int op, const void* key, const void* iv) +{ + WINPR_CIPHER_CTX* ctx = NULL; +#if defined(WITH_OPENSSL) + int operation = 0; + const EVP_CIPHER* evp = NULL; + EVP_CIPHER_CTX* octx = NULL; + + if (!(evp = winpr_openssl_get_evp_cipher(cipher))) + return NULL; + + if (!(octx = EVP_CIPHER_CTX_new())) + return NULL; + + operation = (op == WINPR_ENCRYPT) ? 1 : 0; + + if (EVP_CipherInit_ex(octx, evp, NULL, key, iv, operation) != 1) + { + EVP_CIPHER_CTX_free(octx); + return NULL; + } + + EVP_CIPHER_CTX_set_padding(octx, 0); + ctx = (WINPR_CIPHER_CTX*)octx; +#elif defined(WITH_MBEDTLS) + int key_bitlen; + mbedtls_operation_t operation; + mbedtls_cipher_context_t* mctx; + mbedtls_cipher_type_t cipher_type = winpr_mbedtls_get_cipher_type(cipher); + const mbedtls_cipher_info_t* cipher_info = mbedtls_cipher_info_from_type(cipher_type); + + if (!cipher_info) + return NULL; + + if (!(mctx = (mbedtls_cipher_context_t*)calloc(1, sizeof(mbedtls_cipher_context_t)))) + return NULL; + + operation = (op == WINPR_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT; + mbedtls_cipher_init(mctx); + + if (mbedtls_cipher_setup(mctx, cipher_info) != 0) + { + free(mctx); + return NULL; + } + + key_bitlen = mbedtls_cipher_get_key_bitlen(mctx); + + if (mbedtls_cipher_setkey(mctx, key, key_bitlen, operation) != 0) + { + mbedtls_cipher_free(mctx); + free(mctx); + return NULL; + } + + if (mbedtls_cipher_set_padding_mode(mctx, MBEDTLS_PADDING_NONE) != 0) + { + mbedtls_cipher_free(mctx); + free(mctx); + return NULL; + } + + ctx = (WINPR_CIPHER_CTX*)mctx; +#endif + return ctx; +} + +BOOL winpr_Cipher_SetPadding(WINPR_CIPHER_CTX* ctx, BOOL enabled) +{ + WINPR_ASSERT(ctx); + +#if defined(WITH_OPENSSL) + EVP_CIPHER_CTX_set_padding((EVP_CIPHER_CTX*)ctx, enabled); +#elif defined(WITH_MBEDTLS) + mbedtls_cipher_padding_t option = enabled ? MBEDTLS_PADDING_PKCS7 : MBEDTLS_PADDING_NONE; + if (mbedtls_cipher_set_padding_mode((mbedtls_cipher_context_t*)ctx, option) != 0) + return FALSE; +#else + return FALSE; +#endif + return TRUE; +} + +BOOL winpr_Cipher_Update(WINPR_CIPHER_CTX* ctx, const void* input, size_t ilen, void* output, + size_t* olen) +{ +#if defined(WITH_OPENSSL) + int outl = (int)*olen; + + if (ilen > INT_MAX) + { + WLog_ERR(TAG, "input length %" PRIuz " > %d, abort", ilen, INT_MAX); + return FALSE; + } + + WINPR_ASSERT(ctx); + if (EVP_CipherUpdate((EVP_CIPHER_CTX*)ctx, output, &outl, input, (int)ilen) == 1) + { + *olen = (size_t)outl; + return TRUE; + } + +#elif defined(WITH_MBEDTLS) + + if (mbedtls_cipher_update((mbedtls_cipher_context_t*)ctx, input, ilen, output, olen) == 0) + return TRUE; + +#endif + + WLog_ERR(TAG, "Failed to update the data"); + return FALSE; +} + +BOOL winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, void* output, size_t* olen) +{ +#if defined(WITH_OPENSSL) + int outl = (int)*olen; + + if (EVP_CipherFinal_ex((EVP_CIPHER_CTX*)ctx, output, &outl) == 1) + { + *olen = (size_t)outl; + return TRUE; + } + +#elif defined(WITH_MBEDTLS) + + if (mbedtls_cipher_finish((mbedtls_cipher_context_t*)ctx, output, olen) == 0) + return TRUE; + +#endif + return FALSE; +} + +void winpr_Cipher_Free(WINPR_CIPHER_CTX* ctx) +{ + if (!ctx) + return; + +#if defined(WITH_OPENSSL) + EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)ctx); +#elif defined(WITH_MBEDTLS) + mbedtls_cipher_free((mbedtls_cipher_context_t*)ctx); + free(ctx); +#endif +} + +/** + * Key Generation + */ + +int winpr_Cipher_BytesToKey(int cipher, WINPR_MD_TYPE md, const void* salt, const void* data, + size_t datal, size_t count, void* key, void* iv) +{ + /** + * Key and IV generation compatible with OpenSSL EVP_BytesToKey(): + * https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html + */ +#if defined(WITH_OPENSSL) + const EVP_MD* evp_md = NULL; + const EVP_CIPHER* evp_cipher = NULL; + evp_md = winpr_openssl_get_evp_md((WINPR_MD_TYPE)md); + evp_cipher = winpr_openssl_get_evp_cipher(cipher); + return EVP_BytesToKey(evp_cipher, evp_md, salt, data, datal, count, key, iv); +#elif defined(WITH_MBEDTLS) + int rv = 0; + BYTE md_buf[64]; + int niv, nkey, addmd = 0; + unsigned int mds = 0; + mbedtls_md_context_t ctx; + const mbedtls_md_info_t* md_info; + mbedtls_cipher_type_t cipher_type; + const mbedtls_cipher_info_t* cipher_info; + mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); + md_info = mbedtls_md_info_from_type(md_type); + cipher_type = winpr_mbedtls_get_cipher_type(cipher); + cipher_info = mbedtls_cipher_info_from_type(cipher_type); + nkey = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8; + niv = mbedtls_cipher_info_get_iv_size(cipher_info); + + if ((nkey > 64) || (niv > 64)) + return 0; + + if (!data) + return nkey; + + mbedtls_md_init(&ctx); + + if (mbedtls_md_setup(&ctx, md_info, 0) != 0) + goto err; + + while (1) + { + if (mbedtls_md_starts(&ctx) != 0) + goto err; + + if (addmd++) + { + if (mbedtls_md_update(&ctx, md_buf, mds) != 0) + goto err; + } + + if (mbedtls_md_update(&ctx, data, datal) != 0) + goto err; + + if (salt) + { + if (mbedtls_md_update(&ctx, salt, 8) != 0) + goto err; + } + + if (mbedtls_md_finish(&ctx, md_buf) != 0) + goto err; + + mds = mbedtls_md_get_size(md_info); + + for (unsigned int i = 1; i < (unsigned int)count; i++) + { + if (mbedtls_md_starts(&ctx) != 0) + goto err; + + if (mbedtls_md_update(&ctx, md_buf, mds) != 0) + goto err; + + if (mbedtls_md_finish(&ctx, md_buf) != 0) + goto err; + } + + i = 0; + + if (nkey) + { + while (1) + { + if (nkey == 0) + break; + + if (i == mds) + break; + + if (key) + *(BYTE*)(key++) = md_buf[i]; + + nkey--; + i++; + } + } + + if (niv && (i != mds)) + { + while (1) + { + if (niv == 0) + break; + + if (i == mds) + break; + + if (iv) + *(BYTE*)(iv++) = md_buf[i]; + + niv--; + i++; + } + } + + if ((nkey == 0) && (niv == 0)) + break; + } + + rv = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8; +err: + mbedtls_md_free(&ctx); + SecureZeroMemory(md_buf, 64); + return rv; +#else + return 0; +#endif +} diff --git a/winpr/libwinpr/crypto/crypto.c b/winpr/libwinpr/crypto/crypto.c new file mode 100644 index 0000000..26d371f --- /dev/null +++ b/winpr/libwinpr/crypto/crypto.c @@ -0,0 +1,301 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012-2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/** + * CryptAcquireCertificatePrivateKey + * CryptBinaryToStringA + * CryptBinaryToStringW + * CryptCloseAsyncHandle + * CryptCreateAsyncHandle + * CryptCreateKeyIdentifierFromCSP + * CryptDecodeMessage + * CryptDecodeObject + * CryptDecodeObjectEx + * CryptDecryptAndVerifyMessageSignature + * CryptDecryptMessage + * CryptEncodeObject + * CryptEncodeObjectEx + * CryptEncryptMessage + * CryptEnumKeyIdentifierProperties + * CryptEnumOIDFunction + * CryptEnumOIDInfo + * CryptExportPKCS8 + * CryptExportPublicKeyInfo + * CryptExportPublicKeyInfoEx + * CryptExportPublicKeyInfoFromBCryptKeyHandle + * CryptFindCertificateKeyProvInfo + * CryptFindLocalizedName + * CryptFindOIDInfo + * CryptFormatObject + * CryptFreeOIDFunctionAddress + * CryptGetAsyncParam + * CryptGetDefaultOIDDllList + * CryptGetDefaultOIDFunctionAddress + * CryptGetKeyIdentifierProperty + * CryptGetMessageCertificates + * CryptGetMessageSignerCount + * CryptGetOIDFunctionAddress + * CryptGetOIDFunctionValue + * CryptHashCertificate + * CryptHashCertificate2 + * CryptHashMessage + * CryptHashPublicKeyInfo + * CryptHashToBeSigned + * CryptImportPKCS8 + * CryptImportPublicKeyInfo + * CryptImportPublicKeyInfoEx + * CryptImportPublicKeyInfoEx2 + * CryptInitOIDFunctionSet + * CryptInstallDefaultContext + * CryptInstallOIDFunctionAddress + * CryptLoadSip + * CryptMemAlloc + * CryptMemFree + * CryptMemRealloc + * CryptMsgCalculateEncodedLength + * CryptMsgClose + * CryptMsgControl + * CryptMsgCountersign + * CryptMsgCountersignEncoded + * CryptMsgDuplicate + * CryptMsgEncodeAndSignCTL + * CryptMsgGetAndVerifySigner + * CryptMsgGetParam + * CryptMsgOpenToDecode + * CryptMsgOpenToEncode + * CryptMsgSignCTL + * CryptMsgUpdate + * CryptMsgVerifyCountersignatureEncoded + * CryptMsgVerifyCountersignatureEncodedEx + * CryptQueryObject + * CryptRegisterDefaultOIDFunction + * CryptRegisterOIDFunction + * CryptRegisterOIDInfo + * CryptRetrieveTimeStamp + * CryptSetAsyncParam + * CryptSetKeyIdentifierProperty + * CryptSetOIDFunctionValue + * CryptSignAndEncodeCertificate + * CryptSignAndEncryptMessage + * CryptSignCertificate + * CryptSignMessage + * CryptSignMessageWithKey + * CryptSIPAddProvider + * CryptSIPCreateIndirectData + * CryptSIPGetCaps + * CryptSIPGetSignedDataMsg + * CryptSIPLoad + * CryptSIPPutSignedDataMsg + * CryptSIPRemoveProvider + * CryptSIPRemoveSignedDataMsg + * CryptSIPRetrieveSubjectGuid + * CryptSIPRetrieveSubjectGuidForCatalogFile + * CryptSIPVerifyIndirectData + * CryptUninstallDefaultContext + * CryptUnregisterDefaultOIDFunction + * CryptUnregisterOIDFunction + * CryptUnregisterOIDInfo + * CryptUpdateProtectedState + * CryptVerifyCertificateSignature + * CryptVerifyCertificateSignatureEx + * CryptVerifyDetachedMessageHash + * CryptVerifyDetachedMessageSignature + * CryptVerifyMessageHash + * CryptVerifyMessageSignature + * CryptVerifyMessageSignatureWithKey + * CryptVerifyTimeStampSignature + * DbgInitOSS + * DbgPrintf + * PFXExportCertStore + * PFXExportCertStore2 + * PFXExportCertStoreEx + * PFXImportCertStore + * PFXIsPFXBlob + * PFXVerifyPassword + */ + +#ifndef _WIN32 + +#include "crypto.h" + +#include +#include + +static wListDictionary* g_ProtectedMemoryBlocks = NULL; + +BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) +{ + BYTE* pCipherText = NULL; + size_t cbOut = 0; + size_t cbFinal = 0; + WINPR_CIPHER_CTX* enc = NULL; + BYTE randomKey[256] = { 0 }; + WINPR_PROTECTED_MEMORY_BLOCK* pMemBlock = NULL; + + if (dwFlags != CRYPTPROTECTMEMORY_SAME_PROCESS) + return FALSE; + + if (!g_ProtectedMemoryBlocks) + { + g_ProtectedMemoryBlocks = ListDictionary_New(TRUE); + + if (!g_ProtectedMemoryBlocks) + return FALSE; + } + + pMemBlock = (WINPR_PROTECTED_MEMORY_BLOCK*)calloc(1, sizeof(WINPR_PROTECTED_MEMORY_BLOCK)); + + if (!pMemBlock) + return FALSE; + + pMemBlock->pData = pData; + pMemBlock->cbData = cbData; + pMemBlock->dwFlags = dwFlags; + + winpr_RAND(pMemBlock->salt, 8); + winpr_RAND(randomKey, sizeof(randomKey)); + + winpr_Cipher_BytesToKey(WINPR_CIPHER_AES_256_CBC, WINPR_MD_SHA1, pMemBlock->salt, randomKey, + sizeof(randomKey), 4, pMemBlock->key, pMemBlock->iv); + + SecureZeroMemory(randomKey, sizeof(randomKey)); + + cbOut = pMemBlock->cbData + 16 - 1; + pCipherText = (BYTE*)calloc(1, cbOut); + + if (!pCipherText) + goto out; + + if ((enc = winpr_Cipher_New(WINPR_CIPHER_AES_256_CBC, WINPR_ENCRYPT, pMemBlock->key, + pMemBlock->iv)) == NULL) + goto out; + if (!winpr_Cipher_Update(enc, pMemBlock->pData, pMemBlock->cbData, pCipherText, &cbOut)) + goto out; + if (!winpr_Cipher_Final(enc, pCipherText + cbOut, &cbFinal)) + goto out; + winpr_Cipher_Free(enc); + + CopyMemory(pMemBlock->pData, pCipherText, pMemBlock->cbData); + free(pCipherText); + + return ListDictionary_Add(g_ProtectedMemoryBlocks, pData, pMemBlock); +out: + free(pMemBlock); + free(pCipherText); + winpr_Cipher_Free(enc); + + return FALSE; +} + +BOOL CryptUnprotectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) +{ + BYTE* pPlainText = NULL; + size_t cbOut = 0; + size_t cbFinal = 0; + WINPR_CIPHER_CTX* dec = NULL; + WINPR_PROTECTED_MEMORY_BLOCK* pMemBlock = NULL; + + if (dwFlags != CRYPTPROTECTMEMORY_SAME_PROCESS) + return FALSE; + + if (!g_ProtectedMemoryBlocks) + return FALSE; + + pMemBlock = + (WINPR_PROTECTED_MEMORY_BLOCK*)ListDictionary_GetItemValue(g_ProtectedMemoryBlocks, pData); + + if (!pMemBlock) + goto out; + + cbOut = pMemBlock->cbData + 16 - 1; + + pPlainText = (BYTE*)malloc(cbOut); + + if (!pPlainText) + goto out; + + if ((dec = winpr_Cipher_New(WINPR_CIPHER_AES_256_CBC, WINPR_DECRYPT, pMemBlock->key, + pMemBlock->iv)) == NULL) + goto out; + if (!winpr_Cipher_Update(dec, pMemBlock->pData, pMemBlock->cbData, pPlainText, &cbOut)) + goto out; + if (!winpr_Cipher_Final(dec, pPlainText + cbOut, &cbFinal)) + goto out; + winpr_Cipher_Free(dec); + + CopyMemory(pMemBlock->pData, pPlainText, pMemBlock->cbData); + SecureZeroMemory(pPlainText, pMemBlock->cbData); + free(pPlainText); + + ListDictionary_Remove(g_ProtectedMemoryBlocks, pData); + + free(pMemBlock); + + return TRUE; + +out: + free(pPlainText); + free(pMemBlock); + winpr_Cipher_Free(dec); + return FALSE; +} + +BOOL CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, + DATA_BLOB* pDataOut) +{ + return TRUE; +} + +BOOL CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR* ppszDataDescr, DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, + DATA_BLOB* pDataOut) +{ + return TRUE; +} + +BOOL CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, + DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags) +{ + return TRUE; +} + +BOOL CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, + DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags) +{ + return TRUE; +} + +BOOL CryptBinaryToStringW(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, + DWORD* pcchString) +{ + return TRUE; +} + +BOOL CryptBinaryToStringA(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPSTR pszString, + DWORD* pcchString) +{ + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/crypto/crypto.h b/winpr/libwinpr/crypto/crypto.h new file mode 100644 index 0000000..c5ec0dd --- /dev/null +++ b/winpr/libwinpr/crypto/crypto.h @@ -0,0 +1,43 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012-2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CRYPTO_PRIVATE_H +#define WINPR_CRYPTO_PRIVATE_H + +#ifndef _WIN32 + +typedef struct +{ + BYTE* pData; + DWORD cbData; + DWORD dwFlags; + BYTE key[32]; + BYTE iv[32]; + BYTE salt[8]; +} WINPR_PROTECTED_MEMORY_BLOCK; + +typedef struct +{ + LPCSTR lpszStoreProvider; + DWORD dwMsgAndCertEncodingType; +} WINPR_CERTSTORE; + +#endif + +#endif /* WINPR_CRYPTO_PRIVATE_H */ diff --git a/winpr/libwinpr/crypto/hash.c b/winpr/libwinpr/crypto/hash.c new file mode 100644 index 0000000..92ece48 --- /dev/null +++ b/winpr/libwinpr/crypto/hash.c @@ -0,0 +1,780 @@ +/** + * WinPR: Windows Portable Runtime + * + * Copyright 2015 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#endif +#endif + +#ifdef WITH_MBEDTLS +#ifdef MBEDTLS_MD5_C +#include +#endif +#include +#include +#if MBEDTLS_VERSION_MAJOR < 3 +#define mbedtls_md_info_from_ctx(_ctx) (_ctx->md_info) +#endif +#endif + +#if defined(WITH_INTERNAL_MD4) +#include "md4.h" +#endif + +#if defined(WITH_INTERNAL_MD5) +#include "md5.h" +#include "hmac_md5.h" +#endif + +#include "../log.h" +#define TAG WINPR_TAG("crypto.hash") + +/** + * HMAC + */ + +#ifdef WITH_OPENSSL +extern const EVP_MD* winpr_openssl_get_evp_md(WINPR_MD_TYPE md); +#endif + +#ifdef WITH_OPENSSL +const EVP_MD* winpr_openssl_get_evp_md(WINPR_MD_TYPE md) +{ + const char* name = winpr_md_type_to_string(md); + if (!name) + return NULL; + return EVP_get_digestbyname(name); +} +#endif + +#ifdef WITH_MBEDTLS +mbedtls_md_type_t winpr_mbedtls_get_md_type(int md) +{ + mbedtls_md_type_t type = MBEDTLS_MD_NONE; + + switch (md) + { + case WINPR_MD_MD5: + type = MBEDTLS_MD_MD5; + break; + + case WINPR_MD_SHA1: + type = MBEDTLS_MD_SHA1; + break; + + case WINPR_MD_SHA224: + type = MBEDTLS_MD_SHA224; + break; + + case WINPR_MD_SHA256: + type = MBEDTLS_MD_SHA256; + break; + + case WINPR_MD_SHA384: + type = MBEDTLS_MD_SHA384; + break; + + case WINPR_MD_SHA512: + type = MBEDTLS_MD_SHA512; + break; + } + + return type; +} +#endif + +struct hash_map +{ + const char* name; + WINPR_MD_TYPE md; +}; +static const struct hash_map hashes[] = { { "md2", WINPR_MD_MD2 }, + { "md4", WINPR_MD_MD4 }, + { "md5", WINPR_MD_MD5 }, + { "sha1", WINPR_MD_SHA1 }, + { "sha224", WINPR_MD_SHA224 }, + { "sha256", WINPR_MD_SHA256 }, + { "sha384", WINPR_MD_SHA384 }, + { "sha512", WINPR_MD_SHA512 }, + { "sha3_224", WINPR_MD_SHA3_224 }, + { "sha3_256", WINPR_MD_SHA3_256 }, + { "sha3_384", WINPR_MD_SHA3_384 }, + { "sha3_512", WINPR_MD_SHA3_512 }, + { "shake128", WINPR_MD_SHAKE128 }, + { "shake256", WINPR_MD_SHAKE256 }, + { NULL, WINPR_MD_NONE } }; + +WINPR_MD_TYPE winpr_md_type_from_string(const char* name) +{ + const struct hash_map* cur = hashes; + while (cur->name) + { + if (_stricmp(cur->name, name) == 0) + return cur->md; + cur++; + } + return WINPR_MD_NONE; +} + +const char* winpr_md_type_to_string(WINPR_MD_TYPE md) +{ + const struct hash_map* cur = hashes; + while (cur->name) + { + if (cur->md == md) + return cur->name; + cur++; + } + return NULL; +} + +struct winpr_hmac_ctx_private_st +{ + WINPR_MD_TYPE md; + +#if defined(WITH_INTERNAL_MD5) + WINPR_HMAC_MD5_CTX hmac_md5; +#endif +#if defined(WITH_OPENSSL) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC_CTX* xhmac; +#else + HMAC_CTX* hmac; +#endif +#endif +#if defined(WITH_MBEDTLS) + mbedtls_md_context_t hmac; +#endif +}; + +WINPR_HMAC_CTX* winpr_HMAC_New(void) +{ + WINPR_HMAC_CTX* ctx = (WINPR_HMAC_CTX*)calloc(1, sizeof(WINPR_HMAC_CTX)); + if (!ctx) + return NULL; +#if defined(WITH_OPENSSL) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + + if (!(ctx->hmac = (HMAC_CTX*)calloc(1, sizeof(HMAC_CTX)))) + goto fail; + + HMAC_CTX_init(ctx->hmac); +#elif OPENSSL_VERSION_NUMBER < 0x30000000L + if (!(ctx->hmac = HMAC_CTX_new())) + goto fail; +#else + EVP_MAC* emac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (!emac) + goto fail; + ctx->xhmac = EVP_MAC_CTX_new(emac); + EVP_MAC_free(emac); + if (!ctx->xhmac) + goto fail; +#endif +#elif defined(WITH_MBEDTLS) + mbedtls_md_init(&ctx->hmac); +#endif + return ctx; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + winpr_HMAC_Free(ctx); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const void* key, size_t keylen) +{ + WINPR_ASSERT(ctx); + + ctx->md = md; + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + hmac_md5_init(&ctx->hmac_md5, key, keylen); + return TRUE; +#endif + default: + break; + } + +#if defined(WITH_OPENSSL) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const char* hash = winpr_md_type_to_string(md); + + if (!ctx->xhmac) + return FALSE; + + const char* param_name = OSSL_MAC_PARAM_DIGEST; + const OSSL_PARAM param[] = { OSSL_PARAM_construct_utf8_string(param_name, hash, 0), + OSSL_PARAM_construct_end() }; + + if (EVP_MAC_init(ctx->xhmac, key, keylen, param) == 1) + return TRUE; +#else + HMAC_CTX* hmac = ctx->hmac; + const EVP_MD* evp = winpr_openssl_get_evp_md(md); + + if (!evp || !hmac) + return FALSE; + + if (keylen > INT_MAX) + return FALSE; +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + HMAC_Init_ex(hmac, key, (int)keylen, evp, NULL); /* no return value on OpenSSL 0.9.x */ + return TRUE; +#else + + if (HMAC_Init_ex(hmac, key, (int)keylen, evp, NULL) == 1) + return TRUE; + +#endif +#endif +#elif defined(WITH_MBEDTLS) + mbedtls_md_context_t* hmac = &ctx->hmac; + mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); + const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(md_type); + + if (!md_info || !hmac) + return FALSE; + + if (mbedtls_md_info_from_ctx(hmac) != md_info) + { + mbedtls_md_free(hmac); /* can be called at any time after mbedtls_md_init */ + + if (mbedtls_md_setup(hmac, md_info, 1) != 0) + return FALSE; + } + + if (mbedtls_md_hmac_starts(hmac, key, keylen) == 0) + return TRUE; + +#endif + return FALSE; +} + +BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const void* input, size_t ilen) +{ + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + hmac_md5_update(&ctx->hmac_md5, input, ilen); + return TRUE; +#endif + default: + break; + } + +#if defined(WITH_OPENSSL) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (EVP_MAC_update(ctx->xhmac, input, ilen) == 1) + return TRUE; +#else + HMAC_CTX* hmac = ctx->hmac; +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + HMAC_Update(hmac, input, ilen); /* no return value on OpenSSL 0.9.x */ + return TRUE; +#else + + if (HMAC_Update(hmac, input, ilen) == 1) + return TRUE; +#endif +#endif +#elif defined(WITH_MBEDTLS) + mbedtls_md_context_t* mdctx = &ctx->hmac; + + if (mbedtls_md_hmac_update(mdctx, input, ilen) == 0) + return TRUE; + +#endif + return FALSE; +} + +BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, void* output, size_t olen) +{ + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + if (olen < WINPR_MD5_DIGEST_LENGTH) + return FALSE; + hmac_md5_finalize(&ctx->hmac_md5, output); + return TRUE; +#endif + default: + break; + } + +#if defined(WITH_OPENSSL) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const int rc = EVP_MAC_final(ctx->xhmac, output, NULL, olen); + if (rc == 1) + return TRUE; +#else + HMAC_CTX* hmac = ctx->hmac; +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + HMAC_Final(hmac, output, NULL); /* no return value on OpenSSL 0.9.x */ + return TRUE; +#else + + if (HMAC_Final(hmac, output, NULL) == 1) + return TRUE; + +#endif +#endif +#elif defined(WITH_MBEDTLS) + mbedtls_md_context_t* mdctx = &ctx->hmac; + + if (mbedtls_md_hmac_finish(mdctx, output) == 0) + return TRUE; + +#endif + return FALSE; +} + +void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx) +{ + if (!ctx) + return; + +#if defined(WITH_OPENSSL) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC_CTX_free(ctx->xhmac); +#else + HMAC_CTX* hmac = ctx->hmac; + + if (hmac) + { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + HMAC_CTX_cleanup(hmac); + free(hmac); +#else + HMAC_CTX_free(hmac); +#endif + } +#endif +#elif defined(WITH_MBEDTLS) + mbedtls_md_context_t* hmac = &ctx->hmac; + + if (hmac) + mbedtls_md_free(hmac); + +#endif + + free(ctx); +} + +BOOL winpr_HMAC(WINPR_MD_TYPE md, const void* key, size_t keylen, const void* input, size_t ilen, + void* output, size_t olen) +{ + BOOL result = FALSE; + WINPR_HMAC_CTX* ctx = winpr_HMAC_New(); + + if (!ctx) + return FALSE; + + if (!winpr_HMAC_Init(ctx, md, key, keylen)) + goto out; + + if (!winpr_HMAC_Update(ctx, input, ilen)) + goto out; + + if (!winpr_HMAC_Final(ctx, output, olen)) + goto out; + + result = TRUE; +out: + winpr_HMAC_Free(ctx); + return result; +} + +/** + * Generic Digest API + */ + +struct winpr_digest_ctx_private_st +{ + WINPR_MD_TYPE md; + +#if defined(WITH_INTERNAL_MD4) + WINPR_MD4_CTX md4; +#endif +#if defined(WITH_INTERNAL_MD5) + WINPR_MD5_CTX md5; +#endif +#if defined(WITH_OPENSSL) + EVP_MD_CTX* mdctx; +#endif +#if defined(WITH_MBEDTLS) + mbedtls_md_context_t* mdctx; +#endif +}; + +WINPR_DIGEST_CTX* winpr_Digest_New(void) +{ + WINPR_DIGEST_CTX* ctx = calloc(1, sizeof(WINPR_DIGEST_CTX)); + if (!ctx) + return NULL; + +#if defined(WITH_OPENSSL) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + ctx->mdctx = EVP_MD_CTX_create(); +#else + ctx->mdctx = EVP_MD_CTX_new(); +#endif + if (!ctx->mdctx) + goto fail; + +#elif defined(WITH_MBEDTLS) + ctx->mdctx = (mbedtls_md_context_t*)calloc(1, sizeof(mbedtls_md_context_t)); + + if (!ctx->mdctx) + goto fail; + + mbedtls_md_init(ctx->mdctx); +#endif + return ctx; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + winpr_Digest_Free(ctx); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +#if defined(WITH_OPENSSL) +static BOOL winpr_Digest_Init_Internal(WINPR_DIGEST_CTX* ctx, const EVP_MD* evp) +{ + WINPR_ASSERT(ctx); + EVP_MD_CTX* mdctx = ctx->mdctx; + + if (!mdctx || !evp) + return FALSE; + + if (EVP_DigestInit_ex(mdctx, evp, NULL) != 1) + { + WLog_ERR(TAG, "Failed to initialize digest %s", winpr_md_type_to_string(ctx->md)); + return FALSE; + } + + return TRUE; +} + +#elif defined(WITH_MBEDTLS) +static BOOL winpr_Digest_Init_Internal(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) +{ + WINPR_ASSERT(ctx); + mbedtls_md_context_t* mdctx = ctx->mdctx; + mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); + const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(md_type); + + if (!md_info) + return FALSE; + + if (mbedtls_md_info_from_ctx(mdctx) != md_info) + { + mbedtls_md_free(mdctx); /* can be called at any time after mbedtls_md_init */ + + if (mbedtls_md_setup(mdctx, md_info, 0) != 0) + return FALSE; + } + + if (mbedtls_md_starts(mdctx) != 0) + return FALSE; + + return TRUE; +} +#endif + +BOOL winpr_Digest_Init_Allow_FIPS(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) +{ + WINPR_ASSERT(ctx); + + ctx->md = md; + switch (md) + { + case WINPR_MD_MD5: +#if defined(WITH_INTERNAL_MD5) + winpr_MD5_Init(&ctx->md5); + return TRUE; +#endif + break; + default: + WLog_ERR(TAG, "Invalid FIPS digest %s requested", winpr_md_type_to_string(md)); + return FALSE; + } + +#if defined(WITH_OPENSSL) + const EVP_MD* evp = winpr_openssl_get_evp_md(md); + EVP_MD_CTX_set_flags(ctx->mdctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + return winpr_Digest_Init_Internal(ctx, evp); +#elif defined(WITH_MBEDTLS) + return winpr_Digest_Init_Internal(ctx, md); +#endif +} + +BOOL winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) +{ + WINPR_ASSERT(ctx); + + ctx->md = md; + switch (md) + { +#if defined(WITH_INTERNAL_MD4) + case WINPR_MD_MD4: + winpr_MD4_Init(&ctx->md4); + return TRUE; +#endif +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + winpr_MD5_Init(&ctx->md5); + return TRUE; +#endif + default: + break; + } + +#if defined(WITH_OPENSSL) + const EVP_MD* evp = winpr_openssl_get_evp_md(md); + return winpr_Digest_Init_Internal(ctx, evp); +#else + return winpr_Digest_Init_Internal(ctx, md); +#endif +} + +BOOL winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const void* input, size_t ilen) +{ + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD4) + case WINPR_MD_MD4: + winpr_MD4_Update(&ctx->md4, input, ilen); + return TRUE; +#endif +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + winpr_MD5_Update(&ctx->md5, input, ilen); + return TRUE; +#endif + default: + break; + } + +#if defined(WITH_OPENSSL) + EVP_MD_CTX* mdctx = ctx->mdctx; + + if (EVP_DigestUpdate(mdctx, input, ilen) != 1) + return FALSE; + +#elif defined(WITH_MBEDTLS) + mbedtls_md_context_t* mdctx = ctx->mdctx; + + if (mbedtls_md_update(mdctx, input, ilen) != 0) + return FALSE; + +#endif + return TRUE; +} + +BOOL winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, void* output, size_t olen) +{ + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD4) + case WINPR_MD_MD4: + if (olen < WINPR_MD4_DIGEST_LENGTH) + return FALSE; + winpr_MD4_Final(output, &ctx->md4); + return TRUE; +#endif +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + if (olen < WINPR_MD5_DIGEST_LENGTH) + return FALSE; + winpr_MD5_Final(output, &ctx->md5); + return TRUE; +#endif + + default: + break; + } + +#if defined(WITH_OPENSSL) + EVP_MD_CTX* mdctx = ctx->mdctx; + + if (EVP_DigestFinal_ex(mdctx, output, NULL) == 1) + return TRUE; + +#elif defined(WITH_MBEDTLS) + mbedtls_md_context_t* mdctx = ctx->mdctx; + + if (mbedtls_md_finish(mdctx, output) == 0) + return TRUE; + +#endif + return FALSE; +} + +BOOL winpr_DigestSign_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE digest, void* key) +{ + WINPR_ASSERT(ctx); + +#if defined(WITH_OPENSSL) + const EVP_MD* evp = winpr_openssl_get_evp_md(digest); + if (!evp) + return FALSE; + + const int rdsi = EVP_DigestSignInit(ctx->mdctx, NULL, evp, NULL, key); + if (rdsi <= 0) + return FALSE; + return TRUE; +#else + return FALSE; +#endif +} + +BOOL winpr_DigestSign_Update(WINPR_DIGEST_CTX* ctx, const void* input, size_t ilen) +{ + WINPR_ASSERT(ctx); + +#if defined(WITH_OPENSSL) + EVP_MD_CTX* mdctx = ctx->mdctx; + + if (EVP_DigestSignUpdate(mdctx, input, ilen) != 1) + return FALSE; + return TRUE; +#else + return FALSE; +#endif +} + +BOOL winpr_DigestSign_Final(WINPR_DIGEST_CTX* ctx, void* output, size_t* piolen) +{ + WINPR_ASSERT(ctx); + +#if defined(WITH_OPENSSL) + EVP_MD_CTX* mdctx = ctx->mdctx; + + return EVP_DigestSignFinal(mdctx, output, piolen) == 1; +#else + return FALSE; +#endif +} + +void winpr_Digest_Free(WINPR_DIGEST_CTX* ctx) +{ + if (!ctx) + return; +#if defined(WITH_OPENSSL) + if (ctx->mdctx) + { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + EVP_MD_CTX_destroy(ctx->mdctx); +#else + EVP_MD_CTX_free(ctx->mdctx); +#endif + } + +#elif defined(WITH_MBEDTLS) + if (ctx->mdctx) + { + mbedtls_md_free(ctx->mdctx); + free(ctx->mdctx); + } + +#endif + free(ctx); +} + +BOOL winpr_Digest_Allow_FIPS(WINPR_MD_TYPE md, const void* input, size_t ilen, void* output, + size_t olen) +{ + BOOL result = FALSE; + WINPR_DIGEST_CTX* ctx = winpr_Digest_New(); + + if (!ctx) + return FALSE; + + if (!winpr_Digest_Init_Allow_FIPS(ctx, md)) + goto out; + + if (!winpr_Digest_Update(ctx, input, ilen)) + goto out; + + if (!winpr_Digest_Final(ctx, output, olen)) + goto out; + + result = TRUE; +out: + winpr_Digest_Free(ctx); + return result; +} + +BOOL winpr_Digest(WINPR_MD_TYPE md, const void* input, size_t ilen, void* output, size_t olen) +{ + BOOL result = FALSE; + WINPR_DIGEST_CTX* ctx = winpr_Digest_New(); + + if (!ctx) + return FALSE; + + if (!winpr_Digest_Init(ctx, md)) + goto out; + + if (!winpr_Digest_Update(ctx, input, ilen)) + goto out; + + if (!winpr_Digest_Final(ctx, output, olen)) + goto out; + + result = TRUE; +out: + winpr_Digest_Free(ctx); + return result; +} diff --git a/winpr/libwinpr/crypto/hmac_md5.c b/winpr/libwinpr/crypto/hmac_md5.c new file mode 100644 index 0000000..5550bbd --- /dev/null +++ b/winpr/libwinpr/crypto/hmac_md5.c @@ -0,0 +1,57 @@ +#include +#include + +#include "hmac_md5.h" +#include "md5.h" +#include + +void hmac_md5_init(WINPR_HMAC_MD5_CTX* ctx, const unsigned char* key, size_t key_len) +{ + const WINPR_HMAC_MD5_CTX empty = { 0 }; + unsigned char k_ipad[KEY_IOPAD_SIZE] = { 0 }; + unsigned char k_opad[KEY_IOPAD_SIZE] = { 0 }; + + assert(ctx); + *ctx = empty; + + if (key_len <= KEY_IOPAD_SIZE) + { + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + } + else + { + WINPR_MD5_CTX lctx = { 0 }; + + winpr_MD5_Init(&lctx); + winpr_MD5_Update(&lctx, key, key_len); + winpr_MD5_Final(k_ipad, &lctx); + memcpy(k_opad, k_ipad, KEY_IOPAD_SIZE); + } + for (size_t i = 0; i < KEY_IOPAD_SIZE; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + winpr_MD5_Init(&ctx->icontext); + winpr_MD5_Update(&ctx->icontext, k_ipad, KEY_IOPAD_SIZE); + + winpr_MD5_Init(&ctx->ocontext); + winpr_MD5_Update(&ctx->ocontext, k_opad, KEY_IOPAD_SIZE); +} + +void hmac_md5_update(WINPR_HMAC_MD5_CTX* ctx, const unsigned char* text, size_t text_len) +{ + assert(ctx); + winpr_MD5_Update(&ctx->icontext, text, text_len); +} + +void hmac_md5_finalize(WINPR_HMAC_MD5_CTX* ctx, unsigned char* hmac) +{ + assert(ctx); + winpr_MD5_Final(hmac, &ctx->icontext); + + winpr_MD5_Update(&ctx->ocontext, hmac, 16); + winpr_MD5_Final(hmac, &ctx->ocontext); +} diff --git a/winpr/libwinpr/crypto/hmac_md5.h b/winpr/libwinpr/crypto/hmac_md5.h new file mode 100644 index 0000000..8ade3e4 --- /dev/null +++ b/winpr/libwinpr/crypto/hmac_md5.h @@ -0,0 +1,19 @@ +#ifndef WINPR_HMAC_MD5 +#define WINPR_HMAC_MD5 + +#include + +#include "md5.h" + +#define KEY_IOPAD_SIZE 64 +typedef struct +{ + WINPR_MD5_CTX icontext; + WINPR_MD5_CTX ocontext; +} WINPR_HMAC_MD5_CTX; + +void hmac_md5_init(WINPR_HMAC_MD5_CTX* ctx, const unsigned char* key, size_t key_len); +void hmac_md5_update(WINPR_HMAC_MD5_CTX* ctx, const unsigned char* text, size_t text_len); +void hmac_md5_finalize(WINPR_HMAC_MD5_CTX* ctx, unsigned char* hmac); + +#endif diff --git a/winpr/libwinpr/crypto/md4.c b/winpr/libwinpr/crypto/md4.c new file mode 100644 index 0000000..0743805 --- /dev/null +++ b/winpr/libwinpr/crypto/md4.c @@ -0,0 +1,275 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD4 Message-Digest Algorithm (RFC 1320). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#include + +#include "md4.h" + +/* + * The basic MD4 functions. + * + * F and G are optimized compared to their RFC 1320 definitions, with the + * optimization for F borrowed from Colin Plumb's MD5 implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The MD4 transformation for all three rounds. + */ +#define STEP(f, a, b, c, d, x, s) \ + (a) += f((b), (c), (d)) + (x); \ + (a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * winpr_MD4_u32plus. In practice, this problem may occur if these MD4 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD4 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) (*(const winpr_MD4_u32plus*)&ptr[(n)*4]) +#define GET(n) SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = (winpr_MD4_u32plus)ptr[(n)*4] | ((winpr_MD4_u32plus)ptr[(n)*4 + 1] << 8) | \ + ((winpr_MD4_u32plus)ptr[(n)*4 + 2] << 16) | \ + ((winpr_MD4_u32plus)ptr[(n)*4 + 3] << 24)) +#define GET(n) (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void* body(WINPR_MD4_CTX* ctx, const void* data, unsigned long size) +{ + const unsigned char* ptr = NULL; + winpr_MD4_u32plus a = 0; + winpr_MD4_u32plus b = 0; + winpr_MD4_u32plus c = 0; + winpr_MD4_u32plus d = 0; + winpr_MD4_u32plus saved_a = 0; + winpr_MD4_u32plus saved_b = 0; + winpr_MD4_u32plus saved_c = 0; + winpr_MD4_u32plus saved_d = 0; + const winpr_MD4_u32plus ac1 = 0x5a827999; + const winpr_MD4_u32plus ac2 = 0x6ed9eba1; + + ptr = (const unsigned char*)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do + { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 3) + STEP(F, d, a, b, c, SET(1), 7) + STEP(F, c, d, a, b, SET(2), 11) + STEP(F, b, c, d, a, SET(3), 19) + STEP(F, a, b, c, d, SET(4), 3) + STEP(F, d, a, b, c, SET(5), 7) + STEP(F, c, d, a, b, SET(6), 11) + STEP(F, b, c, d, a, SET(7), 19) + STEP(F, a, b, c, d, SET(8), 3) + STEP(F, d, a, b, c, SET(9), 7) + STEP(F, c, d, a, b, SET(10), 11) + STEP(F, b, c, d, a, SET(11), 19) + STEP(F, a, b, c, d, SET(12), 3) + STEP(F, d, a, b, c, SET(13), 7) + STEP(F, c, d, a, b, SET(14), 11) + STEP(F, b, c, d, a, SET(15), 19) + + /* Round 2 */ + STEP(G, a, b, c, d, GET(0) + ac1, 3) + STEP(G, d, a, b, c, GET(4) + ac1, 5) + STEP(G, c, d, a, b, GET(8) + ac1, 9) + STEP(G, b, c, d, a, GET(12) + ac1, 13) + STEP(G, a, b, c, d, GET(1) + ac1, 3) + STEP(G, d, a, b, c, GET(5) + ac1, 5) + STEP(G, c, d, a, b, GET(9) + ac1, 9) + STEP(G, b, c, d, a, GET(13) + ac1, 13) + STEP(G, a, b, c, d, GET(2) + ac1, 3) + STEP(G, d, a, b, c, GET(6) + ac1, 5) + STEP(G, c, d, a, b, GET(10) + ac1, 9) + STEP(G, b, c, d, a, GET(14) + ac1, 13) + STEP(G, a, b, c, d, GET(3) + ac1, 3) + STEP(G, d, a, b, c, GET(7) + ac1, 5) + STEP(G, c, d, a, b, GET(11) + ac1, 9) + STEP(G, b, c, d, a, GET(15) + ac1, 13) + + /* Round 3 */ + STEP(H, a, b, c, d, GET(0) + ac2, 3) + STEP(H, d, a, b, c, GET(8) + ac2, 9) + STEP(H, c, d, a, b, GET(4) + ac2, 11) + STEP(H, b, c, d, a, GET(12) + ac2, 15) + STEP(H, a, b, c, d, GET(2) + ac2, 3) + STEP(H, d, a, b, c, GET(10) + ac2, 9) + STEP(H, c, d, a, b, GET(6) + ac2, 11) + STEP(H, b, c, d, a, GET(14) + ac2, 15) + STEP(H, a, b, c, d, GET(1) + ac2, 3) + STEP(H, d, a, b, c, GET(9) + ac2, 9) + STEP(H, c, d, a, b, GET(5) + ac2, 11) + STEP(H, b, c, d, a, GET(13) + ac2, 15) + STEP(H, a, b, c, d, GET(3) + ac2, 3) + STEP(H, d, a, b, c, GET(11) + ac2, 9) + STEP(H, c, d, a, b, GET(7) + ac2, 11) + STEP(H, b, c, d, a, GET(15) + ac2, 15) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void winpr_MD4_Init(WINPR_MD4_CTX* ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void winpr_MD4_Update(WINPR_MD4_CTX* ctx, const void* data, unsigned long size) +{ + winpr_MD4_u32plus saved_lo = 0; + unsigned long used = 0; + unsigned long available = 0; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) + { + available = 64 - used; + + if (size < available) + { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char*)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) + { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void winpr_MD4_Final(unsigned char* result, WINPR_MD4_CTX* ctx) +{ + unsigned long used = 0; + unsigned long available = 0; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) + { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} diff --git a/winpr/libwinpr/crypto/md4.h b/winpr/libwinpr/crypto/md4.h new file mode 100644 index 0000000..1ea008e --- /dev/null +++ b/winpr/libwinpr/crypto/md4.h @@ -0,0 +1,46 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD4 Message-Digest Algorithm (RFC 1320). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md4.c for more information. + */ + +#if !defined(WINPR_MD4_H) +#define WINPR_MD4_H + +#include + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef UINT32 winpr_MD4_u32plus; + +typedef struct +{ + winpr_MD4_u32plus lo, hi; + winpr_MD4_u32plus a, b, c, d; + unsigned char buffer[64]; + winpr_MD4_u32plus block[16]; +} WINPR_MD4_CTX; + +extern void winpr_MD4_Init(WINPR_MD4_CTX* ctx); +extern void winpr_MD4_Update(WINPR_MD4_CTX* ctx, const void* data, unsigned long size); +extern void winpr_MD4_Final(unsigned char* result, WINPR_MD4_CTX* ctx); + +#endif diff --git a/winpr/libwinpr/crypto/md5.c b/winpr/libwinpr/crypto/md5.c new file mode 100644 index 0000000..f98a542 --- /dev/null +++ b/winpr/libwinpr/crypto/md5.c @@ -0,0 +1,295 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * MD5_u32plus. In practice, this problem may occur if these MD5 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD5 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) (*(const winpr_MD5_u32plus*)&ptr[(n)*4]) +#define GET(n) SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = (winpr_MD5_u32plus)ptr[(n)*4] | ((winpr_MD5_u32plus)ptr[(n)*4 + 1] << 8) | \ + ((winpr_MD5_u32plus)ptr[(n)*4 + 2] << 16) | \ + ((winpr_MD5_u32plus)ptr[(n)*4 + 3] << 24)) +#define GET(n) (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void* body(WINPR_MD5_CTX* ctx, const void* data, unsigned long size) +{ + const unsigned char* ptr = NULL; + winpr_MD5_u32plus a = 0; + winpr_MD5_u32plus b = 0; + winpr_MD5_u32plus c = 0; + winpr_MD5_u32plus d = 0; + winpr_MD5_u32plus saved_a = 0; + winpr_MD5_u32plus saved_b = 0; + winpr_MD5_u32plus saved_c = 0; + winpr_MD5_u32plus saved_d = 0; + + ptr = (const unsigned char*)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do + { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + + /* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + + /* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + + /* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void winpr_MD5_Init(WINPR_MD5_CTX* ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void winpr_MD5_Update(WINPR_MD5_CTX* ctx, const void* data, unsigned long size) +{ + winpr_MD5_u32plus saved_lo = 0; + unsigned long used = 0; + unsigned long available = 0; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) + { + available = 64 - used; + + if (size < available) + { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char*)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) + { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void winpr_MD5_Final(unsigned char* result, WINPR_MD5_CTX* ctx) +{ + unsigned long used = 0; + unsigned long available = 0; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) + { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} diff --git a/winpr/libwinpr/crypto/md5.h b/winpr/libwinpr/crypto/md5.h new file mode 100644 index 0000000..2056b9a --- /dev/null +++ b/winpr/libwinpr/crypto/md5.h @@ -0,0 +1,48 @@ + + +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#if !defined(WINPR_MD5_H) +#define WINPR_MD5_H + +#include + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef UINT32 winpr_MD5_u32plus; + +typedef struct +{ + winpr_MD5_u32plus lo, hi; + winpr_MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + winpr_MD5_u32plus block[16]; +} WINPR_MD5_CTX; + +extern void winpr_MD5_Init(WINPR_MD5_CTX* ctx); +extern void winpr_MD5_Update(WINPR_MD5_CTX* ctx, const void* data, unsigned long size); +extern void winpr_MD5_Final(unsigned char* result, WINPR_MD5_CTX* ctx); + +#endif diff --git a/winpr/libwinpr/crypto/rand.c b/winpr/libwinpr/crypto/rand.c new file mode 100644 index 0000000..41fe06f --- /dev/null +++ b/winpr/libwinpr/crypto/rand.c @@ -0,0 +1,83 @@ +/** + * WinPR: Windows Portable Runtime + * + * Copyright 2015 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#ifdef WITH_OPENSSL +#include +#include +#endif + +#ifdef WITH_MBEDTLS +#include +#include +#ifdef MBEDTLS_HAVEGE_C +#include +#endif +#include +#endif + +int winpr_RAND(void* output, size_t len) +{ +#if defined(WITH_OPENSSL) + if (len > INT_MAX) + return -1; + if (RAND_bytes(output, (int)len) != 1) + return -1; +#elif defined(WITH_MBEDTLS) +#if defined(MBEDTLS_HAVEGE_C) + mbedtls_havege_state hs; + mbedtls_havege_init(&hs); + + if (mbedtls_havege_random(&hs, output, len) != 0) + return -1; + + mbedtls_havege_free(&hs); +#else + int status; + mbedtls_entropy_context entropy; + mbedtls_hmac_drbg_context hmac_drbg; + const mbedtls_md_info_t* md_info; + + mbedtls_entropy_init(&entropy); + mbedtls_hmac_drbg_init(&hmac_drbg); + + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + if ((status = mbedtls_hmac_drbg_seed(&hmac_drbg, md_info, mbedtls_entropy_func, &entropy, NULL, + 0)) != 0) + return -1; + + status = mbedtls_hmac_drbg_random(&hmac_drbg, output, len); + mbedtls_hmac_drbg_free(&hmac_drbg); + mbedtls_entropy_free(&entropy); + + if (status != 0) + return -1; +#endif +#endif + return 0; +} + +int winpr_RAND_pseudo(void* output, size_t len) +{ + return winpr_RAND(output, len); +} diff --git a/winpr/libwinpr/crypto/rc4.c b/winpr/libwinpr/crypto/rc4.c new file mode 100644 index 0000000..e170785 --- /dev/null +++ b/winpr/libwinpr/crypto/rc4.c @@ -0,0 +1,88 @@ +/** + * WinPR: Windows Portable Runtime + * RC4 implementation for RDP + * + * Copyright 2023 Armin Novak + * Copyright 2023 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "rc4.h" + +#define CTX_SIZE 256 + +struct winpr_int_rc4_ctx +{ + size_t i; + size_t j; + BYTE s[CTX_SIZE]; + BYTE t[CTX_SIZE]; +}; + +static void swap(BYTE* p1, BYTE* p2) +{ + BYTE t = *p1; + *p1 = *p2; + *p2 = t; +} + +winpr_int_RC4_CTX* winpr_int_rc4_new(const BYTE* key, size_t keylength) +{ + winpr_int_RC4_CTX* ctx = calloc(1, sizeof(winpr_int_RC4_CTX)); + if (!ctx) + return NULL; + + for (size_t i = 0; i < CTX_SIZE; i++) + { + ctx->s[i] = i; + ctx->t[i] = key[i % keylength]; + } + + size_t j = 0; + for (size_t i = 0; i < CTX_SIZE; i++) + { + j = (j + ctx->s[i] + ctx->t[i]) % CTX_SIZE; + swap(&ctx->s[i], &ctx->s[j]); + } + return ctx; +} + +void winpr_int_rc4_free(winpr_int_RC4_CTX* ctx) +{ + free(ctx); +} + +BOOL winpr_int_rc4_update(winpr_int_RC4_CTX* ctx, size_t length, const BYTE* input, BYTE* output) +{ + WINPR_ASSERT(ctx); + + UINT32 t1 = ctx->i; + UINT32 t2 = ctx->j; + for (size_t i = 0; i < length; i++) + { + t1 = (t1 + 1) % CTX_SIZE; + t2 = (t2 + ctx->s[t1]) % CTX_SIZE; + swap(&ctx->s[t1], &ctx->s[t2]); + + const size_t idx = ((size_t)ctx->s[t1] + ctx->s[t2]) % CTX_SIZE; + const BYTE val = ctx->s[idx]; + const BYTE out = *input++ ^ val; + *output++ = out; + } + + ctx->i = t1; + ctx->j = t2; + return TRUE; +} diff --git a/winpr/libwinpr/crypto/rc4.h b/winpr/libwinpr/crypto/rc4.h new file mode 100644 index 0000000..63c9954 --- /dev/null +++ b/winpr/libwinpr/crypto/rc4.h @@ -0,0 +1,34 @@ +/** + * WinPR: Windows Portable Runtime + * RC4 implementation for RDP + * + * Copyright 2023 Armin Novak + * Copyright 2023 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WINPR_RC4_H +#define WINPR_RC4_H + +#include +#include + +typedef struct winpr_int_rc4_ctx winpr_int_RC4_CTX; + +void winpr_int_rc4_free(winpr_int_RC4_CTX* ctx); + +WINPR_ATTR_MALLOC(winpr_int_rc4_free, 1) +winpr_int_RC4_CTX* winpr_int_rc4_new(const BYTE* key, size_t keylength); +BOOL winpr_int_rc4_update(winpr_int_RC4_CTX* ctx, size_t length, const BYTE* input, BYTE* output); + +#endif diff --git a/winpr/libwinpr/crypto/test/CMakeLists.txt b/winpr/libwinpr/crypto/test/CMakeLists.txt new file mode 100644 index 0000000..4fb9aa0 --- /dev/null +++ b/winpr/libwinpr/crypto/test/CMakeLists.txt @@ -0,0 +1,34 @@ + +set(MODULE_NAME "TestCrypto") +set(MODULE_PREFIX "TEST_CRYPTO") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestCryptoHash.c + TestCryptoRand.c + TestCryptoCipher.c + TestCryptoProtectData.c + TestCryptoProtectMemory.c + TestCryptoCertEnumCertificatesInStore.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +if(WIN32) + set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} secur32 crypt32 cryptui) +endif() + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c b/winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c new file mode 100644 index 0000000..6b40c8d --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c @@ -0,0 +1,86 @@ + + +#include +#include +#include + +#ifdef _WIN32 +//#define WITH_CRYPTUI 1 +#endif + +#ifdef WITH_CRYPTUI +#include +#endif + +int TestCryptoCertEnumCertificatesInStore(int argc, char* argv[]) +{ + int index = 0; + DWORD status = 0; + LPTSTR pszNameString = NULL; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pCertContext = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * System Store Locations: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa388136/ + */ + + /** + * Requires elevated rights: + * hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY) NULL, + * CERT_SYSTEM_STORE_LOCAL_MACHINE, _T("Remote Desktop")); + */ + + hCertStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL, _T("MY")); + // hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY) NULL, + // CERT_SYSTEM_STORE_CURRENT_USER, _T("MY")); + + if (!hCertStore) + { + printf("Failed to open system store\n"); + return -1; + } + + index = 0; + + while ((pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext))) + { + status = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); + if (status == 0) + return -1; + + pszNameString = (LPTSTR)calloc(status, sizeof(TCHAR)); + if (!pszNameString) + { + printf("Unable to allocate memory\n"); + return -1; + } + + status = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, + pszNameString, status); + if (status == 0) + { + free(pszNameString); + return -1; + } + + _tprintf(_T("Certificate #%d: %s\n"), index++, pszNameString); + + free(pszNameString); + +#ifdef WITH_CRYPTUI + CryptUIDlgViewContext(CERT_STORE_CERTIFICATE_CONTEXT, pCertContext, NULL, NULL, 0, NULL); +#endif + } + + if (!CertCloseStore(hCertStore, 0)) + { + printf("Failed to close system store\n"); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/crypto/test/TestCryptoCipher.c b/winpr/libwinpr/crypto/test/TestCryptoCipher.c new file mode 100644 index 0000000..518b443 --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoCipher.c @@ -0,0 +1,232 @@ + +#include +#include +#include +#include + +static BOOL test_crypto_cipher_aes_128_cbc(void) +{ + WINPR_CIPHER_CTX* ctx = NULL; + BOOL result = FALSE; + BYTE key[] = "0123456789abcdeF"; + BYTE iv[] = "1234567887654321"; + BYTE ibuf[1024] = { 0 }; + BYTE obuf[1024] = { 0 }; + size_t ilen = 0; + size_t olen = 0; + size_t xlen = 0; + const char plaintext[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua."; + + /* encrypt */ + + if (!(ctx = winpr_Cipher_New(WINPR_CIPHER_AES_128_CBC, WINPR_ENCRYPT, key, iv))) + { + fprintf(stderr, "%s: winpr_Cipher_New (encrypt) failed\n", __func__); + return FALSE; + } + + ilen = strnlen(plaintext, sizeof(plaintext)) + 1; + memcpy(ibuf, plaintext, ilen); + + ilen = ((ilen + 15) / 16) * 16; + olen = 0; + xlen = 0; + + if (!winpr_Cipher_Update(ctx, ibuf, ilen, obuf, &olen)) + { + fprintf(stderr, "%s: winpr_Cipher_New (encrypt) failed\n", __func__); + goto out; + } + xlen += olen; + + if (!winpr_Cipher_Final(ctx, obuf + xlen, &olen)) + { + fprintf(stderr, "%s: winpr_Cipher_Final (encrypt) failed\n", __func__); + goto out; + } + xlen += olen; + + if (xlen != ilen) + { + fprintf(stderr, "%s: error, xlen (%" PRIuz ") != ilen (%" PRIuz ") (encrypt)\n", __func__, + xlen, ilen); + goto out; + } + + winpr_Cipher_Free(ctx); + + /* decrypt */ + + if (!(ctx = winpr_Cipher_New(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, key, iv))) + { + fprintf(stderr, "%s: winpr_Cipher_New (decrypt) failed\n", __func__); + return FALSE; + } + + memset(ibuf, 0, sizeof(ibuf)); + memcpy(ibuf, obuf, xlen); + memset(obuf, 0, sizeof(obuf)); + + ilen = xlen; + olen = 0; + xlen = 0; + + if (!winpr_Cipher_Update(ctx, ibuf, ilen, obuf, &olen)) + { + fprintf(stderr, "%s: winpr_Cipher_New (decrypt) failed\n", __func__); + goto out; + } + xlen += olen; + + if (!winpr_Cipher_Final(ctx, obuf + xlen, &olen)) + { + fprintf(stderr, "%s: winpr_Cipher_Final (decrypt) failed\n", __func__); + goto out; + } + xlen += olen; + + if (xlen != ilen) + { + fprintf(stderr, "%s: error, xlen (%" PRIuz ") != ilen (%" PRIuz ") (decrypt)\n", __func__, + xlen, ilen); + goto out; + } + + if (strcmp((const char*)obuf, plaintext)) + { + fprintf(stderr, "%s: error, decrypted data does not match plaintext\n", __func__); + goto out; + } + + result = TRUE; + +out: + winpr_Cipher_Free(ctx); + return result; +} + +static const BYTE* TEST_RC4_KEY = (BYTE*)"Key"; +static const char* TEST_RC4_PLAINTEXT = "Plaintext"; +static const BYTE* TEST_RC4_CIPHERTEXT = (BYTE*)"\xBB\xF3\x16\xE8\xD9\x40\xAF\x0A\xD3"; + +static BOOL test_crypto_cipher_rc4(void) +{ + size_t len = 0; + BOOL rc = FALSE; + BYTE* text = NULL; + WINPR_RC4_CTX* ctx = NULL; + + len = strnlen(TEST_RC4_PLAINTEXT, sizeof(TEST_RC4_PLAINTEXT)); + + if (!(text = (BYTE*)calloc(1, len))) + { + fprintf(stderr, "%s: failed to allocate text buffer (len=%" PRIuz ")\n", __func__, len); + goto out; + } + + if ((ctx = winpr_RC4_New(TEST_RC4_KEY, + strnlen((const char*)TEST_RC4_KEY, sizeof(TEST_RC4_KEY)))) == NULL) + { + fprintf(stderr, "%s: winpr_RC4_New failed\n", __func__); + goto out; + } + rc = winpr_RC4_Update(ctx, len, (const BYTE*)TEST_RC4_PLAINTEXT, text); + winpr_RC4_Free(ctx); + if (!rc) + { + fprintf(stderr, "%s: winpr_RC4_Update failed\n", __func__); + goto out; + } + + if (memcmp(text, TEST_RC4_CIPHERTEXT, len) != 0) + { + char* actual = NULL; + char* expected = NULL; + + actual = winpr_BinToHexString(text, len, FALSE); + expected = winpr_BinToHexString(TEST_RC4_CIPHERTEXT, len, FALSE); + + fprintf(stderr, "%s: unexpected RC4 ciphertext: Actual: %s Expected: %s\n", __func__, + actual, expected); + + free(actual); + free(expected); + goto out; + } + + rc = TRUE; + +out: + free(text); + return rc; +} + +static const BYTE* TEST_RAND_DATA = + (BYTE*)"\x1F\xC2\xEE\x4C\xA3\x66\x80\xA2\xCE\xFE\x56\xB4\x9E\x08\x30\x96" + "\x33\x6A\xA9\x6D\x36\xFD\x3C\xB7\x83\x04\x4E\x5E\xDC\x22\xCD\xF3" + "\x48\xDF\x3A\x2A\x61\xF1\xA8\xFA\x1F\xC6\xC7\x1B\x81\xB4\xE1\x0E" + "\xCB\xA2\xEF\xA1\x12\x4A\x83\xE5\x1D\x72\x1D\x2D\x26\xA8\x6B\xC0"; + +static const BYTE* TEST_CIPHER_KEY = + (BYTE*)"\x9D\x7C\xC0\xA1\x94\x3B\x07\x67\x2F\xD3\x83\x10\x51\x83\x38\x0E" + "\x1C\x74\x8C\x4E\x15\x79\xD6\xFF\xE2\xF0\x37\x7F\x8C\xD7\xD2\x13"; + +static const BYTE* TEST_CIPHER_IV = + (BYTE*)"\xFE\xE3\x9F\xF0\xD1\x5E\x37\x0C\xAB\xAB\x9B\x04\xF3\xDB\x99\x15"; + +static BOOL test_crypto_cipher_key(void) +{ + int status = 0; + BYTE key[32] = { 0 }; + BYTE iv[16] = { 0 }; + BYTE salt[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + + status = winpr_Cipher_BytesToKey(WINPR_CIPHER_AES_256_CBC, WINPR_MD_SHA1, salt, TEST_RAND_DATA, + 64, 4, key, iv); + + if (status != 32 || memcmp(key, TEST_CIPHER_KEY, 32) || memcmp(iv, TEST_CIPHER_IV, 16)) + { + char* akstr = NULL; + char* ekstr = NULL; + char* aivstr = NULL; + char* eivstr = NULL; + + akstr = winpr_BinToHexString(key, 32, 0); + ekstr = winpr_BinToHexString(TEST_CIPHER_KEY, 32, 0); + + aivstr = winpr_BinToHexString(iv, 16, 0); + eivstr = winpr_BinToHexString(TEST_CIPHER_IV, 16, 0); + + fprintf(stderr, "Unexpected EVP_BytesToKey Key: Actual: %s, Expected: %s\n", akstr, ekstr); + fprintf(stderr, "Unexpected EVP_BytesToKey IV : Actual: %s, Expected: %s\n", aivstr, + eivstr); + + free(akstr); + free(ekstr); + free(aivstr); + free(eivstr); + } + + return TRUE; +} + +int TestCryptoCipher(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + + if (!test_crypto_cipher_aes_128_cbc()) + return -1; + + if (!test_crypto_cipher_rc4()) + return -1; + + if (!test_crypto_cipher_key()) + return -1; + + return 0; +} diff --git a/winpr/libwinpr/crypto/test/TestCryptoHash.c b/winpr/libwinpr/crypto/test/TestCryptoHash.c new file mode 100644 index 0000000..3057b9f --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoHash.c @@ -0,0 +1,316 @@ + +#include +#include +#include +#include + +static const char TEST_MD5_DATA[] = "test"; +static const BYTE TEST_MD5_HASH[] = + "\x09\x8f\x6b\xcd\x46\x21\xd3\x73\xca\xde\x4e\x83\x26\x27\xb4\xf6"; + +static BOOL test_crypto_hash_md5(void) +{ + BOOL result = FALSE; + BYTE hash[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + WINPR_DIGEST_CTX* ctx = NULL; + + if (!(ctx = winpr_Digest_New())) + { + fprintf(stderr, "%s: winpr_Digest_New failed\n", __func__); + return FALSE; + } + if (!winpr_Digest_Init(ctx, WINPR_MD_MD5)) + { + fprintf(stderr, "%s: winpr_Digest_Init failed\n", __func__); + goto out; + } + if (!winpr_Digest_Update(ctx, (const BYTE*)TEST_MD5_DATA, + strnlen(TEST_MD5_DATA, sizeof(TEST_MD5_DATA)))) + { + fprintf(stderr, "%s: winpr_Digest_Update failed\n", __func__); + goto out; + } + if (!winpr_Digest_Final(ctx, hash, sizeof(hash))) + { + fprintf(stderr, "%s: winpr_Digest_Final failed\n", __func__); + goto out; + } + if (memcmp(hash, TEST_MD5_HASH, WINPR_MD5_DIGEST_LENGTH) != 0) + { + char* actual = NULL; + char* expected = NULL; + + actual = winpr_BinToHexString(hash, WINPR_MD5_DIGEST_LENGTH, FALSE); + expected = winpr_BinToHexString(TEST_MD5_HASH, WINPR_MD5_DIGEST_LENGTH, FALSE); + + fprintf(stderr, "unexpected MD5 hash: Actual: %s Expected: %s\n", actual, expected); + + free(actual); + free(expected); + + goto out; + } + + result = TRUE; +out: + winpr_Digest_Free(ctx); + return result; +} + +static const char TEST_MD4_DATA[] = "test"; +static const BYTE TEST_MD4_HASH[] = + "\xdb\x34\x6d\x69\x1d\x7a\xcc\x4d\xc2\x62\x5d\xb1\x9f\x9e\x3f\x52"; + +static BOOL test_crypto_hash_md4(void) +{ + BOOL result = FALSE; + BYTE hash[WINPR_MD4_DIGEST_LENGTH] = { 0 }; + WINPR_DIGEST_CTX* ctx = NULL; + + if (!(ctx = winpr_Digest_New())) + { + fprintf(stderr, "%s: winpr_Digest_New failed\n", __func__); + return FALSE; + } + if (!winpr_Digest_Init(ctx, WINPR_MD_MD4)) + { + fprintf(stderr, "%s: winpr_Digest_Init failed\n", __func__); + goto out; + } + if (!winpr_Digest_Update(ctx, (const BYTE*)TEST_MD4_DATA, + strnlen(TEST_MD4_DATA, sizeof(TEST_MD4_DATA)))) + { + fprintf(stderr, "%s: winpr_Digest_Update failed\n", __func__); + goto out; + } + if (!winpr_Digest_Final(ctx, hash, sizeof(hash))) + { + fprintf(stderr, "%s: winpr_Digest_Final failed\n", __func__); + goto out; + } + if (memcmp(hash, TEST_MD4_HASH, WINPR_MD4_DIGEST_LENGTH) != 0) + { + char* actual = NULL; + char* expected = NULL; + + actual = winpr_BinToHexString(hash, WINPR_MD4_DIGEST_LENGTH, FALSE); + expected = winpr_BinToHexString(TEST_MD4_HASH, WINPR_MD4_DIGEST_LENGTH, FALSE); + + fprintf(stderr, "unexpected MD4 hash: Actual: %s Expected: %s\n", actual, expected); + + free(actual); + free(expected); + + goto out; + } + + result = TRUE; +out: + winpr_Digest_Free(ctx); + return result; +} + +static const char TEST_SHA1_DATA[] = "test"; +static const BYTE TEST_SHA1_HASH[] = + "\xa9\x4a\x8f\xe5\xcc\xb1\x9b\xa6\x1c\x4c\x08\x73\xd3\x91\xe9\x87\x98\x2f\xbb\xd3"; + +static BOOL test_crypto_hash_sha1(void) +{ + BOOL result = FALSE; + BYTE hash[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; + WINPR_DIGEST_CTX* ctx = NULL; + + if (!(ctx = winpr_Digest_New())) + { + fprintf(stderr, "%s: winpr_Digest_New failed\n", __func__); + return FALSE; + } + if (!winpr_Digest_Init(ctx, WINPR_MD_SHA1)) + { + fprintf(stderr, "%s: winpr_Digest_Init failed\n", __func__); + goto out; + } + if (!winpr_Digest_Update(ctx, (const BYTE*)TEST_SHA1_DATA, + strnlen(TEST_SHA1_DATA, sizeof(TEST_SHA1_DATA)))) + { + fprintf(stderr, "%s: winpr_Digest_Update failed\n", __func__); + goto out; + } + if (!winpr_Digest_Final(ctx, hash, sizeof(hash))) + { + fprintf(stderr, "%s: winpr_Digest_Final failed\n", __func__); + goto out; + } + + if (memcmp(hash, TEST_SHA1_HASH, WINPR_MD5_DIGEST_LENGTH) != 0) + { + char* actual = NULL; + char* expected = NULL; + + actual = winpr_BinToHexString(hash, WINPR_SHA1_DIGEST_LENGTH, FALSE); + expected = winpr_BinToHexString(TEST_SHA1_HASH, WINPR_SHA1_DIGEST_LENGTH, FALSE); + + fprintf(stderr, "unexpected SHA1 hash: Actual: %s Expected: %s\n", actual, expected); + + free(actual); + free(expected); + + goto out; + } + + result = TRUE; +out: + winpr_Digest_Free(ctx); + return result; +} + +static const char TEST_HMAC_MD5_DATA[] = "Hi There"; +static const BYTE TEST_HMAC_MD5_KEY[] = + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; +static const BYTE TEST_HMAC_MD5_HASH[] = + "\xb5\x79\x91\xa2\x20\x3d\x49\x2d\x73\xfb\x71\x43\xdf\xc5\x08\x28"; + +static BOOL test_crypto_hash_hmac_md5(void) +{ + BYTE hash[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + WINPR_HMAC_CTX* ctx = NULL; + BOOL result = FALSE; + + if (!(ctx = winpr_HMAC_New())) + { + fprintf(stderr, "%s: winpr_HMAC_New failed\n", __func__); + return FALSE; + } + + if (!winpr_HMAC_Init(ctx, WINPR_MD_MD5, TEST_HMAC_MD5_KEY, WINPR_MD5_DIGEST_LENGTH)) + { + fprintf(stderr, "%s: winpr_HMAC_Init failed\n", __func__); + goto out; + } + if (!winpr_HMAC_Update(ctx, (const BYTE*)TEST_HMAC_MD5_DATA, + strnlen(TEST_HMAC_MD5_DATA, sizeof(TEST_HMAC_MD5_DATA)))) + { + fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __func__); + goto out; + } + if (!winpr_HMAC_Update(ctx, (const BYTE*)TEST_HMAC_MD5_DATA, + strnlen(TEST_HMAC_MD5_DATA, sizeof(TEST_HMAC_MD5_DATA)))) + { + fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __func__); + goto out; + } + if (!winpr_HMAC_Final(ctx, hash, sizeof(hash))) + { + fprintf(stderr, "%s: winpr_HMAC_Final failed\n", __func__); + goto out; + } + + if (memcmp(hash, TEST_HMAC_MD5_HASH, WINPR_MD5_DIGEST_LENGTH) != 0) + { + char* actual = NULL; + char* expected = NULL; + + actual = winpr_BinToHexString(hash, WINPR_MD5_DIGEST_LENGTH, FALSE); + expected = winpr_BinToHexString(TEST_HMAC_MD5_HASH, WINPR_MD5_DIGEST_LENGTH, FALSE); + + fprintf(stderr, "unexpected HMAC-MD5 hash: Actual: %s Expected: %s\n", actual, expected); + + free(actual); + free(expected); + + goto out; + } + + result = TRUE; +out: + winpr_HMAC_Free(ctx); + return result; +} + +static const char TEST_HMAC_SHA1_DATA[] = "Hi There"; +static const BYTE TEST_HMAC_SHA1_KEY[] = + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; +static const BYTE TEST_HMAC_SHA1_HASH[] = + "\xab\x23\x08\x2d\xca\x0c\x75\xea\xca\x60\x09\xc0\xb8\x8c\x2d\xf4\xf4\xbf\x88\xee"; + +static BOOL test_crypto_hash_hmac_sha1(void) +{ + BYTE hash[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; + WINPR_HMAC_CTX* ctx = NULL; + BOOL result = FALSE; + + if (!(ctx = winpr_HMAC_New())) + { + fprintf(stderr, "%s: winpr_HMAC_New failed\n", __func__); + return FALSE; + } + + if (!winpr_HMAC_Init(ctx, WINPR_MD_SHA1, TEST_HMAC_SHA1_KEY, WINPR_SHA1_DIGEST_LENGTH)) + { + fprintf(stderr, "%s: winpr_HMAC_Init failed\n", __func__); + goto out; + } + if (!winpr_HMAC_Update(ctx, (const BYTE*)TEST_HMAC_SHA1_DATA, + strnlen(TEST_HMAC_SHA1_DATA, sizeof(TEST_HMAC_SHA1_DATA)))) + { + fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __func__); + goto out; + } + if (!winpr_HMAC_Update(ctx, (const BYTE*)TEST_HMAC_SHA1_DATA, + strnlen(TEST_HMAC_SHA1_DATA, sizeof(TEST_HMAC_SHA1_DATA)))) + { + fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __func__); + goto out; + } + if (!winpr_HMAC_Final(ctx, hash, sizeof(hash))) + { + fprintf(stderr, "%s: winpr_HMAC_Final failed\n", __func__); + goto out; + } + + if (memcmp(hash, TEST_HMAC_SHA1_HASH, WINPR_SHA1_DIGEST_LENGTH) != 0) + { + char* actual = NULL; + char* expected = NULL; + + actual = winpr_BinToHexString(hash, WINPR_SHA1_DIGEST_LENGTH, FALSE); + expected = winpr_BinToHexString(TEST_HMAC_SHA1_HASH, WINPR_SHA1_DIGEST_LENGTH, FALSE); + + fprintf(stderr, "unexpected HMAC-SHA1 hash: Actual: %s Expected: %s\n", actual, expected); + + free(actual); + free(expected); + + goto out; + } + + result = TRUE; +out: + winpr_HMAC_Free(ctx); + return result; +} + +int TestCryptoHash(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + + if (!test_crypto_hash_md5()) + return -1; + + if (!test_crypto_hash_md4()) + return -1; + + if (!test_crypto_hash_sha1()) + return -1; + + if (!test_crypto_hash_hmac_md5()) + return -1; + + if (!test_crypto_hash_hmac_sha1()) + return -1; + + return 0; +} diff --git a/winpr/libwinpr/crypto/test/TestCryptoProtectData.c b/winpr/libwinpr/crypto/test/TestCryptoProtectData.c new file mode 100644 index 0000000..45ef3e0 --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoProtectData.c @@ -0,0 +1,10 @@ + + +#include +#include +#include + +int TestCryptoProtectData(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c b/winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c new file mode 100644 index 0000000..d904c7f --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c @@ -0,0 +1,55 @@ + +#include +#include +#include +#include +#include + +static const char* SECRET_PASSWORD_TEST = "MySecretPassword123!"; + +int TestCryptoProtectMemory(int argc, char* argv[]) +{ + UINT32 cbPlainText = 0; + UINT32 cbCipherText = 0; + const char* pPlainText = NULL; + BYTE* pCipherText = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + pPlainText = SECRET_PASSWORD_TEST; + cbPlainText = strlen(pPlainText) + 1; + cbCipherText = cbPlainText + + (CRYPTPROTECTMEMORY_BLOCK_SIZE - (cbPlainText % CRYPTPROTECTMEMORY_BLOCK_SIZE)); + printf("cbPlainText: %" PRIu32 " cbCipherText: %" PRIu32 "\n", cbPlainText, cbCipherText); + pCipherText = (BYTE*)malloc(cbCipherText); + if (!pCipherText) + { + printf("Unable to allocate memory\n"); + return -1; + } + CopyMemory(pCipherText, pPlainText, cbPlainText); + ZeroMemory(&pCipherText[cbPlainText], (cbCipherText - cbPlainText)); + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + + if (!CryptProtectMemory(pCipherText, cbCipherText, CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + printf("CryptProtectMemory failure\n"); + return -1; + } + + printf("PlainText: %s (cbPlainText = %" PRIu32 ", cbCipherText = %" PRIu32 ")\n", pPlainText, + cbPlainText, cbCipherText); + winpr_HexDump("crypto.test", WLOG_DEBUG, pCipherText, cbCipherText); + + if (!CryptUnprotectMemory(pCipherText, cbCipherText, CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + printf("CryptUnprotectMemory failure\n"); + return -1; + } + + printf("Decrypted CipherText: %s\n", pCipherText); + SecureZeroMemory(pCipherText, cbCipherText); + free(pCipherText); + return 0; +} diff --git a/winpr/libwinpr/crypto/test/TestCryptoRand.c b/winpr/libwinpr/crypto/test/TestCryptoRand.c new file mode 100644 index 0000000..7dde55e --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoRand.c @@ -0,0 +1,26 @@ + +#include +#include +#include + +int TestCryptoRand(int argc, char* argv[]) +{ + char* str = NULL; + BYTE rnd[16] = { 0 }; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + winpr_RAND(rnd, sizeof(rnd)); + + str = winpr_BinToHexString(rnd, sizeof(rnd), FALSE); + // fprintf(stderr, "Rand: %s\n", str); + free(str); + + if (memcmp(rnd, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) + { + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/dsparse/CMakeLists.txt b/winpr/libwinpr/dsparse/CMakeLists.txt new file mode 100644 index 0000000..e455ccc --- /dev/null +++ b/winpr/libwinpr/dsparse/CMakeLists.txt @@ -0,0 +1,26 @@ +# WinPR: Windows Portable Runtime +# libwinpr-dsparse cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(dsparse.c) + +if(WIN32) + winpr_library_add_public(ntdsapi) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/dsparse/ModuleOptions.cmake b/winpr/libwinpr/dsparse/ModuleOptions.cmake new file mode 100644 index 0000000..647de1b --- /dev/null +++ b/winpr/libwinpr/dsparse/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "dsparse") +set(MINWIN_LONG_NAME "Domain Controller and Replication Management Functions") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/dsparse/dsparse.c b/winpr/libwinpr/dsparse/dsparse.c new file mode 100644 index 0000000..e1e4132 --- /dev/null +++ b/winpr/libwinpr/dsparse/dsparse.c @@ -0,0 +1,142 @@ +/** + * WinPR: Windows Portable Runtime + * Active Directory Domain Services Parsing Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +/** + * dsparse.dll: + * + * DsCrackSpnA + * DsCrackSpnW + * DsCrackUnquotedMangledRdnA + * DsCrackUnquotedMangledRdnW + * DsGetRdnW + * DsIsMangledDnA + * DsIsMangledDnW + * DsIsMangledRdnValueA + * DsIsMangledRdnValueW + * DsMakeSpnA + * DsMakeSpnW + * DsQuoteRdnValueA + * DsQuoteRdnValueW + * DsUnquoteRdnValueA + * DsUnquoteRdnValueW + */ + +#if !defined(_WIN32) || defined(_UWP) + +DWORD DsMakeSpnW(LPCWSTR ServiceClass, LPCWSTR ServiceName, LPCWSTR InstanceName, + USHORT InstancePort, LPCWSTR Referrer, DWORD* pcSpnLength, LPWSTR pszSpn) +{ + DWORD res = ERROR_OUTOFMEMORY; + char* ServiceClassA = NULL; + char* ServiceNameA = NULL; + char* InstanceNameA = NULL; + char* ReferrerA = NULL; + char* pszSpnA = NULL; + size_t length = 0; + + WINPR_ASSERT(ServiceClass); + WINPR_ASSERT(ServiceName); + WINPR_ASSERT(pcSpnLength); + + length = *pcSpnLength; + if ((length > 0) && pszSpn) + pszSpnA = calloc(length + 1, sizeof(char)); + + if (ServiceClass) + { + ServiceClassA = ConvertWCharToUtf8Alloc(ServiceClass, NULL); + if (!ServiceClassA) + goto fail; + } + if (ServiceName) + { + ServiceNameA = ConvertWCharToUtf8Alloc(ServiceName, NULL); + if (!ServiceNameA) + goto fail; + } + if (InstanceName) + { + InstanceNameA = ConvertWCharToUtf8Alloc(InstanceName, NULL); + if (!InstanceNameA) + goto fail; + } + if (Referrer) + { + ReferrerA = ConvertWCharToUtf8Alloc(Referrer, NULL); + if (!ReferrerA) + goto fail; + } + res = DsMakeSpnA(ServiceClassA, ServiceNameA, InstanceNameA, InstancePort, ReferrerA, + pcSpnLength, pszSpnA); + + if (res == ERROR_SUCCESS) + { + if (ConvertUtf8NToWChar(pszSpnA, *pcSpnLength, pszSpn, length) < 0) + res = ERROR_OUTOFMEMORY; + } + +fail: + free(ServiceClassA); + free(ServiceNameA); + free(InstanceNameA); + free(ReferrerA); + free(pszSpnA); + return res; +} + +DWORD DsMakeSpnA(LPCSTR ServiceClass, LPCSTR ServiceName, LPCSTR InstanceName, USHORT InstancePort, + LPCSTR Referrer, DWORD* pcSpnLength, LPSTR pszSpn) +{ + DWORD SpnLength = 0; + DWORD ServiceClassLength = 0; + DWORD ServiceNameLength = 0; + + WINPR_ASSERT(ServiceClass); + WINPR_ASSERT(ServiceName); + WINPR_ASSERT(pcSpnLength); + + WINPR_UNUSED(InstanceName); + WINPR_UNUSED(InstancePort); + WINPR_UNUSED(Referrer); + + if ((*pcSpnLength != 0) && (pszSpn == NULL)) + return ERROR_INVALID_PARAMETER; + + ServiceClassLength = (DWORD)strlen(ServiceClass); + ServiceNameLength = (DWORD)strlen(ServiceName); + + SpnLength = ServiceClassLength + 1 + ServiceNameLength + 1; + + if ((*pcSpnLength == 0) || (*pcSpnLength < SpnLength)) + { + *pcSpnLength = SpnLength; + return ERROR_BUFFER_OVERFLOW; + } + + sprintf_s(pszSpn, *pcSpnLength, "%s/%s", ServiceClass, ServiceName); + + return ERROR_SUCCESS; +} + +#endif diff --git a/winpr/libwinpr/dsparse/test/CMakeLists.txt b/winpr/libwinpr/dsparse/test/CMakeLists.txt new file mode 100644 index 0000000..d2b5ea4 --- /dev/null +++ b/winpr/libwinpr/dsparse/test/CMakeLists.txt @@ -0,0 +1,25 @@ + +set(MODULE_NAME "TestDsParse") +set(MODULE_PREFIX "TEST_DSPARSE") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestDsMakeSpn.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/dsparse/test/TestDsMakeSpn.c b/winpr/libwinpr/dsparse/test/TestDsMakeSpn.c new file mode 100644 index 0000000..dcaf635 --- /dev/null +++ b/winpr/libwinpr/dsparse/test/TestDsMakeSpn.c @@ -0,0 +1,151 @@ + +#include +#include +#include +#include +#include + +static BOOL test_DsMakeSpnA(void) +{ + LPCSTR testServiceClass = "HTTP"; + LPCSTR testServiceName = "LAB1-W2K8R2-GW.lab1.awake.local"; + LPCSTR testSpn = "HTTP/LAB1-W2K8R2-GW.lab1.awake.local"; + BOOL rc = FALSE; + CHAR Spn[100] = { 0 }; + DWORD status = 0; + DWORD SpnLength = -1; + + status = DsMakeSpnA(testServiceClass, testServiceName, NULL, 0, NULL, &SpnLength, NULL); + + if (status != ERROR_INVALID_PARAMETER) + { + printf("DsMakeSpnA: expected ERROR_INVALID_PARAMETER\n"); + goto fail; + } + + SpnLength = 0; + status = DsMakeSpnA(testServiceClass, testServiceName, NULL, 0, NULL, &SpnLength, NULL); + + if (status != ERROR_BUFFER_OVERFLOW) + { + printf("DsMakeSpnA: expected ERROR_BUFFER_OVERFLOW\n"); + goto fail; + } + + if (SpnLength != 37) + { + printf("DsMakeSpnA: SpnLength mismatch: Actual: %" PRIu32 ", Expected: 37\n", SpnLength); + goto fail; + } + + status = DsMakeSpnA(testServiceClass, testServiceName, NULL, 0, NULL, &SpnLength, Spn); + + if (status != ERROR_SUCCESS) + { + printf("DsMakeSpnA: expected ERROR_SUCCESS\n"); + goto fail; + } + + if (strcmp(Spn, testSpn) != 0) + { + printf("DsMakeSpnA: SPN mismatch: Actual: %s, Expected: %s\n", Spn, testSpn); + goto fail; + } + + printf("DsMakeSpnA: %s\n", Spn); + rc = TRUE; +fail: + return rc; +} + +static BOOL test_DsMakeSpnW(void) +{ + const CHAR ctestServiceClass[] = { 'H', 'T', 'T', 'P', '\0' }; + const CHAR ctestServiceName[] = { 'L', 'A', 'B', '1', '-', 'W', '2', 'K', '8', 'R', '2', + '-', 'G', 'W', '.', 'l', 'a', 'b', '1', '.', 'a', 'w', + 'a', 'k', 'e', '.', 'l', 'o', 'c', 'a', 'l', '\0' }; + const CHAR ctestSpn[] = { 'H', 'T', 'T', 'P', '/', 'L', 'A', 'B', '1', '-', 'W', '2', 'K', + '8', 'R', '2', '-', 'G', 'W', '.', 'l', 'a', 'b', '1', '.', 'a', + 'w', 'a', 'k', 'e', '.', 'l', 'o', 'c', 'a', 'l', '\0' }; + WCHAR testServiceClass[ARRAYSIZE(ctestServiceClass)] = { 0 }; + WCHAR testServiceName[ARRAYSIZE(ctestServiceName)] = { 0 }; + WCHAR testSpn[ARRAYSIZE(ctestSpn)] = { 0 }; + + BOOL rc = FALSE; + WCHAR Spn[100] = { 0 }; + DWORD status = 0; + DWORD SpnLength = -1; + + ConvertUtf8NToWChar(ctestServiceClass, ARRAYSIZE(ctestServiceClass), testServiceClass, + ARRAYSIZE(testServiceClass)); + ConvertUtf8NToWChar(ctestServiceName, ARRAYSIZE(ctestServiceName), testServiceName, + ARRAYSIZE(testServiceName)); + ConvertUtf8NToWChar(ctestSpn, ARRAYSIZE(ctestSpn), testSpn, ARRAYSIZE(testSpn)); + + status = DsMakeSpnW(testServiceClass, testServiceName, NULL, 0, NULL, &SpnLength, NULL); + + if (status != ERROR_INVALID_PARAMETER) + { + printf("DsMakeSpnW: expected ERROR_INVALID_PARAMETER\n"); + goto fail; + } + + SpnLength = 0; + status = DsMakeSpnW(testServiceClass, testServiceName, NULL, 0, NULL, &SpnLength, NULL); + + if (status != ERROR_BUFFER_OVERFLOW) + { + printf("DsMakeSpnW: expected ERROR_BUFFER_OVERFLOW\n"); + goto fail; + } + + if (SpnLength != 37) + { + printf("DsMakeSpnW: SpnLength mismatch: Actual: %" PRIu32 ", Expected: 37\n", SpnLength); + goto fail; + } + + status = DsMakeSpnW(testServiceClass, testServiceName, NULL, 0, NULL, &SpnLength, Spn); + + if (status != ERROR_SUCCESS) + { + printf("DsMakeSpnW: expected ERROR_SUCCESS\n"); + goto fail; + } + + if (_wcscmp(Spn, testSpn) != 0) + { + char buffer1[8192] = { 0 }; + char buffer2[8192] = { 0 }; + char* SpnA = buffer1; + char* testSpnA = buffer2; + + ConvertWCharToUtf8(Spn, SpnA, ARRAYSIZE(buffer1)); + ConvertWCharToUtf8(testSpn, testSpnA, ARRAYSIZE(buffer2)); + printf("DsMakeSpnW: SPN mismatch: Actual: %s, Expected: %s\n", SpnA, testSpnA); + goto fail; + } + + { + char buffer[8192] = { 0 }; + char* SpnA = buffer; + + ConvertWCharToUtf8(Spn, SpnA, ARRAYSIZE(buffer)); + printf("DsMakeSpnW: %s\n", SpnA); + } + + rc = TRUE; +fail: + return rc; +} +int TestDsMakeSpn(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_DsMakeSpnA()) + return -1; + if (!test_DsMakeSpnW()) + return -2; + return 0; +} diff --git a/winpr/libwinpr/dummy.c b/winpr/libwinpr/dummy.c new file mode 100644 index 0000000..bd2de45 --- /dev/null +++ b/winpr/libwinpr/dummy.c @@ -0,0 +1,5 @@ + +int winpr_dummy() +{ + return 0; +} diff --git a/winpr/libwinpr/environment/CMakeLists.txt b/winpr/libwinpr/environment/CMakeLists.txt new file mode 100644 index 0000000..53c2818 --- /dev/null +++ b/winpr/libwinpr/environment/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-environment cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(environment.c) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/environment/ModuleOptions.cmake b/winpr/libwinpr/environment/ModuleOptions.cmake new file mode 100644 index 0000000..d7b39ae --- /dev/null +++ b/winpr/libwinpr/environment/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "processenvironment") +set(MINWIN_LONG_NAME "Process Environment Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/environment/environment.c b/winpr/libwinpr/environment/environment.c new file mode 100644 index 0000000..c6df6bd --- /dev/null +++ b/winpr/libwinpr/environment/environment.c @@ -0,0 +1,708 @@ +/** + * WinPR: Windows Portable Runtime + * Process Environment Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2013 Thincast Technologies GmbH + * Copyright 2013 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include + +#ifndef _WIN32 + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#if defined(__IOS__) + +#elif defined(__MACOSX__) +#include +#define environ (*_NSGetEnviron()) +#endif + +DWORD GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) +{ + char* cwd = NULL; + size_t length = 0; + + cwd = getcwd(NULL, 0); + + if (!cwd) + return 0; + + length = strlen(cwd); + + if ((nBufferLength == 0) && (lpBuffer == NULL)) + { + free(cwd); + + return (DWORD)length; + } + else + { + if (lpBuffer == NULL) + { + free(cwd); + return 0; + } + + if ((length + 1) > nBufferLength) + { + free(cwd); + return (DWORD)(length + 1); + } + + memcpy(lpBuffer, cwd, length + 1); + free(cwd); + return (DWORD)length; + } +} + +DWORD GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) +{ + return 0; +} + +BOOL SetCurrentDirectoryA(LPCSTR lpPathName) +{ + return TRUE; +} + +BOOL SetCurrentDirectoryW(LPCWSTR lpPathName) +{ + return TRUE; +} + +DWORD SearchPathA(LPCSTR lpPath, LPCSTR lpFileName, LPCSTR lpExtension, DWORD nBufferLength, + LPSTR lpBuffer, LPSTR* lpFilePart) +{ + return 0; +} + +DWORD SearchPathW(LPCWSTR lpPath, LPCWSTR lpFileName, LPCWSTR lpExtension, DWORD nBufferLength, + LPWSTR lpBuffer, LPWSTR* lpFilePart) +{ + return 0; +} + +LPSTR GetCommandLineA(VOID) +{ + return NULL; +} + +LPWSTR GetCommandLineW(VOID) +{ + return NULL; +} + +BOOL NeedCurrentDirectoryForExePathA(LPCSTR ExeName) +{ + return TRUE; +} + +BOOL NeedCurrentDirectoryForExePathW(LPCWSTR ExeName) +{ + return TRUE; +} + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +DWORD GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) +{ +#if !defined(_UWP) + size_t length = 0; + char* env = NULL; + + env = getenv(lpName); + + if (!env) + { + SetLastError(ERROR_ENVVAR_NOT_FOUND); + return 0; + } + + length = strlen(env); + + if ((length + 1 > nSize) || (!lpBuffer)) + return (DWORD)length + 1; + + CopyMemory(lpBuffer, env, length); + lpBuffer[length] = '\0'; + + return (DWORD)length; +#else + SetLastError(ERROR_ENVVAR_NOT_FOUND); + return 0; +#endif +} + +DWORD GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) +{ + SetLastError(ERROR_ENVVAR_NOT_FOUND); + return 0; +} + +BOOL SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue) +{ +#if !defined(_UWP) + if (!lpName) + return FALSE; + + if (lpValue) + { + if (0 != setenv(lpName, lpValue, 1)) + return FALSE; + } + else + { + if (0 != unsetenv(lpName)) + return FALSE; + } + + return TRUE; +#else + return FALSE; +#endif +} + +BOOL SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue) +{ + return FALSE; +} + +/** + * GetEnvironmentStrings function: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms683187/ + * + * The GetEnvironmentStrings function returns a pointer to a block of memory + * that contains the environment variables of the calling process (both the + * system and the user environment variables). Each environment block contains + * the environment variables in the following format: + * + * Var1=Value1\0 + * Var2=Value2\0 + * Var3=Value3\0 + * ... + * VarN=ValueN\0\0 + */ + +extern char** environ; + +LPCH GetEnvironmentStringsA(VOID) +{ +#if !defined(_UWP) + char* p = NULL; + size_t offset = 0; + size_t length = 0; + char** envp = NULL; + DWORD cchEnvironmentBlock = 0; + LPCH lpszEnvironmentBlock = NULL; + + offset = 0; + envp = environ; + + cchEnvironmentBlock = 128; + lpszEnvironmentBlock = (LPCH)calloc(cchEnvironmentBlock, sizeof(CHAR)); + if (!lpszEnvironmentBlock) + return NULL; + + while (*envp) + { + length = strlen(*envp); + + while ((offset + length + 8) > cchEnvironmentBlock) + { + DWORD new_size = 0; + LPCH new_blk = NULL; + + new_size = cchEnvironmentBlock * 2; + new_blk = (LPCH)realloc(lpszEnvironmentBlock, new_size * sizeof(CHAR)); + if (!new_blk) + { + free(lpszEnvironmentBlock); + return NULL; + } + + lpszEnvironmentBlock = new_blk; + cchEnvironmentBlock = new_size; + } + + p = &(lpszEnvironmentBlock[offset]); + + CopyMemory(p, *envp, length * sizeof(CHAR)); + p[length] = '\0'; + + offset += (length + 1); + envp++; + } + + lpszEnvironmentBlock[offset] = '\0'; + + return lpszEnvironmentBlock; +#else + return NULL; +#endif +} + +LPWCH GetEnvironmentStringsW(VOID) +{ + return NULL; +} + +BOOL SetEnvironmentStringsA(LPCH NewEnvironment) +{ + return TRUE; +} + +BOOL SetEnvironmentStringsW(LPWCH NewEnvironment) +{ + return TRUE; +} + +DWORD ExpandEnvironmentStringsA(LPCSTR lpSrc, LPSTR lpDst, DWORD nSize) +{ + return 0; +} + +DWORD ExpandEnvironmentStringsW(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize) +{ + return 0; +} + +BOOL FreeEnvironmentStringsA(LPCH lpszEnvironmentBlock) +{ + free(lpszEnvironmentBlock); + + return TRUE; +} + +BOOL FreeEnvironmentStringsW(LPWCH lpszEnvironmentBlock) +{ + return TRUE; +} + +#endif + +LPCH MergeEnvironmentStrings(PCSTR original, PCSTR merge) +{ + const char* cp = NULL; + char* p = NULL; + size_t offset = 0; + size_t length = 0; + const char* envp = NULL; + DWORD cchEnvironmentBlock = 0; + LPCH lpszEnvironmentBlock = NULL; + const char** mergeStrings = NULL; + size_t mergeStringLength = 0; + size_t mergeArraySize = 128; + size_t mergeLength = 0; + size_t foundMerge = 0; + char* foundEquals = NULL; + + mergeStrings = (LPCSTR*)calloc(mergeArraySize, sizeof(char*)); + + if (!mergeStrings) + return NULL; + + mergeStringLength = 0; + + cp = merge; + + while (*cp && *(cp + 1)) + { + length = strlen(cp); + + if (mergeStringLength == mergeArraySize) + { + const char** new_str = NULL; + + mergeArraySize += 128; + new_str = (const char**)realloc((void*)mergeStrings, mergeArraySize * sizeof(char*)); + + if (!new_str) + { + free((void*)mergeStrings); + return NULL; + } + mergeStrings = new_str; + } + + mergeStrings[mergeStringLength] = cp; + cp += length + 1; + mergeStringLength++; + } + + offset = 0; + + cchEnvironmentBlock = 128; + lpszEnvironmentBlock = (LPCH)calloc(cchEnvironmentBlock, sizeof(CHAR)); + + if (!lpszEnvironmentBlock) + { + free((void*)mergeStrings); + return NULL; + } + + envp = original; + + while ((original != NULL) && (*envp && *(envp + 1))) + { + size_t old_offset = offset; + length = strlen(envp); + + while ((offset + length + 8) > cchEnvironmentBlock) + { + cchEnvironmentBlock *= 2; + LPCH tmp = (LPCH)realloc(lpszEnvironmentBlock, cchEnvironmentBlock * sizeof(CHAR)); + + if (!tmp) + { + free((void*)lpszEnvironmentBlock); + free((void*)mergeStrings); + return NULL; + } + lpszEnvironmentBlock = tmp; + } + + p = &(lpszEnvironmentBlock[offset]); + + // check if this value is in the mergeStrings + foundMerge = 0; + for (size_t run = 0; run < mergeStringLength; run++) + { + if (!mergeStrings[run]) + continue; + + mergeLength = strlen(mergeStrings[run]); + foundEquals = strstr(mergeStrings[run], "="); + + if (!foundEquals) + continue; + + if (strncmp(envp, mergeStrings[run], foundEquals - mergeStrings[run] + 1) == 0) + { + // found variable in merge list ... use this .... + if (*(foundEquals + 1) == '\0') + { + // check if the argument is set ... if not remove variable ... + foundMerge = 1; + } + else + { + while ((offset + mergeLength + 8) > cchEnvironmentBlock) + { + cchEnvironmentBlock *= 2; + LPCH tmp = + (LPCH)realloc(lpszEnvironmentBlock, cchEnvironmentBlock * sizeof(CHAR)); + + if (!tmp) + { + free((void*)lpszEnvironmentBlock); + free((void*)mergeStrings); + return NULL; + } + lpszEnvironmentBlock = tmp; + p = &(lpszEnvironmentBlock[old_offset]); + } + + foundMerge = 1; + CopyMemory(p, mergeStrings[run], mergeLength); + mergeStrings[run] = NULL; + p[mergeLength] = '\0'; + offset += (mergeLength + 1); + } + } + } + + if (foundMerge == 0) + { + CopyMemory(p, envp, length * sizeof(CHAR)); + p[length] = '\0'; + offset += (length + 1); + } + + envp += (length + 1); + } + + // now merge the not already merged env + for (size_t run = 0; run < mergeStringLength; run++) + { + if (!mergeStrings[run]) + continue; + + mergeLength = strlen(mergeStrings[run]); + + while ((offset + mergeLength + 8) > cchEnvironmentBlock) + { + cchEnvironmentBlock *= 2; + LPCH tmp = (LPCH)realloc(lpszEnvironmentBlock, cchEnvironmentBlock * sizeof(CHAR)); + + if (!tmp) + { + free((void*)lpszEnvironmentBlock); + free((void*)mergeStrings); + return NULL; + } + + lpszEnvironmentBlock = tmp; + } + + p = &(lpszEnvironmentBlock[offset]); + + CopyMemory(p, mergeStrings[run], mergeLength); + mergeStrings[run] = NULL; + p[mergeLength] = '\0'; + offset += (mergeLength + 1); + } + + lpszEnvironmentBlock[offset] = '\0'; + + free((void*)mergeStrings); + + return lpszEnvironmentBlock; +} + +DWORD GetEnvironmentVariableEBA(LPCSTR envBlock, LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) +{ + size_t vLength = 0; + char* env = NULL; + char* foundEquals = NULL; + const char* penvb = envBlock; + size_t nLength = 0; + size_t fLength = 0; + size_t lpNameLength = 0; + + if (!lpName || NULL == envBlock) + return 0; + + lpNameLength = strlen(lpName); + + if (lpNameLength < 1) + return 0; + + while (*penvb && *(penvb + 1)) + { + fLength = strlen(penvb); + foundEquals = strstr(penvb, "="); + + if (!foundEquals) + { + /* if no = sign is found the envBlock is broken */ + return 0; + } + + nLength = (foundEquals - penvb); + + if (nLength != lpNameLength) + { + penvb += (fLength + 1); + continue; + } + + if (strncmp(penvb, lpName, nLength) == 0) + { + env = foundEquals + 1; + break; + } + + penvb += (fLength + 1); + } + + if (!env) + return 0; + + vLength = strlen(env); + if (vLength >= UINT32_MAX) + return 0; + + if ((vLength + 1 > nSize) || (!lpBuffer)) + return (DWORD)vLength + 1; + + CopyMemory(lpBuffer, env, vLength + 1); + + return (DWORD)vLength; +} + +BOOL SetEnvironmentVariableEBA(LPSTR* envBlock, LPCSTR lpName, LPCSTR lpValue) +{ + size_t length = 0; + char* envstr = NULL; + char* newEB = NULL; + + if (!lpName) + return FALSE; + + if (lpValue) + { + length = (strlen(lpName) + strlen(lpValue) + 2); /* +2 because of = and \0 */ + envstr = (char*)malloc(length + 1); /* +1 because of closing \0 */ + + if (!envstr) + return FALSE; + + sprintf_s(envstr, length, "%s=%s", lpName, lpValue); + } + else + { + length = strlen(lpName) + 2; /* +2 because of = and \0 */ + envstr = (char*)malloc(length + 1); /* +1 because of closing \0 */ + + if (!envstr) + return FALSE; + + sprintf_s(envstr, length, "%s=", lpName); + } + + envstr[length] = '\0'; + + newEB = MergeEnvironmentStrings((LPCSTR)*envBlock, envstr); + + free(envstr); + free(*envBlock); + + *envBlock = newEB; + + return TRUE; +} + +char** EnvironmentBlockToEnvpA(LPCH lpszEnvironmentBlock) +{ + char* p = NULL; + SSIZE_T index = 0; + size_t count = 0; + size_t length = 0; + char** envp = NULL; + + count = 0; + if (!lpszEnvironmentBlock) + return NULL; + + p = (char*)lpszEnvironmentBlock; + + while (p[0] && p[1]) + { + length = strlen(p); + p += (length + 1); + count++; + } + + index = 0; + p = (char*)lpszEnvironmentBlock; + + envp = (char**)calloc(count + 1, sizeof(char*)); + if (!envp) + return NULL; + envp[count] = NULL; + + while (p[0] && p[1]) + { + length = strlen(p); + envp[index] = _strdup(p); + if (!envp[index]) + { + for (index -= 1; index >= 0; --index) + { + free(envp[index]); + } + free(envp); + return NULL; + } + p += (length + 1); + index++; + } + + return envp; +} + +#ifdef _WIN32 + +// https://devblogs.microsoft.com/oldnewthing/20100203-00/?p=15083 +#define WINPR_MAX_ENVIRONMENT_LENGTH 2048 + +DWORD GetEnvironmentVariableX(const char* lpName, char* lpBuffer, DWORD nSize) +{ + DWORD result = 0; + DWORD nSizeW = 0; + LPWSTR lpNameW = NULL; + LPWSTR lpBufferW = NULL; + LPSTR lpBufferA = lpBuffer; + + lpNameW = ConvertUtf8ToWCharAlloc(lpName, NULL); + if (!lpNameW) + goto cleanup; + + if (!lpBuffer) + { + char lpBufferMaxA[WINPR_MAX_ENVIRONMENT_LENGTH] = { 0 }; + WCHAR lpBufferMaxW[WINPR_MAX_ENVIRONMENT_LENGTH] = { 0 }; + LPSTR lpTmpBuffer = lpBufferMaxA; + + nSizeW = ARRAYSIZE(lpBufferMaxW); + + result = GetEnvironmentVariableW(lpNameW, lpBufferMaxW, nSizeW); + + SSIZE_T rc = + ConvertWCharNToUtf8(lpBufferMaxW, nSizeW, lpTmpBuffer, ARRAYSIZE(lpBufferMaxA)); + if ((rc < 0) || (rc >= UINT32_MAX)) + goto cleanup; + + result = (DWORD)rc + 1; + } + else + { + nSizeW = nSize; + lpBufferW = calloc(nSizeW + 1, sizeof(WCHAR)); + + if (!lpBufferW) + goto cleanup; + + result = GetEnvironmentVariableW(lpNameW, lpBufferW, nSizeW); + + if (result == 0) + goto cleanup; + + SSIZE_T rc = ConvertWCharNToUtf8(lpBufferW, nSizeW, lpBufferA, nSize); + if ((rc < 0) || (rc > UINT32_MAX)) + goto cleanup; + + result = (DWORD)rc; + } + +cleanup: + free(lpBufferW); + free(lpNameW); + + return result; +} + +#else + +DWORD GetEnvironmentVariableX(const char* lpName, char* lpBuffer, DWORD nSize) +{ + return GetEnvironmentVariableA(lpName, lpBuffer, nSize); +} + +#endif diff --git a/winpr/libwinpr/environment/test/CMakeLists.txt b/winpr/libwinpr/environment/test/CMakeLists.txt new file mode 100644 index 0000000..459d425 --- /dev/null +++ b/winpr/libwinpr/environment/test/CMakeLists.txt @@ -0,0 +1,28 @@ + +set(MODULE_NAME "TestEnvironment") +set(MODULE_PREFIX "TEST_ENVIRONMENT") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestEnvironmentGetEnvironmentStrings.c + TestEnvironmentSetEnvironmentVariable.c + TestEnvironmentMergeEnvironmentStrings.c + TestEnvironmentGetSetEB.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/environment/test/TestEnvironmentGetEnvironmentStrings.c b/winpr/libwinpr/environment/test/TestEnvironmentGetEnvironmentStrings.c new file mode 100644 index 0000000..c596399 --- /dev/null +++ b/winpr/libwinpr/environment/test/TestEnvironmentGetEnvironmentStrings.c @@ -0,0 +1,43 @@ + +#include +#include +#include +#include + +int TestEnvironmentGetEnvironmentStrings(int argc, char* argv[]) +{ + int r = -1; + TCHAR* p = NULL; + size_t length = 0; + LPTCH lpszEnvironmentBlock = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + lpszEnvironmentBlock = GetEnvironmentStrings(); + + p = lpszEnvironmentBlock; + + while (p[0] && p[1]) + { + const int rc = _sntprintf(NULL, 0, _T("%s\n"), p); + if (rc < 1) + { + _tprintf(_T("test failed: return %d\n"), rc); + goto fail; + } + length = _tcslen(p); + if (length != (size_t)(rc - 1)) + { + _tprintf(_T("test failed: length %") _T(PRIuz) _T(" != %d [%s]\n"), length, rc - 1, p); + goto fail; + } + p += (length + 1); + } + + r = 0; +fail: + FreeEnvironmentStrings(lpszEnvironmentBlock); + + return r; +} diff --git a/winpr/libwinpr/environment/test/TestEnvironmentGetSetEB.c b/winpr/libwinpr/environment/test/TestEnvironmentGetSetEB.c new file mode 100644 index 0000000..603b4c2 --- /dev/null +++ b/winpr/libwinpr/environment/test/TestEnvironmentGetSetEB.c @@ -0,0 +1,138 @@ + +#include +#include +#include +#include + +int TestEnvironmentGetSetEB(int argc, char* argv[]) +{ + int rc = 0; +#ifndef _WIN32 + char test[1024]; + TCHAR* p = NULL; + DWORD length = 0; + LPTCH lpszEnvironmentBlock = "SHELL=123\0test=1\0test1=2\0DISPLAY=WINPR_TEST_VALUE\0\0"; + LPTCH lpszEnvironmentBlockNew = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + rc = -1; + /* Get length of an variable */ + length = GetEnvironmentVariableEBA(lpszEnvironmentBlock, "DISPLAY", NULL, 0); + + if (0 == length) + return -1; + + /* Get the variable itself */ + p = (LPSTR)malloc(length); + + if (!p) + goto fail; + + if (GetEnvironmentVariableEBA(lpszEnvironmentBlock, "DISPLAY", p, length) != length - 1) + goto fail; + + printf("GetEnvironmentVariableA(WINPR_TEST_VARIABLE) = %s\n", p); + + if (strcmp(p, "WINPR_TEST_VALUE") != 0) + goto fail; + + /* Get length of an non-existing variable */ + length = GetEnvironmentVariableEBA(lpszEnvironmentBlock, "BLA", NULL, 0); + + if (0 != length) + { + printf("Unset variable returned\n"); + goto fail; + } + + /* Get length of an similar called variables */ + length = GetEnvironmentVariableEBA(lpszEnvironmentBlock, "XDISPLAY", NULL, 0); + + if (0 != length) + { + printf("Similar named variable returned (XDISPLAY, length %d)\n", length); + goto fail; + } + + length = GetEnvironmentVariableEBA(lpszEnvironmentBlock, "DISPLAYX", NULL, 0); + + if (0 != length) + { + printf("Similar named variable returned (DISPLAYX, length %d)\n", length); + goto fail; + } + + length = GetEnvironmentVariableEBA(lpszEnvironmentBlock, "DISPLA", NULL, 0); + + if (0 != length) + { + printf("Similar named variable returned (DISPLA, length %d)\n", length); + goto fail; + } + + length = GetEnvironmentVariableEBA(lpszEnvironmentBlock, "ISPLAY", NULL, 0); + + if (0 != length) + { + printf("Similar named variable returned (ISPLAY, length %d)\n", length); + goto fail; + } + + /* Set variable in empty environment block */ + if (SetEnvironmentVariableEBA(&lpszEnvironmentBlockNew, "test", "5")) + { + if (GetEnvironmentVariableEBA(lpszEnvironmentBlockNew, "test", test, 1023)) + { + if (strcmp(test, "5") != 0) + goto fail; + } + else + goto fail; + } + + /* Clear variable */ + if (SetEnvironmentVariableEBA(&lpszEnvironmentBlockNew, "test", NULL)) + { + if (GetEnvironmentVariableEBA(lpszEnvironmentBlockNew, "test", test, 1023)) + goto fail; + else + { + // not found .. this is expected + } + } + + free(lpszEnvironmentBlockNew); + lpszEnvironmentBlockNew = (LPTCH)calloc(1024, sizeof(TCHAR)); + + if (!lpszEnvironmentBlockNew) + goto fail; + + memcpy(lpszEnvironmentBlockNew, lpszEnvironmentBlock, length); + + /* Set variable in empty environment block */ + if (SetEnvironmentVariableEBA(&lpszEnvironmentBlockNew, "test", "5")) + { + if (0 != GetEnvironmentVariableEBA(lpszEnvironmentBlockNew, "testr", test, 1023)) + { + printf("GetEnvironmentVariableEBA returned unset variable\n"); + goto fail; + } + + if (GetEnvironmentVariableEBA(lpszEnvironmentBlockNew, "test", test, 1023)) + { + if (strcmp(test, "5") != 0) + goto fail; + } + else + goto fail; + } + + rc = 0; +fail: + free(p); + free(lpszEnvironmentBlockNew); +#endif + return rc; +} diff --git a/winpr/libwinpr/environment/test/TestEnvironmentMergeEnvironmentStrings.c b/winpr/libwinpr/environment/test/TestEnvironmentMergeEnvironmentStrings.c new file mode 100644 index 0000000..428418d --- /dev/null +++ b/winpr/libwinpr/environment/test/TestEnvironmentMergeEnvironmentStrings.c @@ -0,0 +1,34 @@ + +#include +#include +#include +#include + +int TestEnvironmentMergeEnvironmentStrings(int argc, char* argv[]) +{ +#ifndef _WIN32 + TCHAR* p = NULL; + size_t length = 0; + LPTCH lpszEnvironmentBlock = NULL; + LPTCH lpsz2Merge = "SHELL=123\0test=1\0test1=2\0DISPLAY=:77\0\0"; + LPTCH lpszMergedEnvironmentBlock = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + lpszEnvironmentBlock = GetEnvironmentStrings(); + lpszMergedEnvironmentBlock = MergeEnvironmentStrings(lpszEnvironmentBlock, lpsz2Merge); + p = (TCHAR*)lpszMergedEnvironmentBlock; + + while (p[0] && p[1]) + { + printf("%s\n", p); + length = strlen(p); + p += (length + 1); + } + + FreeEnvironmentStrings(lpszMergedEnvironmentBlock); + FreeEnvironmentStrings(lpszEnvironmentBlock); +#endif + return 0; +} diff --git a/winpr/libwinpr/environment/test/TestEnvironmentSetEnvironmentVariable.c b/winpr/libwinpr/environment/test/TestEnvironmentSetEnvironmentVariable.c new file mode 100644 index 0000000..04b7064 --- /dev/null +++ b/winpr/libwinpr/environment/test/TestEnvironmentSetEnvironmentVariable.c @@ -0,0 +1,72 @@ + +#include +#include +#include +#include +#include + +#define TEST_NAME "WINPR_TEST_VARIABLE" +#define TEST_VALUE "WINPR_TEST_VALUE" +int TestEnvironmentSetEnvironmentVariable(int argc, char* argv[]) +{ + int rc = -1; + DWORD nSize = 0; + LPSTR lpBuffer = NULL; + DWORD error = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + SetEnvironmentVariableA(TEST_NAME, TEST_VALUE); + nSize = GetEnvironmentVariableA(TEST_NAME, NULL, 0); + + /* check if value returned is len + 1 ) */ + if (nSize != strnlen(TEST_VALUE, sizeof(TEST_VALUE)) + 1) + { + printf("GetEnvironmentVariableA not found error\n"); + return -1; + } + + lpBuffer = (LPSTR)malloc(nSize); + + if (!lpBuffer) + return -1; + + nSize = GetEnvironmentVariableA(TEST_NAME, lpBuffer, nSize); + + if (nSize != strnlen(TEST_VALUE, sizeof(TEST_VALUE))) + { + printf("GetEnvironmentVariableA wrong size returned\n"); + goto fail; + } + + if (strcmp(lpBuffer, TEST_VALUE) != 0) + { + printf("GetEnvironmentVariableA returned value doesn't match\n"); + goto fail; + } + + nSize = GetEnvironmentVariableA("__xx__notset_", lpBuffer, nSize); + error = GetLastError(); + + if (0 != nSize || ERROR_ENVVAR_NOT_FOUND != error) + { + printf("GetEnvironmentVariableA not found error\n"); + goto fail; + } + + /* clear variable */ + SetEnvironmentVariableA(TEST_NAME, NULL); + nSize = GetEnvironmentVariableA(TEST_VALUE, NULL, 0); + + if (0 != nSize) + { + printf("SetEnvironmentVariableA failed to clear variable\n"); + goto fail; + } + + rc = 0; +fail: + free(lpBuffer); + return rc; +} diff --git a/winpr/libwinpr/error/CMakeLists.txt b/winpr/libwinpr/error/CMakeLists.txt new file mode 100644 index 0000000..a240131 --- /dev/null +++ b/winpr/libwinpr/error/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-error cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(error.c) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/error/ModuleOptions.cmake b/winpr/libwinpr/error/ModuleOptions.cmake new file mode 100644 index 0000000..b455bd7 --- /dev/null +++ b/winpr/libwinpr/error/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "1") +set(MINWIN_SHORT_NAME "errorhandling") +set(MINWIN_LONG_NAME "Error Handling Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/error/error.c b/winpr/libwinpr/error/error.c new file mode 100644 index 0000000..7746d02 --- /dev/null +++ b/winpr/libwinpr/error/error.c @@ -0,0 +1,99 @@ +/** + * WinPR: Windows Portable Runtime + * Error Handling Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#ifndef _WIN32 + +#include + +#include + +UINT GetErrorMode(void) +{ + return 0; +} + +UINT SetErrorMode(UINT uMode) +{ + return 0; +} + +DWORD GetLastError(VOID) +{ + PTEB pt = NtCurrentTeb(); + if (pt) + { + return pt->LastErrorValue; + } + return ERROR_OUTOFMEMORY; +} + +VOID SetLastError(DWORD dwErrCode) +{ + PTEB pt = NtCurrentTeb(); + if (pt) + { + pt->LastErrorValue = dwErrCode; + } +} + +VOID RestoreLastError(DWORD dwErrCode) +{ +} + +VOID RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, + CONST ULONG_PTR* lpArguments) +{ +} + +LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) +{ + return 0; +} + +LPTOP_LEVEL_EXCEPTION_FILTER +SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) +{ + return NULL; +} + +PVOID AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) +{ + return NULL; +} + +ULONG RemoveVectoredExceptionHandler(PVOID Handle) +{ + return 0; +} + +PVOID AddVectoredContinueHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler) +{ + return NULL; +} + +ULONG RemoveVectoredContinueHandler(PVOID Handle) +{ + return 0; +} + +#endif diff --git a/winpr/libwinpr/error/test/CMakeLists.txt b/winpr/libwinpr/error/test/CMakeLists.txt new file mode 100644 index 0000000..83b16e1 --- /dev/null +++ b/winpr/libwinpr/error/test/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(MODULE_NAME "TestError") +set(MODULE_PREFIX "TEST_ERROR") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestErrorSetLastError.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/error/test/TestErrorSetLastError.c b/winpr/libwinpr/error/test/TestErrorSetLastError.c new file mode 100644 index 0000000..a3d57af --- /dev/null +++ b/winpr/libwinpr/error/test/TestErrorSetLastError.c @@ -0,0 +1,135 @@ +/** + * CTest for winpr's SetLastError/GetLastError + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2013 Thincast Technologies GmbH + * Copyright 2013 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +static int status = 0; + +static LONG* pLoopCount = NULL; +static BOOL bStopTest = FALSE; + +static DWORD WINAPI test_error_thread(LPVOID arg) +{ + int id = 0; + DWORD dwErrorSet = 0; + DWORD dwErrorGet = 0; + + id = (int)(size_t)arg; + + do + { + dwErrorSet = (DWORD)abs(rand()) + 1; + SetLastError(dwErrorSet); + if ((dwErrorGet = GetLastError()) != dwErrorSet) + { + printf("GetLastError() failure (thread %d): Expected: 0x%08" PRIX32 + ", Actual: 0x%08" PRIX32 "\n", + id, dwErrorSet, dwErrorGet); + if (!status) + status = -1; + break; + } + InterlockedIncrement(pLoopCount); + } while (!status && !bStopTest); + + return 0; +} + +int TestErrorSetLastError(int argc, char* argv[]) +{ + DWORD error = 0; + HANDLE threads[4]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* We must initialize WLog here. It will check for settings + * in the environment and if the variables are not set, the last + * error state is changed... */ + WLog_GetRoot(); + + SetLastError(ERROR_ACCESS_DENIED); + + error = GetLastError(); + + if (error != ERROR_ACCESS_DENIED) + { + printf("GetLastError() failure: Expected: 0x%08X, Actual: 0x%08" PRIX32 "\n", + ERROR_ACCESS_DENIED, error); + return -1; + } + + pLoopCount = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + if (!pLoopCount) + { + printf("Unable to allocate memory\n"); + return -1; + } + *pLoopCount = 0; + + for (int i = 0; i < 4; i++) + { + if (!(threads[i] = CreateThread(NULL, 0, test_error_thread, (void*)(size_t)0, 0, NULL))) + { + printf("Failed to create thread #%d\n", i); + return -1; + } + } + + // let the threads run for at least 0.2 seconds + Sleep(200); + bStopTest = TRUE; + + WaitForSingleObject(threads[0], INFINITE); + WaitForSingleObject(threads[1], INFINITE); + WaitForSingleObject(threads[2], INFINITE); + WaitForSingleObject(threads[3], INFINITE); + + CloseHandle(threads[0]); + CloseHandle(threads[1]); + CloseHandle(threads[2]); + CloseHandle(threads[3]); + + error = GetLastError(); + + if (error != ERROR_ACCESS_DENIED) + { + printf("GetLastError() failure: Expected: 0x%08X, Actual: 0x%08" PRIX32 "\n", + ERROR_ACCESS_DENIED, error); + return -1; + } + + if (*pLoopCount < 4) + { + printf("Error: unexpected loop count\n"); + return -1; + } + + printf("Completed %" PRId32 " iterations.\n", *pLoopCount); + winpr_aligned_free(pLoopCount); + + return status; +} diff --git a/winpr/libwinpr/file/CMakeLists.txt b/winpr/libwinpr/file/CMakeLists.txt new file mode 100644 index 0000000..986c873 --- /dev/null +++ b/winpr/libwinpr/file/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-file cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(generic.c namedPipeClient.c pattern.c file.c) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/file/ModuleOptions.cmake b/winpr/libwinpr/file/ModuleOptions.cmake new file mode 100644 index 0000000..e2f0d36 --- /dev/null +++ b/winpr/libwinpr/file/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "file") +set(MINWIN_LONG_NAME "File Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/file/file.c b/winpr/libwinpr/file/file.c new file mode 100644 index 0000000..01328b8 --- /dev/null +++ b/winpr/libwinpr/file/file.c @@ -0,0 +1,1483 @@ +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 Bernhard Miklautz + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#if defined(__FreeBSD_kernel__) && defined(__GLIBC__) +#define _GNU_SOURCE +#define KFREEBSD +#endif + +#include +#include +#include + +#ifdef _WIN32 + +#include + +#else /* _WIN32 */ + +#include "../log.h" +#define TAG WINPR_TAG("file") + +#include +#include + +#include "file.h" +#include +#include +#include +#include +#include + +#ifdef ANDROID +#include +#else +#include +#endif + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +static BOOL FileIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_FILE, FALSE); +} + +static int FileGetFd(HANDLE handle) +{ + WINPR_FILE* file = (WINPR_FILE*)handle; + + if (!FileIsHandled(handle)) + return -1; + + return fileno(file->fp); +} + +static BOOL FileCloseHandle(HANDLE handle) +{ + WINPR_FILE* file = (WINPR_FILE*)handle; + + if (!FileIsHandled(handle)) + return FALSE; + + if (file->fp) + { + /* Don't close stdin/stdout/stderr */ + if (fileno(file->fp) > 2) + { + fclose(file->fp); + file->fp = NULL; + } + } + + free(file->lpFileName); + free(file); + return TRUE; +} + +static BOOL FileSetEndOfFile(HANDLE hFile) +{ + WINPR_FILE* pFile = (WINPR_FILE*)hFile; + INT64 size = 0; + + if (!hFile) + return FALSE; + + size = _ftelli64(pFile->fp); + + if (ftruncate(fileno(pFile->fp), size) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "ftruncate %s failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + SetLastError(map_posix_err(errno)); + return FALSE; + } + + return TRUE; +} + +static DWORD FileSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod) +{ + WINPR_FILE* pFile = (WINPR_FILE*)hFile; + INT64 offset = 0; + int whence = 0; + + if (!hFile) + return INVALID_SET_FILE_POINTER; + + /* If there is a high part, the sign is contained in that + * and the low integer must be interpreted as unsigned. */ + if (lpDistanceToMoveHigh) + { + offset = (INT64)(((UINT64)*lpDistanceToMoveHigh << 32U) | (UINT64)lDistanceToMove); + } + else + offset = lDistanceToMove; + + switch (dwMoveMethod) + { + case FILE_BEGIN: + whence = SEEK_SET; + break; + case FILE_END: + whence = SEEK_END; + break; + case FILE_CURRENT: + whence = SEEK_CUR; + break; + default: + return INVALID_SET_FILE_POINTER; + } + + if (_fseeki64(pFile->fp, offset, whence)) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return INVALID_SET_FILE_POINTER; + } + + return (DWORD)_ftelli64(pFile->fp); +} + +static BOOL FileSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, + PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) +{ + WINPR_FILE* pFile = (WINPR_FILE*)hFile; + int whence = 0; + + if (!hFile) + return FALSE; + + switch (dwMoveMethod) + { + case FILE_BEGIN: + whence = SEEK_SET; + break; + case FILE_END: + whence = SEEK_END; + break; + case FILE_CURRENT: + whence = SEEK_CUR; + break; + default: + return FALSE; + } + + if (_fseeki64(pFile->fp, liDistanceToMove.QuadPart, whence)) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } + + if (lpNewFilePointer) + lpNewFilePointer->QuadPart = _ftelli64(pFile->fp); + + return TRUE; +} + +static BOOL FileRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) +{ + size_t io_status = 0; + WINPR_FILE* file = NULL; + BOOL status = TRUE; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + if (!Object) + return FALSE; + + file = (WINPR_FILE*)Object; + clearerr(file->fp); + io_status = fread(lpBuffer, 1, nNumberOfBytesToRead, file->fp); + + if (io_status == 0 && ferror(file->fp)) + { + status = FALSE; + + switch (errno) + { + case EWOULDBLOCK: + SetLastError(ERROR_NO_DATA); + break; + default: + SetLastError(map_posix_err(errno)); + } + } + + if (lpNumberOfBytesRead) + *lpNumberOfBytesRead = (DWORD)io_status; + + return status; +} + +static BOOL FileWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) +{ + size_t io_status = 0; + WINPR_FILE* file = NULL; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + if (!Object) + return FALSE; + + file = (WINPR_FILE*)Object; + + clearerr(file->fp); + io_status = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, file->fp); + if (io_status == 0 && ferror(file->fp)) + { + SetLastError(map_posix_err(errno)); + return FALSE; + } + + *lpNumberOfBytesWritten = (DWORD)io_status; + return TRUE; +} + +static DWORD FileGetFileSize(HANDLE Object, LPDWORD lpFileSizeHigh) +{ + WINPR_FILE* file = NULL; + INT64 cur = 0; + INT64 size = 0; + + if (!Object) + return 0; + + file = (WINPR_FILE*)Object; + + cur = _ftelli64(file->fp); + + if (cur < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return INVALID_FILE_SIZE; + } + + if (_fseeki64(file->fp, 0, SEEK_END) != 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "_fseeki64(%s) failed with %s [0x%08X]", file->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return INVALID_FILE_SIZE; + } + + size = _ftelli64(file->fp); + + if (size < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return INVALID_FILE_SIZE; + } + + if (_fseeki64(file->fp, cur, SEEK_SET) != 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "_ftelli64(%s) failed with %s [0x%08X]", file->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return INVALID_FILE_SIZE; + } + + if (lpFileSizeHigh) + *lpFileSizeHigh = (UINT32)(size >> 32); + + return (UINT32)(size & 0xFFFFFFFF); +} + +static BOOL FileGetFileInformationByHandle(HANDLE hFile, + LPBY_HANDLE_FILE_INFORMATION lpFileInformation) +{ + WINPR_FILE* pFile = (WINPR_FILE*)hFile; + struct stat st; + UINT64 ft = 0; + const char* lastSep = NULL; + + if (!pFile) + return FALSE; + if (!lpFileInformation) + return FALSE; + + if (fstat(fileno(pFile->fp), &st) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "fstat failed with %s [%#08X]", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return FALSE; + } + + lpFileInformation->dwFileAttributes = 0; + + if (S_ISDIR(st.st_mode)) + lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (lpFileInformation->dwFileAttributes == 0) + lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + + lastSep = strrchr(pFile->lpFileName, '/'); + + if (lastSep) + { + const char* name = lastSep + 1; + const size_t namelen = strlen(name); + + if ((namelen > 1) && (name[0] == '.') && (name[1] != '.')) + lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + } + + if (!(st.st_mode & S_IWUSR)) + lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + +#ifdef _DARWIN_FEATURE_64_BIT_INODE + ft = STAT_TIME_TO_FILETIME(st.st_birthtime); +#else + ft = STAT_TIME_TO_FILETIME(st.st_ctime); +#endif + lpFileInformation->ftCreationTime.dwHighDateTime = ((UINT64)ft) >> 32ULL; + lpFileInformation->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF; + ft = STAT_TIME_TO_FILETIME(st.st_mtime); + lpFileInformation->ftLastWriteTime.dwHighDateTime = ((UINT64)ft) >> 32ULL; + lpFileInformation->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF; + ft = STAT_TIME_TO_FILETIME(st.st_atime); + lpFileInformation->ftLastAccessTime.dwHighDateTime = ((UINT64)ft) >> 32ULL; + lpFileInformation->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF; + lpFileInformation->nFileSizeHigh = ((UINT64)st.st_size) >> 32ULL; + lpFileInformation->nFileSizeLow = st.st_size & 0xFFFFFFFF; + lpFileInformation->dwVolumeSerialNumber = st.st_dev; + lpFileInformation->nNumberOfLinks = st.st_nlink; + lpFileInformation->nFileIndexHigh = (st.st_ino >> 4) & 0xFFFFFFFF; + lpFileInformation->nFileIndexLow = st.st_ino & 0xFFFFFFFF; + return TRUE; +} + +static BOOL FileLockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, + LPOVERLAPPED lpOverlapped) +{ +#ifdef __sun + struct flock lock; + int lckcmd; +#else + int lock = 0; +#endif + WINPR_FILE* pFile = (WINPR_FILE*)hFile; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + if (!hFile) + return FALSE; + + if (pFile->bLocked) + { + WLog_ERR(TAG, "File %s already locked!", pFile->lpFileName); + return FALSE; + } + +#ifdef __sun + lock.l_start = 0; + lock.l_len = 0; + lock.l_whence = SEEK_SET; + + if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK) + lock.l_type = F_WRLCK; + else + lock.l_type = F_WRLCK; + + if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY) + lckcmd = F_SETLK; + else + lckcmd = F_SETLKW; + + if (fcntl(fileno(pFile->fp), lckcmd, &lock) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "F_SETLK failed with %s [0x%08X]", + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } +#else + if (dwFlags & LOCKFILE_EXCLUSIVE_LOCK) + lock = LOCK_EX; + else + lock = LOCK_SH; + + if (dwFlags & LOCKFILE_FAIL_IMMEDIATELY) + lock |= LOCK_NB; + + if (flock(fileno(pFile->fp), lock) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "flock failed with %s [0x%08X]", + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } +#endif + + pFile->bLocked = TRUE; + + return TRUE; +} + +static BOOL FileUnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh) +{ + WINPR_FILE* pFile = (WINPR_FILE*)hFile; +#ifdef __sun + struct flock lock; +#endif + + if (!hFile) + return FALSE; + + if (!pFile->bLocked) + { + WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName); + return FALSE; + } + +#ifdef __sun + lock.l_start = 0; + lock.l_len = 0; + lock.l_whence = SEEK_SET; + lock.l_type = F_UNLCK; + if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "F_UNLCK on %s failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } + +#else + if (flock(fileno(pFile->fp), LOCK_UN) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } +#endif + + return TRUE; +} + +static BOOL FileUnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped) +{ + WINPR_FILE* pFile = (WINPR_FILE*)hFile; +#ifdef __sun + struct flock lock; +#endif + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + if (!hFile) + return FALSE; + + if (!pFile->bLocked) + { + WLog_ERR(TAG, "File %s is not locked!", pFile->lpFileName); + return FALSE; + } + +#ifdef __sun + lock.l_start = 0; + lock.l_len = 0; + lock.l_whence = SEEK_SET; + lock.l_type = F_UNLCK; + if (fcntl(fileno(pFile->fp), F_GETLK, &lock) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "F_UNLCK on %s failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } +#else + if (flock(fileno(pFile->fp), LOCK_UN) < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "flock(LOCK_UN) %s failed with %s [0x%08X]", pFile->lpFileName, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + return FALSE; + } +#endif + + return TRUE; +} + +static UINT64 FileTimeToUS(const FILETIME* ft) +{ + const UINT64 EPOCH_DIFF_US = EPOCH_DIFF * 1000000ULL; + UINT64 tmp = ((UINT64)ft->dwHighDateTime) << 32 | ft->dwLowDateTime; + tmp /= 10; /* 100ns steps to 1us step */ + tmp -= EPOCH_DIFF_US; + return tmp; +} + +static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime, + const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime) +{ + int rc = 0; +#if defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD) + struct stat buf; + /* OpenBSD, NetBSD and DragonflyBSD support POSIX futimens */ + struct timeval timevals[2]; +#else + struct timespec times[2]; /* last access, last modification */ +#endif + WINPR_FILE* pFile = (WINPR_FILE*)hFile; + + if (!hFile) + return FALSE; + +#if defined(__APPLE__) || defined(ANDROID) || defined(__FreeBSD__) || defined(KFREEBSD) + rc = fstat(fileno(pFile->fp), &buf); + + if (rc < 0) + return FALSE; + +#endif + + if (!lpLastAccessTime) + { +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD) + timevals[0].tv_sec = buf.st_atime; +#ifdef _POSIX_SOURCE + TIMESPEC_TO_TIMEVAL(&timevals[0], &buf.st_atim); +#else + TIMESPEC_TO_TIMEVAL(&timevals[0], &buf.st_atimespec); +#endif +#elif defined(ANDROID) + timevals[0].tv_sec = buf.st_atime; + timevals[0].tv_usec = buf.st_atimensec / 1000UL; +#else + times[0].tv_sec = UTIME_OMIT; + times[0].tv_nsec = UTIME_OMIT; +#endif + } + else + { + UINT64 tmp = FileTimeToUS(lpLastAccessTime); +#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD) + timevals[0].tv_sec = tmp / 1000000ULL; + timevals[0].tv_usec = tmp % 1000000ULL; +#else + times[0].tv_sec = tmp / 1000000ULL; + times[0].tv_nsec = (tmp % 1000000ULL) * 1000ULL; +#endif + } + + if (!lpLastWriteTime) + { +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD) + timevals[1].tv_sec = buf.st_mtime; +#ifdef _POSIX_SOURCE + TIMESPEC_TO_TIMEVAL(&timevals[1], &buf.st_mtim); +#else + TIMESPEC_TO_TIMEVAL(&timevals[1], &buf.st_mtimespec); +#endif +#elif defined(ANDROID) + timevals[1].tv_sec = buf.st_mtime; + timevals[1].tv_usec = buf.st_mtimensec / 1000UL; +#else + times[1].tv_sec = UTIME_OMIT; + times[1].tv_nsec = UTIME_OMIT; +#endif + } + else + { + UINT64 tmp = FileTimeToUS(lpLastWriteTime); +#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD) + timevals[1].tv_sec = tmp / 1000000ULL; + timevals[1].tv_usec = tmp % 1000000ULL; +#else + times[1].tv_sec = tmp / 1000000ULL; + times[1].tv_nsec = (tmp % 1000000ULL) * 1000ULL; +#endif + } + + // TODO: Creation time can not be handled! +#if defined(ANDROID) || defined(__FreeBSD__) || defined(__APPLE__) || defined(KFREEBSD) + rc = utimes(pFile->lpFileName, timevals); +#else + rc = futimens(fileno(pFile->fp), times); +#endif + + if (rc != 0) + return FALSE; + + return TRUE; +} + +static HANDLE_OPS fileOps = { + FileIsHandled, + FileCloseHandle, + FileGetFd, + NULL, /* CleanupHandle */ + FileRead, + NULL, /* FileReadEx */ + NULL, /* FileReadScatter */ + FileWrite, + NULL, /* FileWriteEx */ + NULL, /* FileWriteGather */ + FileGetFileSize, + NULL, /* FlushFileBuffers */ + FileSetEndOfFile, + FileSetFilePointer, + FileSetFilePointerEx, + NULL, /* FileLockFile */ + FileLockFileEx, + FileUnlockFile, + FileUnlockFileEx, + FileSetFileTime, + FileGetFileInformationByHandle, +}; + +static HANDLE_OPS shmOps = { + FileIsHandled, + FileCloseHandle, + FileGetFd, + NULL, /* CleanupHandle */ + FileRead, + NULL, /* FileReadEx */ + NULL, /* FileReadScatter */ + FileWrite, + NULL, /* FileWriteEx */ + NULL, /* FileWriteGather */ + NULL, /* FileGetFileSize */ + NULL, /* FlushFileBuffers */ + NULL, /* FileSetEndOfFile */ + NULL, /* FileSetFilePointer */ + NULL, /* SetFilePointerEx */ + NULL, /* FileLockFile */ + NULL, /* FileLockFileEx */ + NULL, /* FileUnlockFile */ + NULL, /* FileUnlockFileEx */ + NULL, /* FileSetFileTime */ + FileGetFileInformationByHandle, +}; + +static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDisposition, BOOL* create) +{ + BOOL writeable = (dwDesiredAccess & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0; + + switch (dwCreationDisposition) + { + case CREATE_ALWAYS: + *create = TRUE; + return (writeable) ? "wb+" : "rwb"; + case CREATE_NEW: + *create = TRUE; + return "wb+"; + case OPEN_ALWAYS: + *create = TRUE; + return "rb+"; + case OPEN_EXISTING: + *create = FALSE; + return (writeable) ? "rb+" : "rb"; + case TRUNCATE_EXISTING: + *create = FALSE; + return "wb+"; + default: + *create = FALSE; + return ""; + } +} + +UINT32 map_posix_err(int fs_errno) +{ + NTSTATUS rc = 0; + + /* try to return NTSTATUS version of error code */ + + switch (fs_errno) + { + case 0: + rc = STATUS_SUCCESS; + break; + + case ENOTCONN: + case ENODEV: + case ENOTDIR: + case ENXIO: + rc = ERROR_FILE_NOT_FOUND; + break; + + case EROFS: + case EPERM: + case EACCES: + rc = ERROR_ACCESS_DENIED; + break; + + case ENOENT: + rc = ERROR_FILE_NOT_FOUND; + break; + + case EBUSY: + rc = ERROR_BUSY_DRIVE; + break; + + case EEXIST: + rc = ERROR_FILE_EXISTS; + break; + + case EISDIR: + rc = STATUS_FILE_IS_A_DIRECTORY; + break; + + case ENOTEMPTY: + rc = STATUS_DIRECTORY_NOT_EMPTY; + break; + + case EMFILE: + rc = STATUS_TOO_MANY_OPENED_FILES; + break; + + default: + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "Missing ERRNO mapping %s [%d]", + winpr_strerror(fs_errno, ebuffer, sizeof(ebuffer)), fs_errno); + rc = STATUS_UNSUCCESSFUL; + } + break; + } + + return (UINT32)rc; +} + +static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + WINPR_FILE* pFile = NULL; + BOOL create = 0; + const char* mode = FileGetMode(dwDesiredAccess, dwCreationDisposition, &create); +#ifdef __sun + struct flock lock; +#else + int lock = 0; +#endif + FILE* fp = NULL; + struct stat st; + + if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) + { + WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag"); + SetLastError(ERROR_NOT_SUPPORTED); + return INVALID_HANDLE_VALUE; + } + + pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE)); + if (!pFile) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return INVALID_HANDLE_VALUE; + } + + WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ); + pFile->common.ops = &fileOps; + + pFile->lpFileName = _strdup(lpFileName); + if (!pFile->lpFileName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + free(pFile); + return INVALID_HANDLE_VALUE; + } + + pFile->dwOpenMode = dwDesiredAccess; + pFile->dwShareMode = dwShareMode; + pFile->dwFlagsAndAttributes = dwFlagsAndAttributes; + pFile->lpSecurityAttributes = lpSecurityAttributes; + pFile->dwCreationDisposition = dwCreationDisposition; + pFile->hTemplateFile = hTemplateFile; + + if (create) + { + if (dwCreationDisposition == CREATE_NEW) + { + if (stat(pFile->lpFileName, &st) == 0) + { + SetLastError(ERROR_FILE_EXISTS); + free(pFile->lpFileName); + free(pFile); + return INVALID_HANDLE_VALUE; + } + } + + fp = winpr_fopen(pFile->lpFileName, "ab"); + if (!fp) + { + SetLastError(map_posix_err(errno)); + free(pFile->lpFileName); + free(pFile); + return INVALID_HANDLE_VALUE; + } + + fp = freopen(pFile->lpFileName, mode, fp); + } + else + { + if (stat(pFile->lpFileName, &st) != 0) + { + SetLastError(map_posix_err(errno)); + free(pFile->lpFileName); + free(pFile); + return INVALID_HANDLE_VALUE; + } + + /* FIFO (named pipe) would block the following fopen + * call if not connected. This renders the channel unusable, + * therefore abort early. */ + if (S_ISFIFO(st.st_mode)) + { + SetLastError(ERROR_FILE_NOT_FOUND); + free(pFile->lpFileName); + free(pFile); + return INVALID_HANDLE_VALUE; + } + } + + if (NULL == fp) + fp = winpr_fopen(pFile->lpFileName, mode); + + pFile->fp = fp; + if (!pFile->fp) + { + /* This case can occur when trying to open a + * not existing file without create flag. */ + SetLastError(map_posix_err(errno)); + free(pFile->lpFileName); + free(pFile); + return INVALID_HANDLE_VALUE; + } + + setvbuf(fp, NULL, _IONBF, 0); + +#ifdef __sun + lock.l_start = 0; + lock.l_len = 0; + lock.l_whence = SEEK_SET; + + if (dwShareMode & FILE_SHARE_READ) + lock.l_type = F_RDLCK; + if (dwShareMode & FILE_SHARE_WRITE) + lock.l_type = F_RDLCK; +#else + if (dwShareMode & FILE_SHARE_READ) + lock = LOCK_SH; + if (dwShareMode & FILE_SHARE_WRITE) + lock = LOCK_EX; +#endif + + if (dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE)) + { +#ifdef __sun + if (fcntl(fileno(pFile->fp), F_SETLKW, &lock) == -1) +#else + if (flock(fileno(pFile->fp), lock) < 0) +#endif + { + char ebuffer[256] = { 0 }; +#ifdef __sun + WLog_ERR(TAG, "F_SETLKW failed with %s [0x%08X]", + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); +#else + WLog_ERR(TAG, "flock failed with %s [0x%08X]", + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); +#endif + + SetLastError(map_posix_err(errno)); + FileCloseHandle(pFile); + return INVALID_HANDLE_VALUE; + } + + pFile->bLocked = TRUE; + } + + if (fstat(fileno(pFile->fp), &st) == 0 && dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY) + { + st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + fchmod(fileno(pFile->fp), st.st_mode); + } + + SetLastError(STATUS_SUCCESS); + return pFile; +} + +static BOOL IsFileDevice(LPCTSTR lpDeviceName) +{ + return TRUE; +} + +static HANDLE_CREATOR _FileHandleCreator = { IsFileDevice, FileCreateFileA }; + +HANDLE_CREATOR* GetFileHandleCreator(void) +{ + return &_FileHandleCreator; +} + +static WINPR_FILE* FileHandle_New(FILE* fp) +{ + WINPR_FILE* pFile = NULL; + char name[MAX_PATH] = { 0 }; + + _snprintf(name, sizeof(name), "device_%d", fileno(fp)); + pFile = (WINPR_FILE*)calloc(1, sizeof(WINPR_FILE)); + if (!pFile) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + pFile->fp = fp; + pFile->common.ops = &shmOps; + pFile->lpFileName = _strdup(name); + + WINPR_HANDLE_SET_TYPE_AND_MODE(pFile, HANDLE_TYPE_FILE, WINPR_FD_READ); + return pFile; +} + +HANDLE GetStdHandle(DWORD nStdHandle) +{ + FILE* fp = NULL; + WINPR_FILE* pFile = NULL; + + switch (nStdHandle) + { + case STD_INPUT_HANDLE: + fp = stdin; + break; + case STD_OUTPUT_HANDLE: + fp = stdout; + break; + case STD_ERROR_HANDLE: + fp = stderr; + break; + default: + return INVALID_HANDLE_VALUE; + } + pFile = FileHandle_New(fp); + if (!pFile) + return INVALID_HANDLE_VALUE; + + return (HANDLE)pFile; +} + +BOOL SetStdHandle(DWORD nStdHandle, HANDLE hHandle) +{ + return FALSE; +} + +BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOldHandle) +{ + return FALSE; +} + +BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, + LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) +{ +#if defined(ANDROID) +#define STATVFS statfs +#else +#define STATVFS statvfs +#endif + + struct STATVFS svfst = { 0 }; + STATVFS(lpRootPathName, &svfst); + *lpSectorsPerCluster = (UINT32)MIN(svfst.f_frsize, UINT32_MAX); + *lpBytesPerSector = 1; + *lpNumberOfFreeClusters = (UINT32)MIN(svfst.f_bavail, UINT32_MAX); + *lpTotalNumberOfClusters = (UINT32)MIN(svfst.f_blocks, UINT32_MAX); + return TRUE; +} + +BOOL GetDiskFreeSpaceW(LPCWSTR lpwRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, + LPDWORD lpTotalNumberOfClusters) +{ + LPSTR lpRootPathName = NULL; + BOOL ret = 0; + if (!lpwRootPathName) + return FALSE; + + lpRootPathName = ConvertWCharToUtf8Alloc(lpwRootPathName, NULL); + if (!lpRootPathName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + ret = GetDiskFreeSpaceA(lpRootPathName, lpSectorsPerCluster, lpBytesPerSector, + lpNumberOfFreeClusters, lpTotalNumberOfClusters); + free(lpRootPathName); + return ret; +} + +#endif /* _WIN32 */ + +/** + * Check if a file name component is valid. + * + * Some file names are not valid on Windows. See "Naming Files, Paths, and Namespaces": + * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx + */ +BOOL ValidFileNameComponent(LPCWSTR lpFileName) +{ + if (!lpFileName) + return FALSE; + + /* CON */ + if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) && + (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) && + (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) && + (lpFileName[3] == L'\0')) + { + return FALSE; + } + + /* PRN */ + if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'P' || lpFileName[0] == L'p')) && + (lpFileName[1] != L'\0' && (lpFileName[1] == L'R' || lpFileName[1] == L'r')) && + (lpFileName[2] != L'\0' && (lpFileName[2] == L'N' || lpFileName[2] == L'n')) && + (lpFileName[3] == L'\0')) + { + return FALSE; + } + + /* AUX */ + if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'A' || lpFileName[0] == L'a')) && + (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) && + (lpFileName[2] != L'\0' && (lpFileName[2] == L'X' || lpFileName[2] == L'x')) && + (lpFileName[3] == L'\0')) + { + return FALSE; + } + + /* NUL */ + if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'N' || lpFileName[0] == L'n')) && + (lpFileName[1] != L'\0' && (lpFileName[1] == L'U' || lpFileName[1] == L'u')) && + (lpFileName[2] != L'\0' && (lpFileName[2] == L'L' || lpFileName[2] == L'l')) && + (lpFileName[3] == L'\0')) + { + return FALSE; + } + + /* LPT0-9 */ + if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'L' || lpFileName[0] == L'l')) && + (lpFileName[1] != L'\0' && (lpFileName[1] == L'P' || lpFileName[1] == L'p')) && + (lpFileName[2] != L'\0' && (lpFileName[2] == L'T' || lpFileName[2] == L't')) && + (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) && + (lpFileName[4] == L'\0')) + { + return FALSE; + } + + /* COM0-9 */ + if ((lpFileName[0] != L'\0' && (lpFileName[0] == L'C' || lpFileName[0] == L'c')) && + (lpFileName[1] != L'\0' && (lpFileName[1] == L'O' || lpFileName[1] == L'o')) && + (lpFileName[2] != L'\0' && (lpFileName[2] == L'M' || lpFileName[2] == L'm')) && + (lpFileName[3] != L'\0' && (L'0' <= lpFileName[3] && lpFileName[3] <= L'9')) && + (lpFileName[4] == L'\0')) + { + return FALSE; + } + + /* Reserved characters */ + for (LPCWSTR c = lpFileName; *c; c++) + { + if ((*c == L'<') || (*c == L'>') || (*c == L':') || (*c == L'"') || (*c == L'/') || + (*c == L'\\') || (*c == L'|') || (*c == L'?') || (*c == L'*')) + { + return FALSE; + } + } + + return TRUE; +} + +#ifdef _UWP + +HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + HANDLE hFile; + CREATEFILE2_EXTENDED_PARAMETERS params = { 0 }; + + params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + + if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS) + params.dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS; + if (dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE) + params.dwFileFlags |= FILE_FLAG_DELETE_ON_CLOSE; + if (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING) + params.dwFileFlags |= FILE_FLAG_NO_BUFFERING; + if (dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL) + params.dwFileFlags |= FILE_FLAG_OPEN_NO_RECALL; + if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT) + params.dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT; + if (dwFlagsAndAttributes & FILE_FLAG_OPEN_REQUIRING_OPLOCK) + params.dwFileFlags |= FILE_FLAG_OPEN_REQUIRING_OPLOCK; + if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) + params.dwFileFlags |= FILE_FLAG_OVERLAPPED; + if (dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS) + params.dwFileFlags |= FILE_FLAG_POSIX_SEMANTICS; + if (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS) + params.dwFileFlags |= FILE_FLAG_RANDOM_ACCESS; + if (dwFlagsAndAttributes & FILE_FLAG_SESSION_AWARE) + params.dwFileFlags |= FILE_FLAG_SESSION_AWARE; + if (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN) + params.dwFileFlags |= FILE_FLAG_SEQUENTIAL_SCAN; + if (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH) + params.dwFileFlags |= FILE_FLAG_WRITE_THROUGH; + + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ARCHIVE) + params.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_COMPRESSED) + params.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DEVICE) + params.dwFileAttributes |= FILE_ATTRIBUTE_DEVICE; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_DIRECTORY) + params.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_ENCRYPTED) + params.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_HIDDEN) + params.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) + params.dwFileAttributes |= FILE_ATTRIBUTE_INTEGRITY_STREAM; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NORMAL) + params.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) + params.dwFileAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) + params.dwFileAttributes |= FILE_ATTRIBUTE_NO_SCRUB_DATA; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_OFFLINE) + params.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY) + params.dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + params.dwFileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SPARSE_FILE) + params.dwFileAttributes |= FILE_ATTRIBUTE_SPARSE_FILE; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_SYSTEM) + params.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_TEMPORARY) + params.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY; + if (dwFlagsAndAttributes & FILE_ATTRIBUTE_VIRTUAL) + params.dwFileAttributes |= FILE_ATTRIBUTE_VIRTUAL; + + if (dwFlagsAndAttributes & SECURITY_ANONYMOUS) + params.dwSecurityQosFlags |= SECURITY_ANONYMOUS; + if (dwFlagsAndAttributes & SECURITY_CONTEXT_TRACKING) + params.dwSecurityQosFlags |= SECURITY_CONTEXT_TRACKING; + if (dwFlagsAndAttributes & SECURITY_DELEGATION) + params.dwSecurityQosFlags |= SECURITY_DELEGATION; + if (dwFlagsAndAttributes & SECURITY_EFFECTIVE_ONLY) + params.dwSecurityQosFlags |= SECURITY_EFFECTIVE_ONLY; + if (dwFlagsAndAttributes & SECURITY_IDENTIFICATION) + params.dwSecurityQosFlags |= SECURITY_IDENTIFICATION; + if (dwFlagsAndAttributes & SECURITY_IMPERSONATION) + params.dwSecurityQosFlags |= SECURITY_IMPERSONATION; + + params.lpSecurityAttributes = lpSecurityAttributes; + params.hTemplateFile = hTemplateFile; + + hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ¶ms); + + return hFile; +} + +HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + HANDLE hFile; + if (!lpFileName) + return NULL; + + WCHAR* lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL); + + if (!lpFileNameW) + return NULL; + + hFile = CreateFileW(lpFileNameW, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + + free(lpFileNameW); + + return hFile; +} + +DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) +{ + BOOL status; + LARGE_INTEGER fileSize = { 0, 0 }; + + if (!lpFileSizeHigh) + return INVALID_FILE_SIZE; + + status = GetFileSizeEx(hFile, &fileSize); + + if (!status) + return INVALID_FILE_SIZE; + + *lpFileSizeHigh = fileSize.HighPart; + + return fileSize.LowPart; +} + +DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod) +{ + BOOL status; + LARGE_INTEGER liDistanceToMove = { 0, 0 }; + LARGE_INTEGER liNewFilePointer = { 0, 0 }; + + liDistanceToMove.LowPart = lDistanceToMove; + + status = SetFilePointerEx(hFile, liDistanceToMove, &liNewFilePointer, dwMoveMethod); + + if (!status) + return INVALID_SET_FILE_POINTER; + + if (lpDistanceToMoveHigh) + *lpDistanceToMoveHigh = liNewFilePointer.HighPart; + + return liNewFilePointer.LowPart; +} + +HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData) +{ + return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch, + NULL, 0); +} + +HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) +{ + return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch, + NULL, 0); +} + +DWORD GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart) +{ + DWORD dwStatus; + WCHAR* lpFileNameW = NULL; + WCHAR* lpBufferW = NULL; + WCHAR* lpFilePartW = NULL; + DWORD nBufferLengthW = nBufferLength * sizeof(WCHAR); + + if (!lpFileName || (nBufferLength < 1)) + return 0; + + lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL); + if (!lpFileNameW) + return 0; + + lpBufferW = (WCHAR*)malloc(nBufferLengthW); + + if (!lpBufferW) + return 0; + + dwStatus = GetFullPathNameW(lpFileNameW, nBufferLengthW, lpBufferW, &lpFilePartW); + + ConvertWCharNToUtf8(lpBufferW, nBufferLengthW / sizeof(WCHAR), lpBuffer, nBufferLength); + + if (lpFilePart) + lpFilePart = lpBuffer + (lpFilePartW - lpBufferW); + + free(lpFileNameW); + free(lpBufferW); + + return dwStatus * 2; +} + +BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, + LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) +{ + BOOL status; + ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 }; + ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 }; + ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 }; + + status = GetDiskFreeSpaceExA(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, + &TotalNumberOfFreeBytes); + + if (!status) + return FALSE; + + *lpBytesPerSector = 1; + *lpSectorsPerCluster = TotalNumberOfBytes.LowPart; + *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart; + *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart; + + return TRUE; +} + +BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, + LPDWORD lpTotalNumberOfClusters) +{ + BOOL status; + ULARGE_INTEGER FreeBytesAvailableToCaller = { 0, 0 }; + ULARGE_INTEGER TotalNumberOfBytes = { 0, 0 }; + ULARGE_INTEGER TotalNumberOfFreeBytes = { 0, 0 }; + + status = GetDiskFreeSpaceExW(lpRootPathName, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, + &TotalNumberOfFreeBytes); + + if (!status) + return FALSE; + + *lpBytesPerSector = 1; + *lpSectorsPerCluster = TotalNumberOfBytes.LowPart; + *lpNumberOfFreeClusters = FreeBytesAvailableToCaller.LowPart; + *lpTotalNumberOfClusters = TotalNumberOfFreeBytes.LowPart; + + return TRUE; +} + +DWORD GetLogicalDriveStringsA(DWORD nBufferLength, LPSTR lpBuffer) +{ + SetLastError(ERROR_INVALID_FUNCTION); + return 0; +} + +DWORD GetLogicalDriveStringsW(DWORD nBufferLength, LPWSTR lpBuffer) +{ + SetLastError(ERROR_INVALID_FUNCTION); + return 0; +} + +BOOL PathIsDirectoryEmptyA(LPCSTR pszPath) +{ + return FALSE; +} + +UINT GetACP(void) +{ + return CP_UTF8; +} + +#endif + +/* Extended API */ + +#ifdef _WIN32 +#include +#endif + +HANDLE GetFileHandleForFileDescriptor(int fd) +{ +#ifdef _WIN32 + return (HANDLE)_get_osfhandle(fd); +#else /* _WIN32 */ + WINPR_FILE* pFile = NULL; + FILE* fp = NULL; + int flags = 0; + + /* Make sure it's a valid fd */ + if (fcntl(fd, F_GETFD) == -1 && errno == EBADF) + return INVALID_HANDLE_VALUE; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) + return INVALID_HANDLE_VALUE; + + if (flags & O_WRONLY) + fp = fdopen(fd, "wb"); + else + fp = fdopen(fd, "rb"); + + if (!fp) + return INVALID_HANDLE_VALUE; + + setvbuf(fp, NULL, _IONBF, 0); + + pFile = FileHandle_New(fp); + if (!pFile) + return INVALID_HANDLE_VALUE; + + return (HANDLE)pFile; +#endif /* _WIN32 */ +} + +FILE* winpr_fopen(const char* path, const char* mode) +{ +#ifndef _WIN32 + return fopen(path, mode); +#else + LPWSTR lpPathW = NULL; + LPWSTR lpModeW = NULL; + FILE* result = NULL; + + if (!path || !mode) + return NULL; + + lpPathW = ConvertUtf8ToWCharAlloc(path, NULL); + if (!lpPathW) + goto cleanup; + + lpModeW = ConvertUtf8ToWCharAlloc(mode, NULL); + if (!lpModeW) + goto cleanup; + + result = _wfopen(lpPathW, lpModeW); + +cleanup: + free(lpPathW); + free(lpModeW); + return result; +#endif +} diff --git a/winpr/libwinpr/file/file.h b/winpr/libwinpr/file/file.h new file mode 100644 index 0000000..b8be851 --- /dev/null +++ b/winpr/libwinpr/file/file.h @@ -0,0 +1,66 @@ +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2015 Armin Novak + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_FILE_PRIV_H +#define WINPR_FILE_PRIV_H + +#include +#include + +#include +#include +#include + +#ifndef _WIN32 + +#include +#include "../handle/handle.h" + +#define EPOCH_DIFF 11644473600LL +#define STAT_TIME_TO_FILETIME(_t) (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL) + +struct winpr_file +{ + WINPR_HANDLE common; + + FILE* fp; + + char* lpFileName; + + DWORD dwOpenMode; + DWORD dwShareMode; + DWORD dwFlagsAndAttributes; + + LPSECURITY_ATTRIBUTES lpSecurityAttributes; + DWORD dwCreationDisposition; + HANDLE hTemplateFile; + + BOOL bLocked; +}; +typedef struct winpr_file WINPR_FILE; + +HANDLE_CREATOR* GetFileHandleCreator(void); + +UINT32 map_posix_err(int fs_errno); + +#endif /* _WIN32 */ + +#endif /* WINPR_FILE_PRIV_H */ diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c new file mode 100644 index 0000000..e1437ec --- /dev/null +++ b/winpr/libwinpr/file/generic.c @@ -0,0 +1,1378 @@ +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifdef WINPR_HAVE_FCNTL_H +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("file") + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef WINPR_HAVE_AIO_H +#undef WINPR_HAVE_AIO_H /* disable for now, incomplete */ +#endif + +#ifdef WINPR_HAVE_AIO_H +#include +#endif + +#ifdef ANDROID +#include +#else +#include +#endif + +#include "../handle/handle.h" + +#include "../pipe/pipe.h" + +#include "file.h" + +/** + * api-ms-win-core-file-l1-2-0.dll: + * + * CreateFileA + * CreateFileW + * CreateFile2 + * DeleteFileA + * DeleteFileW + * CreateDirectoryA + * CreateDirectoryW + * RemoveDirectoryA + * RemoveDirectoryW + * CompareFileTime + * DefineDosDeviceW + * DeleteVolumeMountPointW + * FileTimeToLocalFileTime + * LocalFileTimeToFileTime + * FindClose + * FindCloseChangeNotification + * FindFirstChangeNotificationA + * FindFirstChangeNotificationW + * FindFirstFileA + * FindFirstFileExA + * FindFirstFileExW + * FindFirstFileW + * FindFirstVolumeW + * FindNextChangeNotification + * FindNextFileA + * FindNextFileW + * FindNextVolumeW + * FindVolumeClose + * GetDiskFreeSpaceA + * GetDiskFreeSpaceExA + * GetDiskFreeSpaceExW + * GetDiskFreeSpaceW + * GetDriveTypeA + * GetDriveTypeW + * GetFileAttributesA + * GetFileAttributesExA + * GetFileAttributesExW + * GetFileAttributesW + * GetFileInformationByHandle + * GetFileSize + * GetFileSizeEx + * GetFileTime + * GetFileType + * GetFinalPathNameByHandleA + * GetFinalPathNameByHandleW + * GetFullPathNameA + * GetFullPathNameW + * GetLogicalDrives + * GetLogicalDriveStringsW + * GetLongPathNameA + * GetLongPathNameW + * GetShortPathNameW + * GetTempFileNameW + * GetTempPathW + * GetVolumeInformationByHandleW + * GetVolumeInformationW + * GetVolumeNameForVolumeMountPointW + * GetVolumePathNamesForVolumeNameW + * GetVolumePathNameW + * QueryDosDeviceW + * SetFileAttributesA + * SetFileAttributesW + * SetFileTime + * SetFileValidData + * SetFileInformationByHandle + * ReadFile + * ReadFileEx + * ReadFileScatter + * WriteFile + * WriteFileEx + * WriteFileGather + * FlushFileBuffers + * SetEndOfFile + * SetFilePointer + * SetFilePointerEx + * LockFile + * LockFileEx + * UnlockFile + * UnlockFileEx + */ + +/** + * File System Behavior in the Microsoft Windows Environment: + * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf + */ + +/** + * Asynchronous I/O - The GNU C Library: + * http://www.gnu.org/software/libc/manual/html_node/Asynchronous-I_002fO.html + */ + +/** + * aio.h - asynchronous input and output: + * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html + */ + +/** + * Asynchronous I/O User Guide: + * http://code.google.com/p/kernel/wiki/AIOUserGuide + */ +static wArrayList* _HandleCreators; + +static pthread_once_t _HandleCreatorsInitialized = PTHREAD_ONCE_INIT; + +extern HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void); + +#if defined __linux__ && !defined ANDROID +#include "../comm/comm.h" +#endif /* __linux__ && !defined ANDROID */ + +static void _HandleCreatorsInit(void) +{ + WINPR_ASSERT(_HandleCreators == NULL); + _HandleCreators = ArrayList_New(TRUE); + + if (!_HandleCreators) + return; + + /* + * Register all file handle creators. + */ + ArrayList_Append(_HandleCreators, GetNamedPipeClientHandleCreator()); +#if defined __linux__ && !defined ANDROID + ArrayList_Append(_HandleCreators, GetCommHandleCreator()); +#endif /* __linux__ && !defined ANDROID */ + ArrayList_Append(_HandleCreators, GetFileHandleCreator()); +} + +#ifdef WINPR_HAVE_AIO_H + +static BOOL g_AioSignalHandlerInstalled = FALSE; + +void AioSignalHandler(int signum, siginfo_t* siginfo, void* arg) +{ + WLog_INFO("%d", signum); +} + +int InstallAioSignalHandler() +{ + if (!g_AioSignalHandlerInstalled) + { + struct sigaction action; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGIO); + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = (void*)&AioSignalHandler; + sigaction(SIGIO, &action, NULL); + g_AioSignalHandlerInstalled = TRUE; + } + + return 0; +} + +#endif /* WINPR_HAVE_AIO_H */ + +HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + if (!lpFileName) + return INVALID_HANDLE_VALUE; + + if (pthread_once(&_HandleCreatorsInitialized, _HandleCreatorsInit) != 0) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return INVALID_HANDLE_VALUE; + } + + if (_HandleCreators == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return INVALID_HANDLE_VALUE; + } + + ArrayList_Lock(_HandleCreators); + + for (size_t i = 0; i <= ArrayList_Count(_HandleCreators); i++) + { + HANDLE_CREATOR* creator = ArrayList_GetItem(_HandleCreators, i); + + if (creator && creator->IsHandled(lpFileName)) + { + HANDLE newHandle = + creator->CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + ArrayList_Unlock(_HandleCreators); + return newHandle; + } + } + + ArrayList_Unlock(_HandleCreators); + return INVALID_HANDLE_VALUE; +} + +HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + HANDLE hdl = NULL; + if (!lpFileName) + return NULL; + char* lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, NULL); + + if (!lpFileNameA) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto fail; + } + + hdl = CreateFileA(lpFileNameA, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); +fail: + free(lpFileNameA); + return hdl; +} + +BOOL DeleteFileA(LPCSTR lpFileName) +{ + int status = 0; + status = unlink(lpFileName); + return (status != -1) ? TRUE : FALSE; +} + +BOOL DeleteFileW(LPCWSTR lpFileName) +{ + if (!lpFileName) + return FALSE; + LPSTR lpFileNameA = ConvertWCharToUtf8Alloc(lpFileName, NULL); + BOOL rc = FALSE; + + if (!lpFileNameA) + goto fail; + + rc = DeleteFileA(lpFileNameA); +fail: + free(lpFileNameA); + return rc; +} + +BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + /* + * from http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx + * lpNumberOfBytesRead can be NULL only when the lpOverlapped parameter is not NULL. + */ + + if (!lpNumberOfBytesRead && !lpOverlapped) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->ReadFile) + return handle->ops->ReadFile(handle, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, + lpOverlapped); + + WLog_ERR(TAG, "ReadFile operation not implemented"); + return FALSE; +} + +BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->ReadFileEx) + return handle->ops->ReadFileEx(handle, lpBuffer, nNumberOfBytesToRead, lpOverlapped, + lpCompletionRoutine); + + WLog_ERR(TAG, "ReadFileEx operation not implemented"); + return FALSE; +} + +BOOL ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead, + LPDWORD lpReserved, LPOVERLAPPED lpOverlapped) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->ReadFileScatter) + return handle->ops->ReadFileScatter(handle, aSegmentArray, nNumberOfBytesToRead, lpReserved, + lpOverlapped); + + WLog_ERR(TAG, "ReadFileScatter operation not implemented"); + return FALSE; +} + +BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->WriteFile) + return handle->ops->WriteFile(handle, lpBuffer, nNumberOfBytesToWrite, + lpNumberOfBytesWritten, lpOverlapped); + + WLog_ERR(TAG, "WriteFile operation not implemented"); + return FALSE; +} + +BOOL WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->WriteFileEx) + return handle->ops->WriteFileEx(handle, lpBuffer, nNumberOfBytesToWrite, lpOverlapped, + lpCompletionRoutine); + + WLog_ERR(TAG, "WriteFileEx operation not implemented"); + return FALSE; +} + +BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], + DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, LPOVERLAPPED lpOverlapped) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->WriteFileGather) + return handle->ops->WriteFileGather(handle, aSegmentArray, nNumberOfBytesToWrite, + lpReserved, lpOverlapped); + + WLog_ERR(TAG, "WriteFileGather operation not implemented"); + return FALSE; +} + +BOOL FlushFileBuffers(HANDLE hFile) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->FlushFileBuffers) + return handle->ops->FlushFileBuffers(handle); + + WLog_ERR(TAG, "FlushFileBuffers operation not implemented"); + return FALSE; +} + +BOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFileInformation) +{ + LPWIN32_FILE_ATTRIBUTE_DATA fd = lpFileInformation; + WIN32_FIND_DATAA findFileData; + HANDLE hFind = NULL; + + if (!fd) + return FALSE; + + if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE) + return FALSE; + + FindClose(hFind); + fd->dwFileAttributes = findFileData.dwFileAttributes; + fd->ftCreationTime = findFileData.ftCreationTime; + fd->ftLastAccessTime = findFileData.ftLastAccessTime; + fd->ftLastWriteTime = findFileData.ftLastWriteTime; + fd->nFileSizeHigh = findFileData.nFileSizeHigh; + fd->nFileSizeLow = findFileData.nFileSizeLow; + return TRUE; +} + +BOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFileInformation) +{ + BOOL ret = 0; + if (!lpFileName) + return FALSE; + LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL); + + if (!lpCFileName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = GetFileAttributesExA(lpCFileName, fInfoLevelId, lpFileInformation); + free(lpCFileName); + return ret; +} + +DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName) +{ + WIN32_FIND_DATAA findFileData; + HANDLE hFind = NULL; + + if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE) + return INVALID_FILE_ATTRIBUTES; + + FindClose(hFind); + return findFileData.dwFileAttributes; +} + +DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName) +{ + DWORD ret = 0; + if (!lpFileName) + return FALSE; + LPSTR lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL); + if (!lpCFileName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = GetFileAttributesA(lpCFileName); + free(lpCFileName); + return ret; +} + +BOOL GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->GetFileInformationByHandle) + return handle->ops->GetFileInformationByHandle(handle, lpFileInformation); + + WLog_ERR(TAG, "GetFileInformationByHandle operation not implemented"); + return 0; +} + +static char* append(char* buffer, size_t size, const char* append) +{ + winpr_str_append(append, buffer, size, "|"); + return buffer; +} + +static const char* flagsToStr(char* buffer, size_t size, DWORD flags) +{ + char strflags[32] = { 0 }; + if (flags & FILE_ATTRIBUTE_READONLY) + append(buffer, size, "FILE_ATTRIBUTE_READONLY"); + if (flags & FILE_ATTRIBUTE_HIDDEN) + append(buffer, size, "FILE_ATTRIBUTE_HIDDEN"); + if (flags & FILE_ATTRIBUTE_SYSTEM) + append(buffer, size, "FILE_ATTRIBUTE_SYSTEM"); + if (flags & FILE_ATTRIBUTE_DIRECTORY) + append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY"); + if (flags & FILE_ATTRIBUTE_ARCHIVE) + append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE"); + if (flags & FILE_ATTRIBUTE_DEVICE) + append(buffer, size, "FILE_ATTRIBUTE_DEVICE"); + if (flags & FILE_ATTRIBUTE_NORMAL) + append(buffer, size, "FILE_ATTRIBUTE_NORMAL"); + if (flags & FILE_ATTRIBUTE_TEMPORARY) + append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY"); + if (flags & FILE_ATTRIBUTE_SPARSE_FILE) + append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE"); + if (flags & FILE_ATTRIBUTE_REPARSE_POINT) + append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT"); + if (flags & FILE_ATTRIBUTE_COMPRESSED) + append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED"); + if (flags & FILE_ATTRIBUTE_OFFLINE) + append(buffer, size, "FILE_ATTRIBUTE_OFFLINE"); + if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) + append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED"); + if (flags & FILE_ATTRIBUTE_ENCRYPTED) + append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED"); + if (flags & FILE_ATTRIBUTE_VIRTUAL) + append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL"); + + _snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags); + winpr_str_append(strflags, buffer, size, NULL); + return buffer; +} + +BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) +{ + struct stat st; + int fd = 0; + BOOL rc = FALSE; + + if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) + { + char buffer[8192] = { 0 }; + const char* flags = + flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags); + } + + fd = open(lpFileName, O_RDONLY); + if (fd < 0) + return FALSE; + + if (fstat(fd, &st) != 0) + goto fail; + + if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) + { + st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + } + else + { + st.st_mode |= S_IWUSR; + } + + if (fchmod(fd, st.st_mode) != 0) + goto fail; + + rc = TRUE; +fail: + close(fd); + return rc; +} + +BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) +{ + BOOL ret = 0; + LPSTR lpCFileName = NULL; + + if (!lpFileName) + return FALSE; + + if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) + { + char buffer[8192] = { 0 }; + const char* flags = + flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + WLog_WARN(TAG, "Unsupported flags %s, ignoring!", flags); + } + + lpCFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL); + if (!lpCFileName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = SetFileAttributesA(lpCFileName, dwFileAttributes); + free(lpCFileName); + return ret; +} + +BOOL SetEndOfFile(HANDLE hFile) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->SetEndOfFile) + return handle->ops->SetEndOfFile(handle); + + WLog_ERR(TAG, "SetEndOfFile operation not implemented"); + return FALSE; +} + +DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->GetFileSize) + return handle->ops->GetFileSize(handle, lpFileSizeHigh); + + WLog_ERR(TAG, "GetFileSize operation not implemented"); + return 0; +} + +DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->SetFilePointer) + return handle->ops->SetFilePointer(handle, lDistanceToMove, lpDistanceToMoveHigh, + dwMoveMethod); + + WLog_ERR(TAG, "SetFilePointer operation not implemented"); + return 0; +} + +BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, + DWORD dwMoveMethod) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->SetFilePointerEx) + return handle->ops->SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer, + dwMoveMethod); + + WLog_ERR(TAG, "SetFilePointerEx operation not implemented"); + return 0; +} + +BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->LockFile) + return handle->ops->LockFile(handle, dwFileOffsetLow, dwFileOffsetHigh, + nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh); + + WLog_ERR(TAG, "LockFile operation not implemented"); + return FALSE; +} + +BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->LockFileEx) + return handle->ops->LockFileEx(handle, dwFlags, dwReserved, nNumberOfBytesToLockLow, + nNumberOfBytesToLockHigh, lpOverlapped); + + WLog_ERR(TAG, "LockFileEx operation not implemented"); + return FALSE; +} + +BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->UnlockFile) + return handle->ops->UnlockFile(handle, dwFileOffsetLow, dwFileOffsetHigh, + nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh); + + WLog_ERR(TAG, "UnLockFile operation not implemented"); + return FALSE; +} + +BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->UnlockFileEx) + return handle->ops->UnlockFileEx(handle, dwReserved, nNumberOfBytesToUnlockLow, + nNumberOfBytesToUnlockHigh, lpOverlapped); + + WLog_ERR(TAG, "UnLockFileEx operation not implemented"); + return FALSE; +} + +BOOL WINAPI SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime, + const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime) +{ + ULONG Type = 0; + WINPR_HANDLE* handle = NULL; + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (!winpr_Handle_GetInfo(hFile, &Type, &handle)) + return FALSE; + + handle = (WINPR_HANDLE*)hFile; + + if (handle->ops->SetFileTime) + return handle->ops->SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime); + + WLog_ERR(TAG, "operation not implemented"); + return FALSE; +} + +typedef struct +{ + char magic[16]; + LPSTR lpPath; + LPSTR lpPattern; + DIR* pDir; +} WIN32_FILE_SEARCH; + +static const char file_search_magic[] = "file_srch_magic"; + +static WIN32_FILE_SEARCH* file_search_new(const char* name, size_t namelen, const char* pattern, + size_t patternlen) +{ + WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)calloc(1, sizeof(WIN32_FILE_SEARCH)); + if (!pFileSearch) + return NULL; + strncpy(pFileSearch->magic, file_search_magic, sizeof(pFileSearch->magic)); + + pFileSearch->lpPath = strndup(name, namelen); + pFileSearch->lpPattern = strndup(pattern, patternlen); + if (!pFileSearch->lpPath || !pFileSearch->lpPattern) + goto fail; + + pFileSearch->pDir = opendir(pFileSearch->lpPath); + if (!pFileSearch->pDir) + { + /* Work around for android: + * parent directories are not accessible, so if we have a directory without pattern + * try to open it directly and set pattern to '*' + */ + struct stat fileStat = { 0 }; + if (stat(name, &fileStat) == 0) + { + if (S_ISDIR(fileStat.st_mode)) + { + pFileSearch->pDir = opendir(name); + if (pFileSearch->pDir) + { + free(pFileSearch->lpPath); + free(pFileSearch->lpPattern); + pFileSearch->lpPath = _strdup(name); + pFileSearch->lpPattern = _strdup("*"); + if (!pFileSearch->lpPath || !pFileSearch->lpPattern) + { + closedir(pFileSearch->pDir); + pFileSearch->pDir = NULL; + } + } + } + } + } + if (!pFileSearch->pDir) + goto fail; + + return pFileSearch; +fail: + FindClose(pFileSearch); + return NULL; +} + +static BOOL is_valid_file_search_handle(HANDLE handle) +{ + WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)handle; + if (!pFileSearch) + return FALSE; + if (pFileSearch == INVALID_HANDLE_VALUE) + return FALSE; + if (strcmp(file_search_magic, pFileSearch->magic) != 0) + return FALSE; + return TRUE; +} +static BOOL FindDataFromStat(const char* path, const struct stat* fileStat, + LPWIN32_FIND_DATAA lpFindFileData) +{ + UINT64 ft = 0; + char* lastSep = NULL; + lpFindFileData->dwFileAttributes = 0; + + if (S_ISDIR(fileStat->st_mode)) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (lpFindFileData->dwFileAttributes == 0) + lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + + lastSep = strrchr(path, '/'); + + if (lastSep) + { + const char* name = lastSep + 1; + const size_t namelen = strlen(name); + + if ((namelen > 1) && (name[0] == '.') && (name[1] != '.')) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + } + + if (!(fileStat->st_mode & S_IWUSR)) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + +#ifdef _DARWIN_FEATURE_64_BIT_INODE + ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime); +#else + ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime); +#endif + lpFindFileData->ftCreationTime.dwHighDateTime = ((UINT64)ft) >> 32ULL; + lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF; + ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime); + lpFindFileData->ftLastWriteTime.dwHighDateTime = ((UINT64)ft) >> 32ULL; + lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF; + ft = STAT_TIME_TO_FILETIME(fileStat->st_atime); + lpFindFileData->ftLastAccessTime.dwHighDateTime = ((UINT64)ft) >> 32ULL; + lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF; + lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL; + lpFindFileData->nFileSizeLow = fileStat->st_size & 0xFFFFFFFF; + return TRUE; +} + +HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData) +{ + if (!lpFindFileData || !lpFileName) + { + SetLastError(ERROR_BAD_ARGUMENTS); + return INVALID_HANDLE_VALUE; + } + + const WIN32_FIND_DATAA empty = { 0 }; + *lpFindFileData = empty; + + WIN32_FILE_SEARCH* pFileSearch = NULL; + size_t patternlen = 0; + const size_t flen = strlen(lpFileName); + const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE); + const char* ptr = strrchr(lpFileName, sep); + if (!ptr) + goto fail; + patternlen = strlen(ptr + 1); + if (patternlen == 0) + goto fail; + + pFileSearch = file_search_new(lpFileName, flen - patternlen, ptr + 1, patternlen); + + if (!pFileSearch) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return INVALID_HANDLE_VALUE; + } + + if (FindNextFileA((HANDLE)pFileSearch, lpFindFileData)) + return (HANDLE)pFileSearch; + +fail: + FindClose(pFileSearch); + return INVALID_HANDLE_VALUE; +} + +static BOOL ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA, + LPWIN32_FIND_DATAW lpFindFileDataW) +{ + if (!lpFindFileDataA || !lpFindFileDataW) + return FALSE; + + lpFindFileDataW->dwFileAttributes = lpFindFileDataA->dwFileAttributes; + lpFindFileDataW->ftCreationTime = lpFindFileDataA->ftCreationTime; + lpFindFileDataW->ftLastAccessTime = lpFindFileDataA->ftLastAccessTime; + lpFindFileDataW->ftLastWriteTime = lpFindFileDataA->ftLastWriteTime; + lpFindFileDataW->nFileSizeHigh = lpFindFileDataA->nFileSizeHigh; + lpFindFileDataW->nFileSizeLow = lpFindFileDataA->nFileSizeLow; + lpFindFileDataW->dwReserved0 = lpFindFileDataA->dwReserved0; + lpFindFileDataW->dwReserved1 = lpFindFileDataA->dwReserved1; + + if (ConvertUtf8NToWChar(lpFindFileDataA->cFileName, ARRAYSIZE(lpFindFileDataA->cFileName), + lpFindFileDataW->cFileName, ARRAYSIZE(lpFindFileDataW->cFileName)) < 0) + return FALSE; + + return ConvertUtf8NToWChar(lpFindFileDataA->cAlternateFileName, + ARRAYSIZE(lpFindFileDataA->cAlternateFileName), + lpFindFileDataW->cAlternateFileName, + ARRAYSIZE(lpFindFileDataW->cAlternateFileName)) >= 0; +} + +HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) +{ + LPSTR utfFileName = NULL; + HANDLE h = NULL; + if (!lpFileName) + return FALSE; + LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA)); + + if (!fd) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return INVALID_HANDLE_VALUE; + } + + utfFileName = ConvertWCharToUtf8Alloc(lpFileName, NULL); + if (!utfFileName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + free(fd); + return INVALID_HANDLE_VALUE; + } + + h = FindFirstFileA(utfFileName, fd); + free(utfFileName); + + if (h != INVALID_HANDLE_VALUE) + { + if (!ConvertFindDataAToW(fd, lpFindFileData)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + FindClose(h); + h = INVALID_HANDLE_VALUE; + goto out; + } + } + +out: + free(fd); + return h; +} + +HANDLE FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, + FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) +{ + return INVALID_HANDLE_VALUE; +} + +HANDLE FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, + FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) +{ + return INVALID_HANDLE_VALUE; +} + +BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) +{ + if (!lpFindFileData) + return FALSE; + + const WIN32_FIND_DATAA empty = { 0 }; + *lpFindFileData = empty; + + if (!is_valid_file_search_handle(hFindFile)) + return FALSE; + + WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile; + struct dirent* pDirent = NULL; + while ((pDirent = readdir(pFileSearch->pDir)) != NULL) + { + if (FilePatternMatchA(pDirent->d_name, pFileSearch->lpPattern)) + { + BOOL success = FALSE; + + strncpy(lpFindFileData->cFileName, pDirent->d_name, MAX_PATH); + const size_t namelen = strnlen(lpFindFileData->cFileName, MAX_PATH); + size_t pathlen = strlen(pFileSearch->lpPath); + char* fullpath = (char*)malloc(pathlen + namelen + 2); + + if (fullpath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + memcpy(fullpath, pFileSearch->lpPath, pathlen); + /* Ensure path is terminated with a separator, but prevent + * duplicate separators */ + if (fullpath[pathlen - 1] != '/') + fullpath[pathlen++] = '/'; + memcpy(fullpath + pathlen, pDirent->d_name, namelen); + fullpath[pathlen + namelen] = 0; + + struct stat fileStat = { 0 }; + if (stat(fullpath, &fileStat) != 0) + { + free(fullpath); + SetLastError(map_posix_err(errno)); + errno = 0; + continue; + } + + /* Skip FIFO entries. */ + if (S_ISFIFO(fileStat.st_mode)) + { + free(fullpath); + continue; + } + + success = FindDataFromStat(fullpath, &fileStat, lpFindFileData); + free(fullpath); + return success; + } + } + + SetLastError(ERROR_NO_MORE_FILES); + return FALSE; +} + +BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) +{ + LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA)); + + if (!fd) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + if (FindNextFileA(hFindFile, fd)) + { + if (!ConvertFindDataAToW(fd, lpFindFileData)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + free(fd); + return FALSE; + } + + free(fd); + return TRUE; + } + + free(fd); + return FALSE; +} + +BOOL FindClose(HANDLE hFindFile) +{ + WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile; + if (!pFileSearch) + return FALSE; + + /* Since INVALID_HANDLE_VALUE != NULL the analyzer guesses that there + * is a initialized HANDLE that is not freed properly. + * Disable this return to stop confusing the analyzer. */ +#ifndef __clang_analyzer__ + if (!is_valid_file_search_handle(hFindFile)) + return FALSE; +#endif + + free(pFileSearch->lpPath); + free(pFileSearch->lpPattern); + + if (pFileSearch->pDir) + closedir(pFileSearch->pDir); + + free(pFileSearch); + return TRUE; +} + +BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) +{ + if (!mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR)) + return TRUE; + + return FALSE; +} + +BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) +{ + if (!lpPathName) + return FALSE; + char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, NULL); + BOOL ret = FALSE; + + if (!utfPathName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto fail; + } + + ret = CreateDirectoryA(utfPathName, lpSecurityAttributes); +fail: + free(utfPathName); + return ret; +} + +BOOL RemoveDirectoryA(LPCSTR lpPathName) +{ + int ret = rmdir(lpPathName); + + if (ret != 0) + SetLastError(map_posix_err(errno)); + else + SetLastError(STATUS_SUCCESS); + + return ret == 0; +} + +BOOL RemoveDirectoryW(LPCWSTR lpPathName) +{ + if (!lpPathName) + return FALSE; + char* utfPathName = ConvertWCharToUtf8Alloc(lpPathName, NULL); + BOOL ret = FALSE; + + if (!utfPathName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto fail; + } + + ret = RemoveDirectoryA(utfPathName); +fail: + free(utfPathName); + return ret; +} + +BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags) +{ + struct stat st; + int ret = 0; + ret = stat(lpNewFileName, &st); + + if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0) + { + if (ret == 0) + { + SetLastError(ERROR_ALREADY_EXISTS); + return FALSE; + } + } + else + { + if (ret == 0 && (st.st_mode & S_IWUSR) == 0) + { + SetLastError(ERROR_ACCESS_DENIED); + return FALSE; + } + } + + ret = rename(lpExistingFileName, lpNewFileName); + + if (ret != 0) + SetLastError(map_posix_err(errno)); + + return ret == 0; +} + +BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags) +{ + if (!lpExistingFileName || !lpNewFileName) + return FALSE; + + LPSTR lpCExistingFileName = ConvertWCharToUtf8Alloc(lpExistingFileName, NULL); + LPSTR lpCNewFileName = ConvertWCharToUtf8Alloc(lpNewFileName, NULL); + BOOL ret = FALSE; + + if (!lpCExistingFileName || !lpCNewFileName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto fail; + } + + ret = MoveFileExA(lpCExistingFileName, lpCNewFileName, dwFlags); +fail: + free(lpCNewFileName); + free(lpCExistingFileName); + return ret; +} + +BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName) +{ + return MoveFileExA(lpExistingFileName, lpNewFileName, 0); +} + +BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName) +{ + return MoveFileExW(lpExistingFileName, lpNewFileName, 0); +} + +#endif + +/* Extended API */ + +int UnixChangeFileMode(const char* filename, int flags) +{ + if (!filename) + return -1; +#ifndef _WIN32 + mode_t fl = 0; + fl |= (flags & 0x4000) ? S_ISUID : 0; + fl |= (flags & 0x2000) ? S_ISGID : 0; + fl |= (flags & 0x1000) ? S_ISVTX : 0; + fl |= (flags & 0x0400) ? S_IRUSR : 0; + fl |= (flags & 0x0200) ? S_IWUSR : 0; + fl |= (flags & 0x0100) ? S_IXUSR : 0; + fl |= (flags & 0x0040) ? S_IRGRP : 0; + fl |= (flags & 0x0020) ? S_IWGRP : 0; + fl |= (flags & 0x0010) ? S_IXGRP : 0; + fl |= (flags & 0x0004) ? S_IROTH : 0; + fl |= (flags & 0x0002) ? S_IWOTH : 0; + fl |= (flags & 0x0001) ? S_IXOTH : 0; + return chmod(filename, fl); +#else + int rc; + WCHAR* wfl = ConvertUtf8ToWCharAlloc(filename, NULL); + + if (!wfl) + return -1; + + /* Check for unsupported flags. */ + if (flags & ~(_S_IREAD | _S_IWRITE)) + WLog_WARN(TAG, "Unsupported file mode %d for _wchmod", flags); + + rc = _wchmod(wfl, flags); + free(wfl); + return rc; +#endif +} diff --git a/winpr/libwinpr/file/namedPipeClient.c b/winpr/libwinpr/file/namedPipeClient.c new file mode 100644 index 0000000..fbfeb35 --- /dev/null +++ b/winpr/libwinpr/file/namedPipeClient.c @@ -0,0 +1,305 @@ +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Hewlett-Packard Development Company, L.P. + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 bernhard.miklautz@thincast.com + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("file") + +#ifndef _WIN32 + +#ifdef ANDROID +#include +#else +#include +#endif + +#include "../handle/handle.h" + +#include "../pipe/pipe.h" + +static HANDLE_CREATOR _NamedPipeClientHandleCreator; + +static BOOL NamedPipeClientIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NAMED_PIPE, TRUE); +} + +static BOOL NamedPipeClientCloseHandle(HANDLE handle) +{ + WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)handle; + + if (!NamedPipeClientIsHandled(handle)) + return FALSE; + + if (pNamedPipe->clientfd != -1) + { + // WLOG_DBG(TAG, "closing clientfd %d", pNamedPipe->clientfd); + close(pNamedPipe->clientfd); + } + + if (pNamedPipe->serverfd != -1) + { + // WLOG_DBG(TAG, "closing serverfd %d", pNamedPipe->serverfd); + close(pNamedPipe->serverfd); + } + + if (pNamedPipe->pfnUnrefNamedPipe) + pNamedPipe->pfnUnrefNamedPipe(pNamedPipe); + + free(pNamedPipe->lpFileName); + free(pNamedPipe->lpFilePath); + free(pNamedPipe->name); + free(pNamedPipe); + return TRUE; +} + +static int NamedPipeClientGetFd(HANDLE handle) +{ + WINPR_NAMED_PIPE* file = (WINPR_NAMED_PIPE*)handle; + + if (!NamedPipeClientIsHandled(handle)) + return -1; + + if (file->ServerMode) + return file->serverfd; + else + return file->clientfd; +} + +static HANDLE_OPS ops = { + NamedPipeClientIsHandled, + NamedPipeClientCloseHandle, + NamedPipeClientGetFd, + NULL, /* CleanupHandle */ + NamedPipeRead, + NULL, /* FileReadEx */ + NULL, /* FileReadScatter */ + NamedPipeWrite, + NULL, /* FileWriteEx */ + NULL, /* FileWriteGather */ + NULL, /* FileGetFileSize */ + NULL, /* FlushFileBuffers */ + NULL, /* FileSetEndOfFile */ + NULL, /* FileSetFilePointer */ + NULL, /* SetFilePointerEx */ + NULL, /* FileLockFile */ + NULL, /* FileLockFileEx */ + NULL, /* FileUnlockFile */ + NULL, /* FileUnlockFileEx */ + NULL, /* SetFileTime */ + NULL, /* FileGetFileInformationByHandle */ +}; + +static HANDLE NamedPipeClientCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + char* name = NULL; + int status = 0; + HANDLE hNamedPipe = NULL; + struct sockaddr_un s = { 0 }; + WINPR_NAMED_PIPE* pNamedPipe = NULL; + + if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) + { + WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag"); + SetLastError(ERROR_NOT_SUPPORTED); + return INVALID_HANDLE_VALUE; + } + + if (!lpFileName) + return INVALID_HANDLE_VALUE; + + if (!IsNamedPipeFileNameA(lpFileName)) + return INVALID_HANDLE_VALUE; + + name = GetNamedPipeNameWithoutPrefixA(lpFileName); + + if (!name) + return INVALID_HANDLE_VALUE; + + free(name); + pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE)); + + if (!pNamedPipe) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return INVALID_HANDLE_VALUE; + } + + hNamedPipe = (HANDLE)pNamedPipe; + WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ); + pNamedPipe->name = _strdup(lpFileName); + + if (!pNamedPipe->name) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + free(pNamedPipe); + return INVALID_HANDLE_VALUE; + } + + pNamedPipe->dwOpenMode = 0; + pNamedPipe->dwPipeMode = 0; + pNamedPipe->nMaxInstances = 0; + pNamedPipe->nOutBufferSize = 0; + pNamedPipe->nInBufferSize = 0; + pNamedPipe->nDefaultTimeOut = 0; + pNamedPipe->dwFlagsAndAttributes = dwFlagsAndAttributes; + pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpFileName); + + if (!pNamedPipe->lpFileName) + { + free((void*)pNamedPipe->name); + free(pNamedPipe); + return INVALID_HANDLE_VALUE; + } + + pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpFileName); + + if (!pNamedPipe->lpFilePath) + { + free((void*)pNamedPipe->lpFileName); + free((void*)pNamedPipe->name); + free(pNamedPipe); + return INVALID_HANDLE_VALUE; + } + + pNamedPipe->clientfd = socket(PF_LOCAL, SOCK_STREAM, 0); + pNamedPipe->serverfd = -1; + pNamedPipe->ServerMode = FALSE; + s.sun_family = AF_UNIX; + sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath); + status = connect(pNamedPipe->clientfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un)); + pNamedPipe->common.ops = &ops; + + if (status != 0) + { + close(pNamedPipe->clientfd); + free((char*)pNamedPipe->name); + free((char*)pNamedPipe->lpFileName); + free((char*)pNamedPipe->lpFilePath); + free(pNamedPipe); + return INVALID_HANDLE_VALUE; + } + + if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) + { +#if 0 + int flags = fcntl(pNamedPipe->clientfd, F_GETFL); + + if (flags != -1) + fcntl(pNamedPipe->clientfd, F_SETFL, flags | O_NONBLOCK); + +#endif + } + + return hNamedPipe; +} + +extern HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void); +HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void) +{ + _NamedPipeClientHandleCreator.IsHandled = IsNamedPipeFileNameA; + _NamedPipeClientHandleCreator.CreateFileA = NamedPipeClientCreateFileA; + return &_NamedPipeClientHandleCreator; +} + +#endif + +/* Extended API */ + +#define NAMED_PIPE_PREFIX_PATH "\\\\.\\pipe\\" + +BOOL IsNamedPipeFileNameA(LPCSTR lpName) +{ + if (strncmp(lpName, NAMED_PIPE_PREFIX_PATH, sizeof(NAMED_PIPE_PREFIX_PATH) - 1) != 0) + return FALSE; + + return TRUE; +} + +char* GetNamedPipeNameWithoutPrefixA(LPCSTR lpName) +{ + char* lpFileName = NULL; + + if (!lpName) + return NULL; + + if (!IsNamedPipeFileNameA(lpName)) + return NULL; + + lpFileName = _strdup(&lpName[strnlen(NAMED_PIPE_PREFIX_PATH, sizeof(NAMED_PIPE_PREFIX_PATH))]); + return lpFileName; +} + +char* GetNamedPipeUnixDomainSocketBaseFilePathA(void) +{ + char* lpTempPath = NULL; + char* lpPipePath = NULL; + lpTempPath = GetKnownPath(KNOWN_PATH_TEMP); + + if (!lpTempPath) + return NULL; + + lpPipePath = GetCombinedPath(lpTempPath, ".pipe"); + free(lpTempPath); + return lpPipePath; +} + +char* GetNamedPipeUnixDomainSocketFilePathA(LPCSTR lpName) +{ + char* lpPipePath = NULL; + char* lpFileName = NULL; + char* lpFilePath = NULL; + lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA(); + lpFileName = GetNamedPipeNameWithoutPrefixA(lpName); + lpFilePath = GetCombinedPath(lpPipePath, (char*)lpFileName); + free(lpPipePath); + free(lpFileName); + return lpFilePath; +} + +int GetNamePipeFileDescriptor(HANDLE hNamedPipe) +{ +#ifndef _WIN32 + int fd = 0; + WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe; + + if (!NamedPipeClientIsHandled(hNamedPipe)) + return -1; + + fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd; + return fd; +#else + return -1; +#endif +} diff --git a/winpr/libwinpr/file/pattern.c b/winpr/libwinpr/file/pattern.c new file mode 100644 index 0000000..33adf9f --- /dev/null +++ b/winpr/libwinpr/file/pattern.c @@ -0,0 +1,371 @@ +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifdef WINPR_HAVE_FCNTL_H +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("file") + +/** + * File System Behavior in the Microsoft Windows Environment: + * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf + */ + +LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags) +{ + LPSTR lpWildcard = NULL; + *pFlags = 0; + lpWildcard = strpbrk(lpPattern, "*?~"); + + if (lpWildcard) + { + if (*lpWildcard == '*') + { + *pFlags = WILDCARD_STAR; + return lpWildcard; + } + else if (*lpWildcard == '?') + { + *pFlags = WILDCARD_QM; + return lpWildcard; + } + else if (*lpWildcard == '~') + { + if (lpWildcard[1] == '*') + { + *pFlags = WILDCARD_DOS_STAR; + return lpWildcard; + } + else if (lpWildcard[1] == '?') + { + *pFlags = WILDCARD_DOS_QM; + return lpWildcard; + } + else if (lpWildcard[1] == '.') + { + *pFlags = WILDCARD_DOS_DOT; + return lpWildcard; + } + } + } + + return NULL; +} + +static BOOL FilePatternMatchSubExpressionA(LPCSTR lpFileName, size_t cchFileName, LPCSTR lpX, + size_t cchX, LPCSTR lpY, size_t cchY, LPCSTR lpWildcard, + LPCSTR* ppMatchEnd) +{ + LPCSTR lpMatch = NULL; + + if (!lpFileName) + return FALSE; + + if (*lpWildcard == '*') + { + /* + * S + * <-----< + * X | | e Y + * X * Y == (0)----->-(1)->-----(2)-----(3) + */ + + /* + * State 0: match 'X' + */ + if (_strnicmp(lpFileName, lpX, cchX) != 0) + return FALSE; + + /* + * State 1: match 'S' or 'e' + * + * We use 'e' to transition to state 2 + */ + + /** + * State 2: match Y + */ + + if (cchY != 0) + { + /* TODO: case insensitive character search */ + lpMatch = strchr(&lpFileName[cchX], *lpY); + + if (!lpMatch) + return FALSE; + + if (_strnicmp(lpMatch, lpY, cchY) != 0) + return FALSE; + } + else + { + lpMatch = &lpFileName[cchFileName]; + } + + /** + * State 3: final state + */ + *ppMatchEnd = &lpMatch[cchY]; + return TRUE; + } + else if (*lpWildcard == '?') + { + /** + * X S Y + * X ? Y == (0)---(1)---(2)---(3) + */ + + /* + * State 0: match 'X' + */ + if (cchFileName < cchX) + return FALSE; + + if (_strnicmp(lpFileName, lpX, cchX) != 0) + return FALSE; + + /* + * State 1: match 'S' + */ + + /** + * State 2: match Y + */ + + if (cchY != 0) + { + /* TODO: case insensitive character search */ + lpMatch = strchr(&lpFileName[cchX + 1], *lpY); + + if (!lpMatch) + return FALSE; + + if (_strnicmp(lpMatch, lpY, cchY) != 0) + return FALSE; + } + else + { + if ((cchX + 1) > cchFileName) + return FALSE; + + lpMatch = &lpFileName[cchX + 1]; + } + + /** + * State 3: final state + */ + *ppMatchEnd = &lpMatch[cchY]; + return TRUE; + } + else if (*lpWildcard == '~') + { + WLog_ERR(TAG, "warning: unimplemented '~' pattern match"); + return TRUE; + } + + return FALSE; +} + +BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern) +{ + BOOL match = 0; + LPCSTR lpTail = NULL; + size_t cchTail = 0; + size_t cchPattern = 0; + size_t cchFileName = 0; + DWORD dwFlags = 0; + DWORD dwNextFlags = 0; + LPSTR lpWildcard = NULL; + LPSTR lpNextWildcard = NULL; + + /** + * Wild Card Matching + * + * '*' matches 0 or more characters + * '?' matches exactly one character + * + * '~*' DOS_STAR - matches 0 or more characters until encountering and matching final '.' + * + * '~?' DOS_QM - matches any single character, or upon encountering a period or end of name + * string, advances the expresssion to the end of the set of contiguous DOS_QMs. + * + * '~.' DOS_DOT - matches either a '.' or zero characters beyond name string. + */ + + if (!lpPattern) + return FALSE; + + if (!lpFileName) + return FALSE; + + cchPattern = strlen(lpPattern); + cchFileName = strlen(lpFileName); + + /** + * First and foremost the file system starts off name matching with the expression “*”. + * If the expression contains a single wild card character ‘*’ all matches are satisfied + * immediately. This is the most common wild card character used in Windows and expression + * evaluation is optimized by looking for this character first. + */ + + if ((lpPattern[0] == '*') && (cchPattern == 1)) + return TRUE; + + /** + * Subsequently evaluation of the “*X” expression is performed. This is a case where + * the expression starts off with a wild card character and contains some non-wild card + * characters towards the tail end of the name. This is evaluated by making sure the + * expression starts off with the character ‘*’ and does not contain any wildcards in + * the latter part of the expression. The tail part of the expression beyond the first + * character ‘*’ is matched against the file name at the end uppercasing each character + * if necessary during the comparison. + */ + + if (lpPattern[0] == '*') + { + lpTail = &lpPattern[1]; + cchTail = strlen(lpTail); + + if (!FilePatternFindNextWildcardA(lpTail, &dwFlags)) + { + /* tail contains no wildcards */ + if (cchFileName < cchTail) + return FALSE; + + if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0) + return TRUE; + + return FALSE; + } + } + + /** + * The remaining expressions are evaluated in a non deterministic + * finite order as listed below, where: + * + * 'S' is any single character + * 'S-.' is any single character except the final '.' + * 'e' is a null character transition + * 'EOF' is the end of the name string + * + * S + * <-----< + * X | | e Y + * X * Y == (0)----->-(1)->-----(2)-----(3) + * + * + * S-. + * <-----< + * X | | e Y + * X ~* Y == (0)----->-(1)->-----(2)-----(3) + * + * + * X S S Y + * X ?? Y == (0)---(1)---(2)---(3)---(4) + * + * + * X S-. S-. Y + * X ~?~? == (0)---(1)-----(2)-----(3)---(4) + * | |_______| + * | ^ | + * |_______________| + * ^EOF of .^ + * + */ + lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags); + + if (lpWildcard) + { + LPCSTR lpX = NULL; + LPCSTR lpY = NULL; + size_t cchX = 0; + size_t cchY = 0; + LPCSTR lpMatchEnd = NULL; + LPCSTR lpSubPattern = NULL; + size_t cchSubPattern = 0; + LPCSTR lpSubFileName = NULL; + size_t cchSubFileName = 0; + size_t cchWildcard = 0; + size_t cchNextWildcard = 0; + cchSubPattern = cchPattern; + lpSubPattern = lpPattern; + cchSubFileName = cchFileName; + lpSubFileName = lpFileName; + cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1); + lpNextWildcard = FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags); + + if (!lpNextWildcard) + { + lpX = lpSubPattern; + cchX = (lpWildcard - lpSubPattern); + lpY = &lpSubPattern[cchX + cchWildcard]; + cchY = (cchSubPattern - (lpY - lpSubPattern)); + match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY, + cchY, lpWildcard, &lpMatchEnd); + return match; + } + else + { + while (lpNextWildcard) + { + cchSubFileName = cchFileName - (lpSubFileName - lpFileName); + cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1); + lpX = lpSubPattern; + cchX = (lpWildcard - lpSubPattern); + lpY = &lpSubPattern[cchX + cchWildcard]; + cchY = (lpNextWildcard - lpWildcard) - cchWildcard; + match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, + lpY, cchY, lpWildcard, &lpMatchEnd); + + if (!match) + return FALSE; + + lpSubFileName = lpMatchEnd; + cchWildcard = cchNextWildcard; + lpWildcard = lpNextWildcard; + dwFlags = dwNextFlags; + lpNextWildcard = + FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags); + } + + return TRUE; + } + } + else + { + /* no wildcard characters */ + if (_stricmp(lpFileName, lpPattern) == 0) + return TRUE; + } + + return FALSE; +} diff --git a/winpr/libwinpr/file/test/CMakeLists.txt b/winpr/libwinpr/file/test/CMakeLists.txt new file mode 100644 index 0000000..3999271 --- /dev/null +++ b/winpr/libwinpr/file/test/CMakeLists.txt @@ -0,0 +1,58 @@ + +if (NOT WIN32) + set(MODULE_NAME "TestFile") + set(MODULE_PREFIX "TEST_FILE") + + set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + + set(${MODULE_PREFIX}_TESTS + TestFileCreateFile.c + TestFileDeleteFile.c + TestFileReadFile.c + TestSetFileAttributes.c + TestFileWriteFile.c + TestFilePatternMatch.c + TestFileFindFirstFile.c + TestFileFindFirstFileEx.c + TestFileFindNextFile.c + TestFileGetStdHandle.c + ) + + create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + + add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + + target_link_libraries(${MODULE_NAME} winpr) + + set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + + if(NOT MSVC) + set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}Area") + else() + set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}Area") + endif() + + file(MAKE_DIRECTORY "${TEST_AREA}") + file(WRITE "${TEST_AREA}/TestFile1" "TestFile1") + file(WRITE "${TEST_AREA}/TestFile2" "TestFile2") + file(WRITE "${TEST_AREA}/TestFile3" "TestFile3") + file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory1") + file(WRITE "${TEST_AREA}/TestDirectory1/TestDirectory1File1" "TestDirectory1File1") + file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory2") + file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File1" "TestDirectory2File1") + file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File2" "TestDirectory2File2") + file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory3") + file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File1" "TestDirectory3File1") + file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File2" "TestDirectory3File2") + file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File3" "TestDirectory3File3") + + foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName} ${TEST_AREA}) + endforeach() + + set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + +endif() diff --git a/winpr/libwinpr/file/test/TestFileCreateFile.c b/winpr/libwinpr/file/test/TestFileCreateFile.c new file mode 100644 index 0000000..a939416 --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileCreateFile.c @@ -0,0 +1,94 @@ + +#include +#include +#include +#include +#include +#include +#include + +int TestFileCreateFile(int argc, char* argv[]) +{ + HANDLE handle = NULL; + HRESULT hr = 0; + DWORD written = 0; + const char buffer[] = "Some random text\r\njust want it done."; + char cmp[sizeof(buffer)]; + char sname[8192]; + LPSTR name = NULL; + int rc = 0; + SYSTEMTIME systemTime; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + GetSystemTime(&systemTime); + sprintf_s(sname, sizeof(sname), + "CreateFile-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16 + "%02" PRIu16 "%04" PRIu16, + systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour, + systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds); + name = GetKnownSubPath(KNOWN_PATH_TEMP, sname); + + if (!name) + return -1; + + /* On windows we would need '\\' or '/' as seperator. + * Single '\' do not work. */ + hr = PathCchConvertStyleA(name, strlen(name), PATH_STYLE_UNIX); + + if (FAILED(hr)) + rc = -1; + + handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (!handle) + { + free(name); + return -1; + } + + if (!winpr_PathFileExists(name)) + rc = -1; + + if (!WriteFile(handle, buffer, sizeof(buffer), &written, NULL)) + rc = -1; + + if (written != sizeof(buffer)) + rc = -1; + + written = SetFilePointer(handle, 5, NULL, FILE_BEGIN); + + if (written != 5) + rc = -1; + + written = SetFilePointer(handle, 0, NULL, FILE_CURRENT); + + if (written != 5) + rc = -1; + + written = SetFilePointer(handle, -5, NULL, FILE_CURRENT); + + if (written != 0) + rc = -1; + + if (!ReadFile(handle, cmp, sizeof(cmp), &written, NULL)) + rc = -1; + + if (written != sizeof(cmp)) + rc = -1; + + if (memcmp(buffer, cmp, sizeof(buffer))) + rc = -1; + + if (!CloseHandle(handle)) + rc = -1; + + if (!winpr_DeleteFile(name)) + rc = -1; + + if (winpr_PathFileExists(name)) + rc = -1; + + free(name); + return rc; +} diff --git a/winpr/libwinpr/file/test/TestFileDeleteFile.c b/winpr/libwinpr/file/test/TestFileDeleteFile.c new file mode 100644 index 0000000..81f0599 --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileDeleteFile.c @@ -0,0 +1,50 @@ + +#include +#include +#include +#include +#include + +int TestFileDeleteFile(int argc, char* argv[]) +{ + BOOL rc = FALSE; + int fd = 0; + char validA[] = "/tmp/valid-test-file-XXXXXX"; + char validW[] = "/tmp/valid-test-file-XXXXXX"; + WCHAR* validWW = NULL; + const char invalidA[] = "/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + WCHAR invalidW[sizeof(invalidA)] = { 0 }; + + ConvertUtf8NToWChar(invalidA, ARRAYSIZE(invalidA), invalidW, ARRAYSIZE(invalidW)); + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + rc = DeleteFileA(invalidA); + if (rc) + return -1; + + rc = DeleteFileW(invalidW); + if (rc) + return -1; + + fd = mkstemp(validA); + if (fd < 0) + return -1; + + rc = DeleteFileA(validA); + if (!rc) + return -1; + + fd = mkstemp(validW); + if (fd < 0) + return -1; + + validWW = ConvertUtf8NToWCharAlloc(validW, ARRAYSIZE(validW), NULL); + if (validWW) + rc = DeleteFileW(validWW); + free(validWW); + if (!rc) + return -1; + return 0; +} diff --git a/winpr/libwinpr/file/test/TestFileFindFirstFile.c b/winpr/libwinpr/file/test/TestFileFindFirstFile.c new file mode 100644 index 0000000..04829a3 --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileFindFirstFile.c @@ -0,0 +1,326 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const CHAR testFile1A[] = "TestFile1A"; + +static BOOL create_layout_files(size_t level, const char* BasePath, wArrayList* files) +{ + for (size_t x = 0; x < 10; x++) + { + CHAR FilePath[PATHCCH_MAX_CCH] = { 0 }; + strncpy(FilePath, BasePath, ARRAYSIZE(FilePath)); + + CHAR name[64] = { 0 }; + _snprintf(name, ARRAYSIZE(name), "%zd-TestFile%zd", level, x); + NativePathCchAppendA(FilePath, PATHCCH_MAX_CCH, name); + + HANDLE hdl = + CreateFileA(FilePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hdl == INVALID_HANDLE_VALUE) + return FALSE; + ArrayList_Append(files, FilePath); + CloseHandle(hdl); + } + return TRUE; +} + +static BOOL create_layout_directories(size_t level, size_t max_level, const char* BasePath, + wArrayList* files) +{ + if (level >= max_level) + return TRUE; + + CHAR FilePath[PATHCCH_MAX_CCH] = { 0 }; + strncpy(FilePath, BasePath, ARRAYSIZE(FilePath)); + PathCchConvertStyleA(FilePath, ARRAYSIZE(FilePath), PATH_STYLE_NATIVE); + if (!winpr_PathMakePath(FilePath, NULL)) + return FALSE; + ArrayList_Append(files, FilePath); + + if (!create_layout_files(level + 1, BasePath, files)) + return FALSE; + + for (size_t x = 0; x < 10; x++) + { + CHAR CurFilePath[PATHCCH_MAX_CCH] = { 0 }; + strncpy(CurFilePath, FilePath, ARRAYSIZE(CurFilePath)); + + PathCchConvertStyleA(CurFilePath, ARRAYSIZE(CurFilePath), PATH_STYLE_NATIVE); + + CHAR name[64] = { 0 }; + _snprintf(name, ARRAYSIZE(name), "%zd-TestPath%zd", level, x); + NativePathCchAppendA(CurFilePath, PATHCCH_MAX_CCH, name); + + if (!create_layout_directories(level + 1, max_level, CurFilePath, files)) + return FALSE; + } + return TRUE; +} + +static BOOL create_layout(const char* BasePath, wArrayList* files) +{ + CHAR BasePathNative[PATHCCH_MAX_CCH] = { 0 }; + memcpy(BasePathNative, BasePath, sizeof(BasePathNative)); + PathCchConvertStyleA(BasePathNative, ARRAYSIZE(BasePathNative), PATH_STYLE_NATIVE); + + return create_layout_directories(0, 3, BasePathNative, files); +} + +static void cleanup_layout(const char* BasePath) +{ + winpr_RemoveDirectory_RecursiveA(BasePath); +} + +static BOOL find_first_file_success(const char* FilePath) +{ + BOOL rc = FALSE; + WIN32_FIND_DATAA FindData = { 0 }; + HANDLE hFind = FindFirstFileA(FilePath, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + { + printf("FindFirstFile failure: %s (INVALID_HANDLE_VALUE -1)\n", FilePath); + goto fail; + } + + printf("FindFirstFile: %s", FindData.cFileName); + + if (strcmp(FindData.cFileName, testFile1A) != 0) + { + printf("FindFirstFile failure: Expected: %s, Actual: %s\n", testFile1A, FindData.cFileName); + goto fail; + } + rc = TRUE; +fail: + FindClose(hFind); + return rc; +} + +static BOOL list_directory_dot(const char* BasePath, wArrayList* files) +{ + BOOL rc = FALSE; + CHAR BasePathDot[PATHCCH_MAX_CCH] = { 0 }; + memcpy(BasePathDot, BasePath, ARRAYSIZE(BasePathDot)); + PathCchConvertStyleA(BasePathDot, ARRAYSIZE(BasePathDot), PATH_STYLE_NATIVE); + NativePathCchAppendA(BasePathDot, PATHCCH_MAX_CCH, "."); + WIN32_FIND_DATAA FindData = { 0 }; + HANDLE hFind = FindFirstFileA(BasePathDot, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + return FALSE; + size_t count = 0; + do + { + count++; + if (strcmp(FindData.cFileName, ".") != 0) + goto fail; + } while (FindNextFile(hFind, &FindData)); + + rc = TRUE; +fail: + FindClose(hFind); + + if (count != 1) + return FALSE; + return rc; +} + +static BOOL list_directory_star(const char* BasePath, wArrayList* files) +{ + CHAR BasePathDot[PATHCCH_MAX_CCH] = { 0 }; + memcpy(BasePathDot, BasePath, ARRAYSIZE(BasePathDot)); + PathCchConvertStyleA(BasePathDot, ARRAYSIZE(BasePathDot), PATH_STYLE_NATIVE); + NativePathCchAppendA(BasePathDot, PATHCCH_MAX_CCH, "*"); + WIN32_FIND_DATAA FindData = { 0 }; + HANDLE hFind = FindFirstFileA(BasePathDot, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + return FALSE; + size_t count = 0; + size_t dotcount = 0; + size_t dotdotcount = 0; + do + { + if (strcmp(FindData.cFileName, ".") == 0) + dotcount++; + else if (strcmp(FindData.cFileName, "..") == 0) + dotdotcount++; + else + count++; + } while (FindNextFile(hFind, &FindData)); + FindClose(hFind); + + const char sep = PathGetSeparatorA(PATH_STYLE_NATIVE); + size_t fcount = 0; + const size_t baselen = strlen(BasePath); + const size_t total = ArrayList_Count(files); + for (size_t x = 0; x < total; x++) + { + const char* path = ArrayList_GetItem(files, x); + const size_t pathlen = strlen(path); + if (pathlen < baselen) + continue; + const char* skip = &path[baselen]; + if (*skip == sep) + skip++; + const char* end = strrchr(skip, sep); + if (end) + continue; + fcount++; + } + + if (fcount != count) + return FALSE; + return TRUE; +} + +static BOOL find_first_file_fail(const char* FilePath) +{ + WIN32_FIND_DATAA FindData = { 0 }; + HANDLE hFind = FindFirstFileA(FilePath, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + return TRUE; + + FindClose(hFind); + return FALSE; +} + +static int TestFileFindFirstFileA(const char* str) +{ + int rc = -1; + if (!str) + return -1; + + CHAR BasePath[PATHCCH_MAX_CCH] = { 0 }; + + strncpy(BasePath, str, ARRAYSIZE(BasePath)); + + const size_t length = strnlen(BasePath, PATHCCH_MAX_CCH - 1); + + CHAR FilePath[PATHCCH_MAX_CCH] = { 0 }; + CopyMemory(FilePath, BasePath, length * sizeof(CHAR)); + + PathCchConvertStyleA(BasePath, length, PATH_STYLE_WINDOWS); + + wArrayList* files = ArrayList_New(FALSE); + if (!files) + return -3; + wObject* obj = ArrayList_Object(files); + obj->fnObjectFree = winpr_ObjectStringFree; + obj->fnObjectNew = winpr_ObjectStringClone; + + if (!create_layout(BasePath, files)) + return -1; + + NativePathCchAppendA(FilePath, PATHCCH_MAX_CCH, testFile1A); + + printf("Finding file: %s\n", FilePath); + + if (!find_first_file_fail(FilePath)) + goto fail; + + HANDLE hdl = + CreateFileA(FilePath, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hdl == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(hdl); + + if (!find_first_file_success(FilePath)) + goto fail; + + CHAR BasePathInvalid[PATHCCH_MAX_CCH] = { 0 }; + memcpy(BasePathInvalid, BasePath, ARRAYSIZE(BasePathInvalid)); + PathCchAddBackslashA(BasePathInvalid, PATHCCH_MAX_CCH); + + if (!find_first_file_fail(BasePathInvalid)) + goto fail; + + if (!list_directory_dot(BasePath, files)) + goto fail; + + if (!list_directory_star(BasePath, files)) + goto fail; + + rc = 0; +fail: + DeleteFileA(FilePath); + cleanup_layout(BasePath); + ArrayList_Free(files); + return rc; +} + +static int TestFileFindFirstFileW(const char* str) +{ + WCHAR buffer[32] = { 0 }; + const WCHAR* testFile1W = InitializeConstWCharFromUtf8("TestFile1W", buffer, ARRAYSIZE(buffer)); + int rc = -1; + if (!str) + return -1; + + WCHAR BasePath[PATHCCH_MAX_CCH] = { 0 }; + + ConvertUtf8ToWChar(str, BasePath, ARRAYSIZE(BasePath)); + + const size_t length = _wcsnlen(BasePath, PATHCCH_MAX_CCH - 1); + + WCHAR FilePath[PATHCCH_MAX_CCH] = { 0 }; + CopyMemory(FilePath, BasePath, length * sizeof(WCHAR)); + + PathCchConvertStyleW(BasePath, length, PATH_STYLE_WINDOWS); + NativePathCchAppendW(FilePath, PATHCCH_MAX_CCH, testFile1W); + + CHAR FilePathA[PATHCCH_MAX_CCH] = { 0 }; + ConvertWCharNToUtf8(FilePath, ARRAYSIZE(FilePath), FilePathA, ARRAYSIZE(FilePathA)); + printf("Finding file: %s\n", FilePathA); + + WIN32_FIND_DATAW FindData = { 0 }; + HANDLE hFind = FindFirstFileW(FilePath, &FindData); + + if (hFind == INVALID_HANDLE_VALUE) + { + printf("FindFirstFile failure: %s (INVALID_HANDLE_VALUE -1)\n", FilePathA); + goto fail; + } + + CHAR cFileName[MAX_PATH] = { 0 }; + ConvertWCharNToUtf8(FindData.cFileName, ARRAYSIZE(FindData.cFileName), cFileName, + ARRAYSIZE(cFileName)); + + printf("FindFirstFile: %s", cFileName); + + if (_wcscmp(FindData.cFileName, testFile1W) != 0) + { + printf("FindFirstFile failure: Expected: %s, Actual: %s\n", testFile1A, cFileName); + goto fail; + } + + rc = 0; +fail: + DeleteFileW(FilePath); + FindClose(hFind); + return rc; +} + +int TestFileFindFirstFile(int argc, char* argv[]) +{ + char* str = GetKnownSubPath(KNOWN_PATH_TEMP, "TestFileFindFirstFile"); + if (!str) + return -23; + + cleanup_layout(str); + + int rc1 = -1; + int rc2 = -1; + if (winpr_PathMakePath(str, NULL)) + { + rc1 = TestFileFindFirstFileA(str); + rc2 = 0; // TestFileFindFirstFileW(str); + winpr_RemoveDirectory(str); + } + free(str); + return rc1 + rc2; +} diff --git a/winpr/libwinpr/file/test/TestFileFindFirstFileEx.c b/winpr/libwinpr/file/test/TestFileFindFirstFileEx.c new file mode 100644 index 0000000..b8a7683 --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileFindFirstFileEx.c @@ -0,0 +1,10 @@ + +#include +#include +#include +#include + +int TestFileFindFirstFileEx(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/file/test/TestFileFindNextFile.c b/winpr/libwinpr/file/test/TestFileFindNextFile.c new file mode 100644 index 0000000..ebb9bec --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileFindNextFile.c @@ -0,0 +1,99 @@ + +#include +#include +#include +#include +#include +#include + +static TCHAR testDirectory2File1[] = _T("TestDirectory2File1"); +static TCHAR testDirectory2File2[] = _T("TestDirectory2File2"); + +int TestFileFindNextFile(int argc, char* argv[]) +{ + char* str = NULL; + size_t length = 0; + BOOL status = 0; + HANDLE hFind = NULL; + LPTSTR BasePath = NULL; + WIN32_FIND_DATA FindData; + TCHAR FilePath[PATHCCH_MAX_CCH] = { 0 }; + WINPR_UNUSED(argc); + str = argv[1]; +#ifdef UNICODE + BasePath = ConvertUtf8ToWChar(str, &length); + + if (!BasePath) + { + _tprintf(_T("Unable to allocate memory")); + return -1; + } +#else + BasePath = _strdup(str); + + if (!BasePath) + { + printf("Unable to allocate memory"); + return -1; + } + + length = strlen(BasePath); +#endif + /* Simple filter matching all files inside current directory */ + CopyMemory(FilePath, BasePath, length * sizeof(TCHAR)); + FilePath[length] = 0; + PathCchConvertStyle(BasePath, length, PATH_STYLE_WINDOWS); + NativePathCchAppend(FilePath, PATHCCH_MAX_CCH, _T("TestDirectory2")); + NativePathCchAppend(FilePath, PATHCCH_MAX_CCH, _T("TestDirectory2File*")); + free(BasePath); + _tprintf(_T("Finding file: %s\n"), FilePath); + hFind = FindFirstFile(FilePath, &FindData); + + if (hFind == INVALID_HANDLE_VALUE) + { + _tprintf(_T("FindFirstFile failure: %s\n"), FilePath); + return -1; + } + + _tprintf(_T("FindFirstFile: %s"), FindData.cFileName); + + /** + * The current implementation does not enforce a particular order + */ + + if ((_tcscmp(FindData.cFileName, testDirectory2File1) != 0) && + (_tcscmp(FindData.cFileName, testDirectory2File2) != 0)) + { + _tprintf(_T("FindFirstFile failure: Expected: %s, Actual: %s\n"), testDirectory2File1, + FindData.cFileName); + return -1; + } + + status = FindNextFile(hFind, &FindData); + + if (!status) + { + _tprintf(_T("FindNextFile failure: Expected: TRUE, Actual: %") _T(PRId32) _T("\n"), status); + return -1; + } + + if ((_tcscmp(FindData.cFileName, testDirectory2File1) != 0) && + (_tcscmp(FindData.cFileName, testDirectory2File2) != 0)) + { + _tprintf(_T("FindNextFile failure: Expected: %s, Actual: %s\n"), testDirectory2File2, + FindData.cFileName); + return -1; + } + + status = FindNextFile(hFind, &FindData); + + if (status) + { + _tprintf(_T("FindNextFile failure: Expected: FALSE, Actual: %") _T(PRId32) _T("\n"), + status); + return -1; + } + + FindClose(hFind); + return 0; +} diff --git a/winpr/libwinpr/file/test/TestFileGetStdHandle.c b/winpr/libwinpr/file/test/TestFileGetStdHandle.c new file mode 100644 index 0000000..79ce4ae --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileGetStdHandle.c @@ -0,0 +1,49 @@ + +/** + * WinPR: Windows Portable Runtime + * File Functions + * + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 Bernhard Miklautz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +int TestFileGetStdHandle(int argc, char* argv[]) +{ + HANDLE so = NULL; + const char buf[] = "happy happy"; + DWORD bytesWritten = 0; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + so = GetStdHandle(STD_OUTPUT_HANDLE); + if (so == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "GetStdHandle failed ;(\n"); + return -1; + } + WriteFile(so, buf, strnlen(buf, sizeof(buf)), &bytesWritten, FALSE); + if (bytesWritten != strnlen(buf, sizeof(buf))) + { + fprintf(stderr, "write failed\n"); + return -1; + } + CloseHandle(so); + + return 0; +} diff --git a/winpr/libwinpr/file/test/TestFilePatternMatch.c b/winpr/libwinpr/file/test/TestFilePatternMatch.c new file mode 100644 index 0000000..8f7a2fb --- /dev/null +++ b/winpr/libwinpr/file/test/TestFilePatternMatch.c @@ -0,0 +1,182 @@ + +#include +#include +#include +#include + +int TestFilePatternMatch(int argc, char* argv[]) +{ + /* '*' expression */ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + if (!FilePatternMatchA("document.txt", "*")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", "*"); + return -1; + } + + /* '*X' expression */ + + if (!FilePatternMatchA("document.txt", "*.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", "*.txt"); + return -1; + } + + if (FilePatternMatchA("document.docx", "*.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.docx", "*.txt"); + return -1; + } + + if (FilePatternMatchA("document.txt.bak", "*.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt.bak", "*.txt"); + return -1; + } + + if (FilePatternMatchA("bak", "*.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "bak", "*.txt"); + return -1; + } + + /* 'X*' expression */ + + if (!FilePatternMatchA("document.txt", "document.*")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", "document.*"); + return -1; + } + + /* 'X?' expression */ + + if (!FilePatternMatchA("document.docx", "document.doc?")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.docx", + "document.doc?"); + return -1; + } + + if (FilePatternMatchA("document.doc", "document.doc?")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.doc", + "document.doc?"); + return -1; + } + + /* no wildcards expression */ + + if (!FilePatternMatchA("document.txt", "document.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "document.txt", + "document.txt"); + return -1; + } + + /* 'X * Y' expression */ + + if (!FilePatternMatchA("X123Y.txt", "X*Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y.txt", "X*Y.txt"); + return -1; + } + + if (!FilePatternMatchA("XY.txt", "X*Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XY.txt", "X*Y.txt"); + return -1; + } + + if (FilePatternMatchA("XZ.txt", "X*Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XZ.txt", "X*Y.txt"); + return -1; + } + + if (FilePatternMatchA("X123Z.txt", "X*Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Z.txt", "X*Y.txt"); + return -1; + } + + /* 'X * Y * Z' expression */ + + if (!FilePatternMatchA("X123Y456Z.txt", "X*Y*Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456Z.txt", "X*Y*Z.txt"); + return -1; + } + + if (!FilePatternMatchA("XYZ.txt", "X*Y*Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYZ.txt", "X*Y*Z.txt"); + return -1; + } + + if (!FilePatternMatchA("X123Y456W.txt", "X*Y*Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456W.txt", "X*Y*Z.txt"); + return -1; + } + + if (!FilePatternMatchA("XYW.txt", "X*Y*Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYW.txt", "X*Y*Z.txt"); + return -1; + } + + /* 'X ? Y' expression */ + + if (!FilePatternMatchA("X1Y.txt", "X?Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X1Y.txt", "X?Y.txt"); + return -1; + } + + if (FilePatternMatchA("XY.txt", "X?Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XY.txt", "X?Y.txt"); + return -1; + } + + if (FilePatternMatchA("XZ.txt", "X?Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XZ.txt", "X?Y.txt"); + return -1; + } + + if (FilePatternMatchA("X123Z.txt", "X?Y.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Z.txt", "X?Y.txt"); + return -1; + } + + /* 'X ? Y ? Z' expression */ + + if (!FilePatternMatchA("X123Y456Z.txt", "X?Y?Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456Z.txt", "X?Y?Z.txt"); + return -1; + } + + if (FilePatternMatchA("XYZ.txt", "X?Y?Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYZ.txt", "X?Y?Z.txt"); + return -1; + } + + if (!FilePatternMatchA("X123Y456W.txt", "X?Y?Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "X123Y456W.txt", "X?Y?Z.txt"); + return -1; + } + + if (FilePatternMatchA("XYW.txt", "X?Y?Z.txt")) + { + printf("FilePatternMatchA error: FileName: %s Pattern: %s\n", "XYW.txt", "X?Y?Z.txt"); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/file/test/TestFileReadFile.c b/winpr/libwinpr/file/test/TestFileReadFile.c new file mode 100644 index 0000000..936881a --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileReadFile.c @@ -0,0 +1,10 @@ + +#include +#include +#include +#include + +int TestFileReadFile(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/file/test/TestFileWriteFile.c b/winpr/libwinpr/file/test/TestFileWriteFile.c new file mode 100644 index 0000000..a8283ee --- /dev/null +++ b/winpr/libwinpr/file/test/TestFileWriteFile.c @@ -0,0 +1,10 @@ + +#include +#include +#include +#include + +int TestFileWriteFile(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/file/test/TestSetFileAttributes.c b/winpr/libwinpr/file/test/TestSetFileAttributes.c new file mode 100644 index 0000000..f423ec2 --- /dev/null +++ b/winpr/libwinpr/file/test/TestSetFileAttributes.c @@ -0,0 +1,152 @@ + +#include +#include +#include +#include +#include +#include +#include + +static const DWORD allflags[] = { + 0, + FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_ARCHIVE, + FILE_ATTRIBUTE_DEVICE, + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_TEMPORARY, + FILE_ATTRIBUTE_SPARSE_FILE, + FILE_ATTRIBUTE_REPARSE_POINT, + FILE_ATTRIBUTE_COMPRESSED, + FILE_ATTRIBUTE_OFFLINE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + FILE_ATTRIBUTE_ENCRYPTED, + FILE_ATTRIBUTE_VIRTUAL, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DEVICE | + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_OFFLINE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_VIRTUAL +}; + +static BOOL test_SetFileAttributesA(void) +{ + BOOL rc = FALSE; + HANDLE handle = NULL; + const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY }; + char* name = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd"); + if (!name) + goto fail; + + for (size_t x = 0; x < ARRAYSIZE(allflags); x++) + { + const DWORD flag = allflags[x]; + const BOOL brc = SetFileAttributesA(NULL, flag); + if (brc) + goto fail; + + const BOOL crc = SetFileAttributesA(name, flag); + if (crc) + goto fail; + } + + handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(handle); + + for (size_t x = 0; x < ARRAYSIZE(flags); x++) + { + DWORD attr = 0; + const DWORD flag = flags[x]; + const BOOL brc = SetFileAttributesA(name, flag); + if (!brc) + goto fail; + + attr = GetFileAttributesA(name); + if (flag != 0) + { + if ((attr & flag) == 0) + goto fail; + } + } + + rc = TRUE; + +fail: + DeleteFileA(name); + free(name); + return rc; +} + +static BOOL test_SetFileAttributesW(void) +{ + BOOL rc = FALSE; + WCHAR* name = NULL; + HANDLE handle = NULL; + const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY }; + char* base = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd"); + if (!base) + goto fail; + + name = ConvertUtf8ToWCharAlloc(base, NULL); + if (!name) + goto fail; + + for (size_t x = 0; x < ARRAYSIZE(allflags); x++) + { + const DWORD flag = allflags[x]; + const BOOL brc = SetFileAttributesW(NULL, flag); + if (brc) + goto fail; + + const BOOL crc = SetFileAttributesW(name, flag); + if (crc) + goto fail; + } + + handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(handle); + + for (size_t x = 0; x < ARRAYSIZE(flags); x++) + { + DWORD attr = 0; + const DWORD flag = flags[x]; + const BOOL brc = SetFileAttributesW(name, flag); + if (!brc) + goto fail; + + attr = GetFileAttributesW(name); + if (flag != 0) + { + if ((attr & flag) == 0) + goto fail; + } + } + + rc = TRUE; +fail: + DeleteFileW(name); + free(name); + free(base); + return rc; +} + +int TestSetFileAttributes(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_SetFileAttributesA()) + return -1; + if (!test_SetFileAttributesW()) + return -1; + return 0; +} diff --git a/winpr/libwinpr/handle/CMakeLists.txt b/winpr/libwinpr/handle/CMakeLists.txt new file mode 100644 index 0000000..37c410e --- /dev/null +++ b/winpr/libwinpr/handle/CMakeLists.txt @@ -0,0 +1,23 @@ +# WinPR: Windows Portable Runtime +# libwinpr-handle cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# Copyright 2014 DI (FH) Martin Haimberger +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(handle.c handle.h nonehandle.c nonehandle.h) + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + winpr_library_add_private(rt) +endif() diff --git a/winpr/libwinpr/handle/ModuleOptions.cmake b/winpr/libwinpr/handle/ModuleOptions.cmake new file mode 100644 index 0000000..545a8e9 --- /dev/null +++ b/winpr/libwinpr/handle/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "handle") +set(MINWIN_LONG_NAME "Handle and Object Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/handle/handle.c b/winpr/libwinpr/handle/handle.c new file mode 100644 index 0000000..5c35d42 --- /dev/null +++ b/winpr/libwinpr/handle/handle.c @@ -0,0 +1,81 @@ +/** + * WinPR: Windows Portable Runtime + * Handle Management + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#ifndef _WIN32 + +#include + +#include "../synch/synch.h" +#include "../thread/thread.h" +#include "../pipe/pipe.h" +#include "../comm/comm.h" +#include "../security/security.h" + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include + +#include "../handle/handle.h" + +BOOL CloseHandle(HANDLE hObject) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + + if (!winpr_Handle_GetInfo(hObject, &Type, &Object)) + return FALSE; + + if (!Object) + return FALSE; + + if (!Object->ops) + return FALSE; + + if (Object->ops->CloseHandle) + return Object->ops->CloseHandle(hObject); + + return FALSE; +} + +BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, + LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, + DWORD dwOptions) +{ + *((ULONG_PTR*)lpTargetHandle) = (ULONG_PTR)hSourceHandle; + return TRUE; +} + +BOOL GetHandleInformation(HANDLE hObject, LPDWORD lpdwFlags) +{ + return TRUE; +} + +BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags) +{ + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/handle/handle.h b/winpr/libwinpr/handle/handle.h new file mode 100644 index 0000000..dda6d35 --- /dev/null +++ b/winpr/libwinpr/handle/handle.h @@ -0,0 +1,198 @@ +/** + * WinPR: Windows Portable Runtime + * Handle Management + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_HANDLE_PRIVATE_H +#define WINPR_HANDLE_PRIVATE_H + +#include +#include +#include +#include + +#define HANDLE_TYPE_NONE 0 +#define HANDLE_TYPE_PROCESS 1 +#define HANDLE_TYPE_THREAD 2 +#define HANDLE_TYPE_EVENT 3 +#define HANDLE_TYPE_MUTEX 4 +#define HANDLE_TYPE_SEMAPHORE 5 +#define HANDLE_TYPE_TIMER 6 +#define HANDLE_TYPE_NAMED_PIPE 7 +#define HANDLE_TYPE_ANONYMOUS_PIPE 8 +#define HANDLE_TYPE_ACCESS_TOKEN 9 +#define HANDLE_TYPE_FILE 10 +#define HANDLE_TYPE_TIMER_QUEUE 11 +#define HANDLE_TYPE_TIMER_QUEUE_TIMER 12 +#define HANDLE_TYPE_COMM 13 + +typedef BOOL (*pcIsHandled)(HANDLE handle); +typedef BOOL (*pcCloseHandle)(HANDLE handle); +typedef int (*pcGetFd)(HANDLE handle); +typedef DWORD (*pcCleanupHandle)(HANDLE handle); +typedef BOOL (*pcReadFile)(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcReadFileEx)(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +typedef BOOL (*pcReadFileScatter)(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], + DWORD nNumberOfBytesToRead, LPDWORD lpReserved, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcWriteFile)(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcWriteFileEx)(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +typedef BOOL (*pcWriteFileGather)(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], + DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, + LPOVERLAPPED lpOverlapped); +typedef DWORD (*pcGetFileSize)(HANDLE handle, LPDWORD lpFileSizeHigh); +typedef BOOL (*pcGetFileInformationByHandle)(HANDLE handle, + LPBY_HANDLE_FILE_INFORMATION lpFileInformation); +typedef BOOL (*pcFlushFileBuffers)(HANDLE hFile); +typedef BOOL (*pcSetEndOfFile)(HANDLE handle); +typedef DWORD (*pcSetFilePointer)(HANDLE handle, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod); +typedef BOOL (*pcSetFilePointerEx)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, + PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod); +typedef BOOL (*pcLockFile)(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh); +typedef BOOL (*pcLockFileEx)(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcUnlockFile)(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh); +typedef BOOL (*pcUnlockFileEx)(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcSetFileTime)(HANDLE hFile, const FILETIME* lpCreationTime, + const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime); + +typedef struct +{ + pcIsHandled IsHandled; + pcCloseHandle CloseHandle; + pcGetFd GetFd; + pcCleanupHandle CleanupHandle; + pcReadFile ReadFile; + pcReadFileEx ReadFileEx; + pcReadFileScatter ReadFileScatter; + pcWriteFile WriteFile; + pcWriteFileEx WriteFileEx; + pcWriteFileGather WriteFileGather; + pcGetFileSize GetFileSize; + pcFlushFileBuffers FlushFileBuffers; + pcSetEndOfFile SetEndOfFile; + pcSetFilePointer SetFilePointer; + pcSetFilePointerEx SetFilePointerEx; + pcLockFile LockFile; + pcLockFileEx LockFileEx; + pcUnlockFile UnlockFile; + pcUnlockFileEx UnlockFileEx; + pcSetFileTime SetFileTime; + pcGetFileInformationByHandle GetFileInformationByHandle; +} HANDLE_OPS; + +typedef struct +{ + ULONG Type; + ULONG Mode; + HANDLE_OPS* ops; +} WINPR_HANDLE; + +static INLINE BOOL WINPR_HANDLE_IS_HANDLED(HANDLE handle, ULONG type, BOOL invalidValue) +{ + WINPR_HANDLE* pWinprHandle = (WINPR_HANDLE*)handle; + BOOL invalid = !pWinprHandle; + + if (invalidValue) + { + if (INVALID_HANDLE_VALUE == pWinprHandle) + invalid = TRUE; + } + + if (invalid || (pWinprHandle->Type != type)) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + return TRUE; +} + +static INLINE void WINPR_HANDLE_SET_TYPE_AND_MODE(void* _handle, ULONG _type, ULONG _mode) +{ + WINPR_HANDLE* hdl = (WINPR_HANDLE*)_handle; + + hdl->Type = _type; + hdl->Mode = _mode; +} + +static INLINE BOOL winpr_Handle_GetInfo(HANDLE handle, ULONG* pType, WINPR_HANDLE** pObject) +{ + WINPR_HANDLE* wHandle; + + if (handle == NULL) + return FALSE; + + /* INVALID_HANDLE_VALUE is an invalid value for every handle, but it + * confuses the clang scanbuild analyzer. */ +#ifndef __clang_analyzer__ + if (handle == INVALID_HANDLE_VALUE) + return FALSE; +#endif + + wHandle = (WINPR_HANDLE*)handle; + + *pType = wHandle->Type; + *pObject = handle; + + return TRUE; +} + +static INLINE int winpr_Handle_getFd(HANDLE handle) +{ + WINPR_HANDLE* hdl; + ULONG type; + + if (!winpr_Handle_GetInfo(handle, &type, &hdl)) + return -1; + + if (!hdl || !hdl->ops || !hdl->ops->GetFd) + return -1; + + return hdl->ops->GetFd(handle); +} + +static INLINE DWORD winpr_Handle_cleanup(HANDLE handle) +{ + WINPR_HANDLE* hdl; + ULONG type; + + if (!winpr_Handle_GetInfo(handle, &type, &hdl)) + return WAIT_FAILED; + + if (!hdl || !hdl->ops) + return WAIT_FAILED; + + /* If there is no cleanup function, assume all ok. */ + if (!hdl->ops->CleanupHandle) + return WAIT_OBJECT_0; + + return hdl->ops->CleanupHandle(handle); +} + +#endif /* WINPR_HANDLE_PRIVATE_H */ diff --git a/winpr/libwinpr/handle/nonehandle.c b/winpr/libwinpr/handle/nonehandle.c new file mode 100644 index 0000000..1fe54b8 --- /dev/null +++ b/winpr/libwinpr/handle/nonehandle.c @@ -0,0 +1,82 @@ +/** + * WinPR: Windows Portable Runtime + * NoneHandle a.k.a. brathandle should be used where a handle is needed, but + * functionality is not implemented yet or not implementable. + * + * Copyright 2014 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "nonehandle.h" + +#ifndef _WIN32 + +#include + +static BOOL NoneHandleCloseHandle(HANDLE handle) +{ + WINPR_NONE_HANDLE* none = (WINPR_NONE_HANDLE*)handle; + free(none); + return TRUE; +} + +static BOOL NoneHandleIsHandle(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NONE, FALSE); +} + +static int NoneHandleGetFd(HANDLE handle) +{ + if (!NoneHandleIsHandle(handle)) + return -1; + + return -1; +} + +static HANDLE_OPS ops = { NoneHandleIsHandle, + NoneHandleCloseHandle, + NoneHandleGetFd, + NULL, /* CleanupHandle */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +HANDLE CreateNoneHandle(void) +{ + WINPR_NONE_HANDLE* none = (WINPR_NONE_HANDLE*)calloc(1, sizeof(WINPR_NONE_HANDLE)); + + if (!none) + return NULL; + + none->common.ops = &ops; + return (HANDLE)none; +} + +#endif diff --git a/winpr/libwinpr/handle/nonehandle.h b/winpr/libwinpr/handle/nonehandle.h new file mode 100644 index 0000000..50e224c --- /dev/null +++ b/winpr/libwinpr/handle/nonehandle.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * NoneHandle a.k.a. brathandle should be used where a handle is needed, but + * functionality is not implemented yet or not implementable. + * + * Copyright 2014 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_NONE_HANDLE_PRIVATE_H +#define WINPR_NONE_HANDLE_PRIVATE_H + +#ifndef _WIN32 + +#include +#include "handle.h" + +struct winpr_none_handle +{ + WINPR_HANDLE common; +}; + +typedef struct winpr_none_handle WINPR_NONE_HANDLE; + +HANDLE CreateNoneHandle(void); + +#endif /*_WIN32*/ + +#endif /* WINPR_NONE_HANDLE_PRIVATE_H */ diff --git a/winpr/libwinpr/input/CMakeLists.txt b/winpr/libwinpr/input/CMakeLists.txt new file mode 100644 index 0000000..b23754a --- /dev/null +++ b/winpr/libwinpr/input/CMakeLists.txt @@ -0,0 +1,21 @@ +# WinPR: Windows Portable Runtime +# libwinpr-input cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + virtualkey.c + scancode.c + keycode.c) diff --git a/winpr/libwinpr/input/ModuleOptions.cmake b/winpr/libwinpr/input/ModuleOptions.cmake new file mode 100644 index 0000000..db32710 --- /dev/null +++ b/winpr/libwinpr/input/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "input") +set(MINWIN_LONG_NAME "Input Functions") +set(MODULE_LIBRARY_NAME "input") + diff --git a/winpr/libwinpr/input/keycode.c b/winpr/libwinpr/input/keycode.c new file mode 100644 index 0000000..cf61724 --- /dev/null +++ b/winpr/libwinpr/input/keycode.c @@ -0,0 +1,910 @@ +/** + * WinPR: Windows Portable Runtime + * Keyboard Input + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +/** + * X11 Keycodes + */ + +/** + * Mac OS X + */ + +static DWORD KEYCODE_TO_VKCODE_APPLE[256] = { + VK_KEY_A, /* APPLE_VK_ANSI_A (0x00) */ + VK_KEY_S, /* APPLE_VK_ANSI_S (0x01) */ + VK_KEY_D, /* APPLE_VK_ANSI_D (0x02) */ + VK_KEY_F, /* APPLE_VK_ANSI_F (0x03) */ + VK_KEY_H, /* APPLE_VK_ANSI_H (0x04) */ + VK_KEY_G, /* APPLE_VK_ANSI_G (0x05) */ + VK_KEY_Z, /* APPLE_VK_ANSI_Z (0x06) */ + VK_KEY_X, /* APPLE_VK_ANSI_X (0x07) */ + VK_KEY_C, /* APPLE_VK_ANSI_C (0x08) */ + VK_KEY_V, /* APPLE_VK_ANSI_V (0x09) */ + VK_OEM_102, /* APPLE_VK_ISO_Section (0x0A) */ + VK_KEY_B, /* APPLE_VK_ANSI_B (0x0B) */ + VK_KEY_Q, /* APPLE_VK_ANSI_Q (0x0C) */ + VK_KEY_W, /* APPLE_VK_ANSI_W (0x0D) */ + VK_KEY_E, /* APPLE_VK_ANSI_E (0x0E) */ + VK_KEY_R, /* APPLE_VK_ANSI_R (0x0F) */ + VK_KEY_Y, /* APPLE_VK_ANSI_Y (0x10) */ + VK_KEY_T, /* APPLE_VK_ANSI_T (0x11) */ + VK_KEY_1, /* APPLE_VK_ANSI_1 (0x12) */ + VK_KEY_2, /* APPLE_VK_ANSI_2 (0x13) */ + VK_KEY_3, /* APPLE_VK_ANSI_3 (0x14) */ + VK_KEY_4, /* APPLE_VK_ANSI_4 (0x15) */ + VK_KEY_6, /* APPLE_VK_ANSI_6 (0x16) */ + VK_KEY_5, /* APPLE_VK_ANSI_5 (0x17) */ + VK_OEM_PLUS, /* APPLE_VK_ANSI_Equal (0x18) */ + VK_KEY_9, /* APPLE_VK_ANSI_9 (0x19) */ + VK_KEY_7, /* APPLE_VK_ANSI_7 (0x1A) */ + VK_OEM_MINUS, /* APPLE_VK_ANSI_Minus (0x1B) */ + VK_KEY_8, /* APPLE_VK_ANSI_8 (0x1C) */ + VK_KEY_0, /* APPLE_VK_ANSI_0 (0x1D) */ + VK_OEM_6, /* APPLE_VK_ANSI_RightBracket (0x1E) */ + VK_KEY_O, /* APPLE_VK_ANSI_O (0x1F) */ + VK_KEY_U, /* APPLE_VK_ANSI_U (0x20) */ + VK_OEM_4, /* APPLE_VK_ANSI_LeftBracket (0x21) */ + VK_KEY_I, /* APPLE_VK_ANSI_I (0x22) */ + VK_KEY_P, /* APPLE_VK_ANSI_P (0x23) */ + VK_RETURN, /* APPLE_VK_Return (0x24) */ + VK_KEY_L, /* APPLE_VK_ANSI_L (0x25) */ + VK_KEY_J, /* APPLE_VK_ANSI_J (0x26) */ + VK_OEM_7, /* APPLE_VK_ANSI_Quote (0x27) */ + VK_KEY_K, /* APPLE_VK_ANSI_K (0x28) */ + VK_OEM_1, /* APPLE_VK_ANSI_Semicolon (0x29) */ + VK_OEM_5, /* APPLE_VK_ANSI_Backslash (0x2A) */ + VK_OEM_COMMA, /* APPLE_VK_ANSI_Comma (0x2B) */ + VK_OEM_2, /* APPLE_VK_ANSI_Slash (0x2C) */ + VK_KEY_N, /* APPLE_VK_ANSI_N (0x2D) */ + VK_KEY_M, /* APPLE_VK_ANSI_M (0x2E) */ + VK_OEM_PERIOD, /* APPLE_VK_ANSI_Period (0x2F) */ + VK_TAB, /* APPLE_VK_Tab (0x30) */ + VK_SPACE, /* APPLE_VK_Space (0x31) */ + VK_OEM_3, /* APPLE_VK_ANSI_Grave (0x32) */ + VK_BACK, /* APPLE_VK_Delete (0x33) */ + 0, /* APPLE_VK_0x34 (0x34) */ + VK_ESCAPE, /* APPLE_VK_Escape (0x35) */ + VK_RWIN | KBDEXT, /* APPLE_VK_RightCommand (0x36) */ + VK_LWIN | KBDEXT, /* APPLE_VK_Command (0x37) */ + VK_LSHIFT, /* APPLE_VK_Shift (0x38) */ + VK_CAPITAL, /* APPLE_VK_CapsLock (0x39) */ + VK_LMENU, /* APPLE_VK_Option (0x3A) */ + VK_LCONTROL, /* APPLE_VK_Control (0x3B) */ + VK_RSHIFT, /* APPLE_VK_RightShift (0x3C) */ + VK_RMENU | KBDEXT, /* APPLE_VK_RightOption (0x3D) */ + VK_RWIN | KBDEXT, /* APPLE_VK_RightControl (0x3E) */ + VK_RWIN | KBDEXT, /* APPLE_VK_Function (0x3F) */ + VK_F17, /* APPLE_VK_F17 (0x40) */ + VK_DECIMAL, /* APPLE_VK_ANSI_KeypadDecimal (0x41) */ + 0, /* APPLE_VK_0x42 (0x42) */ + VK_MULTIPLY, /* APPLE_VK_ANSI_KeypadMultiply (0x43) */ + 0, /* APPLE_VK_0x44 (0x44) */ + VK_ADD, /* APPLE_VK_ANSI_KeypadPlus (0x45) */ + 0, /* APPLE_VK_0x46 (0x46) */ + VK_NUMLOCK, /* APPLE_VK_ANSI_KeypadClear (0x47) */ + VK_VOLUME_UP, /* APPLE_VK_VolumeUp (0x48) */ + VK_VOLUME_DOWN, /* APPLE_VK_VolumeDown (0x49) */ + VK_VOLUME_MUTE, /* APPLE_VK_Mute (0x4A) */ + VK_DIVIDE | KBDEXT, /* APPLE_VK_ANSI_KeypadDivide (0x4B) */ + VK_RETURN | KBDEXT, /* APPLE_VK_ANSI_KeypadEnter (0x4C) */ + 0, /* APPLE_VK_0x4D (0x4D) */ + VK_SUBTRACT, /* APPLE_VK_ANSI_KeypadMinus (0x4E) */ + VK_F18, /* APPLE_VK_F18 (0x4F) */ + VK_F19, /* APPLE_VK_F19 (0x50) */ + VK_CLEAR | KBDEXT, /* APPLE_VK_ANSI_KeypadEquals (0x51) */ + VK_NUMPAD0, /* APPLE_VK_ANSI_Keypad0 (0x52) */ + VK_NUMPAD1, /* APPLE_VK_ANSI_Keypad1 (0x53) */ + VK_NUMPAD2, /* APPLE_VK_ANSI_Keypad2 (0x54) */ + VK_NUMPAD3, /* APPLE_VK_ANSI_Keypad3 (0x55) */ + VK_NUMPAD4, /* APPLE_VK_ANSI_Keypad4 (0x56) */ + VK_NUMPAD5, /* APPLE_VK_ANSI_Keypad5 (0x57) */ + VK_NUMPAD6, /* APPLE_VK_ANSI_Keypad6 (0x58) */ + VK_NUMPAD7, /* APPLE_VK_ANSI_Keypad7 (0x59) */ + VK_F20, /* APPLE_VK_F20 (0x5A) */ + VK_NUMPAD8, /* APPLE_VK_ANSI_Keypad8 (0x5B) */ + VK_NUMPAD9, /* APPLE_VK_ANSI_Keypad9 (0x5C) */ + 0, /* APPLE_VK_JIS_Yen (0x5D) */ + 0, /* APPLE_VK_JIS_Underscore (0x5E) */ + VK_DECIMAL, /* APPLE_VK_JIS_KeypadComma (0x5F) */ + VK_F5, /* APPLE_VK_F5 (0x60) */ + VK_F6, /* APPLE_VK_F6 (0x61) */ + VK_F7, /* APPLE_VK_F7 (0x62) */ + VK_F3, /* APPLE_VK_F3 (0x63) */ + VK_F8, /* APPLE_VK_F8 (0x64) */ + VK_F9, /* APPLE_VK_F9 (0x65) */ + 0, /* APPLE_VK_JIS_Eisu (0x66) */ + VK_F11, /* APPLE_VK_F11 (0x67) */ + 0, /* APPLE_VK_JIS_Kana (0x68) */ + VK_SNAPSHOT | KBDEXT, /* APPLE_VK_F13 (0x69) */ + VK_F16, /* APPLE_VK_F16 (0x6A) */ + VK_F14, /* APPLE_VK_F14 (0x6B) */ + 0, /* APPLE_VK_0x6C (0x6C) */ + VK_F10, /* APPLE_VK_F10 (0x6D) */ + 0, /* APPLE_VK_0x6E (0x6E) */ + VK_F12, /* APPLE_VK_F12 (0x6F) */ + 0, /* APPLE_VK_0x70 (0x70) */ + VK_PAUSE | KBDEXT, /* APPLE_VK_F15 (0x71) */ + VK_INSERT | KBDEXT, /* APPLE_VK_Help (0x72) */ + VK_HOME | KBDEXT, /* APPLE_VK_Home (0x73) */ + VK_PRIOR | KBDEXT, /* APPLE_VK_PageUp (0x74) */ + VK_DELETE | KBDEXT, /* APPLE_VK_ForwardDelete (0x75) */ + VK_F4, /* APPLE_VK_F4 (0x76) */ + VK_END | KBDEXT, /* APPLE_VK_End (0x77) */ + VK_F2, /* APPLE_VK_F2 (0x78) */ + VK_NEXT | KBDEXT, /* APPLE_VK_PageDown (0x79) */ + VK_F1, /* APPLE_VK_F1 (0x7A) */ + VK_LEFT | KBDEXT, /* APPLE_VK_LeftArrow (0x7B) */ + VK_RIGHT | KBDEXT, /* APPLE_VK_RightArrow (0x7C) */ + VK_DOWN | KBDEXT, /* APPLE_VK_DownArrow (0x7D) */ + VK_UP | KBDEXT, /* APPLE_VK_UpArrow (0x7E) */ + 0, /* 127 */ + 0, /* 128 */ + 0, /* 129 */ + 0, /* 130 */ + 0, /* 131 */ + 0, /* 132 */ + 0, /* 133 */ + 0, /* 134 */ + 0, /* 135 */ + 0, /* 136 */ + 0, /* 137 */ + 0, /* 138 */ + 0, /* 139 */ + 0, /* 140 */ + 0, /* 141 */ + 0, /* 142 */ + 0, /* 143 */ + 0, /* 144 */ + 0, /* 145 */ + 0, /* 146 */ + 0, /* 147 */ + 0, /* 148 */ + 0, /* 149 */ + 0, /* 150 */ + 0, /* 151 */ + 0, /* 152 */ + 0, /* 153 */ + 0, /* 154 */ + 0, /* 155 */ + 0, /* 156 */ + 0, /* 157 */ + 0, /* 158 */ + 0, /* 159 */ + 0, /* 160 */ + 0, /* 161 */ + 0, /* 162 */ + 0, /* 163 */ + 0, /* 164 */ + 0, /* 165 */ + 0, /* 166 */ + 0, /* 167 */ + 0, /* 168 */ + 0, /* 169 */ + 0, /* 170 */ + 0, /* 171 */ + 0, /* 172 */ + 0, /* 173 */ + 0, /* 174 */ + 0, /* 175 */ + 0, /* 176 */ + 0, /* 177 */ + 0, /* 178 */ + 0, /* 179 */ + 0, /* 180 */ + 0, /* 181 */ + 0, /* 182 */ + 0, /* 183 */ + 0, /* 184 */ + 0, /* 185 */ + 0, /* 186 */ + 0, /* 187 */ + 0, /* 188 */ + 0, /* 189 */ + 0, /* 190 */ + 0, /* 191 */ + 0, /* 192 */ + 0, /* 193 */ + 0, /* 194 */ + 0, /* 195 */ + 0, /* 196 */ + 0, /* 197 */ + 0, /* 198 */ + 0, /* 199 */ + 0, /* 200 */ + 0, /* 201 */ + 0, /* 202 */ + 0, /* 203 */ + 0, /* 204 */ + 0, /* 205 */ + 0, /* 206 */ + 0, /* 207 */ + 0, /* 208 */ + 0, /* 209 */ + 0, /* 210 */ + 0, /* 211 */ + 0, /* 212 */ + 0, /* 213 */ + 0, /* 214 */ + 0, /* 215 */ + 0, /* 216 */ + 0, /* 217 */ + 0, /* 218 */ + 0, /* 219 */ + 0, /* 220 */ + 0, /* 221 */ + 0, /* 222 */ + 0, /* 223 */ + 0, /* 224 */ + 0, /* 225 */ + 0, /* 226 */ + 0, /* 227 */ + 0, /* 228 */ + 0, /* 229 */ + 0, /* 230 */ + 0, /* 231 */ + 0, /* 232 */ + 0, /* 233 */ + 0, /* 234 */ + 0, /* 235 */ + 0, /* 236 */ + 0, /* 237 */ + 0, /* 238 */ + 0, /* 239 */ + 0, /* 240 */ + 0, /* 241 */ + 0, /* 242 */ + 0, /* 243 */ + 0, /* 244 */ + 0, /* 245 */ + 0, /* 246 */ + 0, /* 247 */ + 0, /* 248 */ + 0, /* 249 */ + 0, /* 250 */ + 0, /* 251 */ + 0, /* 252 */ + 0, /* 253 */ + 0, /* 254 */ + 0 /* 255 */ +}; + +/** + * evdev (Linux) + * + * Refer to linux/input-event-codes.h + */ + +static DWORD KEYCODE_TO_VKCODE_EVDEV[256] = { + 0, /* KEY_RESERVED (0) */ + VK_ESCAPE, /* KEY_ESC (1) */ + VK_KEY_1, /* KEY_1 (2) */ + VK_KEY_2, /* KEY_2 (3) */ + VK_KEY_3, /* KEY_3 (4) */ + VK_KEY_4, /* KEY_4 (5) */ + VK_KEY_5, /* KEY_5 (6) */ + VK_KEY_6, /* KEY_6 (7) */ + VK_KEY_7, /* KEY_7 (8) */ + VK_KEY_8, /* KEY_8 (9) */ + VK_KEY_9, /* KEY_9 (10) */ + VK_KEY_0, /* KEY_0 (11) */ + VK_OEM_MINUS, /* KEY_MINUS (12) */ + VK_OEM_PLUS, /* KEY_EQUAL (13) */ + VK_BACK, /* KEY_BACKSPACE (14) */ + VK_TAB, /* KEY_TAB (15) */ + VK_KEY_Q, /* KEY_Q (16) */ + VK_KEY_W, /* KEY_W (17) */ + VK_KEY_E, /* KEY_E (18) */ + VK_KEY_R, /* KEY_R (19) */ + VK_KEY_T, /* KEY_T (20) */ + VK_KEY_Y, /* KEY_Y (21) */ + VK_KEY_U, /* KEY_U (22) */ + VK_KEY_I, /* KEY_I (23) */ + VK_KEY_O, /* KEY_O (24) */ + VK_KEY_P, /* KEY_P (25) */ + VK_OEM_4, /* KEY_LEFTBRACE (26) */ + VK_OEM_6, /* KEY_RIGHTBRACE (27) */ + VK_RETURN, /* KEY_ENTER (28) */ + VK_LCONTROL, /* KEY_LEFTCTRL (29) */ + VK_KEY_A, /* KEY_A (30) */ + VK_KEY_S, /* KEY_S (31) */ + VK_KEY_D, /* KEY_D (32) */ + VK_KEY_F, /* KEY_F (33) */ + VK_KEY_G, /* KEY_G (34) */ + VK_KEY_H, /* KEY_H (35) */ + VK_KEY_J, /* KEY_J (36) */ + VK_KEY_K, /* KEY_K (37) */ + VK_KEY_L, /* KEY_L (38) */ + VK_OEM_1, /* KEY_SEMICOLON (39) */ + VK_OEM_7, /* KEY_APOSTROPHE (40) */ + VK_OEM_3, /* KEY_GRAVE (41) */ + VK_LSHIFT, /* KEY_LEFTSHIFT (42) */ + VK_OEM_5, /* KEY_BACKSLASH (43) */ + VK_KEY_Z, /* KEY_Z (44) */ + VK_KEY_X, /* KEY_X (45) */ + VK_KEY_C, /* KEY_C (46) */ + VK_KEY_V, /* KEY_V (47) */ + VK_KEY_B, /* KEY_B (48) */ + VK_KEY_N, /* KEY_N (49) */ + VK_KEY_M, /* KEY_M (50) */ + VK_OEM_COMMA, /* KEY_COMMA (51) */ + VK_OEM_PERIOD, /* KEY_DOT (52) */ + VK_OEM_2, /* KEY_SLASH (53) */ + VK_RSHIFT, /* KEY_RIGHTSHIFT (54) */ + VK_MULTIPLY, /* KEY_KPASTERISK (55) */ + VK_LMENU, /* KEY_LEFTALT (56) */ + VK_SPACE, /* KEY_SPACE (57) */ + VK_CAPITAL, /* KEY_CAPSLOCK (58) */ + VK_F1, /* KEY_F1 (59) */ + VK_F2, /* KEY_F2 (60) */ + VK_F3, /* KEY_F3 (61) */ + VK_F4, /* KEY_F4 (62) */ + VK_F5, /* KEY_F5 (63) */ + VK_F6, /* KEY_F6 (64) */ + VK_F7, /* KEY_F7 (65) */ + VK_F8, /* KEY_F8 (66) */ + VK_F9, /* KEY_F9 (67) */ + VK_F10, /* KEY_F10 (68) */ + VK_NUMLOCK, /* KEY_NUMLOCK (69) */ + VK_SCROLL, /* KEY_SCROLLLOCK (70) */ + VK_NUMPAD7, /* KEY_KP7 (71) */ + VK_NUMPAD8, /* KEY_KP8 (72) */ + VK_NUMPAD9, /* KEY_KP9 (73) */ + VK_SUBTRACT, /* KEY_KPMINUS (74) */ + VK_NUMPAD4, /* KEY_KP4 (75) */ + VK_NUMPAD5, /* KEY_KP5 (76) */ + VK_NUMPAD6, /* KEY_KP6 (77) */ + VK_ADD, /* KEY_KPPLUS (78) */ + VK_NUMPAD1, /* KEY_KP1 (79) */ + VK_NUMPAD2, /* KEY_KP2 (80) */ + VK_NUMPAD3, /* KEY_KP3 (81) */ + VK_NUMPAD0, /* KEY_KP0 (82) */ + VK_DECIMAL, /* KEY_KPDOT (83) */ + 0, /* (84) */ + 0, /* KEY_ZENKAKUHANKAKU (85) */ + VK_OEM_102, /* KEY_102ND (86) */ + VK_F11, /* KEY_F11 (87) */ + VK_F12, /* KEY_F12 (88) */ + VK_ABNT_C1, /* KEY_RO (89) */ + VK_DBE_KATAKANA, /* KEY_KATAKANA (90) */ + VK_DBE_HIRAGANA, /* KEY_HIRAGANA (91) */ + VK_CONVERT, /* KEY_HENKAN (92) */ + VK_HKTG, /* KEY_KATAKANAHIRAGANA (93) */ + VK_NONCONVERT, /* KEY_MUHENKAN (94) */ + 0, /* KEY_KPJPCOMMA (95) */ + VK_RETURN | KBDEXT, /* KEY_KPENTER (96) */ + VK_RCONTROL | KBDEXT, /* KEY_RIGHTCTRL (97) */ + VK_DIVIDE | KBDEXT, /* KEY_KPSLASH (98) */ + VK_SNAPSHOT | KBDEXT, /* KEY_SYSRQ (99) */ + VK_RMENU | KBDEXT, /* KEY_RIGHTALT (100) */ + 0, /* KEY_LINEFEED (101) */ + VK_HOME | KBDEXT, /* KEY_HOME (102) */ + VK_UP | KBDEXT, /* KEY_UP (103) */ + VK_PRIOR | KBDEXT, /* KEY_PAGEUP (104) */ + VK_LEFT | KBDEXT, /* KEY_LEFT (105) */ + VK_RIGHT | KBDEXT, /* KEY_RIGHT (106) */ + VK_END | KBDEXT, /* KEY_END (107) */ + VK_DOWN | KBDEXT, /* KEY_DOWN (108) */ + VK_NEXT | KBDEXT, /* KEY_PAGEDOWN (109) */ + VK_INSERT | KBDEXT, /* KEY_INSERT (110) */ + VK_DELETE | KBDEXT, /* KEY_DELETE (111) */ + 0, /* KEY_MACRO (112) */ + VK_VOLUME_MUTE | KBDEXT, /* KEY_MUTE (113) */ + VK_VOLUME_DOWN | KBDEXT, /* KEY_VOLUMEDOWN (114) */ + VK_VOLUME_UP | KBDEXT, /* KEY_VOLUMEUP (115) */ + 0, /* KEY_POWER (SC System Power Down) (116) */ + 0, /* KEY_KPEQUAL (117) */ + 0, /* KEY_KPPLUSMINUS (118) */ + VK_PAUSE | KBDEXT, /* KEY_PAUSE (119) */ + 0, /* KEY_SCALE (AL Compiz Scale (Expose)) (120) */ + VK_ABNT_C2, /* KEY_KPCOMMA (121) */ + VK_HANGUL, /* KEY_HANGEUL, KEY_HANGUEL (122) */ + VK_HANJA, /* KEY_HANJA (123) */ + VK_OEM_8, /* KEY_YEN (124) */ + VK_LWIN | KBDEXT, /* KEY_LEFTMETA (125) */ + VK_RWIN | KBDEXT, /* KEY_RIGHTMETA (126) */ + 0, /* KEY_COMPOSE (127) */ + 0, /* KEY_STOP (AC Stop) (128) */ + 0, /* KEY_AGAIN (AC Properties) (129) */ + 0, /* KEY_PROPS (AC Undo) (130) */ + 0, /* KEY_UNDO (131) */ + 0, /* KEY_FRONT (132) */ + 0, /* KEY_COPY (AC Copy) (133) */ + 0, /* KEY_OPEN (AC Open) (134) */ + 0, /* KEY_PASTE (AC Paste) (135) */ + 0, /* KEY_FIND (AC Search) (136) */ + 0, /* KEY_CUT (AC Cut) (137) */ + VK_HELP, /* KEY_HELP (AL Integrated Help Center) (138) */ + VK_APPS | KBDEXT, /* KEY_MENU (Menu (show menu)) (139) */ + 0, /* KEY_CALC (AL Calculator) (140) */ + 0, /* KEY_SETUP (141) */ + VK_SLEEP, /* KEY_SLEEP (SC System Sleep) (142) */ + 0, /* KEY_WAKEUP (System Wake Up) (143) */ + 0, /* KEY_FILE (AL Local Machine Browser) (144) */ + 0, /* KEY_SENDFILE (145) */ + 0, /* KEY_DELETEFILE (146) */ + VK_CONVERT, /* KEY_XFER (147) */ + VK_LAUNCH_APP1, /* KEY_PROG1 (148) */ + VK_LAUNCH_APP2, /* KEY_PROG2 (149) */ + 0, /* KEY_WWW (AL Internet Browser) (150) */ + 0, /* KEY_MSDOS (151) */ + 0, /* KEY_COFFEE, KEY_SCREENLOCK + * (AL Terminal Lock/Screensaver) (152) */ + 0, /* KEY_ROTATE_DISPLAY, KEY_DIRECTION + * (Display orientation for e.g. tablets) (153) */ + 0, /* KEY_CYCLEWINDOWS (154) */ + VK_LAUNCH_MAIL | KBDEXT, /* KEY_MAIL (155) */ + VK_BROWSER_FAVORITES | KBDEXT, /* KEY_BOOKMARKS (AC Bookmarks) (156) */ + 0, /* KEY_COMPUTER (157) */ + VK_BROWSER_BACK | KBDEXT, /* KEY_BACK (AC Back) (158) */ + VK_BROWSER_FORWARD | KBDEXT, /* KEY_FORWARD (AC Forward) (159) */ + 0, /* KEY_CLOSECD (160) */ + 0, /* KEY_EJECTCD (161) */ + 0, /* KEY_EJECTCLOSECD (162) */ + VK_MEDIA_NEXT_TRACK | KBDEXT, /* KEY_NEXTSONG (163) */ + VK_MEDIA_PLAY_PAUSE | KBDEXT, /* KEY_PLAYPAUSE (164) */ + VK_MEDIA_PREV_TRACK | KBDEXT, /* KEY_PREVIOUSSONG (165) */ + VK_MEDIA_STOP | KBDEXT, /* KEY_STOPCD (166) */ + 0, /* KEY_RECORD (167) */ + 0, /* KEY_REWIND (168) */ + 0, /* KEY_PHONE (Media Select Telephone) (169) */ + 0, /* KEY_ISO (170) */ + 0, /* KEY_CONFIG (AL Consumer Control Configuration) (171) */ + VK_BROWSER_HOME | KBDEXT, /* KEY_HOMEPAGE (AC Home) (172) */ + VK_BROWSER_REFRESH | KBDEXT, /* KEY_REFRESH (AC Refresh) (173) */ + 0, /* KEY_EXIT (AC Exit) (174) */ + 0, /* KEY_MOVE (175) */ + 0, /* KEY_EDIT (176) */ + 0, /* KEY_SCROLLUP (177) */ + 0, /* KEY_SCROLLDOWN (178) */ + 0, /* KEY_KPLEFTPAREN (179) */ + 0, /* KEY_KPRIGHTPAREN (180) */ + 0, /* KEY_NEW (AC New) (181) */ + 0, /* KEY_REDO (AC Redo/Repeat) (182) */ + VK_F13, /* KEY_F13 (183) */ + VK_F14, /* KEY_F14 (184) */ + VK_F15, /* KEY_F15 (185) */ + VK_F16, /* KEY_F16 (186) */ + VK_F17, /* KEY_F17 (187) */ + VK_F18, /* KEY_F18 (188) */ + VK_F19, /* KEY_F19 (189) */ + VK_F20, /* KEY_F20 (190) */ + VK_F21, /* KEY_F21 (191) */ + VK_F22, /* KEY_F22 (192) */ + VK_F23, /* KEY_F23 (193) */ + VK_F24, /* KEY_F24 (194) */ + 0, /* (195) */ + 0, /* (196) */ + 0, /* (197) */ + 0, /* (198) */ + 0, /* (199) */ + VK_PLAY, /* KEY_PLAYCD (200) */ + 0, /* KEY_PAUSECD (201) */ + 0, /* KEY_PROG3 (202) */ + 0, /* KEY_PROG4 (203) */ + 0, /* KEY_ALL_APPLICATIONS, KEY_DASHBOARD + * (AC Desktop Show All Applications) (204) */ + 0, /* KEY_SUSPEND (205) */ + 0, /* KEY_CLOSE (AC Close) (206) */ + VK_PLAY, /* KEY_PLAY (207) */ + 0, /* KEY_FASTFORWARD (208) */ + 0, /* KEY_BASSBOOST (209) */ + VK_PRINT | KBDEXT, /* KEY_PRINT (AC Print) (210) */ + 0, /* KEY_HP (211) */ + 0, /* KEY_CAMERA (212) */ + 0, /* KEY_SOUND (213) */ + 0, /* KEY_QUESTION (214) */ + 0, /* KEY_EMAIL (215) */ + 0, /* KEY_CHAT (216) */ + VK_BROWSER_SEARCH | KBDEXT, /* KEY_SEARCH (217) */ + 0, /* KEY_CONNECT (218) */ + 0, /* KEY_FINANCE (AL Checkbook/Finance) (219) */ + 0, /* KEY_SPORT (220) */ + 0, /* KEY_SHOP (221) */ + 0, /* KEY_ALTERASE (222) */ + 0, /* KEY_CANCEL (AC Cancel) (223) */ + 0, /* KEY_BRIGHTNESSDOWN (224) */ + 0, /* KEY_BRIGHTNESSUP (225) */ + 0, /* KEY_MEDIA (226) */ + 0, /* KEY_SWITCHVIDEOMODE + * (Cycle between available video outputs + * (Monitor/LCD/TV-out/etc)) (227) */ + 0, /* KEY_KBDILLUMTOGGLE (228) */ + 0, /* KEY_KBDILLUMDOWN (229) */ + 0, /* KEY_KBDILLUMUP (230) */ + 0, /* KEY_SEND (AC Send) (231) */ + 0, /* KEY_REPLY (AC Reply) (232) */ + 0, /* KEY_FORWARDMAIL (AC Forward Msg) (233) */ + 0, /* KEY_SAVE (AC Save) (234) */ + 0, /* KEY_DOCUMENTS (235) */ + 0, /* KEY_BATTERY (236) */ + 0, /* KEY_BLUETOOTH (237) */ + 0, /* KEY_WLAN (238) */ + 0, /* KEY_UWB (239) */ + 0, /* KEY_UNKNOWN (240) */ + 0, /* KEY_VIDEO_NEXT (drive next video source) (241) */ + 0, /* KEY_VIDEO_PREV (drive previous video source) (242) */ + 0, /* KEY_BRIGHTNESS_CYCLE + * (brightness up, after max is min) (243) */ + 0, /* KEY_BRIGHTNESS_AUTO, KEY_BRIGHTNESS_ZERO + * (Set Auto Brightness: manual brightness control is off, + * rely on ambient) (244) */ + 0, /* KEY_DISPLAY_OFF (display device to off state) (245) */ + 0, /* KEY_WWAN, KEY_WIMAX + * (Wireless WAN (LTE, UMTS, GSM, etc.)) (246) */ + 0, /* KEY_RFKILL (Key that controls all radios) (247) */ + 0, /* KEY_MICMUTE (Mute / unmute the microphone) (248) */ + 0, /* (249) */ + 0, /* (250) */ + 0, /* (251) */ + 0, /* (252) */ + 0, /* (253) */ + 0, /* (254) */ + 0, /* (255) */ +}; + +/** + * XKB + * + * Refer to X Keyboard Configuration Database: + * http://www.freedesktop.org/wiki/Software/XKeyboardConfig + */ + +/* TODO: Finish Japanese Keyboard */ + +static DWORD KEYCODE_TO_VKCODE_XKB[256] = { + 0, /* 0 */ + 0, /* 1 */ + 0, /* 2 */ + 0, /* 3 */ + 0, /* 4 */ + 0, /* 5 */ + 0, /* 6 */ + 0, /* 7 */ + 0, /* 8 */ + VK_ESCAPE, /* 9 */ + VK_KEY_1, /* 10 */ + VK_KEY_2, /* 11 */ + VK_KEY_3, /* 12 */ + VK_KEY_4, /* 13 */ + VK_KEY_5, /* 14 */ + VK_KEY_6, /* 15 */ + VK_KEY_7, /* 16 */ + VK_KEY_8, /* 17 */ + VK_KEY_9, /* 18 */ + VK_KEY_0, /* 19 */ + VK_OEM_MINUS, /* 20 */ + VK_OEM_PLUS, /* 21 */ + VK_BACK, /* 22 */ + VK_TAB, /* 23 */ + VK_KEY_Q, /* 24 */ + VK_KEY_W, /* 25 */ + VK_KEY_E, /* 26 */ + VK_KEY_R, /* 27 */ + VK_KEY_T, /* 28 */ + VK_KEY_Y, /* 29 */ + VK_KEY_U, /* 30 */ + VK_KEY_I, /* 31 */ + VK_KEY_O, /* 32 */ + VK_KEY_P, /* 33 */ + VK_OEM_4, /* 34 */ + VK_OEM_6, /* 35 */ + VK_RETURN, /* 36 */ + VK_LCONTROL, /* 37 */ + VK_KEY_A, /* 38 */ + VK_KEY_S, /* 39 */ + VK_KEY_D, /* 40 */ + VK_KEY_F, /* 41 */ + VK_KEY_G, /* 42 */ + VK_KEY_H, /* 43 */ + VK_KEY_J, /* 44 */ + VK_KEY_K, /* 45 */ + VK_KEY_L, /* 46 */ + VK_OEM_1, /* 47 */ + VK_OEM_7, /* 48 */ + VK_OEM_3, /* 49 */ + VK_LSHIFT, /* 50 */ + VK_OEM_5, /* 51 */ + VK_KEY_Z, /* 52 */ + VK_KEY_X, /* 53 */ + VK_KEY_C, /* 54 */ + VK_KEY_V, /* 55 */ + VK_KEY_B, /* 56 */ + VK_KEY_N, /* 57 */ + VK_KEY_M, /* 58 */ + VK_OEM_COMMA, /* 59 */ + VK_OEM_PERIOD, /* 60 */ + VK_OEM_2, /* 61 */ + VK_RSHIFT, /* 62 */ + VK_MULTIPLY, /* 63 */ + VK_LMENU, /* 64 */ + VK_SPACE, /* 65 */ + VK_CAPITAL, /* 66 */ + VK_F1, /* 67 */ + VK_F2, /* 68 */ + VK_F3, /* 69 */ + VK_F4, /* 70 */ + VK_F5, /* 71 */ + VK_F6, /* 72 */ + VK_F7, /* 73 */ + VK_F8, /* 74 */ + VK_F9, /* 75 */ + VK_F10, /* 76 */ + VK_NUMLOCK, /* 77 */ + VK_SCROLL, /* 78 */ + VK_NUMPAD7, /* 79 */ + VK_NUMPAD8, /* 80 */ + VK_NUMPAD9, /* 81 */ + VK_SUBTRACT, /* 82 */ + VK_NUMPAD4, /* 83 */ + VK_NUMPAD5, /* 84 */ + VK_NUMPAD6, /* 85 */ + VK_ADD, /* 86 */ + VK_NUMPAD1, /* 87 */ + VK_NUMPAD2, /* 88 */ + VK_NUMPAD3, /* 89 */ + VK_NUMPAD0, /* 90 */ + VK_DECIMAL, /* 91 */ + 0, /* 92 */ + 0, /* 93 */ + VK_OEM_102, /* 94 */ + VK_F11, /* 95 */ + VK_F12, /* 96 */ +#ifdef __sun + VK_HOME | KBDEXT, /* 97 */ + VK_UP | KBDEXT, /* 98 */ + VK_PRIOR | KBDEXT, /* 99 */ + VK_LEFT | KBDEXT, /* 100 */ + VK_HKTG, /* 101 */ + VK_RIGHT | KBDEXT, /* 102 */ + VK_END | KBDEXT, /* 103 */ + VK_DOWN | KBDEXT, /* 104 */ + VK_NEXT | KBDEXT, /* 105 */ + VK_INSERT | KBDEXT, /* 106 */ + VK_DELETE | KBDEXT, /* 107 */ + VK_RETURN | KBDEXT, /* 108 */ +#else + VK_ABNT_C1, /* 97 */ + VK_DBE_KATAKANA, /* 98 */ + VK_DBE_HIRAGANA, /* 99 */ + VK_CONVERT, /* 100 */ + VK_HKTG, /* 101 */ + VK_NONCONVERT, /* 102 */ + 0, /* 103 */ + VK_RETURN | KBDEXT, /* 104 */ + VK_RCONTROL | KBDEXT, /* 105 */ + VK_DIVIDE | KBDEXT, /* 106 */ + VK_SNAPSHOT | KBDEXT, /* 107 */ + VK_RMENU | KBDEXT, /* 108 */ +#endif + 0, /* KEY_LINEFEED 109 */ + VK_HOME | KBDEXT, /* 110 */ + VK_UP | KBDEXT, /* 111 */ + VK_PRIOR | KBDEXT, /* 112 */ + VK_LEFT | KBDEXT, /* 113 */ + VK_RIGHT | KBDEXT, /* 114 */ + VK_END | KBDEXT, /* 115 */ + VK_DOWN | KBDEXT, /* 116 */ + VK_NEXT | KBDEXT, /* 117 */ + VK_INSERT | KBDEXT, /* 118 */ + VK_DELETE | KBDEXT, /* 119 */ + 0, /* KEY_MACRO 120 */ + VK_VOLUME_MUTE | KBDEXT, /* 121 */ + VK_VOLUME_DOWN | KBDEXT, /* 122 */ + VK_VOLUME_UP | KBDEXT, /* 123 */ + 0, /* 124 */ + 0, /* 125 */ + 0, /* KEY_KPPLUSMINUS 126 */ + VK_PAUSE | KBDEXT, /* 127 */ + 0, /* KEY_SCALE 128 */ + VK_ABNT_C2, /* KEY_KPCOMMA 129 */ + VK_HANGUL, /* 130 */ + VK_HANJA, /* 131 */ + VK_OEM_8, /* 132 */ + VK_LWIN | KBDEXT, /* 133 */ + VK_RWIN | KBDEXT, /* 134 */ + VK_APPS | KBDEXT, /* 135 */ + 0, /* 136 */ + 0, /* 137 */ + 0, /* 138 */ + 0, /* 139 */ + 0, /* 140 */ + 0, /* 141 */ + 0, /* 142 */ + 0, /* 143 */ + 0, /* 144 */ + 0, /* 145 */ + VK_HELP, /* 146 */ + VK_APPS | KBDEXT, /* KEY_MENU 147 */ + 0, /* KEY_CALC 148 */ + 0, /* KEY_SETUP 149 */ + VK_SLEEP, /* KEY_SLEEP 150 */ + 0, /* KEY_WAKEUP 151 */ + 0, /* KEY_FILE 152 */ + 0, /* KEY_SEND 153 */ + 0, /* KEY_DELETEFILE 154 */ + VK_CONVERT, /* KEY_XFER 155 */ + VK_LAUNCH_APP1, /* KEY_PROG1 156 */ + VK_LAUNCH_APP2, /* KEY_PROG2 157 */ + 0, /* KEY_WWW 158 */ + 0, /* KEY_MSDOS 159 */ + 0, /* KEY_COFFEE 160 */ + 0, /* KEY_DIRECTION 161 */ + 0, /* KEY_CYCLEWINDOWS 162 */ + VK_LAUNCH_MAIL | KBDEXT, /* KEY_MAIL 163 */ + VK_BROWSER_FAVORITES | KBDEXT, /* KEY_BOOKMARKS 164 */ + 0, /* KEY_COMPUTER 165 */ + VK_BROWSER_BACK | KBDEXT, /* KEY_BACK 166 */ + VK_BROWSER_FORWARD | KBDEXT, /* KEY_FORWARD 167 */ + 0, /* KEY_CLOSECD 168 */ + 0, /* KEY_EJECTCD 169 */ + 0, /* KEY_EJECTCLOSECD 170 */ + VK_MEDIA_NEXT_TRACK | KBDEXT, /* KEY_NEXTSONG 171 */ + VK_MEDIA_PLAY_PAUSE | KBDEXT, /* KEY_PLAYPAUSE 172 */ + VK_MEDIA_PREV_TRACK | KBDEXT, /* KEY_PREVIOUSSONG 173 */ + VK_MEDIA_STOP | KBDEXT, /* KEY_STOPCD 174 */ + 0, /* KEY_RECORD 175 */ + 0, /* KEY_REWIND 176 */ + 0, /* KEY_PHONE 177 */ + 0, /* KEY_ISO 178 */ + 0, /* KEY_CONFIG 179 */ + VK_BROWSER_HOME | KBDEXT, /* KEY_HOMEPAGE 180 */ + VK_BROWSER_REFRESH | KBDEXT, /* KEY_REFRESH 181 */ + 0, /* KEY_EXIT 182 */ + 0, /* KEY_MOVE 183 */ + 0, /* KEY_EDIT 184 */ + 0, /* KEY_SCROLLUP 185 */ + 0, /* KEY_SCROLLDOWN 186 */ + 0, /* KEY_KPLEFTPAREN 187 */ + 0, /* KEY_KPRIGHTPAREN 188 */ + 0, /* KEY_NEW 189 */ + 0, /* KEY_REDO 190 */ + VK_F13, /* 191 */ + VK_F14, /* 192 */ + VK_F15, /* 193 */ + VK_F16, /* 194 */ + VK_F17, /* 195 */ + VK_F18, /* 196 */ + VK_F19, /* 197 */ + VK_F20, /* 198 */ + VK_F21, /* 199 */ + VK_F22, /* 200 */ + VK_F23, /* 201 */ + VK_F24, /* 202 */ + 0, /* 203 */ + 0, /* 204 */ + 0, /* 205 */ + VK_LWIN, /* 206 */ + 0, /* 207 */ + VK_PLAY, /* KEY_PLAYCD 208 */ + VK_PAUSE, /* KEY_PAUSECD 209 */ + 0, /* KEY_PROG3 210 */ + 0, /* KEY_PROG4 211 */ + 0, /* KEY_DASHBOARD 212 */ + 0, /* KEY_SUSPEND 213 */ + 0, /* KEY_CLOSE 214 */ + VK_PLAY, /* KEY_PLAY 215 */ + 0, /* KEY_FASTFORWARD 216 */ + 0, /* KEY_BASSBOOST 217 */ + VK_PRINT | KBDEXT, /* KEY_PRINT 218 */ + 0, /* KEY_HP 219 */ + 0, /* KEY_CAMERA 220 */ + 0, /* KEY_SOUND 221 */ + 0, /* KEY_QUESTION 222 */ + 0, /* KEY_EMAIL 223 */ + 0, /* KEY_CHAT 224 */ + VK_BROWSER_SEARCH | KBDEXT, /* KEY_SEARCH 225 */ + 0, /* KEY_CONNECT 226 */ + 0, /* KEY_FINANCE 227 */ + 0, /* KEY_SPORT 228 */ + 0, /* KEY_SHOP 229 */ + 0, /* KEY_ALTERASE 230 */ + 0, /* KEY_CANCEL 231 */ + 0, /* KEY_BRIGHTNESSDOWN 232 */ + 0, /* KEY_BRIGHTNESSUP 233 */ + 0, /* KEY_MEDIA 234 */ + 0, /* KEY_SWITCHVIDEOMODE 235 */ + 0, /* KEY_KBDILLUMTOGGLE 236 */ + 0, /* KEY_KBDILLUMDOWN 237 */ + 0, /* KEY_KBDILLUMUP 238 */ + 0, /* KEY_SEND 239 */ + 0, /* KEY_REPLY 240 */ + 0, /* KEY_FORWARDMAIL 241 */ + 0, /* KEY_SAVE 242 */ + 0, /* KEY_DOCUMENTS 243 */ + 0, /* KEY_BATTERY 244 */ + 0, /* KEY_BLUETOOTH 245 */ + 0, /* KEY_WLAN 246 */ + 0, /* KEY_UWB 247 */ + 0, /* KEY_UNKNOWN 248 */ + 0, /* KEY_VIDEO_NEXT 249 */ + 0, /* KEY_VIDEO_PREV 250 */ + 0, /* KEY_BRIGHTNESS_CYCLE 251 */ + 0, /* KEY_BRIGHTNESS_ZERO 252 */ + 0, /* KEY_DISPLAY_OFF 253 */ + 0, /* 254 */ + 0 /* 255 */ +}; + +DWORD GetVirtualKeyCodeFromKeycode(DWORD keycode, WINPR_KEYCODE_TYPE type) +{ + DWORD vkcode = 0; + + vkcode = VK_NONE; + + switch (type) + { + case WINPR_KEYCODE_TYPE_APPLE: + if (keycode < 0xFF) + vkcode = KEYCODE_TO_VKCODE_APPLE[keycode & 0xFF]; + break; + case WINPR_KEYCODE_TYPE_EVDEV: + if (keycode < 0xFF) + vkcode = KEYCODE_TO_VKCODE_EVDEV[keycode & 0xFF]; + break; + case WINPR_KEYCODE_TYPE_XKB: + if (keycode < 0xFF) + vkcode = KEYCODE_TO_VKCODE_XKB[keycode & 0xFF]; + break; + default: + break; + } + + if (!vkcode) + vkcode = VK_NONE; + + return vkcode; +} + +DWORD GetKeycodeFromVirtualKeyCode(DWORD vkcode, WINPR_KEYCODE_TYPE type) +{ + DWORD* targetArray = NULL; + size_t targetSize = 0; + + switch (type) + { + case WINPR_KEYCODE_TYPE_APPLE: + targetArray = KEYCODE_TO_VKCODE_APPLE; + targetSize = ARRAYSIZE(KEYCODE_TO_VKCODE_APPLE); + break; + case WINPR_KEYCODE_TYPE_EVDEV: + targetArray = KEYCODE_TO_VKCODE_EVDEV; + targetSize = ARRAYSIZE(KEYCODE_TO_VKCODE_EVDEV); + break; + case WINPR_KEYCODE_TYPE_XKB: + targetArray = KEYCODE_TO_VKCODE_XKB; + targetSize = ARRAYSIZE(KEYCODE_TO_VKCODE_XKB); + break; + default: + return 0; + } + + for (DWORD index = 0; index < targetSize; index++) + { + if (vkcode == targetArray[index]) + return index; + } + + return 0; +} diff --git a/winpr/libwinpr/input/scancode.c b/winpr/libwinpr/input/scancode.c new file mode 100644 index 0000000..74d5da5 --- /dev/null +++ b/winpr/libwinpr/input/scancode.c @@ -0,0 +1,190 @@ +/** + * WinPR: Windows Portable Runtime + * Keyboard Input + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +/** + * Virtual Scan Codes + */ + +/** + * Keyboard Type 4 + */ + +static DWORD KBD4T[128] = { + KBD4_T00, KBD4_T01, KBD4_T02, KBD4_T03, KBD4_T04, KBD4_T05, KBD4_T06, KBD4_T07, KBD4_T08, + KBD4_T09, KBD4_T0A, KBD4_T0B, KBD4_T0C, KBD4_T0D, KBD4_T0E, KBD4_T0F, KBD4_T10, KBD4_T11, + KBD4_T12, KBD4_T13, KBD4_T14, KBD4_T15, KBD4_T16, KBD4_T17, KBD4_T18, KBD4_T19, KBD4_T1A, + KBD4_T1B, KBD4_T1C, KBD4_T1D, KBD4_T1E, KBD4_T1F, KBD4_T20, KBD4_T21, KBD4_T22, KBD4_T23, + KBD4_T24, KBD4_T25, KBD4_T26, KBD4_T27, KBD4_T28, KBD4_T29, KBD4_T2A, KBD4_T2B, KBD4_T2C, + KBD4_T2D, KBD4_T2E, KBD4_T2F, KBD4_T30, KBD4_T31, KBD4_T32, KBD4_T33, KBD4_T34, KBD4_T35, + KBD4_T36, KBD4_T37, KBD4_T38, KBD4_T39, KBD4_T3A, KBD4_T3B, KBD4_T3C, KBD4_T3D, KBD4_T3E, + KBD4_T3F, KBD4_T40, KBD4_T41, KBD4_T42, KBD4_T43, KBD4_T44, KBD4_T45, KBD4_T46, KBD4_T47, + KBD4_T48, KBD4_T49, KBD4_T4A, KBD4_T4B, KBD4_T4C, KBD4_T4D, KBD4_T4E, KBD4_T4F, KBD4_T50, + KBD4_T51, KBD4_T52, KBD4_T53, KBD4_T54, KBD4_T55, KBD4_T56, KBD4_T57, KBD4_T58, KBD4_T59, + KBD4_T5A, KBD4_T5B, KBD4_T5C, KBD4_T5D, KBD4_T5E, KBD4_T5F, KBD4_T60, KBD4_T61, KBD4_T62, + KBD4_T63, KBD4_T64, KBD4_T65, KBD4_T66, KBD4_T67, KBD4_T68, KBD4_T69, KBD4_T6A, KBD4_T6B, + KBD4_T6C, KBD4_T6D, KBD4_T6E, KBD4_T6F, KBD4_T70, KBD4_T71, KBD4_T72, KBD4_T73, KBD4_T74, + KBD4_T75, KBD4_T76, KBD4_T77, KBD4_T78, KBD4_T79, KBD4_T7A, KBD4_T7B, KBD4_T7C, KBD4_T7D, + KBD4_T7E, KBD4_T7F +}; + +static DWORD KBD4X[128] = { + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, KBD4_X10, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, KBD4_X19, VK_NONE, + VK_NONE, KBD4_X1C, KBD4_X1D, VK_NONE, VK_NONE, KBD4_X20, KBD4_X21, KBD4_X22, VK_NONE, + KBD4_X24, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, KBD4_X2E, VK_NONE, KBD4_X30, VK_NONE, KBD4_X32, VK_NONE, VK_NONE, KBD4_X35, + VK_NONE, KBD4_X37, KBD4_X38, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, KBD4_X46, KBD4_X47, + KBD4_X48, KBD4_X49, VK_NONE, KBD4_X4B, VK_NONE, KBD4_X4D, VK_NONE, KBD4_X4F, KBD4_X50, + KBD4_X51, KBD4_X52, KBD4_X53, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, KBD4_X5B, KBD4_X5C, KBD4_X5D, KBD4_X5E, KBD4_X5F, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, KBD4_X65, KBD4_X66, KBD4_X67, KBD4_X68, KBD4_X69, KBD4_X6A, KBD4_X6B, + KBD4_X6C, KBD4_X6D, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, +}; + +/** + * Keyboard Type 7 + */ + +static DWORD KBD7T[128] = { + KBD7_T00, KBD7_T01, KBD7_T02, KBD7_T03, KBD7_T04, KBD7_T05, KBD7_T06, KBD7_T07, KBD7_T08, + KBD7_T09, KBD7_T0A, KBD7_T0B, KBD7_T0C, KBD7_T0D, KBD7_T0E, KBD7_T0F, KBD7_T10, KBD7_T11, + KBD7_T12, KBD7_T13, KBD7_T14, KBD7_T15, KBD7_T16, KBD7_T17, KBD7_T18, KBD7_T19, KBD7_T1A, + KBD7_T1B, KBD7_T1C, KBD7_T1D, KBD7_T1E, KBD7_T1F, KBD7_T20, KBD7_T21, KBD7_T22, KBD7_T23, + KBD7_T24, KBD7_T25, KBD7_T26, KBD7_T27, KBD7_T28, KBD7_T29, KBD7_T2A, KBD7_T2B, KBD7_T2C, + KBD7_T2D, KBD7_T2E, KBD7_T2F, KBD7_T30, KBD7_T31, KBD7_T32, KBD7_T33, KBD7_T34, KBD7_T35, + KBD7_T36, KBD7_T37, KBD7_T38, KBD7_T39, KBD7_T3A, KBD7_T3B, KBD7_T3C, KBD7_T3D, KBD7_T3E, + KBD7_T3F, KBD7_T40, KBD7_T41, KBD7_T42, KBD7_T43, KBD7_T44, KBD7_T45, KBD7_T46, KBD7_T47, + KBD7_T48, KBD7_T49, KBD7_T4A, KBD7_T4B, KBD7_T4C, KBD7_T4D, KBD7_T4E, KBD7_T4F, KBD7_T50, + KBD7_T51, KBD7_T52, KBD7_T53, KBD7_T54, KBD7_T55, KBD7_T56, KBD7_T57, KBD7_T58, KBD7_T59, + KBD7_T5A, KBD7_T5B, KBD7_T5C, KBD7_T5D, KBD7_T5E, KBD7_T5F, KBD7_T60, KBD7_T61, KBD7_T62, + KBD7_T63, KBD7_T64, KBD7_T65, KBD7_T66, KBD7_T67, KBD7_T68, KBD7_T69, KBD7_T6A, KBD7_T6B, + KBD7_T6C, KBD7_T6D, KBD7_T6E, KBD7_T6F, KBD7_T70, KBD7_T71, KBD7_T72, KBD7_T73, KBD7_T74, + KBD7_T75, KBD7_T76, KBD7_T77, KBD7_T78, KBD7_T79, KBD7_T7A, KBD7_T7B, KBD7_T7C, KBD7_T7D, + KBD7_T7E, KBD7_T7F +}; + +static DWORD KBD7X[128] = { + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, KBD7_X10, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, KBD7_X19, VK_NONE, + VK_NONE, KBD7_X1C, KBD7_X1D, VK_NONE, VK_NONE, KBD7_X20, KBD7_X21, KBD7_X22, VK_NONE, + KBD7_X24, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, KBD7_X2E, VK_NONE, KBD7_X30, VK_NONE, KBD7_X32, KBD7_X33, VK_NONE, KBD7_X35, + VK_NONE, KBD7_X37, KBD7_X38, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, KBD7_X42, KBD7_X43, KBD7_X44, VK_NONE, KBD7_X46, KBD7_X47, + KBD7_X48, KBD7_X49, VK_NONE, KBD7_X4B, VK_NONE, KBD7_X4D, VK_NONE, KBD7_X4F, KBD7_X50, + KBD7_X51, KBD7_X52, KBD7_X53, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, KBD7_X5B, KBD7_X5C, KBD7_X5D, KBD7_X5E, KBD7_X5F, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, KBD7_X65, KBD7_X66, KBD7_X67, KBD7_X68, KBD7_X69, KBD7_X6A, KBD7_X6B, + KBD7_X6C, KBD7_X6D, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, VK_NONE, + VK_NONE, VK_NONE +}; + +DWORD GetVirtualKeyCodeFromVirtualScanCode(DWORD scancode, DWORD dwKeyboardType) +{ + const DWORD codeIndex = scancode & 0xFF; + + if (codeIndex > 127) + return VK_NONE; + + if ((dwKeyboardType != WINPR_KBD_TYPE_IBM_ENHANCED) && + (dwKeyboardType != WINPR_KBD_TYPE_JAPANESE)) + dwKeyboardType = WINPR_KBD_TYPE_IBM_ENHANCED; + + if (dwKeyboardType == WINPR_KBD_TYPE_IBM_ENHANCED) + return (scancode & KBDEXT) ? KBD4X[codeIndex] : KBD4T[codeIndex]; + else if (dwKeyboardType == WINPR_KBD_TYPE_JAPANESE) + return (scancode & KBDEXT) ? KBD7X[codeIndex] : KBD7T[codeIndex]; + + return VK_NONE; +} + +DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, DWORD dwKeyboardType) +{ + DWORD scancode = 0; + DWORD codeIndex = vkcode & 0xFF; + + if ((dwKeyboardType != WINPR_KBD_TYPE_IBM_ENHANCED) && + (dwKeyboardType != WINPR_KBD_TYPE_JAPANESE)) + dwKeyboardType = WINPR_KBD_TYPE_IBM_ENHANCED; + + if (dwKeyboardType == WINPR_KBD_TYPE_IBM_ENHANCED) + { + if (vkcode & KBDEXT) + { + for (size_t i = 0; i < ARRAYSIZE(KBD4X); i++) + { + if (KBD4X[i] == codeIndex) + { + scancode = (i | KBDEXT); + break; + } + } + } + else + { + for (size_t i = 0; i < ARRAYSIZE(KBD4T); i++) + { + if (KBD4T[i] == codeIndex) + { + scancode = i; + break; + } + } + } + } + else if (dwKeyboardType == WINPR_KBD_TYPE_JAPANESE) + { + if (vkcode & KBDEXT) + { + for (size_t i = 0; i < ARRAYSIZE(KBD7X); i++) + { + if (KBD7X[i] == codeIndex) + { + scancode = (i | KBDEXT); + break; + } + } + } + else + { + for (size_t i = 0; i < ARRAYSIZE(KBD7T); i++) + { + if (KBD7T[i] == codeIndex) + { + scancode = i; + break; + } + } + } + } + + return scancode; +} diff --git a/winpr/libwinpr/input/virtualkey.c b/winpr/libwinpr/input/virtualkey.c new file mode 100644 index 0000000..66f9b09 --- /dev/null +++ b/winpr/libwinpr/input/virtualkey.c @@ -0,0 +1,459 @@ +/** + * WinPR: Windows Portable Runtime + * Keyboard Input + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +/** + * Virtual Key Codes + */ + +typedef struct +{ + DWORD code; /* Windows Virtual Key Code */ + const char* name; /* Virtual Key Code Name */ +} VIRTUAL_KEY_CODE; + +static const VIRTUAL_KEY_CODE VIRTUAL_KEY_CODE_TABLE[256] = { + { 0, NULL }, + { VK_LBUTTON, "VK_LBUTTON" }, + { VK_RBUTTON, "VK_RBUTTON" }, + { VK_CANCEL, "VK_CANCEL" }, + { VK_MBUTTON, "VK_MBUTTON" }, + { VK_XBUTTON1, "VK_XBUTTON1" }, + { VK_XBUTTON2, "VK_XBUTTON2" }, + { 0, NULL }, + { VK_BACK, "VK_BACK" }, + { VK_TAB, "VK_TAB" }, + { 0, NULL }, + { 0, NULL }, + { VK_CLEAR, "VK_CLEAR" }, + { VK_RETURN, "VK_RETURN" }, + { 0, NULL }, + { 0, NULL }, + { VK_SHIFT, "VK_SHIFT" }, + { VK_CONTROL, "VK_CONTROL" }, + { VK_MENU, "VK_MENU" }, + { VK_PAUSE, "VK_PAUSE" }, + { VK_CAPITAL, "VK_CAPITAL" }, + { VK_KANA, "VK_KANA" }, /* also VK_HANGUL */ + { 0, NULL }, + { VK_JUNJA, "VK_JUNJA" }, + { VK_FINAL, "VK_FINAL" }, + { VK_KANJI, "VK_KANJI" }, /* also VK_HANJA */ + { VK_HKTG, "VK_HKTG" }, + { VK_ESCAPE, "VK_ESCAPE" }, + { VK_CONVERT, "VK_CONVERT" }, + { VK_NONCONVERT, "VK_NONCONVERT" }, + { VK_ACCEPT, "VK_ACCEPT" }, + { VK_MODECHANGE, "VK_MODECHANGE" }, + { VK_SPACE, "VK_SPACE" }, + { VK_PRIOR, "VK_PRIOR" }, + { VK_NEXT, "VK_NEXT" }, + { VK_END, "VK_END" }, + { VK_HOME, "VK_HOME" }, + { VK_LEFT, "VK_LEFT" }, + { VK_UP, "VK_UP" }, + { VK_RIGHT, "VK_RIGHT" }, + { VK_DOWN, "VK_DOWN" }, + { VK_SELECT, "VK_SELECT" }, + { VK_PRINT, "VK_PRINT" }, + { VK_EXECUTE, "VK_EXECUTE" }, + { VK_SNAPSHOT, "VK_SNAPSHOT" }, + { VK_INSERT, "VK_INSERT" }, + { VK_DELETE, "VK_DELETE" }, + { VK_HELP, "VK_HELP" }, + { VK_KEY_0, "VK_KEY_0" }, + { VK_KEY_1, "VK_KEY_1" }, + { VK_KEY_2, "VK_KEY_2" }, + { VK_KEY_3, "VK_KEY_3" }, + { VK_KEY_4, "VK_KEY_4" }, + { VK_KEY_5, "VK_KEY_5" }, + { VK_KEY_6, "VK_KEY_6" }, + { VK_KEY_7, "VK_KEY_7" }, + { VK_KEY_8, "VK_KEY_8" }, + { VK_KEY_9, "VK_KEY_9" }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { VK_KEY_A, "VK_KEY_A" }, + { VK_KEY_B, "VK_KEY_B" }, + { VK_KEY_C, "VK_KEY_C" }, + { VK_KEY_D, "VK_KEY_D" }, + { VK_KEY_E, "VK_KEY_E" }, + { VK_KEY_F, "VK_KEY_F" }, + { VK_KEY_G, "VK_KEY_G" }, + { VK_KEY_H, "VK_KEY_H" }, + { VK_KEY_I, "VK_KEY_I" }, + { VK_KEY_J, "VK_KEY_J" }, + { VK_KEY_K, "VK_KEY_K" }, + { VK_KEY_L, "VK_KEY_L" }, + { VK_KEY_M, "VK_KEY_M" }, + { VK_KEY_N, "VK_KEY_N" }, + { VK_KEY_O, "VK_KEY_O" }, + { VK_KEY_P, "VK_KEY_P" }, + { VK_KEY_Q, "VK_KEY_Q" }, + { VK_KEY_R, "VK_KEY_R" }, + { VK_KEY_S, "VK_KEY_S" }, + { VK_KEY_T, "VK_KEY_T" }, + { VK_KEY_U, "VK_KEY_U" }, + { VK_KEY_V, "VK_KEY_V" }, + { VK_KEY_W, "VK_KEY_W" }, + { VK_KEY_X, "VK_KEY_X" }, + { VK_KEY_Y, "VK_KEY_Y" }, + { VK_KEY_Z, "VK_KEY_Z" }, + { VK_LWIN, "VK_LWIN" }, + { VK_RWIN, "VK_RWIN" }, + { VK_APPS, "VK_APPS" }, + { 0, NULL }, + { VK_SLEEP, "VK_SLEEP" }, + { VK_NUMPAD0, "VK_NUMPAD0" }, + { VK_NUMPAD1, "VK_NUMPAD1" }, + { VK_NUMPAD2, "VK_NUMPAD2" }, + { VK_NUMPAD3, "VK_NUMPAD3" }, + { VK_NUMPAD4, "VK_NUMPAD4" }, + { VK_NUMPAD5, "VK_NUMPAD5" }, + { VK_NUMPAD6, "VK_NUMPAD6" }, + { VK_NUMPAD7, "VK_NUMPAD7" }, + { VK_NUMPAD8, "VK_NUMPAD8" }, + { VK_NUMPAD9, "VK_NUMPAD9" }, + { VK_MULTIPLY, "VK_MULTIPLY" }, + { VK_ADD, "VK_ADD" }, + { VK_SEPARATOR, "VK_SEPARATOR" }, + { VK_SUBTRACT, "VK_SUBTRACT" }, + { VK_DECIMAL, "VK_DECIMAL" }, + { VK_DIVIDE, "VK_DIVIDE" }, + { VK_F1, "VK_F1" }, + { VK_F2, "VK_F2" }, + { VK_F3, "VK_F3" }, + { VK_F4, "VK_F4" }, + { VK_F5, "VK_F5" }, + { VK_F6, "VK_F6" }, + { VK_F7, "VK_F7" }, + { VK_F8, "VK_F8" }, + { VK_F9, "VK_F9" }, + { VK_F10, "VK_F10" }, + { VK_F11, "VK_F11" }, + { VK_F12, "VK_F12" }, + { VK_F13, "VK_F13" }, + { VK_F14, "VK_F14" }, + { VK_F15, "VK_F15" }, + { VK_F16, "VK_F16" }, + { VK_F17, "VK_F17" }, + { VK_F18, "VK_F18" }, + { VK_F19, "VK_F19" }, + { VK_F20, "VK_F20" }, + { VK_F21, "VK_F21" }, + { VK_F22, "VK_F22" }, + { VK_F23, "VK_F23" }, + { VK_F24, "VK_F24" }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { VK_NUMLOCK, "VK_NUMLOCK" }, + { VK_SCROLL, "VK_SCROLL" }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { VK_LSHIFT, "VK_LSHIFT" }, + { VK_RSHIFT, "VK_RSHIFT" }, + { VK_LCONTROL, "VK_LCONTROL" }, + { VK_RCONTROL, "VK_RCONTROL" }, + { VK_LMENU, "VK_LMENU" }, + { VK_RMENU, "VK_RMENU" }, + { VK_BROWSER_BACK, "VK_BROWSER_BACK" }, + { VK_BROWSER_FORWARD, "VK_BROWSER_FORWARD" }, + { VK_BROWSER_REFRESH, "VK_BROWSER_REFRESH" }, + { VK_BROWSER_STOP, "VK_BROWSER_STOP" }, + { VK_BROWSER_SEARCH, "VK_BROWSER_SEARCH" }, + { VK_BROWSER_FAVORITES, "VK_BROWSER_FAVORITES" }, + { VK_BROWSER_HOME, "VK_BROWSER_HOME" }, + { VK_VOLUME_MUTE, "VK_VOLUME_MUTE" }, + { VK_VOLUME_DOWN, "VK_VOLUME_DOWN" }, + { VK_VOLUME_UP, "VK_VOLUME_UP" }, + { VK_MEDIA_NEXT_TRACK, "VK_MEDIA_NEXT_TRACK" }, + { VK_MEDIA_PREV_TRACK, "VK_MEDIA_PREV_TRACK" }, + { VK_MEDIA_STOP, "VK_MEDIA_STOP" }, + { VK_MEDIA_PLAY_PAUSE, "VK_MEDIA_PLAY_PAUSE" }, + { VK_LAUNCH_MAIL, "VK_LAUNCH_MAIL" }, + { VK_MEDIA_SELECT, "VK_MEDIA_SELECT" }, + { VK_LAUNCH_APP1, "VK_LAUNCH_APP1" }, + { VK_LAUNCH_APP2, "VK_LAUNCH_APP2" }, + { 0, NULL }, + { 0, NULL }, + { VK_OEM_1, "VK_OEM_1" }, + { VK_OEM_PLUS, "VK_OEM_PLUS" }, + { VK_OEM_COMMA, "VK_OEM_COMMA" }, + { VK_OEM_MINUS, "VK_OEM_MINUS" }, + { VK_OEM_PERIOD, "VK_OEM_PERIOD" }, + { VK_OEM_2, "VK_OEM_2" }, + { VK_OEM_3, "VK_OEM_3" }, + { VK_ABNT_C1, "VK_ABNT_C1" }, + { VK_ABNT_C2, "VK_ABNT_C2" }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { VK_OEM_4, "VK_OEM_4" }, + { VK_OEM_5, "VK_OEM_5" }, + { VK_OEM_6, "VK_OEM_6" }, + { VK_OEM_7, "VK_OEM_7" }, + { VK_OEM_8, "VK_OEM_8" }, + { 0, NULL }, + { 0, NULL }, + { VK_OEM_102, "VK_OEM_102" }, + { 0, NULL }, + { 0, NULL }, + { VK_PROCESSKEY, "VK_PROCESSKEY" }, + { 0, NULL }, + { VK_PACKET, "VK_PACKET" }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { 0, NULL }, + { VK_ATTN, "VK_ATTN" }, + { VK_CRSEL, "VK_CRSEL" }, + { VK_EXSEL, "VK_EXSEL" }, + { VK_EREOF, "VK_EREOF" }, + { VK_PLAY, "VK_PLAY" }, + { VK_ZOOM, "VK_ZOOM" }, + { VK_NONAME, "VK_NONAME" }, + { VK_PA1, "VK_PA1" }, + { VK_OEM_CLEAR, "VK_OEM_CLEAR" }, + { 0, NULL } +}; + +typedef struct +{ + const char* name; + DWORD vkcode; +} XKB_KEYNAME; + +static XKB_KEYNAME XKB_KEYNAME_TABLE[] = { + { "BKSP", VK_BACK }, + { "TAB", VK_TAB }, + { "RTRN", VK_RETURN }, + { "LFSH", VK_LSHIFT }, + { "LALT", VK_LMENU }, + { "CAPS", VK_CAPITAL }, + { "ESC", VK_ESCAPE }, + { "SPCE", VK_SPACE }, + { "AE10", VK_KEY_0 }, + { "AE01", VK_KEY_1 }, + { "AE02", VK_KEY_2 }, + { "AE03", VK_KEY_3 }, + { "AE04", VK_KEY_4 }, + { "AE05", VK_KEY_5 }, + { "AE06", VK_KEY_6 }, + { "AE07", VK_KEY_7 }, + { "AE08", VK_KEY_8 }, + { "AE09", VK_KEY_9 }, + { "AC01", VK_KEY_A }, + { "AB05", VK_KEY_B }, + { "AB03", VK_KEY_C }, + { "AC03", VK_KEY_D }, + { "AD03", VK_KEY_E }, + { "AC04", VK_KEY_F }, + { "AC05", VK_KEY_G }, + { "AC06", VK_KEY_H }, + { "AD08", VK_KEY_I }, + { "AC07", VK_KEY_J }, + { "AC08", VK_KEY_K }, + { "AC09", VK_KEY_L }, + { "AB07", VK_KEY_M }, + { "AB06", VK_KEY_N }, + { "AD09", VK_KEY_O }, + { "AD10", VK_KEY_P }, + { "AD01", VK_KEY_Q }, + { "AD04", VK_KEY_R }, + { "AC02", VK_KEY_S }, + { "AD05", VK_KEY_T }, + { "AD07", VK_KEY_U }, + { "AB04", VK_KEY_V }, + { "AD02", VK_KEY_W }, + { "AB02", VK_KEY_X }, + { "AD06", VK_KEY_Y }, + { "AB01", VK_KEY_Z }, + { "KP0", VK_NUMPAD0 }, + { "KP1", VK_NUMPAD1 }, + { "KP2", VK_NUMPAD2 }, + { "KP3", VK_NUMPAD3 }, + { "KP4", VK_NUMPAD4 }, + { "KP5", VK_NUMPAD5 }, + { "KP6", VK_NUMPAD6 }, + { "KP7", VK_NUMPAD7 }, + { "KP8", VK_NUMPAD8 }, + { "KP9", VK_NUMPAD9 }, + { "KPMU", VK_MULTIPLY }, + { "KPAD", VK_ADD }, + { "KPSU", VK_SUBTRACT }, + { "KPDL", VK_DECIMAL }, + { "AB10", VK_OEM_2 }, + { "FK01", VK_F1 }, + { "FK02", VK_F2 }, + { "FK03", VK_F3 }, + { "FK04", VK_F4 }, + { "FK05", VK_F5 }, + { "FK06", VK_F6 }, + { "FK07", VK_F7 }, + { "FK08", VK_F8 }, + { "FK09", VK_F9 }, + { "FK10", VK_F10 }, + { "FK11", VK_F11 }, + { "FK12", VK_F12 }, + { "NMLK", VK_NUMLOCK }, + { "SCLK", VK_SCROLL }, + { "RTSH", VK_RSHIFT }, + { "LCTL", VK_LCONTROL }, + { "AC10", VK_OEM_1 }, + { "AE12", VK_OEM_PLUS }, + { "AB08", VK_OEM_COMMA }, + { "AE11", VK_OEM_MINUS }, + { "AB09", VK_OEM_PERIOD }, + { "TLDE", VK_OEM_3 }, + { "AB11", VK_ABNT_C1 }, + { "I129", VK_ABNT_C2 }, + { "AD11", VK_OEM_4 }, + { "BKSL", VK_OEM_5 }, + { "AD12", VK_OEM_6 }, + { "AC11", VK_OEM_7 }, + { "LSGT", VK_OEM_102 }, + { "KPEN", VK_RETURN | KBDEXT }, + { "PAUS", VK_PAUSE | KBDEXT }, + { "PGUP", VK_PRIOR | KBDEXT }, + { "PGDN", VK_NEXT | KBDEXT }, + { "END", VK_END | KBDEXT }, + { "HOME", VK_HOME | KBDEXT }, + { "LEFT", VK_LEFT | KBDEXT }, + { "UP", VK_UP | KBDEXT }, + { "RGHT", VK_RIGHT | KBDEXT }, + { "DOWN", VK_DOWN | KBDEXT }, + { "PRSC", VK_SNAPSHOT | KBDEXT }, + { "INS", VK_INSERT | KBDEXT }, + { "DELE", VK_DELETE | KBDEXT }, + { "LWIN", VK_LWIN | KBDEXT }, + { "RWIN", VK_RWIN | KBDEXT }, + { "COMP", VK_APPS | KBDEXT }, + { "KPDV", VK_DIVIDE | KBDEXT }, + { "RCTL", VK_RCONTROL | KBDEXT }, + { "RALT", VK_RMENU | KBDEXT }, + + /* Japanese */ + + { "HENK", VK_CONVERT }, + { "MUHE", VK_NONCONVERT }, + { "HKTG", VK_HKTG }, + + // { "AE13", VK_BACKSLASH_JP }, // JP + // { "LVL3", 0x54} +}; + +const char* GetVirtualKeyName(DWORD vkcode) +{ + const char* vkname = NULL; + + if (vkcode < ARRAYSIZE(VIRTUAL_KEY_CODE_TABLE)) + vkname = VIRTUAL_KEY_CODE_TABLE[vkcode].name; + + if (!vkname) + vkname = "VK_NONE"; + + return vkname; +} + +DWORD GetVirtualKeyCodeFromName(const char* vkname) +{ + for (size_t i = 0; i < ARRAYSIZE(VIRTUAL_KEY_CODE_TABLE); i++) + { + if (VIRTUAL_KEY_CODE_TABLE[i].name) + { + if (strcmp(vkname, VIRTUAL_KEY_CODE_TABLE[i].name) == 0) + return VIRTUAL_KEY_CODE_TABLE[i].code; + } + } + + return VK_NONE; +} + +DWORD GetVirtualKeyCodeFromXkbKeyName(const char* xkbname) +{ + for (size_t i = 0; i < ARRAYSIZE(XKB_KEYNAME_TABLE); i++) + { + if (XKB_KEYNAME_TABLE[i].name) + { + if (strcmp(xkbname, XKB_KEYNAME_TABLE[i].name) == 0) + return XKB_KEYNAME_TABLE[i].vkcode; + } + } + + return VK_NONE; +} diff --git a/winpr/libwinpr/interlocked/CMakeLists.txt b/winpr/libwinpr/interlocked/CMakeLists.txt new file mode 100644 index 0000000..7f16415 --- /dev/null +++ b/winpr/libwinpr/interlocked/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-interlocked cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(interlocked.c) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/interlocked/ModuleOptions.cmake b/winpr/libwinpr/interlocked/ModuleOptions.cmake new file mode 100644 index 0000000..8a1600a --- /dev/null +++ b/winpr/libwinpr/interlocked/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "interlocked") +set(MINWIN_LONG_NAME "Interlocked Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/interlocked/interlocked.c b/winpr/libwinpr/interlocked/interlocked.c new file mode 100644 index 0000000..6c3b0c5 --- /dev/null +++ b/winpr/libwinpr/interlocked/interlocked.c @@ -0,0 +1,488 @@ +/** + * WinPR: Windows Portable Runtime + * Interlocked Singly-Linked Lists + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +/* Singly-Linked List */ + +#ifndef _WIN32 + +#include +#include + +VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead) +{ +#ifdef _WIN64 + ListHead->s.Alignment = 0; + ListHead->s.Region = 0; + ListHead->Header8.Init = 1; +#else + ListHead->Alignment = 0; +#endif +} + +WINPR_PSLIST_ENTRY InterlockedPushEntrySList(WINPR_PSLIST_HEADER ListHead, + WINPR_PSLIST_ENTRY ListEntry) +{ + WINPR_SLIST_HEADER old; + WINPR_SLIST_HEADER newHeader; + +#ifdef _WIN64 + newHeader.HeaderX64.NextEntry = (((ULONG_PTR)ListEntry) >> 4); + + while (1) + { + old = *ListHead; + + ListEntry->Next = (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4); + + newHeader.HeaderX64.Depth = old.HeaderX64.Depth + 1; + newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence + 1; + + if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader.s.Alignment, + old.s.Alignment)) + { + InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader.s.Region, + old.s.Region); + break; + } + } + + return (PSLIST_ENTRY)((ULONG_PTR)old.HeaderX64.NextEntry << 4); +#else + newHeader.s.Next.Next = ListEntry; + + do + { + old = *ListHead; + ListEntry->Next = old.s.Next.Next; + newHeader.s.Depth = old.s.Depth + 1; + newHeader.s.Sequence = old.s.Sequence + 1; + if (old.Alignment > INT64_MAX) + return NULL; + if (newHeader.Alignment > INT64_MAX) + return NULL; + if (ListHead->Alignment > INT64_MAX) + return NULL; + } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment, + (LONGLONG)newHeader.Alignment, + (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment); + + return old.s.Next.Next; +#endif +} + +WINPR_PSLIST_ENTRY InterlockedPushListSListEx(WINPR_PSLIST_HEADER ListHead, WINPR_PSLIST_ENTRY List, + WINPR_PSLIST_ENTRY ListEnd, ULONG Count) +{ +#ifdef _WIN64 + +#else + +#endif + return NULL; +} + +WINPR_PSLIST_ENTRY InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead) +{ + WINPR_SLIST_HEADER old; + WINPR_SLIST_HEADER newHeader; + WINPR_PSLIST_ENTRY entry = NULL; + +#ifdef _WIN64 + while (1) + { + old = *ListHead; + + entry = (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4); + + if (!entry) + return NULL; + + newHeader.HeaderX64.NextEntry = ((ULONG_PTR)entry->Next) >> 4; + newHeader.HeaderX64.Depth = old.HeaderX64.Depth - 1; + newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence - 1; + + if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader.s.Alignment, + old.s.Alignment)) + { + InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader.s.Region, + old.s.Region); + break; + } + } +#else + do + { + old = *ListHead; + + entry = old.s.Next.Next; + + if (!entry) + return NULL; + + newHeader.s.Next.Next = entry->Next; + newHeader.s.Depth = old.s.Depth - 1; + newHeader.s.Sequence = old.s.Sequence + 1; + + if (old.Alignment > INT64_MAX) + return NULL; + if (newHeader.Alignment > INT64_MAX) + return NULL; + if (ListHead->Alignment > INT64_MAX) + return NULL; + } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment, + (LONGLONG)newHeader.Alignment, + (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment); +#endif + return entry; +} + +WINPR_PSLIST_ENTRY InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead) +{ + WINPR_SLIST_HEADER old; + WINPR_SLIST_HEADER newHeader; + + if (!QueryDepthSList(ListHead)) + return NULL; + +#ifdef _WIN64 + newHeader.s.Alignment = 0; + newHeader.s.Region = 0; + newHeader.HeaderX64.HeaderType = 1; + + while (1) + { + old = *ListHead; + newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence + 1; + + if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader.s.Alignment, + old.s.Alignment)) + { + InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader.s.Region, + old.s.Region); + break; + } + } + + return (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4); +#else + newHeader.Alignment = 0; + + do + { + old = *ListHead; + newHeader.s.Sequence = old.s.Sequence + 1; + + if (old.Alignment > INT64_MAX) + return NULL; + if (newHeader.Alignment > INT64_MAX) + return NULL; + if (ListHead->Alignment > INT64_MAX) + return NULL; + } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment, + (LONGLONG)newHeader.Alignment, + (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment); + + return old.s.Next.Next; +#endif +} + +USHORT QueryDepthSList(WINPR_PSLIST_HEADER ListHead) +{ +#ifdef _WIN64 + return ListHead->HeaderX64.Depth; +#else + return ListHead->s.Depth; +#endif +} + +LONG InterlockedIncrement(LONG volatile* Addend) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_add_and_fetch(Addend, 1); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +LONG InterlockedDecrement(LONG volatile* Addend) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_sub_and_fetch(Addend, 1); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +LONG InterlockedExchange(LONG volatile* Target, LONG Value) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_val_compare_and_swap(Target, *Target, Value); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_fetch_and_add(Addend, Value); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +LONG InterlockedCompareExchange(LONG volatile* Destination, LONG Exchange, LONG Comperand) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_val_compare_and_swap(Destination, Comperand, Exchange); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +PVOID InterlockedCompareExchangePointer(PVOID volatile* Destination, PVOID Exchange, + PVOID Comperand) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_val_compare_and_swap(Destination, Comperand, Exchange); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +#endif /* _WIN32 */ + +#if defined(_WIN32) && !defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64) + +/* InterlockedCompareExchange64 already defined */ + +#elif defined(_WIN32) && defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64) + +static volatile HANDLE mutex = NULL; + +BOOL static_mutex_lock(volatile HANDLE* static_mutex) +{ + if (*static_mutex == NULL) + { + HANDLE handle; + + if (!(handle = CreateMutex(NULL, FALSE, NULL))) + return FALSE; + + if (InterlockedCompareExchangePointer((PVOID*)static_mutex, (PVOID)handle, NULL) != NULL) + CloseHandle(handle); + } + + return (WaitForSingleObject(*static_mutex, INFINITE) == WAIT_OBJECT_0); +} + +LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange, + LONGLONG Comperand) +{ + LONGLONG previousValue = 0; + BOOL locked = static_mutex_lock(&mutex); + + previousValue = *Destination; + + if (*Destination == Comperand) + *Destination = Exchange; + + if (locked) + ReleaseMutex(mutex); + else + fprintf(stderr, "WARNING: InterlockedCompareExchange64 operation might have failed\n"); + + return previousValue; +} + +#elif (defined(ANDROID) && ANDROID) || \ + (defined(__GNUC__) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) + +#include + +static pthread_mutex_t mutex; + +LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange, + LONGLONG Comperand) +{ + LONGLONG previousValue = 0; + + pthread_mutex_lock(&mutex); + + previousValue = *Destination; + + if (*Destination == Comperand) + *Destination = Exchange; + + pthread_mutex_unlock(&mutex); + + return previousValue; +} + +#else + +LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange, + LONGLONG Comperand) +{ +#if defined(__GNUC__) || defined(__clang__) + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST + return __sync_val_compare_and_swap(Destination, Comperand, Exchange); + WINPR_PRAGMA_DIAG_POP +#else + return 0; +#endif +} + +#endif + +/* Doubly-Linked List */ + +/** + * Kernel-Mode Basics: Windows Linked Lists: + * http://www.osronline.com/article.cfm?article=499 + * + * Singly and Doubly Linked Lists: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff563802/ + */ + +VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +BOOL IsListEmpty(const WINPR_LIST_ENTRY* ListHead) +{ + return (BOOL)(ListHead->Flink == ListHead); +} + +BOOL RemoveEntryList(WINPR_PLIST_ENTRY Entry) +{ + WINPR_PLIST_ENTRY OldFlink = NULL; + WINPR_PLIST_ENTRY OldBlink = NULL; + + OldFlink = Entry->Flink; + OldBlink = Entry->Blink; + OldFlink->Blink = OldBlink; + OldBlink->Flink = OldFlink; + + return (BOOL)(OldFlink == OldBlink); +} + +VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry) +{ + WINPR_PLIST_ENTRY OldFlink = NULL; + + OldFlink = ListHead->Flink; + Entry->Flink = OldFlink; + Entry->Blink = ListHead; + OldFlink->Blink = Entry; + ListHead->Flink = Entry; +} + +WINPR_PLIST_ENTRY RemoveHeadList(WINPR_PLIST_ENTRY ListHead) +{ + WINPR_PLIST_ENTRY Flink = NULL; + WINPR_PLIST_ENTRY Entry = NULL; + + Entry = ListHead->Flink; + Flink = Entry->Flink; + ListHead->Flink = Flink; + Flink->Blink = ListHead; + + return Entry; +} + +VOID InsertTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry) +{ + WINPR_PLIST_ENTRY OldBlink = NULL; + + OldBlink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = OldBlink; + OldBlink->Flink = Entry; + ListHead->Blink = Entry; +} + +WINPR_PLIST_ENTRY RemoveTailList(WINPR_PLIST_ENTRY ListHead) +{ + WINPR_PLIST_ENTRY Blink = NULL; + WINPR_PLIST_ENTRY Entry = NULL; + + Entry = ListHead->Blink; + Blink = Entry->Blink; + ListHead->Blink = Blink; + Blink->Flink = ListHead; + + return Entry; +} + +VOID AppendTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY ListToAppend) +{ + WINPR_PLIST_ENTRY ListEnd = ListHead->Blink; + + ListHead->Blink->Flink = ListToAppend; + ListHead->Blink = ListToAppend->Blink; + ListToAppend->Blink->Flink = ListHead; + ListToAppend->Blink = ListEnd; +} + +VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead, WINPR_PSINGLE_LIST_ENTRY Entry) +{ + Entry->Next = ListHead->Next; + ListHead->Next = Entry; +} + +WINPR_PSINGLE_LIST_ENTRY PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead) +{ + WINPR_PSINGLE_LIST_ENTRY FirstEntry = NULL; + + FirstEntry = ListHead->Next; + + if (FirstEntry != NULL) + ListHead->Next = FirstEntry->Next; + + return FirstEntry; +} diff --git a/winpr/libwinpr/interlocked/module_5.1.def b/winpr/libwinpr/interlocked/module_5.1.def new file mode 100644 index 0000000..160c9de --- /dev/null +++ b/winpr/libwinpr/interlocked/module_5.1.def @@ -0,0 +1,13 @@ +LIBRARY "libwinpr-interlocked" +EXPORTS + InterlockedCompareExchange64 @1 + InitializeListHead @2 + IsListEmpty @3 + RemoveEntryList @4 + InsertHeadList @5 + RemoveHeadList @6 + InsertTailList @7 + RemoveTailList @8 + AppendTailList @9 + PushEntryList @10 + PopEntryList @11 diff --git a/winpr/libwinpr/interlocked/test/CMakeLists.txt b/winpr/libwinpr/interlocked/test/CMakeLists.txt new file mode 100644 index 0000000..1166410 --- /dev/null +++ b/winpr/libwinpr/interlocked/test/CMakeLists.txt @@ -0,0 +1,28 @@ + +set(MODULE_NAME "TestInterlocked") +set(MODULE_PREFIX "TEST_INTERLOCKED") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestInterlockedAccess.c + TestInterlockedSList.c + TestInterlockedDList.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/interlocked/test/TestInterlockedAccess.c b/winpr/libwinpr/interlocked/test/TestInterlockedAccess.c new file mode 100644 index 0000000..0ecd850 --- /dev/null +++ b/winpr/libwinpr/interlocked/test/TestInterlockedAccess.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +int TestInterlockedAccess(int argc, char* argv[]) +{ + LONG* Addend = NULL; + LONG* Target = NULL; + LONG oldValue = 0; + LONG* Destination = NULL; + LONGLONG oldValue64 = 0; + LONGLONG* Destination64 = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + /* InterlockedIncrement */ + + Addend = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + if (!Addend) + { + printf("Failed to allocate memory\n"); + return -1; + } + + *Addend = 0; + + for (int index = 0; index < 10; index++) + InterlockedIncrement(Addend); + + if (*Addend != 10) + { + printf("InterlockedIncrement failure: Actual: %" PRId32 ", Expected: 10\n", *Addend); + return -1; + } + + /* InterlockedDecrement */ + + for (int index = 0; index < 10; index++) + InterlockedDecrement(Addend); + + if (*Addend != 0) + { + printf("InterlockedDecrement failure: Actual: %" PRId32 ", Expected: 0\n", *Addend); + return -1; + } + + /* InterlockedExchange */ + + Target = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + + if (!Target) + { + printf("Failed to allocate memory\n"); + return -1; + } + + *Target = 0xAA; + + oldValue = InterlockedExchange(Target, 0xFF); + + if (oldValue != 0xAA) + { + printf("InterlockedExchange failure: Actual: 0x%08" PRIX32 ", Expected: 0xAA\n", oldValue); + return -1; + } + + if (*Target != 0xFF) + { + printf("InterlockedExchange failure: Actual: 0x%08" PRIX32 ", Expected: 0xFF\n", *Target); + return -1; + } + + /* InterlockedExchangeAdd */ + + *Addend = 25; + + oldValue = InterlockedExchangeAdd(Addend, 100); + + if (oldValue != 25) + { + printf("InterlockedExchangeAdd failure: Actual: %" PRId32 ", Expected: 25\n", oldValue); + return -1; + } + + if (*Addend != 125) + { + printf("InterlockedExchangeAdd failure: Actual: %" PRId32 ", Expected: 125\n", *Addend); + return -1; + } + + /* InterlockedCompareExchange (*Destination == Comparand) */ + + Destination = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + if (!Destination) + { + printf("Failed to allocate memory\n"); + return -1; + } + + *Destination = (LONG)0xAABBCCDDL; + + oldValue = InterlockedCompareExchange(Destination, (LONG)0xCCDDEEFFL, (LONG)0xAABBCCDDL); + + if (oldValue != (LONG)0xAABBCCDDL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%08" PRIX32 + ", Expected: 0xAABBCCDD\n", + oldValue); + return -1; + } + + if ((*Destination) != (LONG)0xCCDDEEFFL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%08" PRIX32 + ", Expected: 0xCCDDEEFF\n", + *Destination); + return -1; + } + + /* InterlockedCompareExchange (*Destination != Comparand) */ + + *Destination = (LONG)0xAABBCCDDL; + + oldValue = InterlockedCompareExchange(Destination, -857870593L, 0x66778899L); + + if (oldValue != (LONG)0xAABBCCDDL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%08" PRIX32 + ", Expected: 0xAABBCCDD\n", + oldValue); + return -1; + } + + if ((*Destination) != (LONG)0xAABBCCDDL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%08" PRIX32 + ", Expected: 0xAABBCCDD\n", + *Destination); + return -1; + } + + /* InterlockedCompareExchange64 (*Destination == Comparand) */ + + Destination64 = winpr_aligned_malloc(sizeof(LONGLONG), sizeof(LONGLONG)); + if (!Destination64) + { + printf("Failed to allocate memory\n"); + return -1; + } + + *Destination64 = 0x66778899AABBCCDD; + + oldValue64 = + InterlockedCompareExchange64(Destination64, 0x8899AABBCCDDEEFF, 0x66778899AABBCCDD); + + if (oldValue64 != 0x66778899AABBCCDD) + { + printf("InterlockedCompareExchange failure: Actual: 0x%016" PRIX64 + ", Expected: 0x66778899AABBCCDD\n", + oldValue64); + return -1; + } + + if ((*Destination64) != (LONGLONG)0x8899AABBCCDDEEFFLL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%016" PRIX64 + ", Expected: 0x8899AABBCCDDEEFF\n", + *Destination64); + return -1; + } + + /* InterlockedCompareExchange64 (*Destination != Comparand) */ + + *Destination64 = 0x66778899AABBCCDDLL; + + oldValue64 = InterlockedCompareExchange64(Destination64, 0x8899AABBCCDDEEFFLL, 12345); + + if (oldValue64 != 0x66778899AABBCCDDLL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%016" PRIX64 + ", Expected: 0x66778899AABBCCDD\n", + oldValue64); + return -1; + } + + if (*Destination64 != 0x66778899AABBCCDDLL) + { + printf("InterlockedCompareExchange failure: Actual: 0x%016" PRIX64 + ", Expected: 0x66778899AABBCCDD\n", + *Destination64); + return -1; + } + + winpr_aligned_free(Addend); + winpr_aligned_free(Target); + winpr_aligned_free(Destination); + winpr_aligned_free(Destination64); + + return 0; +} diff --git a/winpr/libwinpr/interlocked/test/TestInterlockedDList.c b/winpr/libwinpr/interlocked/test/TestInterlockedDList.c new file mode 100644 index 0000000..f49e37d --- /dev/null +++ b/winpr/libwinpr/interlocked/test/TestInterlockedDList.c @@ -0,0 +1,79 @@ + +#include +#include +#include +#include + +typedef struct +{ + WINPR_LIST_ENTRY ItemEntry; + ULONG Signature; +} LIST_ITEM, *PLIST_ITEM; + +int TestInterlockedDList(int argc, char* argv[]) +{ + ULONG Count = 0; + PLIST_ITEM pListItem = NULL; + WINPR_PLIST_ENTRY pListHead = NULL; + WINPR_PLIST_ENTRY pListEntry = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + pListHead = (WINPR_PLIST_ENTRY)winpr_aligned_malloc(sizeof(WINPR_LIST_ENTRY), + MEMORY_ALLOCATION_ALIGNMENT); + + if (!pListHead) + { + printf("Memory allocation failed.\n"); + return -1; + } + + InitializeListHead(pListHead); + + if (!IsListEmpty(pListHead)) + { + printf("Expected empty list\n"); + return -1; + } + + /* InsertHeadList / RemoveHeadList */ + + printf("InsertHeadList / RemoveHeadList\n"); + + for (Count = 1; Count <= 10; Count += 1) + { + pListItem = + (PLIST_ITEM)winpr_aligned_malloc(sizeof(LIST_ITEM), MEMORY_ALLOCATION_ALIGNMENT); + pListItem->Signature = Count; + InsertHeadList(pListHead, &(pListItem->ItemEntry)); + } + + for (Count = 10; Count >= 1; Count -= 1) + { + pListEntry = RemoveHeadList(pListHead); + pListItem = (PLIST_ITEM)pListEntry; + winpr_aligned_free(pListItem); + } + + /* InsertTailList / RemoveTailList */ + + printf("InsertTailList / RemoveTailList\n"); + + for (Count = 1; Count <= 10; Count += 1) + { + pListItem = + (PLIST_ITEM)winpr_aligned_malloc(sizeof(LIST_ITEM), MEMORY_ALLOCATION_ALIGNMENT); + pListItem->Signature = Count; + InsertTailList(pListHead, &(pListItem->ItemEntry)); + } + + for (Count = 10; Count >= 1; Count -= 1) + { + pListEntry = RemoveTailList(pListHead); + pListItem = (PLIST_ITEM)pListEntry; + winpr_aligned_free(pListItem); + } + + winpr_aligned_free(pListHead); + + return 0; +} diff --git a/winpr/libwinpr/interlocked/test/TestInterlockedSList.c b/winpr/libwinpr/interlocked/test/TestInterlockedSList.c new file mode 100644 index 0000000..71ac8bc --- /dev/null +++ b/winpr/libwinpr/interlocked/test/TestInterlockedSList.c @@ -0,0 +1,85 @@ + +#include +#include +#include +#include + +typedef struct +{ + WINPR_SLIST_ENTRY ItemEntry; + ULONG Signature; +} PROGRAM_ITEM, *PPROGRAM_ITEM; + +int TestInterlockedSList(int argc, char* argv[]) +{ + ULONG Count = 0; + WINPR_PSLIST_ENTRY pFirstEntry = NULL; + WINPR_PSLIST_HEADER pListHead = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + /* Initialize the list header to a MEMORY_ALLOCATION_ALIGNMENT boundary. */ + pListHead = (WINPR_PSLIST_HEADER)winpr_aligned_malloc(sizeof(WINPR_SLIST_HEADER), + MEMORY_ALLOCATION_ALIGNMENT); + + if (!pListHead) + { + printf("Memory allocation failed.\n"); + return -1; + } + + InitializeSListHead(pListHead); + + /* Insert 10 items into the list. */ + for (Count = 1; Count <= 10; Count += 1) + { + PPROGRAM_ITEM pProgramItem = + (PPROGRAM_ITEM)winpr_aligned_malloc(sizeof(PROGRAM_ITEM), MEMORY_ALLOCATION_ALIGNMENT); + + if (!pProgramItem) + { + printf("Memory allocation failed.\n"); + return -1; + } + + pProgramItem->Signature = Count; + pFirstEntry = InterlockedPushEntrySList(pListHead, &(pProgramItem->ItemEntry)); + } + + /* Remove 10 items from the list and display the signature. */ + for (Count = 10; Count >= 1; Count -= 1) + { + PPROGRAM_ITEM pProgramItem = NULL; + WINPR_PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(pListHead); + + if (!pListEntry) + { + printf("List is empty.\n"); + return -1; + } + + pProgramItem = (PPROGRAM_ITEM)pListEntry; + printf("Signature is %" PRIu32 "\n", pProgramItem->Signature); + + /* + * This example assumes that the SLIST_ENTRY structure is the + * first member of the structure. If your structure does not + * follow this convention, you must compute the starting address + * of the structure before calling the free function. + */ + + winpr_aligned_free(pListEntry); + } + + /* Flush the list and verify that the items are gone. */ + pFirstEntry = InterlockedPopEntrySList(pListHead); + + if (pFirstEntry) + { + printf("Error: List is not empty.\n"); + return -1; + } + + winpr_aligned_free(pListHead); + + return 0; +} diff --git a/winpr/libwinpr/io/CMakeLists.txt b/winpr/libwinpr/io/CMakeLists.txt new file mode 100644 index 0000000..c7050d4 --- /dev/null +++ b/winpr/libwinpr/io/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-io cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(device.c io.c io.h) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/io/ModuleOptions.cmake b/winpr/libwinpr/io/ModuleOptions.cmake new file mode 100644 index 0000000..f9f991d --- /dev/null +++ b/winpr/libwinpr/io/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "1") +set(MINWIN_SHORT_NAME "io") +set(MINWIN_LONG_NAME "Asynchronous I/O Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/io/device.c b/winpr/libwinpr/io/device.c new file mode 100644 index 0000000..0643acd --- /dev/null +++ b/winpr/libwinpr/io/device.c @@ -0,0 +1,234 @@ +/** + * WinPR: Windows Portable Runtime + * Asynchronous I/O Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#ifndef _WIN32 + +#include "io.h" + +#include +#include +#include +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include +#include +#include + +/** + * I/O Manager Routines + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff551797/ + * + * These routines are only accessible to kernel drivers, but we need + * similar functionality in WinPR in user space. + * + * This is a best effort non-conflicting port of this API meant for + * non-Windows, WinPR usage only. + * + * References: + * + * Device Objects and Device Stacks: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff543153/ + * + * Driver Development Part 1: Introduction to Drivers: + * http://www.codeproject.com/Articles/9504/Driver-Development-Part-1-Introduction-to-Drivers/ + */ + +#define DEVICE_FILE_PREFIX_PATH "\\Device\\" + +static char* GetDeviceFileNameWithoutPrefixA(LPCSTR lpName) +{ + char* lpFileName = NULL; + + if (!lpName) + return NULL; + + if (strncmp(lpName, DEVICE_FILE_PREFIX_PATH, sizeof(DEVICE_FILE_PREFIX_PATH) - 1) != 0) + return NULL; + + lpFileName = + _strdup(&lpName[strnlen(DEVICE_FILE_PREFIX_PATH, sizeof(DEVICE_FILE_PREFIX_PATH))]); + return lpFileName; +} + +static char* GetDeviceFileUnixDomainSocketBaseFilePathA(void) +{ + char* lpTempPath = NULL; + char* lpPipePath = NULL; + lpTempPath = GetKnownPath(KNOWN_PATH_TEMP); + + if (!lpTempPath) + return NULL; + + lpPipePath = GetCombinedPath(lpTempPath, ".device"); + free(lpTempPath); + return lpPipePath; +} + +static char* GetDeviceFileUnixDomainSocketFilePathA(LPCSTR lpName) +{ + char* lpPipePath = NULL; + char* lpFileName = NULL; + char* lpFilePath = NULL; + lpPipePath = GetDeviceFileUnixDomainSocketBaseFilePathA(); + + if (!lpPipePath) + return NULL; + + lpFileName = GetDeviceFileNameWithoutPrefixA(lpName); + + if (!lpFileName) + { + free(lpPipePath); + return NULL; + } + + lpFilePath = GetCombinedPath(lpPipePath, (char*)lpFileName); + free(lpPipePath); + free(lpFileName); + return lpFilePath; +} + +/** + * IoCreateDevice: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff548397/ + */ + +NTSTATUS _IoCreateDeviceEx(PDRIVER_OBJECT_EX DriverObject, ULONG DeviceExtensionSize, + PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, + ULONG DeviceCharacteristics, BOOLEAN Exclusive, + PDEVICE_OBJECT_EX* DeviceObject) +{ + int status = 0; + char* DeviceBasePath = NULL; + DEVICE_OBJECT_EX* pDeviceObjectEx = NULL; + DeviceBasePath = GetDeviceFileUnixDomainSocketBaseFilePathA(); + + if (!DeviceBasePath) + return STATUS_NO_MEMORY; + + if (!winpr_PathFileExists(DeviceBasePath)) + { + if (mkdir(DeviceBasePath, S_IRUSR | S_IWUSR | S_IXUSR) != 0) + { + free(DeviceBasePath); + return STATUS_ACCESS_DENIED; + } + } + + free(DeviceBasePath); + pDeviceObjectEx = (DEVICE_OBJECT_EX*)calloc(1, sizeof(DEVICE_OBJECT_EX)); + + if (!pDeviceObjectEx) + return STATUS_NO_MEMORY; + + pDeviceObjectEx->DeviceName = + ConvertWCharNToUtf8Alloc(DeviceName->Buffer, DeviceName->Length / sizeof(WCHAR), NULL); + if (!pDeviceObjectEx->DeviceName) + { + free(pDeviceObjectEx); + return STATUS_NO_MEMORY; + } + + pDeviceObjectEx->DeviceFileName = + GetDeviceFileUnixDomainSocketFilePathA(pDeviceObjectEx->DeviceName); + + if (!pDeviceObjectEx->DeviceFileName) + { + free(pDeviceObjectEx->DeviceName); + free(pDeviceObjectEx); + return STATUS_NO_MEMORY; + } + + if (winpr_PathFileExists(pDeviceObjectEx->DeviceFileName)) + { + if (unlink(pDeviceObjectEx->DeviceFileName) == -1) + { + free(pDeviceObjectEx->DeviceName); + free(pDeviceObjectEx->DeviceFileName); + free(pDeviceObjectEx); + return STATUS_ACCESS_DENIED; + } + } + + status = mkfifo(pDeviceObjectEx->DeviceFileName, 0666); + + if (status != 0) + { + free(pDeviceObjectEx->DeviceName); + free(pDeviceObjectEx->DeviceFileName); + free(pDeviceObjectEx); + + switch (errno) + { + case EACCES: + return STATUS_ACCESS_DENIED; + + case EEXIST: + return STATUS_OBJECT_NAME_EXISTS; + + case ENAMETOOLONG: + return STATUS_NAME_TOO_LONG; + + case ENOENT: + case ENOTDIR: + return STATUS_NOT_A_DIRECTORY; + + case ENOSPC: + return STATUS_DISK_FULL; + + default: + return STATUS_INTERNAL_ERROR; + } + } + + *((ULONG_PTR*)(DeviceObject)) = (ULONG_PTR)pDeviceObjectEx; + return STATUS_SUCCESS; +} + +/** + * IoDeleteDevice: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff549083/ + */ + +VOID _IoDeleteDeviceEx(PDEVICE_OBJECT_EX DeviceObject) +{ + DEVICE_OBJECT_EX* pDeviceObjectEx = NULL; + pDeviceObjectEx = (DEVICE_OBJECT_EX*)DeviceObject; + + if (!pDeviceObjectEx) + return; + + unlink(pDeviceObjectEx->DeviceFileName); + free(pDeviceObjectEx->DeviceName); + free(pDeviceObjectEx->DeviceFileName); + free(pDeviceObjectEx); +} + +#endif diff --git a/winpr/libwinpr/io/io.c b/winpr/libwinpr/io/io.c new file mode 100644 index 0000000..2df20be --- /dev/null +++ b/winpr/libwinpr/io/io.c @@ -0,0 +1,276 @@ +/** + * WinPR: Windows Portable Runtime + * Asynchronous I/O Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#ifndef _WIN32 + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "../handle/handle.h" +#include "../pipe/pipe.h" +#include "../log.h" + +#define TAG WINPR_TAG("io") + +BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, BOOL bWait) +{ +#if 1 + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +#else + ULONG Type; + WINPR_HANDLE* Object; + + if (!winpr_Handle_GetInfo(hFile, &Type, &Object)) + return FALSE; + + else if (Type == HANDLE_TYPE_NAMED_PIPE) + { + int status = -1; + DWORD request; + PVOID lpBuffer; + DWORD nNumberOfBytes; + WINPR_NAMED_PIPE* pipe; + + pipe = (WINPR_NAMED_PIPE*)Object; + + if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)) + return FALSE; + + lpBuffer = lpOverlapped->Pointer; + request = (DWORD)lpOverlapped->Internal; + nNumberOfBytes = (DWORD)lpOverlapped->InternalHigh; + + if (request == 0) + { + if (pipe->clientfd == -1) + return FALSE; + + status = read(pipe->clientfd, lpBuffer, nNumberOfBytes); + } + else if (request == 1) + { + if (pipe->clientfd == -1) + return FALSE; + + status = write(pipe->clientfd, lpBuffer, nNumberOfBytes); + } + else if (request == 2) + { + socklen_t length; + struct sockaddr_un s = { 0 }; + + if (pipe->serverfd == -1) + return FALSE; + + length = sizeof(struct sockaddr_un); + + status = accept(pipe->serverfd, (struct sockaddr*)&s, &length); + + if (status < 0) + return FALSE; + + pipe->clientfd = status; + pipe->ServerMode = FALSE; + + status = 0; + } + + if (status < 0) + { + *lpNumberOfBytesTransferred = 0; + return FALSE; + } + + *lpNumberOfBytesTransferred = status; + } + + return TRUE; +#endif +} + +BOOL GetOverlappedResultEx(HANDLE hFile, LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, DWORD dwMilliseconds, + BOOL bAlertable) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, + LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, + ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, + PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, + DWORD dwMilliseconds) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetQueuedCompletionStatusEx(HANDLE CompletionPort, LPOVERLAPPED_ENTRY lpCompletionPortEntries, + ULONG ulCount, PULONG ulNumEntriesRemoved, DWORD dwMilliseconds, + BOOL fAlertable) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL PostQueuedCompletionStatus(HANDLE CompletionPort, DWORD dwNumberOfBytesTransferred, + ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CancelIo(HANDLE hFile) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CancelIoEx(HANDLE hFile, LPOVERLAPPED lpOverlapped) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CancelSynchronousIo(HANDLE hThread) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +#endif + +#ifdef _UWP + +#include +#include + +#include "../log.h" + +#define TAG WINPR_TAG("io") + +BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, BOOL bWait) +{ + return GetOverlappedResultEx(hFile, lpOverlapped, lpNumberOfBytesTransferred, + bWait ? INFINITE : 0, TRUE); +} + +BOOL DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, + LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, + ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, + PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, + DWORD dwMilliseconds) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetQueuedCompletionStatusEx(HANDLE CompletionPort, LPOVERLAPPED_ENTRY lpCompletionPortEntries, + ULONG ulCount, PULONG ulNumEntriesRemoved, DWORD dwMilliseconds, + BOOL fAlertable) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL PostQueuedCompletionStatus(HANDLE CompletionPort, DWORD dwNumberOfBytesTransferred, + ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CancelIo(HANDLE hFile) +{ + return CancelIoEx(hFile, NULL); +} + +BOOL CancelSynchronousIo(HANDLE hThread) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +#endif diff --git a/winpr/libwinpr/io/io.h b/winpr/libwinpr/io/io.h new file mode 100644 index 0000000..17d0013 --- /dev/null +++ b/winpr/libwinpr/io/io.h @@ -0,0 +1,37 @@ +/** + * WinPR: Windows Portable Runtime + * Asynchronous I/O Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_IO_PRIVATE_H +#define WINPR_IO_PRIVATE_H + +#ifndef _WIN32 + +#include + +#include "../handle/handle.h" + +typedef struct +{ + char* DeviceName; + char* DeviceFileName; +} DEVICE_OBJECT_EX; + +#endif + +#endif /* WINPR_IO_PRIVATE_H */ diff --git a/winpr/libwinpr/io/test/CMakeLists.txt b/winpr/libwinpr/io/test/CMakeLists.txt new file mode 100644 index 0000000..ca40897 --- /dev/null +++ b/winpr/libwinpr/io/test/CMakeLists.txt @@ -0,0 +1,25 @@ + +set(MODULE_NAME "TestIo") +set(MODULE_PREFIX "TEST_IO") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestIoGetOverlappedResult.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/io/test/TestIoGetOverlappedResult.c b/winpr/libwinpr/io/test/TestIoGetOverlappedResult.c new file mode 100644 index 0000000..044eb11 --- /dev/null +++ b/winpr/libwinpr/io/test/TestIoGetOverlappedResult.c @@ -0,0 +1,10 @@ + +#include +#include +#include +#include + +int TestIoGetOverlappedResult(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/library/CMakeLists.txt b/winpr/libwinpr/library/CMakeLists.txt new file mode 100644 index 0000000..9db7d0d --- /dev/null +++ b/winpr/libwinpr/library/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-library cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(library.c) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/library/ModuleOptions.cmake b/winpr/libwinpr/library/ModuleOptions.cmake new file mode 100644 index 0000000..affda86 --- /dev/null +++ b/winpr/libwinpr/library/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "1") +set(MINWIN_SHORT_NAME "libraryloader") +set(MINWIN_LONG_NAME "Dynamic-Link Library Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/library/library.c b/winpr/libwinpr/library/library.c new file mode 100644 index 0000000..307dc20 --- /dev/null +++ b/winpr/libwinpr/library/library.c @@ -0,0 +1,381 @@ +/** + * WinPR: Windows Portable Runtime + * Library Loader + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#include "../log.h" +#define TAG WINPR_TAG("library") + +/** + * api-ms-win-core-libraryloader-l1-1-1.dll: + * + * AddDllDirectory + * RemoveDllDirectory + * SetDefaultDllDirectories + * DisableThreadLibraryCalls + * EnumResourceLanguagesExA + * EnumResourceLanguagesExW + * EnumResourceNamesExA + * EnumResourceNamesExW + * EnumResourceTypesExA + * EnumResourceTypesExW + * FindResourceExW + * FindStringOrdinal + * FreeLibrary + * FreeLibraryAndExitThread + * FreeResource + * GetModuleFileNameA + * GetModuleFileNameW + * GetModuleHandleA + * GetModuleHandleExA + * GetModuleHandleExW + * GetModuleHandleW + * GetProcAddress + * LoadLibraryExA + * LoadLibraryExW + * LoadResource + * LoadStringA + * LoadStringW + * LockResource + * QueryOptionalDelayLoadedAPI + * SizeofResource + */ + +#if !defined(_WIN32) || defined(_UWP) + +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include + +#ifdef __MACOSX__ +#include +#endif + +#endif + +DLL_DIRECTORY_COOKIE AddDllDirectory(PCWSTR NewDirectory) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +BOOL RemoveDllDirectory(DLL_DIRECTORY_COOKIE Cookie) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetDefaultDllDirectories(DWORD DirectoryFlags) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +HMODULE LoadLibraryA(LPCSTR lpLibFileName) +{ +#if defined(_UWP) + int status; + HMODULE hModule = NULL; + WCHAR* filenameW = NULL; + + if (!lpLibFileName) + return NULL; + + filenameW = ConvertUtf8ToWCharAlloc(lpLibFileName, NULL); + if (filenameW) + return NULL; + + hModule = LoadLibraryW(filenameW); + free(filenameW); + return hModule; +#else + HMODULE library = NULL; + library = dlopen(lpLibFileName, RTLD_LOCAL | RTLD_LAZY); + + if (!library) + { + const char* err = dlerror(); + WLog_ERR(TAG, "failed with %s", err); + return NULL; + } + + return library; +#endif +} + +HMODULE LoadLibraryW(LPCWSTR lpLibFileName) +{ + if (!lpLibFileName) + return NULL; +#if defined(_UWP) + return LoadPackagedLibrary(lpLibFileName, 0); +#else + HMODULE module = NULL; + char* name = ConvertWCharToUtf8Alloc(lpLibFileName, NULL); + if (!name) + return NULL; + + module = LoadLibraryA(name); + free(name); + return module; +#endif +} + +HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + if (dwFlags != 0) + WLog_WARN(TAG, "does not support dwFlags 0x%08" PRIx32, dwFlags); + + if (hFile) + WLog_WARN(TAG, "does not support hFile != NULL"); + + return LoadLibraryA(lpLibFileName); +} + +HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + if (dwFlags != 0) + WLog_WARN(TAG, "does not support dwFlags 0x%08" PRIx32, dwFlags); + + if (hFile) + WLog_WARN(TAG, "does not support hFile != NULL"); + + return LoadLibraryW(lpLibFileName); +} + +#endif + +#if !defined(_WIN32) && !defined(__CYGWIN__) + +FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) +{ + FARPROC proc = NULL; + proc = dlsym(hModule, lpProcName); + + if (proc == NULL) + { + WLog_ERR(TAG, "GetProcAddress: could not find procedure %s: %s", lpProcName, dlerror()); + return (FARPROC)NULL; + } + + return proc; +} + +BOOL FreeLibrary(HMODULE hLibModule) +{ + int status = 0; + status = dlclose(hLibModule); + + if (status != 0) + return FALSE; + + return TRUE; +} + +HMODULE GetModuleHandleA(LPCSTR lpModuleName) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +HMODULE GetModuleHandleW(LPCWSTR lpModuleName) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +/** + * GetModuleFileName: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197/ + * + * Finding current executable's path without /proc/self/exe: + * http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe + */ + +DWORD GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) +{ + DWORD status = 0; + if (!lpFilename) + { + SetLastError(ERROR_INTERNAL_ERROR); + return 0; + } + + char* name = calloc(nSize, sizeof(char)); + if (!name) + { + SetLastError(ERROR_INTERNAL_ERROR); + return 0; + } + status = GetModuleFileNameA(hModule, name, nSize); + + if ((status > INT_MAX) || (nSize > INT_MAX)) + { + SetLastError(ERROR_INTERNAL_ERROR); + status = 0; + } + + if (status > 0) + { + if (ConvertUtf8NToWChar(name, status, lpFilename, nSize) < 0) + { + free(name); + SetLastError(ERROR_INTERNAL_ERROR); + return 0; + } + } + + free(name); + return status; +} + +DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) +{ +#if defined(__linux__) + SSIZE_T status = 0; + size_t length = 0; + char path[64]; + + if (!hModule) + { + char buffer[4096]; + sprintf_s(path, ARRAYSIZE(path), "/proc/%d/exe", getpid()); + status = readlink(path, buffer, sizeof(buffer)); + + if (status < 0) + { + SetLastError(ERROR_INTERNAL_ERROR); + return 0; + } + + buffer[status] = '\0'; + length = strnlen(buffer, sizeof(buffer)); + + if (length < nSize) + { + CopyMemory(lpFilename, buffer, length); + lpFilename[length] = '\0'; + return (DWORD)length; + } + + CopyMemory(lpFilename, buffer, nSize - 1); + lpFilename[nSize - 1] = '\0'; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return nSize; + } + +#elif defined(__MACOSX__) + int status; + size_t length; + + if (!hModule) + { + char path[4096]; + char buffer[4096]; + uint32_t size = sizeof(path); + status = _NSGetExecutablePath(path, &size); + + if (status != 0) + { + /* path too small */ + SetLastError(ERROR_INTERNAL_ERROR); + return 0; + } + + /* + * _NSGetExecutablePath may not return the canonical path, + * so use realpath to find the absolute, canonical path. + */ + realpath(path, buffer); + length = strnlen(buffer, sizeof(buffer)); + + if (length < nSize) + { + CopyMemory(lpFilename, buffer, length); + lpFilename[length] = '\0'; + return (DWORD)length; + } + + CopyMemory(lpFilename, buffer, nSize - 1); + lpFilename[nSize - 1] = '\0'; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return nSize; + } + +#endif + WLog_ERR(TAG, "is not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +#endif + +HMODULE LoadLibraryX(LPCSTR lpLibFileName) +{ + if (!lpLibFileName) + return NULL; + +#if defined(_WIN32) + HMODULE hm = NULL; + WCHAR* wstr = ConvertUtf8ToWCharAlloc(lpLibFileName, NULL); + + if (wstr) + hm = LoadLibraryW(wstr); + free(wstr); + return hm; +#else + return LoadLibraryA(lpLibFileName); +#endif +} + +HMODULE LoadLibraryExX(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + if (!lpLibFileName) + return NULL; +#if defined(_WIN32) + HMODULE hm = NULL; + WCHAR* wstr = ConvertUtf8ToWCharAlloc(lpLibFileName, NULL); + if (wstr) + hm = LoadLibraryExW(wstr, hFile, dwFlags); + free(wstr); + return hm; +#else + return LoadLibraryExA(lpLibFileName, hFile, dwFlags); +#endif +} diff --git a/winpr/libwinpr/library/test/CMakeLists.txt b/winpr/libwinpr/library/test/CMakeLists.txt new file mode 100644 index 0000000..dca2b25 --- /dev/null +++ b/winpr/libwinpr/library/test/CMakeLists.txt @@ -0,0 +1,33 @@ + +set(MODULE_NAME "TestLibrary") +set(MODULE_PREFIX "TEST_LIBRARY") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestLibraryLoadLibrary.c + TestLibraryGetProcAddress.c + TestLibraryGetModuleFileName.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +set(TEST_AREA "${MODULE_NAME}Area") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + +add_subdirectory(TestLibraryA) +add_subdirectory(TestLibraryB) + diff --git a/winpr/libwinpr/library/test/TestLibraryA/CMakeLists.txt b/winpr/libwinpr/library/test/TestLibraryA/CMakeLists.txt new file mode 100644 index 0000000..73d8b7e --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryA/CMakeLists.txt @@ -0,0 +1,29 @@ +# WinPR: Windows Portable Runtime +# libwinpr-library cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "TestLibraryA") +set(MODULE_PREFIX "TEST_LIBRARY_A") + +set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} TestLibraryA.c) + +add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "") + +set_target_properties(${MODULE_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test/Extra") diff --git a/winpr/libwinpr/library/test/TestLibraryA/TestLibraryA.c b/winpr/libwinpr/library/test/TestLibraryA/TestLibraryA.c new file mode 100644 index 0000000..d11bc4d --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryA/TestLibraryA.c @@ -0,0 +1,14 @@ +#include + +DECLSPEC_EXPORT int FunctionA(int a, int b); +DECLSPEC_EXPORT int FunctionB(int a, int b); + +int FunctionA(int a, int b) +{ + return (a * b); /* multiply */ +} + +int FunctionB(int a, int b) +{ + return (a / b); /* divide */ +} diff --git a/winpr/libwinpr/library/test/TestLibraryB/CMakeLists.txt b/winpr/libwinpr/library/test/TestLibraryB/CMakeLists.txt new file mode 100644 index 0000000..860df8c --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryB/CMakeLists.txt @@ -0,0 +1,30 @@ +# WinPR: Windows Portable Runtime +# libwinpr-library cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "TestLibraryB") +set(MODULE_PREFIX "TEST_LIBRARY_B") + +set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} TestLibraryB.c) + +add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "") + +set_target_properties(${MODULE_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test/Extra") + diff --git a/winpr/libwinpr/library/test/TestLibraryB/TestLibraryB.c b/winpr/libwinpr/library/test/TestLibraryB/TestLibraryB.c new file mode 100644 index 0000000..eac586e --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryB/TestLibraryB.c @@ -0,0 +1,14 @@ +#include + +DECLSPEC_EXPORT int FunctionA(int a, int b); +DECLSPEC_EXPORT int FunctionB(int a, int b); + +int FunctionA(int a, int b) +{ + return (a + b); /* add */ +} + +int FunctionB(int a, int b) +{ + return (a - b); /* subtract */ +} diff --git a/winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c b/winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c new file mode 100644 index 0000000..714522a --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c @@ -0,0 +1,56 @@ + +#include +#include +#include +#include +#include +#include + +int TestLibraryGetModuleFileName(int argc, char* argv[]) +{ + char ModuleFileName[4096]; + DWORD len = 0; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + /* Test insufficient buffer size behaviour */ + SetLastError(ERROR_SUCCESS); + len = GetModuleFileNameA(NULL, ModuleFileName, 2); + if (len != 2) + { + printf("%s: GetModuleFileNameA unexpectedly returned %" PRIu32 " instead of 2\n", __func__, + len); + return -1; + } + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + printf("%s: Invalid last error value: 0x%08" PRIX32 + ". Expected 0x%08X (ERROR_INSUFFICIENT_BUFFER)\n", + __func__, GetLastError(), ERROR_INSUFFICIENT_BUFFER); + return -1; + } + + /* Test with real/sufficient buffer size */ + SetLastError(ERROR_SUCCESS); + len = GetModuleFileNameA(NULL, ModuleFileName, sizeof(ModuleFileName)); + if (len == 0) + { + printf("%s: GetModuleFileNameA failed with error 0x%08" PRIX32 "\n", __func__, + GetLastError()); + return -1; + } + if (len == sizeof(ModuleFileName)) + { + printf("%s: GetModuleFileNameA unexpectedly returned nSize\n", __func__); + return -1; + } + if (GetLastError() != ERROR_SUCCESS) + { + printf("%s: Invalid last error value: 0x%08" PRIX32 ". Expected 0x%08X (ERROR_SUCCESS)\n", + __func__, GetLastError(), ERROR_SUCCESS); + return -1; + } + + printf("GetModuleFileNameA: %s\n", ModuleFileName); + + return 0; +} diff --git a/winpr/libwinpr/library/test/TestLibraryGetProcAddress.c b/winpr/libwinpr/library/test/TestLibraryGetProcAddress.c new file mode 100644 index 0000000..f8f54a6 --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryGetProcAddress.c @@ -0,0 +1,89 @@ + +#include +#include +#include +#include +#include +#include + +typedef int (*TEST_AB_FN)(int a, int b); + +int TestLibraryGetProcAddress(int argc, char* argv[]) +{ + int a = 0; + int b = 0; + int c = 0; + HINSTANCE library = NULL; + TEST_AB_FN pFunctionA = NULL; + TEST_AB_FN pFunctionB = NULL; + LPCSTR SharedLibraryExtension = NULL; + CHAR LibraryPath[PATHCCH_MAX_CCH]; + PCHAR p = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + if (!GetModuleFileNameA(NULL, LibraryPath, PATHCCH_MAX_CCH)) + { + printf("%s: GetModuleFilenameA failed: 0x%08" PRIX32 "\n", __func__, GetLastError()); + return -1; + } + + /* PathCchRemoveFileSpec is not implemented in WinPR */ + + if (!(p = strrchr(LibraryPath, PathGetSeparatorA(PATH_STYLE_NATIVE)))) + { + printf("%s: Error identifying module directory path\n", __func__); + return -1; + } + + *p = 0; + NativePathCchAppendA(LibraryPath, PATHCCH_MAX_CCH, "TestLibraryA"); + SharedLibraryExtension = PathGetSharedLibraryExtensionA(PATH_SHARED_LIB_EXT_WITH_DOT); + NativePathCchAddExtensionA(LibraryPath, PATHCCH_MAX_CCH, SharedLibraryExtension); + printf("%s: Loading Library: '%s'\n", __func__, LibraryPath); + + if (!(library = LoadLibraryA(LibraryPath))) + { + printf("%s: LoadLibraryA failure: 0x%08" PRIX32 "\n", __func__, GetLastError()); + return -1; + } + + if (!(pFunctionA = (TEST_AB_FN)GetProcAddress(library, "FunctionA"))) + { + printf("%s: GetProcAddress failure (FunctionA)\n", __func__); + return -1; + } + + if (!(pFunctionB = (TEST_AB_FN)GetProcAddress(library, "FunctionB"))) + { + printf("%s: GetProcAddress failure (FunctionB)\n", __func__); + return -1; + } + + a = 2; + b = 3; + c = pFunctionA(a, b); /* LibraryA / FunctionA multiplies a and b */ + + if (c != (a * b)) + { + printf("%s: pFunctionA call failed\n", __func__); + return -1; + } + + a = 10; + b = 5; + c = pFunctionB(a, b); /* LibraryA / FunctionB divides a by b */ + + if (c != (a / b)) + { + printf("%s: pFunctionB call failed\n", __func__); + return -1; + } + + if (!FreeLibrary(library)) + { + printf("%s: FreeLibrary failure: 0x%08" PRIX32 "\n", __func__, GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/library/test/TestLibraryLoadLibrary.c b/winpr/libwinpr/library/test/TestLibraryLoadLibrary.c new file mode 100644 index 0000000..4bd6e7c --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryLoadLibrary.c @@ -0,0 +1,51 @@ + +#include +#include +#include +#include +#include +#include + +int TestLibraryLoadLibrary(int argc, char* argv[]) +{ + HINSTANCE library = NULL; + LPCSTR SharedLibraryExtension = NULL; + CHAR LibraryPath[PATHCCH_MAX_CCH]; + PCHAR p = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + if (!GetModuleFileNameA(NULL, LibraryPath, PATHCCH_MAX_CCH)) + { + printf("%s: GetModuleFilenameA failed: 0x%08" PRIX32 "\n", __func__, GetLastError()); + return -1; + } + + /* PathCchRemoveFileSpec is not implemented in WinPR */ + + if (!(p = strrchr(LibraryPath, PathGetSeparatorA(PATH_STYLE_NATIVE)))) + { + printf("%s: Error identifying module directory path\n", __func__); + return -1; + } + *p = 0; + + NativePathCchAppendA(LibraryPath, PATHCCH_MAX_CCH, "TestLibraryA"); + SharedLibraryExtension = PathGetSharedLibraryExtensionA(PATH_SHARED_LIB_EXT_WITH_DOT); + NativePathCchAddExtensionA(LibraryPath, PATHCCH_MAX_CCH, SharedLibraryExtension); + + printf("%s: Loading Library: '%s'\n", __func__, LibraryPath); + + if (!(library = LoadLibraryA(LibraryPath))) + { + printf("%s: LoadLibraryA failure: 0x%08" PRIX32 "\n", __func__, GetLastError()); + return -1; + } + + if (!FreeLibrary(library)) + { + printf("%s: FreeLibrary failure: 0x%08" PRIX32 "\n", __func__, GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/log.h b/winpr/libwinpr/log.h new file mode 100644 index 0000000..60158aa --- /dev/null +++ b/winpr/libwinpr/log.h @@ -0,0 +1,27 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Winpr log defines + * + * Copyright 2014 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_LOG_PRIV_H +#define WINPR_LOG_PRIV_H + +#include + +#define WINPR_TAG(tag) "com.winpr." tag + +#endif /* WINPR_UTILS_DEBUG_H */ diff --git a/winpr/libwinpr/memory/CMakeLists.txt b/winpr/libwinpr/memory/CMakeLists.txt new file mode 100644 index 0000000..5e28d3c --- /dev/null +++ b/winpr/libwinpr/memory/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-memory cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(memory.c memory.h) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/memory/ModuleOptions.cmake b/winpr/libwinpr/memory/ModuleOptions.cmake new file mode 100644 index 0000000..704ddca --- /dev/null +++ b/winpr/libwinpr/memory/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "2") +set(MINWIN_SHORT_NAME "memory") +set(MINWIN_LONG_NAME "Memory Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/memory/memory.c b/winpr/libwinpr/memory/memory.c new file mode 100644 index 0000000..83f4c07 --- /dev/null +++ b/winpr/libwinpr/memory/memory.c @@ -0,0 +1,128 @@ +/** + * WinPR: Windows Portable Runtime + * Memory Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +/** + * api-ms-win-core-memory-l1-1-2.dll: + * + * AllocateUserPhysicalPages + * AllocateUserPhysicalPagesNuma + * CreateFileMappingFromApp + * CreateFileMappingNumaW + * CreateFileMappingW + * CreateMemoryResourceNotification + * FlushViewOfFile + * FreeUserPhysicalPages + * GetLargePageMinimum + * GetMemoryErrorHandlingCapabilities + * GetProcessWorkingSetSizeEx + * GetSystemFileCacheSize + * GetWriteWatch + * MapUserPhysicalPages + * MapViewOfFile + * MapViewOfFileEx + * MapViewOfFileFromApp + * OpenFileMappingW + * PrefetchVirtualMemory + * QueryMemoryResourceNotification + * ReadProcessMemory + * RegisterBadMemoryNotification + * ResetWriteWatch + * SetProcessWorkingSetSizeEx + * SetSystemFileCacheSize + * UnmapViewOfFile + * UnmapViewOfFileEx + * UnregisterBadMemoryNotification + * VirtualAlloc + * VirtualAllocEx + * VirtualAllocExNuma + * VirtualFree + * VirtualFreeEx + * VirtualLock + * VirtualProtect + * VirtualProtectEx + * VirtualQuery + * VirtualQueryEx + * VirtualUnlock + * WriteProcessMemory + */ + +#ifndef _WIN32 + +#include "memory.h" + +HANDLE CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName) +{ + if (hFile != INVALID_HANDLE_VALUE) + { + return NULL; /* not yet implemented */ + } + + return NULL; +} + +HANDLE CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) +{ + return NULL; +} + +HANDLE OpenFileMappingA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) +{ + return NULL; +} + +HANDLE OpenFileMappingW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) +{ + return NULL; +} + +LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap) +{ + return NULL; +} + +LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress) +{ + return NULL; +} + +BOOL FlushViewOfFile(LPCVOID lpBaseAddress, SIZE_T dwNumberOfBytesToFlush) +{ + return TRUE; +} + +BOOL UnmapViewOfFile(LPCVOID lpBaseAddress) +{ + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/memory/memory.h b/winpr/libwinpr/memory/memory.h new file mode 100644 index 0000000..cc2492e --- /dev/null +++ b/winpr/libwinpr/memory/memory.h @@ -0,0 +1,30 @@ +/** + * WinPR: Windows Portable Runtime + * Memory Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_MEMORY_PRIVATE_H +#define WINPR_MEMORY_PRIVATE_H + +#ifndef _WIN32 + +#include +#include + +#endif + +#endif /* WINPR_MEMORY_PRIVATE_H */ diff --git a/winpr/libwinpr/memory/test/CMakeLists.txt b/winpr/libwinpr/memory/test/CMakeLists.txt new file mode 100644 index 0000000..d4fad51 --- /dev/null +++ b/winpr/libwinpr/memory/test/CMakeLists.txt @@ -0,0 +1,23 @@ + +set(MODULE_NAME "TestMemory") +set(MODULE_PREFIX "TEST_MEMORY") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestMemoryCreateFileMapping.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c b/winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c new file mode 100644 index 0000000..e6e7552 --- /dev/null +++ b/winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c @@ -0,0 +1,8 @@ + +#include +#include + +int TestMemoryCreateFileMapping(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/ncrypt/CMakeLists.txt b/winpr/libwinpr/ncrypt/CMakeLists.txt new file mode 100644 index 0000000..2989069 --- /dev/null +++ b/winpr/libwinpr/ncrypt/CMakeLists.txt @@ -0,0 +1,57 @@ +# WinPR: Windows Portable Runtime +# libwinpr-ncrypt cmake build script +# +# Copyright 2021 David Fort +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if (PKCS11_FOUND) + winpr_include_directory_add(${PKCS11_INCLUDE_DIR}) + + option(WITH_OPENSC_PKCS11_LINKED "Directly link opensc-pkcs11" OFF) + if (WITH_OPENSC_PKCS11_LINKED) + + # opensc-pkcs11 is installed without any library prefix. + # Disable this when checking for this library. + set(backup ${CMAKE_FIND_LIBRARY_PREFIXES}) + set(CMAKE_FIND_LIBRARY_PREFIXES "") + find_library(OPENSC_PKCS11 + NAMES opensc-pkcs11 + PATH_SUFFIXES pkcs11 + REQUIRED) + set(CMAKE_FIND_LIBRARY_PREFIXES ${backup}) + + winpr_definition_add(-DWITH_OPENSC_PKCS11_LINKED) + winpr_library_add_private(${OPENSC_PKCS11}) + winpr_library_add_private(${PKCS11_LIBRARY}) + endif() +endif() + +if (PKCS11_FOUND) + winpr_module_add( + ncrypt_pkcs11.c + ) +endif() + +winpr_module_add( + ncrypt.c + ncrypt.h +) + +if (WIN32) + winpr_library_add_public(ncrypt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/ncrypt/ModuleOptions.cmake b/winpr/libwinpr/ncrypt/ModuleOptions.cmake new file mode 100644 index 0000000..80729fc --- /dev/null +++ b/winpr/libwinpr/ncrypt/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "ncrypt") +set(MINWIN_LONG_NAME "NCrypt Functions") +set(MODULE_LIBRARY_NAME "ncrypt") + diff --git a/winpr/libwinpr/ncrypt/ncrypt.c b/winpr/libwinpr/ncrypt/ncrypt.c new file mode 100644 index 0000000..df9c5e1 --- /dev/null +++ b/winpr/libwinpr/ncrypt/ncrypt.c @@ -0,0 +1,347 @@ +/** + * WinPR: Windows Portable Runtime + * NCrypt library + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifndef _WIN32 + +#include +#include "../log.h" + +#include "ncrypt.h" + +#define TAG WINPR_TAG("ncrypt") + +const static char NCRYPT_MAGIC[6] = { 'N', 'C', 'R', 'Y', 'P', 'T' }; + +SECURITY_STATUS checkNCryptHandle(NCRYPT_HANDLE handle, NCryptHandleType matchType) +{ + if (!handle) + { + WLog_VRB(TAG, "invalid handle '%p'", handle); + return ERROR_INVALID_PARAMETER; + } + + const NCryptBaseHandle* base = (NCryptBaseHandle*)handle; + if (memcmp(base->magic, NCRYPT_MAGIC, ARRAYSIZE(NCRYPT_MAGIC)) != 0) + { + char magic1[ARRAYSIZE(NCRYPT_MAGIC) + 1] = { 0 }; + char magic2[ARRAYSIZE(NCRYPT_MAGIC) + 1] = { 0 }; + + memcpy(magic1, base->magic, ARRAYSIZE(NCRYPT_MAGIC)); + memcpy(magic2, NCRYPT_MAGIC, ARRAYSIZE(NCRYPT_MAGIC)); + + WLog_VRB(TAG, "handle '%p' invalid magic '%s' instead of '%s'", base, magic1, magic2); + return ERROR_INVALID_PARAMETER; + } + + switch (base->type) + { + case WINPR_NCRYPT_PROVIDER: + case WINPR_NCRYPT_KEY: + break; + default: + WLog_VRB(TAG, "handle '%p' invalid type %d", base, base->type); + return ERROR_INVALID_PARAMETER; + } + + if ((matchType != WINPR_NCRYPT_INVALID) && (base->type != matchType)) + { + WLog_VRB(TAG, "handle '%p' invalid type %d, expected %d", base, base->type, matchType); + return ERROR_INVALID_PARAMETER; + } + return ERROR_SUCCESS; +} + +void* ncrypt_new_handle(NCryptHandleType kind, size_t len, NCryptGetPropertyFn getProp, + NCryptReleaseFn dtor) +{ + NCryptBaseHandle* ret = calloc(1, len); + if (!ret) + return NULL; + + memcpy(ret->magic, NCRYPT_MAGIC, sizeof(ret->magic)); + ret->type = kind; + ret->getPropertyFn = getProp; + ret->releaseFn = dtor; + return ret; +} + +SECURITY_STATUS winpr_NCryptDefault_dtor(NCRYPT_HANDLE handle) +{ + NCryptBaseHandle* h = (NCryptBaseHandle*)handle; + WINPR_ASSERT(h); + + memset(h->magic, 0, sizeof(h->magic)); + h->type = WINPR_NCRYPT_INVALID; + h->releaseFn = NULL; + free(h); + return ERROR_SUCCESS; +} + +SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, + NCryptProviderName** ppProviderList, DWORD dwFlags) +{ + NCryptProviderName* ret = NULL; + size_t stringAllocSize = 0; +#ifdef WITH_PKCS11 + LPWSTR strPtr = NULL; + static const WCHAR emptyComment[] = { 0 }; + size_t copyAmount = 0; +#endif + + *wProviderCount = 0; + *ppProviderList = NULL; + +#ifdef WITH_PKCS11 + *wProviderCount += 1; + stringAllocSize += (_wcslen(MS_SCARD_PROV) + 1) * 2; + stringAllocSize += sizeof(emptyComment); +#endif + + if (!*wProviderCount) + return ERROR_SUCCESS; + + ret = malloc(*wProviderCount * sizeof(NCryptProviderName) + stringAllocSize); + if (!ret) + return NTE_NO_MEMORY; + +#ifdef WITH_PKCS11 + strPtr = (LPWSTR)(ret + *wProviderCount); + + ret->pszName = strPtr; + copyAmount = (_wcslen(MS_SCARD_PROV) + 1) * 2; + memcpy(strPtr, MS_SCARD_PROV, copyAmount); + strPtr += copyAmount / 2; + + ret->pszComment = strPtr; + copyAmount = sizeof(emptyComment); + memcpy(strPtr, emptyComment, copyAmount); + + *ppProviderList = ret; +#endif + + return ERROR_SUCCESS; +} + +SECURITY_STATUS NCryptOpenStorageProvider(NCRYPT_PROV_HANDLE* phProvider, LPCWSTR pszProviderName, + DWORD dwFlags) +{ + return winpr_NCryptOpenStorageProviderEx(phProvider, pszProviderName, dwFlags, NULL); +} + +SECURITY_STATUS winpr_NCryptOpenStorageProviderEx(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, DWORD dwFlags, + LPCSTR* modulePaths) +{ +#if defined(WITH_PKCS11) + if (pszProviderName && ((_wcscmp(pszProviderName, MS_SMART_CARD_KEY_STORAGE_PROVIDER) == 0) || + (_wcscmp(pszProviderName, MS_SCARD_PROV) == 0))) + return NCryptOpenP11StorageProviderEx(phProvider, pszProviderName, dwFlags, modulePaths); + + char buffer[128] = { 0 }; + ConvertWCharToUtf8(pszProviderName, buffer, sizeof(buffer)); + WLog_WARN(TAG, "provider '%s' not supported", buffer); + return ERROR_NOT_SUPPORTED; +#else + WLog_WARN(TAG, "rebuild with -DWITH_PKCS11=ON to enable smartcard logon support"); + return ERROR_NOT_SUPPORTED; +#endif +} + +SECURITY_STATUS NCryptEnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope, + NCryptKeyName** ppKeyName, PVOID* ppEnumState, DWORD dwFlags) +{ + SECURITY_STATUS ret = 0; + NCryptBaseProvider* provider = (NCryptBaseProvider*)hProvider; + + ret = checkNCryptHandle((NCRYPT_HANDLE)hProvider, WINPR_NCRYPT_PROVIDER); + if (ret != ERROR_SUCCESS) + return ret; + + return provider->enumKeysFn(hProvider, pszScope, ppKeyName, ppEnumState, dwFlags); +} + +SECURITY_STATUS NCryptOpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey, + LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, DWORD dwFlags) +{ + SECURITY_STATUS ret = 0; + NCryptBaseProvider* provider = (NCryptBaseProvider*)hProvider; + + ret = checkNCryptHandle((NCRYPT_HANDLE)hProvider, WINPR_NCRYPT_PROVIDER); + if (ret != ERROR_SUCCESS) + return ret; + if (!phKey || !pszKeyName) + return ERROR_INVALID_PARAMETER; + + return provider->openKeyFn(hProvider, phKey, pszKeyName, dwLegacyKeySpec, dwFlags); +} + +static NCryptKeyGetPropertyEnum propertyStringToEnum(LPCWSTR pszProperty) +{ + if (_wcscmp(pszProperty, NCRYPT_CERTIFICATE_PROPERTY) == 0) + { + return NCRYPT_PROPERTY_CERTIFICATE; + } + else if (_wcscmp(pszProperty, NCRYPT_READER_PROPERTY) == 0) + { + return NCRYPT_PROPERTY_READER; + } + else if (_wcscmp(pszProperty, NCRYPT_WINPR_SLOTID) == 0) + { + return NCRYPT_PROPERTY_SLOTID; + } + else if (_wcscmp(pszProperty, NCRYPT_NAME_PROPERTY) == 0) + { + return NCRYPT_PROPERTY_NAME; + } + + return NCRYPT_PROPERTY_UNKNOWN; +} + +SECURITY_STATUS NCryptGetProperty(NCRYPT_HANDLE hObject, LPCWSTR pszProperty, PBYTE pbOutput, + DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags) +{ + NCryptKeyGetPropertyEnum property = NCRYPT_PROPERTY_UNKNOWN; + NCryptBaseHandle* base = NULL; + + if (!hObject) + return ERROR_INVALID_PARAMETER; + + base = (NCryptBaseHandle*)hObject; + if (memcmp(base->magic, NCRYPT_MAGIC, 6) != 0) + return ERROR_INVALID_HANDLE; + + property = propertyStringToEnum(pszProperty); + if (property == NCRYPT_PROPERTY_UNKNOWN) + return ERROR_NOT_SUPPORTED; + + return base->getPropertyFn(hObject, property, pbOutput, cbOutput, pcbResult, dwFlags); +} + +SECURITY_STATUS NCryptFreeObject(NCRYPT_HANDLE hObject) +{ + NCryptBaseHandle* base = NULL; + SECURITY_STATUS ret = checkNCryptHandle((NCRYPT_HANDLE)hObject, WINPR_NCRYPT_INVALID); + if (ret != ERROR_SUCCESS) + return ret; + + base = (NCryptBaseHandle*)hObject; + if (base->releaseFn) + ret = base->releaseFn(hObject); + + return ret; +} + +SECURITY_STATUS NCryptFreeBuffer(PVOID pvInput) +{ + if (!pvInput) + return ERROR_INVALID_PARAMETER; + + free(pvInput); + return ERROR_SUCCESS; +} + +#else +SECURITY_STATUS winpr_NCryptOpenStorageProviderEx(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, DWORD dwFlags, + LPCSTR* modulePaths) +{ + typedef SECURITY_STATUS (*NCryptOpenStorageProviderFn)(NCRYPT_PROV_HANDLE * phProvider, + LPCWSTR pszProviderName, DWORD dwFlags); + NCryptOpenStorageProviderFn ncryptOpenStorageProviderFn; + SECURITY_STATUS ret; + HANDLE lib = LoadLibraryA("ncrypt.dll"); + if (!lib) + return NTE_PROV_DLL_NOT_FOUND; + + ncryptOpenStorageProviderFn = + (NCryptOpenStorageProviderFn)GetProcAddress(lib, "NCryptOpenStorageProvider"); + if (!ncryptOpenStorageProviderFn) + { + ret = NTE_PROV_DLL_NOT_FOUND; + goto out_free_lib; + } + + ret = ncryptOpenStorageProviderFn(phProvider, pszProviderName, dwFlags); + +out_free_lib: + FreeLibrary(lib); + return ret; +} +#endif /* _WIN32 */ + +const char* winpr_NCryptSecurityStatusError(SECURITY_STATUS status) +{ +#define NTE_CASE(S) \ + case (SECURITY_STATUS)S: \ + return #S + + switch (status) + { + NTE_CASE(ERROR_SUCCESS); + NTE_CASE(ERROR_INVALID_PARAMETER); + NTE_CASE(ERROR_INVALID_HANDLE); + NTE_CASE(ERROR_NOT_SUPPORTED); + + NTE_CASE(NTE_BAD_UID); + NTE_CASE(NTE_BAD_HASH); + NTE_CASE(NTE_BAD_KEY); + NTE_CASE(NTE_BAD_LEN); + NTE_CASE(NTE_BAD_DATA); + NTE_CASE(NTE_BAD_SIGNATURE); + NTE_CASE(NTE_BAD_VER); + NTE_CASE(NTE_BAD_ALGID); + NTE_CASE(NTE_BAD_FLAGS); + NTE_CASE(NTE_BAD_TYPE); + NTE_CASE(NTE_BAD_KEY_STATE); + NTE_CASE(NTE_BAD_HASH_STATE); + NTE_CASE(NTE_NO_KEY); + NTE_CASE(NTE_NO_MEMORY); + NTE_CASE(NTE_EXISTS); + NTE_CASE(NTE_PERM); + NTE_CASE(NTE_NOT_FOUND); + NTE_CASE(NTE_DOUBLE_ENCRYPT); + NTE_CASE(NTE_BAD_PROVIDER); + NTE_CASE(NTE_BAD_PROV_TYPE); + NTE_CASE(NTE_BAD_PUBLIC_KEY); + NTE_CASE(NTE_BAD_KEYSET); + NTE_CASE(NTE_PROV_TYPE_NOT_DEF); + NTE_CASE(NTE_PROV_TYPE_ENTRY_BAD); + NTE_CASE(NTE_KEYSET_NOT_DEF); + NTE_CASE(NTE_KEYSET_ENTRY_BAD); + NTE_CASE(NTE_PROV_TYPE_NO_MATCH); + NTE_CASE(NTE_SIGNATURE_FILE_BAD); + NTE_CASE(NTE_PROVIDER_DLL_FAIL); + NTE_CASE(NTE_PROV_DLL_NOT_FOUND); + NTE_CASE(NTE_BAD_KEYSET_PARAM); + NTE_CASE(NTE_FAIL); + NTE_CASE(NTE_SYS_ERR); + NTE_CASE(NTE_SILENT_CONTEXT); + NTE_CASE(NTE_TOKEN_KEYSET_STORAGE_FULL); + NTE_CASE(NTE_TEMPORARY_PROFILE); + NTE_CASE(NTE_FIXEDPARAMETER); + + default: + return ""; + } + +#undef NTE_CASE +} diff --git a/winpr/libwinpr/ncrypt/ncrypt.h b/winpr/libwinpr/ncrypt/ncrypt.h new file mode 100644 index 0000000..222f655 --- /dev/null +++ b/winpr/libwinpr/ncrypt/ncrypt.h @@ -0,0 +1,94 @@ +/** + * WinPR: Windows Portable Runtime + * NCrypt library + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_LIBWINPR_NCRYPT_NCRYPT_H_ +#define WINPR_LIBWINPR_NCRYPT_NCRYPT_H_ + +#include + +#include +#include +#include +#include +#include + +/** @brief type of ncrypt object */ +typedef enum +{ + WINPR_NCRYPT_INVALID, + WINPR_NCRYPT_PROVIDER, + WINPR_NCRYPT_KEY +} NCryptHandleType; + +/** @brief dtor function for ncrypt object */ +typedef SECURITY_STATUS (*NCryptReleaseFn)(NCRYPT_HANDLE handle); + +/** @brief an enum for the kind of property to retrieve */ +typedef enum +{ + NCRYPT_PROPERTY_CERTIFICATE, + NCRYPT_PROPERTY_READER, + NCRYPT_PROPERTY_SLOTID, + NCRYPT_PROPERTY_NAME, + NCRYPT_PROPERTY_UNKNOWN +} NCryptKeyGetPropertyEnum; + +typedef SECURITY_STATUS (*NCryptGetPropertyFn)(NCRYPT_HANDLE hObject, + NCryptKeyGetPropertyEnum property, PBYTE pbOutput, + DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags); + +/** @brief common ncrypt handle items */ +typedef struct +{ + char magic[6]; + NCryptHandleType type; + NCryptGetPropertyFn getPropertyFn; + NCryptReleaseFn releaseFn; +} NCryptBaseHandle; + +typedef SECURITY_STATUS (*NCryptEnumKeysFn)(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope, + NCryptKeyName** ppKeyName, PVOID* ppEnumState, + DWORD dwFlags); +typedef SECURITY_STATUS (*NCryptOpenKeyFn)(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey, + LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, + DWORD dwFlags); + +/** @brief common ncrypt provider items */ +typedef struct +{ + NCryptBaseHandle baseHandle; + + NCryptEnumKeysFn enumKeysFn; + NCryptOpenKeyFn openKeyFn; +} NCryptBaseProvider; + +SECURITY_STATUS checkNCryptHandle(NCRYPT_HANDLE handle, NCryptHandleType matchType); + +SECURITY_STATUS winpr_NCryptDefault_dtor(NCRYPT_HANDLE handle); + +void* ncrypt_new_handle(NCryptHandleType kind, size_t len, NCryptGetPropertyFn getProp, + NCryptReleaseFn dtor); + +#if defined(WITH_PKCS11) +SECURITY_STATUS NCryptOpenP11StorageProviderEx(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, DWORD dwFlags, + LPCSTR* modulePaths); +#endif + +#endif /* WINPR_LIBWINPR_NCRYPT_NCRYPT_H_ */ diff --git a/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c b/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c new file mode 100644 index 0000000..08e6274 --- /dev/null +++ b/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c @@ -0,0 +1,1297 @@ +/** + * WinPR: Windows Portable Runtime + * NCrypt pkcs11 provider + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "../log.h" +#include "ncrypt.h" + +#define TAG WINPR_TAG("ncryptp11") + +#define MAX_SLOTS 64 +#define MAX_KEYS 64 +#define MAX_KEYS_PER_SLOT 64 + +/** @brief ncrypt provider handle */ +typedef struct +{ + NCryptBaseProvider baseProvider; + + HANDLE library; + CK_FUNCTION_LIST_PTR p11; +} NCryptP11ProviderHandle; + +/** @brief a handle returned by NCryptOpenKey */ +typedef struct +{ + NCryptBaseHandle base; + NCryptP11ProviderHandle* provider; + CK_SLOT_ID slotId; + CK_BYTE keyCertId[64]; + CK_ULONG keyCertIdLen; +} NCryptP11KeyHandle; + +typedef struct +{ + CK_SLOT_ID slotId; + CK_SLOT_INFO slotInfo; + CK_KEY_TYPE keyType; + CK_CHAR keyLabel[256]; + CK_ULONG idLen; + CK_BYTE id[64]; +} NCryptKeyEnum; + +typedef struct +{ + CK_ULONG nslots; + CK_SLOT_ID slots[MAX_SLOTS]; + CK_ULONG nKeys; + NCryptKeyEnum keys[MAX_KEYS]; + CK_ULONG keyIndex; +} P11EnumKeysState; + +typedef struct +{ + const char* label; + BYTE tag[3]; +} piv_cert_tags_t; +static const piv_cert_tags_t piv_cert_tags[] = { + { "Certificate for PIV Authentication", "\x5F\xC1\x05" }, + { "Certificate for Digital Signature", "\x5F\xC1\x0A" }, + { "Certificate for Key Management", "\x5F\xC1\x0B" }, + { "Certificate for Card Authentication", "\x5F\xC1\x01" }, +}; + +static const BYTE APDU_PIV_SELECT_AID[] = { 0x00, 0xA4, 0x04, 0x00, 0x09, 0xA0, 0x00, 0x00, + 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00 }; +static const BYTE APDU_PIV_GET_CHUID[] = { 0x00, 0xCB, 0x3F, 0xFF, 0x05, 0x5C, + 0x03, 0x5F, 0xC1, 0x02, 0x00 }; +#define PIV_CONTAINER_NAME_LEN 36 + +static CK_OBJECT_CLASS object_class_public_key = CKO_PUBLIC_KEY; +static CK_BBOOL object_verify = CK_TRUE; +static CK_KEY_TYPE object_ktype_rsa = CKK_RSA; + +static CK_ATTRIBUTE public_key_filter[] = { + { CKA_CLASS, &object_class_public_key, sizeof(object_class_public_key) }, + { CKA_VERIFY, &object_verify, sizeof(object_verify) }, + { CKA_KEY_TYPE, &object_ktype_rsa, sizeof(object_ktype_rsa) } +}; + +static SECURITY_STATUS NCryptP11StorageProvider_dtor(NCRYPT_HANDLE handle) +{ + NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)handle; + CK_RV rv = 0; + + WINPR_ASSERT(provider); + rv = provider->p11->C_Finalize(NULL); + if (rv != CKR_OK) + { + } + + if (provider->library) + FreeLibrary(provider->library); + + return winpr_NCryptDefault_dtor(handle); +} + +static void fix_padded_string(char* str, size_t maxlen) +{ + char* ptr = str + maxlen - 1; + + while (ptr > str && *ptr == ' ') + ptr--; + ptr++; + *ptr = 0; +} + +static BOOL attributes_have_unallocated_buffers(CK_ATTRIBUTE_PTR attributes, CK_ULONG count) +{ + for (CK_ULONG i = 0; i < count; i++) + { + if (!attributes[i].pValue && (attributes[i].ulValueLen != CK_UNAVAILABLE_INFORMATION)) + return TRUE; + } + + return FALSE; +} + +static BOOL attribute_allocate_attribute_array(CK_ATTRIBUTE_PTR attribute) +{ + attribute->pValue = calloc(attribute->ulValueLen, sizeof(void*)); + return !!attribute->pValue; +} + +static BOOL attribute_allocate_ulong_array(CK_ATTRIBUTE_PTR attribute) +{ + attribute->pValue = calloc(attribute->ulValueLen, sizeof(CK_ULONG)); + return !!attribute->pValue; +} + +static BOOL attribute_allocate_buffer(CK_ATTRIBUTE_PTR attribute) +{ + attribute->pValue = calloc(attribute->ulValueLen, 1); + return !!attribute->pValue; +} + +static BOOL attributes_allocate_buffers(CK_ATTRIBUTE_PTR attributes, CK_ULONG count) +{ + BOOL ret = TRUE; + + for (CK_ULONG i = 0; i < count; i++) + { + if (attributes[i].pValue || (attributes[i].ulValueLen == CK_UNAVAILABLE_INFORMATION)) + continue; + + switch (attributes[i].type) + { + case CKA_WRAP_TEMPLATE: + case CKA_UNWRAP_TEMPLATE: + ret &= attribute_allocate_attribute_array(&attributes[i]); + break; + + case CKA_ALLOWED_MECHANISMS: + ret &= attribute_allocate_ulong_array(&attributes[i]); + break; + + default: + ret &= attribute_allocate_buffer(&attributes[i]); + break; + } + } + + return ret; +} + +static CK_RV object_load_attributes(NCryptP11ProviderHandle* provider, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR attributes, + CK_ULONG count) +{ + CK_RV rv = 0; + + WINPR_ASSERT(provider); + WINPR_ASSERT(provider->p11); + WINPR_ASSERT(provider->p11->C_GetAttributeValue); + + rv = provider->p11->C_GetAttributeValue(session, object, attributes, count); + + switch (rv) + { + case CKR_OK: + if (!attributes_have_unallocated_buffers(attributes, count)) + return rv; + /* fallthrough */ + WINPR_FALLTHROUGH + case CKR_ATTRIBUTE_SENSITIVE: + case CKR_ATTRIBUTE_TYPE_INVALID: + case CKR_BUFFER_TOO_SMALL: + /* attributes need some buffers for the result value */ + if (!attributes_allocate_buffers(attributes, count)) + return CKR_HOST_MEMORY; + + rv = provider->p11->C_GetAttributeValue(session, object, attributes, count); + break; + default: + return rv; + } + + switch (rv) + { + case CKR_ATTRIBUTE_SENSITIVE: + case CKR_ATTRIBUTE_TYPE_INVALID: + case CKR_BUFFER_TOO_SMALL: + WLog_ERR(TAG, "C_GetAttributeValue return %d even after buffer allocation", rv); + break; + } + return rv; +} + +static const char* CK_RV_error_string(CK_RV rv) +{ + static char generic_buffer[200]; +#define ERR_ENTRY(X) \ + case X: \ + return #X + + switch (rv) + { + ERR_ENTRY(CKR_OK); + ERR_ENTRY(CKR_CANCEL); + ERR_ENTRY(CKR_HOST_MEMORY); + ERR_ENTRY(CKR_SLOT_ID_INVALID); + ERR_ENTRY(CKR_GENERAL_ERROR); + ERR_ENTRY(CKR_FUNCTION_FAILED); + ERR_ENTRY(CKR_ARGUMENTS_BAD); + ERR_ENTRY(CKR_NO_EVENT); + ERR_ENTRY(CKR_NEED_TO_CREATE_THREADS); + ERR_ENTRY(CKR_CANT_LOCK); + ERR_ENTRY(CKR_ATTRIBUTE_READ_ONLY); + ERR_ENTRY(CKR_ATTRIBUTE_SENSITIVE); + ERR_ENTRY(CKR_ATTRIBUTE_TYPE_INVALID); + ERR_ENTRY(CKR_ATTRIBUTE_VALUE_INVALID); + ERR_ENTRY(CKR_DATA_INVALID); + ERR_ENTRY(CKR_DATA_LEN_RANGE); + ERR_ENTRY(CKR_DEVICE_ERROR); + ERR_ENTRY(CKR_DEVICE_MEMORY); + ERR_ENTRY(CKR_DEVICE_REMOVED); + ERR_ENTRY(CKR_ENCRYPTED_DATA_INVALID); + ERR_ENTRY(CKR_ENCRYPTED_DATA_LEN_RANGE); + ERR_ENTRY(CKR_FUNCTION_CANCELED); + ERR_ENTRY(CKR_FUNCTION_NOT_PARALLEL); + ERR_ENTRY(CKR_FUNCTION_NOT_SUPPORTED); + ERR_ENTRY(CKR_KEY_HANDLE_INVALID); + ERR_ENTRY(CKR_KEY_SIZE_RANGE); + ERR_ENTRY(CKR_KEY_TYPE_INCONSISTENT); + ERR_ENTRY(CKR_KEY_NOT_NEEDED); + ERR_ENTRY(CKR_KEY_CHANGED); + ERR_ENTRY(CKR_KEY_NEEDED); + ERR_ENTRY(CKR_KEY_INDIGESTIBLE); + ERR_ENTRY(CKR_KEY_FUNCTION_NOT_PERMITTED); + ERR_ENTRY(CKR_KEY_NOT_WRAPPABLE); + ERR_ENTRY(CKR_KEY_UNEXTRACTABLE); + ERR_ENTRY(CKR_MECHANISM_INVALID); + ERR_ENTRY(CKR_MECHANISM_PARAM_INVALID); + ERR_ENTRY(CKR_OBJECT_HANDLE_INVALID); + ERR_ENTRY(CKR_OPERATION_ACTIVE); + ERR_ENTRY(CKR_OPERATION_NOT_INITIALIZED); + ERR_ENTRY(CKR_PIN_INCORRECT); + ERR_ENTRY(CKR_PIN_INVALID); + ERR_ENTRY(CKR_PIN_LEN_RANGE); + ERR_ENTRY(CKR_PIN_EXPIRED); + ERR_ENTRY(CKR_PIN_LOCKED); + ERR_ENTRY(CKR_SESSION_CLOSED); + ERR_ENTRY(CKR_SESSION_COUNT); + ERR_ENTRY(CKR_SESSION_HANDLE_INVALID); + ERR_ENTRY(CKR_SESSION_PARALLEL_NOT_SUPPORTED); + ERR_ENTRY(CKR_SESSION_READ_ONLY); + ERR_ENTRY(CKR_SESSION_EXISTS); + ERR_ENTRY(CKR_SESSION_READ_ONLY_EXISTS); + ERR_ENTRY(CKR_SESSION_READ_WRITE_SO_EXISTS); + ERR_ENTRY(CKR_SIGNATURE_INVALID); + ERR_ENTRY(CKR_SIGNATURE_LEN_RANGE); + ERR_ENTRY(CKR_TEMPLATE_INCOMPLETE); + ERR_ENTRY(CKR_TEMPLATE_INCONSISTENT); + ERR_ENTRY(CKR_TOKEN_NOT_PRESENT); + ERR_ENTRY(CKR_TOKEN_NOT_RECOGNIZED); + ERR_ENTRY(CKR_TOKEN_WRITE_PROTECTED); + ERR_ENTRY(CKR_UNWRAPPING_KEY_HANDLE_INVALID); + ERR_ENTRY(CKR_UNWRAPPING_KEY_SIZE_RANGE); + ERR_ENTRY(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT); + ERR_ENTRY(CKR_USER_ALREADY_LOGGED_IN); + ERR_ENTRY(CKR_USER_NOT_LOGGED_IN); + ERR_ENTRY(CKR_USER_PIN_NOT_INITIALIZED); + ERR_ENTRY(CKR_USER_TYPE_INVALID); + ERR_ENTRY(CKR_USER_ANOTHER_ALREADY_LOGGED_IN); + ERR_ENTRY(CKR_USER_TOO_MANY_TYPES); + ERR_ENTRY(CKR_WRAPPED_KEY_INVALID); + ERR_ENTRY(CKR_WRAPPED_KEY_LEN_RANGE); + ERR_ENTRY(CKR_WRAPPING_KEY_HANDLE_INVALID); + ERR_ENTRY(CKR_WRAPPING_KEY_SIZE_RANGE); + ERR_ENTRY(CKR_WRAPPING_KEY_TYPE_INCONSISTENT); + ERR_ENTRY(CKR_RANDOM_SEED_NOT_SUPPORTED); + ERR_ENTRY(CKR_RANDOM_NO_RNG); + ERR_ENTRY(CKR_DOMAIN_PARAMS_INVALID); + ERR_ENTRY(CKR_BUFFER_TOO_SMALL); + ERR_ENTRY(CKR_SAVED_STATE_INVALID); + ERR_ENTRY(CKR_INFORMATION_SENSITIVE); + ERR_ENTRY(CKR_STATE_UNSAVEABLE); + ERR_ENTRY(CKR_CRYPTOKI_NOT_INITIALIZED); + ERR_ENTRY(CKR_CRYPTOKI_ALREADY_INITIALIZED); + ERR_ENTRY(CKR_MUTEX_BAD); + ERR_ENTRY(CKR_MUTEX_NOT_LOCKED); + ERR_ENTRY(CKR_FUNCTION_REJECTED); + default: + snprintf(generic_buffer, sizeof(generic_buffer), "unknown 0x%lx", rv); + return generic_buffer; + } +#undef ERR_ENTRY +} + +static SECURITY_STATUS collect_keys(NCryptP11ProviderHandle* provider, P11EnumKeysState* state) +{ + CK_OBJECT_HANDLE slotObjects[MAX_KEYS_PER_SLOT] = { 0 }; + const char* step = NULL; + + WINPR_ASSERT(provider); + + CK_FUNCTION_LIST_PTR p11 = provider->p11; + WINPR_ASSERT(p11); + + WLog_DBG(TAG, "checking %" PRIu32 " slots for valid keys...", state->nslots); + state->nKeys = 0; + for (CK_ULONG i = 0; i < state->nslots; i++) + { + CK_SESSION_HANDLE session = (CK_SESSION_HANDLE)NULL; + CK_SLOT_INFO slotInfo = { 0 }; + CK_TOKEN_INFO tokenInfo = { 0 }; + + WINPR_ASSERT(p11->C_GetSlotInfo); + CK_RV rv = p11->C_GetSlotInfo(state->slots[i], &slotInfo); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to retrieve information for slot #%d(%d)", i, state->slots[i]); + continue; + } + + fix_padded_string((char*)slotInfo.slotDescription, sizeof(slotInfo.slotDescription)); + WLog_DBG(TAG, "collecting keys for slot #%d(%lu) descr='%s' flags=0x%x", i, state->slots[i], + slotInfo.slotDescription, slotInfo.flags); + + /* this is a safety guard as we're supposed to have listed only readers with tokens in them + */ + if (!(slotInfo.flags & CKF_TOKEN_PRESENT)) + { + WLog_INFO(TAG, "token not present for slot #%d(%d)", i, state->slots[i]); + continue; + } + + WINPR_ASSERT(p11->C_GetTokenInfo); + rv = p11->C_GetTokenInfo(state->slots[i], &tokenInfo); + if (rv != CKR_OK) + { + WLog_INFO(TAG, "unable to retrieve token info for slot #%d(%d)", i, state->slots[i]); + } + else + { + fix_padded_string((char*)tokenInfo.label, sizeof(tokenInfo.label)); + WLog_DBG(TAG, "token, label='%s' flags=0x%x", tokenInfo.label, tokenInfo.flags); + } + + WINPR_ASSERT(p11->C_OpenSession); + rv = p11->C_OpenSession(state->slots[i], CKF_SERIAL_SESSION, NULL, NULL, &session); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to openSession for slot #%d(%d), session=%p rv=%s", i, + state->slots[i], session, CK_RV_error_string(rv)); + continue; + } + + WINPR_ASSERT(p11->C_FindObjectsInit); + rv = p11->C_FindObjectsInit(session, public_key_filter, ARRAYSIZE(public_key_filter)); + if (rv != CKR_OK) + { + // TODO: shall it be fatal ? + WLog_ERR(TAG, "unable to initiate search for slot #%d(%d), rv=%s", i, state->slots[i], + CK_RV_error_string(rv)); + step = "C_FindObjectsInit"; + goto cleanup_FindObjectsInit; + } + + CK_ULONG nslotObjects = 0; + WINPR_ASSERT(p11->C_FindObjects); + rv = p11->C_FindObjects(session, &slotObjects[0], ARRAYSIZE(slotObjects), &nslotObjects); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to findObjects for slot #%d(%d), rv=%s", i, state->slots[i], + CK_RV_error_string(rv)); + step = "C_FindObjects"; + goto cleanup_FindObjects; + } + + WLog_DBG(TAG, "slot has %d objects", nslotObjects); + for (CK_ULONG j = 0; j < nslotObjects; j++) + { + NCryptKeyEnum* key = &state->keys[state->nKeys]; + CK_OBJECT_CLASS dataClass = CKO_PUBLIC_KEY; + CK_ATTRIBUTE key_or_certAttrs[] = { + { CKA_ID, &key->id, sizeof(key->id) }, + { CKA_CLASS, &dataClass, sizeof(dataClass) }, + { CKA_LABEL, &key->keyLabel, sizeof(key->keyLabel) }, + { CKA_KEY_TYPE, &key->keyType, sizeof(key->keyType) } + }; + + rv = object_load_attributes(provider, session, slotObjects[j], key_or_certAttrs, + ARRAYSIZE(key_or_certAttrs)); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "error getting attributes, rv=%s", CK_RV_error_string(rv)); + continue; + } + + key->idLen = key_or_certAttrs[0].ulValueLen; + key->slotId = state->slots[i]; + key->slotInfo = slotInfo; + state->nKeys++; + } + + cleanup_FindObjects: + WINPR_ASSERT(p11->C_FindObjectsFinal); + rv = p11->C_FindObjectsFinal(session); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "error during C_FindObjectsFinal for slot #%d(%d) (errorStep=%s), rv=%s", + i, state->slots[i], step, CK_RV_error_string(rv)); + } + cleanup_FindObjectsInit: + WINPR_ASSERT(p11->C_CloseSession); + rv = p11->C_CloseSession(session); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "error closing session for slot #%d(%d) (errorStep=%s), rv=%s", i, + state->slots[i], step, CK_RV_error_string(rv)); + } + } + + return ERROR_SUCCESS; +} + +static BOOL convertKeyType(CK_KEY_TYPE k, LPWSTR dest, DWORD len, DWORD* outlen) +{ + DWORD retLen = 0; + const WCHAR* r = NULL; + +#define ALGO_CASE(V, S) \ + case V: \ + r = S; \ + break + switch (k) + { + ALGO_CASE(CKK_RSA, BCRYPT_RSA_ALGORITHM); + ALGO_CASE(CKK_DSA, BCRYPT_DSA_ALGORITHM); + ALGO_CASE(CKK_DH, BCRYPT_DH_ALGORITHM); + ALGO_CASE(CKK_ECDSA, BCRYPT_ECDSA_ALGORITHM); + ALGO_CASE(CKK_RC2, BCRYPT_RC2_ALGORITHM); + ALGO_CASE(CKK_RC4, BCRYPT_RC4_ALGORITHM); + ALGO_CASE(CKK_DES, BCRYPT_DES_ALGORITHM); + ALGO_CASE(CKK_DES3, BCRYPT_3DES_ALGORITHM); + case CKK_DES2: + case CKK_X9_42_DH: + case CKK_KEA: + case CKK_GENERIC_SECRET: + case CKK_CAST: + case CKK_CAST3: + case CKK_CAST128: + case CKK_RC5: + case CKK_IDEA: + case CKK_SKIPJACK: + case CKK_BATON: + case CKK_JUNIPER: + case CKK_CDMF: + case CKK_AES: + case CKK_BLOWFISH: + case CKK_TWOFISH: + default: + break; + } +#undef ALGO_CASE + + retLen = _wcslen(r); + if (outlen) + *outlen = retLen; + + if (!r) + { + if (dest && len > 0) + dest[0] = 0; + return FALSE; + } + else + { + if (retLen + 1 < len) + { + WLog_ERR(TAG, "target buffer is too small for algo name"); + return FALSE; + } + + if (dest) + { + memcpy(dest, r, retLen * 2); + dest[retLen] = 0; + } + } + + return TRUE; +} + +static void wprintKeyName(LPWSTR str, CK_SLOT_ID slotId, CK_BYTE* id, CK_ULONG idLen) +{ + char asciiName[128] = { 0 }; + char* ptr = asciiName; + const CK_BYTE* bytePtr = NULL; + + *ptr = '\\'; + ptr++; + + bytePtr = ((CK_BYTE*)&slotId); + for (CK_ULONG i = 0; i < sizeof(slotId); i++, bytePtr++, ptr += 2) + snprintf(ptr, 3, "%.2x", *bytePtr); + + *ptr = '\\'; + ptr++; + + for (CK_ULONG i = 0; i < idLen; i++, id++, ptr += 2) + snprintf(ptr, 3, "%.2x", *id); + + ConvertUtf8NToWChar(asciiName, ARRAYSIZE(asciiName), str, + strnlen(asciiName, ARRAYSIZE(asciiName)) + 1); +} + +static size_t parseHex(const char* str, const char* end, CK_BYTE* target) +{ + int ret = 0; + + for (; str != end && *str; str++, ret++, target++) + { + CK_BYTE v = 0; + if (*str <= '9' && *str >= '0') + { + v = (*str - '0'); + } + else if (*str <= 'f' && *str >= 'a') + { + v = (10 + *str - 'a'); + } + else if (*str <= 'F' && *str >= 'A') + { + v |= (10 + *str - 'A'); + } + else + { + return 0; + } + v <<= 4; + str++; + + if (!*str || str == end) + return 0; + + if (*str <= '9' && *str >= '0') + { + v |= (*str - '0'); + } + else if (*str <= 'f' && *str >= 'a') + { + v |= (10 + *str - 'a'); + } + else if (*str <= 'F' && *str >= 'A') + { + v |= (10 + *str - 'A'); + } + else + { + return 0; + } + + *target = v; + } + return ret; +} + +static SECURITY_STATUS parseKeyName(LPCWSTR pszKeyName, CK_SLOT_ID* slotId, CK_BYTE* id, + CK_ULONG* idLen) +{ + char asciiKeyName[128] = { 0 }; + char* pos = NULL; + + if (ConvertWCharToUtf8(pszKeyName, asciiKeyName, ARRAYSIZE(asciiKeyName)) < 0) + return NTE_BAD_KEY; + + if (*asciiKeyName != '\\') + return NTE_BAD_KEY; + + pos = strchr(&asciiKeyName[1], '\\'); + if (!pos) + return NTE_BAD_KEY; + + if ((size_t)(pos - &asciiKeyName[1]) > sizeof(CK_SLOT_ID) * 2ull) + return NTE_BAD_KEY; + + *slotId = (CK_SLOT_ID)0; + if (parseHex(&asciiKeyName[1], pos, (CK_BYTE*)slotId) != sizeof(CK_SLOT_ID)) + return NTE_BAD_KEY; + + *idLen = parseHex(pos + 1, NULL, id); + if (!*idLen) + return NTE_BAD_KEY; + + return ERROR_SUCCESS; +} + +static SECURITY_STATUS NCryptP11EnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR pszScope, + NCryptKeyName** ppKeyName, PVOID* ppEnumState, + DWORD dwFlags) +{ + NCryptP11ProviderHandle* provider = (NCryptP11ProviderHandle*)hProvider; + P11EnumKeysState* state = (P11EnumKeysState*)*ppEnumState; + CK_RV rv = { 0 }; + CK_SLOT_ID currentSlot = { 0 }; + CK_SESSION_HANDLE currentSession = (CK_SESSION_HANDLE)NULL; + char slotFilterBuffer[65] = { 0 }; + char* slotFilter = NULL; + size_t slotFilterLen = 0; + + SECURITY_STATUS ret = checkNCryptHandle((NCRYPT_HANDLE)hProvider, WINPR_NCRYPT_PROVIDER); + if (ret != ERROR_SUCCESS) + return ret; + + if (pszScope) + { + /* + * check whether pszScope is of the form \\.\\ for filtering by + * card reader + */ + char asciiScope[128 + 6 + 1] = { 0 }; + size_t asciiScopeLen = 0; + + if (ConvertWCharToUtf8(pszScope, asciiScope, ARRAYSIZE(asciiScope) - 1) < 0) + { + WLog_WARN(TAG, "Invalid scope"); + return NTE_INVALID_PARAMETER; + } + + if (strstr(asciiScope, "\\\\.\\") != asciiScope) + { + WLog_WARN(TAG, "Invalid scope '%s'", asciiScope); + return NTE_INVALID_PARAMETER; + } + + asciiScopeLen = strnlen(asciiScope, ARRAYSIZE(asciiScope)); + if ((asciiScopeLen < 1) || (asciiScope[asciiScopeLen - 1] != '\\')) + { + WLog_WARN(TAG, "Invalid scope '%s'", asciiScope); + return NTE_INVALID_PARAMETER; + } + + asciiScope[asciiScopeLen - 1] = 0; + + strncpy(slotFilterBuffer, &asciiScope[4], sizeof(slotFilterBuffer)); + slotFilter = slotFilterBuffer; + slotFilterLen = asciiScopeLen - 5; + } + + if (!state) + { + state = (P11EnumKeysState*)calloc(1, sizeof(*state)); + if (!state) + return NTE_NO_MEMORY; + + WINPR_ASSERT(provider->p11->C_GetSlotList); + rv = provider->p11->C_GetSlotList(CK_TRUE, NULL, &state->nslots); + if (rv != CKR_OK) + { + /* TODO: perhaps convert rv to NTE_*** errors */ + WLog_WARN(TAG, "C_GetSlotList failed with %u", rv); + return NTE_FAIL; + } + + if (state->nslots > MAX_SLOTS) + state->nslots = MAX_SLOTS; + + rv = provider->p11->C_GetSlotList(CK_TRUE, state->slots, &state->nslots); + if (rv != CKR_OK) + { + free(state); + /* TODO: perhaps convert rv to NTE_*** errors */ + WLog_WARN(TAG, "C_GetSlotList failed with %u", rv); + return NTE_FAIL; + } + + ret = collect_keys(provider, state); + if (ret != ERROR_SUCCESS) + { + free(state); + return ret; + } + + *ppEnumState = state; + } + + for (; state->keyIndex < state->nKeys; state->keyIndex++) + { + NCryptKeyName* keyName = NULL; + NCryptKeyEnum* key = &state->keys[state->keyIndex]; + CK_OBJECT_CLASS oclass = CKO_CERTIFICATE; + CK_CERTIFICATE_TYPE ctype = CKC_X_509; + CK_ATTRIBUTE certificateFilter[] = { { CKA_CLASS, &oclass, sizeof(oclass) }, + { CKA_CERTIFICATE_TYPE, &ctype, sizeof(ctype) }, + { CKA_ID, key->id, key->idLen } }; + CK_ULONG ncertObjects = 0; + CK_OBJECT_HANDLE certObject = 0; + + /* check the reader filter if any */ + if (slotFilter && memcmp(key->slotInfo.slotDescription, slotFilter, slotFilterLen) != 0) + continue; + + if (!currentSession || (currentSlot != key->slotId)) + { + /* if the current session doesn't match the current key's slot, open a new one + */ + if (currentSession) + { + WINPR_ASSERT(provider->p11->C_CloseSession); + rv = provider->p11->C_CloseSession(currentSession); + currentSession = (CK_SESSION_HANDLE)NULL; + } + + WINPR_ASSERT(provider->p11->C_OpenSession); + rv = provider->p11->C_OpenSession(key->slotId, CKF_SERIAL_SESSION, NULL, NULL, + ¤tSession); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to openSession for slot %d", key->slotId); + continue; + } + currentSlot = key->slotId; + } + + /* look if we can find a certificate that matches the key's id */ + WINPR_ASSERT(provider->p11->C_FindObjectsInit); + rv = provider->p11->C_FindObjectsInit(currentSession, certificateFilter, + ARRAYSIZE(certificateFilter)); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to initiate search for slot %d", key->slotId); + continue; + } + + WINPR_ASSERT(provider->p11->C_FindObjects); + rv = provider->p11->C_FindObjects(currentSession, &certObject, 1, &ncertObjects); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to findObjects for slot %d", currentSlot); + goto cleanup_FindObjects; + } + + if (ncertObjects) + { + /* sizeof keyName struct + "\\" + keyName->pszAlgid */ + DWORD algoSz = 0; + size_t KEYNAME_SZ = + (1 + (sizeof(key->slotId) * 2) /*slotId*/ + 1 + (key->idLen * 2) + 1) * 2; + + convertKeyType(key->keyType, NULL, 0, &algoSz); + KEYNAME_SZ += (algoSz + 1) * 2; + + keyName = calloc(1, sizeof(*keyName) + KEYNAME_SZ); + if (!keyName) + { + WLog_ERR(TAG, "unable to allocate keyName"); + goto cleanup_FindObjects; + } + keyName->dwLegacyKeySpec = AT_KEYEXCHANGE | AT_SIGNATURE; + keyName->dwFlags = NCRYPT_MACHINE_KEY_FLAG; + keyName->pszName = (LPWSTR)(keyName + 1); + wprintKeyName(keyName->pszName, key->slotId, key->id, key->idLen); + + keyName->pszAlgid = keyName->pszName + _wcslen(keyName->pszName) + 1; + convertKeyType(key->keyType, keyName->pszAlgid, algoSz + 1, NULL); + } + + cleanup_FindObjects: + WINPR_ASSERT(provider->p11->C_FindObjectsFinal); + rv = provider->p11->C_FindObjectsFinal(currentSession); + + if (keyName) + { + *ppKeyName = keyName; + state->keyIndex++; + return ERROR_SUCCESS; + } + } + + return NTE_NO_MORE_ITEMS; +} + +static SECURITY_STATUS get_piv_container_name(NCryptP11KeyHandle* key, const BYTE* piv_tag, + BYTE* output, size_t output_len) +{ + CK_SLOT_INFO slot_info = { 0 }; + CK_FUNCTION_LIST_PTR p11 = NULL; + WCHAR* reader = NULL; + SCARDCONTEXT context = 0; + SCARDHANDLE card = 0; + DWORD proto = 0; + const SCARD_IO_REQUEST* pci = NULL; + BYTE buf[258] = { 0 }; + char container_name[PIV_CONTAINER_NAME_LEN + 1] = { 0 }; + DWORD buf_len = 0; + SECURITY_STATUS ret = NTE_BAD_KEY; + WinPrAsn1Decoder dec = { 0 }; + WinPrAsn1Decoder dec2 = { 0 }; + size_t len = 0; + BYTE tag = 0; + BYTE* p = NULL; + wStream s = { 0 }; + + WINPR_ASSERT(key); + WINPR_ASSERT(piv_tag); + + WINPR_ASSERT(key->provider); + p11 = key->provider->p11; + WINPR_ASSERT(p11); + + /* Get the reader the card is in */ + WINPR_ASSERT(p11->C_GetSlotInfo); + if (p11->C_GetSlotInfo(key->slotId, &slot_info) != CKR_OK) + return NTE_BAD_KEY; + + fix_padded_string((char*)slot_info.slotDescription, sizeof(slot_info.slotDescription)); + reader = ConvertUtf8NToWCharAlloc((char*)slot_info.slotDescription, + ARRAYSIZE(slot_info.slotDescription), NULL); + ret = NTE_NO_MEMORY; + if (!reader) + goto out; + + ret = NTE_BAD_KEY; + if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &context) != SCARD_S_SUCCESS) + goto out; + + if (SCardConnectW(context, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &card, &proto) != + SCARD_S_SUCCESS) + goto out; + pci = (proto == SCARD_PROTOCOL_T0) ? SCARD_PCI_T0 : SCARD_PCI_T1; + + buf_len = sizeof(buf); + if (SCardTransmit(card, pci, APDU_PIV_SELECT_AID, sizeof(APDU_PIV_SELECT_AID), NULL, buf, + &buf_len) != SCARD_S_SUCCESS) + goto out; + if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61) + goto out; + + buf_len = sizeof(buf); + if (SCardTransmit(card, pci, APDU_PIV_GET_CHUID, sizeof(APDU_PIV_GET_CHUID), NULL, buf, + &buf_len) != SCARD_S_SUCCESS) + goto out; + if ((buf[buf_len - 2] != 0x90 || buf[buf_len - 1] != 0) && buf[buf_len - 2] != 0x61) + goto out; + + /* Find the GUID field in the CHUID data object */ + WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_BER, buf, buf_len); + if (!WinPrAsn1DecReadTagAndLen(&dec, &tag, &len) || tag != 0x53) + goto out; + while (WinPrAsn1DecReadTagLenValue(&dec, &tag, &len, &dec2) && tag != 0x34) + ; + if (tag != 0x34 || len != 16) + goto out; + + s = WinPrAsn1DecGetStream(&dec2); + p = Stream_Buffer(&s); + + /* Construct the value Windows would use for a PIV key's container name */ + snprintf(container_name, PIV_CONTAINER_NAME_LEN + 1, + "%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", p[3], p[2], + p[1], p[0], p[5], p[4], p[7], p[6], p[8], p[9], p[10], p[11], p[12], piv_tag[0], + piv_tag[1], piv_tag[2]); + + /* And convert it to UTF-16 */ + union + { + WCHAR* wc; + BYTE* b; + } cnv; + cnv.b = output; + if (ConvertUtf8NToWChar(container_name, ARRAYSIZE(container_name), cnv.wc, + output_len / sizeof(WCHAR)) > 0) + ret = ERROR_SUCCESS; + +out: + free(reader); + if (card) + SCardDisconnect(card, SCARD_LEAVE_CARD); + if (context) + SCardReleaseContext(context); + return ret; +} + +static SECURITY_STATUS check_for_piv_container_name(NCryptP11KeyHandle* key, BYTE* pbOutput, + DWORD cbOutput, DWORD* pcbResult, char* label, + size_t label_len) +{ + for (size_t i = 0; i < ARRAYSIZE(piv_cert_tags); i++) + { + const piv_cert_tags_t* cur = &piv_cert_tags[i]; + if (strncmp(label, cur->label, label_len) == 0) + { + *pcbResult = (PIV_CONTAINER_NAME_LEN + 1) * sizeof(WCHAR); + if (!pbOutput) + return ERROR_SUCCESS; + else if (cbOutput < (PIV_CONTAINER_NAME_LEN + 1) * sizeof(WCHAR)) + return NTE_NO_MEMORY; + else + return get_piv_container_name(key, cur->tag, pbOutput, cbOutput); + } + } + return NTE_NOT_FOUND; +} + +static SECURITY_STATUS NCryptP11KeyGetProperties(NCryptP11KeyHandle* keyHandle, + NCryptKeyGetPropertyEnum property, PBYTE pbOutput, + DWORD cbOutput, DWORD* pcbResult, DWORD dwFlags) +{ + SECURITY_STATUS ret = NTE_FAIL; + CK_RV rv = 0; + CK_SESSION_HANDLE session = 0; + CK_OBJECT_HANDLE objectHandle = 0; + CK_ULONG objectCount = 0; + NCryptP11ProviderHandle* provider = NULL; + CK_OBJECT_CLASS oclass = CKO_CERTIFICATE; + CK_CERTIFICATE_TYPE ctype = CKC_X_509; + CK_ATTRIBUTE certificateFilter[] = { { CKA_CLASS, &oclass, sizeof(oclass) }, + { CKA_CERTIFICATE_TYPE, &ctype, sizeof(ctype) }, + { CKA_ID, keyHandle->keyCertId, + keyHandle->keyCertIdLen } }; + CK_ATTRIBUTE* objectFilter = certificateFilter; + CK_ULONG objectFilterLen = ARRAYSIZE(certificateFilter); + + WINPR_ASSERT(keyHandle); + provider = keyHandle->provider; + WINPR_ASSERT(provider); + + switch (property) + + { + case NCRYPT_PROPERTY_CERTIFICATE: + case NCRYPT_PROPERTY_NAME: + break; + case NCRYPT_PROPERTY_READER: + { + CK_SLOT_INFO slotInfo; + + WINPR_ASSERT(provider->p11->C_GetSlotInfo); + rv = provider->p11->C_GetSlotInfo(keyHandle->slotId, &slotInfo); + if (rv != CKR_OK) + return NTE_BAD_KEY; + +#define SLOT_DESC_SZ sizeof(slotInfo.slotDescription) + fix_padded_string((char*)slotInfo.slotDescription, SLOT_DESC_SZ); + *pcbResult = 2 * (strnlen((char*)slotInfo.slotDescription, SLOT_DESC_SZ) + 1); + if (pbOutput) + { + union + { + WCHAR* wc; + BYTE* b; + } cnv; + cnv.b = pbOutput; + if (cbOutput < *pcbResult) + return NTE_NO_MEMORY; + + if (ConvertUtf8ToWChar((char*)slotInfo.slotDescription, cnv.wc, + cbOutput / sizeof(WCHAR)) < 0) + return NTE_NO_MEMORY; + } + return ERROR_SUCCESS; + } + case NCRYPT_PROPERTY_SLOTID: + { + *pcbResult = 4; + if (pbOutput) + { + UINT32* ptr = (UINT32*)pbOutput; + + if (cbOutput < 4) + return NTE_NO_MEMORY; + + *ptr = keyHandle->slotId; + } + return ERROR_SUCCESS; + } + case NCRYPT_PROPERTY_UNKNOWN: + default: + return NTE_NOT_SUPPORTED; + } + + WINPR_ASSERT(provider->p11->C_OpenSession); + rv = provider->p11->C_OpenSession(keyHandle->slotId, CKF_SERIAL_SESSION, NULL, NULL, &session); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "error opening session on slot %d", keyHandle->slotId); + return NTE_FAIL; + } + + WINPR_ASSERT(provider->p11->C_FindObjectsInit); + rv = provider->p11->C_FindObjectsInit(session, objectFilter, objectFilterLen); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to initiate search for slot %d", keyHandle->slotId); + goto out; + } + + WINPR_ASSERT(provider->p11->C_FindObjects); + rv = provider->p11->C_FindObjects(session, &objectHandle, 1, &objectCount); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "unable to findObjects for slot %d", keyHandle->slotId); + goto out_final; + } + if (!objectCount) + { + ret = NTE_NOT_FOUND; + goto out_final; + } + + switch (property) + { + case NCRYPT_PROPERTY_CERTIFICATE: + { + CK_ATTRIBUTE certValue = { CKA_VALUE, pbOutput, cbOutput }; + + WINPR_ASSERT(provider->p11->C_GetAttributeValue); + rv = provider->p11->C_GetAttributeValue(session, objectHandle, &certValue, 1); + if (rv != CKR_OK) + { + // TODO: do a kind of translation from CKR_* to NTE_* + } + + *pcbResult = certValue.ulValueLen; + ret = ERROR_SUCCESS; + break; + } + case NCRYPT_PROPERTY_NAME: + { + CK_ATTRIBUTE attr = { CKA_LABEL, NULL, 0 }; + char* label = NULL; + + WINPR_ASSERT(provider->p11->C_GetAttributeValue); + rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1); + if (rv == CKR_OK) + { + label = calloc(1, attr.ulValueLen); + if (!label) + { + ret = NTE_NO_MEMORY; + break; + } + + attr.pValue = label; + rv = provider->p11->C_GetAttributeValue(session, objectHandle, &attr, 1); + } + + if (rv == CKR_OK) + { + /* Check if we have a PIV card */ + ret = check_for_piv_container_name(keyHandle, pbOutput, cbOutput, pcbResult, label, + attr.ulValueLen); + + /* Otherwise, at least for GIDS cards the label will be the correct value */ + if (ret == NTE_NOT_FOUND) + { + union + { + WCHAR* wc; + BYTE* b; + } cnv; + const size_t olen = pbOutput ? cbOutput / sizeof(WCHAR) : 0; + cnv.b = pbOutput; + SSIZE_T size = ConvertUtf8NToWChar(label, attr.ulValueLen, cnv.wc, olen); + if (size < 0) + ret = ERROR_CONVERT_TO_LARGE; + else + ret = ERROR_SUCCESS; + } + } + + free(label); + break; + } + default: + ret = NTE_NOT_SUPPORTED; + break; + } + +out_final: + WINPR_ASSERT(provider->p11->C_FindObjectsFinal); + rv = provider->p11->C_FindObjectsFinal(session); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "error in C_FindObjectsFinal() for slot %d", keyHandle->slotId); + } +out: + WINPR_ASSERT(provider->p11->C_CloseSession); + rv = provider->p11->C_CloseSession(session); + if (rv != CKR_OK) + { + WLog_ERR(TAG, "error in C_CloseSession() for slot %d", keyHandle->slotId); + } + return ret; +} + +static SECURITY_STATUS NCryptP11GetProperty(NCRYPT_HANDLE hObject, NCryptKeyGetPropertyEnum prop, + PBYTE pbOutput, DWORD cbOutput, DWORD* pcbResult, + DWORD dwFlags) +{ + NCryptBaseHandle* base = (NCryptBaseHandle*)hObject; + + WINPR_ASSERT(base); + switch (base->type) + { + case WINPR_NCRYPT_PROVIDER: + return ERROR_CALL_NOT_IMPLEMENTED; + case WINPR_NCRYPT_KEY: + return NCryptP11KeyGetProperties((NCryptP11KeyHandle*)hObject, prop, pbOutput, cbOutput, + pcbResult, dwFlags); + default: + return ERROR_INVALID_HANDLE; + } + return ERROR_SUCCESS; +} + +static SECURITY_STATUS NCryptP11OpenKey(NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE* phKey, + LPCWSTR pszKeyName, DWORD dwLegacyKeySpec, DWORD dwFlags) +{ + SECURITY_STATUS ret = 0; + CK_SLOT_ID slotId = 0; + CK_BYTE keyCertId[64] = { 0 }; + CK_ULONG keyCertIdLen = 0; + NCryptP11KeyHandle* keyHandle = NULL; + + ret = parseKeyName(pszKeyName, &slotId, keyCertId, &keyCertIdLen); + if (ret != ERROR_SUCCESS) + return ret; + + keyHandle = (NCryptP11KeyHandle*)ncrypt_new_handle( + WINPR_NCRYPT_KEY, sizeof(*keyHandle), NCryptP11GetProperty, winpr_NCryptDefault_dtor); + if (!keyHandle) + return NTE_NO_MEMORY; + + keyHandle->provider = (NCryptP11ProviderHandle*)hProvider; + keyHandle->slotId = slotId; + memcpy(keyHandle->keyCertId, keyCertId, sizeof(keyCertId)); + keyHandle->keyCertIdLen = keyCertIdLen; + *phKey = (NCRYPT_KEY_HANDLE)keyHandle; + return ERROR_SUCCESS; +} + +static SECURITY_STATUS initialize_pkcs11(HANDLE handle, + CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR), + NCRYPT_PROV_HANDLE* phProvider) +{ + SECURITY_STATUS status = ERROR_SUCCESS; + NCryptP11ProviderHandle* ret = NULL; + CK_RV rv = 0; + + WINPR_ASSERT(c_get_function_list); + WINPR_ASSERT(phProvider); + + ret = (NCryptP11ProviderHandle*)ncrypt_new_handle( + WINPR_NCRYPT_PROVIDER, sizeof(*ret), NCryptP11GetProperty, NCryptP11StorageProvider_dtor); + if (!ret) + { + if (handle) + FreeLibrary(handle); + return NTE_NO_MEMORY; + } + + ret->library = handle; + ret->baseProvider.enumKeysFn = NCryptP11EnumKeys; + ret->baseProvider.openKeyFn = NCryptP11OpenKey; + + rv = c_get_function_list(&ret->p11); + if (rv != CKR_OK) + { + status = NTE_PROVIDER_DLL_FAIL; + goto fail; + } + + WINPR_ASSERT(ret->p11->C_Initialize); + rv = ret->p11->C_Initialize(NULL); + if (rv != CKR_OK) + { + status = NTE_PROVIDER_DLL_FAIL; + goto fail; + } + + *phProvider = (NCRYPT_PROV_HANDLE)ret; + +fail: + if (status != ERROR_SUCCESS) + ret->baseProvider.baseHandle.releaseFn((NCRYPT_HANDLE)ret); + return status; +} + +SECURITY_STATUS NCryptOpenP11StorageProviderEx(NCRYPT_PROV_HANDLE* phProvider, + LPCWSTR pszProviderName, DWORD dwFlags, + LPCSTR* modulePaths) +{ + SECURITY_STATUS status = ERROR_INVALID_PARAMETER; +#if defined(__LP64__) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || \ + defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define LIBS64 +#endif + + LPCSTR openscPaths[] = { "opensc-pkcs11.so", /* In case winpr is installed in system paths */ +#ifdef __APPLE__ + "/usr/local/lib/pkcs11/opensc-pkcs11.so", +#else + /* linux and UNIXes */ +#ifdef LIBS64 + "/usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so", /* Ubuntu/debian + */ + "/lib64/pkcs11/opensc-pkcs11.so", /* Fedora */ +#else + "/usr/lib/i386-linux-gnu/opensc-pkcs11.so", /* debian */ + "/lib32/pkcs11/opensc-pkcs11.so", /* Fedora */ +#endif +#endif + NULL }; + + if (!phProvider) + return ERROR_INVALID_PARAMETER; + +#if defined(WITH_OPENSC_PKCS11_LINKED) + if (!modulePaths) + return initialize_pkcs11(NULL, C_GetFunctionList, phProvider); +#endif + + if (!modulePaths) + modulePaths = openscPaths; + + while (*modulePaths) + { + HANDLE library = LoadLibrary(*modulePaths); + typedef CK_RV (*c_get_function_list_t)(CK_FUNCTION_LIST_PTR_PTR); + c_get_function_list_t c_get_function_list = NULL; + + WLog_DBG(TAG, "Trying pkcs11-helper module '%s'", *modulePaths); + if (!library) + { + status = NTE_PROV_DLL_NOT_FOUND; + goto out_load_library; + } + + c_get_function_list = (c_get_function_list_t)GetProcAddress(library, "C_GetFunctionList"); + if (!c_get_function_list) + { + status = NTE_PROV_TYPE_ENTRY_BAD; + goto out_load_library; + } + + status = initialize_pkcs11(library, c_get_function_list, phProvider); + if (status != ERROR_SUCCESS) + { + status = NTE_PROVIDER_DLL_FAIL; + goto out_load_library; + } + + WLog_DBG(TAG, "module '%s' loaded", *modulePaths); + return ERROR_SUCCESS; + + out_load_library: + modulePaths++; + } + + return status; +} diff --git a/winpr/libwinpr/ncrypt/test/CMakeLists.txt b/winpr/libwinpr/ncrypt/test/CMakeLists.txt new file mode 100644 index 0000000..fbad47a --- /dev/null +++ b/winpr/libwinpr/ncrypt/test/CMakeLists.txt @@ -0,0 +1,29 @@ +set(MODULE_NAME "TestNCrypt") +set(MODULE_PREFIX "TEST_NCRYPT") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestNCryptSmartcard.c + TestNCryptProviders.c +) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +target_link_libraries(${MODULE_NAME} winpr ${OPENSSL_LIBRARIES}) +if(WIN32) + target_link_libraries(${MODULE_NAME} ncrypt) +endif() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/ncrypt/test/TestNCryptProviders.c b/winpr/libwinpr/ncrypt/test/TestNCryptProviders.c new file mode 100644 index 0000000..8381e68 --- /dev/null +++ b/winpr/libwinpr/ncrypt/test/TestNCryptProviders.c @@ -0,0 +1,51 @@ +/** + * WinPR: Windows Portable Runtime + * Test for NCrypt library + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include + +#define TAG "testNCrypt" + +int TestNCryptProviders(int argc, char* argv[]) +{ + SECURITY_STATUS status = 0; + DWORD nproviders = 0; + NCryptProviderName* providers = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + status = NCryptEnumStorageProviders(&nproviders, &providers, NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + return -1; + + for (DWORD i = 0; i < nproviders; i++) + { + const NCryptProviderName* provider = &providers[i]; + char providerNameStr[256] = { 0 }; + + ConvertWCharToUtf8(provider->pszName, providerNameStr, ARRAYSIZE(providerNameStr)); + printf("%d: %s\n", i, providerNameStr); + } + + NCryptFreeBuffer(providers); + return 0; +} diff --git a/winpr/libwinpr/ncrypt/test/TestNCryptSmartcard.c b/winpr/libwinpr/ncrypt/test/TestNCryptSmartcard.c new file mode 100644 index 0000000..247a033 --- /dev/null +++ b/winpr/libwinpr/ncrypt/test/TestNCryptSmartcard.c @@ -0,0 +1,156 @@ +/** + * WinPR: Windows Portable Runtime + * Test for NCrypt library + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include + +#include +#include + +#define TAG "testNCrypt" + +static void crypto_print_name(const BYTE* b, DWORD sz) +{ + X509_NAME* name = NULL; + X509* x509 = NULL; + BIO* bio = NULL; + char* ret = NULL; + + bio = BIO_new_mem_buf(b, sz); + if (!bio) + return; + + x509 = d2i_X509_bio(bio, NULL); + if (!x509) + goto bio_release; + + name = X509_get_subject_name(x509); + if (!name) + goto x509_release; + + ret = calloc(1024, sizeof(char)); + if (!ret) + goto bio_release; + + char* ret2 = X509_NAME_oneline(name, ret, 1024); + + printf("\t%s\n", ret2); + free(ret); + +x509_release: + X509_free(x509); +bio_release: + BIO_free(bio); +} + +int TestNCryptSmartcard(int argc, char* argv[]) +{ + SECURITY_STATUS status = 0; + DWORD providerCount = 0; + NCryptProviderName* names = NULL; + + status = NCryptEnumStorageProviders(&providerCount, &names, NCRYPT_SILENT_FLAG); + if (status != ERROR_SUCCESS) + return -1; + + for (size_t j = 0; j < providerCount; j++) + { + const NCryptProviderName* name = &names[j]; + NCRYPT_PROV_HANDLE provider = 0; + char providerNameStr[256] = { 0 }; + PVOID enumState = NULL; + size_t i = 0; + NCryptKeyName* keyName = NULL; + + if (ConvertWCharToUtf8(name->pszName, providerNameStr, ARRAYSIZE(providerNameStr)) < 0) + continue; + printf("provider %" PRIuz ": %s\n", j, providerNameStr); + + status = NCryptOpenStorageProvider(&provider, name->pszName, 0); + if (status != ERROR_SUCCESS) + continue; + + while ((status = NCryptEnumKeys(provider, NULL, &keyName, &enumState, + NCRYPT_SILENT_FLAG)) == ERROR_SUCCESS) + { + NCRYPT_KEY_HANDLE phKey = 0; + DWORD dwFlags = 0; + DWORD cbOutput = 0; + char keyNameStr[256] = { 0 }; + WCHAR reader[1024] = { 0 }; + PBYTE certBytes = NULL; + + if (ConvertWCharToUtf8(keyName->pszName, keyNameStr, ARRAYSIZE(keyNameStr)) < 0) + continue; + + printf("\tkey %" PRIuz ": %s\n", i, keyNameStr); + status = NCryptOpenKey(provider, &phKey, keyName->pszName, keyName->dwLegacyKeySpec, + dwFlags); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to open key %s", keyNameStr); + continue; + } + + status = NCryptGetProperty(phKey, NCRYPT_READER_PROPERTY, (PBYTE)reader, sizeof(reader), + &cbOutput, dwFlags); + if (status == ERROR_SUCCESS) + { + char readerStr[1024] = { 0 }; + + ConvertWCharNToUtf8(reader, cbOutput, readerStr, ARRAYSIZE(readerStr)); + printf("\treader: %s\n", readerStr); + } + + cbOutput = 0; + status = + NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, NULL, 0, &cbOutput, dwFlags); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to retrieve certificate len for key '%s'", keyNameStr); + goto endofloop; + } + + certBytes = calloc(1, cbOutput); + status = NCryptGetProperty(phKey, NCRYPT_CERTIFICATE_PROPERTY, certBytes, cbOutput, + &cbOutput, dwFlags); + if (status != ERROR_SUCCESS) + { + WLog_ERR(TAG, "unable to retrieve certificate for key %s", keyNameStr); + goto endofloop; + } + + crypto_print_name(certBytes, cbOutput); + free(certBytes); + + endofloop: + NCryptFreeBuffer(keyName); + NCryptFreeObject((NCRYPT_HANDLE)phKey); + i++; + } + + NCryptFreeBuffer(enumState); + NCryptFreeObject((NCRYPT_HANDLE)provider); + } + + NCryptFreeBuffer(names); + return 0; +} diff --git a/winpr/libwinpr/nt/CMakeLists.txt b/winpr/libwinpr/nt/CMakeLists.txt new file mode 100644 index 0000000..ef335e4 --- /dev/null +++ b/winpr/libwinpr/nt/CMakeLists.txt @@ -0,0 +1,30 @@ +# WinPR: Windows Portable Runtime +# libwinpr-nt cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(nt.c ntstatus.c) + +winpr_library_add_private( + ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_DL_LIBS}) + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + winpr_library_add_private(rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/nt/ModuleOptions.cmake b/winpr/libwinpr/nt/ModuleOptions.cmake new file mode 100644 index 0000000..750a8f5 --- /dev/null +++ b/winpr/libwinpr/nt/ModuleOptions.cmake @@ -0,0 +1,8 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "nt") +set(MINWIN_LONG_NAME "Windows Native System Services") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") diff --git a/winpr/libwinpr/nt/nt.c b/winpr/libwinpr/nt/nt.c new file mode 100644 index 0000000..1806901 --- /dev/null +++ b/winpr/libwinpr/nt/nt.c @@ -0,0 +1,69 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Native System Services + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2013 Thincast Technologies GmbH + * Copyright 2013 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "../log.h" +#define TAG WINPR_TAG("nt") + +#ifndef _WIN32 + +#include +#include + +#include "../handle/handle.h" + +static pthread_once_t sTebOnceControl = PTHREAD_ONCE_INIT; +static pthread_key_t sTebKey; + +static void sTebDestruct(void* teb) +{ + free(teb); +} + +static void sTebInitOnce(void) +{ + pthread_key_create(&sTebKey, sTebDestruct); +} + +PTEB NtCurrentTeb(void) +{ + PTEB teb = NULL; + + if (pthread_once(&sTebOnceControl, sTebInitOnce) == 0) + { + if ((teb = pthread_getspecific(sTebKey)) == NULL) + { + teb = calloc(1, sizeof(TEB)); + if (teb) + pthread_setspecific(sTebKey, teb); + } + } + return teb; +} +#endif diff --git a/winpr/libwinpr/nt/ntstatus.c b/winpr/libwinpr/nt/ntstatus.c new file mode 100644 index 0000000..e2f174e --- /dev/null +++ b/winpr/libwinpr/nt/ntstatus.c @@ -0,0 +1,4660 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR ntstatus helper + * + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +struct ntstatus_map +{ + DWORD code; + const char* tag; +}; + +static const struct ntstatus_map win32errmap[] = { + { 0x00000000, "ERROR_SUCCESS" }, + { 0x00000001, "ERROR_INVALID_FUNCTION" }, + { 0x00000002, "ERROR_FILE_NOT_FOUND" }, + { 0x00000003, "ERROR_PATH_NOT_FOUND" }, + { 0x00000004, "ERROR_TOO_MANY_OPEN_FILES" }, + { 0x00000005, "ERROR_ACCESS_DENIED" }, + { 0x00000006, "ERROR_INVALID_HANDLE" }, + { 0x00000007, "ERROR_ARENA_TRASHED" }, + { 0x00000008, "ERROR_NOT_ENOUGH_MEMORY" }, + { 0x00000009, "ERROR_INVALID_BLOCK" }, + { 0x0000000A, "ERROR_BAD_ENVIRONMENT" }, + { 0x0000000B, "ERROR_BAD_FORMAT" }, + { 0x0000000C, "ERROR_INVALID_ACCESS" }, + { 0x0000000D, "ERROR_INVALID_DATA" }, + { 0x0000000E, "ERROR_OUTOFMEMORY" }, + { 0x0000000F, "ERROR_INVALID_DRIVE" }, + { 0x00000010, "ERROR_CURRENT_DIRECTORY" }, + { 0x00000011, "ERROR_NOT_SAME_DEVICE" }, + { 0x00000012, "ERROR_NO_MORE_FILES" }, + { 0x00000013, "ERROR_WRITE_PROTECT" }, + { 0x00000014, "ERROR_BAD_UNIT" }, + { 0x00000015, "ERROR_NOT_READY" }, + { 0x00000016, "ERROR_BAD_COMMAND" }, + { 0x00000017, "ERROR_CRC" }, + { 0x00000018, "ERROR_BAD_LENGTH" }, + { 0x00000019, "ERROR_SEEK" }, + { 0x0000001A, "ERROR_NOT_DOS_DISK" }, + { 0x0000001B, "ERROR_SECTOR_NOT_FOUND" }, + { 0x0000001C, "ERROR_OUT_OF_PAPER" }, + { 0x0000001D, "ERROR_WRITE_FAULT" }, + { 0x0000001E, "ERROR_READ_FAULT" }, + { 0x0000001F, "ERROR_GEN_FAILURE" }, + { 0x00000020, "ERROR_SHARING_VIOLATION" }, + { 0x00000021, "ERROR_LOCK_VIOLATION" }, + { 0x00000022, "ERROR_WRONG_DISK" }, + { 0x00000024, "ERROR_SHARING_BUFFER_EXCEEDED" }, + { 0x00000026, "ERROR_HANDLE_EOF" }, + { 0x00000027, "ERROR_HANDLE_DISK_FULL" }, + { 0x00000032, "ERROR_NOT_SUPPORTED" }, + { 0x00000033, "ERROR_REM_NOT_LIST" }, + { 0x00000034, "ERROR_DUP_NAME" }, + { 0x00000035, "ERROR_BAD_NETPATH" }, + { 0x00000036, "ERROR_NETWORK_BUSY" }, + { 0x00000037, "ERROR_DEV_NOT_EXIST" }, + { 0x00000038, "ERROR_TOO_MANY_CMDS" }, + { 0x00000039, "ERROR_ADAP_HDW_ERR" }, + { 0x0000003A, "ERROR_BAD_NET_RESP" }, + { 0x0000003B, "ERROR_UNEXP_NET_ERR" }, + { 0x0000003C, "ERROR_BAD_REM_ADAP" }, + { 0x0000003D, "ERROR_PRINTQ_FULL" }, + { 0x0000003E, "ERROR_NO_SPOOL_SPACE" }, + { 0x0000003F, "ERROR_PRINT_CANCELLED" }, + { 0x00000040, "ERROR_NETNAME_DELETED" }, + { 0x00000041, "ERROR_NETWORK_ACCESS_DENIED" }, + { 0x00000042, "ERROR_BAD_DEV_TYPE" }, + { 0x00000043, "ERROR_BAD_NET_NAME" }, + { 0x00000044, "ERROR_TOO_MANY_NAMES" }, + { 0x00000045, "ERROR_TOO_MANY_SESS" }, + { 0x00000046, "ERROR_SHARING_PAUSED" }, + { 0x00000047, "ERROR_REQ_NOT_ACCEP" }, + { 0x00000048, "ERROR_REDIR_PAUSED" }, + { 0x00000050, "ERROR_FILE_EXISTS" }, + { 0x00000052, "ERROR_CANNOT_MAKE" }, + { 0x00000053, "ERROR_FAIL_I24" }, + { 0x00000054, "ERROR_OUT_OF_STRUCTURES" }, + { 0x00000055, "ERROR_ALREADY_ASSIGNED" }, + { 0x00000056, "ERROR_INVALID_PASSWORD" }, + { 0x00000057, "ERROR_INVALID_PARAMETER" }, + { 0x00000058, "ERROR_NET_WRITE_FAULT" }, + { 0x00000059, "ERROR_NO_PROC_SLOTS" }, + { 0x00000064, "ERROR_TOO_MANY_SEMAPHORES" }, + { 0x00000065, "ERROR_EXCL_SEM_ALREADY_OWNED" }, + { 0x00000066, "ERROR_SEM_IS_SET" }, + { 0x00000067, "ERROR_TOO_MANY_SEM_REQUESTS" }, + { 0x00000068, "ERROR_INVALID_AT_INTERRUPT_TIME" }, + { 0x00000069, "ERROR_SEM_OWNER_DIED" }, + { 0x0000006A, "ERROR_SEM_USER_LIMIT" }, + { 0x0000006B, "ERROR_DISK_CHANGE" }, + { 0x0000006C, "ERROR_DRIVE_LOCKED" }, + { 0x0000006D, "ERROR_BROKEN_PIPE" }, + { 0x0000006E, "ERROR_OPEN_FAILED" }, + { 0x0000006F, "ERROR_BUFFER_OVERFLOW" }, + { 0x00000070, "ERROR_DISK_FULL" }, + { 0x00000071, "ERROR_NO_MORE_SEARCH_HANDLES" }, + { 0x00000072, "ERROR_INVALID_TARGET_HANDLE" }, + { 0x00000075, "ERROR_INVALID_CATEGORY" }, + { 0x00000076, "ERROR_INVALID_VERIFY_SWITCH" }, + { 0x00000077, "ERROR_BAD_DRIVER_LEVEL" }, + { 0x00000078, "ERROR_CALL_NOT_IMPLEMENTED" }, + { 0x00000079, "ERROR_SEM_TIMEOUT" }, + { 0x0000007A, "ERROR_INSUFFICIENT_BUFFER" }, + { 0x0000007B, "ERROR_INVALID_NAME" }, + { 0x0000007C, "ERROR_INVALID_LEVEL" }, + { 0x0000007D, "ERROR_NO_VOLUME_LABEL" }, + { 0x0000007E, "ERROR_MOD_NOT_FOUND" }, + { 0x0000007F, "ERROR_PROC_NOT_FOUND" }, + { 0x00000080, "ERROR_WAIT_NO_CHILDREN" }, + { 0x00000081, "ERROR_CHILD_NOT_COMPLETE" }, + { 0x00000082, "ERROR_DIRECT_ACCESS_HANDLE" }, + { 0x00000083, "ERROR_NEGATIVE_SEEK" }, + { 0x00000084, "ERROR_SEEK_ON_DEVICE" }, + { 0x00000085, "ERROR_IS_JOIN_TARGET" }, + { 0x00000086, "ERROR_IS_JOINED" }, + { 0x00000087, "ERROR_IS_SUBSTED" }, + { 0x00000088, "ERROR_NOT_JOINED" }, + { 0x00000089, "ERROR_NOT_SUBSTED" }, + { 0x0000008A, "ERROR_JOIN_TO_JOIN" }, + { 0x0000008B, "ERROR_SUBST_TO_SUBST" }, + { 0x0000008C, "ERROR_JOIN_TO_SUBST" }, + { 0x0000008D, "ERROR_SUBST_TO_JOIN" }, + { 0x0000008E, "ERROR_BUSY_DRIVE" }, + { 0x0000008F, "ERROR_SAME_DRIVE" }, + { 0x00000090, "ERROR_DIR_NOT_ROOT" }, + { 0x00000091, "ERROR_DIR_NOT_EMPTY" }, + { 0x00000092, "ERROR_IS_SUBST_PATH" }, + { 0x00000093, "ERROR_IS_JOIN_PATH" }, + { 0x00000094, "ERROR_PATH_BUSY" }, + { 0x00000095, "ERROR_IS_SUBST_TARGET" }, + { 0x00000096, "ERROR_SYSTEM_TRACE" }, + { 0x00000097, "ERROR_INVALID_EVENT_COUNT" }, + { 0x00000098, "ERROR_TOO_MANY_MUXWAITERS" }, + { 0x00000099, "ERROR_INVALID_LIST_FORMAT" }, + { 0x0000009A, "ERROR_LABEL_TOO_LONG" }, + { 0x0000009B, "ERROR_TOO_MANY_TCBS" }, + { 0x0000009C, "ERROR_SIGNAL_REFUSED" }, + { 0x0000009D, "ERROR_DISCARDED" }, + { 0x0000009E, "ERROR_NOT_LOCKED" }, + { 0x0000009F, "ERROR_BAD_THREADID_ADDR" }, + { 0x000000A0, "ERROR_BAD_ARGUMENTS" }, + { 0x000000A1, "ERROR_BAD_PATHNAME" }, + { 0x000000A2, "ERROR_SIGNAL_PENDING" }, + { 0x000000A4, "ERROR_MAX_THRDS_REACHED" }, + { 0x000000A7, "ERROR_LOCK_FAILED" }, + { 0x000000AA, "ERROR_BUSY" }, + { 0x000000AD, "ERROR_CANCEL_VIOLATION" }, + { 0x000000AE, "ERROR_ATOMIC_LOCKS_NOT_SUPPORTED" }, + { 0x000000B4, "ERROR_INVALID_SEGMENT_NUMBER" }, + { 0x000000B6, "ERROR_INVALID_ORDINAL" }, + { 0x000000B7, "ERROR_ALREADY_EXISTS" }, + { 0x000000BA, "ERROR_INVALID_FLAG_NUMBER" }, + { 0x000000BB, "ERROR_SEM_NOT_FOUND" }, + { 0x000000BC, "ERROR_INVALID_STARTING_CODESEG" }, + { 0x000000BD, "ERROR_INVALID_STACKSEG" }, + { 0x000000BE, "ERROR_INVALID_MODULETYPE" }, + { 0x000000BF, "ERROR_INVALID_EXE_SIGNATURE" }, + { 0x000000C0, "ERROR_EXE_MARKED_INVALID" }, + { 0x000000C1, "ERROR_BAD_EXE_FORMAT" }, + { 0x000000C2, "ERROR_ITERATED_DATA_EXCEEDS_64k" }, + { 0x000000C3, "ERROR_INVALID_MINALLOCSIZE" }, + { 0x000000C4, "ERROR_DYNLINK_FROM_INVALID_RING" }, + { 0x000000C5, "ERROR_IOPL_NOT_ENABLED" }, + { 0x000000C6, "ERROR_INVALID_SEGDPL" }, + { 0x000000C7, "ERROR_AUTODATASEG_EXCEEDS_64k" }, + { 0x000000C8, "ERROR_RING2SEG_MUST_BE_MOVABLE" }, + { 0x000000C9, "ERROR_RELOC_CHAIN_XEEDS_SEGLIM" }, + { 0x000000CA, "ERROR_INFLOOP_IN_RELOC_CHAIN" }, + { 0x000000CB, "ERROR_ENVVAR_NOT_FOUND" }, + { 0x000000CD, "ERROR_NO_SIGNAL_SENT" }, + { 0x000000CE, "ERROR_FILENAME_EXCED_RANGE" }, + { 0x000000CF, "ERROR_RING2_STACK_IN_USE" }, + { 0x000000D0, "ERROR_META_EXPANSION_TOO_LONG" }, + { 0x000000D1, "ERROR_INVALID_SIGNAL_NUMBER" }, + { 0x000000D2, "ERROR_THREAD_1_INACTIVE" }, + { 0x000000D4, "ERROR_LOCKED" }, + { 0x000000D6, "ERROR_TOO_MANY_MODULES" }, + { 0x000000D7, "ERROR_NESTING_NOT_ALLOWED" }, + { 0x000000D8, "ERROR_EXE_MACHINE_TYPE_MISMATCH" }, + { 0x000000D9, "ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY" }, + { 0x000000DA, "ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY" }, + { 0x000000DC, "ERROR_FILE_CHECKED_OUT" }, + { 0x000000DD, "ERROR_CHECKOUT_REQUIRED" }, + { 0x000000DE, "ERROR_BAD_FILE_TYPE" }, + { 0x000000DF, "ERROR_FILE_TOO_LARGE" }, + { 0x000000E0, "ERROR_FORMS_AUTH_REQUIRED" }, + { 0x000000E1, "ERROR_VIRUS_INFECTED" }, + { 0x000000E2, "ERROR_VIRUS_DELETED" }, + { 0x000000E5, "ERROR_PIPE_LOCAL" }, + { 0x000000E6, "ERROR_BAD_PIPE" }, + { 0x000000E7, "ERROR_PIPE_BUSY" }, + { 0x000000E8, "ERROR_NO_DATA" }, + { 0x000000E9, "ERROR_PIPE_NOT_CONNECTED" }, + { 0x000000EA, "ERROR_MORE_DATA" }, + { 0x000000F0, "ERROR_VC_DISCONNECTED" }, + { 0x000000FE, "ERROR_INVALID_EA_NAME" }, + { 0x000000FF, "ERROR_EA_LIST_INCONSISTENT" }, + { 0x00000102, "WAIT_TIMEOUT" }, + { 0x00000103, "ERROR_NO_MORE_ITEMS" }, + { 0x0000010A, "ERROR_CANNOT_COPY" }, + { 0x0000010B, "ERROR_DIRECTORY" }, + { 0x00000113, "ERROR_EAS_DIDNT_FIT" }, + { 0x00000114, "ERROR_EA_FILE_CORRUPT" }, + { 0x00000115, "ERROR_EA_TABLE_FULL" }, + { 0x00000116, "ERROR_INVALID_EA_HANDLE" }, + { 0x0000011A, "ERROR_EAS_NOT_SUPPORTED" }, + { 0x00000120, "ERROR_NOT_OWNER" }, + { 0x0000012A, "ERROR_TOO_MANY_POSTS" }, + { 0x0000012B, "ERROR_PARTIAL_COPY" }, + { 0x0000012C, "ERROR_OPLOCK_NOT_GRANTED" }, + { 0x0000012D, "ERROR_INVALID_OPLOCK_PROTOCOL" }, + { 0x0000012E, "ERROR_DISK_TOO_FRAGMENTED" }, + { 0x0000012F, "ERROR_DELETE_PENDING" }, + { 0x0000013D, "ERROR_MR_MID_NOT_FOUND" }, + { 0x0000013E, "ERROR_SCOPE_NOT_FOUND" }, + { 0x0000015E, "ERROR_FAIL_NOACTION_REBOOT" }, + { 0x0000015F, "ERROR_FAIL_SHUTDOWN" }, + { 0x00000160, "ERROR_FAIL_RESTART" }, + { 0x00000161, "ERROR_MAX_SESSIONS_REACHED" }, + { 0x00000190, "ERROR_THREAD_MODE_ALREADY_BACKGROUND" }, + { 0x00000191, "ERROR_THREAD_MODE_NOT_BACKGROUND" }, + { 0x00000192, "ERROR_PROCESS_MODE_ALREADY_BACKGROUND" }, + { 0x00000193, "ERROR_PROCESS_MODE_NOT_BACKGROUND" }, + { 0x000001E7, "ERROR_INVALID_ADDRESS" }, + { 0x000001F4, "ERROR_USER_PROFILE_LOAD" }, + { 0x00000216, "ERROR_ARITHMETIC_OVERFLOW" }, + { 0x00000217, "ERROR_PIPE_CONNECTED" }, + { 0x00000218, "ERROR_PIPE_LISTENING" }, + { 0x00000219, "ERROR_VERIFIER_STOP" }, + { 0x0000021A, "ERROR_ABIOS_ERROR" }, + { 0x0000021B, "ERROR_WX86_WARNING" }, + { 0x0000021C, "ERROR_WX86_ERROR" }, + { 0x0000021D, "ERROR_TIMER_NOT_CANCELED" }, + { 0x0000021E, "ERROR_UNWIND" }, + { 0x0000021F, "ERROR_BAD_STACK" }, + { 0x00000220, "ERROR_INVALID_UNWIND_TARGET" }, + { 0x00000221, "ERROR_INVALID_PORT_ATTRIBUTES" }, + { 0x00000222, "ERROR_PORT_MESSAGE_TOO_LONG" }, + { 0x00000223, "ERROR_INVALID_QUOTA_LOWER" }, + { 0x00000224, "ERROR_DEVICE_ALREADY_ATTACHED" }, + { 0x00000225, "ERROR_INSTRUCTION_MISALIGNMENT" }, + { 0x00000226, "ERROR_PROFILING_NOT_STARTED" }, + { 0x00000227, "ERROR_PROFILING_NOT_STOPPED" }, + { 0x00000228, "ERROR_COULD_NOT_INTERPRET" }, + { 0x00000229, "ERROR_PROFILING_AT_LIMIT" }, + { 0x0000022A, "ERROR_CANT_WAIT" }, + { 0x0000022B, "ERROR_CANT_TERMINATE_SELF" }, + { 0x0000022C, "ERROR_UNEXPECTED_MM_CREATE_ERR" }, + { 0x0000022D, "ERROR_UNEXPECTED_MM_MAP_ERROR" }, + { 0x0000022E, "ERROR_UNEXPECTED_MM_EXTEND_ERR" }, + { 0x0000022F, "ERROR_BAD_FUNCTION_TABLE" }, + { 0x00000230, "ERROR_NO_GUID_TRANSLATION" }, + { 0x00000231, "ERROR_INVALID_LDT_SIZE" }, + { 0x00000233, "ERROR_INVALID_LDT_OFFSET" }, + { 0x00000234, "ERROR_INVALID_LDT_DESCRIPTOR" }, + { 0x00000235, "ERROR_TOO_MANY_THREADS" }, + { 0x00000236, "ERROR_THREAD_NOT_IN_PROCESS" }, + { 0x00000237, "ERROR_PAGEFILE_QUOTA_EXCEEDED" }, + { 0x00000238, "ERROR_LOGON_SERVER_CONFLICT" }, + { 0x00000239, "ERROR_SYNCHRONIZATION_REQUIRED" }, + { 0x0000023A, "ERROR_NET_OPEN_FAILED" }, + { 0x0000023B, "ERROR_IO_PRIVILEGE_FAILED" }, + { 0x0000023C, "ERROR_CONTROL_C_EXIT" }, + { 0x0000023D, "ERROR_MISSING_SYSTEMFILE" }, + { 0x0000023E, "ERROR_UNHANDLED_EXCEPTION" }, + { 0x0000023F, "ERROR_APP_INIT_FAILURE" + "properly (,{0x%lx). Click OK to terminate the application." }, + { 0x00000240, "ERROR_PAGEFILE_CREATE_FAILED" }, + { 0x00000241, "ERROR_INVALID_IMAGE_HASH" }, + { 0x00000242, "ERROR_NO_PAGEFILE" }, + { 0x00000243, "ERROR_ILLEGAL_FLOAT_CONTEXT" }, + { 0x00000244, "ERROR_NO_EVENT_PAIR" }, + { 0x00000245, "ERROR_DOMAIN_CTRLR_CONFIG_ERROR" }, + { 0x00000246, "ERROR_ILLEGAL_CHARACTER" }, + { 0x00000247, "ERROR_UNDEFINED_CHARACTER" }, + { 0x00000248, "ERROR_FLOPPY_VOLUME" }, + { 0x00000249, "ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT" }, + { 0x0000024A, "ERROR_BACKUP_CONTROLLER" }, + { 0x0000024B, "ERROR_MUTANT_LIMIT_EXCEEDED" }, + { 0x0000024C, "ERROR_FS_DRIVER_REQUIRED" }, + { 0x0000024D, "ERROR_CANNOT_LOAD_REGISTRY_FILE" }, + { 0x0000024E, "ERROR_DEBUG_ATTACH_FAILED" + + }, + { 0x0000024F, "ERROR_SYSTEM_PROCESS_TERMINATED" + "terminated unexpectedly with a status of ,{0x%08x (,{0x%08x ,{0x%08x). The " }, + { 0x00000250, "ERROR_DATA_NOT_ACCEPTED" }, + { 0x00000251, "ERROR_VDM_HARD_ERROR" }, + { 0x00000252, "ERROR_DRIVER_CANCEL_TIMEOUT" }, + { 0x00000253, "ERROR_REPLY_MESSAGE_MISMATCH" + + }, + { 0x00000254, "ERROR_LOST_WRITEBEHIND_DATA" + + }, + { 0x00000255, "ERROR_CLIENT_SERVER_PARAMETERS_INVALID" }, + { 0x00000256, "ERROR_NOT_TINY_STREAM" }, + { 0x00000257, "ERROR_STACK_OVERFLOW_READ" }, + { 0x00000258, "ERROR_CONVERT_TO_LARGE" }, + { 0x00000259, "ERROR_FOUND_OUT_OF_SCOPE" }, + { 0x0000025A, "ERROR_ALLOCATE_BUCKET" }, + { 0x0000025B, "ERROR_MARSHALL_OVERFLOW" }, + { 0x0000025C, "ERROR_INVALID_VARIANT" }, + { 0x0000025D, "ERROR_BAD_COMPRESSION_BUFFER" }, + { 0x0000025E, "ERROR_AUDIT_FAILED" }, + { 0x0000025F, "ERROR_TIMER_RESOLUTION_NOT_SET" }, + { 0x00000260, "ERROR_INSUFFICIENT_LOGON_INFO" }, + { 0x00000261, "ERROR_BAD_DLL_ENTRYPOINT" + + }, + { 0x00000262, "ERROR_BAD_SERVICE_ENTRYPOINT" + + }, + { 0x00000263, "ERROR_IP_ADDRESS_CONFLICT1" }, + { 0x00000264, "ERROR_IP_ADDRESS_CONFLICT2" }, + { 0x00000265, "ERROR_REGISTRY_QUOTA_LIMIT" }, + { 0x00000266, "ERROR_NO_CALLBACK_ACTIVE" }, + { 0x00000267, "ERROR_PWD_TOO_SHORT" }, + { 0x00000268, "ERROR_PWD_TOO_RECENT" }, + { 0x00000269, "ERROR_PWD_HISTORY_CONFLICT" }, + { 0x0000026A, "ERROR_UNSUPPORTED_COMPRESSION" }, + { 0x0000026B, "ERROR_INVALID_HW_PROFILE" }, + { 0x0000026C, "ERROR_INVALID_PLUGPLAY_DEVICE_PATH" }, + { 0x0000026D, "ERROR_QUOTA_LIST_INCONSISTENT" }, + { 0x0000026E, "ERROR_EVALUATION_EXPIRATION" + + "in 1 hour. To restore access to this installation of Windows, upgrade this " }, + { 0x0000026F, "ERROR_ILLEGAL_DLL_RELOCATION" + + }, + { 0x00000270, "ERROR_DLL_INIT_FAILED_LOGOFF" }, + { 0x00000271, "ERROR_VALIDATE_CONTINUE" }, + { 0x00000272, "ERROR_NO_MORE_MATCHES" }, + { 0x00000273, "ERROR_RANGE_LIST_CONFLICT" }, + { 0x00000274, "ERROR_SERVER_SID_MISMATCH" }, + { 0x00000275, "ERROR_CANT_ENABLE_DENY_ONLY" }, + { 0x00000276, "ERROR_FLOAT_MULTIPLE_FAULTS" }, + { 0x00000277, "ERROR_FLOAT_MULTIPLE_TRAPS" }, + { 0x00000278, "ERROR_NOINTERFACE" }, + { 0x00000279, "ERROR_DRIVER_FAILED_SLEEP" }, + { 0x0000027A, "ERROR_CORRUPT_SYSTEM_FILE" }, + { 0x0000027B, "ERROR_COMMITMENT_MINIMUM" + + }, + { 0x0000027C, "ERROR_PNP_RESTART_ENUMERATION" }, + { 0x0000027D, "ERROR_SYSTEM_IMAGE_BAD_SIGNATURE" }, + { 0x0000027E, "ERROR_PNP_REBOOT_REQUIRED" }, + { 0x0000027F, "ERROR_INSUFFICIENT_POWER" }, + { 0x00000281, "ERROR_SYSTEM_SHUTDOWN" }, + { 0x00000282, "ERROR_PORT_NOT_SET" }, + { 0x00000283, "ERROR_DS_VERSION_CHECK_FAILURE" }, + { 0x00000284, "ERROR_RANGE_NOT_FOUND" }, + { 0x00000286, "ERROR_NOT_SAFE_MODE_DRIVER" }, + { 0x00000287, "ERROR_FAILED_DRIVER_ENTRY" }, + { 0x00000288, "ERROR_DEVICE_ENUMERATION_ERROR" }, + { 0x00000289, "ERROR_MOUNT_POINT_NOT_RESOLVED" }, + { 0x0000028A, "ERROR_INVALID_DEVICE_OBJECT_PARAMETER" }, + { 0x0000028B, "ERROR_MCA_OCCURED" }, + { 0x0000028C, "ERROR_DRIVER_DATABASE_ERROR" }, + { 0x0000028D, "ERROR_SYSTEM_HIVE_TOO_LARGE" }, + { 0x0000028E, "ERROR_DRIVER_FAILED_PRIOR_UNLOAD" }, + { 0x0000028F, "ERROR_VOLSNAP_PREPARE_HIBERNATE" }, + { 0x00000290, "ERROR_HIBERNATION_FAILURE" }, + { 0x00000299, "ERROR_FILE_SYSTEM_LIMITATION" }, + { 0x0000029C, "ERROR_ASSERTION_FAILURE" }, + { 0x0000029D, "ERROR_ACPI_ERROR" }, + { 0x0000029E, "ERROR_WOW_ASSERTION" }, + { 0x0000029F, "ERROR_PNP_BAD_MPS_TABLE" }, + { 0x000002A0, "ERROR_PNP_TRANSLATION_FAILED" }, + { 0x000002A1, "ERROR_PNP_IRQ_TRANSLATION_FAILED" }, + { 0x000002A2, "ERROR_PNP_INVALID_ID" }, + { 0x000002A3, "ERROR_WAKE_SYSTEM_DEBUGGER" }, + { 0x000002A4, "ERROR_HANDLES_CLOSED" }, + { 0x000002A5, "ERROR_EXTRANEOUS_INFORMATION" }, + { 0x000002A6, "ERROR_RXACT_COMMIT_NECESSARY" }, + { 0x000002A7, "ERROR_MEDIA_CHECK" }, + { 0x000002A8, "ERROR_GUID_SUBSTITUTION_MADE" + + }, + { 0x000002A9, "ERROR_STOPPED_ON_SYMLINK" }, + { 0x000002AA, "ERROR_LONGJUMP" }, + { 0x000002AB, "ERROR_PLUGPLAY_QUERY_VETOED" }, + { 0x000002AC, "ERROR_UNWIND_CONSOLIDATE" }, + { 0x000002AD, "ERROR_REGISTRY_HIVE_RECOVERED" }, + { 0x000002AE, "ERROR_DLL_MIGHT_BE_INSECURE" }, + { 0x000002AF, "ERROR_DLL_MIGHT_BE_INCOMPATIBLE" + + }, + { 0x000002B0, "ERROR_DBG_EXCEPTION_NOT_HANDLED" }, + { 0x000002B1, "ERROR_DBG_REPLY_LATER" }, + { 0x000002B2, "ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE" }, + { 0x000002B3, "ERROR_DBG_TERMINATE_THREAD" }, + { 0x000002B4, "ERROR_DBG_TERMINATE_PROCESS" }, + { 0x000002B5, "ERROR_DBG_CONTROL_C" }, + { 0x000002B6, "ERROR_DBG_PRINTEXCEPTION_C" }, + { 0x000002B7, "ERROR_DBG_RIPEXCEPTION" }, + { 0x000002B8, "ERROR_DBG_CONTROL_BREAK" }, + { 0x000002B9, "ERROR_DBG_COMMAND_EXCEPTION" }, + { 0x000002BA, "ERROR_OBJECT_NAME_EXISTS" }, + { 0x000002BB, "ERROR_THREAD_WAS_SUSPENDED" }, + { 0x000002BC, "ERROR_IMAGE_NOT_AT_BASE" }, + { 0x000002BD, "ERROR_RXACT_STATE_CREATED" }, + { 0x000002BE, + "ERROR_SEGMENT_NOTIFICATION" + "or moving an MS-DOS or Win16 program segment image. An exception is raised so a debugger " }, + { 0x000002BF, "ERROR_BAD_CURRENT_DIRECTORY" + + }, + { 0x000002C0, "ERROR_FT_READ_RECOVERY_FROM_BACKUP" + + }, + { 0x000002C1, "ERROR_FT_WRITE_RECOVERY" + + }, + { 0x000002C2, "ERROR_IMAGE_MACHINE_TYPE_MISMATCH" + + }, + { 0x000002C3, "ERROR_RECEIVE_PARTIAL" }, + { 0x000002C4, "ERROR_RECEIVE_EXPEDITED" }, + { 0x000002C5, "ERROR_RECEIVE_PARTIAL_EXPEDITED" + + }, + { 0x000002C6, "ERROR_EVENT_DONE" }, + { 0x000002C7, "ERROR_EVENT_PENDING" }, + { 0x000002C8, "ERROR_CHECKING_FILE_SYSTEM" }, + { 0x000002C9, "ERROR_FATAL_APP_EXIT" }, + { 0x000002CA, "ERROR_PREDEFINED_HANDLE" }, + { 0x000002CB, "ERROR_WAS_UNLOCKED" }, + { 0x000002CD, "ERROR_WAS_LOCKED" }, + { 0x000002CF, "ERROR_ALREADY_WIN32" }, + { 0x000002D0, "ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE" }, + { 0x000002D1, "ERROR_NO_YIELD_PERFORMED" }, + { 0x000002D2, "ERROR_TIMER_RESUME_IGNORED" }, + { 0x000002D3, "ERROR_ARBITRATION_UNHANDLED" }, + { 0x000002D4, "ERROR_CARDBUS_NOT_SUPPORTED" }, + { 0x000002D5, "ERROR_MP_PROCESSOR_MISMATCH" }, + { 0x000002D6, "ERROR_HIBERNATED" }, + { 0x000002D7, "ERROR_RESUME_HIBERNATION" }, + { 0x000002D8, "ERROR_FIRMWARE_UPDATED" }, + { 0x000002D9, "ERROR_DRIVERS_LEAKING_LOCKED_PAGES" }, + { 0x000002DA, "ERROR_WAKE_SYSTEM" }, + { 0x000002DF, "ERROR_ABANDONED_WAIT_0" }, + { 0x000002E4, "ERROR_ELEVATION_REQUIRED" }, + { 0x000002E5, "ERROR_REPARSE" }, + { 0x000002E6, "ERROR_OPLOCK_BREAK_IN_PROGRESS" }, + { 0x000002E7, "ERROR_VOLUME_MOUNTED" }, + { 0x000002E8, "ERROR_RXACT_COMMITTED" }, + { 0x000002E9, "ERROR_NOTIFY_CLEANUP" }, + { 0x000002EA, "ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED" + + }, + { 0x000002EB, "ERROR_PAGE_FAULT_TRANSITION" }, + { 0x000002EC, "ERROR_PAGE_FAULT_DEMAND_ZERO" }, + { 0x000002ED, "ERROR_PAGE_FAULT_COPY_ON_WRITE" }, + { 0x000002EE, "ERROR_PAGE_FAULT_GUARD_PAGE" }, + { 0x000002EF, "ERROR_PAGE_FAULT_PAGING_FILE" }, + { 0x000002F0, "ERROR_CACHE_PAGE_LOCKED" }, + { 0x000002F1, "ERROR_CRASH_DUMP" }, + { 0x000002F2, "ERROR_BUFFER_ALL_ZEROS" }, + { 0x000002F3, "ERROR_REPARSE_OBJECT" }, + { 0x000002F4, "ERROR_RESOURCE_REQUIREMENTS_CHANGED" }, + { 0x000002F5, "ERROR_TRANSLATION_COMPLETE" }, + { 0x000002F6, "ERROR_NOTHING_TO_TERMINATE" }, + { 0x000002F7, "ERROR_PROCESS_NOT_IN_JOB" }, + { 0x000002F8, "ERROR_PROCESS_IN_JOB" }, + { 0x000002F9, "ERROR_VOLSNAP_HIBERNATE_READY" }, + { 0x000002FA, "ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY" }, + { 0x000002FB, "ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED" }, + { 0x000002FC, "ERROR_INTERRUPT_STILL_CONNECTED" }, + { 0x000002FD, "ERROR_WAIT_FOR_OPLOCK" }, + { 0x000002FE, "ERROR_DBG_EXCEPTION_HANDLED" }, + { 0x000002FF, "ERROR_DBG_CONTINUE" }, + { 0x00000300, "ERROR_CALLBACK_POP_STACK" }, + { 0x00000301, "ERROR_COMPRESSION_DISABLED" }, + { 0x00000302, "ERROR_CANTFETCHBACKWARDS" }, + { 0x00000303, "ERROR_CANTSCROLLBACKWARDS" }, + { 0x00000304, "ERROR_ROWSNOTRELEASED" }, + { 0x00000305, "ERROR_BAD_ACCESSOR_FLAGS" }, + { 0x00000306, "ERROR_ERRORS_ENCOUNTERED" }, + { 0x00000307, "ERROR_NOT_CAPABLE" }, + { 0x00000308, "ERROR_REQUEST_OUT_OF_SEQUENCE" }, + { 0x00000309, "ERROR_VERSION_PARSE_ERROR" }, + { 0x0000030A, "ERROR_BADSTARTPOSITION" }, + { 0x0000030B, "ERROR_MEMORY_HARDWARE" }, + { 0x0000030C, "ERROR_DISK_REPAIR_DISABLED" }, + { 0x0000030D, "ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE" }, + { 0x0000030E, "ERROR_SYSTEM_POWERSTATE_TRANSITION" }, + { 0x0000030F, "ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION" }, + { 0x00000310, "ERROR_MCA_EXCEPTION" }, + { 0x00000311, "ERROR_ACCESS_AUDIT_BY_POLICY" }, + { 0x00000312, "ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY" }, + { 0x00000313, "ERROR_ABANDON_HIBERFILE" }, + { 0x00000314, "ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED" + + }, + { 0x00000315, "ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR" + + }, + { 0x00000316, "ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR" + + }, + { 0x000003E2, "ERROR_EA_ACCESS_DENIED" }, + { 0x000003E3, "ERROR_OPERATION_ABORTED" }, + { 0x000003E4, "ERROR_IO_INCOMPLETE" }, + { 0x000003E5, "ERROR_IO_PENDING" }, + { 0x000003E6, "ERROR_NOACCESS" }, + { 0x000003E7, "ERROR_SWAPERROR" }, + { 0x000003E9, "ERROR_STACK_OVERFLOW" }, + { 0x000003EA, "ERROR_INVALID_MESSAGE" }, + { 0x000003EB, "ERROR_CAN_NOT_COMPLETE" }, + { 0x000003EC, "ERROR_INVALID_FLAGS" }, + { 0x000003ED, "ERROR_UNRECOGNIZED_VOLUME" }, + { 0x000003EE, "ERROR_FILE_INVALID" }, + { 0x000003EF, "ERROR_FULLSCREEN_MODE" }, + { 0x000003F0, "ERROR_NO_TOKEN" }, + { 0x000003F1, "ERROR_BADDB" }, + { 0x000003F2, "ERROR_BADKEY" }, + { 0x000003F3, "ERROR_CANTOPEN" }, + { 0x000003F4, "ERROR_CANTREAD" }, + { 0x000003F5, "ERROR_CANTWRITE" }, + { 0x000003F6, "ERROR_REGISTRY_RECOVERED" }, + { 0x000003F7, "ERROR_REGISTRY_CORRUPT" }, + { 0x000003F8, "ERROR_REGISTRY_IO_FAILED" }, + { 0x000003F9, "ERROR_NOT_REGISTRY_FILE" }, + { 0x000003FA, "ERROR_KEY_DELETED" }, + { 0x000003FB, "ERROR_NO_LOG_SPACE" }, + { 0x000003FC, "ERROR_KEY_HAS_CHILDREN" }, + { 0x000003FD, "ERROR_CHILD_MUST_BE_VOLATILE" }, + { 0x000003FE, "ERROR_NOTIFY_ENUM_DIR" }, + { 0x0000041B, "ERROR_DEPENDENT_SERVICES_RUNNING" }, + { 0x0000041C, "ERROR_INVALID_SERVICE_CONTROL" }, + { 0x0000041D, "ERROR_SERVICE_REQUEST_TIMEOUT" }, + { 0x0000041E, "ERROR_SERVICE_NO_THREAD" }, + { 0x0000041F, "ERROR_SERVICE_DATABASE_LOCKED" }, + { 0x00000420, "ERROR_SERVICE_ALREADY_RUNNING" }, + { 0x00000421, "ERROR_INVALID_SERVICE_ACCOUNT" }, + { 0x00000422, "ERROR_SERVICE_DISABLED" }, + { 0x00000423, "ERROR_CIRCULAR_DEPENDENCY" }, + { 0x00000424, "ERROR_SERVICE_DOES_NOT_EXIST" }, + { 0x00000425, "ERROR_SERVICE_CANNOT_ACCEPT_CTRL" }, + { 0x00000426, "ERROR_SERVICE_NOT_ACTIVE" }, + { 0x00000427, "ERROR_FAILED_SERVICE_CONTROLLER_CONNECT" }, + { 0x00000428, "ERROR_EXCEPTION_IN_SERVICE" }, + { 0x00000429, "ERROR_DATABASE_DOES_NOT_EXIST" }, + { 0x0000042A, "ERROR_SERVICE_SPECIFIC_ERROR" }, + { 0x0000042B, "ERROR_PROCESS_ABORTED" }, + { 0x0000042C, "ERROR_SERVICE_DEPENDENCY_FAIL" }, + { 0x0000042D, "ERROR_SERVICE_LOGON_FAILED" }, + { 0x0000042E, "ERROR_SERVICE_START_HANG" }, + { 0x0000042F, "ERROR_INVALID_SERVICE_LOCK" }, + { 0x00000430, "ERROR_SERVICE_MARKED_FOR_DELETE" }, + { 0x00000431, "ERROR_SERVICE_EXISTS" }, + { 0x00000432, "ERROR_ALREADY_RUNNING_LKG" }, + { 0x00000433, "ERROR_SERVICE_DEPENDENCY_DELETED" }, + { 0x00000434, "ERROR_BOOT_ALREADY_ACCEPTED" }, + { 0x00000435, "ERROR_SERVICE_NEVER_STARTED" }, + { 0x00000436, "ERROR_DUPLICATE_SERVICE_NAME" }, + { 0x00000437, "ERROR_DIFFERENT_SERVICE_ACCOUNT" }, + { 0x00000438, "ERROR_CANNOT_DETECT_DRIVER_FAILURE" }, + { 0x00000439, "ERROR_CANNOT_DETECT_PROCESS_ABORT" }, + { 0x0000043A, "ERROR_NO_RECOVERY_PROGRAM" }, + { 0x0000043B, "ERROR_SERVICE_NOT_IN_EXE" }, + { 0x0000043C, "ERROR_NOT_SAFEBOOT_SERVICE" }, + { 0x0000044C, "ERROR_END_OF_MEDIA" }, + { 0x0000044D, "ERROR_FILEMARK_DETECTED" }, + { 0x0000044E, "ERROR_BEGINNING_OF_MEDIA" }, + { 0x0000044F, "ERROR_SETMARK_DETECTED" }, + { 0x00000450, "ERROR_NO_DATA_DETECTED" }, + { 0x00000451, "ERROR_PARTITION_FAILURE" }, + { 0x00000452, "ERROR_INVALID_BLOCK_LENGTH" }, + { 0x00000453, "ERROR_DEVICE_NOT_PARTITIONED" }, + { 0x00000454, "ERROR_UNABLE_TO_LOCK_MEDIA" }, + { 0x00000455, "ERROR_UNABLE_TO_UNLOAD_MEDIA" }, + { 0x00000456, "ERROR_MEDIA_CHANGED" }, + { 0x00000457, "ERROR_BUS_RESET" }, + { 0x00000458, "ERROR_NO_MEDIA_IN_DRIVE" }, + { 0x00000459, "ERROR_NO_UNICODE_TRANSLATION" }, + { 0x0000045A, "ERROR_DLL_INIT_FAILED" }, + { 0x0000045B, "ERROR_SHUTDOWN_IN_PROGRESS" }, + { 0x0000045C, "ERROR_NO_SHUTDOWN_IN_PROGRESS" }, + { 0x0000045D, "ERROR_IO_DEVICE" }, + { 0x0000045E, "ERROR_SERIAL_NO_DEVICE" }, + { 0x0000045F, "ERROR_IRQ_BUSY" }, + { 0x00000460, "ERROR_MORE_WRITES" + "port. (The IOCTL_SERIAL_XOFF_COUNTER reached zero.)" }, + { 0x00000461, "ERROR_COUNTER_TIMEOUT" + "expired. (The IOCTL_SERIAL_XOFF_COUNTER did not reach zero.)" }, + { 0x00000462, "ERROR_FLOPPY_ID_MARK_NOT_FOUND" }, + { 0x00000463, "ERROR_FLOPPY_WRONG_CYLINDER" }, + { 0x00000464, "ERROR_FLOPPY_UNKNOWN_ERROR" }, + { 0x00000465, "ERROR_FLOPPY_BAD_REGISTERS" }, + { 0x00000466, "ERROR_DISK_RECALIBRATE_FAILED" }, + { 0x00000467, "ERROR_DISK_OPERATION_FAILED" }, + { 0x00000468, "ERROR_DISK_RESET_FAILED" }, + { 0x00000469, "ERROR_EOM_OVERFLOW" }, + { 0x0000046A, "ERROR_NOT_ENOUGH_SERVER_MEMORY" }, + { 0x0000046B, "ERROR_POSSIBLE_DEADLOCK" }, + { 0x0000046C, "ERROR_MAPPED_ALIGNMENT" }, + { 0x00000474, "ERROR_SET_POWER_STATE_VETOED" }, + { 0x00000475, "ERROR_SET_POWER_STATE_FAILED" }, + { 0x00000476, "ERROR_TOO_MANY_LINKS" }, + { 0x0000047E, "ERROR_OLD_WIN_VERSION" }, + { 0x0000047F, "ERROR_APP_WRONG_OS" }, + { 0x00000480, "ERROR_SINGLE_INSTANCE_APP" }, + { 0x00000481, "ERROR_RMODE_APP" }, + { 0x00000482, "ERROR_INVALID_DLL" }, + { 0x00000483, "ERROR_NO_ASSOCIATION" }, + { 0x00000484, "ERROR_DDE_FAIL" }, + { 0x00000485, "ERROR_DLL_NOT_FOUND" }, + { 0x00000486, "ERROR_NO_MORE_USER_HANDLES" }, + { 0x00000487, "ERROR_MESSAGE_SYNC_ONLY" }, + { 0x00000488, "ERROR_SOURCE_ELEMENT_EMPTY" }, + { 0x00000489, "ERROR_DESTINATION_ELEMENT_FULL" }, + { 0x0000048A, "ERROR_ILLEGAL_ELEMENT_ADDRESS" }, + { 0x0000048B, "ERROR_MAGAZINE_NOT_PRESENT" }, + { 0x0000048C, "ERROR_DEVICE_REINITIALIZATION_NEEDED" }, + { 0x0000048D, "ERROR_DEVICE_REQUIRES_CLEANING" }, + { 0x0000048E, "ERROR_DEVICE_DOOR_OPEN" }, + { 0x0000048F, "ERROR_DEVICE_NOT_CONNECTED" }, + { 0x00000490, "ERROR_NOT_FOUND" }, + { 0x00000491, "ERROR_NO_MATCH" }, + { 0x00000492, "ERROR_SET_NOT_FOUND" }, + { 0x00000493, "ERROR_POINT_NOT_FOUND" }, + { 0x00000494, "ERROR_NO_TRACKING_SERVICE" }, + { 0x00000495, "ERROR_NO_VOLUME_ID" }, + { 0x00000497, "ERROR_UNABLE_TO_REMOVE_REPLACED" }, + { 0x00000498, "ERROR_UNABLE_TO_MOVE_REPLACEMENT" }, + { 0x00000499, "ERROR_UNABLE_TO_MOVE_REPLACEMENT_2" }, + { 0x0000049A, "ERROR_JOURNAL_DELETE_IN_PROGRESS" }, + { 0x0000049B, "ERROR_JOURNAL_NOT_ACTIVE" }, + { 0x0000049C, "ERROR_POTENTIAL_FILE_FOUND" }, + { 0x0000049D, "ERROR_JOURNAL_ENTRY_DELETED" }, + { 0x000004A6, "ERROR_SHUTDOWN_IS_SCHEDULED" }, + { 0x000004A7, "ERROR_SHUTDOWN_USERS_LOGGED_ON" }, + { 0x000004B0, "ERROR_BAD_DEVICE" }, + { 0x000004B1, "ERROR_CONNECTION_UNAVAIL" }, + { 0x000004B2, "ERROR_DEVICE_ALREADY_REMEMBERED" }, + { 0x000004B3, "ERROR_NO_NET_OR_BAD_PATH" }, + { 0x000004B4, "ERROR_BAD_PROVIDER" }, + { 0x000004B5, "ERROR_CANNOT_OPEN_PROFILE" }, + { 0x000004B6, "ERROR_BAD_PROFILE" }, + { 0x000004B7, "ERROR_NOT_CONTAINER" }, + { 0x000004B8, "ERROR_EXTENDED_ERROR" }, + { 0x000004B9, "ERROR_INVALID_GROUPNAME" }, + { 0x000004BA, "ERROR_INVALID_COMPUTERNAME" }, + { 0x000004BB, "ERROR_INVALID_EVENTNAME" }, + { 0x000004BC, "ERROR_INVALID_DOMAINNAME" }, + { 0x000004BD, "ERROR_INVALID_SERVICENAME" }, + { 0x000004BE, "ERROR_INVALID_NETNAME" }, + { 0x000004BF, "ERROR_INVALID_SHARENAME" }, + { 0x000004C0, "ERROR_INVALID_PASSWORDNAME" }, + { 0x000004C1, "ERROR_INVALID_MESSAGENAME" }, + { 0x000004C2, "ERROR_INVALID_MESSAGEDEST" }, + { 0x000004C3, "ERROR_SESSION_CREDENTIAL_CONFLICT" }, + { 0x000004C4, "ERROR_REMOTE_SESSION_LIMIT_EXCEEDED" }, + { 0x000004C5, "ERROR_DUP_DOMAINNAME" }, + { 0x000004C6, "ERROR_NO_NETWORK" }, + { 0x000004C7, "ERROR_CANCELLED" }, + { 0x000004C8, "ERROR_USER_MAPPED_FILE" }, + { 0x000004C9, "ERROR_CONNECTION_REFUSED" }, + { 0x000004CA, "ERROR_GRACEFUL_DISCONNECT" }, + { 0x000004CB, "ERROR_ADDRESS_ALREADY_ASSOCIATED" }, + { 0x000004CC, "ERROR_ADDRESS_NOT_ASSOCIATED" }, + { 0x000004CD, "ERROR_CONNECTION_INVALID" }, + { 0x000004CE, "ERROR_CONNECTION_ACTIVE" }, + { 0x000004CF, "ERROR_NETWORK_UNREACHABLE" }, + { 0x000004D0, "ERROR_HOST_UNREACHABLE" }, + { 0x000004D1, "ERROR_PROTOCOL_UNREACHABLE" }, + { 0x000004D2, "ERROR_PORT_UNREACHABLE" }, + { 0x000004D3, "ERROR_REQUEST_ABORTED" }, + { 0x000004D4, "ERROR_CONNECTION_ABORTED" }, + { 0x000004D5, "ERROR_RETRY" }, + { 0x000004D6, "ERROR_CONNECTION_COUNT_LIMIT" }, + { 0x000004D7, "ERROR_LOGIN_TIME_RESTRICTION" }, + { 0x000004D8, "ERROR_LOGIN_WKSTA_RESTRICTION" }, + { 0x000004D9, "ERROR_INCORRECT_ADDRESS" }, + { 0x000004DA, "ERROR_ALREADY_REGISTERED" }, + { 0x000004DB, "ERROR_SERVICE_NOT_FOUND" }, + { 0x000004DC, "ERROR_NOT_AUTHENTICATED" }, + { 0x000004DD, "ERROR_NOT_LOGGED_ON" }, + { 0x000004DE, "ERROR_CONTINUE" }, + { 0x000004DF, "ERROR_ALREADY_INITIALIZED" }, + { 0x000004E0, "ERROR_NO_MORE_DEVICES" }, + { 0x000004E1, "ERROR_NO_SUCH_SITE" }, + { 0x000004E2, "ERROR_DOMAIN_CONTROLLER_EXISTS" }, + { 0x000004E3, "ERROR_ONLY_IF_CONNECTED" }, + { 0x000004E4, "ERROR_OVERRIDE_NOCHANGES" }, + { 0x000004E5, "ERROR_BAD_USER_PROFILE" }, + { 0x000004E6, "ERROR_NOT_SUPPORTED_ON_SBS" }, + { 0x000004E7, "ERROR_SERVER_SHUTDOWN_IN_PROGRESS" }, + { 0x000004E8, "ERROR_HOST_DOWN" }, + { 0x000004E9, "ERROR_NON_ACCOUNT_SID" }, + { 0x000004EA, "ERROR_NON_DOMAIN_SID" }, + { 0x000004EB, "ERROR_APPHELP_BLOCK" }, + { 0x000004EC, "ERROR_ACCESS_DISABLED_BY_POLICY" }, + { 0x000004ED, "ERROR_REG_NAT_CONSUMPTION" }, + { 0x000004EE, "ERROR_CSCSHARE_OFFLINE" }, + { 0x000004EF, "ERROR_PKINIT_FAILURE" }, + { 0x000004F0, "ERROR_SMARTCARD_SUBSYSTEM_FAILURE" }, + { 0x000004F1, "ERROR_DOWNGRADE_DETECTED" }, + { 0x000004F7, "ERROR_MACHINE_LOCKED" }, + { 0x000004F9, "ERROR_CALLBACK_SUPPLIED_INVALID_DATA" }, + { 0x000004FA, "ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED" }, + { 0x000004FB, "ERROR_DRIVER_BLOCKED" }, + { 0x000004FC, "ERROR_INVALID_IMPORT_OF_NON_DLL" }, + { 0x000004FD, "ERROR_ACCESS_DISABLED_WEBBLADE" }, + { 0x000004FE, "ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER" }, + { 0x000004FF, "ERROR_RECOVERY_FAILURE" }, + { 0x00000500, "ERROR_ALREADY_FIBER" }, + { 0x00000501, "ERROR_ALREADY_THREAD" }, + { 0x00000502, "ERROR_STACK_BUFFER_OVERRUN" }, + { 0x00000503, "ERROR_PARAMETER_QUOTA_EXCEEDED" }, + { 0x00000504, "ERROR_DEBUGGER_INACTIVE" }, + { 0x00000505, "ERROR_DELAY_LOAD_FAILED" }, + { 0x00000506, "ERROR_VDM_DISALLOWED" + "16-bit applications. Check your permissions with your system administrator." }, + { 0x00000507, "ERROR_UNIDENTIFIED_ERROR" }, + { 0x00000508, "ERROR_INVALID_CRUNTIME_PARAMETER" }, + { 0x00000509, "ERROR_BEYOND_VDL" }, + { 0x0000050A, "ERROR_INCOMPATIBLE_SERVICE_SID_TYPE" }, + { 0x0000050B, "ERROR_DRIVER_PROCESS_TERMINATED" }, + { 0x0000050C, "ERROR_IMPLEMENTATION_LIMIT" }, + { 0x0000050D, "ERROR_PROCESS_IS_PROTECTED" }, + { 0x0000050E, "ERROR_SERVICE_NOTIFY_CLIENT_LAGGING" }, + { 0x0000050F, "ERROR_DISK_QUOTA_EXCEEDED" }, + { 0x00000510, "ERROR_CONTENT_BLOCKED" }, + { 0x00000511, "ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE" }, + { 0x00000513, "ERROR_INVALID_LABEL" }, + { 0x00000514, "ERROR_NOT_ALL_ASSIGNED" }, + { 0x00000515, "ERROR_SOME_NOT_MAPPED" }, + { 0x00000516, "ERROR_NO_QUOTAS_FOR_ACCOUNT" }, + { 0x00000517, "ERROR_LOCAL_USER_SESSION_KEY" }, + { 0x00000518, "ERROR_NULL_LM_PASSWORD" }, + { 0x00000519, "ERROR_UNKNOWN_REVISION" }, + { 0x0000051A, "ERROR_REVISION_MISMATCH" }, + { 0x0000051B, "ERROR_INVALID_OWNER" }, + { 0x0000051C, "ERROR_INVALID_PRIMARY_GROUP" }, + { 0x0000051D, "ERROR_NO_IMPERSONATION_TOKEN" }, + { 0x0000051E, "ERROR_CANT_DISABLE_MANDATORY" }, + { 0x0000051F, "ERROR_NO_LOGON_SERVERS" }, + { 0x00000520, "ERROR_NO_SUCH_LOGON_SESSION" }, + { 0x00000521, "ERROR_NO_SUCH_PRIVILEGE" }, + { 0x00000522, "ERROR_PRIVILEGE_NOT_HELD" }, + { 0x00000523, "ERROR_INVALID_ACCOUNT_NAME" }, + { 0x00000524, "ERROR_USER_EXISTS" }, + { 0x00000525, "ERROR_NO_SUCH_USER" }, + { 0x00000526, "ERROR_GROUP_EXISTS" }, + { 0x00000527, "ERROR_NO_SUCH_GROUP" }, + { 0x00000528, "ERROR_MEMBER_IN_GROUP" }, + { 0x00000529, "ERROR_MEMBER_NOT_IN_GROUP" }, + { 0x0000052A, "ERROR_LAST_ADMIN" }, + { 0x0000052B, "ERROR_WRONG_PASSWORD" }, + { 0x0000052C, "ERROR_ILL_FORMED_PASSWORD" }, + { 0x0000052D, "ERROR_PASSWORD_RESTRICTION" }, + { 0x0000052E, "ERROR_LOGON_FAILURE" }, + { 0x0000052F, "ERROR_ACCOUNT_RESTRICTION" }, + { 0x00000530, "ERROR_INVALID_LOGON_HOURS" }, + { 0x00000531, "ERROR_INVALID_WORKSTATION" }, + { 0x00000532, "ERROR_PASSWORD_EXPIRED" }, + { 0x00000533, "ERROR_ACCOUNT_DISABLED" }, + { 0x00000534, "ERROR_NONE_MAPPED" }, + { 0x00000535, "ERROR_TOO_MANY_LUIDS_REQUESTED" }, + { 0x00000536, "ERROR_LUIDS_EXHAUSTED" }, + { 0x00000537, "ERROR_INVALID_SUB_AUTHORITY" }, + { 0x00000538, "ERROR_INVALID_ACL" }, + { 0x00000539, "ERROR_INVALID_SID" }, + { 0x0000053A, "ERROR_INVALID_SECURITY_DESCR" }, + { 0x0000053C, "ERROR_BAD_INHERITANCE_ACL" }, + { 0x0000053D, "ERROR_SERVER_DISABLED" }, + { 0x0000053E, "ERROR_SERVER_NOT_DISABLED" }, + { 0x0000053F, "ERROR_INVALID_ID_AUTHORITY" }, + { 0x00000540, "ERROR_ALLOTTED_SPACE_EXCEEDED" }, + { 0x00000541, "ERROR_INVALID_GROUP_ATTRIBUTES" }, + { 0x00000542, "ERROR_BAD_IMPERSONATION_LEVEL" }, + { 0x00000543, "ERROR_CANT_OPEN_ANONYMOUS" }, + { 0x00000544, "ERROR_BAD_VALIDATION_CLASS" }, + { 0x00000545, "ERROR_BAD_TOKEN_TYPE" }, + { 0x00000546, "ERROR_NO_SECURITY_ON_OBJECT" }, + { 0x00000547, "ERROR_CANT_ACCESS_DOMAIN_INFO" }, + { 0x00000548, "ERROR_INVALID_SERVER_STATE" }, + { 0x00000549, "ERROR_INVALID_DOMAIN_STATE" }, + { 0x0000054A, "ERROR_INVALID_DOMAIN_ROLE" }, + { 0x0000054B, "ERROR_NO_SUCH_DOMAIN" }, + { 0x0000054C, "ERROR_DOMAIN_EXISTS" }, + { 0x0000054D, "ERROR_DOMAIN_LIMIT_EXCEEDED" }, + { 0x0000054E, "ERROR_INTERNAL_DB_CORRUPTION" }, + { 0x0000054F, "ERROR_INTERNAL_ERROR" }, + { 0x00000550, "ERROR_GENERIC_NOT_MAPPED" }, + { 0x00000551, "ERROR_BAD_DESCRIPTOR_FORMAT" }, + { 0x00000552, "ERROR_NOT_LOGON_PROCESS" }, + { 0x00000553, "ERROR_LOGON_SESSION_EXISTS" }, + { 0x00000554, "ERROR_NO_SUCH_PACKAGE" }, + { 0x00000555, "ERROR_BAD_LOGON_SESSION_STATE" }, + { 0x00000556, "ERROR_LOGON_SESSION_COLLISION" }, + { 0x00000557, "ERROR_INVALID_LOGON_TYPE" }, + { 0x00000558, "ERROR_CANNOT_IMPERSONATE" }, + { 0x00000559, "ERROR_RXACT_INVALID_STATE" }, + { 0x0000055A, "ERROR_RXACT_COMMIT_FAILURE" }, + { 0x0000055B, "ERROR_SPECIAL_ACCOUNT" }, + { 0x0000055C, "ERROR_SPECIAL_GROUP" }, + { 0x0000055D, "ERROR_SPECIAL_USER" }, + { 0x0000055E, "ERROR_MEMBERS_PRIMARY_GROUP" }, + { 0x0000055F, "ERROR_TOKEN_ALREADY_IN_USE" }, + { 0x00000560, "ERROR_NO_SUCH_ALIAS" }, + { 0x00000561, "ERROR_MEMBER_NOT_IN_ALIAS" }, + { 0x00000562, "ERROR_MEMBER_IN_ALIAS" }, + { 0x00000563, "ERROR_ALIAS_EXISTS" }, + { 0x00000564, "ERROR_LOGON_NOT_GRANTED" }, + { 0x00000565, "ERROR_TOO_MANY_SECRETS" }, + { 0x00000566, "ERROR_SECRET_TOO_LONG" }, + { 0x00000567, "ERROR_INTERNAL_DB_ERROR" }, + { 0x00000568, "ERROR_TOO_MANY_CONTEXT_IDS" }, + { 0x00000569, "ERROR_LOGON_TYPE_NOT_GRANTED" }, + { 0x0000056A, "ERROR_NT_CROSS_ENCRYPTION_REQUIRED" }, + { 0x0000056B, "ERROR_NO_SUCH_MEMBER" }, + { 0x0000056C, "ERROR_INVALID_MEMBER" }, + { 0x0000056D, "ERROR_TOO_MANY_SIDS" }, + { 0x0000056E, "ERROR_LM_CROSS_ENCRYPTION_REQUIRED" }, + { 0x0000056F, "ERROR_NO_INHERITANCE" }, + { 0x00000570, "ERROR_FILE_CORRUPT" }, + { 0x00000571, "ERROR_DISK_CORRUPT" }, + { 0x00000572, "ERROR_NO_USER_SESSION_KEY" }, + { 0x00000573, "ERROR_LICENSE_QUOTA_EXCEEDED" }, + { 0x00000574, "ERROR_WRONG_TARGET_NAME" }, + { 0x00000575, "ERROR_MUTUAL_AUTH_FAILED" }, + { 0x00000576, "ERROR_TIME_SKEW" }, + { 0x00000577, "ERROR_CURRENT_DOMAIN_NOT_ALLOWED" }, + { 0x00000578, "ERROR_INVALID_WINDOW_HANDLE" }, + { 0x00000579, "ERROR_INVALID_MENU_HANDLE" }, + { 0x0000057A, "ERROR_INVALID_CURSOR_HANDLE" }, + { 0x0000057B, "ERROR_INVALID_ACCEL_HANDLE" }, + { 0x0000057C, "ERROR_INVALID_HOOK_HANDLE" }, + { 0x0000057D, "ERROR_INVALID_DWP_HANDLE" }, + { 0x0000057E, "ERROR_TLW_WITH_WSCHILD" }, + { 0x0000057F, "ERROR_CANNOT_FIND_WND_CLASS" }, + { 0x00000580, "ERROR_WINDOW_OF_OTHER_THREAD" }, + { 0x00000581, "ERROR_HOTKEY_ALREADY_REGISTERED" }, + { 0x00000582, "ERROR_CLASS_ALREADY_EXISTS" }, + { 0x00000583, "ERROR_CLASS_DOES_NOT_EXIST" }, + { 0x00000584, "ERROR_CLASS_HAS_WINDOWS" }, + { 0x00000585, "ERROR_INVALID_INDEX" }, + { 0x00000586, "ERROR_INVALID_ICON_HANDLE" }, + { 0x00000587, "ERROR_PRIVATE_DIALOG_INDEX" }, + { 0x00000588, "ERROR_LISTBOX_ID_NOT_FOUND" }, + { 0x00000589, "ERROR_NO_WILDCARD_CHARACTERS" }, + { 0x0000058A, "ERROR_CLIPBOARD_NOT_OPEN" }, + { 0x0000058B, "ERROR_HOTKEY_NOT_REGISTERED" }, + { 0x0000058C, "ERROR_WINDOW_NOT_DIALOG" }, + { 0x0000058D, "ERROR_CONTROL_ID_NOT_FOUND" }, + { 0x0000058E, "ERROR_INVALID_COMBOBOX_MESSAGE" }, + { 0x0000058F, "ERROR_WINDOW_NOT_COMBOBOX" }, + { 0x00000590, "ERROR_INVALID_EDIT_HEIGHT" }, + { 0x00000591, "ERROR_DC_NOT_FOUND" }, + { 0x00000592, "ERROR_INVALID_HOOK_FILTER" }, + { 0x00000593, "ERROR_INVALID_FILTER_PROC" }, + { 0x00000594, "ERROR_HOOK_NEEDS_HMOD" }, + { 0x00000595, "ERROR_GLOBAL_ONLY_HOOK" }, + { 0x00000596, "ERROR_JOURNAL_HOOK_SET" }, + { 0x00000597, "ERROR_HOOK_NOT_INSTALLED" }, + { 0x00000598, "ERROR_INVALID_LB_MESSAGE" }, + { 0x00000599, "ERROR_SETCOUNT_ON_BAD_LB_SETCOUNT" }, + { 0x0000059A, "ERROR_LB_WITHOUT_TABSTOPS" }, + { 0x0000059B, "ERROR_DESTROY_OBJECT_OF_OTHER_THREAD" }, + { 0x0000059C, "ERROR_CHILD_WINDOW_MENU" }, + { 0x0000059D, "ERROR_NO_SYSTEM_MENU" }, + { 0x0000059E, "ERROR_INVALID_MSGBOX_STYLE" }, + { 0x0000059F, "ERROR_INVALID_SPI_VALUE" }, + { 0x000005A0, "ERROR_SCREEN_ALREADY_LOCKED" }, + { 0x000005A1, "ERROR_HWNDS_HAVE_DIFF_PARENT" }, + { 0x000005A2, "ERROR_NOT_CHILD_WINDOW" }, + { 0x000005A3, "ERROR_INVALID_GW_COMMAND" }, + { 0x000005A4, "ERROR_INVALID_THREAD_ID" }, + { 0x000005A5, "ERROR_NON_MDICHILD_WINDOW" }, + { 0x000005A6, "ERROR_POPUP_ALREADY_ACTIVE" }, + { 0x000005A7, "ERROR_NO_SCROLLBARS" }, + { 0x000005A8, "ERROR_INVALID_SCROLLBAR_RANGE" }, + { 0x000005A9, "ERROR_INVALID_SHOWWIN_COMMAND" }, + { 0x000005AA, "ERROR_NO_SYSTEM_RESOURCES" }, + { 0x000005AB, "ERROR_NONPAGED_SYSTEM_RESOURCES" }, + { 0x000005AC, "ERROR_PAGED_SYSTEM_RESOURCES" }, + { 0x000005AD, "ERROR_WORKING_SET_QUOTA" }, + { 0x000005AE, "ERROR_PAGEFILE_QUOTA" }, + { 0x000005AF, "ERROR_COMMITMENT_LIMIT" }, + { 0x000005B0, "ERROR_MENU_ITEM_NOT_FOUND" }, + { 0x000005B1, "ERROR_INVALID_KEYBOARD_HANDLE" }, + { 0x000005B2, "ERROR_HOOK_TYPE_NOT_ALLOWED" }, + { 0x000005B3, "ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION" }, + { 0x000005B4, "ERROR_TIMEOUT" }, + { 0x000005B5, "ERROR_INVALID_MONITOR_HANDLE" }, + { 0x000005B6, "ERROR_INCORRECT_SIZE" }, + { 0x000005B7, "ERROR_SYMLINK_CLASS_DISABLED" }, + { 0x000005B8, "ERROR_SYMLINK_NOT_SUPPORTED" }, + { 0x000005DC, "ERROR_EVENTLOG_FILE_CORRUPT" }, + { 0x000005DD, "ERROR_EVENTLOG_CANT_START" }, + { 0x000005DE, "ERROR_LOG_FILE_FULL" }, + { 0x000005DF, "ERROR_EVENTLOG_FILE_CHANGED" }, + { 0x0000060E, "ERROR_INVALID_TASK_NAME" }, + { 0x0000060F, "ERROR_INVALID_TASK_INDEX" }, + { 0x00000610, "ERROR_THREAD_ALREADY_IN_TASK" }, + { 0x00000641, "ERROR_INSTALL_SERVICE_FAILURE" }, + { 0x00000642, "ERROR_INSTALL_USEREXIT" }, + { 0x00000643, "ERROR_INSTALL_FAILURE" }, + { 0x00000644, "ERROR_INSTALL_SUSPEND" }, + { 0x00000645, "ERROR_UNKNOWN_PRODUCT" }, + { 0x00000646, "ERROR_UNKNOWN_FEATURE" }, + { 0x00000647, "ERROR_UNKNOWN_COMPONENT" }, + { 0x00000648, "ERROR_UNKNOWN_PROPERTY" }, + { 0x00000649, "ERROR_INVALID_HANDLE_STATE" }, + { 0x0000064A, "ERROR_BAD_CONFIGURATION" }, + { 0x0000064B, "ERROR_INDEX_ABSENT" }, + { 0x0000064C, "ERROR_INSTALL_SOURCE_ABSENT" }, + { 0x0000064D, "ERROR_INSTALL_PACKAGE_VERSION" }, + { 0x0000064E, "ERROR_PRODUCT_UNINSTALLED" }, + { 0x0000064F, "ERROR_BAD_QUERY_SYNTAX" }, + { 0x00000650, "ERROR_INVALID_FIELD" }, + { 0x00000651, "ERROR_DEVICE_REMOVED" }, + { 0x00000652, "ERROR_INSTALL_ALREADY_RUNNING" }, + { 0x00000653, "ERROR_INSTALL_PACKAGE_OPEN_FAILED" }, + { 0x00000654, "ERROR_INSTALL_PACKAGE_INVALID" }, + { 0x00000655, "ERROR_INSTALL_UI_FAILURE" }, + { 0x00000656, "ERROR_INSTALL_LOG_FAILURE" }, + { 0x00000657, "ERROR_INSTALL_LANGUAGE_UNSUPPORTED" }, + { 0x00000658, "ERROR_INSTALL_TRANSFORM_FAILURE" }, + { 0x00000659, "ERROR_INSTALL_PACKAGE_REJECTED" }, + { 0x0000065A, "ERROR_FUNCTION_NOT_CALLED" }, + { 0x0000065B, "ERROR_FUNCTION_FAILED" }, + { 0x0000065C, "ERROR_INVALID_TABLE" }, + { 0x0000065D, "ERROR_DATATYPE_MISMATCH" }, + { 0x0000065E, "ERROR_UNSUPPORTED_TYPE" }, + { 0x0000065F, "ERROR_CREATE_FAILED" }, + { 0x00000660, "ERROR_INSTALL_TEMP_UNWRITABLE" }, + { 0x00000661, "ERROR_INSTALL_PLATFORM_UNSUPPORTED" }, + { 0x00000662, "ERROR_INSTALL_NOTUSED" }, + { 0x00000663, "ERROR_PATCH_PACKAGE_OPEN_FAILED" }, + { 0x00000664, "ERROR_PATCH_PACKAGE_INVALID" }, + { 0x00000665, "ERROR_PATCH_PACKAGE_UNSUPPORTED" }, + { 0x00000666, "ERROR_PRODUCT_VERSION" }, + { 0x00000667, "ERROR_INVALID_COMMAND_LINE" }, + { 0x00000668, "ERROR_INSTALL_REMOTE_DISALLOWED" }, + { 0x00000669, "ERROR_SUCCESS_REBOOT_INITIATED" }, + { 0x0000066A, "ERROR_PATCH_TARGET_NOT_FOUND" }, + { 0x0000066B, "ERROR_PATCH_PACKAGE_REJECTED" }, + { 0x0000066C, "ERROR_INSTALL_TRANSFORM_REJECTED" }, + { 0x0000066D, "ERROR_INSTALL_REMOTE_PROHIBITED" }, + { 0x0000066E, "ERROR_PATCH_REMOVAL_UNSUPPORTED" }, + { 0x0000066F, "ERROR_UNKNOWN_PATCH" }, + { 0x00000670, "ERROR_PATCH_NO_SEQUENCE" }, + { 0x00000671, "ERROR_PATCH_REMOVAL_DISALLOWED" }, + { 0x00000672, "ERROR_INVALID_PATCH_XML" }, + { 0x00000673, "ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT" }, + { 0x00000674, "ERROR_INSTALL_SERVICE_SAFEBOOT" }, + { 0x000006A4, "RPC_S_INVALID_STRING_BINDING" }, + { 0x000006A5, "RPC_S_WRONG_KIND_OF_BINDING" }, + { 0x000006A6, "RPC_S_INVALID_BINDING" }, + { 0x000006A7, "RPC_S_PROTSEQ_NOT_SUPPORTED" }, + { 0x000006A8, "RPC_S_INVALID_RPC_PROTSEQ" }, + { 0x000006A9, "RPC_S_INVALID_STRING_UUID" }, + { 0x000006AA, "RPC_S_INVALID_ENDPOINT_FORMAT" }, + { 0x000006AB, "RPC_S_INVALID_NET_ADDR" }, + { 0x000006AC, "RPC_S_NO_ENDPOINT_FOUND" }, + { 0x000006AD, "RPC_S_INVALID_TIMEOUT" }, + { 0x000006AE, "RPC_S_OBJECT_NOT_FOUND" }, + { 0x000006AF, "RPC_S_ALREADY_REGISTERED" }, + { 0x000006B0, "RPC_S_TYPE_ALREADY_REGISTERED" }, + { 0x000006B1, "RPC_S_ALREADY_LISTENING" }, + { 0x000006B2, "RPC_S_NO_PROTSEQS_REGISTERED" }, + { 0x000006B3, "RPC_S_NOT_LISTENING" }, + { 0x000006B4, "RPC_S_UNKNOWN_MGR_TYPE" }, + { 0x000006B5, "RPC_S_UNKNOWN_IF" }, + { 0x000006B6, "RPC_S_NO_BINDINGS" }, + { 0x000006B7, "RPC_S_NO_PROTSEQS" }, + { 0x000006B8, "RPC_S_CANT_CREATE_ENDPOINT" }, + { 0x000006B9, "RPC_S_OUT_OF_RESOURCES" }, + { 0x000006BA, "RPC_S_SERVER_UNAVAILABLE" }, + { 0x000006BB, "RPC_S_SERVER_TOO_BUSY" }, + { 0x000006BC, "RPC_S_INVALID_NETWORK_OPTIONS" }, + { 0x000006BD, "RPC_S_NO_CALL_ACTIVE" }, + { 0x000006BE, "RPC_S_CALL_FAILED" }, + { 0x000006BF, "RPC_S_CALL_FAILED_DNE" }, + { 0x000006C0, "RPC_S_PROTOCOL_ERROR" }, + { 0x000006C1, "RPC_S_PROXY_ACCESS_DENIED" }, + { 0x000006C2, "RPC_S_UNSUPPORTED_TRANS_SYN" }, + { 0x000006C4, "RPC_S_UNSUPPORTED_TYPE" }, + { 0x000006C5, "RPC_S_INVALID_TAG" }, + { 0x000006C6, "RPC_S_INVALID_BOUND" }, + { 0x000006C7, "RPC_S_NO_ENTRY_NAME" }, + { 0x000006C8, "RPC_S_INVALID_NAME_SYNTAX" }, + { 0x000006C9, "RPC_S_UNSUPPORTED_NAME_SYNTAX" }, + { 0x000006CB, "RPC_S_UUID_NO_ADDRESS" }, + { 0x000006CC, "RPC_S_DUPLICATE_ENDPOINT" }, + { 0x000006CD, "RPC_S_UNKNOWN_AUTHN_TYPE" }, + { 0x000006CE, "RPC_S_MAX_CALLS_TOO_SMALL" }, + { 0x000006CF, "RPC_S_STRING_TOO_LONG" }, + { 0x000006D0, "RPC_S_PROTSEQ_NOT_FOUND" }, + { 0x000006D1, "RPC_S_PROCNUM_OUT_OF_RANGE" }, + { 0x000006D2, "RPC_S_BINDING_HAS_NO_AUTH" }, + { 0x000006D3, "RPC_S_UNKNOWN_AUTHN_SERVICE" }, + { 0x000006D4, "RPC_S_UNKNOWN_AUTHN_LEVEL" }, + { 0x000006D5, "RPC_S_INVALID_AUTH_IDENTITY" }, + { 0x000006D6, "RPC_S_UNKNOWN_AUTHZ_SERVICE" }, + { 0x000006D7, "EPT_S_INVALID_ENTRY" }, + { 0x000006D8, "EPT_S_CANT_PERFORM_OP" }, + { 0x000006D9, "EPT_S_NOT_REGISTERED" }, + { 0x000006DA, "RPC_S_NOTHING_TO_EXPORT" }, + { 0x000006DB, "RPC_S_INCOMPLETE_NAME" }, + { 0x000006DC, "RPC_S_INVALID_VERS_OPTION" }, + { 0x000006DD, "RPC_S_NO_MORE_MEMBERS" }, + { 0x000006DE, "RPC_S_NOT_ALL_OBJS_UNEXPORTED" }, + { 0x000006DF, "RPC_S_INTERFACE_NOT_FOUND" }, + { 0x000006E0, "RPC_S_ENTRY_ALREADY_EXISTS" }, + { 0x000006E1, "RPC_S_ENTRY_NOT_FOUND" }, + { 0x000006E2, "RPC_S_NAME_SERVICE_UNAVAILABLE" }, + { 0x000006E3, "RPC_S_INVALID_NAF_ID" }, + { 0x000006E4, "RPC_S_CANNOT_SUPPORT" }, + { 0x000006E5, "RPC_S_NO_CONTEXT_AVAILABLE" }, + { 0x000006E6, "RPC_S_INTERNAL_ERROR" }, + { 0x000006E7, "RPC_S_ZERO_DIVIDE" }, + { 0x000006E8, "RPC_S_ADDRESS_ERROR" }, + { 0x000006E9, "RPC_S_FP_DIV_ZERO" }, + { 0x000006EA, "RPC_S_FP_UNDERFLOW" }, + { 0x000006EB, "RPC_S_FP_OVERFLOW" }, + { 0x000006EC, "RPC_X_NO_MORE_ENTRIES" }, + { 0x000006ED, "RPC_X_SS_CHAR_TRANS_OPEN_FAIL" }, + { 0x000006EE, "RPC_X_SS_CHAR_TRANS_SHORT_FILE" }, + { 0x000006EF, "RPC_X_SS_IN_NULL_CONTEXT" }, + { 0x000006F1, "RPC_X_SS_CONTEXT_DAMAGED" }, + { 0x000006F2, "RPC_X_SS_HANDLES_MISMATCH" }, + { 0x000006F3, "RPC_X_SS_CANNOT_GET_CALL_HANDLE" }, + { 0x000006F4, "RPC_X_NULL_REF_POINTER" }, + { 0x000006F5, "RPC_X_ENUM_VALUE_OUT_OF_RANGE" }, + { 0x000006F6, "RPC_X_BYTE_COUNT_TOO_SMALL" }, + { 0x000006F7, "RPC_X_BAD_STUB_DATA" }, + { 0x000006F8, "ERROR_INVALID_USER_BUFFER" }, + { 0x000006F9, "ERROR_UNRECOGNIZED_MEDIA" }, + { 0x000006FA, "ERROR_NO_TRUST_LSA_SECRET" }, + { 0x000006FB, "ERROR_NO_TRUST_SAM_ACCOUNT" }, + { 0x000006FC, "ERROR_TRUSTED_DOMAIN_FAILURE" }, + { 0x000006FD, "ERROR_TRUSTED_RELATIONSHIP_FAILURE" }, + { 0x000006FE, "ERROR_TRUST_FAILURE" }, + { 0x000006FF, "RPC_S_CALL_IN_PROGRESS" }, + { 0x00000700, "ERROR_NETLOGON_NOT_STARTED" }, + { 0x00000701, "ERROR_ACCOUNT_EXPIRED" }, + { 0x00000702, "ERROR_REDIRECTOR_HAS_OPEN_HANDLES" }, + { 0x00000703, "ERROR_PRINTER_DRIVER_ALREADY_INSTALLED" }, + { 0x00000704, "ERROR_UNKNOWN_PORT" }, + { 0x00000705, "ERROR_UNKNOWN_PRINTER_DRIVER" }, + { 0x00000706, "ERROR_UNKNOWN_PRINTPROCESSOR" }, + { 0x00000707, "ERROR_INVALID_SEPARATOR_FILE" }, + { 0x00000708, "ERROR_INVALID_PRIORITY" }, + { 0x00000709, "ERROR_INVALID_PRINTER_NAME" }, + { 0x0000070A, "ERROR_PRINTER_ALREADY_EXISTS" }, + { 0x0000070B, "ERROR_INVALID_PRINTER_COMMAND" }, + { 0x0000070C, "ERROR_INVALID_DATATYPE" }, + { 0x0000070D, "ERROR_INVALID_ENVIRONMENT" }, + { 0x0000070E, "RPC_S_NO_MORE_BINDINGS" }, + { 0x0000070F, "ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT" }, + { 0x00000710, "ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT" }, + { 0x00000711, "ERROR_NOLOGON_SERVER_TRUST_ACCOUNT" }, + { 0x00000712, "ERROR_DOMAIN_TRUST_INCONSISTENT" }, + { 0x00000713, "ERROR_SERVER_HAS_OPEN_HANDLES" }, + { 0x00000714, "ERROR_RESOURCE_DATA_NOT_FOUND" }, + { 0x00000715, "ERROR_RESOURCE_TYPE_NOT_FOUND" }, + { 0x00000716, "ERROR_RESOURCE_NAME_NOT_FOUND" }, + { 0x00000717, "ERROR_RESOURCE_LANG_NOT_FOUND" }, + { 0x00000718, "ERROR_NOT_ENOUGH_QUOTA" }, + { 0x00000719, "RPC_S_NO_INTERFACES" }, + { 0x0000071A, "RPC_S_CALL_CANCELLED" }, + { 0x0000071B, "RPC_S_BINDING_INCOMPLETE" }, + { 0x0000071C, "RPC_S_COMM_FAILURE" }, + { 0x0000071D, "RPC_S_UNSUPPORTED_AUTHN_LEVEL" }, + { 0x0000071E, "RPC_S_NO_PRINC_NAME" }, + { 0x0000071F, "RPC_S_NOT_RPC_ERROR" }, + { 0x00000720, "RPC_S_UUID_LOCAL_ONLY" }, + { 0x00000721, "RPC_S_SEC_PKG_ERROR" }, + { 0x00000722, "RPC_S_NOT_CANCELLED" }, + { 0x00000723, "RPC_X_INVALID_ES_ACTION" }, + { 0x00000724, "RPC_X_WRONG_ES_VERSION" }, + { 0x00000725, "RPC_X_WRONG_STUB_VERSION" }, + { 0x00000726, "RPC_X_INVALID_PIPE_OBJECT" }, + { 0x00000727, "RPC_X_WRONG_PIPE_ORDER" }, + { 0x00000728, "RPC_X_WRONG_PIPE_VERSION" }, + { 0x0000076A, "RPC_S_GROUP_MEMBER_NOT_FOUND" }, + { 0x0000076B, "EPT_S_CANT_CREATE" }, + { 0x0000076C, "RPC_S_INVALID_OBJECT" }, + { 0x0000076D, "ERROR_INVALID_TIME" }, + { 0x0000076E, "ERROR_INVALID_FORM_NAME" }, + { 0x0000076F, "ERROR_INVALID_FORM_SIZE" }, + { 0x00000770, "ERROR_ALREADY_WAITING" }, + { 0x00000771, "ERROR_PRINTER_DELETED" }, + { 0x00000772, "ERROR_INVALID_PRINTER_STATE" }, + { 0x00000773, "ERROR_PASSWORD_MUST_CHANGE" }, + { 0x00000774, "ERROR_DOMAIN_CONTROLLER_NOT_FOUND" }, + { 0x00000775, "ERROR_ACCOUNT_LOCKED_OUT" }, + { 0x00000776, "OR_INVALID_OXID" }, + { 0x00000777, "OR_INVALID_OID" }, + { 0x00000778, "OR_INVALID_SET" }, + { 0x00000779, "RPC_S_SEND_INCOMPLETE" }, + { 0x0000077A, "RPC_S_INVALID_ASYNC_HANDLE" }, + { 0x0000077B, "RPC_S_INVALID_ASYNC_CALL" }, + { 0x0000077C, "RPC_X_PIPE_CLOSED" }, + { 0x0000077D, "RPC_X_PIPE_DISCIPLINE_ERROR" }, + { 0x0000077E, "RPC_X_PIPE_EMPTY" }, + { 0x0000077F, "ERROR_NO_SITENAME" }, + { 0x00000780, "ERROR_CANT_ACCESS_FILE" }, + { 0x00000781, "ERROR_CANT_RESOLVE_FILENAME" }, + { 0x00000782, "RPC_S_ENTRY_TYPE_MISMATCH" }, + { 0x00000783, "RPC_S_NOT_ALL_OBJS_EXPORTED" }, + { 0x00000784, "RPC_S_INTERFACE_NOT_EXPORTED" }, + { 0x00000785, "RPC_S_PROFILE_NOT_ADDED" }, + { 0x00000786, "RPC_S_PRF_ELT_NOT_ADDED" }, + { 0x00000787, "RPC_S_PRF_ELT_NOT_REMOVED" }, + { 0x00000788, "RPC_S_GRP_ELT_NOT_ADDED" }, + { 0x00000789, "RPC_S_GRP_ELT_NOT_REMOVED" }, + { 0x0000078A, "ERROR_KM_DRIVER_BLOCKED" }, + { 0x0000078B, "ERROR_CONTEXT_EXPIRED" }, + { 0x0000078C, "ERROR_PER_USER_TRUST_QUOTA_EXCEEDED" }, + { 0x0000078D, "ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED" }, + { 0x0000078E, "ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED" }, + { 0x0000078F, "ERROR_AUTHENTICATION_FIREWALL_FAILED" }, + { 0x00000790, "ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED" }, + { 0x000007D0, "ERROR_INVALID_PIXEL_FORMAT" }, + { 0x000007D1, "ERROR_BAD_DRIVER" }, + { 0x000007D2, "ERROR_INVALID_WINDOW_STYLE" }, + { 0x000007D3, "ERROR_METAFILE_NOT_SUPPORTED" }, + { 0x000007D4, "ERROR_TRANSFORM_NOT_SUPPORTED" }, + { 0x000007D5, "ERROR_CLIPPING_NOT_SUPPORTED" }, + { 0x000007DA, "ERROR_INVALID_CMM" }, + { 0x000007DB, "ERROR_INVALID_PROFILE" }, + { 0x000007DC, "ERROR_TAG_NOT_FOUND" }, + { 0x000007DD, "ERROR_TAG_NOT_PRESENT" }, + { 0x000007DE, "ERROR_DUPLICATE_TAG" }, + { 0x000007DF, "ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE" }, + { 0x000007E0, "ERROR_PROFILE_NOT_FOUND" }, + { 0x000007E1, "ERROR_INVALID_COLORSPACE" }, + { 0x000007E2, "ERROR_ICM_NOT_ENABLED" }, + { 0x000007E3, "ERROR_DELETING_ICM_XFORM" }, + { 0x000007E4, "ERROR_INVALID_TRANSFORM" }, + { 0x000007E5, "ERROR_COLORSPACE_MISMATCH" }, + { 0x000007E6, "ERROR_INVALID_COLORINDEX" }, + { 0x000007E7, "ERROR_PROFILE_DOES_NOT_MATCH_DEVICE" }, + { 0x00000836, "NERR_NetNotStarted" }, + { 0x00000837, "NERR_UnknownServer" }, + { 0x00000838, "NERR_ShareMem" }, + { 0x00000839, "NERR_NoNetworkResource" }, + { 0x0000083A, "NERR_RemoteOnly" }, + { 0x0000083B, "NERR_DevNotRedirected" }, + { 0x0000083C, "ERROR_CONNECTED_OTHER_PASSWORD" }, + { 0x0000083D, "ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT" }, + { 0x00000842, "NERR_ServerNotStarted" }, + { 0x00000843, "NERR_ItemNotFound" }, + { 0x00000844, "NERR_UnknownDevDir" }, + { 0x00000845, "NERR_RedirectedPath" }, + { 0x00000846, "NERR_DuplicateShare" }, + { 0x00000847, "NERR_NoRoom" }, + { 0x00000849, "NERR_TooManyItems" }, + { 0x0000084A, "NERR_InvalidMaxUsers" }, + { 0x0000084B, "NERR_BufTooSmall" }, + { 0x0000084F, "NERR_RemoteErr" }, + { 0x00000853, "NERR_LanmanIniError" }, + { 0x00000858, "NERR_NetworkError" }, + { 0x00000859, "NERR_WkstaInconsistentState" }, + { 0x0000085A, "NERR_WkstaNotStarted" }, + { 0x0000085B, "NERR_BrowserNotStarted" }, + { 0x0000085C, "NERR_InternalError" }, + { 0x0000085D, "NERR_BadTransactConfig" }, + { 0x0000085E, "NERR_InvalidAPI" }, + { 0x0000085F, "NERR_BadEventName" }, + { 0x00000860, "NERR_DupNameReboot" }, + { 0x00000862, "NERR_CfgCompNotFound" }, + { 0x00000863, "NERR_CfgParamNotFound" }, + { 0x00000865, "NERR_LineTooLong" }, + { 0x00000866, "NERR_QNotFound" }, + { 0x00000867, "NERR_JobNotFound" }, + { 0x00000868, "NERR_DestNotFound" }, + { 0x00000869, "NERR_DestExists" }, + { 0x0000086A, "NERR_QExists" }, + { 0x0000086B, "NERR_QNoRoom" }, + { 0x0000086C, "NERR_JobNoRoom" }, + { 0x0000086D, "NERR_DestNoRoom" }, + { 0x0000086E, "NERR_DestIdle" }, + { 0x0000086F, "NERR_DestInvalidOp" }, + { 0x00000870, "NERR_ProcNoRespond" }, + { 0x00000871, "NERR_SpoolerNotLoaded" }, + { 0x00000872, "NERR_DestInvalidState" }, + { 0x00000873, "NERR_QinvalidState" }, + { 0x00000874, "NERR_JobInvalidState" }, + { 0x00000875, "NERR_SpoolNoMemory" }, + { 0x00000876, "NERR_DriverNotFound" }, + { 0x00000877, "NERR_DataTypeInvalid" }, + { 0x00000878, "NERR_ProcNotFound" }, + { 0x00000884, "NERR_ServiceTableLocked" }, + { 0x00000885, "NERR_ServiceTableFull" }, + { 0x00000886, "NERR_ServiceInstalled" }, + { 0x00000887, "NERR_ServiceEntryLocked" }, + { 0x00000888, "NERR_ServiceNotInstalled" }, + { 0x00000889, "NERR_BadServiceName" }, + { 0x0000088A, "NERR_ServiceCtlTimeout" }, + { 0x0000088B, "NERR_ServiceCtlBusy" }, + { 0x0000088C, "NERR_BadServiceProgName" }, + { 0x0000088D, "NERR_ServiceNotCtrl" }, + { 0x0000088E, "NERR_ServiceKillProc" }, + { 0x0000088F, "NERR_ServiceCtlNotValid" }, + { 0x00000890, "NERR_NotInDispatchTbl" }, + { 0x00000891, "NERR_BadControlRecv" }, + { 0x00000892, "NERR_ServiceNotStarting" }, + { 0x00000898, "NERR_AlreadyLoggedOn" }, + { 0x00000899, "NERR_NotLoggedOn" }, + { 0x0000089A, "NERR_BadUsername" }, + { 0x0000089B, "NERR_BadPassword" }, + { 0x0000089C, "NERR_UnableToAddName_W" }, + { 0x0000089D, "NERR_UnableToAddName_F" }, + { 0x0000089E, "NERR_UnableToDelName_W" }, + { 0x0000089F, "NERR_UnableToDelName_F" }, + { 0x000008A1, "NERR_LogonsPaused" }, + { 0x000008A2, "NERR_LogonServerConflict" }, + { 0x000008A3, "NERR_LogonNoUserPath" }, + { 0x000008A4, "NERR_LogonScriptError" }, + { 0x000008A6, "NERR_StandaloneLogon" }, + { 0x000008A7, "NERR_LogonServerNotFound" }, + { 0x000008A8, "NERR_LogonDomainExists" }, + { 0x000008A9, "NERR_NonValidatedLogon" }, + { 0x000008AB, "NERR_ACFNotFound" }, + { 0x000008AC, "NERR_GroupNotFound" }, + { 0x000008AD, "NERR_UserNotFound" }, + { 0x000008AE, "NERR_ResourceNotFound" }, + { 0x000008AF, "NERR_GroupExists" }, + { 0x000008B0, "NERR_UserExists" }, + { 0x000008B1, "NERR_ResourceExists" }, + { 0x000008B2, "NERR_NotPrimary" }, + { 0x000008B3, "NERR_ACFNotLoaded" }, + { 0x000008B4, "NERR_ACFNoRoom" }, + { 0x000008B5, "NERR_ACFFileIOFail" }, + { 0x000008B6, "NERR_ACFTooManyLists" }, + { 0x000008B7, "NERR_UserLogon" }, + { 0x000008B8, "NERR_ACFNoParent" }, + { 0x000008B9, "NERR_CanNotGrowSegment" }, + { 0x000008BA, "NERR_SpeGroupOp" }, + { 0x000008BB, "NERR_NotInCache" }, + { 0x000008BC, "NERR_UserInGroup" }, + { 0x000008BD, "NERR_UserNotInGroup" }, + { 0x000008BE, "NERR_AccountUndefined" }, + { 0x000008BF, "NERR_AccountExpired" }, + { 0x000008C0, "NERR_InvalidWorkstation" }, + { 0x000008C1, "NERR_InvalidLogonHours" }, + { 0x000008C2, "NERR_PasswordExpired" }, + { 0x000008C3, "NERR_PasswordCantChange" }, + { 0x000008C4, "NERR_PasswordHistConflict" }, + { 0x000008C5, "NERR_PasswordTooShort" }, + { 0x000008C6, "NERR_PasswordTooRecent" }, + { 0x000008C7, "NERR_InvalidDatabase" }, + { 0x000008C8, "NERR_DatabaseUpToDate" }, + { 0x000008C9, "NERR_SyncRequired" }, + { 0x000008CA, "NERR_UseNotFound" }, + { 0x000008CB, "NERR_BadAsgType_type" }, + { 0x000008CC, "NERR_DeviceIsShared" }, + { 0x000008DE, "NERR_NoComputerName" }, + { 0x000008DF, "NERR_MsgAlreadyStarted" }, + { 0x000008E0, "NERR_MsgInitFailed" }, + { 0x000008E1, "NERR_NameNotFound" }, + { 0x000008E2, "NERR_AlreadyForwarded" }, + { 0x000008E3, "NERR_AddForwarded" }, + { 0x000008E4, "NERR_AlreadyExists" }, + { 0x000008E5, "NERR_TooManyNames" }, + { 0x000008E6, "NERR_DelComputerName" }, + { 0x000008E7, "NERR_LocalForward" }, + { 0x000008E8, "NERR_GrpMsgProcessor" }, + { 0x000008E9, "NERR_PausedRemote" }, + { 0x000008EA, "NERR_BadReceive" }, + { 0x000008EB, "NERR_NameInUse" }, + { 0x000008EC, "NERR_MsgNotStarted" }, + { 0x000008ED, "NERR_NotLocalName" }, + { 0x000008EE, "NERR_NoForwardName" }, + { 0x000008EF, "NERR_RemoteFull" }, + { 0x000008F0, "NERR_NameNotForwarded" }, + { 0x000008F1, "NERR_TruncatedBroadcast" }, + { 0x000008F6, "NERR_InvalidDevice" }, + { 0x000008F7, "NERR_WriteFault" }, + { 0x000008F9, "NERR_DuplicateName" }, + { 0x000008FA, "NERR_DeleteLater" }, + { 0x000008FB, "NERR_IncompleteDel" }, + { 0x000008FC, "NERR_MultipleNets" }, + { 0x00000906, "NERR_NetNameNotFound" }, + { 0x00000907, "NERR_DeviceNotShared" }, + { 0x00000908, "NERR_ClientNameNotFound" }, + { 0x0000090A, "NERR_FileIdNotFound" }, + { 0x0000090B, "NERR_ExecFailure" }, + { 0x0000090C, "NERR_TmpFile" }, + { 0x0000090D, "NERR_TooMuchData" }, + { 0x0000090E, "NERR_DeviceShareConflict" }, + { 0x0000090F, "NERR_BrowserTableIncomplete" }, + { 0x00000910, "NERR_NotLocalDomain" }, + { 0x00000911, "NERR_IsDfsShare" }, + { 0x0000091B, "NERR_DevInvalidOpCode" }, + { 0x0000091C, "NERR_DevNotFound" }, + { 0x0000091D, "NERR_DevNotOpen" }, + { 0x0000091E, "NERR_BadQueueDevString" }, + { 0x0000091F, "NERR_BadQueuePriority" }, + { 0x00000921, "NERR_NoCommDevs" }, + { 0x00000922, "NERR_QueueNotFound" }, + { 0x00000924, "NERR_BadDevString" }, + { 0x00000925, "NERR_BadDev" }, + { 0x00000926, "NERR_InUseBySpooler" }, + { 0x00000927, "NERR_CommDevInUse" }, + { 0x0000092F, "NERR_InvalidComputer" }, + { 0x00000932, "NERR_MaxLenExceeded" }, + { 0x00000934, "NERR_BadComponent" }, + { 0x00000935, "NERR_CantType" }, + { 0x0000093A, "NERR_TooManyEntries" }, + { 0x00000942, "NERR_ProfileFileTooBig" }, + { 0x00000943, "NERR_ProfileOffset" }, + { 0x00000944, "NERR_ProfileCleanup" }, + { 0x00000945, "NERR_ProfileUnknownCmd" }, + { 0x00000946, "NERR_ProfileLoadErr" }, + { 0x00000947, "NERR_ProfileSaveErr" }, + { 0x00000949, "NERR_LogOverflow" }, + { 0x0000094A, "NERR_LogFileChanged" }, + { 0x0000094B, "NERR_LogFileCorrupt" }, + { 0x0000094C, "NERR_SourceIsDir" }, + { 0x0000094D, "NERR_BadSource" }, + { 0x0000094E, "NERR_BadDest" }, + { 0x0000094F, "NERR_DifferentServers" }, + { 0x00000951, "NERR_RunSrvPaused" }, + { 0x00000955, "NERR_ErrCommRunSrv" }, + { 0x00000957, "NERR_ErrorExecingGhost" }, + { 0x00000958, "NERR_ShareNotFound" }, + { 0x00000960, "NERR_InvalidLana" }, + { 0x00000961, "NERR_OpenFiles" }, + { 0x00000962, "NERR_ActiveConns" }, + { 0x00000963, "NERR_BadPasswordCore" }, + { 0x00000964, "NERR_DevInUse" }, + { 0x00000965, "NERR_LocalDrive" }, + { 0x0000097E, "NERR_AlertExists" }, + { 0x0000097F, "NERR_TooManyAlerts" }, + { 0x00000980, "NERR_NoSuchAlert" }, + { 0x00000981, "NERR_BadRecipient" }, + { 0x00000982, "NERR_AcctLimitExceeded" }, + { 0x00000988, "NERR_InvalidLogSeek" }, + { 0x00000992, "NERR_BadUasConfig" }, + { 0x00000993, "NERR_InvalidUASOp" }, + { 0x00000994, "NERR_LastAdmin" }, + { 0x00000995, "NERR_DCNotFound" }, + { 0x00000996, "NERR_LogonTrackingError" }, + { 0x00000997, "NERR_NetlogonNotStarted" }, + { 0x00000998, "NERR_CanNotGrowUASFile" }, + { 0x00000999, "NERR_TimeDiffAtDC" }, + { 0x0000099A, "NERR_PasswordMismatch" }, + { 0x0000099C, "NERR_NoSuchServer" }, + { 0x0000099D, "NERR_NoSuchSession" }, + { 0x0000099E, "NERR_NoSuchConnection" }, + { 0x0000099F, "NERR_TooManyServers" }, + { 0x000009A0, "NERR_TooManySessions" }, + { 0x000009A1, "NERR_TooManyConnections" }, + { 0x000009A2, "NERR_TooManyFiles" }, + { 0x000009A3, "NERR_NoAlternateServers" }, + { 0x000009A6, "NERR_TryDownLevel" }, + { 0x000009B0, "NERR_UPSDriverNotStarted" }, + { 0x000009B1, "NERR_UPSInvalidConfig" }, + { 0x000009B2, "NERR_UPSInvalidCommPort" }, + { 0x000009B3, "NERR_UPSSignalAsserted" }, + { 0x000009B4, "NERR_UPSShutdownFailed" }, + { 0x000009C4, "NERR_BadDosRetCode" }, + { 0x000009C5, "NERR_ProgNeedsExtraMem" }, + { 0x000009C6, "NERR_BadDosFunction" }, + { 0x000009C7, "NERR_RemoteBootFailed" }, + { 0x000009C8, "NERR_BadFileCheckSum" }, + { 0x000009C9, "NERR_NoRplBootSystem" }, + { 0x000009CA, "NERR_RplLoadrNetBiosErr" }, + { 0x000009CB, "NERR_RplLoadrDiskErr" }, + { 0x000009CC, "NERR_ImageParamErr" }, + { 0x000009CD, "NERR_TooManyImageParams" }, + { 0x000009CE, "NERR_NonDosFloppyUsed" }, + { 0x000009CF, "NERR_RplBootRestart" }, + { 0x000009D0, "NERR_RplSrvrCallFailed" }, + { 0x000009D1, "NERR_CantConnectRplSrvr" }, + { 0x000009D2, "NERR_CantOpenImageFile" }, + { 0x000009D3, "NERR_CallingRplSrvr" }, + { 0x000009D4, "NERR_StartingRplBoot" }, + { 0x000009D5, "NERR_RplBootServiceTerm" }, + { 0x000009D6, "NERR_RplBootStartFailed" }, + { 0x000009D7, "NERR_RPL_CONNECTED" }, + { 0x000009F6, "NERR_BrowserConfiguredToNotRun" }, + { 0x00000A32, "NERR_RplNoAdaptersStarted" }, + { 0x00000A33, "NERR_RplBadRegistry" }, + { 0x00000A34, "NERR_RplBadDatabase" }, + { 0x00000A35, "NERR_RplRplfilesShare" }, + { 0x00000A36, "NERR_RplNotRplServer" }, + { 0x00000A37, "NERR_RplCannotEnum" }, + { 0x00000A38, "NERR_RplWkstaInfoCorrupted" }, + { 0x00000A39, "NERR_RplWkstaNotFound" }, + { 0x00000A3A, "NERR_RplWkstaNameUnavailable" }, + { 0x00000A3B, "NERR_RplProfileInfoCorrupted" }, + { 0x00000A3C, "NERR_RplProfileNotFound" }, + { 0x00000A3D, "NERR_RplProfileNameUnavailable" }, + { 0x00000A3E, "NERR_RplProfileNotEmpty" }, + { 0x00000A3F, "NERR_RplConfigInfoCorrupted" }, + { 0x00000A40, "NERR_RplConfigNotFound" }, + { 0x00000A41, "NERR_RplAdapterInfoCorrupted" }, + { 0x00000A42, "NERR_RplInternal" }, + { 0x00000A43, "NERR_RplVendorInfoCorrupted" }, + { 0x00000A44, "NERR_RplBootInfoCorrupted" }, + { 0x00000A45, "NERR_RplWkstaNeedsUserAcct" }, + { 0x00000A46, "NERR_RplNeedsRPLUSERAcct" }, + { 0x00000A47, "NERR_RplBootNotFound" }, + { 0x00000A48, "NERR_RplIncompatibleProfile" }, + { 0x00000A49, "NERR_RplAdapterNameUnavailable" }, + { 0x00000A4A, "NERR_RplConfigNotEmpty" }, + { 0x00000A4B, "NERR_RplBootInUse" }, + { 0x00000A4C, "NERR_RplBackupDatabase" }, + { 0x00000A4D, "NERR_RplAdapterNotFound" }, + { 0x00000A4E, "NERR_RplVendorNotFound" }, + { 0x00000A4F, "NERR_RplVendorNameUnavailable" }, + { 0x00000A50, "NERR_RplBootNameUnavailable" }, + { 0x00000A51, "NERR_RplConfigNameUnavailable" }, + { 0x00000A64, "NERR_DfsInternalCorruption" }, + { 0x00000A65, "NERR_DfsVolumeDataCorrupt" }, + { 0x00000A66, "NERR_DfsNoSuchVolume" }, + { 0x00000A67, "NERR_DfsVolumeAlreadyExists" }, + { 0x00000A68, "NERR_DfsAlreadyShared" }, + { 0x00000A69, "NERR_DfsNoSuchShare" }, + { 0x00000A6A, "NERR_DfsNotALeafVolume" }, + { 0x00000A6B, "NERR_DfsLeafVolume" }, + { 0x00000A6C, "NERR_DfsVolumeHasMultipleServers" }, + { 0x00000A6D, "NERR_DfsCantCreateJunctionPoint" }, + { 0x00000A6E, "NERR_DfsServerNotDfsAware" }, + { 0x00000A6F, "NERR_DfsBadRenamePath" }, + { 0x00000A70, "NERR_DfsVolumeIsOffline" }, + { 0x00000A71, "NERR_DfsNoSuchServer" }, + { 0x00000A72, "NERR_DfsCyclicalName" }, + { 0x00000A73, "NERR_DfsNotSupportedInServerDfs" }, + { 0x00000A74, "NERR_DfsDuplicateService" }, + { 0x00000A75, "NERR_DfsCantRemoveLastServerShare" }, + { 0x00000A76, "NERR_DfsVolumeIsInterDfs" }, + { 0x00000A77, "NERR_DfsInconsistent" }, + { 0x00000A78, "NERR_DfsServerUpgraded" }, + { 0x00000A79, "NERR_DfsDataIsIdentical" }, + { 0x00000A7A, "NERR_DfsCantRemoveDfsRoot" }, + { 0x00000A7B, "NERR_DfsChildOrParentInDfs" }, + { 0x00000A82, "NERR_DfsInternalError" }, + { 0x00000A83, "NERR_SetupAlreadyJoined" }, + { 0x00000A84, "NERR_SetupNotJoined" }, + { 0x00000A85, "NERR_SetupDomainController" }, + { 0x00000A86, "NERR_DefaultJoinRequired" }, + { 0x00000A87, "NERR_InvalidWorkgroupName" }, + { 0x00000A88, "NERR_NameUsesIncompatibleCodePage" }, + { 0x00000A89, "NERR_ComputerAccountNotFound" }, + { 0x00000A8A, "NERR_PersonalSku" }, + { 0x00000A8D, "NERR_PasswordMustChange" }, + { 0x00000A8E, "NERR_AccountLockedOut" }, + { 0x00000A8F, "NERR_PasswordTooLong" }, + { 0x00000A90, "NERR_PasswordNotComplexEnough" }, + { 0x00000A91, "NERR_PasswordFilterError" }, + { 0x00000BB8, "ERROR_UNKNOWN_PRINT_MONITOR" }, + { 0x00000BB9, "ERROR_PRINTER_DRIVER_IN_USE" }, + { 0x00000BBA, "ERROR_SPOOL_FILE_NOT_FOUND" }, + { 0x00000BBB, "ERROR_SPL_NO_STARTDOC" }, + { 0x00000BBC, "ERROR_SPL_NO_ADDJOB" }, + { 0x00000BBD, "ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED" }, + { 0x00000BBE, "ERROR_PRINT_MONITOR_ALREADY_INSTALLED" }, + { 0x00000BBF, "ERROR_INVALID_PRINT_MONITOR" }, + { 0x00000BC0, "ERROR_PRINT_MONITOR_IN_USE" }, + { 0x00000BC1, "ERROR_PRINTER_HAS_JOBS_QUEUED" }, + { 0x00000BC2, "ERROR_SUCCESS_REBOOT_REQUIRED" }, + { 0x00000BC3, "ERROR_SUCCESS_RESTART_REQUIRED" }, + { 0x00000BC4, "ERROR_PRINTER_NOT_FOUND" }, + { 0x00000BC5, "ERROR_PRINTER_DRIVER_WARNED" }, + { 0x00000BC6, "ERROR_PRINTER_DRIVER_BLOCKED" }, + { 0x00000BC7, "ERROR_PRINTER_DRIVER_PACKAGE_IN_USE" }, + { 0x00000BC8, "ERROR_CORE_DRIVER_PACKAGE_NOT_FOUND" }, + { 0x00000BC9, "ERROR_FAIL_REBOOT_REQUIRED" }, + { 0x00000BCA, "ERROR_FAIL_REBOOT_INITIATED" }, + { 0x00000BCB, "ERROR_PRINTER_DRIVER_DOWNLOAD_NEEDED" }, + { 0x00000BCE, "ERROR_PRINTER_NOT_SHAREABLE" }, + { 0x00000F6E, "ERROR_IO_REISSUE_AS_CACHED" }, + { 0x00000FA0, "ERROR_WINS_INTERNAL" }, + { 0x00000FA1, "ERROR_CAN_NOT_DEL_LOCAL_WINS" }, + { 0x00000FA2, "ERROR_STATIC_INIT" }, + { 0x00000FA3, "ERROR_INC_BACKUP" }, + { 0x00000FA4, "ERROR_FULL_BACKUP" }, + { 0x00000FA5, "ERROR_REC_NON_EXISTENT" }, + { 0x00000FA6, "ERROR_RPL_NOT_ALLOWED" }, + { 0x00000FD2, "PEERDIST_ERROR_CONTENTINFO_VERSION_UNSUPPORTED" }, + { 0x00000FD3, "PEERDIST_ERROR_CANNOT_PARSE_CONTENTINFO" }, + { 0x00000FD4, "PEERDIST_ERROR_MISSING_DATA" }, + { 0x00000FD5, "PEERDIST_ERROR_NO_MORE" }, + { 0x00000FD6, "PEERDIST_ERROR_NOT_INITIALIZED" }, + { 0x00000FD7, "PEERDIST_ERROR_ALREADY_INITIALIZED" }, + { 0x00000FD8, "PEERDIST_ERROR_SHUTDOWN_IN_PROGRESS" }, + { 0x00000FD9, "PEERDIST_ERROR_INVALIDATED" }, + { 0x00000FDA, "PEERDIST_ERROR_ALREADY_EXISTS" }, + { 0x00000FDB, "PEERDIST_ERROR_OPERATION_NOTFOUND" }, + { 0x00000FDC, "PEERDIST_ERROR_ALREADY_COMPLETED" }, + { 0x00000FDD, "PEERDIST_ERROR_OUT_OF_BOUNDS" }, + { 0x00000FDE, "PEERDIST_ERROR_VERSION_UNSUPPORTED" }, + { 0x00000FDF, "PEERDIST_ERROR_INVALID_CONFIGURATION" }, + { 0x00000FE0, "PEERDIST_ERROR_NOT_LICENSED" }, + { 0x00000FE1, "PEERDIST_ERROR_SERVICE_UNAVAILABLE" }, + { 0x00001004, "ERROR_DHCP_ADDRESS_CONFLICT" + + }, + { 0x00001068, "ERROR_WMI_GUID_NOT_FOUND" }, + { 0x00001069, "ERROR_WMI_INSTANCE_NOT_FOUND" }, + { 0x0000106A, "ERROR_WMI_ITEMID_NOT_FOUND" }, + { 0x0000106B, "ERROR_WMI_TRY_AGAIN" }, + { 0x0000106C, "ERROR_WMI_DP_NOT_FOUND" }, + { 0x0000106D, "ERROR_WMI_UNRESOLVED_INSTANCE_REF" }, + { 0x0000106E, "ERROR_WMI_ALREADY_ENABLED" }, + { 0x0000106F, "ERROR_WMI_GUID_DISCONNECTED" }, + { 0x00001070, "ERROR_WMI_SERVER_UNAVAILABLE" }, + { 0x00001071, "ERROR_WMI_DP_FAILED" }, + { 0x00001072, "ERROR_WMI_INVALID_MOF" }, + { 0x00001073, "ERROR_WMI_INVALID_REGINFO" }, + { 0x00001074, "ERROR_WMI_ALREADY_DISABLED" }, + { 0x00001075, "ERROR_WMI_READ_ONLY" }, + { 0x00001076, "ERROR_WMI_SET_FAILURE" }, + { 0x000010CC, "ERROR_INVALID_MEDIA" }, + { 0x000010CD, "ERROR_INVALID_LIBRARY" }, + { 0x000010CE, "ERROR_INVALID_MEDIA_POOL" }, + { 0x000010CF, "ERROR_DRIVE_MEDIA_MISMATCH" }, + { 0x000010D0, "ERROR_MEDIA_OFFLINE" }, + { 0x000010D1, "ERROR_LIBRARY_OFFLINE" }, + { 0x000010D2, "ERROR_EMPTY" }, + { 0x000010D3, "ERROR_NOT_EMPTY" }, + { 0x000010D4, "ERROR_MEDIA_UNAVAILABLE" }, + { 0x000010D5, "ERROR_RESOURCE_DISABLED" }, + { 0x000010D6, "ERROR_INVALID_CLEANER" }, + { 0x000010D7, "ERROR_UNABLE_TO_CLEAN" }, + { 0x000010D8, "ERROR_OBJECT_NOT_FOUND" }, + { 0x000010D9, "ERROR_DATABASE_FAILURE" }, + { 0x000010DA, "ERROR_DATABASE_FULL" }, + { 0x000010DB, "ERROR_MEDIA_INCOMPATIBLE" }, + { 0x000010DC, "ERROR_RESOURCE_NOT_PRESENT" }, + { 0x000010DD, "ERROR_INVALID_OPERATION" }, + { 0x000010DE, "ERROR_MEDIA_NOT_AVAILABLE" }, + { 0x000010DF, "ERROR_DEVICE_NOT_AVAILABLE" }, + { 0x000010E0, "ERROR_REQUEST_REFUSED" }, + { 0x000010E1, "ERROR_INVALID_DRIVE_OBJECT" }, + { 0x000010E2, "ERROR_LIBRARY_FULL" }, + { 0x000010E3, "ERROR_MEDIUM_NOT_ACCESSIBLE" }, + { 0x000010E4, "ERROR_UNABLE_TO_LOAD_MEDIUM" }, + { 0x000010E5, "ERROR_UNABLE_TO_INVENTORY_DRIVE" }, + { 0x000010E6, "ERROR_UNABLE_TO_INVENTORY_SLOT" }, + { 0x000010E7, "ERROR_UNABLE_TO_INVENTORY_TRANSPORT" }, + { 0x000010E8, "ERROR_TRANSPORT_FULL" }, + { 0x000010E9, "ERROR_CONTROLLING_IEPORT" }, + { 0x000010EA, "ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA" }, + { 0x000010EB, "ERROR_CLEANER_SLOT_SET" }, + { 0x000010EC, "ERROR_CLEANER_SLOT_NOT_SET" }, + { 0x000010ED, "ERROR_CLEANER_CARTRIDGE_SPENT" }, + { 0x000010EE, "ERROR_UNEXPECTED_OMID" }, + { 0x000010EF, "ERROR_CANT_DELETE_LAST_ITEM" }, + { 0x000010F0, "ERROR_MESSAGE_EXCEEDS_MAX_SIZE" }, + { 0x000010F1, "ERROR_VOLUME_CONTAINS_SYS_FILES" }, + { 0x000010F2, "ERROR_INDIGENOUS_TYPE" }, + { 0x000010F3, "ERROR_NO_SUPPORTING_DRIVES" }, + { 0x000010F4, "ERROR_CLEANER_CARTRIDGE_INSTALLED" }, + { 0x000010F5, "ERROR_IEPORT_FULL" }, + { 0x000010FE, "ERROR_FILE_OFFLINE" }, + { 0x000010FF, "ERROR_REMOTE_STORAGE_NOT_ACTIVE" }, + { 0x00001100, "ERROR_REMOTE_STORAGE_MEDIA_ERROR" }, + { 0x00001126, "ERROR_NOT_A_REPARSE_POINT" }, + { 0x00001127, "ERROR_REPARSE_ATTRIBUTE_CONFLICT" }, + { 0x00001128, "ERROR_INVALID_REPARSE_DATA" }, + { 0x00001129, "ERROR_REPARSE_TAG_INVALID" }, + { 0x0000112A, "ERROR_REPARSE_TAG_MISMATCH" }, + { 0x00001194, "ERROR_VOLUME_NOT_SIS_ENABLED" }, + { 0x00001389, "ERROR_DEPENDENT_RESOURCE_EXISTS" }, + { 0x0000138A, "ERROR_DEPENDENCY_NOT_FOUND" }, + { 0x0000138B, "ERROR_DEPENDENCY_ALREADY_EXISTS" }, + { 0x0000138C, "ERROR_RESOURCE_NOT_ONLINE" }, + { 0x0000138D, "ERROR_HOST_NODE_NOT_AVAILABLE" }, + { 0x0000138E, "ERROR_RESOURCE_NOT_AVAILABLE" }, + { 0x0000138F, "ERROR_RESOURCE_NOT_FOUND" }, + { 0x00001390, "ERROR_SHUTDOWN_CLUSTER" }, + { 0x00001391, "ERROR_CANT_EVICT_ACTIVE_NODE" }, + { 0x00001392, "ERROR_OBJECT_ALREADY_EXISTS" }, + { 0x00001393, "ERROR_OBJECT_IN_LIST" }, + { 0x00001394, "ERROR_GROUP_NOT_AVAILABLE" }, + { 0x00001395, "ERROR_GROUP_NOT_FOUND" }, + { 0x00001396, "ERROR_GROUP_NOT_ONLINE" }, + { 0x00001397, "ERROR_HOST_NODE_NOT_RESOURCE_OWNER" }, + { 0x00001398, "ERROR_HOST_NODE_NOT_GROUP_OWNER" }, + { 0x00001399, "ERROR_RESMON_CREATE_FAILED" }, + { 0x0000139A, "ERROR_RESMON_ONLINE_FAILED" }, + { 0x0000139B, "ERROR_RESOURCE_ONLINE" }, + { 0x0000139C, "ERROR_QUORUM_RESOURCE" }, + { 0x0000139D, "ERROR_NOT_QUORUM_CAPABLE" }, + { 0x0000139E, "ERROR_CLUSTER_SHUTTING_DOWN" }, + { 0x0000139F, "ERROR_INVALID_STATE" }, + { 0x000013A0, "ERROR_RESOURCE_PROPERTIES_STORED" }, + { 0x000013A1, "ERROR_NOT_QUORUM_CLASS" }, + { 0x000013A2, "ERROR_CORE_RESOURCE" }, + { 0x000013A3, "ERROR_QUORUM_RESOURCE_ONLINE_FAILED" }, + { 0x000013A4, "ERROR_QUORUMLOG_OPEN_FAILED" }, + { 0x000013A5, "ERROR_CLUSTERLOG_CORRUPT" }, + { 0x000013A6, "ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE" }, + { 0x000013A7, "ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE" }, + { 0x000013A8, "ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND" }, + { 0x000013A9, "ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE" }, + { 0x000013AA, "ERROR_QUORUM_OWNER_ALIVE" }, + { 0x000013AB, "ERROR_NETWORK_NOT_AVAILABLE" }, + { 0x000013AC, "ERROR_NODE_NOT_AVAILABLE" }, + { 0x000013AD, "ERROR_ALL_NODES_NOT_AVAILABLE" }, + { 0x000013AE, "ERROR_RESOURCE_FAILED" }, + { 0x000013AF, "ERROR_CLUSTER_INVALID_NODE" }, + { 0x000013B0, "ERROR_CLUSTER_NODE_EXISTS" }, + { 0x000013B1, "ERROR_CLUSTER_JOIN_IN_PROGRESS" }, + { 0x000013B2, "ERROR_CLUSTER_NODE_NOT_FOUND" }, + { 0x000013B3, "ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND" }, + { 0x000013B4, "ERROR_CLUSTER_NETWORK_EXISTS" }, + { 0x000013B5, "ERROR_CLUSTER_NETWORK_NOT_FOUND" }, + { 0x000013B6, "ERROR_CLUSTER_NETINTERFACE_EXISTS" }, + { 0x000013B7, "ERROR_CLUSTER_NETINTERFACE_NOT_FOUND" }, + { 0x000013B8, "ERROR_CLUSTER_INVALID_REQUEST" }, + { 0x000013B9, "ERROR_CLUSTER_INVALID_NETWORK_PROVIDER" }, + { 0x000013BA, "ERROR_CLUSTER_NODE_DOWN" }, + { 0x000013BB, "ERROR_CLUSTER_NODE_UNREACHABLE" }, + { 0x000013BC, "ERROR_CLUSTER_NODE_NOT_MEMBER" }, + { 0x000013BD, "ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS" }, + { 0x000013BE, "ERROR_CLUSTER_INVALID_NETWORK" }, + { 0x000013C0, "ERROR_CLUSTER_NODE_UP" }, + { 0x000013C1, "ERROR_CLUSTER_IPADDR_IN_USE" }, + { 0x000013C2, "ERROR_CLUSTER_NODE_NOT_PAUSED" }, + { 0x000013C3, "ERROR_CLUSTER_NO_SECURITY_CONTEXT" }, + { 0x000013C4, "ERROR_CLUSTER_NETWORK_NOT_INTERNAL" }, + { 0x000013C5, "ERROR_CLUSTER_NODE_ALREADY_UP" }, + { 0x000013C6, "ERROR_CLUSTER_NODE_ALREADY_DOWN" }, + { 0x000013C7, "ERROR_CLUSTER_NETWORK_ALREADY_ONLINE" }, + { 0x000013C8, "ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE" }, + { 0x000013C9, "ERROR_CLUSTER_NODE_ALREADY_MEMBER" }, + { 0x000013CA, "ERROR_CLUSTER_LAST_INTERNAL_NETWORK" }, + { 0x000013CB, "ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS" }, + { 0x000013CC, "ERROR_INVALID_OPERATION_ON_QUORUM" }, + { 0x000013CD, "ERROR_DEPENDENCY_NOT_ALLOWED" }, + { 0x000013CE, "ERROR_CLUSTER_NODE_PAUSED" }, + { 0x000013CF, "ERROR_NODE_CANT_HOST_RESOURCE" }, + { 0x000013D0, "ERROR_CLUSTER_NODE_NOT_READY" }, + { 0x000013D1, "ERROR_CLUSTER_NODE_SHUTTING_DOWN" }, + { 0x000013D2, "ERROR_CLUSTER_JOIN_ABORTED" }, + { 0x000013D3, "ERROR_CLUSTER_INCOMPATIBLE_VERSIONS" }, + { 0x000013D4, "ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED" }, + { 0x000013D5, "ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED" }, + { 0x000013D6, "ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND" }, + { 0x000013D7, "ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED" }, + { 0x000013D8, "ERROR_CLUSTER_RESNAME_NOT_FOUND" }, + { 0x000013D9, "ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED" }, + { 0x000013DA, "ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST" }, + { 0x000013DB, "ERROR_CLUSTER_DATABASE_SEQMISMATCH" }, + { 0x000013DC, "ERROR_RESMON_INVALID_STATE" }, + { 0x000013DD, "ERROR_CLUSTER_GUM_NOT_LOCKER" }, + { 0x000013DE, "ERROR_QUORUM_DISK_NOT_FOUND" }, + { 0x000013DF, "ERROR_DATABASE_BACKUP_CORRUPT" }, + { 0x000013E0, "ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT" }, + { 0x000013E1, "ERROR_RESOURCE_PROPERTY_UNCHANGEABLE" }, + { 0x00001702, "ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE" }, + { 0x00001703, "ERROR_CLUSTER_QUORUMLOG_NOT_FOUND" }, + { 0x00001704, "ERROR_CLUSTER_MEMBERSHIP_HALT" }, + { 0x00001705, "ERROR_CLUSTER_INSTANCE_ID_MISMATCH" }, + { 0x00001706, "ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP" }, + { 0x00001707, "ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH" }, + { 0x00001708, "ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP" + + }, + { 0x00001709, "ERROR_CLUSTER_PARAMETER_MISMATCH" }, + { 0x0000170A, "ERROR_NODE_CANNOT_BE_CLUSTERED" }, + { 0x0000170B, "ERROR_CLUSTER_WRONG_OS_VERSION" }, + { 0x0000170C, "ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME" }, + { 0x0000170D, "ERROR_CLUSCFG_ALREADY_COMMITTED" }, + { 0x0000170E, "ERROR_CLUSCFG_ROLLBACK_FAILED" }, + { 0x0000170F, "ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT" }, + { 0x00001710, "ERROR_CLUSTER_OLD_VERSION" }, + { 0x00001711, "ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME" }, + { 0x00001712, "ERROR_CLUSTER_NO_NET_ADAPTERS" }, + { 0x00001713, "ERROR_CLUSTER_POISONED" }, + { 0x00001714, "ERROR_CLUSTER_GROUP_MOVING" }, + { 0x00001715, "ERROR_CLUSTER_RESOURCE_TYPE_BUSY" }, + { 0x00001716, "ERROR_RESOURCE_CALL_TIMED_OUT" }, + { 0x00001717, "ERROR_INVALID_CLUSTER_IPV6_ADDRESS" }, + { 0x00001718, "ERROR_CLUSTER_INTERNAL_INVALID_FUNCTION" }, + { 0x00001719, "ERROR_CLUSTER_PARAMETER_OUT_OF_BOUNDS" }, + { 0x0000171A, "ERROR_CLUSTER_PARTIAL_SEND" }, + { 0x0000171B, "ERROR_CLUSTER_REGISTRY_INVALID_FUNCTION" }, + { 0x0000171C, "ERROR_CLUSTER_INVALID_STRING_TERMINATION" }, + { 0x0000171D, "ERROR_CLUSTER_INVALID_STRING_FORMAT" }, + { 0x0000171E, "ERROR_CLUSTER_DATABASE_TRANSACTION_IN_PROGRESS" }, + { 0x0000171F, "ERROR_CLUSTER_DATABASE_TRANSACTION_NOT_IN_PROGRESS" }, + { 0x00001720, "ERROR_CLUSTER_NULL_DATA" }, + { 0x00001721, "ERROR_CLUSTER_PARTIAL_READ" }, + { 0x00001722, "ERROR_CLUSTER_PARTIAL_WRITE" }, + { 0x00001723, "ERROR_CLUSTER_CANT_DESERIALIZE_DATA" }, + { 0x00001724, "ERROR_DEPENDENT_RESOURCE_PROPERTY_CONFLICT" }, + { 0x00001725, "ERROR_CLUSTER_NO_QUORUM" }, + { 0x00001726, "ERROR_CLUSTER_INVALID_IPV6_NETWORK" }, + { 0x00001727, "ERROR_CLUSTER_INVALID_IPV6_TUNNEL_NETWORK" }, + { 0x00001728, "ERROR_QUORUM_NOT_ALLOWED_IN_THIS_GROUP" }, + { 0x00001770, "ERROR_ENCRYPTION_FAILED" }, + { 0x00001771, "ERROR_DECRYPTION_FAILED" }, + { 0x00001772, "ERROR_FILE_ENCRYPTED" }, + { 0x00001773, "ERROR_NO_RECOVERY_POLICY" }, + { 0x00001774, "ERROR_NO_EFS" }, + { 0x00001775, "ERROR_WRONG_EFS" }, + { 0x00001776, "ERROR_NO_USER_KEYS" }, + { 0x00001777, "ERROR_FILE_NOT_ENCRYPTED" }, + { 0x00001778, "ERROR_NOT_EXPORT_FORMAT" }, + { 0x00001779, "ERROR_FILE_READ_ONLY" }, + { 0x0000177A, "ERROR_DIR_EFS_DISALLOWED" }, + { 0x0000177B, "ERROR_EFS_SERVER_NOT_TRUSTED" }, + { 0x0000177C, "ERROR_BAD_RECOVERY_POLICY" }, + { 0x0000177D, "ERROR_EFS_ALG_BLOB_TOO_BIG" }, + { 0x0000177E, "ERROR_VOLUME_NOT_SUPPORT_EFS" }, + { 0x0000177F, "ERROR_EFS_DISABLED" }, + { 0x00001780, "ERROR_EFS_VERSION_NOT_SUPPORT" }, + { 0x00001781, "ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE" }, + { 0x00001782, "ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER" }, + { 0x00001783, "ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE" }, + { 0x00001784, "ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE" }, + { 0x00001785, "ERROR_CS_ENCRYPTION_FILE_NOT_CSE" }, + { 0x000017E6, "ERROR_NO_BROWSER_SERVERS_FOUND" }, + { 0x00001838, "SCHED_E_SERVICE_NOT_LOCALSYSTEM" }, + { 0x000019C8, "ERROR_LOG_SECTOR_INVALID" }, + { 0x000019C9, "ERROR_LOG_SECTOR_PARITY_INVALID" }, + { 0x000019CA, "ERROR_LOG_SECTOR_REMAPPED" }, + { 0x000019CB, "ERROR_LOG_BLOCK_INCOMPLETE" }, + { 0x000019CC, "ERROR_LOG_INVALID_RANGE" }, + { 0x000019CD, "ERROR_LOG_BLOCKS_EXHAUSTED" }, + { 0x000019CE, "ERROR_LOG_READ_CONTEXT_INVALID" }, + { 0x000019CF, "ERROR_LOG_RESTART_INVALID" }, + { 0x000019D0, "ERROR_LOG_BLOCK_VERSION" }, + { 0x000019D1, "ERROR_LOG_BLOCK_INVALID" }, + { 0x000019D2, "ERROR_LOG_READ_MODE_INVALID" }, + { 0x000019D3, "ERROR_LOG_NO_RESTART" }, + { 0x000019D4, "ERROR_LOG_METADATA_CORRUPT" }, + { 0x000019D5, "ERROR_LOG_METADATA_INVALID" }, + { 0x000019D6, "ERROR_LOG_METADATA_INCONSISTENT" }, + { 0x000019D7, "ERROR_LOG_RESERVATION_INVALID" }, + { 0x000019D8, "ERROR_LOG_CANT_DELETE" }, + { 0x000019D9, "ERROR_LOG_CONTAINER_LIMIT_EXCEEDED" }, + { 0x000019DA, "ERROR_LOG_START_OF_LOG" }, + { 0x000019DB, "ERROR_LOG_POLICY_ALREADY_INSTALLED" }, + { 0x000019DC, "ERROR_LOG_POLICY_NOT_INSTALLED" }, + { 0x000019DD, "ERROR_LOG_POLICY_INVALID" }, + { 0x000019DE, "ERROR_LOG_POLICY_CONFLICT" }, + { 0x000019DF, "ERROR_LOG_PINNED_ARCHIVE_TAIL" }, + { 0x000019E0, "ERROR_LOG_RECORD_NONEXISTENT" }, + { 0x000019E1, "ERROR_LOG_RECORDS_RESERVED_INVALID" }, + { 0x000019E2, "ERROR_LOG_SPACE_RESERVED_INVALID" }, + { 0x000019E3, "ERROR_LOG_TAIL_INVALID" }, + { 0x000019E4, "ERROR_LOG_FULL" }, + { 0x000019E5, "ERROR_COULD_NOT_RESIZE_LOG" }, + { 0x000019E6, "ERROR_LOG_MULTIPLEXED" }, + { 0x000019E7, "ERROR_LOG_DEDICATED" }, + { 0x000019E8, "ERROR_LOG_ARCHIVE_NOT_IN_PROGRESS" }, + { 0x000019E9, "ERROR_LOG_ARCHIVE_IN_PROGRESS" }, + { 0x000019EA, "ERROR_LOG_EPHEMERAL" }, + { 0x000019EB, "ERROR_LOG_NOT_ENOUGH_CONTAINERS" }, + { 0x000019EC, "ERROR_LOG_CLIENT_ALREADY_REGISTERED" }, + { 0x000019ED, "ERROR_LOG_CLIENT_NOT_REGISTERED" }, + { 0x000019EE, "ERROR_LOG_FULL_HANDLER_IN_PROGRESS" }, + { 0x000019EF, "ERROR_LOG_CONTAINER_READ_FAILED" }, + { 0x000019F0, "ERROR_LOG_CONTAINER_WRITE_FAILED" }, + { 0x000019F1, "ERROR_LOG_CONTAINER_OPEN_FAILED" }, + { 0x000019F2, "ERROR_LOG_CONTAINER_STATE_INVALID" }, + { 0x000019F3, "ERROR_LOG_STATE_INVALID" }, + { 0x000019F4, "ERROR_LOG_PINNED" }, + { 0x000019F5, "ERROR_LOG_METADATA_FLUSH_FAILED" }, + { 0x000019F6, "ERROR_LOG_INCONSISTENT_SECURITY" }, + { 0x000019F7, "ERROR_LOG_APPENDED_FLUSH_FAILED" }, + { 0x000019F8, "ERROR_LOG_PINNED_RESERVATION" }, + { 0x00001A2C, "ERROR_INVALID_TRANSACTION" }, + { 0x00001A2D, "ERROR_TRANSACTION_NOT_ACTIVE" }, + { 0x00001A2E, "ERROR_TRANSACTION_REQUEST_NOT_VALID" }, + { 0x00001A2F, "ERROR_TRANSACTION_NOT_REQUESTED" }, + { 0x00001A30, "ERROR_TRANSACTION_ALREADY_ABORTED" }, + { 0x00001A31, "ERROR_TRANSACTION_ALREADY_COMMITTED" }, + { 0x00001A32, "ERROR_TM_INITIALIZATION_FAILED" }, + { 0x00001A33, "ERROR_RESOURCEMANAGER_READ_ONLY" }, + { 0x00001A34, "ERROR_TRANSACTION_NOT_JOINED" }, + { 0x00001A35, "ERROR_TRANSACTION_SUPERIOR_EXISTS" }, + { 0x00001A36, "ERROR_CRM_PROTOCOL_ALREADY_EXISTS" }, + { 0x00001A37, "ERROR_TRANSACTION_PROPAGATION_FAILED" }, + { 0x00001A38, "ERROR_CRM_PROTOCOL_NOT_FOUND" }, + { 0x00001A39, "ERROR_TRANSACTION_INVALID_MARSHALL_BUFFER" }, + { 0x00001A3A, "ERROR_CURRENT_TRANSACTION_NOT_VALID" }, + { 0x00001A3B, "ERROR_TRANSACTION_NOT_FOUND" }, + { 0x00001A3C, "ERROR_RESOURCEMANAGER_NOT_FOUND" }, + { 0x00001A3D, "ERROR_ENLISTMENT_NOT_FOUND" }, + { 0x00001A3E, "ERROR_TRANSACTIONMANAGER_NOT_FOUND" }, + { 0x00001A3F, "ERROR_TRANSACTIONMANAGER_NOT_ONLINE" }, + { 0x00001A40, "ERROR_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION" }, + { 0x00001A90, "ERROR_TRANSACTIONAL_CONFLICT" }, + { 0x00001A91, "ERROR_RM_NOT_ACTIVE" }, + { 0x00001A92, "ERROR_RM_METADATA_CORRUPT" }, + { 0x00001A93, "ERROR_DIRECTORY_NOT_RM" }, + { 0x00001A95, "ERROR_TRANSACTIONS_UNSUPPORTED_REMOTE" }, + { 0x00001A96, "ERROR_LOG_RESIZE_INVALID_SIZE" }, + { 0x00001A97, "ERROR_OBJECT_NO_LONGER_EXISTS" }, + { 0x00001A98, "ERROR_STREAM_MINIVERSION_NOT_FOUND" }, + { 0x00001A99, "ERROR_STREAM_MINIVERSION_NOT_VALID" }, + { 0x00001A9A, "ERROR_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION" }, + { 0x00001A9B, "ERROR_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT" }, + { 0x00001A9C, "ERROR_CANT_CREATE_MORE_STREAM_MINIVERSIONS" }, + { 0x00001A9E, "ERROR_REMOTE_FILE_VERSION_MISMATCH" }, + { 0x00001A9F, "ERROR_HANDLE_NO_LONGER_VALID" }, + { 0x00001AA0, "ERROR_NO_TXF_METADATA" }, + { 0x00001AA1, "ERROR_LOG_CORRUPTION_DETECTED" }, + { 0x00001AA2, "ERROR_CANT_RECOVER_WITH_HANDLE_OPEN" }, + { 0x00001AA3, "ERROR_RM_DISCONNECTED" }, + { 0x00001AA4, "ERROR_ENLISTMENT_NOT_SUPERIOR" }, + { 0x00001AA5, "ERROR_RECOVERY_NOT_NEEDED" }, + { 0x00001AA6, "ERROR_RM_ALREADY_STARTED" }, + { 0x00001AA7, "ERROR_FILE_IDENTITY_NOT_PERSISTENT" }, + { 0x00001AA8, "ERROR_CANT_BREAK_TRANSACTIONAL_DEPENDENCY" }, + { 0x00001AA9, "ERROR_CANT_CROSS_RM_BOUNDARY" }, + { 0x00001AAA, "ERROR_TXF_DIR_NOT_EMPTY" }, + { 0x00001AAB, "ERROR_INDOUBT_TRANSACTIONS_EXIST" }, + { 0x00001AAC, "ERROR_TM_VOLATILE" }, + { 0x00001AAD, "ERROR_ROLLBACK_TIMER_EXPIRED" }, + { 0x00001AAE, "ERROR_TXF_ATTRIBUTE_CORRUPT" }, + { 0x00001AAF, "ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION" }, + { 0x00001AB0, "ERROR_TRANSACTIONAL_OPEN_NOT_ALLOWED" }, + { 0x00001AB1, "ERROR_LOG_GROWTH_FAILED" }, + { 0x00001AB2, "ERROR_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE" }, + { 0x00001AB3, "ERROR_TXF_METADATA_ALREADY_PRESENT" }, + { 0x00001AB4, "ERROR_TRANSACTION_SCOPE_CALLBACKS_NOT_SET" }, + { 0x00001AB5, "ERROR_TRANSACTION_REQUIRED_PROMOTION" }, + { 0x00001AB6, "ERROR_CANNOT_EXECUTE_FILE_IN_TRANSACTION" }, + { 0x00001AB7, "ERROR_TRANSACTIONS_NOT_FROZEN" }, + { 0x00001AB8, "ERROR_TRANSACTION_FREEZE_IN_PROGRESS" }, + { 0x00001AB9, "ERROR_NOT_SNAPSHOT_VOLUME" }, + { 0x00001ABA, "ERROR_NO_SAVEPOINT_WITH_OPEN_FILES" }, + { 0x00001ABB, "ERROR_DATA_LOST_REPAIR" }, + { 0x00001ABC, "ERROR_SPARSE_NOT_ALLOWED_IN_TRANSACTION" }, + { 0x00001ABD, "ERROR_TM_IDENTITY_MISMATCH" }, + { 0x00001ABE, "ERROR_FLOATED_SECTION" }, + { 0x00001ABF, "ERROR_CANNOT_ACCEPT_TRANSACTED_WORK" }, + { 0x00001AC0, "ERROR_CANNOT_ABORT_TRANSACTIONS" }, + { 0x00001B59, "ERROR_CTX_WINSTATION_NAME_INVALID" }, + { 0x00001B5A, "ERROR_CTX_INVALID_PD" }, + { 0x00001B5B, "ERROR_CTX_PD_NOT_FOUND" }, + { 0x00001B5C, "ERROR_CTX_WD_NOT_FOUND" }, + { 0x00001B5D, "ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY" }, + { 0x00001B5E, "ERROR_CTX_SERVICE_NAME_COLLISION" }, + { 0x00001B5F, "ERROR_CTX_CLOSE_PENDING" }, + { 0x00001B60, "ERROR_CTX_NO_OUTBUF" }, + { 0x00001B61, "ERROR_CTX_MODEM_INF_NOT_FOUND" }, + { 0x00001B62, "ERROR_CTX_INVALID_MODEMNAME" }, + { 0x00001B63, "ERROR_CTX_MODEM_RESPONSE_ERROR" }, + { 0x00001B64, "ERROR_CTX_MODEM_RESPONSE_TIMEOUT" }, + { 0x00001B65, "ERROR_CTX_MODEM_RESPONSE_NO_CARRIER" }, + { 0x00001B66, "ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE" }, + { 0x00001B67, "ERROR_CTX_MODEM_RESPONSE_BUSY" }, + { 0x00001B68, "ERROR_CTX_MODEM_RESPONSE_VOICE" }, + { 0x00001B69, "ERROR_CTX_TD_ERROR" }, + { 0x00001B6E, "ERROR_CTX_WINSTATION_NOT_FOUND" }, + { 0x00001B6F, "ERROR_CTX_WINSTATION_ALREADY_EXISTS" }, + { 0x00001B70, "ERROR_CTX_WINSTATION_BUSY" }, + { 0x00001B71, "ERROR_CTX_BAD_VIDEO_MODE" }, + { 0x00001B7B, "ERROR_CTX_GRAPHICS_INVALID" }, + { 0x00001B7D, "ERROR_CTX_LOGON_DISABLED" }, + { 0x00001B7E, "ERROR_CTX_NOT_CONSOLE" }, + { 0x00001B80, "ERROR_CTX_CLIENT_QUERY_TIMEOUT" }, + { 0x00001B81, "ERROR_CTX_CONSOLE_DISCONNECT" }, + { 0x00001B82, "ERROR_CTX_CONSOLE_CONNECT" }, + { 0x00001B84, "ERROR_CTX_SHADOW_DENIED" }, + { 0x00001B85, "ERROR_CTX_WINSTATION_ACCESS_DENIED" }, + { 0x00001B89, "ERROR_CTX_INVALID_WD" }, + { 0x00001B8A, "ERROR_CTX_SHADOW_INVALID" }, + { 0x00001B8B, "ERROR_CTX_SHADOW_DISABLED" }, + { 0x00001B8C, "ERROR_CTX_CLIENT_LICENSE_IN_USE" }, + { 0x00001B8D, "ERROR_CTX_CLIENT_LICENSE_NOT_SET" }, + { 0x00001B8E, "ERROR_CTX_LICENSE_NOT_AVAILABLE" }, + { 0x00001B8F, "ERROR_CTX_LICENSE_CLIENT_INVALID" }, + { 0x00001B90, "ERROR_CTX_LICENSE_EXPIRED" }, + { 0x00001B91, "ERROR_CTX_SHADOW_NOT_RUNNING" }, + { 0x00001B92, "ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE" }, + { 0x00001B93, "ERROR_ACTIVATION_COUNT_EXCEEDED" }, + { 0x00001B94, "ERROR_CTX_WINSTATIONS_DISABLED" }, + { 0x00001B95, "ERROR_CTX_ENCRYPTION_LEVEL_REQUIRED" }, + { 0x00001B96, "ERROR_CTX_SESSION_IN_USE" }, + { 0x00001B97, "ERROR_CTX_NO_FORCE_LOGOFF" }, + { 0x00001B98, "ERROR_CTX_ACCOUNT_RESTRICTION" }, + { 0x00001B99, "ERROR_RDP_PROTOCOL_ERROR" }, + { 0x00001B9A, "ERROR_CTX_CDM_CONNECT" }, + { 0x00001B9B, "ERROR_CTX_CDM_DISCONNECT" }, + { 0x00001B9C, "ERROR_CTX_SECURITY_LAYER_ERROR" }, + { 0x00001B9D, "ERROR_TS_INCOMPATIBLE_SESSIONS" }, + { 0x00001F41, "FRS_ERR_INVALID_API_SEQUENCE" }, + { 0x00001F42, "FRS_ERR_STARTING_SERVICE" }, + { 0x00001F43, "FRS_ERR_STOPPING_SERVICE" }, + { 0x00001F44, "FRS_ERR_INTERNAL_API" }, + { 0x00001F45, "FRS_ERR_INTERNAL" }, + { 0x00001F46, "FRS_ERR_SERVICE_COMM" }, + { 0x00001F47, "FRS_ERR_INSUFFICIENT_PRIV" }, + { 0x00001F48, "FRS_ERR_AUTHENTICATION" }, + { 0x00001F49, "FRS_ERR_PARENT_INSUFFICIENT_PRIV" }, + { 0x00001F4A, "FRS_ERR_PARENT_AUTHENTICATION" }, + { 0x00001F4B, "FRS_ERR_CHILD_TO_PARENT_COMM" }, + { 0x00001F4C, "FRS_ERR_PARENT_TO_CHILD_COMM" }, + { 0x00001F4D, "FRS_ERR_SYSVOL_POPULATE" }, + { 0x00001F4E, "FRS_ERR_SYSVOL_POPULATE_TIMEOUT" }, + { 0x00001F4F, "FRS_ERR_SYSVOL_IS_BUSY" }, + { 0x00001F50, "FRS_ERR_SYSVOL_DEMOTE" }, + { 0x00001F51, "FRS_ERR_INVALID_SERVICE_PARAMETER" }, + { 0x00002008, "ERROR_DS_NOT_INSTALLED" }, + { 0x00002009, "ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY" }, + { 0x0000200A, "ERROR_DS_NO_ATTRIBUTE_OR_VALUE" }, + { 0x0000200B, "ERROR_DS_INVALID_ATTRIBUTE_SYNTAX" }, + { 0x0000200C, "ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED" }, + { 0x0000200D, "ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS" }, + { 0x0000200E, "ERROR_DS_BUSY" }, + { 0x0000200F, "ERROR_DS_UNAVAILABLE" }, + { 0x00002010, "ERROR_DS_NO_RIDS_ALLOCATED" }, + { 0x00002011, "ERROR_DS_NO_MORE_RIDS" }, + { 0x00002012, "ERROR_DS_INCORRECT_ROLE_OWNER" }, + { 0x00002013, "ERROR_DS_RIDMGR_INIT_ERROR" }, + { 0x00002014, "ERROR_DS_OBJ_CLASS_VIOLATION" }, + { 0x00002015, "ERROR_DS_CANT_ON_NON_LEAF" }, + { 0x00002016, "ERROR_DS_CANT_ON_RDN" }, + { 0x00002017, "ERROR_DS_CANT_MOD_OBJ_CLASS" }, + { 0x00002018, "ERROR_DS_CROSS_DOM_MOVE_ERROR" }, + { 0x00002019, "ERROR_DS_GC_NOT_AVAILABLE" }, + { 0x0000201A, "ERROR_SHARED_POLICY" }, + { 0x0000201B, "ERROR_POLICY_OBJECT_NOT_FOUND" }, + { 0x0000201C, "ERROR_POLICY_ONLY_IN_DS" }, + { 0x0000201D, "ERROR_PROMOTION_ACTIVE" }, + { 0x0000201E, "ERROR_NO_PROMOTION_ACTIVE" }, + { 0x00002020, "ERROR_DS_OPERATIONS_ERROR" }, + { 0x00002021, "ERROR_DS_PROTOCOL_ERROR" }, + { 0x00002022, "ERROR_DS_TIMELIMIT_EXCEEDED" }, + { 0x00002023, "ERROR_DS_SIZELIMIT_EXCEEDED" }, + { 0x00002024, "ERROR_DS_ADMIN_LIMIT_EXCEEDED" }, + { 0x00002025, "ERROR_DS_COMPARE_FALSE" }, + { 0x00002026, "ERROR_DS_COMPARE_TRUE" }, + { 0x00002027, "ERROR_DS_AUTH_METHOD_NOT_SUPPORTED" }, + { 0x00002028, "ERROR_DS_STRONG_AUTH_REQUIRED" }, + { 0x00002029, "ERROR_DS_INAPPROPRIATE_AUTH" }, + { 0x0000202A, "ERROR_DS_AUTH_UNKNOWN" }, + { 0x0000202B, "ERROR_DS_REFERRAL" }, + { 0x0000202C, "ERROR_DS_UNAVAILABLE_CRIT_EXTENSION" }, + { 0x0000202D, "ERROR_DS_CONFIDENTIALITY_REQUIRED" }, + { 0x0000202E, "ERROR_DS_INAPPROPRIATE_MATCHING" }, + { 0x0000202F, "ERROR_DS_CONSTRAINT_VIOLATION" }, + { 0x00002030, "ERROR_DS_NO_SUCH_OBJECT" }, + { 0x00002031, "ERROR_DS_ALIAS_PROBLEM" }, + { 0x00002032, "ERROR_DS_INVALID_DN_SYNTAX" }, + { 0x00002033, "ERROR_DS_IS_LEAF" }, + { 0x00002034, "ERROR_DS_ALIAS_DEREF_PROBLEM" }, + { 0x00002035, "ERROR_DS_UNWILLING_TO_PERFORM" }, + { 0x00002036, "ERROR_DS_LOOP_DETECT" }, + { 0x00002037, "ERROR_DS_NAMING_VIOLATION" }, + { 0x00002038, "ERROR_DS_OBJECT_RESULTS_TOO_LARGE" }, + { 0x00002039, "ERROR_DS_AFFECTS_MULTIPLE_DSAS" }, + { 0x0000203A, "ERROR_DS_SERVER_DOWN" }, + { 0x0000203B, "ERROR_DS_LOCAL_ERROR" }, + { 0x0000203C, "ERROR_DS_ENCODING_ERROR" }, + { 0x0000203D, "ERROR_DS_DECODING_ERROR" }, + { 0x0000203E, "ERROR_DS_FILTER_UNKNOWN" }, + { 0x0000203F, "ERROR_DS_PARAM_ERROR" }, + { 0x00002040, "ERROR_DS_NOT_SUPPORTED" }, + { 0x00002041, "ERROR_DS_NO_RESULTS_RETURNED" }, + { 0x00002042, "ERROR_DS_CONTROL_NOT_FOUND" }, + { 0x00002043, "ERROR_DS_CLIENT_LOOP" }, + { 0x00002044, "ERROR_DS_REFERRAL_LIMIT_EXCEEDED" }, + { 0x00002045, "ERROR_DS_SORT_CONTROL_MISSING" }, + { 0x00002046, "ERROR_DS_OFFSET_RANGE_ERROR" }, + { 0x0000206D, "ERROR_DS_ROOT_MUST_BE_NC" }, + { 0x0000206E, "ERROR_DS_ADD_REPLICA_INHIBITED" }, + { 0x0000206F, "ERROR_DS_ATT_NOT_DEF_IN_SCHEMA" }, + { 0x00002070, "ERROR_DS_MAX_OBJ_SIZE_EXCEEDED" }, + { 0x00002071, "ERROR_DS_OBJ_STRING_NAME_EXISTS" }, + { 0x00002072, "ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA" }, + { 0x00002073, "ERROR_DS_RDN_DOESNT_MATCH_SCHEMA" }, + { 0x00002074, "ERROR_DS_NO_REQUESTED_ATTS_FOUND" }, + { 0x00002075, "ERROR_DS_USER_BUFFER_TO_SMALL" }, + { 0x00002076, "ERROR_DS_ATT_IS_NOT_ON_OBJ" }, + { 0x00002077, "ERROR_DS_ILLEGAL_MOD_OPERATION" }, + { 0x00002078, "ERROR_DS_OBJ_TOO_LARGE" }, + { 0x00002079, "ERROR_DS_BAD_INSTANCE_TYPE" }, + { 0x0000207A, "ERROR_DS_MASTERDSA_REQUIRED" }, + { 0x0000207B, "ERROR_DS_OBJECT_CLASS_REQUIRED" }, + { 0x0000207C, "ERROR_DS_MISSING_REQUIRED_ATT" }, + { 0x0000207D, "ERROR_DS_ATT_NOT_DEF_FOR_CLASS" }, + { 0x0000207E, "ERROR_DS_ATT_ALREADY_EXISTS" }, + { 0x00002080, "ERROR_DS_CANT_ADD_ATT_VALUES" }, + { 0x00002081, "ERROR_DS_SINGLE_VALUE_CONSTRAINT" }, + { 0x00002082, "ERROR_DS_RANGE_CONSTRAINT" }, + { 0x00002083, "ERROR_DS_ATT_VAL_ALREADY_EXISTS" }, + { 0x00002084, "ERROR_DS_CANT_REM_MISSING_ATT" }, + { 0x00002085, "ERROR_DS_CANT_REM_MISSING_ATT_VAL" }, + { 0x00002086, "ERROR_DS_ROOT_CANT_BE_SUBREF" }, + { 0x00002087, "ERROR_DS_NO_CHAINING" }, + { 0x00002088, "ERROR_DS_NO_CHAINED_EVAL" }, + { 0x00002089, "ERROR_DS_NO_PARENT_OBJECT" }, + { 0x0000208A, "ERROR_DS_PARENT_IS_AN_ALIAS" }, + { 0x0000208B, "ERROR_DS_CANT_MIX_MASTER_AND_REPS" }, + { 0x0000208C, "ERROR_DS_CHILDREN_EXIST" }, + { 0x0000208D, "ERROR_DS_OBJ_NOT_FOUND" }, + { 0x0000208E, "ERROR_DS_ALIASED_OBJ_MISSING" }, + { 0x0000208F, "ERROR_DS_BAD_NAME_SYNTAX" }, + { 0x00002090, "ERROR_DS_ALIAS_POINTS_TO_ALIAS" }, + { 0x00002091, "ERROR_DS_CANT_DEREF_ALIAS" }, + { 0x00002092, "ERROR_DS_OUT_OF_SCOPE" }, + { 0x00002093, "ERROR_DS_OBJECT_BEING_REMOVED" }, + { 0x00002094, "ERROR_DS_CANT_DELETE_DSA_OBJ" }, + { 0x00002095, "ERROR_DS_GENERIC_ERROR" }, + { 0x00002096, "ERROR_DS_DSA_MUST_BE_INT_MASTER" }, + { 0x00002097, "ERROR_DS_CLASS_NOT_DSA" }, + { 0x00002098, "ERROR_DS_INSUFF_ACCESS_RIGHTS" }, + { 0x00002099, "ERROR_DS_ILLEGAL_SUPERIOR" }, + { 0x0000209A, "ERROR_DS_ATTRIBUTE_OWNED_BY_SAM" }, + { 0x0000209B, "ERROR_DS_NAME_TOO_MANY_PARTS" }, + { 0x0000209C, "ERROR_DS_NAME_TOO_LONG" }, + { 0x0000209D, "ERROR_DS_NAME_VALUE_TOO_LONG" }, + { 0x0000209E, "ERROR_DS_NAME_UNPARSEABLE" }, + { 0x0000209F, "ERROR_DS_NAME_TYPE_UNKNOWN" }, + { 0x000020A0, "ERROR_DS_NOT_AN_OBJECT" }, + { 0x000020A1, "ERROR_DS_SEC_DESC_TOO_SHORT" }, + { 0x000020A2, "ERROR_DS_SEC_DESC_INVALID" }, + { 0x000020A3, "ERROR_DS_NO_DELETED_NAME" }, + { 0x000020A4, "ERROR_DS_SUBREF_MUST_HAVE_PARENT" }, + { 0x000020A5, "ERROR_DS_NCNAME_MUST_BE_NC" }, + { 0x000020A6, "ERROR_DS_CANT_ADD_SYSTEM_ONLY" }, + { 0x000020A7, "ERROR_DS_CLASS_MUST_BE_CONCRETE" }, + { 0x000020A8, "ERROR_DS_INVALID_DMD" }, + { 0x000020A9, "ERROR_DS_OBJ_GUID_EXISTS" }, + { 0x000020AA, "ERROR_DS_NOT_ON_BACKLINK" }, + { 0x000020AB, "ERROR_DS_NO_CROSSREF_FOR_NC" }, + { 0x000020AC, "ERROR_DS_SHUTTING_DOWN" }, + { 0x000020AD, "ERROR_DS_UNKNOWN_OPERATION" }, + { 0x000020AE, "ERROR_DS_INVALID_ROLE_OWNER" }, + { 0x000020AF, "ERROR_DS_COULDNT_CONTACT_FSMO" }, + { 0x000020B0, "ERROR_DS_CROSS_NC_DN_RENAME" }, + { 0x000020B1, "ERROR_DS_CANT_MOD_SYSTEM_ONLY" }, + { 0x000020B2, "ERROR_DS_REPLICATOR_ONLY" }, + { 0x000020B3, "ERROR_DS_OBJ_CLASS_NOT_DEFINED" }, + { 0x000020B4, "ERROR_DS_OBJ_CLASS_NOT_SUBCLASS" }, + { 0x000020B5, "ERROR_DS_NAME_REFERENCE_INVALID" }, + { 0x000020B6, "ERROR_DS_CROSS_REF_EXISTS" }, + { 0x000020B7, "ERROR_DS_CANT_DEL_MASTER_CROSSREF" }, + { 0x000020B8, "ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD" }, + { 0x000020B9, "ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX" }, + { 0x000020BA, "ERROR_DS_DUP_RDN" }, + { 0x000020BB, "ERROR_DS_DUP_OID" }, + { 0x000020BC, "ERROR_DS_DUP_MAPI_ID" }, + { 0x000020BD, "ERROR_DS_DUP_SCHEMA_ID_GUID" }, + { 0x000020BE, "ERROR_DS_DUP_LDAP_DISPLAY_NAME" }, + { 0x000020BF, "ERROR_DS_SEMANTIC_ATT_TEST" }, + { 0x000020C0, "ERROR_DS_SYNTAX_MISMATCH" }, + { 0x000020C1, "ERROR_DS_EXISTS_IN_MUST_HAVE" }, + { 0x000020C2, "ERROR_DS_EXISTS_IN_MAY_HAVE" }, + { 0x000020C3, "ERROR_DS_NONEXISTENT_MAY_HAVE" }, + { 0x000020C4, "ERROR_DS_NONEXISTENT_MUST_HAVE" }, + { 0x000020C5, "ERROR_DS_AUX_CLS_TEST_FAIL" }, + { 0x000020C6, "ERROR_DS_NONEXISTENT_POSS_SUP" }, + { 0x000020C7, "ERROR_DS_SUB_CLS_TEST_FAIL" }, + { 0x000020C8, "ERROR_DS_BAD_RDN_ATT_ID_SYNTAX" }, + { 0x000020C9, "ERROR_DS_EXISTS_IN_AUX_CLS" }, + { 0x000020CA, "ERROR_DS_EXISTS_IN_SUB_CLS" }, + { 0x000020CB, "ERROR_DS_EXISTS_IN_POSS_SUP" }, + { 0x000020CC, "ERROR_DS_RECALCSCHEMA_FAILED" }, + { 0x000020CD, "ERROR_DS_TREE_DELETE_NOT_FINISHED" }, + { 0x000020CE, "ERROR_DS_CANT_DELETE" }, + { 0x000020CF, "ERROR_DS_ATT_SCHEMA_REQ_ID" }, + { 0x000020D0, "ERROR_DS_BAD_ATT_SCHEMA_SYNTAX" }, + { 0x000020D1, "ERROR_DS_CANT_CACHE_ATT" }, + { 0x000020D2, "ERROR_DS_CANT_CACHE_CLASS" }, + { 0x000020D3, "ERROR_DS_CANT_REMOVE_ATT_CACHE" }, + { 0x000020D4, "ERROR_DS_CANT_REMOVE_CLASS_CACHE" }, + { 0x000020D5, "ERROR_DS_CANT_RETRIEVE_DN" }, + { 0x000020D6, "ERROR_DS_MISSING_SUPREF" }, + { 0x000020D7, "ERROR_DS_CANT_RETRIEVE_INSTANCE" }, + { 0x000020D8, "ERROR_DS_CODE_INCONSISTENCY" }, + { 0x000020D9, "ERROR_DS_DATABASE_ERROR" }, + { 0x000020DA, "ERROR_DS_GOVERNSID_MISSING" }, + { 0x000020DB, "ERROR_DS_MISSING_EXPECTED_ATT" }, + { 0x000020DC, "ERROR_DS_NCNAME_MISSING_CR_REF" }, + { 0x000020DD, "ERROR_DS_SECURITY_CHECKING_ERROR" }, + { 0x000020DE, "ERROR_DS_SCHEMA_NOT_LOADED" }, + { 0x000020DF, "ERROR_DS_SCHEMA_ALLOC_FAILED" }, + { 0x000020E0, "ERROR_DS_ATT_SCHEMA_REQ_SYNTAX" }, + { 0x000020E1, "ERROR_DS_GCVERIFY_ERROR" }, + { 0x000020E2, "ERROR_DS_DRA_SCHEMA_MISMATCH" }, + { 0x000020E3, "ERROR_DS_CANT_FIND_DSA_OBJ" }, + { 0x000020E4, "ERROR_DS_CANT_FIND_EXPECTED_NC" }, + { 0x000020E5, "ERROR_DS_CANT_FIND_NC_IN_CACHE" }, + { 0x000020E6, "ERROR_DS_CANT_RETRIEVE_CHILD" }, + { 0x000020E7, "ERROR_DS_SECURITY_ILLEGAL_MODIFY" }, + { 0x000020E8, "ERROR_DS_CANT_REPLACE_HIDDEN_REC" }, + { 0x000020E9, "ERROR_DS_BAD_HIERARCHY_FILE" }, + { 0x000020EA, "ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED" }, + { 0x000020EB, "ERROR_DS_CONFIG_PARAM_MISSING" }, + { 0x000020EC, "ERROR_DS_COUNTING_AB_INDICES_FAILED" }, + { 0x000020ED, "ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED" }, + { 0x000020EE, "ERROR_DS_INTERNAL_FAILURE" }, + { 0x000020EF, "ERROR_DS_UNKNOWN_ERROR" }, + { 0x000020F0, "ERROR_DS_ROOT_REQUIRES_CLASS_TO" }, + { 0x000020F1, "ERROR_DS_REFUSING_FSMO_ROLES" }, + { 0x000020F2, "ERROR_DS_MISSING_FSMO_SETTINGS" }, + { 0x000020F3, "ERROR_DS_UNABLE_TO_SURRENDER_ROLES" }, + { 0x000020F4, "ERROR_DS_DRA_GENERIC" }, + { 0x000020F5, "ERROR_DS_DRA_INVALID_PARAMETER" }, + { 0x000020F6, "ERROR_DS_DRA_BUSY" }, + { 0x000020F7, "ERROR_DS_DRA_BAD_DN" }, + { 0x000020F8, "ERROR_DS_DRA_BAD_NC" }, + { 0x000020F9, "ERROR_DS_DRA_DN_EXISTS" }, + { 0x000020FA, "ERROR_DS_DRA_INTERNAL_ERROR" }, + { 0x000020FB, "ERROR_DS_DRA_INCONSISTENT_DIT" }, + { 0x000020FC, "ERROR_DS_DRA_CONNECTION_FAILED" }, + { 0x000020FD, "ERROR_DS_DRA_BAD_INSTANCE_TYPE" }, + { 0x000020FE, "ERROR_DS_DRA_OUT_OF_MEM" }, + { 0x000020FF, "ERROR_DS_DRA_MAIL_PROBLEM" }, + { 0x00002100, "ERROR_DS_DRA_REF_ALREADY_EXISTS" }, + { 0x00002101, "ERROR_DS_DRA_REF_NOT_FOUND" }, + { 0x00002102, "ERROR_DS_DRA_OBJ_IS_REP_SOURCE" }, + { 0x00002103, "ERROR_DS_DRA_DB_ERROR" }, + { 0x00002104, "ERROR_DS_DRA_NO_REPLICA" }, + { 0x00002105, "ERROR_DS_DRA_ACCESS_DENIED" }, + { 0x00002106, "ERROR_DS_DRA_NOT_SUPPORTED" }, + { 0x00002107, "ERROR_DS_DRA_RPC_CANCELLED" }, + { 0x00002108, "ERROR_DS_DRA_SOURCE_DISABLED" }, + { 0x00002109, "ERROR_DS_DRA_SINK_DISABLED" }, + { 0x0000210A, "ERROR_DS_DRA_NAME_COLLISION" }, + { 0x0000210B, "ERROR_DS_DRA_SOURCE_REINSTALLED" }, + { 0x0000210C, "ERROR_DS_DRA_MISSING_PARENT" }, + { 0x0000210D, "ERROR_DS_DRA_PREEMPTED" }, + { 0x0000210E, "ERROR_DS_DRA_ABANDON_SYNC" }, + { 0x0000210F, "ERROR_DS_DRA_SHUTDOWN" }, + { 0x00002110, "ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET" }, + { 0x00002111, "ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA" }, + { 0x00002112, "ERROR_DS_DRA_EXTN_CONNECTION_FAILED" }, + { 0x00002113, "ERROR_DS_INSTALL_SCHEMA_MISMATCH" }, + { 0x00002114, "ERROR_DS_DUP_LINK_ID" }, + { 0x00002115, "ERROR_DS_NAME_ERROR_RESOLVING" }, + { 0x00002116, "ERROR_DS_NAME_ERROR_NOT_FOUND" }, + { 0x00002117, "ERROR_DS_NAME_ERROR_NOT_UNIQUE" }, + { 0x00002118, "ERROR_DS_NAME_ERROR_NO_MAPPING" }, + { 0x00002119, "ERROR_DS_NAME_ERROR_DOMAIN_ONLY" }, + { 0x0000211A, "ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING" }, + { 0x0000211B, "ERROR_DS_CONSTRUCTED_ATT_MOD" }, + { 0x0000211C, "ERROR_DS_WRONG_OM_OBJ_CLASS" }, + { 0x0000211D, "ERROR_DS_DRA_REPL_PENDING" }, + { 0x0000211E, "ERROR_DS_DS_REQUIRED" }, + { 0x0000211F, "ERROR_DS_INVALID_LDAP_DISPLAY_NAME" }, + { 0x00002120, "ERROR_DS_NON_BASE_SEARCH" }, + { 0x00002121, "ERROR_DS_CANT_RETRIEVE_ATTS" }, + { 0x00002122, "ERROR_DS_BACKLINK_WITHOUT_LINK" }, + { 0x00002123, "ERROR_DS_EPOCH_MISMATCH" }, + { 0x00002124, "ERROR_DS_SRC_NAME_MISMATCH" }, + { 0x00002125, "ERROR_DS_SRC_AND_DST_NC_IDENTICAL" }, + { 0x00002126, "ERROR_DS_DST_NC_MISMATCH" + + }, + { 0x00002127, "ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC" }, + { 0x00002128, "ERROR_DS_SRC_GUID_MISMATCH" }, + { 0x00002129, "ERROR_DS_CANT_MOVE_DELETED_OBJECT" }, + { 0x0000212A, "ERROR_DS_PDC_OPERATION_IN_PROGRESS" }, + { 0x0000212B, "ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD" + + }, + { 0x0000212C, "ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION" + + }, + { 0x0000212D, "ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS" }, + { 0x0000212E, "ERROR_DS_NC_MUST_HAVE_NC_PARENT" }, + { 0x0000212F, + "ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE" + "partners. (Applies only to Windows 2000 operating system domain naming masters.)" }, + { 0x00002130, "ERROR_DS_DST_DOMAIN_NOT_NATIVE" }, + { 0x00002131, "ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER" }, + { 0x00002132, "ERROR_DS_CANT_MOVE_ACCOUNT_GROUP" }, + { 0x00002133, "ERROR_DS_CANT_MOVE_RESOURCE_GROUP" }, + { 0x00002134, "ERROR_DS_INVALID_SEARCH_FLAG" }, + { 0x00002135, "ERROR_DS_NO_TREE_DELETE_ABOVE_NC" }, + { 0x00002136, "ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE" }, + { 0x00002137, "ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE" }, + { 0x00002138, "ERROR_DS_SAM_INIT_FAILURE" }, + { 0x00002139, "ERROR_DS_SENSITIVE_GROUP_VIOLATION" }, + { 0x0000213A, "ERROR_DS_CANT_MOD_PRIMARYGROUPID" }, + { 0x0000213B, "ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD" }, + { 0x0000213C, "ERROR_DS_NONSAFE_SCHEMA_CHANGE" + + }, + { 0x0000213D, "ERROR_DS_SCHEMA_UPDATE_DISALLOWED" }, + { 0x0000213E, "ERROR_DS_CANT_CREATE_UNDER_SCHEMA" }, + { 0x0000213F, "ERROR_DS_INSTALL_NO_SRC_SCH_VERSION" }, + { 0x00002140, "ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE" }, + { 0x00002141, "ERROR_DS_INVALID_GROUP_TYPE" }, + { 0x00002142, "ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN" }, + { 0x00002143, "ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN" }, + { 0x00002144, "ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER" }, + { 0x00002145, "ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER" }, + { 0x00002146, "ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER" }, + { 0x00002147, "ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER" }, + { 0x00002148, "ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER" }, + { 0x00002149, "ERROR_DS_HAVE_PRIMARY_MEMBERS" }, + { 0x0000214A, "ERROR_DS_STRING_SD_CONVERSION_FAILED" }, + { 0x0000214B, "ERROR_DS_NAMING_MASTER_GC" }, + { 0x0000214C, "ERROR_DS_DNS_LOOKUP_FAILURE" }, + { 0x0000214D, "ERROR_DS_COULDNT_UPDATE_SPNS" }, + { 0x0000214E, "ERROR_DS_CANT_RETRIEVE_SD" }, + { 0x0000214F, "ERROR_DS_KEY_NOT_UNIQUE" }, + { 0x00002150, "ERROR_DS_WRONG_LINKED_ATT_SYNTAX" }, + { 0x00002151, "ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD" }, + { 0x00002152, "ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY" }, + { 0x00002153, "ERROR_DS_CANT_START" }, + { 0x00002154, "ERROR_DS_INIT_FAILURE" }, + { 0x00002155, "ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION" }, + { 0x00002156, "ERROR_DS_SOURCE_DOMAIN_IN_FOREST" }, + { 0x00002157, "ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST" }, + { 0x00002158, "ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED" }, + { 0x00002159, "ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN" }, + { 0x0000215A, "ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER" }, + { 0x0000215B, "ERROR_DS_SRC_SID_EXISTS_IN_FOREST" }, + { 0x0000215C, "ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH" }, + { 0x0000215D, "ERROR_SAM_INIT_FAILURE" }, + { 0x0000215E, "ERROR_DS_DRA_SCHEMA_INFO_SHIP" }, + { 0x0000215F, "ERROR_DS_DRA_SCHEMA_CONFLICT" }, + { 0x00002160, "ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT" }, + { 0x00002161, "ERROR_DS_DRA_OBJ_NC_MISMATCH" }, + { 0x00002162, "ERROR_DS_NC_STILL_HAS_DSAS" }, + { 0x00002163, "ERROR_DS_GC_REQUIRED" }, + { 0x00002164, "ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY" }, + { 0x00002165, "ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS" }, + { 0x00002166, "ERROR_DS_CANT_ADD_TO_GC" }, + { 0x00002167, "ERROR_DS_NO_CHECKPOINT_WITH_PDC" }, + { 0x00002168, "ERROR_DS_SOURCE_AUDITING_NOT_ENABLED" }, + { 0x00002169, "ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC" }, + { 0x0000216A, "ERROR_DS_INVALID_NAME_FOR_SPN" }, + { 0x0000216B, "ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS" }, + { 0x0000216C, "ERROR_DS_UNICODEPWD_NOT_IN_QUOTES" }, + { 0x0000216D, "ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" }, + { 0x0000216E, "ERROR_DS_MUST_BE_RUN_ON_DST_DC" }, + { 0x0000216F, "ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER" }, + { 0x00002170, "ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ" }, + { 0x00002171, "ERROR_DS_INIT_FAILURE_CONSOLE" }, + { 0x00002172, "ERROR_DS_SAM_INIT_FAILURE_CONSOLE" }, + { 0x00002173, "ERROR_DS_FOREST_VERSION_TOO_HIGH" }, + { 0x00002174, "ERROR_DS_DOMAIN_VERSION_TOO_HIGH" }, + { 0x00002175, "ERROR_DS_FOREST_VERSION_TOO_LOW" }, + { 0x00002176, "ERROR_DS_DOMAIN_VERSION_TOO_LOW" }, + { 0x00002177, "ERROR_DS_INCOMPATIBLE_VERSION" }, + { 0x00002178, "ERROR_DS_LOW_DSA_VERSION" + + }, + { 0x00002179, "ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN" }, + { 0x0000217A, "ERROR_DS_NOT_SUPPORTED_SORT_ORDER" }, + { 0x0000217B, "ERROR_DS_NAME_NOT_UNIQUE" }, + { 0x0000217C, "ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4" }, + { 0x0000217D, "ERROR_DS_OUT_OF_VERSION_STORE" }, + { 0x0000217E, "ERROR_DS_INCOMPATIBLE_CONTROLS_USED" }, + { 0x0000217F, "ERROR_DS_NO_REF_DOMAIN" }, + { 0x00002180, "ERROR_DS_RESERVED_LINK_ID" }, + { 0x00002181, "ERROR_DS_LINK_ID_NOT_AVAILABLE" }, + { 0x00002182, "ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER" }, + { 0x00002183, "ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE" }, + { 0x00002184, "ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC" }, + { 0x00002185, "ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG" }, + { 0x00002186, "ERROR_DS_MODIFYDN_WRONG_GRANDPARENT" }, + { 0x00002187, "ERROR_DS_NAME_ERROR_TRUST_REFERRAL" }, + { 0x00002188, "ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER" }, + { 0x00002189, "ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD" }, + { 0x0000218A, "ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2" + + }, + { 0x0000218B, "ERROR_DS_THREAD_LIMIT_EXCEEDED" }, + { 0x0000218C, "ERROR_DS_NOT_CLOSEST" }, + { 0x0000218D, "ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF" }, + { 0x0000218E, "ERROR_DS_SINGLE_USER_MODE_FAILED" }, + { 0x0000218F, "ERROR_DS_NTDSCRIPT_SYNTAX_ERROR" }, + { 0x00002190, "ERROR_DS_NTDSCRIPT_PROCESS_ERROR" }, + { 0x00002191, "ERROR_DS_DIFFERENT_REPL_EPOCHS" }, + { 0x00002192, "ERROR_DS_DRS_EXTENSIONS_CHANGED" }, + { 0x00002193, "ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR" }, + { 0x00002194, "ERROR_DS_NO_MSDS_INTID" }, + { 0x00002195, "ERROR_DS_DUP_MSDS_INTID" }, + { 0x00002196, "ERROR_DS_EXISTS_IN_RDNATTID" }, + { 0x00002197, "ERROR_DS_AUTHORIZATION_FAILED" }, + { 0x00002198, "ERROR_DS_INVALID_SCRIPT" }, + { 0x00002199, "ERROR_DS_REMOTE_CROSSREF_OP_FAILED" }, + { 0x0000219A, "ERROR_DS_CROSS_REF_BUSY" }, + { 0x0000219B, "ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN" }, + { 0x0000219C, "ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC" }, + { 0x0000219D, "ERROR_DS_DUPLICATE_ID_FOUND" }, + { 0x0000219E, "ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT" }, + { 0x0000219F, "ERROR_DS_GROUP_CONVERSION_ERROR" }, + { 0x000021A0, "ERROR_DS_CANT_MOVE_APP_BASIC_GROUP" }, + { 0x000021A1, "ERROR_DS_CANT_MOVE_APP_QUERY_GROUP" }, + { 0x000021A2, "ERROR_DS_ROLE_NOT_VERIFIED" }, + { 0x000021A3, "ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL" }, + { 0x000021A4, "ERROR_DS_DOMAIN_RENAME_IN_PROGRESS" }, + { 0x000021A5, "ERROR_DS_EXISTING_AD_CHILD_NC" }, + { 0x000021A6, "ERROR_DS_REPL_LIFETIME_EXCEEDED" }, + { 0x000021A7, "ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER" }, + { 0x000021A8, "ERROR_DS_LDAP_SEND_QUEUE_FULL" + + }, + { 0x000021A9, "ERROR_DS_DRA_OUT_SCHEDULE_WINDOW" }, + { 0x000021AA, "ERROR_DS_POLICY_NOT_KNOWN" }, + { 0x000021AB, "ERROR_NO_SITE_SETTINGS_OBJECT" }, + { 0x000021AC, "ERROR_NO_SECRETS" }, + { 0x000021AD, "ERROR_NO_WRITABLE_DC_FOUND" }, + { 0x000021AE, "ERROR_DS_NO_SERVER_OBJECT" }, + { 0x000021AF, "ERROR_DS_NO_NTDSA_OBJECT" }, + { 0x000021B0, "ERROR_DS_NON_ASQ_SEARCH" }, + { 0x000021B1, "ERROR_DS_AUDIT_FAILURE" }, + { 0x000021B2, "ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE" }, + { 0x000021B3, "ERROR_DS_INVALID_SEARCH_FLAG_TUPLE" }, + { 0x000021BF, "ERROR_DS_DRA_RECYCLED_TARGET" }, + { 0x000021C2, "ERROR_DS_HIGH_DSA_VERSION" }, + { 0x000021C7, "ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST" }, + { 0x000021C8, "ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST" }, + { 0x00002329, "DNS_ERROR_RCODE_FORMAT_ERROR" }, + { 0x0000232A, "DNS_ERROR_RCODE_SERVER_FAILURE" }, + { 0x0000232B, "DNS_ERROR_RCODE_NAME_ERROR" }, + { 0x0000232C, "DNS_ERROR_RCODE_NOT_IMPLEMENTED" }, + { 0x0000232D, "DNS_ERROR_RCODE_REFUSED" }, + { 0x0000232E, "DNS_ERROR_RCODE_YXDOMAIN" }, + { 0x0000232F, "DNS_ERROR_RCODE_YXRRSET" }, + { 0x00002330, "DNS_ERROR_RCODE_NXRRSET" }, + { 0x00002331, "DNS_ERROR_RCODE_NOTAUTH" }, + { 0x00002332, "DNS_ERROR_RCODE_NOTZONE" }, + { 0x00002338, "DNS_ERROR_RCODE_BADSIG" }, + { 0x00002339, "DNS_ERROR_RCODE_BADKEY" }, + { 0x0000233A, "DNS_ERROR_RCODE_BADTIME" }, + { 0x0000251D, "DNS_INFO_NO_RECORDS" }, + { 0x0000251E, "DNS_ERROR_BAD_PACKET" }, + { 0x0000251F, "DNS_ERROR_NO_PACKET" }, + { 0x00002520, "DNS_ERROR_RCODE" }, + { 0x00002521, "DNS_ERROR_UNSECURE_PACKET" }, + { 0x0000254F, "DNS_ERROR_INVALID_TYPE" }, + { 0x00002550, "DNS_ERROR_INVALID_IP_ADDRESS" }, + { 0x00002551, "DNS_ERROR_INVALID_PROPERTY" }, + { 0x00002552, "DNS_ERROR_TRY_AGAIN_LATER" }, + { 0x00002553, "DNS_ERROR_NOT_UNIQUE" }, + { 0x00002554, "DNS_ERROR_NON_RFC_NAME" }, + { 0x00002555, "DNS_STATUS_FQDN" }, + { 0x00002556, "DNS_STATUS_DOTTED_NAME" }, + { 0x00002557, "DNS_STATUS_SINGLE_PART_NAME" }, + { 0x00002558, "DNS_ERROR_INVALID_NAME_CHAR" }, + { 0x00002559, "DNS_ERROR_NUMERIC_NAME" }, + { 0x0000255A, "DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER" }, + { 0x0000255B, "DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION" }, + { 0x0000255C, "DNS_ERROR_CANNOT_FIND_ROOT_HINTS" }, + { 0x0000255D, "DNS_ERROR_INCONSISTENT_ROOT_HINTS" }, + { 0x0000255E, "DNS_ERROR_DWORD_VALUE_TOO_SMALL" }, + { 0x0000255F, "DNS_ERROR_DWORD_VALUE_TOO_LARGE" }, + { 0x00002560, "DNS_ERROR_BACKGROUND_LOADING" }, + { 0x00002561, "DNS_ERROR_NOT_ALLOWED_ON_RODC" }, + { 0x00002581, "DNS_ERROR_ZONE_DOES_NOT_EXIST" }, + { 0x00002582, "DNS_ERROR_NO_ZONE_INFO" }, + { 0x00002583, "DNS_ERROR_INVALID_ZONE_OPERATION" }, + { 0x00002584, "DNS_ERROR_ZONE_CONFIGURATION_ERROR" }, + { 0x00002585, "DNS_ERROR_ZONE_HAS_NO_SOA_RECORD" }, + { 0x00002586, "DNS_ERROR_ZONE_HAS_NO_NS_RECORDS" }, + { 0x00002587, "DNS_ERROR_ZONE_LOCKED" }, + { 0x00002588, "DNS_ERROR_ZONE_CREATION_FAILED" }, + { 0x00002589, "DNS_ERROR_ZONE_ALREADY_EXISTS" }, + { 0x0000258A, "DNS_ERROR_AUTOZONE_ALREADY_EXISTS" }, + { 0x0000258B, "DNS_ERROR_INVALID_ZONE_TYPE" }, + { 0x0000258C, "DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP" }, + { 0x0000258D, "DNS_ERROR_ZONE_NOT_SECONDARY" }, + { 0x0000258E, "DNS_ERROR_NEED_SECONDARY_ADDRESSES" }, + { 0x0000258F, "DNS_ERROR_WINS_INIT_FAILED" }, + { 0x00002590, "DNS_ERROR_NEED_WINS_SERVERS" }, + { 0x00002591, "DNS_ERROR_NBSTAT_INIT_FAILED" }, + { 0x00002592, "DNS_ERROR_SOA_DELETE_INVALID" }, + { 0x00002593, "DNS_ERROR_FORWARDER_ALREADY_EXISTS" }, + { 0x00002594, "DNS_ERROR_ZONE_REQUIRES_MASTER_IP" }, + { 0x00002595, "DNS_ERROR_ZONE_IS_SHUTDOWN" }, + { 0x000025B3, "DNS_ERROR_PRIMARY_REQUIRES_DATAFILE" }, + { 0x000025B4, "DNS_ERROR_INVALID_DATAFILE_NAME" }, + { 0x000025B5, "DNS_ERROR_DATAFILE_OPEN_FAILURE" }, + { 0x000025B6, "DNS_ERROR_FILE_WRITEBACK_FAILED" }, + { 0x000025B7, "DNS_ERROR_DATAFILE_PARSING" }, + { 0x000025E5, "DNS_ERROR_RECORD_DOES_NOT_EXIST" }, + { 0x000025E6, "DNS_ERROR_RECORD_FORMAT" }, + { 0x000025E7, "DNS_ERROR_NODE_CREATION_FAILED" }, + { 0x000025E8, "DNS_ERROR_UNKNOWN_RECORD_TYPE" }, + { 0x000025E9, "DNS_ERROR_RECORD_TIMED_OUT" }, + { 0x000025EA, "DNS_ERROR_NAME_NOT_IN_ZONE" }, + { 0x000025EB, "DNS_ERROR_CNAME_LOOP" }, + { 0x000025EC, "DNS_ERROR_NODE_IS_CNAME" }, + { 0x000025ED, "DNS_ERROR_CNAME_COLLISION" }, + { 0x000025EE, "DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT" }, + { 0x000025EF, "DNS_ERROR_RECORD_ALREADY_EXISTS" }, + { 0x000025F0, "DNS_ERROR_SECONDARY_DATA" }, + { 0x000025F1, "DNS_ERROR_NO_CREATE_CACHE_DATA" }, + { 0x000025F2, "DNS_ERROR_NAME_DOES_NOT_EXIST" }, + { 0x000025F3, "DNS_WARNING_PTR_CREATE_FAILED" }, + { 0x000025F4, "DNS_WARNING_DOMAIN_UNDELETED" }, + { 0x000025F5, "DNS_ERROR_DS_UNAVAILABLE" }, + { 0x000025F6, "DNS_ERROR_DS_ZONE_ALREADY_EXISTS" }, + { 0x000025F7, "DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE" }, + { 0x00002617, "DNS_INFO_AXFR_COMPLETE" }, + { 0x00002618, "DNS_ERROR_AXFR" }, + { 0x00002619, "DNS_INFO_ADDED_LOCAL_WINS" }, + { 0x00002649, "DNS_STATUS_CONTINUE_NEEDED" }, + { 0x0000267B, "DNS_ERROR_NO_TCPIP" }, + { 0x0000267C, "DNS_ERROR_NO_DNS_SERVERS" }, + { 0x000026AD, "DNS_ERROR_DP_DOES_NOT_EXIST" }, + { 0x000026AE, "DNS_ERROR_DP_ALREADY_EXISTS" }, + { 0x000026AF, "DNS_ERROR_DP_NOT_ENLISTED" }, + { 0x000026B0, "DNS_ERROR_DP_ALREADY_ENLISTED" }, + { 0x000026B1, "DNS_ERROR_DP_NOT_AVAILABLE" }, + { 0x000026B2, "DNS_ERROR_DP_FSMO_ERROR" }, + { 0x00002714, "WSAEINTR" }, + { 0x00002719, "WSAEBADF" }, + { 0x0000271D, "WSAEACCES" }, + { 0x0000271E, "WSAEFAULT" }, + { 0x00002726, "WSAEINVAL" }, + { 0x00002728, "WSAEMFILE" }, + { 0x00002733, "WSAEWOULDBLOCK" }, + { 0x00002734, "WSAEINPROGRESS" }, + { 0x00002735, "WSAEALREADY" }, + { 0x00002736, "WSAENOTSOCK" }, + { 0x00002737, "WSAEDESTADDRREQ" }, + { 0x00002738, "WSAEMSGSIZE" }, + { 0x00002739, "WSAEPROTOTYPE" }, + { 0x0000273A, "WSAENOPROTOOPT" }, + { 0x0000273B, "WSAEPROTONOSUPPORT" }, + { 0x0000273C, "WSAESOCKTNOSUPPORT" }, + { 0x0000273D, "WSAEOPNOTSUPP" }, + { 0x0000273E, "WSAEPFNOSUPPORT" }, + { 0x0000273F, "WSAEAFNOSUPPORT" }, + { 0x00002740, "WSAEADDRINUSE" }, + { 0x00002741, "WSAEADDRNOTAVAIL" }, + { 0x00002742, "WSAENETDOWN" }, + { 0x00002743, "WSAENETUNREACH" }, + { 0x00002744, "WSAENETRESET" }, + { 0x00002745, "WSAECONNABORTED" }, + { 0x00002746, "WSAECONNRESET" }, + { 0x00002747, "WSAENOBUFS" }, + { 0x00002748, "WSAEISCONN" }, + { 0x00002749, "WSAENOTCONN" }, + { 0x0000274A, "WSAESHUTDOWN" }, + { 0x0000274B, "WSAETOOMANYREFS" }, + { 0x0000274C, "WSAETIMEDOUT" }, + { 0x0000274D, "WSAECONNREFUSED" }, + { 0x0000274E, "WSAELOOP" }, + { 0x0000274F, "WSAENAMETOOLONG" }, + { 0x00002750, "WSAEHOSTDOWN" }, + { 0x00002751, "WSAEHOSTUNREACH" }, + { 0x00002752, "WSAENOTEMPTY" }, + { 0x00002753, "WSAEPROCLIM" }, + { 0x00002754, "WSAEUSERS" }, + { 0x00002755, "WSAEDQUOT" }, + { 0x00002756, "WSAESTALE" }, + { 0x00002757, "WSAEREMOTE" }, + { 0x0000276B, "WSASYSNOTREADY" }, + { 0x0000276C, "WSAVERNOTSUPPORTED" }, + { 0x0000276D, "WSANOTINITIALISED" }, + { 0x00002775, "WSAEDISCON" }, + { 0x00002776, "WSAENOMORE" }, + { 0x00002777, "WSAECANCELLED" }, + { 0x00002778, "WSAEINVALIDPROCTABLE" }, + { 0x00002779, "WSAEINVALIDPROVIDER" }, + { 0x0000277A, "WSAEPROVIDERFAILEDINIT" }, + { 0x0000277B, "WSASYSCALLFAILURE" }, + { 0x0000277C, "WSASERVICE_NOT_FOUND" }, + { 0x0000277D, "WSATYPE_NOT_FOUND" }, + { 0x0000277E, "WSA_E_NO_MORE" }, + { 0x0000277F, "WSA_E_CANCELLED" }, + { 0x00002780, "WSAEREFUSED" }, + { 0x00002AF9, "WSAHOST_NOT_FOUND" }, + { 0x00002AFA, "WSATRY_AGAIN" }, + { 0x00002AFB, "WSANO_RECOVERY" }, + { 0x00002AFC, "WSANO_DATA" }, + { 0x00002AFD, "WSA_QOS_RECEIVERS" }, + { 0x00002AFE, "WSA_QOS_SENDERS" }, + { 0x00002AFF, "WSA_QOS_NO_SENDERS" }, + { 0x00002B00, "WSA_QOS_NO_RECEIVERS" }, + { 0x00002B01, "WSA_QOS_REQUEST_CONFIRMED" }, + { 0x00002B02, "WSA_QOS_ADMISSION_FAILURE" }, + { 0x00002B03, "WSA_QOS_POLICY_FAILURE" }, + { 0x00002B04, "WSA_QOS_BAD_STYLE" }, + { 0x00002B05, "WSA_QOS_BAD_OBJECT" }, + { 0x00002B06, "WSA_QOS_TRAFFIC_CTRL_ERROR" }, + { 0x00002B07, "WSA_QOS_GENERIC_ERROR" }, + { 0x00002B08, "WSA_QOS_ESERVICETYPE" }, + { 0x00002B09, "WSA_QOS_EFLOWSPEC" }, + { 0x00002B0A, "WSA_QOS_EPROVSPECBUF" }, + { 0x00002B0B, "WSA_QOS_EFILTERSTYLE" }, + { 0x00002B0C, "WSA_QOS_EFILTERTYPE" }, + { 0x00002B0D, "WSA_QOS_EFILTERCOUNT" }, + { 0x00002B0E, "WSA_QOS_EOBJLENGTH" }, + { 0x00002B0F, "WSA_QOS_EFLOWCOUNT" }, + { 0x00002B10, "WSA_QOS_EUNKOWNPSOBJ" }, + { 0x00002B11, "WSA_QOS_EPOLICYOBJ" }, + { 0x00002B12, "WSA_QOS_EFLOWDESC" }, + { 0x00002B13, "WSA_QOS_EPSFLOWSPEC" }, + { 0x00002B14, "WSA_QOS_EPSFILTERSPEC" }, + { 0x00002B15, "WSA_QOS_ESDMODEOBJ" }, + { 0x00002B16, "WSA_QOS_ESHAPERATEOBJ" }, + { 0x00002B17, "WSA_QOS_RESERVED_PETYPE" }, + { 0x000032C8, "ERROR_IPSEC_QM_POLICY_EXISTS" }, + { 0x000032C9, "ERROR_IPSEC_QM_POLICY_NOT_FOUND" }, + { 0x000032CA, "ERROR_IPSEC_QM_POLICY_IN_USE" }, + { 0x000032CB, "ERROR_IPSEC_MM_POLICY_EXISTS" }, + { 0x000032CC, "ERROR_IPSEC_MM_POLICY_NOT_FOUND" }, + { 0x000032CD, "ERROR_IPSEC_MM_POLICY_IN_USE" }, + { 0x000032CE, "ERROR_IPSEC_MM_FILTER_EXISTS" }, + { 0x000032CF, "ERROR_IPSEC_MM_FILTER_NOT_FOUND" }, + { 0x000032D0, "ERROR_IPSEC_TRANSPORT_FILTER_EXISTS" }, + { 0x000032D1, "ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND" }, + { 0x000032D2, "ERROR_IPSEC_MM_AUTH_EXISTS" }, + { 0x000032D3, "ERROR_IPSEC_MM_AUTH_NOT_FOUND" }, + { 0x000032D4, "ERROR_IPSEC_MM_AUTH_IN_USE" }, + { 0x000032D5, "ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND" }, + { 0x000032D6, "ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND" }, + { 0x000032D7, "ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND" }, + { 0x000032D8, "ERROR_IPSEC_TUNNEL_FILTER_EXISTS" }, + { 0x000032D9, "ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND" }, + { 0x000032DA, "ERROR_IPSEC_MM_FILTER_PENDING_DELETION" }, + { 0x000032DB, "ERROR_IPSEC_TRANSPORT_FILTER_ENDING_DELETION" }, + { 0x000032DC, "ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION" }, + { 0x000032DD, "ERROR_IPSEC_MM_POLICY_PENDING_ELETION" }, + { 0x000032DE, "ERROR_IPSEC_MM_AUTH_PENDING_DELETION" }, + { 0x000032DF, "ERROR_IPSEC_QM_POLICY_PENDING_DELETION" }, + { 0x000032E0, "WARNING_IPSEC_MM_POLICY_PRUNED" }, + { 0x000032E1, "WARNING_IPSEC_QM_POLICY_PRUNED" }, + { 0x000035E8, "ERROR_IPSEC_IKE_NEG_STATUS_BEGIN" }, + { 0x000035E9, "ERROR_IPSEC_IKE_AUTH_FAIL" }, + { 0x000035EA, "ERROR_IPSEC_IKE_ATTRIB_FAIL" }, + { 0x000035EB, "ERROR_IPSEC_IKE_NEGOTIATION_PENDING" }, + { 0x000035EC, "ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR" }, + { 0x000035ED, "ERROR_IPSEC_IKE_TIMED_OUT" }, + { 0x000035EE, "ERROR_IPSEC_IKE_NO_CERT" }, + { 0x000035EF, "ERROR_IPSEC_IKE_SA_DELETED" }, + { 0x000035F0, "ERROR_IPSEC_IKE_SA_REAPED" }, + { 0x000035F1, "ERROR_IPSEC_IKE_MM_ACQUIRE_DROP" }, + { 0x000035F2, "ERROR_IPSEC_IKE_QM_ACQUIRE_DROP" }, + { 0x000035F3, "ERROR_IPSEC_IKE_QUEUE_DROP_MM" }, + { 0x000035F4, "ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM" }, + { 0x000035F5, "ERROR_IPSEC_IKE_DROP_NO_RESPONSE" }, + { 0x000035F6, "ERROR_IPSEC_IKE_MM_DELAY_DROP" }, + { 0x000035F7, "ERROR_IPSEC_IKE_QM_DELAY_DROP" }, + { 0x000035F8, "ERROR_IPSEC_IKE_ERROR" }, + { 0x000035F9, "ERROR_IPSEC_IKE_CRL_FAILED" }, + { 0x000035FA, "ERROR_IPSEC_IKE_INVALID_KEY_USAGE" }, + { 0x000035FB, "ERROR_IPSEC_IKE_INVALID_CERT_TYPE" }, + { 0x000035FC, "ERROR_IPSEC_IKE_NO_PRIVATE_KEY" }, + { 0x000035FE, "ERROR_IPSEC_IKE_DH_FAIL" }, + { 0x00003600, "ERROR_IPSEC_IKE_INVALID_HEADER" }, + { 0x00003601, "ERROR_IPSEC_IKE_NO_POLICY" }, + { 0x00003602, "ERROR_IPSEC_IKE_INVALID_SIGNATURE" }, + { 0x00003603, "ERROR_IPSEC_IKE_KERBEROS_ERROR" }, + { 0x00003604, "ERROR_IPSEC_IKE_NO_PUBLIC_KEY" }, + { 0x00003605, "ERROR_IPSEC_IKE_PROCESS_ERR" }, + { 0x00003606, "ERROR_IPSEC_IKE_PROCESS_ERR_SA" }, + { 0x00003607, "ERROR_IPSEC_IKE_PROCESS_ERR_PROP" }, + { 0x00003608, "ERROR_IPSEC_IKE_PROCESS_ERR_TRANS" }, + { 0x00003609, "ERROR_IPSEC_IKE_PROCESS_ERR_KE" }, + { 0x0000360A, "ERROR_IPSEC_IKE_PROCESS_ERR_ID" }, + { 0x0000360B, "ERROR_IPSEC_IKE_PROCESS_ERR_CERT" }, + { 0x0000360C, "ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ" }, + { 0x0000360D, "ERROR_IPSEC_IKE_PROCESS_ERR_HASH" }, + { 0x0000360E, "ERROR_IPSEC_IKE_PROCESS_ERR_SIG" }, + { 0x0000360F, "ERROR_IPSEC_IKE_PROCESS_ERR_NONCE" }, + { 0x00003610, "ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY" }, + { 0x00003611, "ERROR_IPSEC_IKE_PROCESS_ERR_DELETE" }, + { 0x00003612, "ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR" }, + { 0x00003613, "ERROR_IPSEC_IKE_INVALID_PAYLOAD" }, + { 0x00003614, "ERROR_IPSEC_IKE_LOAD_SOFT_SA" }, + { 0x00003615, "ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN" }, + { 0x00003616, "ERROR_IPSEC_IKE_INVALID_COOKIE" }, + { 0x00003617, "ERROR_IPSEC_IKE_NO_PEER_CERT" }, + { 0x00003618, "ERROR_IPSEC_IKE_PEER_CRL_FAILED" }, + { 0x00003619, "ERROR_IPSEC_IKE_POLICY_CHANGE" }, + { 0x0000361A, "ERROR_IPSEC_IKE_NO_MM_POLICY" }, + { 0x0000361B, "ERROR_IPSEC_IKE_NOTCBPRIV" }, + { 0x0000361C, "ERROR_IPSEC_IKE_SECLOADFAIL" }, + { 0x0000361D, "ERROR_IPSEC_IKE_FAILSSPINIT" }, + { 0x0000361E, "ERROR_IPSEC_IKE_FAILQUERYSSP" }, + { 0x0000361F, "ERROR_IPSEC_IKE_SRVACQFAIL" }, + { 0x00003620, "ERROR_IPSEC_IKE_SRVQUERYCRED" }, + { 0x00003621, "ERROR_IPSEC_IKE_GETSPIFAIL" + + }, + { 0x00003622, "ERROR_IPSEC_IKE_INVALID_FILTER" }, + { 0x00003623, "ERROR_IPSEC_IKE_OUT_OF_MEMORY" }, + { 0x00003624, "ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED" }, + { 0x00003625, "ERROR_IPSEC_IKE_INVALID_POLICY" }, + { 0x00003626, "ERROR_IPSEC_IKE_UNKNOWN_DOI" }, + { 0x00003627, "ERROR_IPSEC_IKE_INVALID_SITUATION" }, + { 0x00003628, "ERROR_IPSEC_IKE_DH_FAILURE" }, + { 0x00003629, "ERROR_IPSEC_IKE_INVALID_GROUP" }, + { 0x0000362A, "ERROR_IPSEC_IKE_ENCRYPT" }, + { 0x0000362B, "ERROR_IPSEC_IKE_DECRYPT" }, + { 0x0000362C, "ERROR_IPSEC_IKE_POLICY_MATCH" }, + { 0x0000362D, "ERROR_IPSEC_IKE_UNSUPPORTED_ID" }, + { 0x0000362E, "ERROR_IPSEC_IKE_INVALID_HASH" }, + { 0x0000362F, "ERROR_IPSEC_IKE_INVALID_HASH_ALG" }, + { 0x00003630, "ERROR_IPSEC_IKE_INVALID_HASH_SIZE" }, + { 0x00003631, "ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG" }, + { 0x00003632, "ERROR_IPSEC_IKE_INVALID_AUTH_ALG" }, + { 0x00003633, "ERROR_IPSEC_IKE_INVALID_SIG" }, + { 0x00003634, "ERROR_IPSEC_IKE_LOAD_FAILED" }, + { 0x00003635, "ERROR_IPSEC_IKE_RPC_DELETE" }, + { 0x00003636, "ERROR_IPSEC_IKE_BENIGN_REINIT" }, + { 0x00003637, "ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY" }, + { 0x00003639, "ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN" }, + { 0x0000363A, "ERROR_IPSEC_IKE_MM_LIMIT" }, + { 0x0000363B, "ERROR_IPSEC_IKE_NEGOTIATION_DISABLED" }, + { 0x0000363C, "ERROR_IPSEC_IKE_QM_LIMIT" }, + { 0x0000363D, "ERROR_IPSEC_IKE_MM_EXPIRED" }, + { 0x0000363E, "ERROR_IPSEC_IKE_PEER_MM_ASSUMED_INVALID" }, + { 0x0000363F, "ERROR_IPSEC_IKE_CERT_CHAIN_POLICY_MISMATCH" }, + { 0x00003640, "ERROR_IPSEC_IKE_UNEXPECTED_MESSAGE_ID" }, + { 0x00003641, "ERROR_IPSEC_IKE_INVALID_UMATTS" }, + { 0x00003642, "ERROR_IPSEC_IKE_DOS_COOKIE_SENT" }, + { 0x00003643, "ERROR_IPSEC_IKE_SHUTTING_DOWN" }, + { 0x00003644, "ERROR_IPSEC_IKE_CGA_AUTH_FAILED" }, + { 0x00003645, "ERROR_IPSEC_IKE_PROCESS_ERR_NATOA" }, + { 0x00003646, "ERROR_IPSEC_IKE_INVALID_MM_FOR_QM" }, + { 0x00003647, "ERROR_IPSEC_IKE_QM_EXPIRED" }, + { 0x00003648, "ERROR_IPSEC_IKE_TOO_MANY_FILTERS" }, + { 0x00003649, "ERROR_IPSEC_IKE_NEG_STATUS_END" }, + { 0x000036B0, "ERROR_SXS_SECTION_NOT_FOUND" }, + { 0x000036B1, "ERROR_SXS_CANT_GEN_ACTCTX" }, + { 0x000036B2, "ERROR_SXS_INVALID_ACTCTXDATA_FORMAT" }, + { 0x000036B3, "ERROR_SXS_ASSEMBLY_NOT_FOUND" }, + { 0x000036B4, "ERROR_SXS_MANIFEST_FORMAT_ERROR" }, + { 0x000036B5, "ERROR_SXS_MANIFEST_PARSE_ERROR" }, + { 0x000036B6, "ERROR_SXS_ACTIVATION_CONTEXT_DISABLED" }, + { 0x000036B7, "ERROR_SXS_KEY_NOT_FOUND" }, + { 0x000036B8, "ERROR_SXS_VERSION_CONFLICT" }, + { 0x000036B9, "ERROR_SXS_WRONG_SECTION_TYPE" }, + { 0x000036BA, "ERROR_SXS_THREAD_QUERIES_DISABLED" }, + { 0x000036BB, "ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET" }, + { 0x000036BC, "ERROR_SXS_UNKNOWN_ENCODING_GROUP" }, + { 0x000036BD, "ERROR_SXS_UNKNOWN_ENCODING" }, + { 0x000036BE, "ERROR_SXS_INVALID_XML_NAMESPACE_URI" }, + { 0x000036BF, "ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_OT_INSTALLED" }, + { 0x000036C0, "ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED" }, + { 0x000036C1, "ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE" }, + { 0x000036C2, "ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE" }, + { 0x000036C3, "ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE" }, + { 0x000036C4, "ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT" }, + { 0x000036C5, "ERROR_SXS_DUPLICATE_DLL_NAME" }, + { 0x000036C6, "ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME" }, + { 0x000036C7, "ERROR_SXS_DUPLICATE_CLSID" }, + { 0x000036C8, "ERROR_SXS_DUPLICATE_IID" }, + { 0x000036C9, "ERROR_SXS_DUPLICATE_TLBID" }, + { 0x000036CA, "ERROR_SXS_DUPLICATE_PROGID" }, + { 0x000036CB, "ERROR_SXS_DUPLICATE_ASSEMBLY_NAME" }, + { 0x000036CC, "ERROR_SXS_FILE_HASH_MISMATCH" }, + { 0x000036CD, "ERROR_SXS_POLICY_PARSE_ERROR" }, + { 0x000036CE, "ERROR_SXS_XML_E_MISSINGQUOTE" }, + { 0x000036CF, "ERROR_SXS_XML_E_COMMENTSYNTAX" }, + { 0x000036D0, "ERROR_SXS_XML_E_BADSTARTNAMECHAR" }, + { 0x000036D1, "ERROR_SXS_XML_E_BADNAMECHAR" }, + { 0x000036D2, "ERROR_SXS_XML_E_BADCHARINSTRING" }, + { 0x000036D3, "ERROR_SXS_XML_E_XMLDECLSYNTAX" }, + { 0x000036D4, "ERROR_SXS_XML_E_BADCHARDATA" }, + { 0x000036D5, "ERROR_SXS_XML_E_MISSINGWHITESPACE" }, + { 0x000036D6, "ERROR_SXS_XML_E_EXPECTINGTAGEND" }, + { 0x000036D7, "ERROR_SXS_XML_E_MISSINGSEMICOLON" }, + { 0x000036D8, "ERROR_SXS_XML_E_UNBALANCEDPAREN" }, + { 0x000036D9, "ERROR_SXS_XML_E_INTERNALERROR" }, + { 0x000036DA, "ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE" }, + { 0x000036DB, "ERROR_SXS_XML_E_INCOMPLETE_ENCODING" }, + { 0x000036DC, "ERROR_SXS_XML_E_MISSING_PAREN" }, + { 0x000036DD, "ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE" }, + { 0x000036DE, "ERROR_SXS_XML_E_MULTIPLE_COLONS" }, + { 0x000036DF, "ERROR_SXS_XML_E_INVALID_DECIMAL" }, + { 0x000036E0, "ERROR_SXS_XML_E_INVALID_HEXIDECIMAL" }, + { 0x000036E1, "ERROR_SXS_XML_E_INVALID_UNICODE" }, + { 0x000036E2, "ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK" }, + { 0x000036E3, "ERROR_SXS_XML_E_UNEXPECTEDENDTAG" }, + { 0x000036E4, "ERROR_SXS_XML_E_UNCLOSEDTAG" }, + { 0x000036E5, "ERROR_SXS_XML_E_DUPLICATEATTRIBUTE" }, + { 0x000036E6, "ERROR_SXS_XML_E_MULTIPLEROOTS" }, + { 0x000036E7, "ERROR_SXS_XML_E_INVALIDATROOTLEVEL" }, + { 0x000036E8, "ERROR_SXS_XML_E_BADXMLDECL" }, + { 0x000036E9, "ERROR_SXS_XML_E_MISSINGROOT" }, + { 0x000036EA, "ERROR_SXS_XML_E_UNEXPECTEDEOF" }, + { 0x000036EB, "ERROR_SXS_XML_E_BADPEREFINSUBSET" }, + { 0x000036EC, "ERROR_SXS_XML_E_UNCLOSEDSTARTTAG" }, + { 0x000036ED, "ERROR_SXS_XML_E_UNCLOSEDENDTAG" }, + { 0x000036EE, "ERROR_SXS_XML_E_UNCLOSEDSTRING" }, + { 0x000036EF, "ERROR_SXS_XML_E_UNCLOSEDCOMMENT" }, + { 0x000036F0, "ERROR_SXS_XML_E_UNCLOSEDDECL" }, + { 0x000036F1, "ERROR_SXS_XML_E_UNCLOSEDCDATA" }, + { 0x000036F2, "ERROR_SXS_XML_E_RESERVEDNAMESPACE" }, + { 0x000036F3, "ERROR_SXS_XML_E_INVALIDENCODING" }, + { 0x000036F4, "ERROR_SXS_XML_E_INVALIDSWITCH" }, + { 0x000036F5, "ERROR_SXS_XML_E_BADXMLCASE" }, + { 0x000036F6, "ERROR_SXS_XML_E_INVALID_STANDALONE" }, + { 0x000036F7, "ERROR_SXS_XML_E_UNEXPECTED_STANDALONE" }, + { 0x000036F8, "ERROR_SXS_XML_E_INVALID_VERSION" }, + { 0x000036F9, "ERROR_SXS_XML_E_MISSINGEQUALS" }, + { 0x000036FA, "ERROR_SXS_PROTECTION_RECOVERY_FAILED" }, + { 0x000036FB, "ERROR_SXS_PROTECTION_PUBLIC_KEY_OO_SHORT" }, + { 0x000036FC, "ERROR_SXS_PROTECTION_CATALOG_NOT_VALID" }, + { 0x000036FD, "ERROR_SXS_UNTRANSLATABLE_HRESULT" }, + { 0x000036FE, "ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING" }, + { 0x000036FF, "ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE" }, + { 0x00003700, "ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME" }, + { 0x00003701, "ERROR_SXS_ASSEMBLY_MISSING" }, + { 0x00003702, "ERROR_SXS_CORRUPT_ACTIVATION_STACK" }, + { 0x00003703, "ERROR_SXS_CORRUPTION" }, + { 0x00003704, "ERROR_SXS_EARLY_DEACTIVATION" }, + { 0x00003705, "ERROR_SXS_INVALID_DEACTIVATION" }, + { 0x00003706, "ERROR_SXS_MULTIPLE_DEACTIVATION" }, + { 0x00003707, "ERROR_SXS_PROCESS_TERMINATION_REQUESTED" }, + { 0x00003708, "ERROR_SXS_RELEASE_ACTIVATION_ONTEXT" }, + { 0x00003709, "ERROR_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY" }, + { 0x0000370A, "ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE" }, + { 0x0000370B, "ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME" }, + { 0x0000370C, "ERROR_SXS_IDENTITY_DUPLICATE_ATTRIBUTE" }, + { 0x0000370D, "ERROR_SXS_IDENTITY_PARSE_ERROR" }, + { 0x0000370E, "ERROR_MALFORMED_SUBSTITUTION_STRING" }, + { 0x0000370F, "ERROR_SXS_INCORRECT_PUBLIC_KEY_OKEN" }, + { 0x00003710, "ERROR_UNMAPPED_SUBSTITUTION_STRING" }, + { 0x00003711, "ERROR_SXS_ASSEMBLY_NOT_LOCKED" }, + { 0x00003712, "ERROR_SXS_COMPONENT_STORE_CORRUPT" }, + { 0x00003713, "ERROR_ADVANCED_INSTALLER_FAILED" }, + { 0x00003714, "ERROR_XML_ENCODING_MISMATCH" }, + { 0x00003715, "ERROR_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT" }, + { 0x00003716, "ERROR_SXS_IDENTITIES_DIFFERENT" }, + { 0x00003717, "ERROR_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT" }, + { 0x00003718, "ERROR_SXS_FILE_NOT_PART_OF_ASSEMBLY" }, + { 0x00003719, "ERROR_SXS_MANIFEST_TOO_BIG" }, + { 0x0000371A, "ERROR_SXS_SETTING_NOT_REGISTERED" }, + { 0x0000371B, "ERROR_SXS_TRANSACTION_CLOSURE_INCOMPLETE" }, + { 0x00003A98, "ERROR_EVT_INVALID_CHANNEL_PATH" }, + { 0x00003A99, "ERROR_EVT_INVALID_QUERY" }, + { 0x00003A9A, "ERROR_EVT_PUBLISHER_METADATA_NOT_FOUND" }, + { 0x00003A9B, "ERROR_EVT_EVENT_TEMPLATE_NOT_FOUND" }, + { 0x00003A9C, "ERROR_EVT_INVALID_PUBLISHER_NAME" }, + { 0x00003A9D, "ERROR_EVT_INVALID_EVENT_DATA" }, + { 0x00003A9F, "ERROR_EVT_CHANNEL_NOT_FOUND" }, + { 0x00003AA0, "ERROR_EVT_MALFORMED_XML_TEXT" }, + { 0x00003AA1, "ERROR_EVT_SUBSCRIPTION_TO_DIRECT_CHANNEL" }, + { 0x00003AA2, "ERROR_EVT_CONFIGURATION_ERROR" }, + { 0x00003AA3, "ERROR_EVT_QUERY_RESULT_STALE" }, + { 0x00003AA4, "ERROR_EVT_QUERY_RESULT_INVALID_POSITION" }, + { 0x00003AA5, "ERROR_EVT_NON_VALIDATING_MSXML" }, + { 0x00003AA6, "ERROR_EVT_FILTER_ALREADYSCOPED" }, + { 0x00003AA7, "ERROR_EVT_FILTER_NOTELTSET" }, + { 0x00003AA8, "ERROR_EVT_FILTER_INVARG" }, + { 0x00003AA9, "ERROR_EVT_FILTER_INVTEST" }, + { 0x00003AAA, "ERROR_EVT_FILTER_INVTYPE" }, + { 0x00003AAB, "ERROR_EVT_FILTER_PARSEERR" }, + { 0x00003AAC, "ERROR_EVT_FILTER_UNSUPPORTEDOP" }, + { 0x00003AAD, "ERROR_EVT_FILTER_UNEXPECTEDTOKEN" }, + { 0x00003AAE, "ERROR_EVT_INVALID_OPERATION_OVER_ENABLED_DIRECT_CHANNEL" }, + { 0x00003AAF, "ERROR_EVT_INVALID_CHANNEL_PROPERTY_VALUE" + + }, + { 0x00003AB0, "ERROR_EVT_INVALID_PUBLISHER_PROPERTY_VALUE" + + }, + { 0x00003AB1, "ERROR_EVT_CHANNEL_CANNOT_ACTIVATE" }, + { 0x00003AB2, "ERROR_EVT_FILTER_TOO_COMPLEX" }, + { 0x00003AB3, "ERROR_EVT_MESSAGE_NOT_FOUND" }, + { 0x00003AB4, "ERROR_EVT_MESSAGE_ID_NOT_FOUND" }, + { 0x00003AB5, "ERROR_EVT_UNRESOLVED_VALUE_INSERT" }, + { 0x00003AB6, "ERROR_EVT_UNRESOLVED_PARAMETER_INSERT" }, + { 0x00003AB7, "ERROR_EVT_MAX_INSERTS_REACHED" }, + { 0x00003AB8, "ERROR_EVT_EVENT_DEFINITION_NOT_OUND" }, + { 0x00003AB9, "ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND" }, + { 0x00003ABA, "ERROR_EVT_VERSION_TOO_OLD" }, + { 0x00003ABB, "ERROR_EVT_VERSION_TOO_NEW" }, + { 0x00003ABC, "ERROR_EVT_CANNOT_OPEN_CHANNEL_OF_QUERY" }, + { 0x00003ABD, "ERROR_EVT_PUBLISHER_DISABLED" }, + { 0x00003AE8, "ERROR_EC_SUBSCRIPTION_CANNOT_ACTIVATE" }, + { 0x00003AE9, "ERROR_EC_LOG_DISABLED" }, + { 0x00003AFC, "ERROR_MUI_FILE_NOT_FOUND" }, + { 0x00003AFD, "ERROR_MUI_INVALID_FILE" }, + { 0x00003AFE, "ERROR_MUI_INVALID_RC_CONFIG" }, + { 0x00003AFF, "ERROR_MUI_INVALID_LOCALE_NAME" }, + { 0x00003B00, "ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME" }, + { 0x00003B01, "ERROR_MUI_FILE_NOT_LOADED" }, + { 0x00003B02, "ERROR_RESOURCE_ENUM_USER_STOP" }, + { 0x00003B03, "ERROR_MUI_INTLSETTINGS_UILANG_NOT_INSTALLED" }, + { 0x00003B04, "ERROR_MUI_INTLSETTINGS_INVALID_LOCALE_NAME" }, + { 0x00003B60, "ERROR_MCA_INVALID_CAPABILITIES_STRING" }, + { 0x00003B61, "ERROR_MCA_INVALID_VCP_VERSION" }, + { 0x00003B62, "ERROR_MCA_MONITOR_VIOLATES_MCCS_SPECIFICATION" }, + { 0x00003B63, "ERROR_MCA_MCCS_VERSION_MISMATCH" }, + { 0x00003B64, "ERROR_MCA_UNSUPPORTED_MCCS_VERSION" }, + { 0x00003B65, "ERROR_MCA_INTERNAL_ERROR" }, + { 0x00003B66, "ERROR_MCA_INVALID_TECHNOLOGY_TYPE_RETURNED" }, + { 0x00003B67, "ERROR_MCA_UNSUPPORTED_COLOR_TEMPERATURE" }, + { 0x00003B92, "ERROR_AMBIGUOUS_SYSTEM_DEVICE" }, + { 0x00003BC3, "ERROR_SYSTEM_DEVICE_NOT_FOUND" } +}; + +static const struct ntstatus_map ntstatusmap[] = { + + { 0x00000000, "STATUS_SUCCESS" }, + { 0x00000000, "STATUS_WAIT_0" }, + { 0x00000001, "STATUS_WAIT_1" }, + { 0x00000002, "STATUS_WAIT_2" }, + { 0x00000003, "STATUS_WAIT_3" }, + { 0x0000003F, "STATUS_WAIT_63" }, + { 0x00000080, "STATUS_ABANDONED" }, + { 0x00000080, "STATUS_ABANDONED_WAIT_0" }, + { 0x000000BF, "STATUS_ABANDONED_WAIT_63" }, + { 0x000000C0, "STATUS_USER_APC" }, + { 0x00000101, "STATUS_ALERTED" }, + { 0x00000102, "STATUS_TIMEOUT" }, + { 0x00000103, "STATUS_PENDING" }, + { 0x00000104, "STATUS_REPARSE" }, + { 0x00000105, "STATUS_MORE_ENTRIES" }, + { 0x00000106, "STATUS_NOT_ALL_ASSIGNED" }, + { 0x00000107, "STATUS_SOME_NOT_MAPPED" }, + { 0x00000108, "STATUS_OPLOCK_BREAK_IN_PROGRESS" }, + { 0x00000109, "STATUS_VOLUME_MOUNTED" }, + { 0x0000010A, "STATUS_RXACT_COMMITTED" }, + { 0x0000010B, "STATUS_NOTIFY_CLEANUP" }, + { 0x0000010C, "STATUS_NOTIFY_ENUM_DIR" }, + { 0x0000010D, "STATUS_NO_QUOTAS_FOR_ACCOUNT" }, + { 0x0000010E, "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED" }, + { 0x00000110, "STATUS_PAGE_FAULT_TRANSITION" }, + { 0x00000111, "STATUS_PAGE_FAULT_DEMAND_ZERO" }, + { 0x00000112, "STATUS_PAGE_FAULT_COPY_ON_WRITE" }, + { 0x00000113, "STATUS_PAGE_FAULT_GUARD_PAGE" }, + { 0x00000114, "STATUS_PAGE_FAULT_PAGING_FILE" }, + { 0x00000115, "STATUS_CACHE_PAGE_LOCKED" }, + { 0x00000116, "STATUS_CRASH_DUMP" }, + { 0x00000117, "STATUS_BUFFER_ALL_ZEROS" }, + { 0x00000118, "STATUS_REPARSE_OBJECT" }, + { 0x00000119, "STATUS_RESOURCE_REQUIREMENTS_CHANGED" }, + { 0x00000120, "STATUS_TRANSLATION_COMPLETE" }, + { 0x00000121, "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY" }, + { 0x00000122, "STATUS_NOTHING_TO_TERMINATE" }, + { 0x00000123, "STATUS_PROCESS_NOT_IN_JOB" }, + { 0x00000124, "STATUS_PROCESS_IN_JOB" }, + { 0x00000125, "STATUS_VOLSNAP_HIBERNATE_READY" }, + { 0x00000126, "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY" }, + { 0x00000127, "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED" }, + { 0x00000128, "STATUS_INTERRUPT_STILL_CONNECTED" }, + { 0x00000129, "STATUS_PROCESS_CLONED" }, + { 0x0000012A, "STATUS_FILE_LOCKED_WITH_ONLY_READERS" }, + { 0x0000012B, "STATUS_FILE_LOCKED_WITH_WRITERS" }, + { 0x00000202, "STATUS_RESOURCEMANAGER_READ_ONLY" }, + { 0x00000367, "STATUS_WAIT_FOR_OPLOCK" }, + { 0x00010001, "DBG_EXCEPTION_HANDLED" }, + { 0x00010002, "DBG_CONTINUE" }, + { 0x001C0001, "STATUS_FLT_IO_COMPLETE" }, + { 0x40000000, "STATUS_OBJECT_NAME_EXISTS" }, + { 0x40000001, "STATUS_THREAD_WAS_SUSPENDED" }, + { 0x40000002, "STATUS_WORKING_SET_LIMIT_RANGE" }, + { 0x40000003, "STATUS_IMAGE_NOT_AT_BASE" }, + { 0x40000004, "STATUS_RXACT_STATE_CREATED" }, + { 0x40000005, "STATUS_SEGMENT_NOTIFICATION" }, + { 0x40000006, "STATUS_LOCAL_USER_SESSION_KEY" }, + { 0x40000007, "STATUS_BAD_CURRENT_DIRECTORY" }, + { 0x40000008, "STATUS_SERIAL_MORE_WRITES" }, + { 0x40000009, "STATUS_REGISTRY_RECOVERED" }, + { 0x4000000A, "STATUS_FT_READ_RECOVERY_FROM_BACKUP" }, + { 0x4000000B, "STATUS_FT_WRITE_RECOVERY" }, + { 0x4000000C, "STATUS_SERIAL_COUNTER_TIMEOUT" }, + { 0x4000000D, "STATUS_NULL_LM_PASSWORD" }, + { 0x4000000E, "STATUS_IMAGE_MACHINE_TYPE_MISMATCH" }, + { 0x4000000F, "STATUS_RECEIVE_PARTIAL" }, + { 0x40000010, "STATUS_RECEIVE_EXPEDITED" }, + { 0x40000011, "STATUS_RECEIVE_PARTIAL_EXPEDITED" }, + { 0x40000012, "STATUS_EVENT_DONE" }, + { 0x40000013, "STATUS_EVENT_PENDING" }, + { 0x40000014, "STATUS_CHECKING_FILE_SYSTEM" }, + { 0x40000015, "STATUS_FATAL_APP_EXIT" }, + { 0x40000016, "STATUS_PREDEFINED_HANDLE" }, + { 0x40000017, "STATUS_WAS_UNLOCKED" }, + { 0x40000018, "STATUS_SERVICE_NOTIFICATION" }, + { 0x40000019, "STATUS_WAS_LOCKED" }, + { 0x4000001A, "STATUS_LOG_HARD_ERROR" }, + { 0x4000001B, "STATUS_ALREADY_WIN32" }, + { 0x4000001C, "STATUS_WX86_UNSIMULATE" }, + { 0x4000001D, "STATUS_WX86_CONTINUE" }, + { 0x4000001E, "STATUS_WX86_SINGLE_STEP" }, + { 0x4000001F, "STATUS_WX86_BREAKPOINT" }, + { 0x40000020, "STATUS_WX86_EXCEPTION_CONTINUE" }, + { 0x40000021, "STATUS_WX86_EXCEPTION_LASTCHANCE" }, + { 0x40000022, "STATUS_WX86_EXCEPTION_CHAIN" }, + { 0x40000023, "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE" }, + { 0x40000024, "STATUS_NO_YIELD_PERFORMED" }, + { 0x40000025, "STATUS_TIMER_RESUME_IGNORED" }, + { 0x40000026, "STATUS_ARBITRATION_UNHANDLED" }, + { 0x40000027, "STATUS_CARDBUS_NOT_SUPPORTED" }, + { 0x40000028, "STATUS_WX86_CREATEWX86TIB" }, + { 0x40000029, "STATUS_MP_PROCESSOR_MISMATCH" }, + { 0x4000002A, "STATUS_HIBERNATED" }, + { 0x4000002B, "STATUS_RESUME_HIBERNATION" }, + { 0x4000002C, "STATUS_FIRMWARE_UPDATED" }, + { 0x4000002D, "STATUS_DRIVERS_LEAKING_LOCKED_PAGES" }, + { 0x4000002E, "STATUS_MESSAGE_RETRIEVED" }, + { 0x4000002F, "STATUS_SYSTEM_POWERSTATE_TRANSITION" }, + { 0x40000030, "STATUS_ALPC_CHECK_COMPLETION_LIST" }, + { 0x40000031, "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION" }, + { 0x40000032, "STATUS_ACCESS_AUDIT_BY_POLICY" }, + { 0x40000033, "STATUS_ABANDON_HIBERFILE" }, + { 0x40000034, "STATUS_BIZRULES_NOT_ENABLED" }, + { 0x40000294, "STATUS_WAKE_SYSTEM" }, + { 0x40000370, "STATUS_DS_SHUTTING_DOWN" }, + { 0x40010001, "DBG_REPLY_LATER" }, + { 0x40010002, "DBG_UNABLE_TO_PROVIDE_HANDLE" }, + { 0x40010003, "DBG_TERMINATE_THREAD" }, + { 0x40010004, "DBG_TERMINATE_PROCESS" }, + { 0x40010005, "DBG_CONTROL_C" }, + { 0x40010006, "DBG_PRINTEXCEPTION_C" }, + { 0x40010007, "DBG_RIPEXCEPTION" }, + { 0x40010008, "DBG_CONTROL_BREAK" }, + { 0x40010009, "DBG_COMMAND_EXCEPTION" }, + { 0x40020056, "RPC_NT_UUID_LOCAL_ONLY" }, + { 0x400200AF, "RPC_NT_SEND_INCOMPLETE" }, + { 0x400A0004, "STATUS_CTX_CDM_CONNECT" }, + { 0x400A0005, "STATUS_CTX_CDM_DISCONNECT" }, + { 0x4015000D, "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT" }, + { 0x40190034, "STATUS_RECOVERY_NOT_NEEDED" }, + { 0x40190035, "STATUS_RM_ALREADY_STARTED" }, + { 0x401A000C, "STATUS_LOG_NO_RESTART" }, + { 0x401B00EC, "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST" }, + { 0x401E000A, "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED" }, + { 0x401E0117, "STATUS_GRAPHICS_DRIVER_MISMATCH" }, + { 0x401E0307, "STATUS_GRAPHICS_MODE_NOT_PINNED" }, + { 0x401E031E, "STATUS_GRAPHICS_NO_PREFERRED_MODE" }, + { 0x401E034B, "STATUS_GRAPHICS_DATASET_IS_EMPTY" }, + { 0x401E034C, "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET" }, + { 0x401E0351, "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED" }, + { 0x401E042F, "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS" }, + { 0x401E0437, "STATUS_GRAPHICS_LEADLINK_START_DEFERRED" }, + { 0x401E0439, "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY" }, + { 0x401E043A, "STATUS_GRAPHICS_START_DEFERRED" }, + { 0x40230001, "STATUS_NDIS_INDICATION_REQUIRED" }, + { 0x80000001, "STATUS_GUARD_PAGE_VIOLATION" }, + { 0x80000002, "STATUS_DATATYPE_MISALIGNMENT" }, + { 0x80000003, "STATUS_BREAKPOINT" }, + { 0x80000004, "STATUS_SINGLE_STEP" }, + { 0x80000005, "STATUS_BUFFER_OVERFLOW" }, + { 0x80000006, "STATUS_NO_MORE_FILES" }, + { 0x80000007, "STATUS_WAKE_SYSTEM_DEBUGGER" }, + { 0x8000000A, "STATUS_HANDLES_CLOSED" }, + { 0x8000000B, "STATUS_NO_INHERITANCE" }, + { 0x8000000C, "STATUS_GUID_SUBSTITUTION_MADE" }, + { 0x8000000D, "STATUS_PARTIAL_COPY" }, + { 0x8000000E, "STATUS_DEVICE_PAPER_EMPTY" }, + { 0x8000000F, "STATUS_DEVICE_POWERED_OFF" }, + { 0x80000010, "STATUS_DEVICE_OFF_LINE" }, + { 0x80000011, "STATUS_DEVICE_BUSY" }, + { 0x80000012, "STATUS_NO_MORE_EAS" }, + { 0x80000013, "STATUS_INVALID_EA_NAME" }, + { 0x80000014, "STATUS_EA_LIST_INCONSISTENT" }, + { 0x80000015, "STATUS_INVALID_EA_FLAG" }, + { 0x80000016, "STATUS_VERIFY_REQUIRED" }, + { 0x80000017, "STATUS_EXTRANEOUS_INFORMATION" }, + { 0x80000018, "STATUS_RXACT_COMMIT_NECESSARY" }, + { 0x8000001A, "STATUS_NO_MORE_ENTRIES" }, + { 0x8000001B, "STATUS_FILEMARK_DETECTED" }, + { 0x8000001C, "STATUS_MEDIA_CHANGED" }, + { 0x8000001D, "STATUS_BUS_RESET" }, + { 0x8000001E, "STATUS_END_OF_MEDIA" }, + { 0x8000001F, "STATUS_BEGINNING_OF_MEDIA" }, + { 0x80000020, "STATUS_MEDIA_CHECK" }, + { 0x80000021, "STATUS_SETMARK_DETECTED" }, + { 0x80000022, "STATUS_NO_DATA_DETECTED" }, + { 0x80000023, "STATUS_REDIRECTOR_HAS_OPEN_HANDLES" }, + { 0x80000024, "STATUS_SERVER_HAS_OPEN_HANDLES" }, + { 0x80000025, "STATUS_ALREADY_DISCONNECTED" }, + { 0x80000026, "STATUS_LONGJUMP" }, + { 0x80000027, "STATUS_CLEANER_CARTRIDGE_INSTALLED" }, + { 0x80000028, "STATUS_PLUGPLAY_QUERY_VETOED" }, + { 0x80000029, "STATUS_UNWIND_CONSOLIDATE" }, + { 0x8000002A, "STATUS_REGISTRY_HIVE_RECOVERED" }, + { 0x8000002B, "STATUS_DLL_MIGHT_BE_INSECURE" }, + { 0x8000002C, "STATUS_DLL_MIGHT_BE_INCOMPATIBLE" }, + { 0x8000002D, "STATUS_STOPPED_ON_SYMLINK" }, + { 0x80000288, "STATUS_DEVICE_REQUIRES_CLEANING" }, + { 0x80000289, "STATUS_DEVICE_DOOR_OPEN" }, + { 0x80000803, "STATUS_DATA_LOST_REPAIR" }, + { 0x80010001, "DBG_EXCEPTION_NOT_HANDLED" }, + { 0x80130001, "STATUS_CLUSTER_NODE_ALREADY_UP" }, + { 0x80130002, "STATUS_CLUSTER_NODE_ALREADY_DOWN" }, + { 0x80130003, "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE" }, + { 0x80130004, "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE" }, + { 0x80130005, "STATUS_CLUSTER_NODE_ALREADY_MEMBER" }, + { 0x80190009, "STATUS_COULD_NOT_RESIZE_LOG" }, + { 0x80190029, "STATUS_NO_TXF_METADATA" }, + { 0x80190031, "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN" }, + { 0x80190041, "STATUS_TXF_METADATA_ALREADY_PRESENT" }, + { 0x80190042, "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET" }, + { 0x801B00EB, "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED" }, + { 0x801C0001, "STATUS_FLT_BUFFER_TOO_SMALL" }, + { 0x80210001, "STATUS_FVE_PARTIAL_METADATA" }, + { 0x80210002, "STATUS_FVE_TRANSIENT_STATE" }, + { 0xC0000001, "STATUS_UNSUCCESSFUL" }, + { 0xC0000002, "STATUS_NOT_IMPLEMENTED" }, + { 0xC0000003, "STATUS_INVALID_INFO_CLASS" }, + { 0xC0000004, "STATUS_INFO_LENGTH_MISMATCH" }, + { 0xC0000005, "STATUS_ACCESS_VIOLATION" }, + { 0xC0000006, "STATUS_IN_PAGE_ERROR" }, + { 0xC0000007, "STATUS_PAGEFILE_QUOTA" }, + { 0xC0000008, "STATUS_INVALID_HANDLE" }, + { 0xC0000009, "STATUS_BAD_INITIAL_STACK" }, + { 0xC000000A, "STATUS_BAD_INITIAL_PC" }, + { 0xC000000B, "STATUS_INVALID_CID" }, + { 0xC000000C, "STATUS_TIMER_NOT_CANCELED" }, + { 0xC000000D, "STATUS_INVALID_PARAMETER" }, + { 0xC000000E, "STATUS_NO_SUCH_DEVICE" }, + { 0xC000000F, "STATUS_NO_SUCH_FILE" }, + { 0xC0000010, "STATUS_INVALID_DEVICE_REQUEST" }, + { 0xC0000011, "STATUS_END_OF_FILE" }, + { 0xC0000012, "STATUS_WRONG_VOLUME" }, + { 0xC0000013, "STATUS_NO_MEDIA_IN_DEVICE" }, + { 0xC0000014, "STATUS_UNRECOGNIZED_MEDIA" }, + { 0xC0000015, "STATUS_NONEXISTENT_SECTOR" }, + { 0xC0000016, "STATUS_MORE_PROCESSING_REQUIRED" }, + { 0xC0000017, "STATUS_NO_MEMORY" }, + { 0xC0000018, "STATUS_CONFLICTING_ADDRESSES" }, + { 0xC0000019, "STATUS_NOT_MAPPED_VIEW" }, + { 0xC000001A, "STATUS_UNABLE_TO_FREE_VM" }, + { 0xC000001B, "STATUS_UNABLE_TO_DELETE_SECTION" }, + { 0xC000001C, "STATUS_INVALID_SYSTEM_SERVICE" }, + { 0xC000001D, "STATUS_ILLEGAL_INSTRUCTION" }, + { 0xC000001E, "STATUS_INVALID_LOCK_SEQUENCE" }, + { 0xC000001F, "STATUS_INVALID_VIEW_SIZE" }, + { 0xC0000020, "STATUS_INVALID_FILE_FOR_SECTION" }, + { 0xC0000021, "STATUS_ALREADY_COMMITTED" }, + { 0xC0000022, "STATUS_ACCESS_DENIED" }, + { 0xC0000023, "STATUS_BUFFER_TOO_SMALL" }, + { 0xC0000024, "STATUS_OBJECT_TYPE_MISMATCH" }, + { 0xC0000025, "STATUS_NONCONTINUABLE_EXCEPTION" }, + { 0xC0000026, "STATUS_INVALID_DISPOSITION" }, + { 0xC0000027, "STATUS_UNWIND" }, + { 0xC0000028, "STATUS_BAD_STACK" }, + { 0xC0000029, "STATUS_INVALID_UNWIND_TARGET" }, + { 0xC000002A, "STATUS_NOT_LOCKED" }, + { 0xC000002B, "STATUS_PARITY_ERROR" }, + { 0xC000002C, "STATUS_UNABLE_TO_DECOMMIT_VM" }, + { 0xC000002D, "STATUS_NOT_COMMITTED" }, + { 0xC000002E, "STATUS_INVALID_PORT_ATTRIBUTES" }, + { 0xC000002F, "STATUS_PORT_MESSAGE_TOO_LONG" }, + { 0xC0000030, "STATUS_INVALID_PARAMETER_MIX" }, + { 0xC0000031, "STATUS_INVALID_QUOTA_LOWER" }, + { 0xC0000032, "STATUS_DISK_CORRUPT_ERROR" }, + { 0xC0000033, "STATUS_OBJECT_NAME_INVALID" }, + { 0xC0000034, "STATUS_OBJECT_NAME_NOT_FOUND" }, + { 0xC0000035, "STATUS_OBJECT_NAME_COLLISION" }, + { 0xC0000037, "STATUS_PORT_DISCONNECTED" }, + { 0xC0000038, "STATUS_DEVICE_ALREADY_ATTACHED" }, + { 0xC0000039, "STATUS_OBJECT_PATH_INVALID" }, + { 0xC000003A, "STATUS_OBJECT_PATH_NOT_FOUND" }, + { 0xC000003B, "STATUS_OBJECT_PATH_SYNTAX_BAD" }, + { 0xC000003C, "STATUS_DATA_OVERRUN" }, + { 0xC000003D, "STATUS_DATA_LATE_ERROR" }, + { 0xC000003E, "STATUS_DATA_ERROR" }, + { 0xC000003F, "STATUS_CRC_ERROR" }, + { 0xC0000040, "STATUS_SECTION_TOO_BIG" }, + { 0xC0000041, "STATUS_PORT_CONNECTION_REFUSED" }, + { 0xC0000042, "STATUS_INVALID_PORT_HANDLE" }, + { 0xC0000043, "STATUS_SHARING_VIOLATION" }, + { 0xC0000044, "STATUS_QUOTA_EXCEEDED" }, + { 0xC0000045, "STATUS_INVALID_PAGE_PROTECTION" }, + { 0xC0000046, "STATUS_MUTANT_NOT_OWNED" }, + { 0xC0000047, "STATUS_SEMAPHORE_LIMIT_EXCEEDED" }, + { 0xC0000048, "STATUS_PORT_ALREADY_SET" }, + { 0xC0000049, "STATUS_SECTION_NOT_IMAGE" }, + { 0xC000004A, "STATUS_SUSPEND_COUNT_EXCEEDED" }, + { 0xC000004B, "STATUS_THREAD_IS_TERMINATING" }, + { 0xC000004C, "STATUS_BAD_WORKING_SET_LIMIT" }, + { 0xC000004D, "STATUS_INCOMPATIBLE_FILE_MAP" }, + { 0xC000004E, "STATUS_SECTION_PROTECTION" }, + { 0xC000004F, "STATUS_EAS_NOT_SUPPORTED" }, + { 0xC0000050, "STATUS_EA_TOO_LARGE" }, + { 0xC0000051, "STATUS_NONEXISTENT_EA_ENTRY" }, + { 0xC0000052, "STATUS_NO_EAS_ON_FILE" }, + { 0xC0000053, "STATUS_EA_CORRUPT_ERROR" }, + { 0xC0000054, "STATUS_FILE_LOCK_CONFLICT" }, + { 0xC0000055, "STATUS_LOCK_NOT_GRANTED" }, + { 0xC0000056, "STATUS_DELETE_PENDING" }, + { 0xC0000057, "STATUS_CTL_FILE_NOT_SUPPORTED" }, + { 0xC0000058, "STATUS_UNKNOWN_REVISION" }, + { 0xC0000059, "STATUS_REVISION_MISMATCH" }, + { 0xC000005A, "STATUS_INVALID_OWNER" }, + { 0xC000005B, "STATUS_INVALID_PRIMARY_GROUP" }, + { 0xC000005C, "STATUS_NO_IMPERSONATION_TOKEN" }, + { 0xC000005D, "STATUS_CANT_DISABLE_MANDATORY" }, + { 0xC000005E, "STATUS_NO_LOGON_SERVERS" }, + { 0xC000005F, "STATUS_NO_SUCH_LOGON_SESSION" }, + { 0xC0000060, "STATUS_NO_SUCH_PRIVILEGE" }, + { 0xC0000061, "STATUS_PRIVILEGE_NOT_HELD" }, + { 0xC0000062, "STATUS_INVALID_ACCOUNT_NAME" }, + { 0xC0000063, "STATUS_USER_EXISTS" }, + { 0xC0000064, "STATUS_NO_SUCH_USER" }, + { 0xC0000065, "STATUS_GROUP_EXISTS" }, + { 0xC0000066, "STATUS_NO_SUCH_GROUP" }, + { 0xC0000067, "STATUS_MEMBER_IN_GROUP" }, + { 0xC0000068, "STATUS_MEMBER_NOT_IN_GROUP" }, + { 0xC0000069, "STATUS_LAST_ADMIN" }, + { 0xC000006A, "STATUS_WRONG_PASSWORD" }, + { 0xC000006B, "STATUS_ILL_FORMED_PASSWORD" }, + { 0xC000006C, "STATUS_PASSWORD_RESTRICTION" }, + { 0xC000006D, "STATUS_LOGON_FAILURE" }, + { 0xC000006E, "STATUS_ACCOUNT_RESTRICTION" }, + { 0xC000006F, "STATUS_INVALID_LOGON_HOURS" }, + { 0xC0000070, "STATUS_INVALID_WORKSTATION" }, + { 0xC0000071, "STATUS_PASSWORD_EXPIRED" }, + { 0xC0000072, "STATUS_ACCOUNT_DISABLED" }, + { 0xC0000073, "STATUS_NONE_MAPPED" }, + { 0xC0000074, "STATUS_TOO_MANY_LUIDS_REQUESTED" }, + { 0xC0000075, "STATUS_LUIDS_EXHAUSTED" }, + { 0xC0000076, "STATUS_INVALID_SUB_AUTHORITY" }, + { 0xC0000077, "STATUS_INVALID_ACL" }, + { 0xC0000078, "STATUS_INVALID_SID" }, + { 0xC0000079, "STATUS_INVALID_SECURITY_DESCR" }, + { 0xC000007A, "STATUS_PROCEDURE_NOT_FOUND" }, + { 0xC000007B, "STATUS_INVALID_IMAGE_FORMAT" }, + { 0xC000007C, "STATUS_NO_TOKEN" }, + { 0xC000007D, "STATUS_BAD_INHERITANCE_ACL" }, + { 0xC000007E, "STATUS_RANGE_NOT_LOCKED" }, + { 0xC000007F, "STATUS_DISK_FULL" }, + { 0xC0000080, "STATUS_SERVER_DISABLED" }, + { 0xC0000081, "STATUS_SERVER_NOT_DISABLED" }, + { 0xC0000082, "STATUS_TOO_MANY_GUIDS_REQUESTED" }, + { 0xC0000083, "STATUS_GUIDS_EXHAUSTED" }, + { 0xC0000084, "STATUS_INVALID_ID_AUTHORITY" }, + { 0xC0000085, "STATUS_AGENTS_EXHAUSTED" }, + { 0xC0000086, "STATUS_INVALID_VOLUME_LABEL" }, + { 0xC0000087, "STATUS_SECTION_NOT_EXTENDED" }, + { 0xC0000088, "STATUS_NOT_MAPPED_DATA" }, + { 0xC0000089, "STATUS_RESOURCE_DATA_NOT_FOUND" }, + { 0xC000008A, "STATUS_RESOURCE_TYPE_NOT_FOUND" }, + { 0xC000008B, "STATUS_RESOURCE_NAME_NOT_FOUND" }, + { 0xC000008C, "STATUS_ARRAY_BOUNDS_EXCEEDED" }, + { 0xC000008D, "STATUS_FLOAT_DENORMAL_OPERAND" }, + { 0xC000008E, "STATUS_FLOAT_DIVIDE_BY_ZERO" }, + { 0xC000008F, "STATUS_FLOAT_INEXACT_RESULT" }, + { 0xC0000090, "STATUS_FLOAT_INVALID_OPERATION" }, + { 0xC0000091, "STATUS_FLOAT_OVERFLOW" }, + { 0xC0000092, "STATUS_FLOAT_STACK_CHECK" }, + { 0xC0000093, "STATUS_FLOAT_UNDERFLOW" }, + { 0xC0000094, "STATUS_INTEGER_DIVIDE_BY_ZERO" }, + { 0xC0000095, "STATUS_INTEGER_OVERFLOW" }, + { 0xC0000096, "STATUS_PRIVILEGED_INSTRUCTION" }, + { 0xC0000097, "STATUS_TOO_MANY_PAGING_FILES" }, + { 0xC0000098, "STATUS_FILE_INVALID" }, + { 0xC0000099, "STATUS_ALLOTTED_SPACE_EXCEEDED" }, + { 0xC000009A, "STATUS_INSUFFICIENT_RESOURCES" }, + { 0xC000009B, "STATUS_DFS_EXIT_PATH_FOUND" }, + { 0xC000009C, "STATUS_DEVICE_DATA_ERROR" }, + { 0xC000009D, "STATUS_DEVICE_NOT_CONNECTED" }, + { 0xC000009F, "STATUS_FREE_VM_NOT_AT_BASE" }, + { 0xC00000A0, "STATUS_MEMORY_NOT_ALLOCATED" }, + { 0xC00000A1, "STATUS_WORKING_SET_QUOTA" }, + { 0xC00000A2, "STATUS_MEDIA_WRITE_PROTECTED" }, + { 0xC00000A3, "STATUS_DEVICE_NOT_READY" }, + { 0xC00000A4, "STATUS_INVALID_GROUP_ATTRIBUTES" }, + { 0xC00000A5, "STATUS_BAD_IMPERSONATION_LEVEL" }, + { 0xC00000A6, "STATUS_CANT_OPEN_ANONYMOUS" }, + { 0xC00000A7, "STATUS_BAD_VALIDATION_CLASS" }, + { 0xC00000A8, "STATUS_BAD_TOKEN_TYPE" }, + { 0xC00000A9, "STATUS_BAD_MASTER_BOOT_RECORD" }, + { 0xC00000AA, "STATUS_INSTRUCTION_MISALIGNMENT" }, + { 0xC00000AB, "STATUS_INSTANCE_NOT_AVAILABLE" }, + { 0xC00000AC, "STATUS_PIPE_NOT_AVAILABLE" }, + { 0xC00000AD, "STATUS_INVALID_PIPE_STATE" }, + { 0xC00000AE, "STATUS_PIPE_BUSY" }, + { 0xC00000AF, "STATUS_ILLEGAL_FUNCTION" }, + { 0xC00000B0, "STATUS_PIPE_DISCONNECTED" }, + { 0xC00000B1, "STATUS_PIPE_CLOSING" }, + { 0xC00000B2, "STATUS_PIPE_CONNECTED" }, + { 0xC00000B3, "STATUS_PIPE_LISTENING" }, + { 0xC00000B4, "STATUS_INVALID_READ_MODE" }, + { 0xC00000B5, "STATUS_IO_TIMEOUT" }, + { 0xC00000B6, "STATUS_FILE_FORCED_CLOSED" }, + { 0xC00000B7, "STATUS_PROFILING_NOT_STARTED" }, + { 0xC00000B8, "STATUS_PROFILING_NOT_STOPPED" }, + { 0xC00000B9, "STATUS_COULD_NOT_INTERPRET" }, + { 0xC00000BA, "STATUS_FILE_IS_A_DIRECTORY" }, + { 0xC00000BB, "STATUS_NOT_SUPPORTED" }, + { 0xC00000BC, "STATUS_REMOTE_NOT_LISTENING" }, + { 0xC00000BD, "STATUS_DUPLICATE_NAME" }, + { 0xC00000BE, "STATUS_BAD_NETWORK_PATH" }, + { 0xC00000BF, "STATUS_NETWORK_BUSY" }, + { 0xC00000C0, "STATUS_DEVICE_DOES_NOT_EXIST" }, + { 0xC00000C1, "STATUS_TOO_MANY_COMMANDS" }, + { 0xC00000C2, "STATUS_ADAPTER_HARDWARE_ERROR" }, + { 0xC00000C3, "STATUS_INVALID_NETWORK_RESPONSE" }, + { 0xC00000C4, "STATUS_UNEXPECTED_NETWORK_ERROR" }, + { 0xC00000C5, "STATUS_BAD_REMOTE_ADAPTER" }, + { 0xC00000C6, "STATUS_PRINT_QUEUE_FULL" }, + { 0xC00000C7, "STATUS_NO_SPOOL_SPACE" }, + { 0xC00000C8, "STATUS_PRINT_CANCELLED" }, + { 0xC00000C9, "STATUS_NETWORK_NAME_DELETED" }, + { 0xC00000CA, "STATUS_NETWORK_ACCESS_DENIED" }, + { 0xC00000CB, "STATUS_BAD_DEVICE_TYPE" }, + { 0xC00000CC, "STATUS_BAD_NETWORK_NAME" }, + { 0xC00000CD, "STATUS_TOO_MANY_NAMES" }, + { 0xC00000CE, "STATUS_TOO_MANY_SESSIONS" }, + { 0xC00000CF, "STATUS_SHARING_PAUSED" }, + { 0xC00000D0, "STATUS_REQUEST_NOT_ACCEPTED" }, + { 0xC00000D1, "STATUS_REDIRECTOR_PAUSED" }, + { 0xC00000D2, "STATUS_NET_WRITE_FAULT" }, + { 0xC00000D3, "STATUS_PROFILING_AT_LIMIT" }, + { 0xC00000D4, "STATUS_NOT_SAME_DEVICE" }, + { 0xC00000D5, "STATUS_FILE_RENAMED" }, + { 0xC00000D6, "STATUS_VIRTUAL_CIRCUIT_CLOSED" }, + { 0xC00000D7, "STATUS_NO_SECURITY_ON_OBJECT" }, + { 0xC00000D8, "STATUS_CANT_WAIT" }, + { 0xC00000D9, "STATUS_PIPE_EMPTY" }, + { 0xC00000DA, "STATUS_CANT_ACCESS_DOMAIN_INFO" }, + { 0xC00000DB, "STATUS_CANT_TERMINATE_SELF" }, + { 0xC00000DC, "STATUS_INVALID_SERVER_STATE" }, + { 0xC00000DD, "STATUS_INVALID_DOMAIN_STATE" }, + { 0xC00000DE, "STATUS_INVALID_DOMAIN_ROLE" }, + { 0xC00000DF, "STATUS_NO_SUCH_DOMAIN" }, + { 0xC00000E0, "STATUS_DOMAIN_EXISTS" }, + { 0xC00000E1, "STATUS_DOMAIN_LIMIT_EXCEEDED" }, + { 0xC00000E2, "STATUS_OPLOCK_NOT_GRANTED" }, + { 0xC00000E3, "STATUS_INVALID_OPLOCK_PROTOCOL" }, + { 0xC00000E4, "STATUS_INTERNAL_DB_CORRUPTION" }, + { 0xC00000E5, "STATUS_INTERNAL_ERROR" }, + { 0xC00000E6, "STATUS_GENERIC_NOT_MAPPED" }, + { 0xC00000E7, "STATUS_BAD_DESCRIPTOR_FORMAT" }, + { 0xC00000E8, "STATUS_INVALID_USER_BUFFER" }, + { 0xC00000E9, "STATUS_UNEXPECTED_IO_ERROR" }, + { 0xC00000EA, "STATUS_UNEXPECTED_MM_CREATE_ERR" }, + { 0xC00000EB, "STATUS_UNEXPECTED_MM_MAP_ERROR" }, + { 0xC00000EC, "STATUS_UNEXPECTED_MM_EXTEND_ERR" }, + { 0xC00000ED, "STATUS_NOT_LOGON_PROCESS" }, + { 0xC00000EE, "STATUS_LOGON_SESSION_EXISTS" }, + { 0xC00000EF, "STATUS_INVALID_PARAMETER_1" }, + { 0xC00000F0, "STATUS_INVALID_PARAMETER_2" }, + { 0xC00000F1, "STATUS_INVALID_PARAMETER_3" }, + { 0xC00000F2, "STATUS_INVALID_PARAMETER_4" }, + { 0xC00000F3, "STATUS_INVALID_PARAMETER_5" }, + { 0xC00000F4, "STATUS_INVALID_PARAMETER_6" }, + { 0xC00000F5, "STATUS_INVALID_PARAMETER_7" }, + { 0xC00000F6, "STATUS_INVALID_PARAMETER_8" }, + { 0xC00000F7, "STATUS_INVALID_PARAMETER_9" }, + { 0xC00000F8, "STATUS_INVALID_PARAMETER_10" }, + { 0xC00000F9, "STATUS_INVALID_PARAMETER_11" }, + { 0xC00000FA, "STATUS_INVALID_PARAMETER_12" }, + { 0xC00000FB, "STATUS_REDIRECTOR_NOT_STARTED" }, + { 0xC00000FC, "STATUS_REDIRECTOR_STARTED" }, + { 0xC00000FD, "STATUS_STACK_OVERFLOW" }, + { 0xC00000FE, "STATUS_NO_SUCH_PACKAGE" }, + { 0xC00000FF, "STATUS_BAD_FUNCTION_TABLE" }, + { 0xC0000100, "STATUS_VARIABLE_NOT_FOUND" }, + { 0xC0000101, "STATUS_DIRECTORY_NOT_EMPTY" }, + { 0xC0000102, "STATUS_FILE_CORRUPT_ERROR" }, + { 0xC0000103, "STATUS_NOT_A_DIRECTORY" }, + { 0xC0000104, "STATUS_BAD_LOGON_SESSION_STATE" }, + { 0xC0000105, "STATUS_LOGON_SESSION_COLLISION" }, + { 0xC0000106, "STATUS_NAME_TOO_LONG" }, + { 0xC0000107, "STATUS_FILES_OPEN" }, + { 0xC0000108, "STATUS_CONNECTION_IN_USE" }, + { 0xC0000109, "STATUS_MESSAGE_NOT_FOUND" }, + { 0xC000010A, "STATUS_PROCESS_IS_TERMINATING" }, + { 0xC000010B, "STATUS_INVALID_LOGON_TYPE" }, + { 0xC000010C, "STATUS_NO_GUID_TRANSLATION" }, + { 0xC000010D, "STATUS_CANNOT_IMPERSONATE" }, + { 0xC000010E, "STATUS_IMAGE_ALREADY_LOADED" }, + { 0xC0000117, "STATUS_NO_LDT" }, + { 0xC0000118, "STATUS_INVALID_LDT_SIZE" }, + { 0xC0000119, "STATUS_INVALID_LDT_OFFSET" }, + { 0xC000011A, "STATUS_INVALID_LDT_DESCRIPTOR" }, + { 0xC000011B, "STATUS_INVALID_IMAGE_NE_FORMAT" }, + { 0xC000011C, "STATUS_RXACT_INVALID_STATE" }, + { 0xC000011D, "STATUS_RXACT_COMMIT_FAILURE" }, + { 0xC000011E, "STATUS_MAPPED_FILE_SIZE_ZERO" }, + { 0xC000011F, "STATUS_TOO_MANY_OPENED_FILES" }, + { 0xC0000120, "STATUS_CANCELLED" }, + { 0xC0000121, "STATUS_CANNOT_DELETE" }, + { 0xC0000122, "STATUS_INVALID_COMPUTER_NAME" }, + { 0xC0000123, "STATUS_FILE_DELETED" }, + { 0xC0000124, "STATUS_SPECIAL_ACCOUNT" }, + { 0xC0000125, "STATUS_SPECIAL_GROUP" }, + { 0xC0000126, "STATUS_SPECIAL_USER" }, + { 0xC0000127, "STATUS_MEMBERS_PRIMARY_GROUP" }, + { 0xC0000128, "STATUS_FILE_CLOSED" }, + { 0xC0000129, "STATUS_TOO_MANY_THREADS" }, + { 0xC000012A, "STATUS_THREAD_NOT_IN_PROCESS" }, + { 0xC000012B, "STATUS_TOKEN_ALREADY_IN_USE" }, + { 0xC000012C, "STATUS_PAGEFILE_QUOTA_EXCEEDED" }, + { 0xC000012D, "STATUS_COMMITMENT_LIMIT" }, + { 0xC000012E, "STATUS_INVALID_IMAGE_LE_FORMAT" }, + { 0xC000012F, "STATUS_INVALID_IMAGE_NOT_MZ" }, + { 0xC0000130, "STATUS_INVALID_IMAGE_PROTECT" }, + { 0xC0000131, "STATUS_INVALID_IMAGE_WIN_16" }, + { 0xC0000132, "STATUS_LOGON_SERVER_CONFLICT" }, + { 0xC0000133, "STATUS_TIME_DIFFERENCE_AT_DC" }, + { 0xC0000134, "STATUS_SYNCHRONIZATION_REQUIRED" }, + { 0xC0000135, "STATUS_DLL_NOT_FOUND" }, + { 0xC0000136, "STATUS_OPEN_FAILED" }, + { 0xC0000137, "STATUS_IO_PRIVILEGE_FAILED" }, + { 0xC0000138, "STATUS_ORDINAL_NOT_FOUND" }, + { 0xC0000139, "STATUS_ENTRYPOINT_NOT_FOUND" }, + { 0xC000013A, "STATUS_CONTROL_C_EXIT" }, + { 0xC000013B, "STATUS_LOCAL_DISCONNECT" }, + { 0xC000013C, "STATUS_REMOTE_DISCONNECT" }, + { 0xC000013D, "STATUS_REMOTE_RESOURCES" }, + { 0xC000013E, "STATUS_LINK_FAILED" }, + { 0xC000013F, "STATUS_LINK_TIMEOUT" }, + { 0xC0000140, "STATUS_INVALID_CONNECTION" }, + { 0xC0000141, "STATUS_INVALID_ADDRESS" }, + { 0xC0000142, "STATUS_DLL_INIT_FAILED" }, + { 0xC0000143, "STATUS_MISSING_SYSTEMFILE" }, + { 0xC0000144, "STATUS_UNHANDLED_EXCEPTION" }, + { 0xC0000145, "STATUS_APP_INIT_FAILURE" }, + { 0xC0000146, "STATUS_PAGEFILE_CREATE_FAILED" }, + { 0xC0000147, "STATUS_NO_PAGEFILE" }, + { 0xC0000148, "STATUS_INVALID_LEVEL" }, + { 0xC0000149, "STATUS_WRONG_PASSWORD_CORE" }, + { 0xC000014A, "STATUS_ILLEGAL_FLOAT_CONTEXT" }, + { 0xC000014B, "STATUS_PIPE_BROKEN" }, + { 0xC000014C, "STATUS_REGISTRY_CORRUPT" }, + { 0xC000014D, "STATUS_REGISTRY_IO_FAILED" }, + { 0xC000014E, "STATUS_NO_EVENT_PAIR" }, + { 0xC000014F, "STATUS_UNRECOGNIZED_VOLUME" }, + { 0xC0000150, "STATUS_SERIAL_NO_DEVICE_INITED" }, + { 0xC0000151, "STATUS_NO_SUCH_ALIAS" }, + { 0xC0000152, "STATUS_MEMBER_NOT_IN_ALIAS" }, + { 0xC0000153, "STATUS_MEMBER_IN_ALIAS" }, + { 0xC0000154, "STATUS_ALIAS_EXISTS" }, + { 0xC0000155, "STATUS_LOGON_NOT_GRANTED" }, + { 0xC0000156, "STATUS_TOO_MANY_SECRETS" }, + { 0xC0000157, "STATUS_SECRET_TOO_LONG" }, + { 0xC0000158, "STATUS_INTERNAL_DB_ERROR" }, + { 0xC0000159, "STATUS_FULLSCREEN_MODE" }, + { 0xC000015A, "STATUS_TOO_MANY_CONTEXT_IDS" }, + { 0xC000015B, "STATUS_LOGON_TYPE_NOT_GRANTED" }, + { 0xC000015C, "STATUS_NOT_REGISTRY_FILE" }, + { 0xC000015D, "STATUS_NT_CROSS_ENCRYPTION_REQUIRED" }, + { 0xC000015E, "STATUS_DOMAIN_CTRLR_CONFIG_ERROR" }, + { 0xC000015F, "STATUS_FT_MISSING_MEMBER" }, + { 0xC0000160, "STATUS_ILL_FORMED_SERVICE_ENTRY" }, + { 0xC0000161, "STATUS_ILLEGAL_CHARACTER" }, + { 0xC0000162, "STATUS_UNMAPPABLE_CHARACTER" }, + { 0xC0000163, "STATUS_UNDEFINED_CHARACTER" }, + { 0xC0000164, "STATUS_FLOPPY_VOLUME" }, + { 0xC0000165, "STATUS_FLOPPY_ID_MARK_NOT_FOUND" }, + { 0xC0000166, "STATUS_FLOPPY_WRONG_CYLINDER" }, + { 0xC0000167, "STATUS_FLOPPY_UNKNOWN_ERROR" }, + { 0xC0000168, "STATUS_FLOPPY_BAD_REGISTERS" }, + { 0xC0000169, "STATUS_DISK_RECALIBRATE_FAILED" }, + { 0xC000016A, "STATUS_DISK_OPERATION_FAILED" }, + { 0xC000016B, "STATUS_DISK_RESET_FAILED" }, + { 0xC000016C, "STATUS_SHARED_IRQ_BUSY" }, + { 0xC000016D, "STATUS_FT_ORPHANING" }, + { 0xC000016E, "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT" }, + { 0xC0000172, "STATUS_PARTITION_FAILURE" }, + { 0xC0000173, "STATUS_INVALID_BLOCK_LENGTH" }, + { 0xC0000174, "STATUS_DEVICE_NOT_PARTITIONED" }, + { 0xC0000175, "STATUS_UNABLE_TO_LOCK_MEDIA" }, + { 0xC0000176, "STATUS_UNABLE_TO_UNLOAD_MEDIA" }, + { 0xC0000177, "STATUS_EOM_OVERFLOW" }, + { 0xC0000178, "STATUS_NO_MEDIA" }, + { 0xC000017A, "STATUS_NO_SUCH_MEMBER" }, + { 0xC000017B, "STATUS_INVALID_MEMBER" }, + { 0xC000017C, "STATUS_KEY_DELETED" }, + { 0xC000017D, "STATUS_NO_LOG_SPACE" }, + { 0xC000017E, "STATUS_TOO_MANY_SIDS" }, + { 0xC000017F, "STATUS_LM_CROSS_ENCRYPTION_REQUIRED" }, + { 0xC0000180, "STATUS_KEY_HAS_CHILDREN" }, + { 0xC0000181, "STATUS_CHILD_MUST_BE_VOLATILE" }, + { 0xC0000182, "STATUS_DEVICE_CONFIGURATION_ERROR" }, + { 0xC0000183, "STATUS_DRIVER_INTERNAL_ERROR" }, + { 0xC0000184, "STATUS_INVALID_DEVICE_STATE" }, + { 0xC0000185, "STATUS_IO_DEVICE_ERROR" }, + { 0xC0000186, "STATUS_DEVICE_PROTOCOL_ERROR" }, + { 0xC0000187, "STATUS_BACKUP_CONTROLLER" }, + { 0xC0000188, "STATUS_LOG_FILE_FULL" }, + { 0xC0000189, "STATUS_TOO_LATE" }, + { 0xC000018A, "STATUS_NO_TRUST_LSA_SECRET" }, + { 0xC000018B, "STATUS_NO_TRUST_SAM_ACCOUNT" }, + { 0xC000018C, "STATUS_TRUSTED_DOMAIN_FAILURE" }, + { 0xC000018D, "STATUS_TRUSTED_RELATIONSHIP_FAILURE" }, + { 0xC000018E, "STATUS_EVENTLOG_FILE_CORRUPT" }, + { 0xC000018F, "STATUS_EVENTLOG_CANT_START" }, + { 0xC0000190, "STATUS_TRUST_FAILURE" }, + { 0xC0000191, "STATUS_MUTANT_LIMIT_EXCEEDED" }, + { 0xC0000192, "STATUS_NETLOGON_NOT_STARTED" }, + { 0xC0000193, "STATUS_ACCOUNT_EXPIRED" }, + { 0xC0000194, "STATUS_POSSIBLE_DEADLOCK" }, + { 0xC0000195, "STATUS_NETWORK_CREDENTIAL_CONFLICT" }, + { 0xC0000196, "STATUS_REMOTE_SESSION_LIMIT" }, + { 0xC0000197, "STATUS_EVENTLOG_FILE_CHANGED" }, + { 0xC0000198, "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT" }, + { 0xC0000199, "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT" }, + { 0xC000019A, "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT" }, + { 0xC000019B, "STATUS_DOMAIN_TRUST_INCONSISTENT" }, + { 0xC000019C, "STATUS_FS_DRIVER_REQUIRED" }, + { 0xC000019D, "STATUS_IMAGE_ALREADY_LOADED_AS_DLL" }, + { 0xC000019E, "STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING" }, + { 0xC000019F, "STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME" }, + { 0xC00001A0, "STATUS_SECURITY_STREAM_IS_INCONSISTENT" }, + { 0xC00001A1, "STATUS_INVALID_LOCK_RANGE" }, + { 0xC00001A2, "STATUS_INVALID_ACE_CONDITION" }, + { 0xC00001A3, "STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT" }, + { 0xC00001A4, "STATUS_NOTIFICATION_GUID_ALREADY_DEFINED" }, + { 0xC0000201, "STATUS_NETWORK_OPEN_RESTRICTION" }, + { 0xC0000202, "STATUS_NO_USER_SESSION_KEY" }, + { 0xC0000203, "STATUS_USER_SESSION_DELETED" }, + { 0xC0000204, "STATUS_RESOURCE_LANG_NOT_FOUND" }, + { 0xC0000205, "STATUS_INSUFF_SERVER_RESOURCES" }, + { 0xC0000206, "STATUS_INVALID_BUFFER_SIZE" }, + { 0xC0000207, "STATUS_INVALID_ADDRESS_COMPONENT" }, + { 0xC0000208, "STATUS_INVALID_ADDRESS_WILDCARD" }, + { 0xC0000209, "STATUS_TOO_MANY_ADDRESSES" }, + { 0xC000020A, "STATUS_ADDRESS_ALREADY_EXISTS" }, + { 0xC000020B, "STATUS_ADDRESS_CLOSED" }, + { 0xC000020C, "STATUS_CONNECTION_DISCONNECTED" }, + { 0xC000020D, "STATUS_CONNECTION_RESET" }, + { 0xC000020E, "STATUS_TOO_MANY_NODES" }, + { 0xC000020F, "STATUS_TRANSACTION_ABORTED" }, + { 0xC0000210, "STATUS_TRANSACTION_TIMED_OUT" }, + { 0xC0000211, "STATUS_TRANSACTION_NO_RELEASE" }, + { 0xC0000212, "STATUS_TRANSACTION_NO_MATCH" }, + { 0xC0000213, "STATUS_TRANSACTION_RESPONDED" }, + { 0xC0000214, "STATUS_TRANSACTION_INVALID_ID" }, + { 0xC0000215, "STATUS_TRANSACTION_INVALID_TYPE" }, + { 0xC0000216, "STATUS_NOT_SERVER_SESSION" }, + { 0xC0000217, "STATUS_NOT_CLIENT_SESSION" }, + { 0xC0000218, "STATUS_CANNOT_LOAD_REGISTRY_FILE" }, + { 0xC0000219, "STATUS_DEBUG_ATTACH_FAILED" }, + { 0xC000021A, "STATUS_SYSTEM_PROCESS_TERMINATED" }, + { 0xC000021B, "STATUS_DATA_NOT_ACCEPTED" }, + { 0xC000021C, "STATUS_NO_BROWSER_SERVERS_FOUND" }, + { 0xC000021D, "STATUS_VDM_HARD_ERROR" }, + { 0xC000021E, "STATUS_DRIVER_CANCEL_TIMEOUT" }, + { 0xC000021F, "STATUS_REPLY_MESSAGE_MISMATCH" }, + { 0xC0000220, "STATUS_MAPPED_ALIGNMENT" }, + { 0xC0000221, "STATUS_IMAGE_CHECKSUM_MISMATCH" }, + { 0xC0000222, "STATUS_LOST_WRITEBEHIND_DATA" }, + { 0xC0000223, "STATUS_CLIENT_SERVER_PARAMETERS_INVALID" }, + { 0xC0000224, "STATUS_PASSWORD_MUST_CHANGE" }, + { 0xC0000225, "STATUS_NOT_FOUND" }, + { 0xC0000226, "STATUS_NOT_TINY_STREAM" }, + { 0xC0000227, "STATUS_RECOVERY_FAILURE" }, + { 0xC0000228, "STATUS_STACK_OVERFLOW_READ" }, + { 0xC0000229, "STATUS_FAIL_CHECK" }, + { 0xC000022A, "STATUS_DUPLICATE_OBJECTID" }, + { 0xC000022B, "STATUS_OBJECTID_EXISTS" }, + { 0xC000022C, "STATUS_CONVERT_TO_LARGE" }, + { 0xC000022D, "STATUS_RETRY" }, + { 0xC000022E, "STATUS_FOUND_OUT_OF_SCOPE" }, + { 0xC000022F, "STATUS_ALLOCATE_BUCKET" }, + { 0xC0000230, "STATUS_PROPSET_NOT_FOUND" }, + { 0xC0000231, "STATUS_MARSHALL_OVERFLOW" }, + { 0xC0000232, "STATUS_INVALID_VARIANT" }, + { 0xC0000233, "STATUS_DOMAIN_CONTROLLER_NOT_FOUND" }, + { 0xC0000234, "STATUS_ACCOUNT_LOCKED_OUT" }, + { 0xC0000235, "STATUS_HANDLE_NOT_CLOSABLE" }, + { 0xC0000236, "STATUS_CONNECTION_REFUSED" }, + { 0xC0000237, "STATUS_GRACEFUL_DISCONNECT" }, + { 0xC0000238, "STATUS_ADDRESS_ALREADY_ASSOCIATED" }, + { 0xC0000239, "STATUS_ADDRESS_NOT_ASSOCIATED" }, + { 0xC000023A, "STATUS_CONNECTION_INVALID" }, + { 0xC000023B, "STATUS_CONNECTION_ACTIVE" }, + { 0xC000023C, "STATUS_NETWORK_UNREACHABLE" }, + { 0xC000023D, "STATUS_HOST_UNREACHABLE" }, + { 0xC000023E, "STATUS_PROTOCOL_UNREACHABLE" }, + { 0xC000023F, "STATUS_PORT_UNREACHABLE" }, + { 0xC0000240, "STATUS_REQUEST_ABORTED" }, + { 0xC0000241, "STATUS_CONNECTION_ABORTED" }, + { 0xC0000242, "STATUS_BAD_COMPRESSION_BUFFER" }, + { 0xC0000243, "STATUS_USER_MAPPED_FILE" }, + { 0xC0000244, "STATUS_AUDIT_FAILED" }, + { 0xC0000245, "STATUS_TIMER_RESOLUTION_NOT_SET" }, + { 0xC0000246, "STATUS_CONNECTION_COUNT_LIMIT" }, + { 0xC0000247, "STATUS_LOGIN_TIME_RESTRICTION" }, + { 0xC0000248, "STATUS_LOGIN_WKSTA_RESTRICTION" }, + { 0xC0000249, "STATUS_IMAGE_MP_UP_MISMATCH" }, + { 0xC0000250, "STATUS_INSUFFICIENT_LOGON_INFO" }, + { 0xC0000251, "STATUS_BAD_DLL_ENTRYPOINT" }, + { 0xC0000252, "STATUS_BAD_SERVICE_ENTRYPOINT" }, + { 0xC0000253, "STATUS_LPC_REPLY_LOST" }, + { 0xC0000254, "STATUS_IP_ADDRESS_CONFLICT1" }, + { 0xC0000255, "STATUS_IP_ADDRESS_CONFLICT2" }, + { 0xC0000256, "STATUS_REGISTRY_QUOTA_LIMIT" }, + { 0xC0000257, "STATUS_PATH_NOT_COVERED" }, + { 0xC0000258, "STATUS_NO_CALLBACK_ACTIVE" }, + { 0xC0000259, "STATUS_LICENSE_QUOTA_EXCEEDED" }, + { 0xC000025A, "STATUS_PWD_TOO_SHORT" }, + { 0xC000025B, "STATUS_PWD_TOO_RECENT" }, + { 0xC000025C, "STATUS_PWD_HISTORY_CONFLICT" }, + { 0xC000025E, "STATUS_PLUGPLAY_NO_DEVICE" }, + { 0xC000025F, "STATUS_UNSUPPORTED_COMPRESSION" }, + { 0xC0000260, "STATUS_INVALID_HW_PROFILE" }, + { 0xC0000261, "STATUS_INVALID_PLUGPLAY_DEVICE_PATH" }, + { 0xC0000262, "STATUS_DRIVER_ORDINAL_NOT_FOUND" }, + { 0xC0000263, "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND" }, + { 0xC0000264, "STATUS_RESOURCE_NOT_OWNED" }, + { 0xC0000265, "STATUS_TOO_MANY_LINKS" }, + { 0xC0000266, "STATUS_QUOTA_LIST_INCONSISTENT" }, + { 0xC0000267, "STATUS_FILE_IS_OFFLINE" }, + { 0xC0000268, "STATUS_EVALUATION_EXPIRATION" }, + { 0xC0000269, "STATUS_ILLEGAL_DLL_RELOCATION" }, + { 0xC000026A, "STATUS_LICENSE_VIOLATION" }, + { 0xC000026B, "STATUS_DLL_INIT_FAILED_LOGOFF" }, + { 0xC000026C, "STATUS_DRIVER_UNABLE_TO_LOAD" }, + { 0xC000026D, "STATUS_DFS_UNAVAILABLE" }, + { 0xC000026E, "STATUS_VOLUME_DISMOUNTED" }, + { 0xC000026F, "STATUS_WX86_INTERNAL_ERROR" }, + { 0xC0000270, "STATUS_WX86_FLOAT_STACK_CHECK" }, + { 0xC0000271, "STATUS_VALIDATE_CONTINUE" }, + { 0xC0000272, "STATUS_NO_MATCH" }, + { 0xC0000273, "STATUS_NO_MORE_MATCHES" }, + { 0xC0000275, "STATUS_NOT_A_REPARSE_POINT" }, + { 0xC0000276, "STATUS_IO_REPARSE_TAG_INVALID" }, + { 0xC0000277, "STATUS_IO_REPARSE_TAG_MISMATCH" }, + { 0xC0000278, "STATUS_IO_REPARSE_DATA_INVALID" }, + { 0xC0000279, "STATUS_IO_REPARSE_TAG_NOT_HANDLED" }, + { 0xC0000280, "STATUS_REPARSE_POINT_NOT_RESOLVED" }, + { 0xC0000281, "STATUS_DIRECTORY_IS_A_REPARSE_POINT" }, + { 0xC0000282, "STATUS_RANGE_LIST_CONFLICT" }, + { 0xC0000283, "STATUS_SOURCE_ELEMENT_EMPTY" }, + { 0xC0000284, "STATUS_DESTINATION_ELEMENT_FULL" }, + { 0xC0000285, "STATUS_ILLEGAL_ELEMENT_ADDRESS" }, + { 0xC0000286, "STATUS_MAGAZINE_NOT_PRESENT" }, + { 0xC0000287, "STATUS_REINITIALIZATION_NEEDED" }, + { 0xC000028A, "STATUS_ENCRYPTION_FAILED" }, + { 0xC000028B, "STATUS_DECRYPTION_FAILED" }, + { 0xC000028C, "STATUS_RANGE_NOT_FOUND" }, + { 0xC000028D, "STATUS_NO_RECOVERY_POLICY" }, + { 0xC000028E, "STATUS_NO_EFS" }, + { 0xC000028F, "STATUS_WRONG_EFS" }, + { 0xC0000290, "STATUS_NO_USER_KEYS" }, + { 0xC0000291, "STATUS_FILE_NOT_ENCRYPTED" }, + { 0xC0000292, "STATUS_NOT_EXPORT_FORMAT" }, + { 0xC0000293, "STATUS_FILE_ENCRYPTED" }, + { 0xC0000295, "STATUS_WMI_GUID_NOT_FOUND" }, + { 0xC0000296, "STATUS_WMI_INSTANCE_NOT_FOUND" }, + { 0xC0000297, "STATUS_WMI_ITEMID_NOT_FOUND" }, + { 0xC0000298, "STATUS_WMI_TRY_AGAIN" }, + { 0xC0000299, "STATUS_SHARED_POLICY" }, + { 0xC000029A, "STATUS_POLICY_OBJECT_NOT_FOUND" }, + { 0xC000029B, "STATUS_POLICY_ONLY_IN_DS" }, + { 0xC000029C, "STATUS_VOLUME_NOT_UPGRADED" }, + { 0xC000029D, "STATUS_REMOTE_STORAGE_NOT_ACTIVE" }, + { 0xC000029E, "STATUS_REMOTE_STORAGE_MEDIA_ERROR" }, + { 0xC000029F, "STATUS_NO_TRACKING_SERVICE" }, + { 0xC00002A0, "STATUS_SERVER_SID_MISMATCH" }, + { 0xC00002A1, "STATUS_DS_NO_ATTRIBUTE_OR_VALUE" }, + { 0xC00002A2, "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX" }, + { 0xC00002A3, "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED" }, + { 0xC00002A4, "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS" }, + { 0xC00002A5, "STATUS_DS_BUSY" }, + { 0xC00002A6, "STATUS_DS_UNAVAILABLE" }, + { 0xC00002A7, "STATUS_DS_NO_RIDS_ALLOCATED" }, + { 0xC00002A8, "STATUS_DS_NO_MORE_RIDS" }, + { 0xC00002A9, "STATUS_DS_INCORRECT_ROLE_OWNER" }, + { 0xC00002AA, "STATUS_DS_RIDMGR_INIT_ERROR" }, + { 0xC00002AB, "STATUS_DS_OBJ_CLASS_VIOLATION" }, + { 0xC00002AC, "STATUS_DS_CANT_ON_NON_LEAF" }, + { 0xC00002AD, "STATUS_DS_CANT_ON_RDN" }, + { 0xC00002AE, "STATUS_DS_CANT_MOD_OBJ_CLASS" }, + { 0xC00002AF, "STATUS_DS_CROSS_DOM_MOVE_FAILED" }, + { 0xC00002B0, "STATUS_DS_GC_NOT_AVAILABLE" }, + { 0xC00002B1, "STATUS_DIRECTORY_SERVICE_REQUIRED" }, + { 0xC00002B2, "STATUS_REPARSE_ATTRIBUTE_CONFLICT" }, + { 0xC00002B3, "STATUS_CANT_ENABLE_DENY_ONLY" }, + { 0xC00002B4, "STATUS_FLOAT_MULTIPLE_FAULTS" }, + { 0xC00002B5, "STATUS_FLOAT_MULTIPLE_TRAPS" }, + { 0xC00002B6, "STATUS_DEVICE_REMOVED" }, + { 0xC00002B7, "STATUS_JOURNAL_DELETE_IN_PROGRESS" }, + { 0xC00002B8, "STATUS_JOURNAL_NOT_ACTIVE" }, + { 0xC00002B9, "STATUS_NOINTERFACE" }, + { 0xC00002C1, "STATUS_DS_ADMIN_LIMIT_EXCEEDED" }, + { 0xC00002C2, "STATUS_DRIVER_FAILED_SLEEP" }, + { 0xC00002C3, "STATUS_MUTUAL_AUTHENTICATION_FAILED" }, + { 0xC00002C4, "STATUS_CORRUPT_SYSTEM_FILE" }, + { 0xC00002C5, "STATUS_DATATYPE_MISALIGNMENT_ERROR" }, + { 0xC00002C6, "STATUS_WMI_READ_ONLY" }, + { 0xC00002C7, "STATUS_WMI_SET_FAILURE" }, + { 0xC00002C8, "STATUS_COMMITMENT_MINIMUM" }, + { 0xC00002C9, "STATUS_REG_NAT_CONSUMPTION" }, + { 0xC00002CA, "STATUS_TRANSPORT_FULL" }, + { 0xC00002CB, "STATUS_DS_SAM_INIT_FAILURE" }, + { 0xC00002CC, "STATUS_ONLY_IF_CONNECTED" }, + { 0xC00002CD, "STATUS_DS_SENSITIVE_GROUP_VIOLATION" }, + { 0xC00002CE, "STATUS_PNP_RESTART_ENUMERATION" }, + { 0xC00002CF, "STATUS_JOURNAL_ENTRY_DELETED" }, + { 0xC00002D0, "STATUS_DS_CANT_MOD_PRIMARYGROUPID" }, + { 0xC00002D1, "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE" }, + { 0xC00002D2, "STATUS_PNP_REBOOT_REQUIRED" }, + { 0xC00002D3, "STATUS_POWER_STATE_INVALID" }, + { 0xC00002D4, "STATUS_DS_INVALID_GROUP_TYPE" }, + { 0xC00002D5, "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN" }, + { 0xC00002D6, "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN" }, + { 0xC00002D7, "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER" }, + { 0xC00002D8, "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER" }, + { 0xC00002D9, "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER" }, + { 0xC00002DA, "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER" }, + { 0xC00002DB, "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER" }, + { 0xC00002DC, "STATUS_DS_HAVE_PRIMARY_MEMBERS" }, + { 0xC00002DD, "STATUS_WMI_NOT_SUPPORTED" }, + { 0xC00002DE, "STATUS_INSUFFICIENT_POWER" }, + { 0xC00002DF, "STATUS_SAM_NEED_BOOTKEY_PASSWORD" }, + { 0xC00002E0, "STATUS_SAM_NEED_BOOTKEY_FLOPPY" }, + { 0xC00002E1, "STATUS_DS_CANT_START" }, + { 0xC00002E2, "STATUS_DS_INIT_FAILURE" }, + { 0xC00002E3, "STATUS_SAM_INIT_FAILURE" }, + { 0xC00002E4, "STATUS_DS_GC_REQUIRED" }, + { 0xC00002E5, "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY" }, + { 0xC00002E6, "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS" }, + { 0xC00002E7, "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" }, + { 0xC00002E9, "STATUS_CURRENT_DOMAIN_NOT_ALLOWED" }, + { 0xC00002EA, "STATUS_CANNOT_MAKE" }, + { 0xC00002EB, "STATUS_SYSTEM_SHUTDOWN" }, + { 0xC00002EC, "STATUS_DS_INIT_FAILURE_CONSOLE" }, + { 0xC00002ED, "STATUS_DS_SAM_INIT_FAILURE_CONSOLE" }, + { 0xC00002EE, "STATUS_UNFINISHED_CONTEXT_DELETED" }, + { 0xC00002EF, "STATUS_NO_TGT_REPLY" }, + { 0xC00002F0, "STATUS_OBJECTID_NOT_FOUND" }, + { 0xC00002F1, "STATUS_NO_IP_ADDRESSES" }, + { 0xC00002F2, "STATUS_WRONG_CREDENTIAL_HANDLE" }, + { 0xC00002F3, "STATUS_CRYPTO_SYSTEM_INVALID" }, + { 0xC00002F4, "STATUS_MAX_REFERRALS_EXCEEDED" }, + { 0xC00002F5, "STATUS_MUST_BE_KDC" }, + { 0xC00002F6, "STATUS_STRONG_CRYPTO_NOT_SUPPORTED" }, + { 0xC00002F7, "STATUS_TOO_MANY_PRINCIPALS" }, + { 0xC00002F8, "STATUS_NO_PA_DATA" }, + { 0xC00002F9, "STATUS_PKINIT_NAME_MISMATCH" }, + { 0xC00002FA, "STATUS_SMARTCARD_LOGON_REQUIRED" }, + { 0xC00002FB, "STATUS_KDC_INVALID_REQUEST" }, + { 0xC00002FC, "STATUS_KDC_UNABLE_TO_REFER" }, + { 0xC00002FD, "STATUS_KDC_UNKNOWN_ETYPE" }, + { 0xC00002FE, "STATUS_SHUTDOWN_IN_PROGRESS" }, + { 0xC00002FF, "STATUS_SERVER_SHUTDOWN_IN_PROGRESS" }, + { 0xC0000300, "STATUS_NOT_SUPPORTED_ON_SBS" }, + { 0xC0000301, "STATUS_WMI_GUID_DISCONNECTED" }, + { 0xC0000302, "STATUS_WMI_ALREADY_DISABLED" }, + { 0xC0000303, "STATUS_WMI_ALREADY_ENABLED" }, + { 0xC0000304, "STATUS_MFT_TOO_FRAGMENTED" }, + { 0xC0000305, "STATUS_COPY_PROTECTION_FAILURE" }, + { 0xC0000306, "STATUS_CSS_AUTHENTICATION_FAILURE" }, + { 0xC0000307, "STATUS_CSS_KEY_NOT_PRESENT" }, + { 0xC0000308, "STATUS_CSS_KEY_NOT_ESTABLISHED" }, + { 0xC0000309, "STATUS_CSS_SCRAMBLED_SECTOR" }, + { 0xC000030A, "STATUS_CSS_REGION_MISMATCH" }, + { 0xC000030B, "STATUS_CSS_RESETS_EXHAUSTED" }, + { 0xC0000320, "STATUS_PKINIT_FAILURE" }, + { 0xC0000321, "STATUS_SMARTCARD_SUBSYSTEM_FAILURE" }, + { 0xC0000322, "STATUS_NO_KERB_KEY" }, + { 0xC0000350, "STATUS_HOST_DOWN" }, + { 0xC0000351, "STATUS_UNSUPPORTED_PREAUTH" }, + { 0xC0000352, "STATUS_EFS_ALG_BLOB_TOO_BIG" }, + { 0xC0000353, "STATUS_PORT_NOT_SET" }, + { 0xC0000354, "STATUS_DEBUGGER_INACTIVE" }, + { 0xC0000355, "STATUS_DS_VERSION_CHECK_FAILURE" }, + { 0xC0000356, "STATUS_AUDITING_DISABLED" }, + { 0xC0000357, "STATUS_PRENT4_MACHINE_ACCOUNT" }, + { 0xC0000358, "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER" }, + { 0xC0000359, "STATUS_INVALID_IMAGE_WIN_32" }, + { 0xC000035A, "STATUS_INVALID_IMAGE_WIN_64" }, + { 0xC000035B, "STATUS_BAD_BINDINGS" }, + { 0xC000035C, "STATUS_NETWORK_SESSION_EXPIRED" }, + { 0xC000035D, "STATUS_APPHELP_BLOCK" }, + { 0xC000035E, "STATUS_ALL_SIDS_FILTERED" }, + { 0xC000035F, "STATUS_NOT_SAFE_MODE_DRIVER" }, + { 0xC0000361, "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT" }, + { 0xC0000362, "STATUS_ACCESS_DISABLED_BY_POLICY_PATH" }, + { 0xC0000363, "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER" }, + { 0xC0000364, "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER" }, + { 0xC0000365, "STATUS_FAILED_DRIVER_ENTRY" }, + { 0xC0000366, "STATUS_DEVICE_ENUMERATION_ERROR" }, + { 0xC0000368, "STATUS_MOUNT_POINT_NOT_RESOLVED" }, + { 0xC0000369, "STATUS_INVALID_DEVICE_OBJECT_PARAMETER" }, + { 0xC000036A, "STATUS_MCA_OCCURED" }, + { 0xC000036B, "STATUS_DRIVER_BLOCKED_CRITICAL" }, + { 0xC000036C, "STATUS_DRIVER_BLOCKED" }, + { 0xC000036D, "STATUS_DRIVER_DATABASE_ERROR" }, + { 0xC000036E, "STATUS_SYSTEM_HIVE_TOO_LARGE" }, + { 0xC000036F, "STATUS_INVALID_IMPORT_OF_NON_DLL" }, + { 0xC0000371, "STATUS_NO_SECRETS" }, + { 0xC0000372, "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY" }, + { 0xC0000373, "STATUS_FAILED_STACK_SWITCH" }, + { 0xC0000374, "STATUS_HEAP_CORRUPTION" }, + { 0xC0000380, "STATUS_SMARTCARD_WRONG_PIN" }, + { 0xC0000381, "STATUS_SMARTCARD_CARD_BLOCKED" }, + { 0xC0000382, "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED" }, + { 0xC0000383, "STATUS_SMARTCARD_NO_CARD" }, + { 0xC0000384, "STATUS_SMARTCARD_NO_KEY_CONTAINER" }, + { 0xC0000385, "STATUS_SMARTCARD_NO_CERTIFICATE" }, + { 0xC0000386, "STATUS_SMARTCARD_NO_KEYSET" }, + { 0xC0000387, "STATUS_SMARTCARD_IO_ERROR" }, + { 0xC0000388, "STATUS_DOWNGRADE_DETECTED" }, + { 0xC0000389, "STATUS_SMARTCARD_CERT_REVOKED" }, + { 0xC000038A, "STATUS_ISSUING_CA_UNTRUSTED" }, + { 0xC000038B, "STATUS_REVOCATION_OFFLINE_C" }, + { 0xC000038C, "STATUS_PKINIT_CLIENT_FAILURE" }, + { 0xC000038D, "STATUS_SMARTCARD_CERT_EXPIRED" }, + { 0xC000038E, "STATUS_DRIVER_FAILED_PRIOR_UNLOAD" }, + { 0xC000038F, "STATUS_SMARTCARD_SILENT_CONTEXT" }, + { 0xC0000401, "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED" }, + { 0xC0000402, "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED" }, + { 0xC0000403, "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED" }, + { 0xC0000404, "STATUS_DS_NAME_NOT_UNIQUE" }, + { 0xC0000405, "STATUS_DS_DUPLICATE_ID_FOUND" }, + { 0xC0000406, "STATUS_DS_GROUP_CONVERSION_ERROR" }, + { 0xC0000407, "STATUS_VOLSNAP_PREPARE_HIBERNATE" }, + { 0xC0000408, "STATUS_USER2USER_REQUIRED" }, + { 0xC0000409, "STATUS_STACK_BUFFER_OVERRUN" }, + { 0xC000040A, "STATUS_NO_S4U_PROT_SUPPORT" }, + { 0xC000040B, "STATUS_CROSSREALM_DELEGATION_FAILURE" }, + { 0xC000040C, "STATUS_REVOCATION_OFFLINE_KDC" }, + { 0xC000040D, "STATUS_ISSUING_CA_UNTRUSTED_KDC" }, + { 0xC000040E, "STATUS_KDC_CERT_EXPIRED" }, + { 0xC000040F, "STATUS_KDC_CERT_REVOKED" }, + { 0xC0000410, "STATUS_PARAMETER_QUOTA_EXCEEDED" }, + { 0xC0000411, "STATUS_HIBERNATION_FAILURE" }, + { 0xC0000412, "STATUS_DELAY_LOAD_FAILED" }, + { 0xC0000413, "STATUS_AUTHENTICATION_FIREWALL_FAILED" }, + { 0xC0000414, "STATUS_VDM_DISALLOWED" }, + { 0xC0000415, "STATUS_HUNG_DISPLAY_DRIVER_THREAD" }, + { 0xC0000416, "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE" }, + { 0xC0000417, "STATUS_INVALID_CRUNTIME_PARAMETER" }, + { 0xC0000418, "STATUS_NTLM_BLOCKED" }, + { 0xC0000419, "STATUS_DS_SRC_SID_EXISTS_IN_FOREST" }, + { 0xC000041A, "STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST" }, + { 0xC000041B, "STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST" }, + { 0xC000041C, "STATUS_INVALID_USER_PRINCIPAL_NAME" }, + { 0xC0000420, "STATUS_ASSERTION_FAILURE" }, + { 0xC0000421, "STATUS_VERIFIER_STOP" }, + { 0xC0000423, "STATUS_CALLBACK_POP_STACK" }, + { 0xC0000424, "STATUS_INCOMPATIBLE_DRIVER_BLOCKED" }, + { 0xC0000425, "STATUS_HIVE_UNLOADED" }, + { 0xC0000426, "STATUS_COMPRESSION_DISABLED" }, + { 0xC0000427, "STATUS_FILE_SYSTEM_LIMITATION" }, + { 0xC0000428, "STATUS_INVALID_IMAGE_HASH" }, + { 0xC0000429, "STATUS_NOT_CAPABLE" }, + { 0xC000042A, "STATUS_REQUEST_OUT_OF_SEQUENCE" }, + { 0xC000042B, "STATUS_IMPLEMENTATION_LIMIT" }, + { 0xC000042C, "STATUS_ELEVATION_REQUIRED" }, + { 0xC000042D, "STATUS_NO_SECURITY_CONTEXT" }, + { 0xC000042E, "STATUS_PKU2U_CERT_FAILURE" }, + { 0xC0000432, "STATUS_BEYOND_VDL" }, + { 0xC0000433, "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS" }, + { 0xC0000434, "STATUS_PTE_CHANGED" }, + { 0xC0000435, "STATUS_PURGE_FAILED" }, + { 0xC0000440, "STATUS_CRED_REQUIRES_CONFIRMATION" }, + { 0xC0000441, "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE" }, + { 0xC0000442, "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER" }, + { 0xC0000443, "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE" }, + { 0xC0000444, "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE" }, + { 0xC0000445, "STATUS_CS_ENCRYPTION_FILE_NOT_CSE" }, + { 0xC0000446, "STATUS_INVALID_LABEL" }, + { 0xC0000450, "STATUS_DRIVER_PROCESS_TERMINATED" }, + { 0xC0000451, "STATUS_AMBIGUOUS_SYSTEM_DEVICE" }, + { 0xC0000452, "STATUS_SYSTEM_DEVICE_NOT_FOUND" }, + { 0xC0000453, "STATUS_RESTART_BOOT_APPLICATION" }, + { 0xC0000454, "STATUS_INSUFFICIENT_NVRAM_RESOURCES" }, + { 0xC0000460, "STATUS_NO_RANGES_PROCESSED" }, + { 0xC0000463, "STATUS_DEVICE_FEATURE_NOT_SUPPORTED" }, + { 0xC0000464, "STATUS_DEVICE_UNREACHABLE" }, + { 0xC0000465, "STATUS_INVALID_TOKEN" }, + { 0xC0000466, "STATUS_SERVER_UNAVAILABLE" }, + { 0xC0000467, "STATUS_FILE_NOT_AVAILABLE" }, + { 0xC0000480, "STATUS_SHARE_UNAVAILABLE" }, + { 0xC0000500, "STATUS_INVALID_TASK_NAME" }, + { 0xC0000501, "STATUS_INVALID_TASK_INDEX" }, + { 0xC0000502, "STATUS_THREAD_ALREADY_IN_TASK" }, + { 0xC0000503, "STATUS_CALLBACK_BYPASS" }, + { 0xC0000602, "STATUS_FAIL_FAST_EXCEPTION" }, + { 0xC0000603, "STATUS_IMAGE_CERT_REVOKED" }, + { 0xC0000700, "STATUS_PORT_CLOSED" }, + { 0xC0000701, "STATUS_MESSAGE_LOST" }, + { 0xC0000702, "STATUS_INVALID_MESSAGE" }, + { 0xC0000703, "STATUS_REQUEST_CANCELED" }, + { 0xC0000704, "STATUS_RECURSIVE_DISPATCH" }, + { 0xC0000705, "STATUS_LPC_RECEIVE_BUFFER_EXPECTED" }, + { 0xC0000706, "STATUS_LPC_INVALID_CONNECTION_USAGE" }, + { 0xC0000707, "STATUS_LPC_REQUESTS_NOT_ALLOWED" }, + { 0xC0000708, "STATUS_RESOURCE_IN_USE" }, + { 0xC0000709, "STATUS_HARDWARE_MEMORY_ERROR" }, + { 0xC000070A, "STATUS_THREADPOOL_HANDLE_EXCEPTION" }, + { 0xC000070B, "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED" }, + { 0xC000070C, "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED" }, + { 0xC000070D, "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED" }, + { 0xC000070E, "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED" }, + { 0xC000070F, "STATUS_THREADPOOL_RELEASED_DURING_OPERATION" }, + { 0xC0000710, "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING" }, + { 0xC0000711, "STATUS_APC_RETURNED_WHILE_IMPERSONATING" }, + { 0xC0000712, "STATUS_PROCESS_IS_PROTECTED" }, + { 0xC0000713, "STATUS_MCA_EXCEPTION" }, + { 0xC0000714, "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE" }, + { 0xC0000715, "STATUS_SYMLINK_CLASS_DISABLED" }, + { 0xC0000716, "STATUS_INVALID_IDN_NORMALIZATION" }, + { 0xC0000717, "STATUS_NO_UNICODE_TRANSLATION" }, + { 0xC0000718, "STATUS_ALREADY_REGISTERED" }, + { 0xC0000719, "STATUS_CONTEXT_MISMATCH" }, + { 0xC000071A, "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST" }, + { 0xC000071B, "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY" }, + { 0xC000071C, "STATUS_INVALID_THREAD" }, + { 0xC000071D, "STATUS_CALLBACK_RETURNED_TRANSACTION" }, + { 0xC000071E, "STATUS_CALLBACK_RETURNED_LDR_LOCK" }, + { 0xC000071F, "STATUS_CALLBACK_RETURNED_LANG" }, + { 0xC0000720, "STATUS_CALLBACK_RETURNED_PRI_BACK" }, + { 0xC0000721, "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY" }, + { 0xC0000800, "STATUS_DISK_REPAIR_DISABLED" }, + { 0xC0000801, "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS" }, + { 0xC0000802, "STATUS_DISK_QUOTA_EXCEEDED" }, + { 0xC0000804, "STATUS_CONTENT_BLOCKED" }, + { 0xC0000805, "STATUS_BAD_CLUSTERS" }, + { 0xC0000806, "STATUS_VOLUME_DIRTY" }, + { 0xC0000901, "STATUS_FILE_CHECKED_OUT" }, + { 0xC0000902, "STATUS_CHECKOUT_REQUIRED" }, + { 0xC0000903, "STATUS_BAD_FILE_TYPE" }, + { 0xC0000904, "STATUS_FILE_TOO_LARGE" }, + { 0xC0000905, "STATUS_FORMS_AUTH_REQUIRED" }, + { 0xC0000906, "STATUS_VIRUS_INFECTED" }, + { 0xC0000907, "STATUS_VIRUS_DELETED" }, + { 0xC0000908, "STATUS_BAD_MCFG_TABLE" }, + { 0xC0000909, "STATUS_CANNOT_BREAK_OPLOCK" }, + { 0xC0009898, "STATUS_WOW_ASSERTION" }, + { 0xC000A000, "STATUS_INVALID_SIGNATURE" }, + { 0xC000A001, "STATUS_HMAC_NOT_SUPPORTED" }, + { 0xC000A010, "STATUS_IPSEC_QUEUE_OVERFLOW" }, + { 0xC000A011, "STATUS_ND_QUEUE_OVERFLOW" }, + { 0xC000A012, "STATUS_HOPLIMIT_EXCEEDED" }, + { 0xC000A013, "STATUS_PROTOCOL_NOT_SUPPORTED" }, + { 0xC000A080, "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED" }, + { 0xC000A081, "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR" }, + { 0xC000A082, "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR" }, + { 0xC000A083, "STATUS_XML_PARSE_ERROR" }, + { 0xC000A084, "STATUS_XMLDSIG_ERROR" }, + { 0xC000A085, "STATUS_WRONG_COMPARTMENT" }, + { 0xC000A086, "STATUS_AUTHIP_FAILURE" }, + { 0xC000A087, "STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS" }, + { 0xC000A088, "STATUS_DS_OID_NOT_FOUND" }, + { 0xC000A100, "STATUS_HASH_NOT_SUPPORTED" }, + { 0xC000A101, "STATUS_HASH_NOT_PRESENT" }, + { 0xC000A2A1, "STATUS_OFFLOAD_READ_FLT_NOT_SUPPORTED" }, + { 0xC000A2A2, "STATUS_OFFLOAD_WRITE_FLT_NOT_SUPPORTED" }, + { 0xC000A2A3, "STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED" }, + { 0xC0010001, "DBG_NO_STATE_CHANGE" }, + { 0xC0010002, "DBG_APP_NOT_IDLE" }, + { 0xC0020001, "RPC_NT_INVALID_STRING_BINDING" }, + { 0xC0020002, "RPC_NT_WRONG_KIND_OF_BINDING" }, + { 0xC0020003, "RPC_NT_INVALID_BINDING" }, + { 0xC0020004, "RPC_NT_PROTSEQ_NOT_SUPPORTED" }, + { 0xC0020005, "RPC_NT_INVALID_RPC_PROTSEQ" }, + { 0xC0020006, "RPC_NT_INVALID_STRING_UUID" }, + { 0xC0020007, "RPC_NT_INVALID_ENDPOINT_FORMAT" }, + { 0xC0020008, "RPC_NT_INVALID_NET_ADDR" }, + { 0xC0020009, "RPC_NT_NO_ENDPOINT_FOUND" }, + { 0xC002000A, "RPC_NT_INVALID_TIMEOUT" }, + { 0xC002000B, "RPC_NT_OBJECT_NOT_FOUND" }, + { 0xC002000C, "RPC_NT_ALREADY_REGISTERED" }, + { 0xC002000D, "RPC_NT_TYPE_ALREADY_REGISTERED" }, + { 0xC002000E, "RPC_NT_ALREADY_LISTENING" }, + { 0xC002000F, "RPC_NT_NO_PROTSEQS_REGISTERED" }, + { 0xC0020010, "RPC_NT_NOT_LISTENING" }, + { 0xC0020011, "RPC_NT_UNKNOWN_MGR_TYPE" }, + { 0xC0020012, "RPC_NT_UNKNOWN_IF" }, + { 0xC0020013, "RPC_NT_NO_BINDINGS" }, + { 0xC0020014, "RPC_NT_NO_PROTSEQS" }, + { 0xC0020015, "RPC_NT_CANT_CREATE_ENDPOINT" }, + { 0xC0020016, "RPC_NT_OUT_OF_RESOURCES" }, + { 0xC0020017, "RPC_NT_SERVER_UNAVAILABLE" }, + { 0xC0020018, "RPC_NT_SERVER_TOO_BUSY" }, + { 0xC0020019, "RPC_NT_INVALID_NETWORK_OPTIONS" }, + { 0xC002001A, "RPC_NT_NO_CALL_ACTIVE" }, + { 0xC002001B, "RPC_NT_CALL_FAILED" }, + { 0xC002001C, "RPC_NT_CALL_FAILED_DNE" }, + { 0xC002001D, "RPC_NT_PROTOCOL_ERROR" }, + { 0xC002001F, "RPC_NT_UNSUPPORTED_TRANS_SYN" }, + { 0xC0020021, "RPC_NT_UNSUPPORTED_TYPE" }, + { 0xC0020022, "RPC_NT_INVALID_TAG" }, + { 0xC0020023, "RPC_NT_INVALID_BOUND" }, + { 0xC0020024, "RPC_NT_NO_ENTRY_NAME" }, + { 0xC0020025, "RPC_NT_INVALID_NAME_SYNTAX" }, + { 0xC0020026, "RPC_NT_UNSUPPORTED_NAME_SYNTAX" }, + { 0xC0020028, "RPC_NT_UUID_NO_ADDRESS" }, + { 0xC0020029, "RPC_NT_DUPLICATE_ENDPOINT" }, + { 0xC002002A, "RPC_NT_UNKNOWN_AUTHN_TYPE" }, + { 0xC002002B, "RPC_NT_MAX_CALLS_TOO_SMALL" }, + { 0xC002002C, "RPC_NT_STRING_TOO_LONG" }, + { 0xC002002D, "RPC_NT_PROTSEQ_NOT_FOUND" }, + { 0xC002002E, "RPC_NT_PROCNUM_OUT_OF_RANGE" }, + { 0xC002002F, "RPC_NT_BINDING_HAS_NO_AUTH" }, + { 0xC0020030, "RPC_NT_UNKNOWN_AUTHN_SERVICE" }, + { 0xC0020031, "RPC_NT_UNKNOWN_AUTHN_LEVEL" }, + { 0xC0020032, "RPC_NT_INVALID_AUTH_IDENTITY" }, + { 0xC0020033, "RPC_NT_UNKNOWN_AUTHZ_SERVICE" }, + { 0xC0020034, "EPT_NT_INVALID_ENTRY" }, + { 0xC0020035, "EPT_NT_CANT_PERFORM_OP" }, + { 0xC0020036, "EPT_NT_NOT_REGISTERED" }, + { 0xC0020037, "RPC_NT_NOTHING_TO_EXPORT" }, + { 0xC0020038, "RPC_NT_INCOMPLETE_NAME" }, + { 0xC0020039, "RPC_NT_INVALID_VERS_OPTION" }, + { 0xC002003A, "RPC_NT_NO_MORE_MEMBERS" }, + { 0xC002003B, "RPC_NT_NOT_ALL_OBJS_UNEXPORTED" }, + { 0xC002003C, "RPC_NT_INTERFACE_NOT_FOUND" }, + { 0xC002003D, "RPC_NT_ENTRY_ALREADY_EXISTS" }, + { 0xC002003E, "RPC_NT_ENTRY_NOT_FOUND" }, + { 0xC002003F, "RPC_NT_NAME_SERVICE_UNAVAILABLE" }, + { 0xC0020040, "RPC_NT_INVALID_NAF_ID" }, + { 0xC0020041, "RPC_NT_CANNOT_SUPPORT" }, + { 0xC0020042, "RPC_NT_NO_CONTEXT_AVAILABLE" }, + { 0xC0020043, "RPC_NT_INTERNAL_ERROR" }, + { 0xC0020044, "RPC_NT_ZERO_DIVIDE" }, + { 0xC0020045, "RPC_NT_ADDRESS_ERROR" }, + { 0xC0020046, "RPC_NT_FP_DIV_ZERO" }, + { 0xC0020047, "RPC_NT_FP_UNDERFLOW" }, + { 0xC0020048, "RPC_NT_FP_OVERFLOW" }, + { 0xC0020049, "RPC_NT_CALL_IN_PROGRESS" }, + { 0xC002004A, "RPC_NT_NO_MORE_BINDINGS" }, + { 0xC002004B, "RPC_NT_GROUP_MEMBER_NOT_FOUND" }, + { 0xC002004C, "EPT_NT_CANT_CREATE" }, + { 0xC002004D, "RPC_NT_INVALID_OBJECT" }, + { 0xC002004F, "RPC_NT_NO_INTERFACES" }, + { 0xC0020050, "RPC_NT_CALL_CANCELLED" }, + { 0xC0020051, "RPC_NT_BINDING_INCOMPLETE" }, + { 0xC0020052, "RPC_NT_COMM_FAILURE" }, + { 0xC0020053, "RPC_NT_UNSUPPORTED_AUTHN_LEVEL" }, + { 0xC0020054, "RPC_NT_NO_PRINC_NAME" }, + { 0xC0020055, "RPC_NT_NOT_RPC_ERROR" }, + { 0xC0020057, "RPC_NT_SEC_PKG_ERROR" }, + { 0xC0020058, "RPC_NT_NOT_CANCELLED" }, + { 0xC0020062, "RPC_NT_INVALID_ASYNC_HANDLE" }, + { 0xC0020063, "RPC_NT_INVALID_ASYNC_CALL" }, + { 0xC0020064, "RPC_NT_PROXY_ACCESS_DENIED" }, + { 0xC0030001, "RPC_NT_NO_MORE_ENTRIES" }, + { 0xC0030002, "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL" }, + { 0xC0030003, "RPC_NT_SS_CHAR_TRANS_SHORT_FILE" }, + { 0xC0030004, "RPC_NT_SS_IN_NULL_CONTEXT" }, + { 0xC0030005, "RPC_NT_SS_CONTEXT_MISMATCH" }, + { 0xC0030006, "RPC_NT_SS_CONTEXT_DAMAGED" }, + { 0xC0030007, "RPC_NT_SS_HANDLES_MISMATCH" }, + { 0xC0030008, "RPC_NT_SS_CANNOT_GET_CALL_HANDLE" }, + { 0xC0030009, "RPC_NT_NULL_REF_POINTER" }, + { 0xC003000A, "RPC_NT_ENUM_VALUE_OUT_OF_RANGE" }, + { 0xC003000B, "RPC_NT_BYTE_COUNT_TOO_SMALL" }, + { 0xC003000C, "RPC_NT_BAD_STUB_DATA" }, + { 0xC0030059, "RPC_NT_INVALID_ES_ACTION" }, + { 0xC003005A, "RPC_NT_WRONG_ES_VERSION" }, + { 0xC003005B, "RPC_NT_WRONG_STUB_VERSION" }, + { 0xC003005C, "RPC_NT_INVALID_PIPE_OBJECT" }, + { 0xC003005D, "RPC_NT_INVALID_PIPE_OPERATION" }, + { 0xC003005E, "RPC_NT_WRONG_PIPE_VERSION" }, + { 0xC003005F, "RPC_NT_PIPE_CLOSED" }, + { 0xC0030060, "RPC_NT_PIPE_DISCIPLINE_ERROR" }, + { 0xC0030061, "RPC_NT_PIPE_EMPTY" }, + { 0xC0040035, "STATUS_PNP_BAD_MPS_TABLE" }, + { 0xC0040036, "STATUS_PNP_TRANSLATION_FAILED" }, + { 0xC0040037, "STATUS_PNP_IRQ_TRANSLATION_FAILED" }, + { 0xC0040038, "STATUS_PNP_INVALID_ID" }, + { 0xC0040039, "STATUS_IO_REISSUE_AS_CACHED" }, + { 0xC00A0001, "STATUS_CTX_WINSTATION_NAME_INVALID" }, + { 0xC00A0002, "STATUS_CTX_INVALID_PD" }, + { 0xC00A0003, "STATUS_CTX_PD_NOT_FOUND" }, + { 0xC00A0006, "STATUS_CTX_CLOSE_PENDING" }, + { 0xC00A0007, "STATUS_CTX_NO_OUTBUF" }, + { 0xC00A0008, "STATUS_CTX_MODEM_INF_NOT_FOUND" }, + { 0xC00A0009, "STATUS_CTX_INVALID_MODEMNAME" }, + { 0xC00A000A, "STATUS_CTX_RESPONSE_ERROR" }, + { 0xC00A000B, "STATUS_CTX_MODEM_RESPONSE_TIMEOUT" }, + { 0xC00A000C, "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER" }, + { 0xC00A000D, "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE" }, + { 0xC00A000E, "STATUS_CTX_MODEM_RESPONSE_BUSY" }, + { 0xC00A000F, "STATUS_CTX_MODEM_RESPONSE_VOICE" }, + { 0xC00A0010, "STATUS_CTX_TD_ERROR" }, + { 0xC00A0012, "STATUS_CTX_LICENSE_CLIENT_INVALID" }, + { 0xC00A0013, "STATUS_CTX_LICENSE_NOT_AVAILABLE" }, + { 0xC00A0014, "STATUS_CTX_LICENSE_EXPIRED" }, + { 0xC00A0015, "STATUS_CTX_WINSTATION_NOT_FOUND" }, + { 0xC00A0016, "STATUS_CTX_WINSTATION_NAME_COLLISION" }, + { 0xC00A0017, "STATUS_CTX_WINSTATION_BUSY" }, + { 0xC00A0018, "STATUS_CTX_BAD_VIDEO_MODE" }, + { 0xC00A0022, "STATUS_CTX_GRAPHICS_INVALID" }, + { 0xC00A0024, "STATUS_CTX_NOT_CONSOLE" }, + { 0xC00A0026, "STATUS_CTX_CLIENT_QUERY_TIMEOUT" }, + { 0xC00A0027, "STATUS_CTX_CONSOLE_DISCONNECT" }, + { 0xC00A0028, "STATUS_CTX_CONSOLE_CONNECT" }, + { 0xC00A002A, "STATUS_CTX_SHADOW_DENIED" }, + { 0xC00A002B, "STATUS_CTX_WINSTATION_ACCESS_DENIED" }, + { 0xC00A002E, "STATUS_CTX_INVALID_WD" }, + { 0xC00A002F, "STATUS_CTX_WD_NOT_FOUND" }, + { 0xC00A0030, "STATUS_CTX_SHADOW_INVALID" }, + { 0xC00A0031, "STATUS_CTX_SHADOW_DISABLED" }, + { 0xC00A0032, "STATUS_RDP_PROTOCOL_ERROR" }, + { 0xC00A0033, "STATUS_CTX_CLIENT_LICENSE_NOT_SET" }, + { 0xC00A0034, "STATUS_CTX_CLIENT_LICENSE_IN_USE" }, + { 0xC00A0035, "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE" }, + { 0xC00A0036, "STATUS_CTX_SHADOW_NOT_RUNNING" }, + { 0xC00A0037, "STATUS_CTX_LOGON_DISABLED" }, + { 0xC00A0038, "STATUS_CTX_SECURITY_LAYER_ERROR" }, + { 0xC00A0039, "STATUS_TS_INCOMPATIBLE_SESSIONS" }, + { 0xC00B0001, "STATUS_MUI_FILE_NOT_FOUND" }, + { 0xC00B0002, "STATUS_MUI_INVALID_FILE" }, + { 0xC00B0003, "STATUS_MUI_INVALID_RC_CONFIG" }, + { 0xC00B0004, "STATUS_MUI_INVALID_LOCALE_NAME" }, + { 0xC00B0005, "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME" }, + { 0xC00B0006, "STATUS_MUI_FILE_NOT_LOADED" }, + { 0xC00B0007, "STATUS_RESOURCE_ENUM_USER_STOP" }, + { 0xC0130001, "STATUS_CLUSTER_INVALID_NODE" }, + { 0xC0130002, "STATUS_CLUSTER_NODE_EXISTS" }, + { 0xC0130003, "STATUS_CLUSTER_JOIN_IN_PROGRESS" }, + { 0xC0130004, "STATUS_CLUSTER_NODE_NOT_FOUND" }, + { 0xC0130005, "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND" }, + { 0xC0130006, "STATUS_CLUSTER_NETWORK_EXISTS" }, + { 0xC0130007, "STATUS_CLUSTER_NETWORK_NOT_FOUND" }, + { 0xC0130008, "STATUS_CLUSTER_NETINTERFACE_EXISTS" }, + { 0xC0130009, "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND" }, + { 0xC013000A, "STATUS_CLUSTER_INVALID_REQUEST" }, + { 0xC013000B, "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER" }, + { 0xC013000C, "STATUS_CLUSTER_NODE_DOWN" }, + { 0xC013000D, "STATUS_CLUSTER_NODE_UNREACHABLE" }, + { 0xC013000E, "STATUS_CLUSTER_NODE_NOT_MEMBER" }, + { 0xC013000F, "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS" }, + { 0xC0130010, "STATUS_CLUSTER_INVALID_NETWORK" }, + { 0xC0130011, "STATUS_CLUSTER_NO_NET_ADAPTERS" }, + { 0xC0130012, "STATUS_CLUSTER_NODE_UP" }, + { 0xC0130013, "STATUS_CLUSTER_NODE_PAUSED" }, + { 0xC0130014, "STATUS_CLUSTER_NODE_NOT_PAUSED" }, + { 0xC0130015, "STATUS_CLUSTER_NO_SECURITY_CONTEXT" }, + { 0xC0130016, "STATUS_CLUSTER_NETWORK_NOT_INTERNAL" }, + { 0xC0130017, "STATUS_CLUSTER_POISONED" }, + { 0xC0140001, "STATUS_ACPI_INVALID_OPCODE" }, + { 0xC0140002, "STATUS_ACPI_STACK_OVERFLOW" }, + { 0xC0140003, "STATUS_ACPI_ASSERT_FAILED" }, + { 0xC0140004, "STATUS_ACPI_INVALID_INDEX" }, + { 0xC0140005, "STATUS_ACPI_INVALID_ARGUMENT" }, + { 0xC0140006, "STATUS_ACPI_FATAL" }, + { 0xC0140007, "STATUS_ACPI_INVALID_SUPERNAME" }, + { 0xC0140008, "STATUS_ACPI_INVALID_ARGTYPE" }, + { 0xC0140009, "STATUS_ACPI_INVALID_OBJTYPE" }, + { 0xC014000A, "STATUS_ACPI_INVALID_TARGETTYPE" }, + { 0xC014000B, "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT" }, + { 0xC014000C, "STATUS_ACPI_ADDRESS_NOT_MAPPED" }, + { 0xC014000D, "STATUS_ACPI_INVALID_EVENTTYPE" }, + { 0xC014000E, "STATUS_ACPI_HANDLER_COLLISION" }, + { 0xC014000F, "STATUS_ACPI_INVALID_DATA" }, + { 0xC0140010, "STATUS_ACPI_INVALID_REGION" }, + { 0xC0140011, "STATUS_ACPI_INVALID_ACCESS_SIZE" }, + { 0xC0140012, "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK" }, + { 0xC0140013, "STATUS_ACPI_ALREADY_INITIALIZED" }, + { 0xC0140014, "STATUS_ACPI_NOT_INITIALIZED" }, + { 0xC0140015, "STATUS_ACPI_INVALID_MUTEX_LEVEL" }, + { 0xC0140016, "STATUS_ACPI_MUTEX_NOT_OWNED" }, + { 0xC0140017, "STATUS_ACPI_MUTEX_NOT_OWNER" }, + { 0xC0140018, "STATUS_ACPI_RS_ACCESS" }, + { 0xC0140019, "STATUS_ACPI_INVALID_TABLE" }, + { 0xC0140020, "STATUS_ACPI_REG_HANDLER_FAILED" }, + { 0xC0140021, "STATUS_ACPI_POWER_REQUEST_FAILED" }, + { 0xC0150001, "STATUS_SXS_SECTION_NOT_FOUND" }, + { 0xC0150002, "STATUS_SXS_CANT_GEN_ACTCTX" }, + { 0xC0150003, "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT" }, + { 0xC0150004, "STATUS_SXS_ASSEMBLY_NOT_FOUND" }, + { 0xC0150005, "STATUS_SXS_MANIFEST_FORMAT_ERROR" }, + { 0xC0150006, "STATUS_SXS_MANIFEST_PARSE_ERROR" }, + { 0xC0150007, "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED" }, + { 0xC0150008, "STATUS_SXS_KEY_NOT_FOUND" }, + { 0xC0150009, "STATUS_SXS_VERSION_CONFLICT" }, + { 0xC015000A, "STATUS_SXS_WRONG_SECTION_TYPE" }, + { 0xC015000B, "STATUS_SXS_THREAD_QUERIES_DISABLED" }, + { 0xC015000C, "STATUS_SXS_ASSEMBLY_MISSING" }, + { 0xC015000E, "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET" }, + { 0xC015000F, "STATUS_SXS_EARLY_DEACTIVATION" }, + { 0xC0150010, "STATUS_SXS_INVALID_DEACTIVATION" }, + { 0xC0150011, "STATUS_SXS_MULTIPLE_DEACTIVATION" }, + { 0xC0150012, "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY" }, + { 0xC0150013, "STATUS_SXS_PROCESS_TERMINATION_REQUESTED" }, + { 0xC0150014, "STATUS_SXS_CORRUPT_ACTIVATION_STACK" }, + { 0xC0150015, "STATUS_SXS_CORRUPTION" }, + { 0xC0150016, "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE" }, + { 0xC0150017, "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME" }, + { 0xC0150018, "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE" }, + { 0xC0150019, "STATUS_SXS_IDENTITY_PARSE_ERROR" }, + { 0xC015001A, "STATUS_SXS_COMPONENT_STORE_CORRUPT" }, + { 0xC015001B, "STATUS_SXS_FILE_HASH_MISMATCH" }, + { 0xC015001C, "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT" }, + { 0xC015001D, "STATUS_SXS_IDENTITIES_DIFFERENT" }, + { 0xC015001E, "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT" }, + { 0xC015001F, "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY" }, + { 0xC0150020, "STATUS_ADVANCED_INSTALLER_FAILED" }, + { 0xC0150021, "STATUS_XML_ENCODING_MISMATCH" }, + { 0xC0150022, "STATUS_SXS_MANIFEST_TOO_BIG" }, + { 0xC0150023, "STATUS_SXS_SETTING_NOT_REGISTERED" }, + { 0xC0150024, "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE" }, + { 0xC0150025, "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED" }, + { 0xC0150026, "STATUS_GENERIC_COMMAND_FAILED" }, + { 0xC0150027, "STATUS_SXS_FILE_HASH_MISSING" }, + { 0xC0190001, "STATUS_TRANSACTIONAL_CONFLICT" }, + { 0xC0190002, "STATUS_INVALID_TRANSACTION" }, + { 0xC0190003, "STATUS_TRANSACTION_NOT_ACTIVE" }, + { 0xC0190004, "STATUS_TM_INITIALIZATION_FAILED" }, + { 0xC0190005, "STATUS_RM_NOT_ACTIVE" }, + { 0xC0190006, "STATUS_RM_METADATA_CORRUPT" }, + { 0xC0190007, "STATUS_TRANSACTION_NOT_JOINED" }, + { 0xC0190008, "STATUS_DIRECTORY_NOT_RM" }, + { 0xC019000A, "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE" }, + { 0xC019000B, "STATUS_LOG_RESIZE_INVALID_SIZE" }, + { 0xC019000C, "STATUS_REMOTE_FILE_VERSION_MISMATCH" }, + { 0xC019000F, "STATUS_CRM_PROTOCOL_ALREADY_EXISTS" }, + { 0xC0190010, "STATUS_TRANSACTION_PROPAGATION_FAILED" }, + { 0xC0190011, "STATUS_CRM_PROTOCOL_NOT_FOUND" }, + { 0xC0190012, "STATUS_TRANSACTION_SUPERIOR_EXISTS" }, + { 0xC0190013, "STATUS_TRANSACTION_REQUEST_NOT_VALID" }, + { 0xC0190014, "STATUS_TRANSACTION_NOT_REQUESTED" }, + { 0xC0190015, "STATUS_TRANSACTION_ALREADY_ABORTED" }, + { 0xC0190016, "STATUS_TRANSACTION_ALREADY_COMMITTED" }, + { 0xC0190017, "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER" }, + { 0xC0190018, "STATUS_CURRENT_TRANSACTION_NOT_VALID" }, + { 0xC0190019, "STATUS_LOG_GROWTH_FAILED" }, + { 0xC0190021, "STATUS_OBJECT_NO_LONGER_EXISTS" }, + { 0xC0190022, "STATUS_STREAM_MINIVERSION_NOT_FOUND" }, + { 0xC0190023, "STATUS_STREAM_MINIVERSION_NOT_VALID" }, + { 0xC0190024, "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION" }, + { 0xC0190025, "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT" }, + { 0xC0190026, "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS" }, + { 0xC0190028, "STATUS_HANDLE_NO_LONGER_VALID" }, + { 0xC0190030, "STATUS_LOG_CORRUPTION_DETECTED" }, + { 0xC0190032, "STATUS_RM_DISCONNECTED" }, + { 0xC0190033, "STATUS_ENLISTMENT_NOT_SUPERIOR" }, + { 0xC0190036, "STATUS_FILE_IDENTITY_NOT_PERSISTENT" }, + { 0xC0190037, "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY" }, + { 0xC0190038, "STATUS_CANT_CROSS_RM_BOUNDARY" }, + { 0xC0190039, "STATUS_TXF_DIR_NOT_EMPTY" }, + { 0xC019003A, "STATUS_INDOUBT_TRANSACTIONS_EXIST" }, + { 0xC019003B, "STATUS_TM_VOLATILE" }, + { 0xC019003C, "STATUS_ROLLBACK_TIMER_EXPIRED" }, + { 0xC019003D, "STATUS_TXF_ATTRIBUTE_CORRUPT" }, + { 0xC019003E, "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION" }, + { 0xC019003F, "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED" }, + { 0xC0190040, "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE" }, + { 0xC0190043, "STATUS_TRANSACTION_REQUIRED_PROMOTION" }, + { 0xC0190044, "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION" }, + { 0xC0190045, "STATUS_TRANSACTIONS_NOT_FROZEN" }, + { 0xC0190046, "STATUS_TRANSACTION_FREEZE_IN_PROGRESS" }, + { 0xC0190047, "STATUS_NOT_SNAPSHOT_VOLUME" }, + { 0xC0190048, "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES" }, + { 0xC0190049, "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION" }, + { 0xC019004A, "STATUS_TM_IDENTITY_MISMATCH" }, + { 0xC019004B, "STATUS_FLOATED_SECTION" }, + { 0xC019004C, "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK" }, + { 0xC019004D, "STATUS_CANNOT_ABORT_TRANSACTIONS" }, + { 0xC019004E, "STATUS_TRANSACTION_NOT_FOUND" }, + { 0xC019004F, "STATUS_RESOURCEMANAGER_NOT_FOUND" }, + { 0xC0190050, "STATUS_ENLISTMENT_NOT_FOUND" }, + { 0xC0190051, "STATUS_TRANSACTIONMANAGER_NOT_FOUND" }, + { 0xC0190052, "STATUS_TRANSACTIONMANAGER_NOT_ONLINE" }, + { 0xC0190053, "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION" }, + { 0xC0190054, "STATUS_TRANSACTION_NOT_ROOT" }, + { 0xC0190055, "STATUS_TRANSACTION_OBJECT_EXPIRED" }, + { 0xC0190056, "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION" }, + { 0xC0190057, "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED" }, + { 0xC0190058, "STATUS_TRANSACTION_RECORD_TOO_LONG" }, + { 0xC0190059, "STATUS_NO_LINK_TRACKING_IN_TRANSACTION" }, + { 0xC019005A, "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION" }, + { 0xC019005B, "STATUS_TRANSACTION_INTEGRITY_VIOLATED" }, + { 0xC0190060, "STATUS_EXPIRED_HANDLE" }, + { 0xC0190061, "STATUS_TRANSACTION_NOT_ENLISTED" }, + { 0xC01A0001, "STATUS_LOG_SECTOR_INVALID" }, + { 0xC01A0002, "STATUS_LOG_SECTOR_PARITY_INVALID" }, + { 0xC01A0003, "STATUS_LOG_SECTOR_REMAPPED" }, + { 0xC01A0004, "STATUS_LOG_BLOCK_INCOMPLETE" }, + { 0xC01A0005, "STATUS_LOG_INVALID_RANGE" }, + { 0xC01A0006, "STATUS_LOG_BLOCKS_EXHAUSTED" }, + { 0xC01A0007, "STATUS_LOG_READ_CONTEXT_INVALID" }, + { 0xC01A0008, "STATUS_LOG_RESTART_INVALID" }, + { 0xC01A0009, "STATUS_LOG_BLOCK_VERSION" }, + { 0xC01A000A, "STATUS_LOG_BLOCK_INVALID" }, + { 0xC01A000B, "STATUS_LOG_READ_MODE_INVALID" }, + { 0xC01A000D, "STATUS_LOG_METADATA_CORRUPT" }, + { 0xC01A000E, "STATUS_LOG_METADATA_INVALID" }, + { 0xC01A000F, "STATUS_LOG_METADATA_INCONSISTENT" }, + { 0xC01A0010, "STATUS_LOG_RESERVATION_INVALID" }, + { 0xC01A0011, "STATUS_LOG_CANT_DELETE" }, + { 0xC01A0012, "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED" }, + { 0xC01A0013, "STATUS_LOG_START_OF_LOG" }, + { 0xC01A0014, "STATUS_LOG_POLICY_ALREADY_INSTALLED" }, + { 0xC01A0015, "STATUS_LOG_POLICY_NOT_INSTALLED" }, + { 0xC01A0016, "STATUS_LOG_POLICY_INVALID" }, + { 0xC01A0017, "STATUS_LOG_POLICY_CONFLICT" }, + { 0xC01A0018, "STATUS_LOG_PINNED_ARCHIVE_TAIL" }, + { 0xC01A0019, "STATUS_LOG_RECORD_NONEXISTENT" }, + { 0xC01A001A, "STATUS_LOG_RECORDS_RESERVED_INVALID" }, + { 0xC01A001B, "STATUS_LOG_SPACE_RESERVED_INVALID" }, + { 0xC01A001C, "STATUS_LOG_TAIL_INVALID" }, + { 0xC01A001D, "STATUS_LOG_FULL" }, + { 0xC01A001E, "STATUS_LOG_MULTIPLEXED" }, + { 0xC01A001F, "STATUS_LOG_DEDICATED" }, + { 0xC01A0020, "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS" }, + { 0xC01A0021, "STATUS_LOG_ARCHIVE_IN_PROGRESS" }, + { 0xC01A0022, "STATUS_LOG_EPHEMERAL" }, + { 0xC01A0023, "STATUS_LOG_NOT_ENOUGH_CONTAINERS" }, + { 0xC01A0024, "STATUS_LOG_CLIENT_ALREADY_REGISTERED" }, + { 0xC01A0025, "STATUS_LOG_CLIENT_NOT_REGISTERED" }, + { 0xC01A0026, "STATUS_LOG_FULL_HANDLER_IN_PROGRESS" }, + { 0xC01A0027, "STATUS_LOG_CONTAINER_READ_FAILED" }, + { 0xC01A0028, "STATUS_LOG_CONTAINER_WRITE_FAILED" }, + { 0xC01A0029, "STATUS_LOG_CONTAINER_OPEN_FAILED" }, + { 0xC01A002A, "STATUS_LOG_CONTAINER_STATE_INVALID" }, + { 0xC01A002B, "STATUS_LOG_STATE_INVALID" }, + { 0xC01A002C, "STATUS_LOG_PINNED" }, + { 0xC01A002D, "STATUS_LOG_METADATA_FLUSH_FAILED" }, + { 0xC01A002E, "STATUS_LOG_INCONSISTENT_SECURITY" }, + { 0xC01A002F, "STATUS_LOG_APPENDED_FLUSH_FAILED" }, + { 0xC01A0030, "STATUS_LOG_PINNED_RESERVATION" }, + { 0xC01B00EA, "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD" }, + { 0xC01C0001, "STATUS_FLT_NO_HANDLER_DEFINED" }, + { 0xC01C0002, "STATUS_FLT_CONTEXT_ALREADY_DEFINED" }, + { 0xC01C0003, "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST" }, + { 0xC01C0004, "STATUS_FLT_DISALLOW_FAST_IO" }, + { 0xC01C0005, "STATUS_FLT_INVALID_NAME_REQUEST" }, + { 0xC01C0006, "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION" }, + { 0xC01C0007, "STATUS_FLT_NOT_INITIALIZED" }, + { 0xC01C0008, "STATUS_FLT_FILTER_NOT_READY" }, + { 0xC01C0009, "STATUS_FLT_POST_OPERATION_CLEANUP" }, + { 0xC01C000A, "STATUS_FLT_INTERNAL_ERROR" }, + { 0xC01C000B, "STATUS_FLT_DELETING_OBJECT" }, + { 0xC01C000C, "STATUS_FLT_MUST_BE_NONPAGED_POOL" }, + { 0xC01C000D, "STATUS_FLT_DUPLICATE_ENTRY" }, + { 0xC01C000E, "STATUS_FLT_CBDQ_DISABLED" }, + { 0xC01C000F, "STATUS_FLT_DO_NOT_ATTACH" }, + { 0xC01C0010, "STATUS_FLT_DO_NOT_DETACH" }, + { 0xC01C0011, "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION" }, + { 0xC01C0012, "STATUS_FLT_INSTANCE_NAME_COLLISION" }, + { 0xC01C0013, "STATUS_FLT_FILTER_NOT_FOUND" }, + { 0xC01C0014, "STATUS_FLT_VOLUME_NOT_FOUND" }, + { 0xC01C0015, "STATUS_FLT_INSTANCE_NOT_FOUND" }, + { 0xC01C0016, "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND" }, + { 0xC01C0017, "STATUS_FLT_INVALID_CONTEXT_REGISTRATION" }, + { 0xC01C0018, "STATUS_FLT_NAME_CACHE_MISS" }, + { 0xC01C0019, "STATUS_FLT_NO_DEVICE_OBJECT" }, + { 0xC01C001A, "STATUS_FLT_VOLUME_ALREADY_MOUNTED" }, + { 0xC01C001B, "STATUS_FLT_ALREADY_ENLISTED" }, + { 0xC01C001C, "STATUS_FLT_CONTEXT_ALREADY_LINKED" }, + { 0xC01C0020, "STATUS_FLT_NO_WAITER_FOR_REPLY" }, + { 0xC01D0001, "STATUS_MONITOR_NO_DESCRIPTOR" }, + { 0xC01D0002, "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT" }, + { 0xC01D0003, "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM" }, + { 0xC01D0004, "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK" }, + { 0xC01D0005, "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED" }, + { 0xC01D0006, "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK" }, + { 0xC01D0007, "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK" }, + { 0xC01D0008, "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA" }, + { 0xC01D0009, "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK" }, + { 0xC01D000A, "STATUS_MONITOR_INVALID_MANUFACTURE_DATE" }, + { 0xC01E0000, "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER" }, + { 0xC01E0001, "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER" }, + { 0xC01E0002, "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER" }, + { 0xC01E0003, "STATUS_GRAPHICS_ADAPTER_WAS_RESET" }, + { 0xC01E0004, "STATUS_GRAPHICS_INVALID_DRIVER_MODEL" }, + { 0xC01E0005, "STATUS_GRAPHICS_PRESENT_MODE_CHANGED" }, + { 0xC01E0006, "STATUS_GRAPHICS_PRESENT_OCCLUDED" }, + { 0xC01E0007, "STATUS_GRAPHICS_PRESENT_DENIED" }, + { 0xC01E0008, "STATUS_GRAPHICS_CANNOTCOLORCONVERT" }, + { 0xC01E000B, "STATUS_GRAPHICS_PRESENT_REDIRECTION_DISABLED" }, + { 0xC01E000C, "STATUS_GRAPHICS_PRESENT_UNOCCLUDED" }, + { 0xC01E0100, "STATUS_GRAPHICS_NO_VIDEO_MEMORY" }, + { 0xC01E0101, "STATUS_GRAPHICS_CANT_LOCK_MEMORY" }, + { 0xC01E0102, "STATUS_GRAPHICS_ALLOCATION_BUSY" }, + { 0xC01E0103, "STATUS_GRAPHICS_TOO_MANY_REFERENCES" }, + { 0xC01E0104, "STATUS_GRAPHICS_TRY_AGAIN_LATER" }, + { 0xC01E0105, "STATUS_GRAPHICS_TRY_AGAIN_NOW" }, + { 0xC01E0106, "STATUS_GRAPHICS_ALLOCATION_INVALID" }, + { 0xC01E0107, "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE" }, + { 0xC01E0108, "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED" }, + { 0xC01E0109, "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION" }, + { 0xC01E0110, "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE" }, + { 0xC01E0111, "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION" }, + { 0xC01E0112, "STATUS_GRAPHICS_ALLOCATION_CLOSED" }, + { 0xC01E0113, "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE" }, + { 0xC01E0114, "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE" }, + { 0xC01E0115, "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE" }, + { 0xC01E0116, "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST" }, + { 0xC01E0200, "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE" }, + { 0xC01E0300, "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY" }, + { 0xC01E0301, "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED" }, + { 0xC01E0302, "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED" }, + { 0xC01E0303, "STATUS_GRAPHICS_INVALID_VIDPN" }, + { 0xC01E0304, "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE" }, + { 0xC01E0305, "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET" }, + { 0xC01E0306, "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED" }, + { 0xC01E0308, "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET" }, + { 0xC01E0309, "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET" }, + { 0xC01E030A, "STATUS_GRAPHICS_INVALID_FREQUENCY" }, + { 0xC01E030B, "STATUS_GRAPHICS_INVALID_ACTIVE_REGION" }, + { 0xC01E030C, "STATUS_GRAPHICS_INVALID_TOTAL_REGION" }, + { 0xC01E0310, "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE" }, + { 0xC01E0311, "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE" }, + { 0xC01E0312, "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET" }, + { 0xC01E0313, "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY" }, + { 0xC01E0314, "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET" }, + { 0xC01E0315, "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET" }, + { 0xC01E0316, "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET" }, + { 0xC01E0317, "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET" }, + { 0xC01E0318, "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET" }, + { 0xC01E0319, "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH" }, + { 0xC01E031A, "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY" }, + { 0xC01E031B, "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET" }, + { 0xC01E031C, "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE" }, + { 0xC01E031D, "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET" }, + { 0xC01E031F, "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET" }, + { 0xC01E0320, "STATUS_GRAPHICS_STALE_MODESET" }, + { 0xC01E0321, "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET" }, + { 0xC01E0322, "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE" }, + { 0xC01E0323, "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN" }, + { 0xC01E0324, "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE" }, + { 0xC01E0325, "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION" }, + { 0xC01E0326, "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES" }, + { 0xC01E0327, "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY" }, + { 0xC01E0328, "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE" }, + { 0xC01E0329, "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET" }, + { 0xC01E032A, "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET" }, + { 0xC01E032B, "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR" }, + { 0xC01E032C, "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET" }, + { 0xC01E032D, "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET" }, + { 0xC01E032E, "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE" }, + { 0xC01E032F, "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE" }, + { 0xC01E0330, "STATUS_GRAPHICS_RESOURCES_NOT_RELATED" }, + { 0xC01E0331, "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE" }, + { 0xC01E0332, "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE" }, + { 0xC01E0333, "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET" }, + { 0xC01E0334, "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER" }, + { 0xC01E0335, "STATUS_GRAPHICS_NO_VIDPNMGR" }, + { 0xC01E0336, "STATUS_GRAPHICS_NO_ACTIVE_VIDPN" }, + { 0xC01E0337, "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY" }, + { 0xC01E0338, "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED" }, + { 0xC01E0339, "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY" }, + { 0xC01E033A, "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE" }, + { 0xC01E033B, "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE" }, + { 0xC01E033C, "STATUS_GRAPHICS_INVALID_STRIDE" }, + { 0xC01E033D, "STATUS_GRAPHICS_INVALID_PIXELFORMAT" }, + { 0xC01E033E, "STATUS_GRAPHICS_INVALID_COLORBASIS" }, + { 0xC01E033F, "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE" }, + { 0xC01E0340, "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY" }, + { 0xC01E0341, "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT" }, + { 0xC01E0342, "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE" }, + { 0xC01E0343, "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN" }, + { 0xC01E0344, "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL" }, + { 0xC01E0345, "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION" }, + { 0xC01E0346, "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED" }, + { 0xC01E0347, "STATUS_GRAPHICS_INVALID_GAMMA_RAMP" }, + { 0xC01E0348, "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED" }, + { 0xC01E0349, "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED" }, + { 0xC01E034A, "STATUS_GRAPHICS_MODE_NOT_IN_MODESET" }, + { 0xC01E034D, "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON" }, + { 0xC01E034E, "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE" }, + { 0xC01E034F, "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE" }, + { 0xC01E0350, "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS" }, + { 0xC01E0352, "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING" }, + { 0xC01E0353, "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED" }, + { 0xC01E0354, "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS" }, + { 0xC01E0355, "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT" }, + { 0xC01E0356, "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM" }, + { 0xC01E0357, "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN" }, + { 0xC01E0358, "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT" }, + { 0xC01E0359, "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED" }, + { 0xC01E035A, "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION" }, + { 0xC01E035B, "STATUS_GRAPHICS_INVALID_CLIENT_TYPE" }, + { 0xC01E035C, "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET" }, + { 0xC01E0400, "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED" }, + { 0xC01E0401, "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED" }, + { 0xC01E0430, "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER" }, + { 0xC01E0431, "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED" }, + { 0xC01E0432, "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED" }, + { 0xC01E0433, "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY" }, + { 0xC01E0434, "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED" }, + { 0xC01E0435, "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON" }, + { 0xC01E0436, "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE" }, + { 0xC01E0438, "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER" }, + { 0xC01E043B, "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED" }, + { 0xC01E0500, "STATUS_GRAPHICS_OPM_NOT_SUPPORTED" }, + { 0xC01E0501, "STATUS_GRAPHICS_COPP_NOT_SUPPORTED" }, + { 0xC01E0502, "STATUS_GRAPHICS_UAB_NOT_SUPPORTED" }, + { 0xC01E0503, "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS" }, + { 0xC01E0504, "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL" }, + { 0xC01E0505, "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST" }, + { 0xC01E0506, "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME" }, + { 0xC01E0507, "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP" }, + { 0xC01E0508, "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED" }, + { 0xC01E050A, "STATUS_GRAPHICS_OPM_INVALID_POINTER" }, + { 0xC01E050B, "STATUS_GRAPHICS_OPM_INTERNAL_ERROR" }, + { 0xC01E050C, "STATUS_GRAPHICS_OPM_INVALID_HANDLE" }, + { 0xC01E050D, "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE" }, + { 0xC01E050E, "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH" }, + { 0xC01E050F, "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED" }, + { 0xC01E0510, "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED" }, + { 0xC01E0511, "STATUS_GRAPHICS_PVP_HFS_FAILED" }, + { 0xC01E0512, "STATUS_GRAPHICS_OPM_INVALID_SRM" }, + { 0xC01E0513, "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP" }, + { 0xC01E0514, "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP" }, + { 0xC01E0515, "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA" }, + { 0xC01E0516, "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET" }, + { 0xC01E0517, "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH" }, + { 0xC01E0518, "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE" }, + { 0xC01E051A, "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS" }, + { 0xC01E051B, "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS" }, + { 0xC01E051C, "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS" }, + { 0xC01E051D, "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST" }, + { 0xC01E051E, "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR" }, + { 0xC01E051F, "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS" }, + { 0xC01E0520, "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED" }, + { 0xC01E0521, "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST" }, + { 0xC01E0580, "STATUS_GRAPHICS_I2C_NOT_SUPPORTED" }, + { 0xC01E0581, "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST" }, + { 0xC01E0582, "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA" }, + { 0xC01E0583, "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA" }, + { 0xC01E0584, "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED" }, + { 0xC01E0585, "STATUS_GRAPHICS_DDCCI_INVALID_DATA" }, + { 0xC01E0586, "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE" }, + { 0xC01E0587, "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING" }, + { 0xC01E0588, "STATUS_GRAPHICS_MCA_INTERNAL_ERROR" }, + { 0xC01E0589, "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND" }, + { 0xC01E058A, "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH" }, + { 0xC01E058B, "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM" }, + { 0xC01E058C, "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE" }, + { 0xC01E058D, "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS" }, + { 0xC01E05E0, "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED" }, + { 0xC01E05E1, "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME" }, + { 0xC01E05E2, "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP" }, + { 0xC01E05E3, "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED" }, + { 0xC01E05E4, "STATUS_GRAPHICS_INVALID_POINTER" }, + { 0xC01E05E5, "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE" }, + { 0xC01E05E6, "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL" }, + { 0xC01E05E7, "STATUS_GRAPHICS_INTERNAL_ERROR" }, + { 0xC01E05E8, "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS" }, + { 0xC0210000, "STATUS_FVE_LOCKED_VOLUME" }, + { 0xC0210001, "STATUS_FVE_NOT_ENCRYPTED" }, + { 0xC0210002, "STATUS_FVE_BAD_INFORMATION" }, + { 0xC0210003, "STATUS_FVE_TOO_SMALL" }, + { 0xC0210004, "STATUS_FVE_FAILED_WRONG_FS" }, + { 0xC0210005, "STATUS_FVE_FAILED_BAD_FS" }, + { 0xC0210006, "STATUS_FVE_FS_NOT_EXTENDED" }, + { 0xC0210007, "STATUS_FVE_FS_MOUNTED" }, + { 0xC0210008, "STATUS_FVE_NO_LICENSE" }, + { 0xC0210009, "STATUS_FVE_ACTION_NOT_ALLOWED" }, + { 0xC021000A, "STATUS_FVE_BAD_DATA" }, + { 0xC021000B, "STATUS_FVE_VOLUME_NOT_BOUND" }, + { 0xC021000C, "STATUS_FVE_NOT_DATA_VOLUME" }, + { 0xC021000D, "STATUS_FVE_CONV_READ_ERROR" }, + { 0xC021000E, "STATUS_FVE_CONV_WRITE_ERROR" }, + { 0xC021000F, "STATUS_FVE_OVERLAPPED_UPDATE" }, + { 0xC0210010, "STATUS_FVE_FAILED_SECTOR_SIZE" }, + { 0xC0210011, "STATUS_FVE_FAILED_AUTHENTICATION" }, + { 0xC0210012, "STATUS_FVE_NOT_OS_VOLUME" }, + { 0xC0210013, "STATUS_FVE_KEYFILE_NOT_FOUND" }, + { 0xC0210014, "STATUS_FVE_KEYFILE_INVALID" }, + { 0xC0210015, "STATUS_FVE_KEYFILE_NO_VMK" }, + { 0xC0210016, "STATUS_FVE_TPM_DISABLED" }, + { 0xC0210017, "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO" }, + { 0xC0210018, "STATUS_FVE_TPM_INVALID_PCR" }, + { 0xC0210019, "STATUS_FVE_TPM_NO_VMK" }, + { 0xC021001A, "STATUS_FVE_PIN_INVALID" }, + { 0xC021001B, "STATUS_FVE_AUTH_INVALID_APPLICATION" }, + { 0xC021001C, "STATUS_FVE_AUTH_INVALID_CONFIG" }, + { 0xC021001D, "STATUS_FVE_DEBUGGER_ENABLED" }, + { 0xC021001E, "STATUS_FVE_DRY_RUN_FAILED" }, + { 0xC021001F, "STATUS_FVE_BAD_METADATA_POINTER" }, + { 0xC0210020, "STATUS_FVE_OLD_METADATA_COPY" }, + { 0xC0210021, "STATUS_FVE_REBOOT_REQUIRED" }, + { 0xC0210022, "STATUS_FVE_RAW_ACCESS" }, + { 0xC0210023, "STATUS_FVE_RAW_BLOCKED" }, + { 0xC0210026, "STATUS_FVE_NO_FEATURE_LICENSE" }, + { 0xC0210027, "STATUS_FVE_POLICY_USER_DISABLE_RDV_NOT_ALLOWED" }, + { 0xC0210028, "STATUS_FVE_CONV_RECOVERY_FAILED" }, + { 0xC0210029, "STATUS_FVE_VIRTUALIZED_SPACE_TOO_BIG" }, + { 0xC0210030, "STATUS_FVE_VOLUME_TOO_SMALL" }, + { 0xC0220001, "STATUS_FWP_CALLOUT_NOT_FOUND" }, + { 0xC0220002, "STATUS_FWP_CONDITION_NOT_FOUND" }, + { 0xC0220003, "STATUS_FWP_FILTER_NOT_FOUND" }, + { 0xC0220004, "STATUS_FWP_LAYER_NOT_FOUND" }, + { 0xC0220005, "STATUS_FWP_PROVIDER_NOT_FOUND" }, + { 0xC0220006, "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND" }, + { 0xC0220007, "STATUS_FWP_SUBLAYER_NOT_FOUND" }, + { 0xC0220008, "STATUS_FWP_NOT_FOUND" }, + { 0xC0220009, "STATUS_FWP_ALREADY_EXISTS" }, + { 0xC022000A, "STATUS_FWP_IN_USE" }, + { 0xC022000B, "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS" }, + { 0xC022000C, "STATUS_FWP_WRONG_SESSION" }, + { 0xC022000D, "STATUS_FWP_NO_TXN_IN_PROGRESS" }, + { 0xC022000E, "STATUS_FWP_TXN_IN_PROGRESS" }, + { 0xC022000F, "STATUS_FWP_TXN_ABORTED" }, + { 0xC0220010, "STATUS_FWP_SESSION_ABORTED" }, + { 0xC0220011, "STATUS_FWP_INCOMPATIBLE_TXN" }, + { 0xC0220012, "STATUS_FWP_TIMEOUT" }, + { 0xC0220013, "STATUS_FWP_NET_EVENTS_DISABLED" }, + { 0xC0220014, "STATUS_FWP_INCOMPATIBLE_LAYER" }, + { 0xC0220015, "STATUS_FWP_KM_CLIENTS_ONLY" }, + { 0xC0220016, "STATUS_FWP_LIFETIME_MISMATCH" }, + { 0xC0220017, "STATUS_FWP_BUILTIN_OBJECT" }, + { 0xC0220018, "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS" }, + { 0xC0220018, "STATUS_FWP_TOO_MANY_CALLOUTS" }, + { 0xC0220019, "STATUS_FWP_NOTIFICATION_DROPPED" }, + { 0xC022001A, "STATUS_FWP_TRAFFIC_MISMATCH" }, + { 0xC022001B, "STATUS_FWP_INCOMPATIBLE_SA_STATE" }, + { 0xC022001C, "STATUS_FWP_NULL_POINTER" }, + { 0xC022001D, "STATUS_FWP_INVALID_ENUMERATOR" }, + { 0xC022001E, "STATUS_FWP_INVALID_FLAGS" }, + { 0xC022001F, "STATUS_FWP_INVALID_NET_MASK" }, + { 0xC0220020, "STATUS_FWP_INVALID_RANGE" }, + { 0xC0220021, "STATUS_FWP_INVALID_INTERVAL" }, + { 0xC0220022, "STATUS_FWP_ZERO_LENGTH_ARRAY" }, + { 0xC0220023, "STATUS_FWP_NULL_DISPLAY_NAME" }, + { 0xC0220024, "STATUS_FWP_INVALID_ACTION_TYPE" }, + { 0xC0220025, "STATUS_FWP_INVALID_WEIGHT" }, + { 0xC0220026, "STATUS_FWP_MATCH_TYPE_MISMATCH" }, + { 0xC0220027, "STATUS_FWP_TYPE_MISMATCH" }, + { 0xC0220028, "STATUS_FWP_OUT_OF_BOUNDS" }, + { 0xC0220029, "STATUS_FWP_RESERVED" }, + { 0xC022002A, "STATUS_FWP_DUPLICATE_CONDITION" }, + { 0xC022002B, "STATUS_FWP_DUPLICATE_KEYMOD" }, + { 0xC022002C, "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER" }, + { 0xC022002D, "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER" }, + { 0xC022002E, "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER" }, + { 0xC022002F, "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT" }, + { 0xC0220030, "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD" }, + { 0xC0220031, "STATUS_FWP_INCOMPATIBLE_DH_GROUP" }, + { 0xC0220032, "STATUS_FWP_EM_NOT_SUPPORTED" }, + { 0xC0220033, "STATUS_FWP_NEVER_MATCH" }, + { 0xC0220034, "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH" }, + { 0xC0220035, "STATUS_FWP_INVALID_PARAMETER" }, + { 0xC0220036, "STATUS_FWP_TOO_MANY_SUBLAYERS" }, + { 0xC0220037, "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED" }, + { 0xC0220038, "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG" }, + { 0xC0220039, "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG" }, + { 0xC022003C, "STATUS_FWP_DUPLICATE_AUTH_METHOD" }, + { 0xC0220100, "STATUS_FWP_TCPIP_NOT_READY" }, + { 0xC0220101, "STATUS_FWP_INJECT_HANDLE_CLOSING" }, + { 0xC0220102, "STATUS_FWP_INJECT_HANDLE_STALE" }, + { 0xC0220103, "STATUS_FWP_CANNOT_PEND" }, + { 0xC0230002, "STATUS_NDIS_CLOSING" }, + { 0xC0230004, "STATUS_NDIS_BAD_VERSION" }, + { 0xC0230005, "STATUS_NDIS_BAD_CHARACTERISTICS" }, + { 0xC0230006, "STATUS_NDIS_ADAPTER_NOT_FOUND" }, + { 0xC0230007, "STATUS_NDIS_OPEN_FAILED" }, + { 0xC0230008, "STATUS_NDIS_DEVICE_FAILED" }, + { 0xC0230009, "STATUS_NDIS_MULTICAST_FULL" }, + { 0xC023000A, "STATUS_NDIS_MULTICAST_EXISTS" }, + { 0xC023000B, "STATUS_NDIS_MULTICAST_NOT_FOUND" }, + { 0xC023000C, "STATUS_NDIS_REQUEST_ABORTED" }, + { 0xC023000D, "STATUS_NDIS_RESET_IN_PROGRESS" }, + { 0xC023000F, "STATUS_NDIS_INVALID_PACKET" }, + { 0xC0230010, "STATUS_NDIS_INVALID_DEVICE_REQUEST" }, + { 0xC0230011, "STATUS_NDIS_ADAPTER_NOT_READY" }, + { 0xC0230014, "STATUS_NDIS_INVALID_LENGTH" }, + { 0xC0230015, "STATUS_NDIS_INVALID_DATA" }, + { 0xC0230016, "STATUS_NDIS_BUFFER_TOO_SHORT" }, + { 0xC0230017, "STATUS_NDIS_INVALID_OID" }, + { 0xC0230018, "STATUS_NDIS_ADAPTER_REMOVED" }, + { 0xC0230019, "STATUS_NDIS_UNSUPPORTED_MEDIA" }, + { 0xC023001A, "STATUS_NDIS_GROUP_ADDRESS_IN_USE" }, + { 0xC023001B, "STATUS_NDIS_FILE_NOT_FOUND" }, + { 0xC023001C, "STATUS_NDIS_ERROR_READING_FILE" }, + { 0xC023001D, "STATUS_NDIS_ALREADY_MAPPED" }, + { 0xC023001E, "STATUS_NDIS_RESOURCE_CONFLICT" }, + { 0xC023001F, "STATUS_NDIS_MEDIA_DISCONNECTED" }, + { 0xC0230022, "STATUS_NDIS_INVALID_ADDRESS" }, + { 0xC023002A, "STATUS_NDIS_PAUSED" }, + { 0xC023002B, "STATUS_NDIS_INTERFACE_NOT_FOUND" }, + { 0xC023002C, "STATUS_NDIS_UNSUPPORTED_REVISION" }, + { 0xC023002D, "STATUS_NDIS_INVALID_PORT" }, + { 0xC023002E, "STATUS_NDIS_INVALID_PORT_STATE" }, + { 0xC023002F, "STATUS_NDIS_LOW_POWER_STATE" }, + { 0xC02300BB, "STATUS_NDIS_NOT_SUPPORTED" }, + { 0xC023100F, "STATUS_NDIS_OFFLOAD_POLICY" }, + { 0xC0231012, "STATUS_NDIS_OFFLOAD_CONNECTION_REJECTED" }, + { 0xC0231013, "STATUS_NDIS_OFFLOAD_PATH_REJECTED" }, + { 0xC0232000, "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED" }, + { 0xC0232001, "STATUS_NDIS_DOT11_MEDIA_IN_USE" }, + { 0xC0232002, "STATUS_NDIS_DOT11_POWER_STATE_INVALID" }, + { 0xC0232003, "STATUS_NDIS_PM_WOL_PATTERN_LIST_FULL" }, + { 0xC0232004, "STATUS_NDIS_PM_PROTOCOL_OFFLOAD_LIST_FULL" }, + { 0xC0360001, "STATUS_IPSEC_BAD_SPI" }, + { 0xC0360002, "STATUS_IPSEC_SA_LIFETIME_EXPIRED" }, + { 0xC0360003, "STATUS_IPSEC_WRONG_SA" }, + { 0xC0360004, "STATUS_IPSEC_REPLAY_CHECK_FAILED" }, + { 0xC0360005, "STATUS_IPSEC_INVALID_PACKET" }, + { 0xC0360006, "STATUS_IPSEC_INTEGRITY_CHECK_FAILED" }, + { 0xC0360007, "STATUS_IPSEC_CLEAR_TEXT_DROP" }, + { 0xC0360008, "STATUS_IPSEC_AUTH_FIREWALL_DROP" }, + { 0xC0360009, "STATUS_IPSEC_THROTTLE_DROP" }, + { 0xC0368000, "STATUS_IPSEC_DOSP_BLOCK" }, + { 0xC0368001, "STATUS_IPSEC_DOSP_RECEIVED_MULTICAST" }, + { 0xC0368002, "STATUS_IPSEC_DOSP_INVALID_PACKET" }, + { 0xC0368003, "STATUS_IPSEC_DOSP_STATE_LOOKUP_FAILED" }, + { 0xC0368004, "STATUS_IPSEC_DOSP_MAX_ENTRIES" }, + { 0xC0368005, "STATUS_IPSEC_DOSP_KEYMOD_NOT_ALLOWED" }, + { 0xC0368006, "STATUS_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES" }, + { 0xC038005B, "STATUS_VOLMGR_MIRROR_NOT_SUPPORTED" }, + { 0xC038005C, "STATUS_VOLMGR_RAID5_NOT_SUPPORTED" }, + { 0xC03A0014, "STATUS_VIRTDISK_PROVIDER_NOT_FOUND" }, + { 0xC03A0015, "STATUS_VIRTDISK_NOT_VIRTUAL_DISK" }, + { 0xC03A0016, "STATUS_VHD_PARENT_VHD_ACCESS_DENIED" }, + { 0xC03A0017, "STATUS_VHD_CHILD_PARENT_SIZE_MISMATCH" }, + { 0xC03A0018, "STATUS_VHD_DIFFERENCING_CHAIN_CYCLE_DETECTED" }, + { 0xC03A0019, "STATUS_VHD_DIFFERENCING_CHAIN_ERROR_IN_PARENT" } +}; + +static int ntstatus_compare(const void* pKey, const void* pValue) +{ + const DWORD* key = (const DWORD*)pKey; + const struct ntstatus_map* cur = (const struct ntstatus_map*)pValue; + if (*key == cur->code) + return 0; + return *key < cur->code ? -1 : 1; +} + +const char* NtStatus2Tag(DWORD ntstatus) +{ + +#if 1 /* Requires sorted struct */ + size_t count = ARRAYSIZE(ntstatusmap); + size_t base = sizeof(ntstatusmap[0]); + const struct ntstatus_map* found = + bsearch(&ntstatus, ntstatusmap, count, base, ntstatus_compare); + if (!found) + return NULL; + return found->tag; +#else + for (size_t x = 0; x < ARRAYSIZE(ntstatusmap); x++) + { + const struct ntstatus_map* cur = &ntstatusmap[x]; + if (cur->code == ntstatus) + return cur->tag; + } + + return NULL; +#endif +} + +const char* Win32ErrorCode2Tag(UINT16 code) +{ +#if 1 /* Requires sorted struct */ + DWORD ntstatus = code; + size_t count = ARRAYSIZE(win32errmap); + size_t base = sizeof(win32errmap[0]); + const struct ntstatus_map* found = + bsearch(&ntstatus, win32errmap, count, base, ntstatus_compare); + if (!found) + return NULL; + return found->tag; +#else + for (size_t x = 0; x < ARRAYSIZE(win32errmap); x++) + { + const struct ntstatus_map* cur = &win32errmap[x]; + if (cur->code == ntstatus) + return cur->tag; + } + + return NULL; +#endif +} diff --git a/winpr/libwinpr/nt/test/CMakeLists.txt b/winpr/libwinpr/nt/test/CMakeLists.txt new file mode 100644 index 0000000..ba50db2 --- /dev/null +++ b/winpr/libwinpr/nt/test/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(MODULE_NAME "TestNt") +set(MODULE_PREFIX "TEST_NT") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestNtCurrentTeb.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/nt/test/TestNtCurrentTeb.c b/winpr/libwinpr/nt/test/TestNtCurrentTeb.c new file mode 100644 index 0000000..6ee3836 --- /dev/null +++ b/winpr/libwinpr/nt/test/TestNtCurrentTeb.c @@ -0,0 +1,24 @@ + +#include + +#include + +int TestNtCurrentTeb(int argc, char* argv[]) +{ +#ifndef _WIN32 + PTEB teb = NULL; + + teb = NtCurrentTeb(); + + if (!teb) + { + printf("NtCurrentTeb() returned NULL\n"); + return -1; + } +#endif + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + return 0; +} diff --git a/winpr/libwinpr/path/CMakeLists.txt b/winpr/libwinpr/path/CMakeLists.txt new file mode 100644 index 0000000..1e4ed92 --- /dev/null +++ b/winpr/libwinpr/path/CMakeLists.txt @@ -0,0 +1,26 @@ +# WinPR: Windows Portable Runtime +# libwinpr-path cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(path.c shell.c) + +if (IOS) + winpr_module_add(shell_ios.m) +endif (IOS) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/path/ModuleOptions.cmake b/winpr/libwinpr/path/ModuleOptions.cmake new file mode 100644 index 0000000..b330a21 --- /dev/null +++ b/winpr/libwinpr/path/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "path") +set(MINWIN_LONG_NAME "Path Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/path/include/PathAllocCombine.c b/winpr/libwinpr/path/include/PathAllocCombine.c new file mode 100644 index 0000000..abdbd29 --- /dev/null +++ b/winpr/libwinpr/path/include/PathAllocCombine.c @@ -0,0 +1,180 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define CUR_PATH_SEPARATOR_STR "\\" +#define PATH_ALLOC_COMBINE PathAllocCombineA +*/ + +/** + * FIXME: These implementations of the PathAllocCombine functions have + * several issues: + * - pszPathIn or pszMore may be NULL (but not both) + * - no check if pszMore is fully qualified (if so, it must be directly + * copied to the output buffer without being combined with pszPathIn. + * - if pszMore begins with a _single_ backslash it must be combined with + * only the root of the path pointed to by pszPathIn and there's no code + * to extract the root of pszPathIn. + * - the function will crash with some short string lengths of the parameters + */ + +#if DEFINE_UNICODE + +HRESULT PATH_ALLOC_COMBINE(PCWSTR pszPathIn, PCWSTR pszMore, unsigned long dwFlags, + PWSTR* ppszPathOut) +{ + PWSTR pszPathOut; + BOOL backslashIn; + BOOL backslashMore; + size_t pszMoreLength; + size_t pszPathInLength; + size_t pszPathOutLength; + WLog_WARN(TAG, "has known bugs and needs fixing."); + + if (!ppszPathOut) + return E_INVALIDARG; + + if (!pszPathIn && !pszMore) + return E_INVALIDARG; + + if (!pszMore) + return E_FAIL; /* valid but not implemented, see top comment */ + + if (!pszPathIn) + return E_FAIL; /* valid but not implemented, see top comment */ + + pszPathInLength = _wcslen(pszPathIn); + pszMoreLength = _wcslen(pszMore); + + /* prevent segfaults - the complete implementation below is buggy */ + if (pszPathInLength < 3) + return E_FAIL; + + backslashIn = (pszPathIn[pszPathInLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + backslashMore = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (backslashMore) + { + if ((pszPathIn[1] == ':') && (pszPathIn[2] == CUR_PATH_SEPARATOR_CHR)) + { + const WCHAR colon[] = { ':', '\0' }; + size_t sizeOfBuffer; + pszPathOutLength = sizeof(WCHAR) + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * sizeof(WCHAR); + pszPathOut = (PWSTR)calloc(sizeOfBuffer, sizeof(WCHAR)); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + _wcsncat(pszPathOut, &pszPathIn[0], 1); + _wcsncat(pszPathOut, colon, ARRAYSIZE(colon)); + _wcsncat(pszPathOut, pszMore, pszMoreLength); + *ppszPathOut = pszPathOut; + return S_OK; + } + } + else + { + const WCHAR sep[] = CUR_PATH_SEPARATOR_STR; + size_t sizeOfBuffer; + pszPathOutLength = pszPathInLength + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * 2; + pszPathOut = (PWSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + _wcsncat(pszPathOut, pszPathIn, pszPathInLength); + if (!backslashIn) + _wcsncat(pszPathOut, sep, ARRAYSIZE(sep)); + _wcsncat(pszPathOut, pszMore, pszMoreLength); + + *ppszPathOut = pszPathOut; + return S_OK; + } + + return E_FAIL; +} + +#else + +HRESULT PATH_ALLOC_COMBINE(PCSTR pszPathIn, PCSTR pszMore, unsigned long dwFlags, PSTR* ppszPathOut) +{ + PSTR pszPathOut; + BOOL backslashIn; + BOOL backslashMore; + int pszMoreLength; + int pszPathInLength; + int pszPathOutLength; + WLog_WARN(TAG, "has known bugs and needs fixing."); + + if (!ppszPathOut) + return E_INVALIDARG; + + if (!pszPathIn && !pszMore) + return E_INVALIDARG; + + if (!pszMore) + return E_FAIL; /* valid but not implemented, see top comment */ + + if (!pszPathIn) + return E_FAIL; /* valid but not implemented, see top comment */ + + pszPathInLength = strlen(pszPathIn); + pszMoreLength = strlen(pszMore); + + /* prevent segfaults - the complete implementation below is buggy */ + if (pszPathInLength < 3) + return E_FAIL; + + backslashIn = (pszPathIn[pszPathInLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + backslashMore = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (backslashMore) + { + if ((pszPathIn[1] == ':') && (pszPathIn[2] == CUR_PATH_SEPARATOR_CHR)) + { + size_t sizeOfBuffer; + pszPathOutLength = 2 + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * 2; + pszPathOut = (PSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + sprintf_s(pszPathOut, sizeOfBuffer, "%c:%s", pszPathIn[0], pszMore); + *ppszPathOut = pszPathOut; + return S_OK; + } + } + else + { + size_t sizeOfBuffer; + pszPathOutLength = pszPathInLength + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * 2; + pszPathOut = (PSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + if (backslashIn) + sprintf_s(pszPathOut, sizeOfBuffer, "%s%s", pszPathIn, pszMore); + else + sprintf_s(pszPathOut, sizeOfBuffer, "%s" CUR_PATH_SEPARATOR_STR "%s", pszPathIn, + pszMore); + + *ppszPathOut = pszPathOut; + return S_OK; + } + + return E_FAIL; +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE +*/ diff --git a/winpr/libwinpr/path/include/PathCchAddExtension.c b/winpr/libwinpr/path/include/PathCchAddExtension.c new file mode 100644 index 0000000..498cfab --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAddExtension.c @@ -0,0 +1,101 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_ADD_EXTENSION(PWSTR pszPath, size_t cchPath, PCWSTR pszExt) +{ + LPWSTR pDot; + BOOL bExtDot; + LPWSTR pBackslash; + size_t pszExtLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszExt) + return E_INVALIDARG; + + pszExtLength = _wcslen(pszExt); + pszPathLength = _wcslen(pszPath); + bExtDot = (pszExt[0] == '.') ? TRUE : FALSE; + + pDot = _wcsrchr(pszPath, '.'); + pBackslash = _wcsrchr(pszPath, CUR_PATH_SEPARATOR_CHR); + + if (pDot && pBackslash) + { + if (pDot > pBackslash) + return S_FALSE; + } + + if (cchPath > pszPathLength + pszExtLength + ((bExtDot) ? 0 : 1)) + { + const WCHAR dot[] = { '.', '\0' }; + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + + if (!bExtDot) + _wcsncat(ptr, dot, _wcslen(dot)); + _wcsncat(ptr, pszExt, pszExtLength); + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#else + +HRESULT PATH_CCH_ADD_EXTENSION(PSTR pszPath, size_t cchPath, PCSTR pszExt) +{ + CHAR* pDot; + BOOL bExtDot; + CHAR* pBackslash; + size_t pszExtLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszExt) + return E_INVALIDARG; + + pszExtLength = strlen(pszExt); + pszPathLength = strlen(pszPath); + bExtDot = (pszExt[0] == '.') ? TRUE : FALSE; + + pDot = strrchr(pszPath, '.'); + pBackslash = strrchr(pszPath, CUR_PATH_SEPARATOR_CHR); + + if (pDot && pBackslash) + { + if (pDot > pBackslash) + return S_FALSE; + } + + if (cchPath > pszPathLength + pszExtLength + ((bExtDot) ? 0 : 1)) + { + if (bExtDot) + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, "%s", pszExt); + else + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, ".%s", pszExt); + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION +*/ diff --git a/winpr/libwinpr/path/include/PathCchAddSeparator.c b/winpr/libwinpr/path/include/PathCchAddSeparator.c new file mode 100644 index 0000000..0ef391c --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAddSeparator.c @@ -0,0 +1,64 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_ADD_SEPARATOR(PWSTR pszPath, size_t cchPath) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = _wcslen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#else + +HRESULT PATH_CCH_ADD_SEPARATOR(PSTR pszPath, size_t cchPath) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = strlen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR +*/ diff --git a/winpr/libwinpr/path/include/PathCchAddSeparatorEx.c b/winpr/libwinpr/path/include/PathCchAddSeparatorEx.c new file mode 100644 index 0000000..02832d8 --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAddSeparatorEx.c @@ -0,0 +1,66 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_ADD_SEPARATOR_EX(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = _wcslen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#else + +HRESULT PATH_CCH_ADD_SEPARATOR_EX(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = strlen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX +*/ diff --git a/winpr/libwinpr/path/include/PathCchAppend.c b/winpr/libwinpr/path/include/PathCchAppend.c new file mode 100644 index 0000000..a4f58cb --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAppend.c @@ -0,0 +1,131 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define CUR_PATH_SEPARATOR_STR "\\" +#define PATH_CCH_APPEND PathCchAppendA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_APPEND(PWSTR pszPath, size_t cchPath, PCWSTR pszMore) +{ + BOOL pathBackslash; + BOOL moreBackslash; + size_t pszMoreLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszMore) + return E_INVALIDARG; + + if (cchPath == 0 || cchPath > PATHCCH_MAX_CCH) + return E_INVALIDARG; + + pszMoreLength = _wcslen(pszMore); + pszPathLength = _wcslen(pszPath); + + pathBackslash = (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + moreBackslash = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (pathBackslash && moreBackslash) + { + if ((pszPathLength + pszMoreLength - 1) < cchPath) + { + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + _wcsncat(ptr, &pszMore[1], _wcslen(&pszMore[1])); + return S_OK; + } + } + else if ((pathBackslash && !moreBackslash) || (!pathBackslash && moreBackslash)) + { + if ((pszPathLength + pszMoreLength) < cchPath) + { + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + _wcsncat(ptr, pszMore, _wcslen(pszMore)); + return S_OK; + } + } + else if (!pathBackslash && !moreBackslash) + { + if ((pszPathLength + pszMoreLength + 1) < cchPath) + { + const WCHAR sep[] = CUR_PATH_SEPARATOR_STR; + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + _wcsncat(ptr, sep, _wcslen(sep)); + _wcsncat(ptr, pszMore, _wcslen(pszMore)); + return S_OK; + } + } + + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); +} + +#else + +HRESULT PATH_CCH_APPEND(PSTR pszPath, size_t cchPath, PCSTR pszMore) +{ + BOOL pathBackslash = FALSE; + BOOL moreBackslash = FALSE; + size_t pszMoreLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszMore) + return E_INVALIDARG; + + if (cchPath == 0 || cchPath > PATHCCH_MAX_CCH) + return E_INVALIDARG; + + pszPathLength = strlen(pszPath); + if (pszPathLength > 0) + pathBackslash = (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + pszMoreLength = strlen(pszMore); + if (pszMoreLength > 0) + moreBackslash = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (pathBackslash && moreBackslash) + { + if ((pszPathLength + pszMoreLength - 1) < cchPath) + { + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, "%s", &pszMore[1]); + return S_OK; + } + } + else if ((pathBackslash && !moreBackslash) || (!pathBackslash && moreBackslash)) + { + if ((pszPathLength + pszMoreLength) < cchPath) + { + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, "%s", pszMore); + return S_OK; + } + } + else if (!pathBackslash && !moreBackslash) + { + if ((pszPathLength + pszMoreLength + 1) < cchPath) + { + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, CUR_PATH_SEPARATOR_STR "%s", + pszMore); + return S_OK; + } + } + + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND +*/ diff --git a/winpr/libwinpr/path/path.c b/winpr/libwinpr/path/path.c new file mode 100644 index 0000000..82e6be1 --- /dev/null +++ b/winpr/libwinpr/path/path.c @@ -0,0 +1,1181 @@ +/** + * WinPR: Windows Portable Runtime + * Path Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include + +#define PATH_SLASH_CHR '/' +#define PATH_SLASH_STR "/" + +#define PATH_BACKSLASH_CHR '\\' +#define PATH_BACKSLASH_STR "\\" + +#ifdef _WIN32 +#define PATH_SLASH_STR_W L"/" +#define PATH_BACKSLASH_STR_W L"\\" +#else +#define PATH_SLASH_STR_W \ + { \ + '/', '\0' \ + } +#define PATH_BACKSLASH_STR_W \ + { \ + '\\', '\0' \ + } +#endif + +#ifdef _WIN32 +#define PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_SEPARATOR_STR PATH_BACKSLASH_STR +#define PATH_SEPARATOR_STR_W PATH_BACKSLASH_STR_W +#else +#define PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_SEPARATOR_STR PATH_SLASH_STR +#define PATH_SEPARATOR_STR_W PATH_SLASH_STR_W +#endif + +#define SHARED_LIBRARY_EXT_DLL "dll" +#define SHARED_LIBRARY_EXT_SO "so" +#define SHARED_LIBRARY_EXT_DYLIB "dylib" + +#ifdef _WIN32 +#define SHARED_LIBRARY_EXT SHARED_LIBRARY_EXT_DLL +#elif defined(__APPLE__) +#define SHARED_LIBRARY_EXT SHARED_LIBRARY_EXT_DYLIB +#else +#define SHARED_LIBRARY_EXT SHARED_LIBRARY_EXT_SO +#endif + +#include "../log.h" +#define TAG WINPR_TAG("path") + +/* + * PathCchAddBackslash + */ + +/* Windows-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashA +#include "include/PathCchAddSeparator.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashW +#include "include/PathCchAddSeparator.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR + +/* Unix-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_CCH_ADD_SEPARATOR PathCchAddSlashA +#include "include/PathCchAddSeparator.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_CCH_ADD_SEPARATOR PathCchAddSlashW +#include "include/PathCchAddSeparator.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR + +/* Native-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define PATH_CCH_ADD_SEPARATOR PathCchAddSeparatorA +#include "include/PathCchAddSeparator.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define PATH_CCH_ADD_SEPARATOR PathCchAddSeparatorW +#include "include/PathCchAddSeparator.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR + +/* + * PathCchRemoveBackslash + */ + +HRESULT PathCchRemoveBackslashA(PSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchRemoveBackslashW(PWSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathCchAddBackslashEx + */ + +/* Windows-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExA +#include "include/PathCchAddSeparatorEx.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExW +#include "include/PathCchAddSeparatorEx.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX + +/* Unix-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSlashExA +#include "include/PathCchAddSeparatorEx.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSlashExW +#include "include/PathCchAddSeparatorEx.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX + +/* Native-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSeparatorExA +#include "include/PathCchAddSeparatorEx.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSeparatorExW +#include "include/PathCchAddSeparatorEx.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX + +HRESULT PathCchRemoveBackslashExA(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchRemoveBackslashExW(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathCchAddExtension + */ + +/* Windows-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionA +#include "include/PathCchAddExtension.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionW +#include "include/PathCchAddExtension.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION + +/* Unix-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_CCH_ADD_EXTENSION UnixPathCchAddExtensionA +#include "include/PathCchAddExtension.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define PATH_CCH_ADD_EXTENSION UnixPathCchAddExtensionW +#include "include/PathCchAddExtension.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION + +/* Native-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define PATH_CCH_ADD_EXTENSION NativePathCchAddExtensionA +#include "include/PathCchAddExtension.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define PATH_CCH_ADD_EXTENSION NativePathCchAddExtensionW +#include "include/PathCchAddExtension.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION + +/* + * PathCchAppend + */ + +/* Windows-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR +#define PATH_CCH_APPEND PathCchAppendA +#include "include/PathCchAppend.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR_W +#define PATH_CCH_APPEND PathCchAppendW +#include "include/PathCchAppend.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND + +/* Unix-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR +#define PATH_CCH_APPEND UnixPathCchAppendA +#include "include/PathCchAppend.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR_W +#define PATH_CCH_APPEND UnixPathCchAppendW +#include "include/PathCchAppend.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND + +/* Native-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR +#define PATH_CCH_APPEND NativePathCchAppendA +#include "include/PathCchAppend.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR_W +#define PATH_CCH_APPEND NativePathCchAppendW +#include "include/PathCchAppend.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND + +/* + * PathCchAppendEx + */ + +HRESULT PathCchAppendExA(PSTR pszPath, size_t cchPath, PCSTR pszMore, unsigned long dwFlags) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchAppendExW(PWSTR pszPath, size_t cchPath, PCWSTR pszMore, unsigned long dwFlags) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathCchCanonicalize + */ + +HRESULT PathCchCanonicalizeA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchCanonicalizeW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathCchCanonicalizeEx + */ + +HRESULT PathCchCanonicalizeExA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, + unsigned long dwFlags) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchCanonicalizeExW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, + unsigned long dwFlags) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathAllocCanonicalize + */ + +HRESULT PathAllocCanonicalizeA(PCSTR pszPathIn, unsigned long dwFlags, PSTR* ppszPathOut) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathAllocCanonicalizeW(PCWSTR pszPathIn, unsigned long dwFlags, PWSTR* ppszPathOut) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathCchCombine + */ + +HRESULT PathCchCombineA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, PCSTR pszMore) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchCombineW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, PCWSTR pszMore) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathCchCombineEx + */ + +HRESULT PathCchCombineExA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, PCSTR pszMore, + unsigned long dwFlags) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchCombineExW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, PCWSTR pszMore, + unsigned long dwFlags) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * PathAllocCombine + */ + +/* Windows-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR +#define PATH_ALLOC_COMBINE PathAllocCombineA +#include "include/PathAllocCombine.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR_W +#define PATH_ALLOC_COMBINE PathAllocCombineW +#include "include/PathAllocCombine.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE + +/* Unix-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR +#define PATH_ALLOC_COMBINE UnixPathAllocCombineA +#include "include/PathAllocCombine.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR_W +#define PATH_ALLOC_COMBINE UnixPathAllocCombineW +#include "include/PathAllocCombine.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE + +/* Native-style Paths */ + +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR +#define PATH_ALLOC_COMBINE NativePathAllocCombineA +#include "include/PathAllocCombine.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE + +#define DEFINE_UNICODE TRUE +#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR +#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR_W +#define PATH_ALLOC_COMBINE NativePathAllocCombineW +#include "include/PathAllocCombine.c" +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE + +/** + * PathCchFindExtension + */ + +HRESULT PathCchFindExtensionA(PCSTR pszPath, size_t cchPath, PCSTR* ppszExt) +{ + const char* p = (const char*)pszPath; + + if (!pszPath || !cchPath || !ppszExt) + return E_INVALIDARG; + + /* find end of string */ + + while (*p && --cchPath) + { + p++; + } + + if (*p) + { + /* pszPath is not null terminated within the cchPath range */ + return E_INVALIDARG; + } + + /* If no extension is found, ppszExt must point to the string's terminating null */ + *ppszExt = p; + + /* search backwards for '.' */ + + while (p > pszPath) + { + if (*p == '.') + { + *ppszExt = (PCSTR)p; + break; + } + + if ((*p == '\\') || (*p == '/') || (*p == ':')) + break; + + p--; + } + + return S_OK; +} + +HRESULT PathCchFindExtensionW(PCWSTR pszPath, size_t cchPath, PCWSTR* ppszExt) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/** + * PathCchRenameExtension + */ + +HRESULT PathCchRenameExtensionA(PSTR pszPath, size_t cchPath, PCSTR pszExt) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchRenameExtensionW(PWSTR pszPath, size_t cchPath, PCWSTR pszExt) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/** + * PathCchRemoveExtension + */ + +HRESULT PathCchRemoveExtensionA(PSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchRemoveExtensionW(PWSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/** + * PathCchIsRoot + */ + +BOOL PathCchIsRootA(PCSTR pszPath) +{ + WLog_ERR(TAG, "not implemented"); + return FALSE; +} + +BOOL PathCchIsRootW(PCWSTR pszPath) +{ + WLog_ERR(TAG, "not implemented"); + return FALSE; +} + +/** + * PathIsUNCEx + */ + +BOOL PathIsUNCExA(PCSTR pszPath, PCSTR* ppszServer) +{ + if (!pszPath) + return FALSE; + + if ((pszPath[0] == '\\') && (pszPath[1] == '\\')) + { + *ppszServer = &pszPath[2]; + return TRUE; + } + + return FALSE; +} + +BOOL PathIsUNCExW(PCWSTR pszPath, PCWSTR* ppszServer) +{ + if (!pszPath) + return FALSE; + + if ((pszPath[0] == '\\') && (pszPath[1] == '\\')) + { + *ppszServer = &pszPath[2]; + return TRUE; + } + + return FALSE; +} + +/** + * PathCchSkipRoot + */ + +HRESULT PathCchSkipRootA(PCSTR pszPath, PCSTR* ppszRootEnd) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchSkipRootW(PCWSTR pszPath, PCWSTR* ppszRootEnd) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/** + * PathCchStripToRoot + */ + +HRESULT PathCchStripToRootA(PSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchStripToRootW(PWSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/** + * PathCchStripPrefix + */ + +HRESULT PathCchStripPrefixA(PSTR pszPath, size_t cchPath) +{ + BOOL hasPrefix = 0; + + if (!pszPath) + return E_INVALIDARG; + + if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH) + return E_INVALIDARG; + + hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') && + (pszPath[3] == '\\')) + ? TRUE + : FALSE; + + if (hasPrefix) + { + if (cchPath < 6) + return S_FALSE; + + if (IsCharAlpha(pszPath[4]) && (pszPath[5] == ':')) /* like C: */ + { + memmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4); + /* since the passed pszPath must not necessarily be null terminated + * and we always have enough space after the strip we can always + * ensure the null termination of the stripped result + */ + pszPath[cchPath - 4] = 0; + return S_OK; + } + } + + return S_FALSE; +} + +HRESULT PathCchStripPrefixW(PWSTR pszPath, size_t cchPath) +{ + BOOL hasPrefix = 0; + + if (!pszPath) + return E_INVALIDARG; + + if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH) + return E_INVALIDARG; + + hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') && + (pszPath[3] == '\\')) + ? TRUE + : FALSE; + + if (hasPrefix) + { + int rc = 0; + if (cchPath < 6) + return S_FALSE; + + rc = (_wcslen(&pszPath[4]) + 1); + if ((rc < 0) || ((INT64)cchPath < rc)) + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + if (IsCharAlphaW(pszPath[4]) && (pszPath[5] == L':')) /* like C: */ + { + wmemmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4); + /* since the passed pszPath must not necessarily be null terminated + * and we always have enough space after the strip we can always + * ensure the null termination of the stripped result + */ + pszPath[cchPath - 4] = 0; + return S_OK; + } + } + + return S_FALSE; +} + +/** + * PathCchRemoveFileSpec + */ + +HRESULT PathCchRemoveFileSpecA(PSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +HRESULT PathCchRemoveFileSpecW(PWSTR pszPath, size_t cchPath) +{ + WLog_ERR(TAG, "not implemented"); + return E_NOTIMPL; +} + +/* + * Path Portability Functions + */ + +/** + * PathCchConvertStyle + */ + +HRESULT PathCchConvertStyleA(PSTR pszPath, size_t cchPath, unsigned long dwFlags) +{ + if (dwFlags == PATH_STYLE_WINDOWS) + { + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_SLASH_CHR) + pszPath[index] = PATH_BACKSLASH_CHR; + } + } + else if (dwFlags == PATH_STYLE_UNIX) + { + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_BACKSLASH_CHR) + pszPath[index] = PATH_SLASH_CHR; + } + } + else if (dwFlags == PATH_STYLE_NATIVE) + { +#if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR) + /* Unix-style to Windows-style */ + + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_SLASH_CHR) + pszPath[index] = PATH_BACKSLASH_CHR; + } +#elif (PATH_SEPARATOR_CHR == PATH_SLASH_CHR) + /* Windows-style to Unix-style */ + + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_BACKSLASH_CHR) + pszPath[index] = PATH_SLASH_CHR; + } +#else + /* Unexpected error */ + return E_FAIL; +#endif + } + else + { + /* Gangnam style? */ + return E_FAIL; + } + + return S_OK; +} + +HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlags) +{ + if (dwFlags == PATH_STYLE_WINDOWS) + { + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_SLASH_CHR) + pszPath[index] = PATH_BACKSLASH_CHR; + } + } + else if (dwFlags == PATH_STYLE_UNIX) + { + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_BACKSLASH_CHR) + pszPath[index] = PATH_SLASH_CHR; + } + } + else if (dwFlags == PATH_STYLE_NATIVE) + { +#if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR) + { + /* Unix-style to Windows-style */ + + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_SLASH_CHR) + pszPath[index] = PATH_BACKSLASH_CHR; + } + } +#elif (PATH_SEPARATOR_CHR == PATH_SLASH_CHR) + { + /* Windows-style to Unix-style */ + + for (size_t index = 0; index < cchPath; index++) + { + if (pszPath[index] == PATH_BACKSLASH_CHR) + pszPath[index] = PATH_SLASH_CHR; + } + } +#else + { + /* Unexpected error */ + return E_FAIL; + } +#endif + } + else + { + /* Gangnam style? */ + return E_FAIL; + } + + return S_OK; +} + +/** + * PathGetSeparator + */ + +char PathGetSeparatorA(unsigned long dwFlags) +{ + char separator = PATH_SEPARATOR_CHR; + + if (!dwFlags) + dwFlags = PATH_STYLE_NATIVE; + + if (dwFlags == PATH_STYLE_WINDOWS) + separator = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_UNIX) + separator = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_NATIVE) + separator = PATH_SEPARATOR_CHR; + + return separator; +} + +WCHAR PathGetSeparatorW(unsigned long dwFlags) +{ + union + { + WCHAR w; + char c[2]; + } cnv; + + cnv.c[0] = PATH_SEPARATOR_CHR; + cnv.c[1] = '\0'; + + if (!dwFlags) + dwFlags = PATH_STYLE_NATIVE; + + if (dwFlags == PATH_STYLE_WINDOWS) + cnv.c[0] = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_UNIX) + cnv.c[0] = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_NATIVE) + cnv.c[0] = PATH_SEPARATOR_CHR; + + return cnv.w; +} + +/** + * PathGetSharedLibraryExtension + */ +static const CHAR SharedLibraryExtensionDllA[] = "dll"; +static const CHAR SharedLibraryExtensionSoA[] = "so"; +static const CHAR SharedLibraryExtensionDylibA[] = "dylib"; + +static const CHAR SharedLibraryExtensionDotDllA[] = ".dll"; +static const CHAR SharedLibraryExtensionDotSoA[] = ".so"; +static const CHAR SharedLibraryExtensionDotDylibA[] = ".dylib"; +PCSTR PathGetSharedLibraryExtensionA(unsigned long dwFlags) +{ + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT) + { + if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT) + { + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL) + return SharedLibraryExtensionDotDllA; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO) + return SharedLibraryExtensionDotSoA; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB) + return SharedLibraryExtensionDotDylibA; + } + else + { + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL) + return SharedLibraryExtensionDllA; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO) + return SharedLibraryExtensionSoA; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB) + return SharedLibraryExtensionDylibA; + } + } + + if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT) + { +#ifdef _WIN32 + return SharedLibraryExtensionDotDllA; +#elif defined(__APPLE__) + if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO) + return SharedLibraryExtensionDotSoA; + else + return SharedLibraryExtensionDotDylibA; +#else + return SharedLibraryExtensionDotSoA; +#endif + } + else + { +#ifdef _WIN32 + return SharedLibraryExtensionDllA; +#elif defined(__APPLE__) + if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO) + return SharedLibraryExtensionSoA; + else + return SharedLibraryExtensionDylibA; +#else + return SharedLibraryExtensionSoA; +#endif + } + + return NULL; +} + +PCWSTR PathGetSharedLibraryExtensionW(unsigned long dwFlags) +{ + WCHAR buffer[6][16] = { 0 }; + const WCHAR* SharedLibraryExtensionDotDllW = InitializeConstWCharFromUtf8( + SharedLibraryExtensionDotDllA, buffer[0], ARRAYSIZE(buffer[0])); + const WCHAR* SharedLibraryExtensionDotSoW = + InitializeConstWCharFromUtf8(SharedLibraryExtensionDotSoA, buffer[1], ARRAYSIZE(buffer[1])); + const WCHAR* SharedLibraryExtensionDotDylibW = InitializeConstWCharFromUtf8( + SharedLibraryExtensionDotDylibA, buffer[2], ARRAYSIZE(buffer[2])); + const WCHAR* SharedLibraryExtensionDllW = + InitializeConstWCharFromUtf8(SharedLibraryExtensionDllA, buffer[3], ARRAYSIZE(buffer[3])); + const WCHAR* SharedLibraryExtensionSoW = + InitializeConstWCharFromUtf8(SharedLibraryExtensionSoA, buffer[4], ARRAYSIZE(buffer[4])); + const WCHAR* SharedLibraryExtensionDylibW = + InitializeConstWCharFromUtf8(SharedLibraryExtensionDylibA, buffer[5], ARRAYSIZE(buffer[5])); + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT) + { + if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT) + { + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL) + return SharedLibraryExtensionDotDllW; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO) + return SharedLibraryExtensionDotSoW; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB) + return SharedLibraryExtensionDotDylibW; + } + else + { + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL) + return SharedLibraryExtensionDllW; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO) + return SharedLibraryExtensionSoW; + + if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB) + return SharedLibraryExtensionDylibW; + } + } + + if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT) + { +#ifdef _WIN32 + return SharedLibraryExtensionDotDllW; +#elif defined(__APPLE__) + if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO) + return SharedLibraryExtensionDotSoW; + else + return SharedLibraryExtensionDotDylibW; +#else + return SharedLibraryExtensionDotSoW; +#endif + } + else + { +#ifdef _WIN32 + return SharedLibraryExtensionDllW; +#elif defined(__APPLE__) + if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO) + return SharedLibraryExtensionSoW; + else + return SharedLibraryExtensionDylibW; +#else + return SharedLibraryExtensionSoW; +#endif + } + + return NULL; +} + +const char* GetKnownPathIdString(int id) +{ + switch (id) + { + case KNOWN_PATH_HOME: + return "KNOWN_PATH_HOME"; + case KNOWN_PATH_TEMP: + return "KNOWN_PATH_TEMP"; + case KNOWN_PATH_XDG_DATA_HOME: + return "KNOWN_PATH_XDG_DATA_HOME"; + case KNOWN_PATH_XDG_CONFIG_HOME: + return "KNOWN_PATH_XDG_CONFIG_HOME"; + case KNOWN_PATH_XDG_CACHE_HOME: + return "KNOWN_PATH_XDG_CACHE_HOME"; + case KNOWN_PATH_XDG_RUNTIME_DIR: + return "KNOWN_PATH_XDG_RUNTIME_DIR"; + default: + return "KNOWN_PATH_UNKNOWN_ID"; + } +} + +static WCHAR* concat(const WCHAR* path, size_t pathlen, const WCHAR* name, size_t namelen) +{ + WCHAR* str = calloc(pathlen + namelen + 1, sizeof(WCHAR)); + if (!str) + return NULL; + + _wcsncat(str, path, pathlen); + _wcsncat(str, name, namelen); + return str; +} + +BOOL winpr_RemoveDirectory_RecursiveA(LPCSTR lpPathName) +{ + WCHAR* name = ConvertUtf8ToWCharAlloc(lpPathName, NULL); + if (!name) + return FALSE; + const BOOL rc = winpr_RemoveDirectory_RecursiveW(name); + free(name); + return rc; +} + +BOOL winpr_RemoveDirectory_RecursiveW(LPCWSTR lpPathName) +{ + BOOL ret = FALSE; + + if (!lpPathName) + return FALSE; + + const size_t pathnamelen = _wcslen(lpPathName); + const size_t path_slash_len = pathnamelen + 3; + WCHAR* path_slash = calloc(pathnamelen + 4, sizeof(WCHAR)); + if (!path_slash) + return FALSE; + _wcsncat(path_slash, lpPathName, pathnamelen); + + WCHAR starbuffer[8] = { 0 }; + const WCHAR* star = InitializeConstWCharFromUtf8("*", starbuffer, ARRAYSIZE(starbuffer)); + const HRESULT hr = NativePathCchAppendW(path_slash, path_slash_len, star); + if (FAILED(hr)) + goto fail; + + WIN32_FIND_DATAW findFileData = { 0 }; + HANDLE dir = FindFirstFileW(path_slash, &findFileData); + + if (dir == INVALID_HANDLE_VALUE) + goto fail; + + ret = TRUE; + path_slash[path_slash_len - 1] = '\0'; /* remove trailing '*' */ + do + { + const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName)); + + if ((len == 1 && findFileData.cFileName[0] == '.') || + (len == 2 && findFileData.cFileName[0] == '.' && findFileData.cFileName[1] == '.')) + { + continue; + } + + WCHAR* fullpath = concat(path_slash, path_slash_len, findFileData.cFileName, len); + if (!fullpath) + goto fail; + + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ret = winpr_RemoveDirectory_RecursiveW(fullpath); + else + ret = DeleteFileW(fullpath); + + free(fullpath); + + if (!ret) + break; + } while (ret && FindNextFileW(dir, &findFileData) != 0); + + FindClose(dir); + + if (ret) + { + if (!RemoveDirectoryW(lpPathName)) + ret = FALSE; + } + +fail: + free(path_slash); + return ret; +} diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c new file mode 100644 index 0000000..4380a9b --- /dev/null +++ b/winpr/libwinpr/path/shell.c @@ -0,0 +1,821 @@ +/** + * WinPR: Windows Portable Runtime + * Path Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "../log.h" +#define TAG WINPR_TAG("path.shell") + +#if defined(__IOS__) +#include "shell_ios.h" +#endif + +#if defined(WIN32) +#include +#else +#include +#include +#endif + +static char* GetPath_XDG_CONFIG_HOME(void); +static char* GetPath_XDG_RUNTIME_DIR(void); + +/** + * SHGetKnownFolderPath function: + * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/ + */ + +/** + * XDG Base Directory Specification: + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + +char* GetEnvAlloc(LPCSTR lpName) +{ + DWORD nSize = 0; + DWORD nStatus = 0; + char* env = NULL; + + nSize = GetEnvironmentVariableX(lpName, NULL, 0); + + if (nSize > 0) + { + env = malloc(nSize); + + if (!env) + return NULL; + + nStatus = GetEnvironmentVariableX(lpName, env, nSize); + + if (nStatus != (nSize - 1)) + { + free(env); + return NULL; + } + } + + return env; +} + +static char* GetPath_HOME(void) +{ + char* path = NULL; +#ifdef _WIN32 + path = GetEnvAlloc("UserProfile"); +#elif defined(__IOS__) + path = ios_get_home(); +#else + path = GetEnvAlloc("HOME"); +#endif + return path; +} + +static char* GetPath_TEMP(void) +{ + char* path = NULL; +#ifdef _WIN32 + path = GetEnvAlloc("TEMP"); +#elif defined(__IOS__) + path = ios_get_temp(); +#else + path = GetEnvAlloc("TMPDIR"); + + if (!path) + path = _strdup("/tmp"); + +#endif + return path; +} + +static char* GetPath_XDG_DATA_HOME(void) +{ + char* path = NULL; +#if defined(WIN32) || defined(__IOS__) + path = GetPath_XDG_CONFIG_HOME(); +#else + size_t size = 0; + char* home = NULL; + /** + * There is a single base directory relative to which user-specific data files should be + * written. This directory is defined by the environment variable $XDG_DATA_HOME. + * + * $XDG_DATA_HOME defines the base directory relative to which user specific data files should + * be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to + * $HOME/.local/share should be used. + */ + path = GetEnvAlloc("XDG_DATA_HOME"); + + if (path) + return path; + + home = GetPath_HOME(); + + if (!home) + return NULL; + + size = strlen(home) + strlen("/.local/share") + 1; + path = (char*)malloc(size); + + if (!path) + { + free(home); + return NULL; + } + + sprintf_s(path, size, "%s%s", home, "/.local/share"); + free(home); +#endif + return path; +} + +static char* GetPath_XDG_CONFIG_HOME(void) +{ + char* path = NULL; +#if defined(WIN32) && !defined(_UWP) + path = calloc(MAX_PATH, sizeof(char)); + + if (!path) + return NULL; + + if (FAILED(SHGetFolderPathA(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path))) + { + free(path); + return NULL; + } + +#elif defined(__IOS__) + path = ios_get_data(); +#else + size_t size = 0; + char* home = NULL; + /** + * There is a single base directory relative to which user-specific configuration files should + * be written. This directory is defined by the environment variable $XDG_CONFIG_HOME. + * + * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration + * files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to + * $HOME/.config should be used. + */ + path = GetEnvAlloc("XDG_CONFIG_HOME"); + + if (path) + return path; + + home = GetPath_HOME(); + + if (!home) + home = GetPath_TEMP(); + + if (!home) + return NULL; + + size = strlen(home) + strlen("/.config") + 1; + path = (char*)malloc(size); + + if (!path) + { + free(home); + return NULL; + } + + sprintf_s(path, size, "%s%s", home, "/.config"); + free(home); +#endif + return path; +} + +static char* GetPath_XDG_CACHE_HOME(void) +{ + char* path = NULL; + char* home = NULL; +#if defined(WIN32) + home = GetPath_XDG_RUNTIME_DIR(); + + if (home) + { + path = GetCombinedPath(home, "cache"); + + if (!winpr_PathFileExists(path)) + if (!CreateDirectoryA(path, NULL)) + path = NULL; + } + + free(home); +#elif defined(__IOS__) + path = ios_get_cache(); +#else + size_t size = 0; + /** + * There is a single base directory relative to which user-specific non-essential (cached) data + * should be written. This directory is defined by the environment variable $XDG_CACHE_HOME. + * + * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data + * files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to + * $HOME/.cache should be used. + */ + path = GetEnvAlloc("XDG_CACHE_HOME"); + + if (path) + return path; + + home = GetPath_HOME(); + + if (!home) + return NULL; + + size = strlen(home) + strlen("/.cache") + 1; + path = (char*)malloc(size); + + if (!path) + { + free(home); + return NULL; + } + + sprintf_s(path, size, "%s%s", home, "/.cache"); + free(home); +#endif + return path; +} + +char* GetPath_XDG_RUNTIME_DIR(void) +{ + char* path = NULL; +#if defined(WIN32) && !defined(_UWP) + path = calloc(MAX_PATH, sizeof(char)); + + if (!path) + return NULL; + + if (FAILED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path))) + { + free(path); + return NULL; + } + +#else + /** + * There is a single base directory relative to which user-specific runtime files and other file + * objects should be placed. This directory is defined by the environment variable + * $XDG_RUNTIME_DIR. + * + * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential + * runtime files and other file objects (such as sockets, named pipes, ...) should be stored. + * The directory MUST be owned by the user, and he MUST be the only one having read and write + * access to it. Its Unix access mode MUST be 0700. + * + * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created + * when the user first logs in and if the user fully logs out the directory MUST be removed. If + * the user logs in more than once he should get pointed to the same directory, and it is + * mandatory that the directory continues to exist from his first login to his last logout on + * the system, and not removed in between. Files in the directory MUST not survive reboot or a + * full logout/login cycle. + * + * The directory MUST be on a local file system and not shared with any other system. The + * directory MUST by fully-featured by the standards of the operating system. More specifically, + * on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper + * permissions, file locking, sparse files, memory mapping, file change notifications, a + * reliable hard link count must be supported, and no restrictions on the file name character + * set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To + * ensure that your files are not removed, they should have their access time timestamp modified + * at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file. + * + * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with + * similar capabilities and print a warning message. Applications should use this directory for + * communication and synchronization purposes and should not place larger files in it, since it + * might reside in runtime memory and cannot necessarily be swapped out to disk. + */ + path = GetEnvAlloc("XDG_RUNTIME_DIR"); +#endif + + if (path) + return path; + + path = GetPath_TEMP(); + return path; +} + +char* GetKnownPath(int id) +{ + char* path = NULL; + + switch (id) + { + case KNOWN_PATH_HOME: + path = GetPath_HOME(); + break; + + case KNOWN_PATH_TEMP: + path = GetPath_TEMP(); + break; + + case KNOWN_PATH_XDG_DATA_HOME: + path = GetPath_XDG_DATA_HOME(); + break; + + case KNOWN_PATH_XDG_CONFIG_HOME: + path = GetPath_XDG_CONFIG_HOME(); + break; + + case KNOWN_PATH_XDG_CACHE_HOME: + path = GetPath_XDG_CACHE_HOME(); + break; + + case KNOWN_PATH_XDG_RUNTIME_DIR: + path = GetPath_XDG_RUNTIME_DIR(); + break; + + default: + path = NULL; + break; + } + + if (!path) + WLog_WARN(TAG, "Path %s is %p", GetKnownPathIdString(id), path); + return path; +} + +char* GetKnownSubPath(int id, const char* path) +{ + char* subPath = NULL; + char* knownPath = NULL; + knownPath = GetKnownPath(id); + + if (!knownPath) + return NULL; + + subPath = GetCombinedPath(knownPath, path); + free(knownPath); + return subPath; +} + +char* GetEnvironmentPath(char* name) +{ + char* env = NULL; + DWORD nSize = 0; + DWORD nStatus = 0; + nSize = GetEnvironmentVariableX(name, NULL, 0); + + if (nSize) + { + env = (LPSTR)malloc(nSize); + + if (!env) + return NULL; + + nStatus = GetEnvironmentVariableX(name, env, nSize); + + if (nStatus != (nSize - 1)) + { + free(env); + return NULL; + } + } + + return env; +} + +char* GetEnvironmentSubPath(char* name, const char* path) +{ + char* env = NULL; + char* subpath = NULL; + env = GetEnvironmentPath(name); + + if (!env) + return NULL; + + subpath = GetCombinedPath(env, path); + free(env); + return subpath; +} + +char* GetCombinedPath(const char* basePath, const char* subPath) +{ + size_t length = 0; + HRESULT status = 0; + char* path = NULL; + char* subPathCpy = NULL; + size_t basePathLength = 0; + size_t subPathLength = 0; + + if (basePath) + basePathLength = strlen(basePath); + + if (subPath) + subPathLength = strlen(subPath); + + length = basePathLength + subPathLength + 1; + path = (char*)calloc(1, length + 1); + + if (!path) + goto fail; + + if (basePath) + CopyMemory(path, basePath, basePathLength); + + if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE))) + goto fail; + + if (!subPath) + return path; + + subPathCpy = _strdup(subPath); + + if (!subPathCpy) + goto fail; + + if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE))) + goto fail; + + status = NativePathCchAppendA(path, length + 1, subPathCpy); + if (FAILED(status)) + goto fail; + + free(subPathCpy); + return path; + +fail: + free(path); + free(subPathCpy); + return NULL; +} + +BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes) +{ +#if defined(_UWP) + return FALSE; +#elif defined(_WIN32) + return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS); +#else + const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE); + char* dup = NULL; + BOOL result = TRUE; + /* we only operate on a non-null, absolute path */ +#if defined(__OS2__) + + if (!path) + return FALSE; + +#else + + if (!path || *path != delim) + return FALSE; + +#endif + + if (!(dup = _strdup(path))) + return FALSE; + +#ifdef __OS2__ + p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup; + + while (p) +#else + for (char* p = dup; p;) +#endif + { + if ((p = strchr(p + 1, delim))) + *p = '\0'; + + if (mkdir(dup, 0777) != 0) + if (errno != EEXIST) + { + result = FALSE; + break; + } + + if (p) + *p = delim; + } + + free(dup); + return (result); +#endif +} + +BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes) +{ +#if defined(_UWP) + return FALSE; +#elif defined(_WIN32) + return (SHCreateDirectoryExW(NULL, path, lpAttributes) == ERROR_SUCCESS); +#else + const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE); + const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE); + char* dup = NULL; + BOOL result = TRUE; + /* we only operate on a non-null, absolute path */ +#if defined(__OS2__) + + if (!path) + return FALSE; + +#else + + if (!path || *path != wdelim) + return FALSE; + +#endif + + dup = ConvertWCharToUtf8Alloc(path, NULL); + if (!dup) + return FALSE; + +#ifdef __OS2__ + p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup; + + while (p) +#else + for (char* p = dup; p;) +#endif + { + if ((p = strchr(p + 1, delim))) + *p = '\0'; + + if (mkdir(dup, 0777) != 0) + if (errno != EEXIST) + { + result = FALSE; + break; + } + + if (p) + *p = delim; + } + + free(dup); + return (result); +#endif +} + +#if !defined(_WIN32) || defined(_UWP) + +BOOL PathIsRelativeA(LPCSTR pszPath) +{ + if (!pszPath) + return FALSE; + + return pszPath[0] != '/'; +} + +BOOL PathIsRelativeW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret = FALSE; + + if (!pszPath) + goto fail; + + lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL); + if (!lpFileNameA) + goto fail; + ret = PathIsRelativeA(lpFileNameA); +fail: + free(lpFileNameA); + return ret; +} + +BOOL PathFileExistsA(LPCSTR pszPath) +{ + struct stat stat_info; + + if (stat(pszPath, &stat_info) != 0) + return FALSE; + + return TRUE; +} + +BOOL PathFileExistsW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret = FALSE; + + if (!pszPath) + goto fail; + lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL); + if (!lpFileNameA) + goto fail; + + ret = winpr_PathFileExists(lpFileNameA); +fail: + free(lpFileNameA); + return ret; +} + +BOOL PathIsDirectoryEmptyA(LPCSTR pszPath) +{ + struct dirent* dp = NULL; + int empty = 1; + DIR* dir = opendir(pszPath); + + if (dir == NULL) /* Not a directory or doesn't exist */ + return 1; + + while ((dp = readdir(dir)) != NULL) + { + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; /* Skip . and .. */ + + empty = 0; + break; + } + + closedir(dir); + return empty; +} + +BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret = FALSE; + if (!pszPath) + goto fail; + lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL); + if (!lpFileNameA) + goto fail; + ret = PathIsDirectoryEmptyA(lpFileNameA); +fail: + free(lpFileNameA); + return ret; +} + +#else + +#ifdef _MSC_VER +#pragma comment(lib, "shlwapi.lib") +#endif + +#endif + +BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName) +{ +#ifndef _WIN32 + return MoveFileA(lpExistingFileName, lpNewFileName); +#else + BOOL result = FALSE; + LPWSTR lpExistingFileNameW = NULL; + LPWSTR lpNewFileNameW = NULL; + + if (!lpExistingFileName || !lpNewFileName) + return FALSE; + + lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL); + if (!lpExistingFileNameW) + goto cleanup; + lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL); + if (!lpNewFileNameW) + goto cleanup; + + result = MoveFileW(lpExistingFileNameW, lpNewFileNameW); + +cleanup: + free(lpExistingFileNameW); + free(lpNewFileNameW); + return result; +#endif +} + +BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags) +{ +#ifndef _WIN32 + return MoveFileExA(lpExistingFileName, lpNewFileName, dwFlags); +#else + BOOL result = FALSE; + LPWSTR lpExistingFileNameW = NULL; + LPWSTR lpNewFileNameW = NULL; + + if (!lpExistingFileName || !lpNewFileName) + return FALSE; + + lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL); + if (!lpExistingFileNameW) + goto cleanup; + lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL); + if (!lpNewFileNameW) + goto cleanup; + + result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags); + +cleanup: + free(lpExistingFileNameW); + free(lpNewFileNameW); + return result; +#endif +} + +BOOL winpr_DeleteFile(const char* lpFileName) +{ +#ifndef _WIN32 + return DeleteFileA(lpFileName); +#else + LPWSTR lpFileNameW = NULL; + BOOL result = FALSE; + + if (lpFileName) + { + lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL); + if (!lpFileNameW) + goto cleanup; + } + + result = DeleteFileW(lpFileNameW); + +cleanup: + free(lpFileNameW); + return result; +#endif +} + +BOOL winpr_RemoveDirectory(LPCSTR lpPathName) +{ +#ifndef _WIN32 + return RemoveDirectoryA(lpPathName); +#else + LPWSTR lpPathNameW = NULL; + BOOL result = FALSE; + + if (lpPathName) + { + lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, NULL); + if (!lpPathNameW) + goto cleanup; + } + + result = RemoveDirectoryW(lpPathNameW); + +cleanup: + free(lpPathNameW); + return result; +#endif +} + +BOOL winpr_PathFileExists(const char* pszPath) +{ + if (!pszPath) + return FALSE; +#ifndef _WIN32 + return PathFileExistsA(pszPath); +#else + WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, NULL); + BOOL result = FALSE; + + if (!pathW) + return FALSE; + + result = PathFileExistsW(pathW); + free(pathW); + + return result; +#endif +} + +BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes) +{ + if (!path) + return FALSE; +#ifndef _WIN32 + return PathMakePathA(path, lpAttributes); +#else + WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, NULL); + BOOL result = FALSE; + + if (!pathW) + return FALSE; + + result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS; + free(pathW); + + return result; +#endif +} diff --git a/winpr/libwinpr/path/shell_ios.h b/winpr/libwinpr/path/shell_ios.h new file mode 100644 index 0000000..3144d8d --- /dev/null +++ b/winpr/libwinpr/path/shell_ios.h @@ -0,0 +1,9 @@ +#ifndef SHELL_IOS_H_ +#define SHELL_IOS_H_ + +char* ios_get_home(void); +char* ios_get_temp(void); +char* ios_get_data(void); +char* ios_get_cache(void); + +#endif diff --git a/winpr/libwinpr/path/shell_ios.m b/winpr/libwinpr/path/shell_ios.m new file mode 100644 index 0000000..7e1185b --- /dev/null +++ b/winpr/libwinpr/path/shell_ios.m @@ -0,0 +1,54 @@ +/** + * WinPR: Windows Portable Runtime + * Path Functions + * + * Copyright 2016 Armin Novak + * Copyright 2016 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#include + +#include "shell_ios.h" + +NSString *ios_get_directory_for_search_path(NSSearchPathDirectory searchPath) +{ + return [NSSearchPathForDirectoriesInDomains(searchPath, NSUserDomainMask, YES) lastObject]; +} + +char *ios_get_home(void) +{ + NSString *path = ios_get_directory_for_search_path(NSDocumentDirectory); + return strdup([path UTF8String]); +} + +char *ios_get_temp(void) +{ + NSString *tmp_path = NSTemporaryDirectory(); + return strdup([tmp_path UTF8String]); +} + +char *ios_get_data(void) +{ + NSString *path = ios_get_directory_for_search_path(NSApplicationSupportDirectory); + return strdup([path UTF8String]); +} + +char *ios_get_cache(void) +{ + NSString *path = ios_get_directory_for_search_path(NSCachesDirectory); + return strdup([path UTF8String]); +} diff --git a/winpr/libwinpr/path/test/CMakeLists.txt b/winpr/libwinpr/path/test/CMakeLists.txt new file mode 100644 index 0000000..974907e --- /dev/null +++ b/winpr/libwinpr/path/test/CMakeLists.txt @@ -0,0 +1,48 @@ + +set(MODULE_NAME "TestPath") +set(MODULE_PREFIX "TEST_PATH") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestPathCchAddBackslash.c + TestPathCchRemoveBackslash.c + TestPathCchAddBackslashEx.c + TestPathCchRemoveBackslashEx.c + TestPathCchAddExtension.c + TestPathCchAppend.c + TestPathCchAppendEx.c + TestPathCchCanonicalize.c + TestPathCchCanonicalizeEx.c + TestPathAllocCanonicalize.c + TestPathCchCombine.c + TestPathCchCombineEx.c + TestPathAllocCombine.c + TestPathCchFindExtension.c + TestPathCchRenameExtension.c + TestPathCchRemoveExtension.c + TestPathCchIsRoot.c + TestPathIsUNCEx.c + TestPathCchSkipRoot.c + TestPathCchStripToRoot.c + TestPathCchStripPrefix.c + TestPathCchRemoveFileSpec.c + TestPathShell.c + TestPathMakePath.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/path/test/TestPathAllocCanonicalize.c b/winpr/libwinpr/path/test/TestPathAllocCanonicalize.c new file mode 100644 index 0000000..d04fff1 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathAllocCanonicalize.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathAllocCanonicalize(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathAllocCombine.c b/winpr/libwinpr/path/test/TestPathAllocCombine.c new file mode 100644 index 0000000..4630df0 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathAllocCombine.c @@ -0,0 +1,98 @@ + +#include +#include +#include +#include +#include + +static const TCHAR testBasePathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testBasePathNoBackslash[] = _T("C:\\Program Files"); +static const TCHAR testMorePathBackslash[] = _T("\\Microsoft Visual Studio 11.0"); +static const TCHAR testMorePathNoBackslash[] = _T("Microsoft Visual Studio 11.0"); +static const TCHAR testPathOut[] = _T("C:\\Program Files\\Microsoft Visual Studio 11.0"); +static const TCHAR testPathOutMorePathBackslash[] = _T("C:\\Microsoft Visual Studio 11.0"); + +int TestPathAllocCombine(int argc, char* argv[]) +{ + HRESULT status = 0; + LPTSTR PathOut = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Base Path: Backslash, More Path: No Backslash */ + + status = PathAllocCombine(testBasePathBackslash, testMorePathNoBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch 1: Actual: %s, Expected: %s\n"), PathOut, testPathOut); + return -1; + } + + free(PathOut); + + /* Base Path: Backslash, More Path: Backslash */ + + status = PathAllocCombine(testBasePathBackslash, testMorePathBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOutMorePathBackslash) != 0) + { + _tprintf(_T("Path Mismatch 2: Actual: %s, Expected: %s\n"), PathOut, + testPathOutMorePathBackslash); + return -1; + } + + free(PathOut); + + /* Base Path: No Backslash, More Path: Backslash */ + + status = PathAllocCombine(testBasePathNoBackslash, testMorePathBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOutMorePathBackslash) != 0) + { + _tprintf(_T("Path Mismatch 3: Actual: %s, Expected: %s\n"), PathOut, + testPathOutMorePathBackslash); + return -1; + } + + free(PathOut); + + /* Base Path: No Backslash, More Path: No Backslash */ + + status = PathAllocCombine(testBasePathNoBackslash, testMorePathNoBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch 4: Actual: %s, Expected: %s\n"), PathOut, testPathOut); + return -1; + } + + free(PathOut); + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAddBackslash.c b/winpr/libwinpr/path/test/TestPathCchAddBackslash.c new file mode 100644 index 0000000..0a414e2 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAddBackslash.c @@ -0,0 +1,100 @@ + +#include +#include +#include +#include +#include + +static const TCHAR testPathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testPathNoBackslash[] = _T("C:\\Program Files"); + +int TestPathCchAddBackslash(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * PathCchAddBackslash returns S_OK if the function was successful, + * S_FALSE if the path string already ends in a backslash, + * or an error code otherwise. + */ + + _tcscpy(Path, testPathNoBackslash); + + /* Add a backslash to a path without a trailing backslash, expect S_OK */ + + status = PathCchAddBackslash(Path, PATHCCH_MAX_CCH); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Add a backslash to a path with a trailing backslash, expect S_FALSE */ + + _tcscpy(Path, testPathBackslash); + + status = PathCchAddBackslash(Path, PATHCCH_MAX_CCH); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Use NULL PSTR, expect FAILED(status) */ + + status = PathCchAddBackslash(NULL, PATHCCH_MAX_CCH); + + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddBackslash unexpectedly succeded with null buffer. Status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* Use insufficient size value, expect FAILED(status) */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslash(Path, 7); + + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddBackslash unexpectedly succeded with insufficient buffer size. ") + _T("Status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Use minimum required size value, expect S_OK */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslash(Path, 8); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslash failed with status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c b/winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c new file mode 100644 index 0000000..4a84200 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c @@ -0,0 +1,103 @@ + +#include +#include +#include +#include +#include + +static const TCHAR testPathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testPathNoBackslash[] = _T("C:\\Program Files"); + +int TestPathCchAddBackslashEx(int argc, char* argv[]) +{ + HRESULT status = 0; + LPTSTR pszEnd = NULL; + size_t cchRemaining = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * PathCchAddBackslashEx returns S_OK if the function was successful, + * S_FALSE if the path string already ends in a backslash, + * or an error code otherwise. + */ + + _tcscpy(Path, testPathNoBackslash); + + /* Add a backslash to a path without a trailing backslash, expect S_OK */ + + status = PathCchAddBackslashEx(Path, sizeof(Path) / sizeof(TCHAR), &pszEnd, &cchRemaining); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Add a backslash to a path with a trailing backslash, expect S_FALSE */ + + _tcscpy(Path, testPathBackslash); + + status = PathCchAddBackslashEx(Path, sizeof(Path) / sizeof(TCHAR), &pszEnd, &cchRemaining); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Use NULL PSTR, expect FAILED(status) */ + + status = PathCchAddBackslashEx(NULL, PATHCCH_MAX_CCH, NULL, NULL); + + if (SUCCEEDED(status)) + { + _tprintf( + _T("PathCchAddBackslashEx unexpectedly succeded with null buffer. Status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* Use insufficient size value, expect FAILED(status) */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslashEx(Path, 7, NULL, NULL); + + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddBackslashEx unexpectedly succeded with insufficient buffer size. ") + _T("Status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Use minimum required size value, expect S_OK */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslashEx(Path, 8, NULL, NULL); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslashEx failed with status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAddExtension.c b/winpr/libwinpr/path/test/TestPathCchAddExtension.c new file mode 100644 index 0000000..71f5ddf --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAddExtension.c @@ -0,0 +1,140 @@ + +#include +#include +#include +#include +#include + +static const TCHAR testExtDot[] = _T(".exe"); +static const TCHAR testExtNoDot[] = _T("exe"); +static const TCHAR testPathNoExtension[] = _T("C:\\Windows\\System32\\cmd"); +static const TCHAR testPathExtension[] = _T("C:\\Windows\\System32\\cmd.exe"); + +int TestPathCchAddExtension(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Path: no extension, Extension: dot */ + + _tcscpy(Path, testPathNoExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtDot); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: no extension, Extension: no dot */ + + _tcscpy(Path, testPathNoExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtNoDot); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: extension, Extension: dot */ + + _tcscpy(Path, testPathExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtDot); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: extension, Extension: no dot */ + + _tcscpy(Path, testPathExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtDot); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: NULL */ + + status = PathCchAddExtension(NULL, PATHCCH_MAX_CCH, testExtDot); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAddExtension with null buffer returned status: 0x%08") _T( + PRIX32) _T(" (expected E_INVALIDARG)\n"), + status); + return -1; + } + + /* Extension: NULL */ + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, NULL); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAddExtension with null extension returned status: 0x%08") _T( + PRIX32) _T(" (expected E_INVALIDARG)\n"), + status); + return -1; + } + + /* Insufficient Buffer size */ + + _tcscpy(Path, _T("C:\\456789")); + status = PathCchAddExtension(Path, 9 + 4, _T(".jpg")); + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddExtension with insufficient buffer unexpectedly succeeded with ") + _T("status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Minimum required buffer size */ + + _tcscpy(Path, _T("C:\\456789")); + status = PathCchAddExtension(Path, 9 + 4 + 1, _T(".jpg")); + if (FAILED(status)) + { + _tprintf(_T("PathCchAddExtension with sufficient buffer unexpectedly failed with status: ") + _T("0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAppend.c b/winpr/libwinpr/path/test/TestPathCchAppend.c new file mode 100644 index 0000000..93524ca --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAppend.c @@ -0,0 +1,151 @@ + +#include +#include +#include +#include +#include + +static const TCHAR testBasePathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testBasePathNoBackslash[] = _T("C:\\Program Files"); +static const TCHAR testMorePathBackslash[] = _T("\\Microsoft Visual Studio 11.0"); +static const TCHAR testMorePathNoBackslash[] = _T("Microsoft Visual Studio 11.0"); +static const TCHAR testPathOut[] = _T("C:\\Program Files\\Microsoft Visual Studio 11.0"); + +int TestPathCchAppend(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Base Path: Backslash, More Path: No Backslash */ + + _tcscpy(Path, testBasePathBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathNoBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* Base Path: Backslash, More Path: Backslash */ + + _tcscpy(Path, testBasePathBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* Base Path: No Backslash, More Path: Backslash */ + + _tcscpy(Path, testBasePathNoBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* Base Path: No Backslash, More Path: No Backslash */ + + _tcscpy(Path, testBasePathNoBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathNoBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* According to msdn a NULL Path is an invalid argument */ + status = PathCchAppend(NULL, PATHCCH_MAX_CCH, testMorePathNoBackslash); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with NULL path unexpectedly returned status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* According to msdn a NULL pszMore is an invalid argument (although optional !?) */ + _tcscpy(Path, testBasePathNoBackslash); + status = PathCchAppend(Path, PATHCCH_MAX_CCH, NULL); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with NULL pszMore unexpectedly returned status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* According to msdn cchPath must be > 0 and <= PATHCCH_MAX_CCH */ + _tcscpy(Path, testBasePathNoBackslash); + status = PathCchAppend(Path, 0, testMorePathNoBackslash); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with cchPath value 0 unexpectedly returned status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + _tcscpy(Path, testBasePathNoBackslash); + status = PathCchAppend(Path, PATHCCH_MAX_CCH + 1, testMorePathNoBackslash); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with cchPath value > PATHCCH_MAX_CCH unexpectedly returned ") + _T("status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Resulting file must not exceed PATHCCH_MAX_CCH */ + + for (size_t i = 0; i < PATHCCH_MAX_CCH - 1; i++) + Path[i] = _T('X'); + + Path[PATHCCH_MAX_CCH - 1] = 0; + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, _T("\\This cannot be appended to Path")); + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAppend unexepectedly succeeded with status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAppendEx.c b/winpr/libwinpr/path/test/TestPathCchAppendEx.c new file mode 100644 index 0000000..b6d83f5 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAppendEx.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchAppendEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCanonicalize.c b/winpr/libwinpr/path/test/TestPathCchCanonicalize.c new file mode 100644 index 0000000..a7fa4ce --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCanonicalize.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchCanonicalize(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c b/winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c new file mode 100644 index 0000000..2d670eb --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchCanonicalizeEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCombine.c b/winpr/libwinpr/path/test/TestPathCchCombine.c new file mode 100644 index 0000000..4b6f4e4 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCombine.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchCombine(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCombineEx.c b/winpr/libwinpr/path/test/TestPathCchCombineEx.c new file mode 100644 index 0000000..89b794f --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCombineEx.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchCombineEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchFindExtension.c b/winpr/libwinpr/path/test/TestPathCchFindExtension.c new file mode 100644 index 0000000..f4b4151 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchFindExtension.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +static const char testPathExtension[] = "C:\\Windows\\System32\\cmd.exe"; + +int TestPathCchFindExtension(int argc, char* argv[]) +{ + PCSTR pszExt = NULL; + PCSTR pszTmp = NULL; + HRESULT hr = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Test invalid args */ + + hr = PathCchFindExtensionA(NULL, sizeof(testPathExtension), &pszExt); + if (SUCCEEDED(hr)) + { + printf( + "PathCchFindExtensionA unexpectedly succeeded with pszPath = NULL. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + + hr = PathCchFindExtensionA(testPathExtension, 0, &pszExt); + if (SUCCEEDED(hr)) + { + printf("PathCchFindExtensionA unexpectedly succeeded with cchPath = 0. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + + hr = PathCchFindExtensionA(testPathExtension, sizeof(testPathExtension), NULL); + if (SUCCEEDED(hr)) + { + printf( + "PathCchFindExtensionA unexpectedly succeeded with ppszExt = NULL. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + + /* Test missing null-termination of pszPath */ + + hr = PathCchFindExtensionA("c:\\45.789", 9, &pszExt); /* nb: correct would be 10 */ + if (SUCCEEDED(hr)) + { + printf("PathCchFindExtensionA unexpectedly succeeded with unterminated pszPath. result: " + "0x%08" PRIX32 "\n", + hr); + return -1; + } + + /* Test passing of an empty terminated string (must succeed) */ + + pszExt = NULL; + pszTmp = ""; + hr = PathCchFindExtensionA(pszTmp, 1, &pszExt); + if (hr != S_OK) + { + printf("PathCchFindExtensionA failed with an empty terminated string. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + /* pszExt must point to the strings terminating 0 now */ + if (pszExt != pszTmp) + { + printf("PathCchFindExtensionA failed with an empty terminated string: pszExt pointer " + "mismatch\n"); + return -1; + } + + /* Test a path without file extension (must succeed) */ + + pszExt = NULL; + pszTmp = "c:\\4.678\\"; + hr = PathCchFindExtensionA(pszTmp, 10, &pszExt); + if (hr != S_OK) + { + printf("PathCchFindExtensionA failed with a directory path. result: 0x%08" PRIX32 "\n", hr); + return -1; + } + /* The extension must not have been found and pszExt must point to the + * strings terminating NULL now */ + if (pszExt != &pszTmp[9]) + { + printf("PathCchFindExtensionA failed with a directory path: pszExt pointer mismatch\n"); + return -1; + } + + /* Non-special tests */ + + pszExt = NULL; + if (PathCchFindExtensionA(testPathExtension, sizeof(testPathExtension), &pszExt) != S_OK) + { + printf("PathCchFindExtensionA failure: expected S_OK\n"); + return -1; + } + + if (!pszExt || strcmp(pszExt, ".exe")) + { + printf("PathCchFindExtensionA failure: unexpected extension\n"); + return -1; + } + + printf("Extension: %s\n", pszExt); + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchIsRoot.c b/winpr/libwinpr/path/test/TestPathCchIsRoot.c new file mode 100644 index 0000000..1ffda37 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchIsRoot.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchIsRoot(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c b/winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c new file mode 100644 index 0000000..f23e72b --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchRemoveBackslash(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c b/winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c new file mode 100644 index 0000000..80eb1aa --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchRemoveBackslashEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveExtension.c b/winpr/libwinpr/path/test/TestPathCchRemoveExtension.c new file mode 100644 index 0000000..bd315cb --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveExtension.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchRemoveExtension(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c b/winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c new file mode 100644 index 0000000..686e367 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchRemoveFileSpec(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRenameExtension.c b/winpr/libwinpr/path/test/TestPathCchRenameExtension.c new file mode 100644 index 0000000..cf021b1 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRenameExtension.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchRenameExtension(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchSkipRoot.c b/winpr/libwinpr/path/test/TestPathCchSkipRoot.c new file mode 100644 index 0000000..dca5ad9 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchSkipRoot.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchSkipRoot(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchStripPrefix.c b/winpr/libwinpr/path/test/TestPathCchStripPrefix.c new file mode 100644 index 0000000..aaec4bc --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchStripPrefix.c @@ -0,0 +1,128 @@ + +#include +#include +#include +#include +#include + +/** + * Naming Files, Paths, and Namespaces: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247/ + */ + +static const TCHAR testPathPrefixFileNamespace[] = _T("\\\\?\\C:\\Program Files\\"); +static const TCHAR testPathNoPrefixFileNamespace[] = _T("C:\\Program Files\\"); +static const TCHAR testPathPrefixFileNamespaceMinimum[] = _T("\\\\?\\C:"); +static const TCHAR testPathNoPrefixFileNamespaceMinimum[] = _T("C:"); + +static const TCHAR testPathPrefixDeviceNamespace[] = _T("\\\\?\\GLOBALROOT"); + +int TestPathCchStripPrefix(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * PathCchStripPrefix returns S_OK if the prefix was removed, S_FALSE if + * the path did not have a prefix to remove, or an HRESULT failure code. + */ + + /* Path with prefix (File Namespace) */ + + _tcscpy(Path, testPathPrefixFileNamespace); + + status = PathCchStripPrefix(Path, sizeof(testPathPrefixFileNamespace) / sizeof(TCHAR)); + + if (status != S_OK) + { + _tprintf(_T("PathCchStripPrefix status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathNoPrefixFileNamespace) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, + testPathNoPrefixFileNamespace); + return -1; + } + + /* Path with prefix (Device Namespace) */ + + _tcscpy(Path, testPathPrefixDeviceNamespace); + + status = PathCchStripPrefix(Path, sizeof(testPathPrefixDeviceNamespace) / sizeof(TCHAR)); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchStripPrefix status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathPrefixDeviceNamespace) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, + testPathPrefixDeviceNamespace); + return -1; + } + + /* NULL Path */ + status = PathCchStripPrefix(NULL, PATHCCH_MAX_CCH); + if (status != E_INVALIDARG) + { + _tprintf( + _T("PathCchStripPrefix with null path unexpectedly succeeded with status 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* Invalid cchPath values: 0, 1, 2, 3 and > PATHCCH_MAX_CCH */ + for (int i = 0; i < 5; i++) + { + _tcscpy(Path, testPathPrefixFileNamespace); + if (i == 4) + i = PATHCCH_MAX_CCH + 1; + status = PathCchStripPrefix(Path, i); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchStripPrefix with invalid cchPath value %d unexpectedly succeeded ") + _T("with status 0x%08") _T(PRIX32) _T("\n"), + i, status); + return -1; + } + } + + /* Minimum Path that would get successfully stripped on windows */ + _tcscpy(Path, testPathPrefixFileNamespaceMinimum); + size_t i = sizeof(testPathPrefixFileNamespaceMinimum) / sizeof(TCHAR); + i = i - 1; /* include testing of a non-null terminated string */ + status = PathCchStripPrefix(Path, i); + if (status != S_OK) + { + _tprintf(_T("PathCchStripPrefix with minimum valid strippable path length unexpectedly ") + _T("returned status 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + if (_tcscmp(Path, testPathNoPrefixFileNamespaceMinimum)) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, + testPathNoPrefixFileNamespaceMinimum); + return -1; + } + + /* Invalid drive letter symbol */ + _tcscpy(Path, _T("\\\\?\\5:")); + status = PathCchStripPrefix(Path, 6); + if (status == S_OK) + { + _tprintf( + _T("PathCchStripPrefix with invalid drive letter symbol unexpectedly succeeded\n")); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchStripToRoot.c b/winpr/libwinpr/path/test/TestPathCchStripToRoot.c new file mode 100644 index 0000000..3b96e5c --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchStripToRoot.c @@ -0,0 +1,12 @@ + +#include +#include +#include +#include +#include + +int TestPathCchStripToRoot(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathIsUNCEx.c b/winpr/libwinpr/path/test/TestPathIsUNCEx.c new file mode 100644 index 0000000..4fdf4f7 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathIsUNCEx.c @@ -0,0 +1,52 @@ + +#include +#include +#include +#include +#include + +static const TCHAR testServer[] = _T("server\\share\\path\\file"); +static const TCHAR testPathUNC[] = _T("\\\\server\\share\\path\\file"); +static const TCHAR testPathNotUNC[] = _T("C:\\share\\path\\file"); + +int TestPathIsUNCEx(int argc, char* argv[]) +{ + BOOL status = 0; + LPCTSTR Server = NULL; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Path is UNC */ + + _tcscpy(Path, testPathUNC); + + status = PathIsUNCEx(Path, &Server); + + if (!status) + { + _tprintf(_T("PathIsUNCEx status: 0x%d\n"), status); + return -1; + } + + if (_tcscmp(Server, testServer) != 0) + { + _tprintf(_T("Server Name Mismatch: Actual: %s, Expected: %s\n"), Server, testServer); + return -1; + } + + /* Path is not UNC */ + + _tcscpy(Path, testPathNotUNC); + + status = PathIsUNCEx(Path, &Server); + + if (status) + { + _tprintf(_T("PathIsUNCEx status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathMakePath.c b/winpr/libwinpr/path/test/TestPathMakePath.c new file mode 100644 index 0000000..488cab3 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathMakePath.c @@ -0,0 +1,83 @@ +#include +#include +#include + +#include +#include +#include + +int TestPathMakePath(int argc, char* argv[]) +{ + size_t baseLen = 0; + BOOL success = 0; + char tmp[64] = { 0 }; + char* path = NULL; + char* cur = NULL; + char delim = PathGetSeparatorA(0); + char* base = GetKnownPath(KNOWN_PATH_TEMP); + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!base) + { + fprintf(stderr, "Failed to get temporary directory!\n"); + return -1; + } + + baseLen = strlen(base); + srand(time(NULL)); + + for (int x = 0; x < 5; x++) + { + sprintf_s(tmp, ARRAYSIZE(tmp), "%08X", rand()); + path = GetCombinedPath(base, tmp); + free(base); + + if (!path) + { + fprintf(stderr, "GetCombinedPath failed!\n"); + return -1; + } + + base = path; + } + + printf("Creating path %s\n", path); + success = winpr_PathMakePath(path, NULL); + + if (!success) + { + fprintf(stderr, "MakePath failed!\n"); + free(path); + return -1; + } + + success = winpr_PathFileExists(path); + + if (!success) + { + fprintf(stderr, "MakePath lied about success!\n"); + free(path); + return -1; + } + + while (strlen(path) > baseLen) + { + if (!winpr_RemoveDirectory(path)) + { + fprintf(stderr, "winpr_RemoveDirectory %s failed!\n", path); + free(path); + return -1; + } + + cur = strrchr(path, delim); + + if (cur) + *cur = '\0'; + } + + free(path); + printf("%s success!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathShell.c b/winpr/libwinpr/path/test/TestPathShell.c new file mode 100644 index 0000000..1925fea --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathShell.c @@ -0,0 +1,58 @@ + +#include +#include +#include +#include +#include + +int TestPathShell(int argc, char* argv[]) +{ + const int paths[] = { KNOWN_PATH_HOME, KNOWN_PATH_TEMP, + KNOWN_PATH_XDG_DATA_HOME, KNOWN_PATH_XDG_CONFIG_HOME, + KNOWN_PATH_XDG_CACHE_HOME, KNOWN_PATH_XDG_RUNTIME_DIR, + KNOWN_PATH_XDG_CONFIG_HOME }; + const char* names[] = { "KNOWN_PATH_HOME", "KNOWN_PATH_TEMP", + "KNOWN_PATH_XDG_DATA_HOME", "KNOWN_PATH_XDG_CONFIG_HOME", + "KNOWN_PATH_XDG_CACHE_HOME", "KNOWN_PATH_XDG_RUNTIME_DIR", + "KNOWN_PATH_XDG_CONFIG_HOME" }; + int rc = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + for (size_t x = 0; x < sizeof(paths) / sizeof(paths[0]); x++) + { + const int id = paths[x]; + const char* name = names[x]; + { + char* path = GetKnownPath(id); + + if (!path) + { + fprintf(stderr, "GetKnownPath(%d) failed\n", id); + rc = -1; + } + else + { + printf("%s Path: %s\n", name, path); + } + free(path); + } + { + char* path = GetKnownSubPath(id, "freerdp"); + + if (!path) + { + fprintf(stderr, "GetKnownSubPath(%d) failed\n", id); + rc = -1; + } + else + { + printf("%s SubPath: %s\n", name, path); + } + free(path); + } + } + + return rc; +} diff --git a/winpr/libwinpr/pipe/CMakeLists.txt b/winpr/libwinpr/pipe/CMakeLists.txt new file mode 100644 index 0000000..a78b98a --- /dev/null +++ b/winpr/libwinpr/pipe/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-pipe cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(pipe.c pipe.h) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/pipe/ModuleOptions.cmake b/winpr/libwinpr/pipe/ModuleOptions.cmake new file mode 100644 index 0000000..557e6a1 --- /dev/null +++ b/winpr/libwinpr/pipe/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "namedpipe") +set(MINWIN_LONG_NAME "Named Pipe Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/pipe/pipe.c b/winpr/libwinpr/pipe/pipe.c new file mode 100644 index 0000000..dba1bf3 --- /dev/null +++ b/winpr/libwinpr/pipe/pipe.c @@ -0,0 +1,930 @@ +/** + * WinPR: Windows Portable Runtime + * Pipe Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2017 Armin Novak + * Copyright 2017 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifndef _WIN32 + +#include "../handle/handle.h" + +#include +#include +#include +#include +#include + +#ifdef WINPR_HAVE_SYS_AIO_H +#undef WINPR_HAVE_SYS_AIO_H /* disable for now, incomplete */ +#endif + +#ifdef WINPR_HAVE_SYS_AIO_H +#include +#endif + +#include "pipe.h" + +#include "../log.h" +#define TAG WINPR_TAG("pipe") + +/* + * Since the WinPR implementation of named pipes makes use of UNIX domain + * sockets, it is not possible to bind the same name more than once (i.e., + * SO_REUSEADDR does not work with UNIX domain sockets). As a result, the + * first call to CreateNamedPipe with name n creates a "shared" UNIX domain + * socket descriptor that gets duplicated via dup() for the first and all + * subsequent calls to CreateNamedPipe with name n. + * + * The following array keeps track of the references to the shared socked + * descriptors. If an entry's reference count is zero the base socket + * descriptor gets closed and the entry is removed from the list. + */ + +static wArrayList* g_NamedPipeServerSockets = NULL; + +typedef struct +{ + char* name; + int serverfd; + int references; +} NamedPipeServerSocketEntry; + +static BOOL PipeIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_ANONYMOUS_PIPE, FALSE); +} + +static int PipeGetFd(HANDLE handle) +{ + WINPR_PIPE* pipe = (WINPR_PIPE*)handle; + + if (!PipeIsHandled(handle)) + return -1; + + return pipe->fd; +} + +static BOOL PipeCloseHandle(HANDLE handle) +{ + WINPR_PIPE* pipe = (WINPR_PIPE*)handle; + + if (!PipeIsHandled(handle)) + return FALSE; + + if (pipe->fd != -1) + { + close(pipe->fd); + pipe->fd = -1; + } + + free(handle); + return TRUE; +} + +static BOOL PipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) +{ + SSIZE_T io_status = 0; + WINPR_PIPE* pipe = NULL; + BOOL status = TRUE; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + pipe = (WINPR_PIPE*)Object; + + do + { + io_status = read(pipe->fd, lpBuffer, nNumberOfBytesToRead); + } while ((io_status < 0) && (errno == EINTR)); + + if (io_status < 0) + { + status = FALSE; + + switch (errno) + { + case EWOULDBLOCK: + SetLastError(ERROR_NO_DATA); + break; + } + } + + if (lpNumberOfBytesRead) + *lpNumberOfBytesRead = (DWORD)io_status; + + return status; +} + +static BOOL PipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) +{ + SSIZE_T io_status = 0; + WINPR_PIPE* pipe = NULL; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + pipe = (WINPR_PIPE*)Object; + + do + { + io_status = write(pipe->fd, lpBuffer, nNumberOfBytesToWrite); + } while ((io_status < 0) && (errno == EINTR)); + + if ((io_status < 0) && (errno == EWOULDBLOCK)) + io_status = 0; + + *lpNumberOfBytesWritten = (DWORD)io_status; + return TRUE; +} + +static HANDLE_OPS ops = { PipeIsHandled, + PipeCloseHandle, + PipeGetFd, + NULL, /* CleanupHandle */ + PipeRead, + NULL, /* FileReadEx */ + NULL, /* FileReadScatter */ + PipeWrite, + NULL, /* FileWriteEx */ + NULL, /* FileWriteGather */ + NULL, /* FileGetFileSize */ + NULL, /* FlushFileBuffers */ + NULL, /* FileSetEndOfFile */ + NULL, /* FileSetFilePointer */ + NULL, /* SetFilePointerEx */ + NULL, /* FileLockFile */ + NULL, /* FileLockFileEx */ + NULL, /* FileUnlockFile */ + NULL, /* FileUnlockFileEx */ + NULL /* SetFileTime */ + , + NULL }; + +static BOOL NamedPipeIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NAMED_PIPE, TRUE); +} + +static int NamedPipeGetFd(HANDLE handle) +{ + WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*)handle; + + if (!NamedPipeIsHandled(handle)) + return -1; + + if (pipe->ServerMode) + return pipe->serverfd; + + return pipe->clientfd; +} + +static BOOL NamedPipeCloseHandle(HANDLE handle) +{ + WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)handle; + + /* This check confuses the analyzer. Since not all handle + * types are handled here, it guesses that the memory of a + * NamedPipeHandle may leak. */ +#ifndef __clang_analyzer__ + if (!NamedPipeIsHandled(handle)) + return FALSE; +#endif + + if (pNamedPipe->pfnUnrefNamedPipe) + pNamedPipe->pfnUnrefNamedPipe(pNamedPipe); + + free(pNamedPipe->name); + free(pNamedPipe->lpFileName); + free(pNamedPipe->lpFilePath); + + if (pNamedPipe->serverfd != -1) + close(pNamedPipe->serverfd); + + if (pNamedPipe->clientfd != -1) + close(pNamedPipe->clientfd); + + free(pNamedPipe); + return TRUE; +} + +BOOL NamedPipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) +{ + SSIZE_T io_status = 0; + WINPR_NAMED_PIPE* pipe = NULL; + BOOL status = TRUE; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + pipe = (WINPR_NAMED_PIPE*)Object; + + if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)) + { + if (pipe->clientfd == -1) + return FALSE; + + do + { + io_status = read(pipe->clientfd, lpBuffer, nNumberOfBytesToRead); + } while ((io_status < 0) && (errno == EINTR)); + + if (io_status == 0) + { + SetLastError(ERROR_BROKEN_PIPE); + status = FALSE; + } + else if (io_status < 0) + { + status = FALSE; + + switch (errno) + { + case EWOULDBLOCK: + SetLastError(ERROR_NO_DATA); + break; + + default: + SetLastError(ERROR_BROKEN_PIPE); + break; + } + } + + if (lpNumberOfBytesRead) + *lpNumberOfBytesRead = (DWORD)io_status; + } + else + { + /* Overlapped I/O */ + if (!lpOverlapped) + return FALSE; + + if (pipe->clientfd == -1) + return FALSE; + + pipe->lpOverlapped = lpOverlapped; +#ifdef WINPR_HAVE_SYS_AIO_H + { + int aio_status; + struct aiocb cb = { 0 }; + + cb.aio_fildes = pipe->clientfd; + cb.aio_buf = lpBuffer; + cb.aio_nbytes = nNumberOfBytesToRead; + cb.aio_offset = lpOverlapped->Offset; + cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + cb.aio_sigevent.sigev_signo = SIGIO; + cb.aio_sigevent.sigev_value.sival_ptr = (void*)lpOverlapped; + InstallAioSignalHandler(); + aio_status = aio_read(&cb); + WLog_DBG(TAG, "aio_read status: %d", aio_status); + + if (aio_status < 0) + status = FALSE; + + return status; + } +#else + /* synchronous behavior */ + lpOverlapped->Internal = 0; + lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToRead; + lpOverlapped->Pointer = (PVOID)lpBuffer; + SetEvent(lpOverlapped->hEvent); +#endif + } + + return status; +} + +BOOL NamedPipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) +{ + SSIZE_T io_status = 0; + WINPR_NAMED_PIPE* pipe = NULL; + BOOL status = TRUE; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + pipe = (WINPR_NAMED_PIPE*)Object; + + if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)) + { + if (pipe->clientfd == -1) + return FALSE; + + do + { + io_status = write(pipe->clientfd, lpBuffer, nNumberOfBytesToWrite); + } while ((io_status < 0) && (errno == EINTR)); + + if (io_status < 0) + { + *lpNumberOfBytesWritten = 0; + + switch (errno) + { + case EWOULDBLOCK: + io_status = 0; + status = TRUE; + break; + + default: + status = FALSE; + } + } + + *lpNumberOfBytesWritten = (DWORD)io_status; + return status; + } + else + { + /* Overlapped I/O */ + if (!lpOverlapped) + return FALSE; + + if (pipe->clientfd == -1) + return FALSE; + + pipe->lpOverlapped = lpOverlapped; +#ifdef WINPR_HAVE_SYS_AIO_H + { + struct aiocb cb = { 0 }; + + cb.aio_fildes = pipe->clientfd; + cb.aio_buf = (void*)lpBuffer; + cb.aio_nbytes = nNumberOfBytesToWrite; + cb.aio_offset = lpOverlapped->Offset; + cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + cb.aio_sigevent.sigev_signo = SIGIO; + cb.aio_sigevent.sigev_value.sival_ptr = (void*)lpOverlapped; + InstallAioSignalHandler(); + io_status = aio_write(&cb); + WLog_DBG("aio_write status: %" PRIdz, io_status); + + if (io_status < 0) + status = FALSE; + + return status; + } +#else + /* synchronous behavior */ + lpOverlapped->Internal = 1; + lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToWrite; + { + union + { + LPCVOID cpv; + PVOID pv; + } cnv; + cnv.cpv = lpBuffer; + lpOverlapped->Pointer = cnv.pv; + } + SetEvent(lpOverlapped->hEvent); +#endif + } + + return TRUE; +} + +static HANDLE_OPS namedOps = { NamedPipeIsHandled, + NamedPipeCloseHandle, + NamedPipeGetFd, + NULL, /* CleanupHandle */ + NamedPipeRead, + NULL, + NULL, + NamedPipeWrite, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +static BOOL InitWinPRPipeModule(void) +{ + if (g_NamedPipeServerSockets) + return TRUE; + + g_NamedPipeServerSockets = ArrayList_New(FALSE); + return g_NamedPipeServerSockets != NULL; +} + +/* + * Unnamed pipe + */ + +BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, + DWORD nSize) +{ + int pipe_fd[2]; + WINPR_PIPE* pReadPipe = NULL; + WINPR_PIPE* pWritePipe = NULL; + + WINPR_UNUSED(lpPipeAttributes); + WINPR_UNUSED(nSize); + + pipe_fd[0] = -1; + pipe_fd[1] = -1; + + if (pipe(pipe_fd) < 0) + { + WLog_ERR(TAG, "failed to create pipe"); + return FALSE; + } + + pReadPipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE)); + pWritePipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE)); + + if (!pReadPipe || !pWritePipe) + { + free(pReadPipe); + free(pWritePipe); + return FALSE; + } + + pReadPipe->fd = pipe_fd[0]; + pWritePipe->fd = pipe_fd[1]; + WINPR_HANDLE_SET_TYPE_AND_MODE(pReadPipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ); + pReadPipe->common.ops = &ops; + *((ULONG_PTR*)hReadPipe) = (ULONG_PTR)pReadPipe; + WINPR_HANDLE_SET_TYPE_AND_MODE(pWritePipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ); + pWritePipe->common.ops = &ops; + *((ULONG_PTR*)hWritePipe) = (ULONG_PTR)pWritePipe; + return TRUE; +} + +/** + * Named pipe + */ + +static void winpr_unref_named_pipe(WINPR_NAMED_PIPE* pNamedPipe) +{ + NamedPipeServerSocketEntry* baseSocket = NULL; + + if (!pNamedPipe) + return; + + WINPR_ASSERT(pNamedPipe->name); + WINPR_ASSERT(g_NamedPipeServerSockets); + // WLog_VRB(TAG, "%p (%s)", (void*) pNamedPipe, pNamedPipe->name); + ArrayList_Lock(g_NamedPipeServerSockets); + + for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++) + { + baseSocket = + (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index); + WINPR_ASSERT(baseSocket->name); + + if (!strcmp(baseSocket->name, pNamedPipe->name)) + { + WINPR_ASSERT(baseSocket->references > 0); + WINPR_ASSERT(baseSocket->serverfd != -1); + + if (--baseSocket->references == 0) + { + // WLog_DBG(TAG, "removing shared server socked resource"); + // WLog_DBG(TAG, "closing shared serverfd %d", baseSocket->serverfd); + ArrayList_Remove(g_NamedPipeServerSockets, baseSocket); + close(baseSocket->serverfd); + free(baseSocket->name); + free(baseSocket); + } + + break; + } + } + + ArrayList_Unlock(g_NamedPipeServerSockets); +} + +HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, + DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, + LPSECURITY_ATTRIBUTES lpSecurityAttributes) +{ + char* lpPipePath = NULL; + WINPR_NAMED_PIPE* pNamedPipe = NULL; + int serverfd = -1; + NamedPipeServerSocketEntry* baseSocket = NULL; + + WINPR_UNUSED(lpSecurityAttributes); + + if (dwOpenMode & FILE_FLAG_OVERLAPPED) + { + WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag"); + SetLastError(ERROR_NOT_SUPPORTED); + return INVALID_HANDLE_VALUE; + } + + if (!lpName) + return INVALID_HANDLE_VALUE; + + if (!InitWinPRPipeModule()) + return INVALID_HANDLE_VALUE; + + pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE)); + + if (!pNamedPipe) + return INVALID_HANDLE_VALUE; + + ArrayList_Lock(g_NamedPipeServerSockets); + WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ); + pNamedPipe->serverfd = -1; + pNamedPipe->clientfd = -1; + + if (!(pNamedPipe->name = _strdup(lpName))) + goto out; + + if (!(pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpName))) + goto out; + + if (!(pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpName))) + goto out; + + pNamedPipe->dwOpenMode = dwOpenMode; + pNamedPipe->dwPipeMode = dwPipeMode; + pNamedPipe->nMaxInstances = nMaxInstances; + pNamedPipe->nOutBufferSize = nOutBufferSize; + pNamedPipe->nInBufferSize = nInBufferSize; + pNamedPipe->nDefaultTimeOut = nDefaultTimeOut; + pNamedPipe->dwFlagsAndAttributes = dwOpenMode; + pNamedPipe->clientfd = -1; + pNamedPipe->ServerMode = TRUE; + pNamedPipe->common.ops = &namedOps; + + for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++) + { + baseSocket = + (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index); + + if (!strcmp(baseSocket->name, lpName)) + { + serverfd = baseSocket->serverfd; + // WLog_DBG(TAG, "using shared socked resource for pipe %p (%s)", (void*) pNamedPipe, + // lpName); + break; + } + } + + /* If this is the first instance of the named pipe... */ + if (serverfd == -1) + { + struct sockaddr_un s = { 0 }; + /* Create the UNIX domain socket and start listening. */ + if (!(lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA())) + goto out; + + if (!winpr_PathFileExists(lpPipePath)) + { + if (!CreateDirectoryA(lpPipePath, 0)) + { + free(lpPipePath); + goto out; + } + + UnixChangeFileMode(lpPipePath, 0xFFFF); + } + + free(lpPipePath); + + if (winpr_PathFileExists(pNamedPipe->lpFilePath)) + winpr_DeleteFile(pNamedPipe->lpFilePath); + + if ((serverfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "CreateNamedPipeA: socket error, %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + goto out; + } + + s.sun_family = AF_UNIX; + sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath); + + if (bind(serverfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un)) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "CreateNamedPipeA: bind error, %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + goto out; + } + + if (listen(serverfd, 2) == -1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "CreateNamedPipeA: listen error, %s", + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + goto out; + } + + UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF); + + if (!(baseSocket = (NamedPipeServerSocketEntry*)malloc(sizeof(NamedPipeServerSocketEntry)))) + goto out; + + if (!(baseSocket->name = _strdup(lpName))) + { + free(baseSocket); + goto out; + } + + baseSocket->serverfd = serverfd; + baseSocket->references = 0; + + if (!ArrayList_Append(g_NamedPipeServerSockets, baseSocket)) + { + free(baseSocket->name); + free(baseSocket); + goto out; + } + + // WLog_DBG(TAG, "created shared socked resource for pipe %p (%s). base serverfd = %d", + // (void*) pNamedPipe, lpName, serverfd); + } + + pNamedPipe->serverfd = dup(baseSocket->serverfd); + // WLog_DBG(TAG, "using serverfd %d (duplicated from %d)", pNamedPipe->serverfd, + // baseSocket->serverfd); + pNamedPipe->pfnUnrefNamedPipe = winpr_unref_named_pipe; + baseSocket->references++; + + if (dwOpenMode & FILE_FLAG_OVERLAPPED) + { +#if 0 + int flags = fcntl(pNamedPipe->serverfd, F_GETFL); + + if (flags != -1) + fcntl(pNamedPipe->serverfd, F_SETFL, flags | O_NONBLOCK); + +#endif + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of baseSocket + ArrayList_Unlock(g_NamedPipeServerSockets); + return pNamedPipe; +out: + NamedPipeCloseHandle(pNamedPipe); + + if (serverfd != -1) + close(serverfd); + + ArrayList_Unlock(g_NamedPipeServerSockets); + return INVALID_HANDLE_VALUE; +} + +HANDLE CreateNamedPipeW(LPCWSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, + DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, + LPSECURITY_ATTRIBUTES lpSecurityAttributes) +{ + WLog_ERR(TAG, "is not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped) +{ + int status = 0; + socklen_t length = 0; + WINPR_NAMED_PIPE* pNamedPipe = NULL; + + if (lpOverlapped) + { + WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + + if (!hNamedPipe) + return FALSE; + + pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe; + + if (!(pNamedPipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED)) + { + struct sockaddr_un s = { 0 }; + length = sizeof(struct sockaddr_un); + status = accept(pNamedPipe->serverfd, (struct sockaddr*)&s, &length); + + if (status < 0) + { + WLog_ERR(TAG, "ConnectNamedPipe: accept error"); + return FALSE; + } + + pNamedPipe->clientfd = status; + pNamedPipe->ServerMode = FALSE; + } + else + { + if (!lpOverlapped) + return FALSE; + + if (pNamedPipe->serverfd == -1) + return FALSE; + + pNamedPipe->lpOverlapped = lpOverlapped; + /* synchronous behavior */ + lpOverlapped->Internal = 2; + lpOverlapped->InternalHigh = (ULONG_PTR)0; + lpOverlapped->Pointer = (PVOID)NULL; + SetEvent(lpOverlapped->hEvent); + } + + return TRUE; +} + +BOOL DisconnectNamedPipe(HANDLE hNamedPipe) +{ + WINPR_NAMED_PIPE* pNamedPipe = NULL; + pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe; + + if (pNamedPipe->clientfd != -1) + { + close(pNamedPipe->clientfd); + pNamedPipe->clientfd = -1; + } + + return TRUE; +} + +BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead, + LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL TransactNamedPipe(HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize, + LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, + LPOVERLAPPED lpOverlapped) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut) +{ + BOOL status = 0; + DWORD nWaitTime = 0; + char* lpFilePath = NULL; + DWORD dwSleepInterval = 0; + + if (!lpNamedPipeName) + return FALSE; + + lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpNamedPipeName); + + if (!lpFilePath) + return FALSE; + + if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT) + nTimeOut = 50; + + nWaitTime = 0; + status = TRUE; + dwSleepInterval = 10; + + while (!winpr_PathFileExists(lpFilePath)) + { + Sleep(dwSleepInterval); + nWaitTime += dwSleepInterval; + + if (nWaitTime >= nTimeOut) + { + status = FALSE; + break; + } + } + + free(lpFilePath); + return status; +} + +BOOL WaitNamedPipeW(LPCWSTR lpNamedPipeName, DWORD nTimeOut) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount, + LPDWORD lpCollectDataTimeout) +{ + int fd = 0; + int flags = 0; + WINPR_NAMED_PIPE* pNamedPipe = NULL; + pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe; + + if (lpMode) + { + pNamedPipe->dwPipeMode = *lpMode; + fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd; + + if (fd == -1) + return FALSE; + + flags = fcntl(fd, F_GETFL); + + if (flags < 0) + return FALSE; + + if (pNamedPipe->dwPipeMode & PIPE_NOWAIT) + flags = (flags | O_NONBLOCK); + else + flags = (flags & ~(O_NONBLOCK)); + + if (fcntl(fd, F_SETFL, flags) < 0) + return FALSE; + } + + if (lpMaxCollectionCount) + { + } + + if (lpCollectDataTimeout) + { + } + + return TRUE; +} + +BOOL ImpersonateNamedPipeClient(HANDLE hNamedPipe) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetNamedPipeClientComputerNameA(HANDLE Pipe, LPCSTR ClientComputerName, + ULONG ClientComputerNameLength) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL GetNamedPipeClientComputerNameW(HANDLE Pipe, LPCWSTR ClientComputerName, + ULONG ClientComputerNameLength) +{ + WLog_ERR(TAG, "Not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +#endif diff --git a/winpr/libwinpr/pipe/pipe.h b/winpr/libwinpr/pipe/pipe.h new file mode 100644 index 0000000..192de06 --- /dev/null +++ b/winpr/libwinpr/pipe/pipe.h @@ -0,0 +1,75 @@ +/** + * WinPR: Windows Portable Runtime + * Pipe Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_PIPE_PRIVATE_H +#define WINPR_PIPE_PRIVATE_H + +#ifndef _WIN32 + +#include +#include + +#include "../handle/handle.h" + +struct winpr_pipe +{ + WINPR_HANDLE common; + + int fd; +}; +typedef struct winpr_pipe WINPR_PIPE; + +typedef struct winpr_named_pipe WINPR_NAMED_PIPE; + +typedef void (*fnUnrefNamedPipe)(WINPR_NAMED_PIPE* pNamedPipe); + +struct winpr_named_pipe +{ + WINPR_HANDLE common; + + int clientfd; + int serverfd; + + char* name; + char* lpFileName; + char* lpFilePath; + + BOOL ServerMode; + DWORD dwOpenMode; + DWORD dwPipeMode; + DWORD nMaxInstances; + DWORD nOutBufferSize; + DWORD nInBufferSize; + DWORD nDefaultTimeOut; + DWORD dwFlagsAndAttributes; + LPOVERLAPPED lpOverlapped; + + fnUnrefNamedPipe pfnUnrefNamedPipe; +}; + +BOOL winpr_destroy_named_pipe(WINPR_NAMED_PIPE* pNamedPipe); + +BOOL NamedPipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); +BOOL NamedPipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); + +#endif + +#endif /* WINPR_PIPE_PRIVATE_H */ diff --git a/winpr/libwinpr/pipe/test/CMakeLists.txt b/winpr/libwinpr/pipe/test/CMakeLists.txt new file mode 100644 index 0000000..b9bf685 --- /dev/null +++ b/winpr/libwinpr/pipe/test/CMakeLists.txt @@ -0,0 +1,27 @@ + +set(MODULE_NAME "TestPipe") +set(MODULE_PREFIX "TEST_PIPE") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestPipeCreatePipe.c + TestPipeCreateNamedPipe.c + TestPipeCreateNamedPipeOverlapped.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c new file mode 100644 index 0000000..8c8ead2 --- /dev/null +++ b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipe.c @@ -0,0 +1,510 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include "../pipe.h" + +#define PIPE_BUFFER_SIZE 32 + +static HANDLE ReadyEvent; + +static LPTSTR lpszPipeNameMt = _T("\\\\.\\pipe\\winpr_test_pipe_mt"); +static LPTSTR lpszPipeNameSt = _T("\\\\.\\pipe\\winpr_test_pipe_st"); + +static BOOL testFailed = FALSE; + +static DWORD WINAPI named_pipe_client_thread(LPVOID arg) +{ + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + BYTE* lpWriteBuffer = NULL; + BOOL fSuccess = FALSE; + DWORD nNumberOfBytesToRead = 0; + DWORD nNumberOfBytesToWrite = 0; + DWORD lpNumberOfBytesRead = 0; + DWORD lpNumberOfBytesWritten = 0; + WaitForSingleObject(ReadyEvent, INFINITE); + hNamedPipe = + CreateFile(lpszPipeNameMt, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (hNamedPipe == INVALID_HANDLE_VALUE) + { + printf("%s: Named Pipe CreateFile failure: INVALID_HANDLE_VALUE\n", __func__); + goto out; + } + + if (!(lpReadBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE))) + { + printf("%s: Error allocating read buffer\n", __func__); + goto out; + } + + if (!(lpWriteBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE))) + { + printf("%s: Error allocating write buffer\n", __func__); + goto out; + } + + lpNumberOfBytesWritten = 0; + nNumberOfBytesToWrite = PIPE_BUFFER_SIZE; + FillMemory(lpWriteBuffer, PIPE_BUFFER_SIZE, 0x59); + + if (!WriteFile(hNamedPipe, lpWriteBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten, + NULL) || + lpNumberOfBytesWritten != nNumberOfBytesToWrite) + { + printf("%s: Client NamedPipe WriteFile failure\n", __func__); + goto out; + } + + lpNumberOfBytesRead = 0; + nNumberOfBytesToRead = PIPE_BUFFER_SIZE; + ZeroMemory(lpReadBuffer, PIPE_BUFFER_SIZE); + + if (!ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, NULL) || + lpNumberOfBytesRead != nNumberOfBytesToRead) + { + printf("%s: Client NamedPipe ReadFile failure\n", __func__); + goto out; + } + + printf("Client ReadFile: %" PRIu32 " bytes\n", lpNumberOfBytesRead); + winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, lpNumberOfBytesRead); + fSuccess = TRUE; +out: + free(lpReadBuffer); + free(lpWriteBuffer); + CloseHandle(hNamedPipe); + + if (!fSuccess) + testFailed = TRUE; + + ExitThread(0); + return 0; +} + +static DWORD WINAPI named_pipe_server_thread(LPVOID arg) +{ + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + BYTE* lpWriteBuffer = NULL; + BOOL fSuccess = FALSE; + BOOL fConnected = FALSE; + DWORD nNumberOfBytesToRead = 0; + DWORD nNumberOfBytesToWrite = 0; + DWORD lpNumberOfBytesRead = 0; + DWORD lpNumberOfBytesWritten = 0; + hNamedPipe = CreateNamedPipe( + lpszPipeNameMt, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, NULL); + + if (!hNamedPipe) + { + printf("%s: CreateNamedPipe failure: NULL handle\n", __func__); + goto out; + } + + if (hNamedPipe == INVALID_HANDLE_VALUE) + { + printf("%s: CreateNamedPipe failure: INVALID_HANDLE_VALUE\n", __func__); + goto out; + } + + SetEvent(ReadyEvent); + + /** + * Note: + * If a client connects before ConnectNamedPipe is called, the function returns zero and + * GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client connects in the + * interval between the call to CreateNamedPipe and the call to ConnectNamedPipe. + * In this situation, there is a good connection between client and server, even though + * the function returns zero. + */ + fConnected = + ConnectNamedPipe(hNamedPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + if (!fConnected) + { + printf("%s: ConnectNamedPipe failure\n", __func__); + goto out; + } + + if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE))) + { + printf("%s: Error allocating read buffer\n", __func__); + goto out; + } + + if (!(lpWriteBuffer = (BYTE*)malloc(PIPE_BUFFER_SIZE))) + { + printf("%s: Error allocating write buffer\n", __func__); + goto out; + } + + lpNumberOfBytesRead = 0; + nNumberOfBytesToRead = PIPE_BUFFER_SIZE; + + if (!ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, NULL) || + lpNumberOfBytesRead != nNumberOfBytesToRead) + { + printf("%s: Server NamedPipe ReadFile failure\n", __func__); + goto out; + } + + printf("Server ReadFile: %" PRIu32 " bytes\n", lpNumberOfBytesRead); + winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, lpNumberOfBytesRead); + lpNumberOfBytesWritten = 0; + nNumberOfBytesToWrite = PIPE_BUFFER_SIZE; + FillMemory(lpWriteBuffer, PIPE_BUFFER_SIZE, 0x45); + + if (!WriteFile(hNamedPipe, lpWriteBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten, + NULL) || + lpNumberOfBytesWritten != nNumberOfBytesToWrite) + { + printf("%s: Server NamedPipe WriteFile failure\n", __func__); + goto out; + } + + fSuccess = TRUE; +out: + free(lpReadBuffer); + free(lpWriteBuffer); + CloseHandle(hNamedPipe); + + if (!fSuccess) + testFailed = TRUE; + + ExitThread(0); + return 0; +} + +#define TESTNUMPIPESST 16 +static DWORD WINAPI named_pipe_single_thread(LPVOID arg) +{ + HANDLE servers[TESTNUMPIPESST] = { 0 }; + HANDLE clients[TESTNUMPIPESST] = { 0 }; + DWORD dwRead = 0; + DWORD dwWritten = 0; + int numPipes = 0; + BOOL bSuccess = FALSE; + numPipes = TESTNUMPIPESST; + WaitForSingleObject(ReadyEvent, INFINITE); + + for (int i = 0; i < numPipes; i++) + { + if (!(servers[i] = CreateNamedPipe(lpszPipeNameSt, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, + PIPE_BUFFER_SIZE, 0, NULL))) + { + printf("%s: CreateNamedPipe #%d failed\n", __func__, i); + goto out; + } + } + +#ifndef _WIN32 + + for (int i = 0; i < numPipes; i++) + { + WINPR_NAMED_PIPE* p = (WINPR_NAMED_PIPE*)servers[i]; + + if (strcmp(lpszPipeNameSt, p->name)) + { + printf("%s: Pipe name mismatch for pipe #%d ([%s] instead of [%s])\n", __func__, i, + p->name, lpszPipeNameSt); + goto out; + } + + if (p->clientfd != -1) + { + printf("%s: Unexpected client fd value for pipe #%d (%d instead of -1)\n", __func__, i, + p->clientfd); + goto out; + } + + if (p->serverfd < 1) + { + printf("%s: Unexpected server fd value for pipe #%d (%d is not > 0)\n", __func__, i, + p->serverfd); + goto out; + } + + if (p->ServerMode == FALSE) + { + printf("%s: Unexpected ServerMode value for pipe #%d (0 instead of 1)\n", __func__, i); + goto out; + } + } + +#endif + + for (int i = 0; i < numPipes; i++) + { + BOOL fConnected = 0; + if ((clients[i] = CreateFile(lpszPipeNameSt, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) + { + printf("%s: CreateFile #%d failed\n", __func__, i); + goto out; + } + + /** + * Note: + * If a client connects before ConnectNamedPipe is called, the function returns zero and + * GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client connects in the + * interval between the call to CreateNamedPipe and the call to ConnectNamedPipe. + * In this situation, there is a good connection between client and server, even though + * the function returns zero. + */ + fConnected = + ConnectNamedPipe(servers[i], NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + if (!fConnected) + { + printf("%s: ConnectNamedPipe #%d failed. (%" PRIu32 ")\n", __func__, i, GetLastError()); + goto out; + } + } + +#ifndef _WIN32 + + for (int i = 0; i < numPipes; i++) + { + WINPR_NAMED_PIPE* p = servers[i]; + + if (p->clientfd < 1) + { + printf("%s: Unexpected client fd value for pipe #%d (%d is not > 0)\n", __func__, i, + p->clientfd); + goto out; + } + + if (p->ServerMode) + { + printf("%s: Unexpected ServerMode value for pipe #%d (1 instead of 0)\n", __func__, i); + goto out; + } + } + + for (int i = 0; i < numPipes; i++) + { + { + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + /* Test writing from clients to servers */ + sprintf_s(sndbuf, sizeof(sndbuf), "CLIENT->SERVER ON PIPE #%05d", i); + + if (!WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL) || + dwWritten != sizeof(sndbuf)) + { + printf("%s: Error writing to client end of pipe #%d\n", __func__, i); + goto out; + } + + if (!ReadFile(servers[i], rcvbuf, dwWritten, &dwRead, NULL) || dwRead != dwWritten) + { + printf("%s: Error reading on server end of pipe #%d\n", __func__, i); + goto out; + } + + if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf))) + { + printf("%s: Error data read on server end of pipe #%d is corrupted\n", __func__, i); + goto out; + } + } + { + + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + /* Test writing from servers to clients */ + + sprintf_s(sndbuf, sizeof(sndbuf), "SERVER->CLIENT ON PIPE #%05d", i); + + if (!WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL) || + dwWritten != sizeof(sndbuf)) + { + printf("%s: Error writing to server end of pipe #%d\n", __func__, i); + goto out; + } + + if (!ReadFile(clients[i], rcvbuf, dwWritten, &dwRead, NULL) || dwRead != dwWritten) + { + printf("%s: Error reading on client end of pipe #%d\n", __func__, i); + goto out; + } + + if (memcmp(sndbuf, rcvbuf, sizeof(sndbuf))) + { + printf("%s: Error data read on client end of pipe #%d is corrupted\n", __func__, i); + goto out; + } + } + } + +#endif + /** + * After DisconnectNamedPipe on server end + * ReadFile/WriteFile must fail on client end + */ + int i = numPipes - 1; + DisconnectNamedPipe(servers[i]); + { + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, NULL)) + { + printf("%s: Error ReadFile on client should have failed after DisconnectNamedPipe on " + "server\n", + __func__); + goto out; + } + + if (WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL)) + { + printf( + "%s: Error WriteFile on client end should have failed after DisconnectNamedPipe on " + "server\n", + __func__); + goto out; + } + } + CloseHandle(servers[i]); + CloseHandle(clients[i]); + numPipes--; + /** + * After CloseHandle (without calling DisconnectNamedPipe first) on server end + * ReadFile/WriteFile must fail on client end + */ + i = numPipes - 1; + CloseHandle(servers[i]); + + { + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + + if (ReadFile(clients[i], rcvbuf, sizeof(rcvbuf), &dwRead, NULL)) + { + printf( + "%s: Error ReadFile on client end should have failed after CloseHandle on server\n", + __func__); + goto out; + } + + if (WriteFile(clients[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL)) + { + printf("%s: Error WriteFile on client end should have failed after CloseHandle on " + "server\n", + __func__); + goto out; + } + } + CloseHandle(clients[i]); + numPipes--; + /** + * After CloseHandle on client end + * ReadFile/WriteFile must fail on server end + */ + i = numPipes - 1; + CloseHandle(clients[i]); + + { + char sndbuf[PIPE_BUFFER_SIZE] = { 0 }; + char rcvbuf[PIPE_BUFFER_SIZE] = { 0 }; + + if (ReadFile(servers[i], rcvbuf, sizeof(rcvbuf), &dwRead, NULL)) + { + printf( + "%s: Error ReadFile on server end should have failed after CloseHandle on client\n", + __func__); + goto out; + } + + if (WriteFile(servers[i], sndbuf, sizeof(sndbuf), &dwWritten, NULL)) + { + printf("%s: Error WriteFile on server end should have failed after CloseHandle on " + "client\n", + __func__); + goto out; + } + } + + DisconnectNamedPipe(servers[i]); + CloseHandle(servers[i]); + numPipes--; + + /* Close all remaining pipes */ + for (int i = 0; i < numPipes; i++) + { + DisconnectNamedPipe(servers[i]); + CloseHandle(servers[i]); + CloseHandle(clients[i]); + } + + bSuccess = TRUE; +out: + + if (!bSuccess) + testFailed = TRUE; + + return 0; +} + +int TestPipeCreateNamedPipe(int argc, char* argv[]) +{ + HANDLE SingleThread = NULL; + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; + HANDLE hPipe = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + /* Verify that CreateNamedPipe returns INVALID_HANDLE_VALUE on failure */ + hPipe = CreateNamedPipeA(NULL, 0, 0, 0, 0, 0, 0, NULL); + if (hPipe != INVALID_HANDLE_VALUE) + { + printf("CreateNamedPipe unexpectedly returned %p instead of INVALID_HANDLE_VALUE (%p)\n", + hPipe, INVALID_HANDLE_VALUE); + return -1; + } + +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + if (!(ReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("CreateEvent failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + if (!(SingleThread = CreateThread(NULL, 0, named_pipe_single_thread, NULL, 0, NULL))) + { + printf("CreateThread (SingleThread) failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + if (!(ClientThread = CreateThread(NULL, 0, named_pipe_client_thread, NULL, 0, NULL))) + { + printf("CreateThread (ClientThread) failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + if (!(ServerThread = CreateThread(NULL, 0, named_pipe_server_thread, NULL, 0, NULL))) + { + printf("CreateThread (ServerThread) failure: (%" PRIu32 ")\n", GetLastError()); + return -1; + } + WaitForSingleObject(SingleThread, INFINITE); + WaitForSingleObject(ClientThread, INFINITE); + WaitForSingleObject(ServerThread, INFINITE); + CloseHandle(SingleThread); + CloseHandle(ClientThread); + CloseHandle(ServerThread); + return testFailed; +} diff --git a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c new file mode 100644 index 0000000..de95840 --- /dev/null +++ b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c @@ -0,0 +1,397 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PIPE_BUFFER_SIZE 32 +#define PIPE_TIMEOUT_MS 20000 // 20 seconds + +static BYTE SERVER_MESSAGE[PIPE_BUFFER_SIZE]; +static BYTE CLIENT_MESSAGE[PIPE_BUFFER_SIZE]; + +static BOOL bClientSuccess = FALSE; +static BOOL bServerSuccess = FALSE; + +static HANDLE serverReadyEvent = NULL; + +static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped"); + +static DWORD WINAPI named_pipe_client_thread(LPVOID arg) +{ + DWORD status = 0; + HANDLE hEvent = NULL; + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + BOOL fSuccess = FALSE; + OVERLAPPED overlapped = { 0 }; + DWORD nNumberOfBytesToRead = 0; + DWORD nNumberOfBytesToWrite = 0; + DWORD NumberOfBytesTransferred = 0; + + WINPR_UNUSED(arg); + + status = WaitForSingleObject(serverReadyEvent, PIPE_TIMEOUT_MS); + if (status != WAIT_OBJECT_0) + { + printf("client: failed to wait for server ready event: %" PRIu32 "\n", status); + goto finish; + } + + /* 1: initialize overlapped structure */ + if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("client: CreateEvent failure: %" PRIu32 "\n", GetLastError()); + goto finish; + } + overlapped.hEvent = hEvent; + + /* 2: connect to server named pipe */ + + hNamedPipe = CreateFile(lpszPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + + if (hNamedPipe == INVALID_HANDLE_VALUE) + { + printf("client: Named Pipe CreateFile failure: %" PRIu32 "\n", GetLastError()); + goto finish; + } + + /* 3: write to named pipe */ + + nNumberOfBytesToWrite = PIPE_BUFFER_SIZE; + NumberOfBytesTransferred = 0; + + fSuccess = WriteFile(hNamedPipe, CLIENT_MESSAGE, nNumberOfBytesToWrite, NULL, &overlapped); + + if (!fSuccess) + fSuccess = (GetLastError() == ERROR_IO_PENDING); + + if (!fSuccess) + { + printf("client: NamedPipe WriteFile failure (initial): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS); + if (status != WAIT_OBJECT_0) + { + printf("client: failed to wait for overlapped event (write): %" PRIu32 "\n", status); + goto finish; + } + + fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE); + if (!fSuccess) + { + printf("client: NamedPipe WriteFile failure (final): %" PRIu32 "\n", GetLastError()); + goto finish; + } + printf("client: WriteFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred); + + /* 4: read from named pipe */ + + if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE))) + { + printf("client: Error allocating read buffer\n"); + goto finish; + } + + nNumberOfBytesToRead = PIPE_BUFFER_SIZE; + NumberOfBytesTransferred = 0; + + fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, NULL, &overlapped); + + if (!fSuccess) + fSuccess = (GetLastError() == ERROR_IO_PENDING); + + if (!fSuccess) + { + printf("client: NamedPipe ReadFile failure (initial): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + status = WaitForMultipleObjects(1, &hEvent, FALSE, PIPE_TIMEOUT_MS); + if (status != WAIT_OBJECT_0) + { + printf("client: failed to wait for overlapped event (read): %" PRIu32 "\n", status); + goto finish; + } + + fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, TRUE); + if (!fSuccess) + { + printf("client: NamedPipe ReadFile failure (final): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + printf("client: ReadFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred); + winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred); + + if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE || + memcmp(lpReadBuffer, SERVER_MESSAGE, PIPE_BUFFER_SIZE)) + { + printf("client: received unexpected data from server\n"); + goto finish; + } + + printf("client: finished successfully\n"); + bClientSuccess = TRUE; + +finish: + free(lpReadBuffer); + if (hNamedPipe) + CloseHandle(hNamedPipe); + if (hEvent) + CloseHandle(hEvent); + + return 0; +} + +static DWORD WINAPI named_pipe_server_thread(LPVOID arg) +{ + DWORD status = 0; + HANDLE hEvent = NULL; + HANDLE hNamedPipe = NULL; + BYTE* lpReadBuffer = NULL; + OVERLAPPED overlapped = { 0 }; + BOOL fSuccess = FALSE; + BOOL fConnected = FALSE; + DWORD nNumberOfBytesToRead = 0; + DWORD nNumberOfBytesToWrite = 0; + DWORD NumberOfBytesTransferred = 0; + + WINPR_UNUSED(arg); + + /* 1: initialize overlapped structure */ + if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("server: CreateEvent failure: %" PRIu32 "\n", GetLastError()); + SetEvent(serverReadyEvent); /* unblock client thread */ + goto finish; + } + overlapped.hEvent = hEvent; + + /* 2: create named pipe and set ready event */ + + hNamedPipe = + CreateNamedPipe(lpszPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, + PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 0, NULL); + + if (hNamedPipe == INVALID_HANDLE_VALUE) + { + printf("server: CreateNamedPipe failure: %" PRIu32 "\n", GetLastError()); + SetEvent(serverReadyEvent); /* unblock client thread */ + goto finish; + } + + SetEvent(serverReadyEvent); + + /* 3: connect named pipe */ + +#if 0 + /* This sleep will most certainly cause ERROR_PIPE_CONNECTED below */ + Sleep(2000); +#endif + + fConnected = ConnectNamedPipe(hNamedPipe, &overlapped); + status = GetLastError(); + + /** + * At this point if fConnected is FALSE, we have to check GetLastError() for: + * ERROR_PIPE_CONNECTED: + * client has already connected before we have called ConnectNamedPipe. + * this is quite common depending on the timings and indicates success + * ERROR_IO_PENDING: + * Since we're using ConnectNamedPipe asynchronously here, the function returns + * immediately and this error code simply indicates that the operation is + * still in progress. Hence we have to wait for the completion event and use + * GetOverlappedResult to query the actual result of the operation (note that + * the lpNumberOfBytesTransferred parameter is undefined/useless for a + * ConnectNamedPipe operation) + */ + + if (!fConnected) + fConnected = (status == ERROR_PIPE_CONNECTED); + + printf("server: ConnectNamedPipe status: %" PRIu32 "\n", status); + + if (!fConnected && status == ERROR_IO_PENDING) + { + DWORD dwDummy = 0; + printf("server: waiting up to %u ms for connection ...\n", PIPE_TIMEOUT_MS); + status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS); + if (status == WAIT_OBJECT_0) + fConnected = GetOverlappedResult(hNamedPipe, &overlapped, &dwDummy, FALSE); + else + printf("server: failed to wait for overlapped event (connect): %" PRIu32 "\n", status); + } + + if (!fConnected) + { + printf("server: ConnectNamedPipe failed: %" PRIu32 "\n", status); + goto finish; + } + + printf("server: named pipe successfully connected\n"); + + /* 4: read from named pipe */ + + if (!(lpReadBuffer = (BYTE*)calloc(1, PIPE_BUFFER_SIZE))) + { + printf("server: Error allocating read buffer\n"); + goto finish; + } + + nNumberOfBytesToRead = PIPE_BUFFER_SIZE; + NumberOfBytesTransferred = 0; + + fSuccess = ReadFile(hNamedPipe, lpReadBuffer, nNumberOfBytesToRead, NULL, &overlapped); + + if (!fSuccess) + fSuccess = (GetLastError() == ERROR_IO_PENDING); + + if (!fSuccess) + { + printf("server: NamedPipe ReadFile failure (initial): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS); + if (status != WAIT_OBJECT_0) + { + printf("server: failed to wait for overlapped event (read): %" PRIu32 "\n", status); + goto finish; + } + + fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE); + if (!fSuccess) + { + printf("server: NamedPipe ReadFile failure (final): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + printf("server: ReadFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred); + winpr_HexDump("pipe.test", WLOG_DEBUG, lpReadBuffer, NumberOfBytesTransferred); + + if (NumberOfBytesTransferred != PIPE_BUFFER_SIZE || + memcmp(lpReadBuffer, CLIENT_MESSAGE, PIPE_BUFFER_SIZE)) + { + printf("server: received unexpected data from client\n"); + goto finish; + } + + /* 5: write to named pipe */ + + nNumberOfBytesToWrite = PIPE_BUFFER_SIZE; + NumberOfBytesTransferred = 0; + + fSuccess = WriteFile(hNamedPipe, SERVER_MESSAGE, nNumberOfBytesToWrite, NULL, &overlapped); + + if (!fSuccess) + fSuccess = (GetLastError() == ERROR_IO_PENDING); + + if (!fSuccess) + { + printf("server: NamedPipe WriteFile failure (initial): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + status = WaitForSingleObject(hEvent, PIPE_TIMEOUT_MS); + if (status != WAIT_OBJECT_0) + { + printf("server: failed to wait for overlapped event (write): %" PRIu32 "\n", status); + goto finish; + } + + fSuccess = GetOverlappedResult(hNamedPipe, &overlapped, &NumberOfBytesTransferred, FALSE); + if (!fSuccess) + { + printf("server: NamedPipe WriteFile failure (final): %" PRIu32 "\n", GetLastError()); + goto finish; + } + + printf("server: WriteFile transferred %" PRIu32 " bytes:\n", NumberOfBytesTransferred); + // winpr_HexDump("pipe.test", WLOG_DEBUG, lpWriteBuffer, NumberOfBytesTransferred); + + bServerSuccess = TRUE; + printf("server: finished successfully\n"); + +finish: + CloseHandle(hNamedPipe); + CloseHandle(hEvent); + free(lpReadBuffer); + return 0; +} + +int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) +{ + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; + int result = -1; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + FillMemory(SERVER_MESSAGE, PIPE_BUFFER_SIZE, 0xAA); + FillMemory(CLIENT_MESSAGE, PIPE_BUFFER_SIZE, 0xBB); + + if (!(serverReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("CreateEvent failed: %" PRIu32 "\n", GetLastError()); + goto out; + } + if (!(ClientThread = CreateThread(NULL, 0, named_pipe_client_thread, NULL, 0, NULL))) + { + printf("CreateThread (client) failed: %" PRIu32 "\n", GetLastError()); + goto out; + } + if (!(ServerThread = CreateThread(NULL, 0, named_pipe_server_thread, NULL, 0, NULL))) + { + printf("CreateThread (server) failed: %" PRIu32 "\n", GetLastError()); + goto out; + } + + if (WAIT_OBJECT_0 != WaitForSingleObject(ClientThread, INFINITE)) + { + printf("%s: Failed to wait for client thread: %" PRIu32 "\n", __func__, GetLastError()); + goto out; + } + if (WAIT_OBJECT_0 != WaitForSingleObject(ServerThread, INFINITE)) + { + printf("%s: Failed to wait for server thread: %" PRIu32 "\n", __func__, GetLastError()); + goto out; + } + + if (bClientSuccess && bServerSuccess) + result = 0; + +out: + + if (ClientThread) + CloseHandle(ClientThread); + if (ServerThread) + CloseHandle(ServerThread); + if (serverReadyEvent) + CloseHandle(serverReadyEvent); + +#ifndef _WIN32 + if (result == 0) + { + printf("%s: Error, this test is currently expected not to succeed on this platform.\n", + __func__); + result = -1; + } + else + { + printf("%s: This test is currently expected to fail on this platform.\n", __func__); + result = 0; + } +#endif + + return result; +} diff --git a/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c b/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c new file mode 100644 index 0000000..db346ef --- /dev/null +++ b/winpr/libwinpr/pipe/test/TestPipeCreatePipe.c @@ -0,0 +1,72 @@ + +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 16 + +int TestPipeCreatePipe(int argc, char* argv[]) +{ + BOOL status = 0; + DWORD dwRead = 0; + DWORD dwWrite = 0; + HANDLE hReadPipe = NULL; + HANDLE hWritePipe = NULL; + BYTE readBuffer[BUFFER_SIZE] = { 0 }; + BYTE writeBuffer[BUFFER_SIZE] = { 0 }; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + status = CreatePipe(&hReadPipe, &hWritePipe, NULL, BUFFER_SIZE * 2); + + if (!status) + { + _tprintf(_T("CreatePipe failed\n")); + return -1; + } + + FillMemory(writeBuffer, sizeof(writeBuffer), 0xAA); + status = WriteFile(hWritePipe, &writeBuffer, sizeof(writeBuffer), &dwWrite, NULL); + + if (!status) + { + _tprintf(_T("WriteFile failed\n")); + return -1; + } + + if (dwWrite != sizeof(writeBuffer)) + { + _tprintf(_T("WriteFile: unexpected number of bytes written: Actual: %") _T( + PRIu32) _T(", Expected: %") _T(PRIuz) _T("\n"), + dwWrite, sizeof(writeBuffer)); + return -1; + } + + status = ReadFile(hReadPipe, &readBuffer, sizeof(readBuffer), &dwRead, NULL); + + if (!status) + { + _tprintf(_T("ReadFile failed\n")); + return -1; + } + + if (dwRead != sizeof(readBuffer)) + { + _tprintf(_T("ReadFile: unexpected number of bytes read: Actual: %") _T( + PRIu32) _T(", Expected: %") _T(PRIuz) _T("\n"), + dwWrite, sizeof(readBuffer)); + return -1; + } + + if (memcmp(readBuffer, writeBuffer, BUFFER_SIZE) != 0) + { + _tprintf(_T("ReadFile: read buffer is different from write buffer\n")); + return -1; + } + + CloseHandle(hReadPipe); + CloseHandle(hWritePipe); + + return 0; +} diff --git a/winpr/libwinpr/pool/CMakeLists.txt b/winpr/libwinpr/pool/CMakeLists.txt new file mode 100644 index 0000000..2e25916 --- /dev/null +++ b/winpr/libwinpr/pool/CMakeLists.txt @@ -0,0 +1,41 @@ +# WinPR: Windows Portable Runtime +# libwinpr-pool cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + synch.c + work.c + timer.c + io.c + cleanup_group.c + pool.c + pool.h + callback.c + callback_cleanup.c) + +winpr_library_add_private( + ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_DL_LIBS}) + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + winpr_library_add_private(rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + + diff --git a/winpr/libwinpr/pool/ModuleOptions.cmake b/winpr/libwinpr/pool/ModuleOptions.cmake new file mode 100644 index 0000000..d5472ec --- /dev/null +++ b/winpr/libwinpr/pool/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "1") +set(MINWIN_SHORT_NAME "threadpool") +set(MINWIN_LONG_NAME "Thread Pool API") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/pool/callback.c b/winpr/libwinpr/pool/callback.c new file mode 100644 index 0000000..1a4dde1 --- /dev/null +++ b/winpr/libwinpr/pool/callback.c @@ -0,0 +1,54 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Callback) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static BOOL(WINAPI* pCallbackMayRunLong)(PTP_CALLBACK_INSTANCE pci); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { + pCallbackMayRunLong = (void*)GetProcAddress(kernel32, "CallbackMayRunLong"); + } + return TRUE; +} +#endif + +BOOL winpr_CallbackMayRunLong(PTP_CALLBACK_INSTANCE pci) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pCallbackMayRunLong) + return pCallbackMayRunLong(pci); +#endif + /* No default implementation */ + return FALSE; +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/callback_cleanup.c b/winpr/libwinpr/pool/callback_cleanup.c new file mode 100644 index 0000000..a4a24db --- /dev/null +++ b/winpr/libwinpr/pool/callback_cleanup.c @@ -0,0 +1,140 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Callback Clean-up) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "pool.h" + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static VOID(WINAPI* pSetEventWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HANDLE evt); +static VOID(WINAPI* pReleaseSemaphoreWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HANDLE sem, + DWORD crel); +static VOID(WINAPI* pReleaseMutexWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HANDLE mut); +static VOID(WINAPI* pLeaveCriticalSectionWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, + PCRITICAL_SECTION pcs); +static VOID(WINAPI* pFreeLibraryWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HMODULE mod); +static VOID(WINAPI* pDisassociateCurrentThreadFromCallback)(PTP_CALLBACK_INSTANCE pci); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { + pSetEventWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "SetEventWhenCallbackReturns"); + pReleaseSemaphoreWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "ReleaseSemaphoreWhenCallbackReturns"); + pReleaseMutexWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "ReleaseMutexWhenCallbackReturns"); + pLeaveCriticalSectionWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "LeaveCriticalSectionWhenCallbackReturns"); + pFreeLibraryWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "FreeLibraryWhenCallbackReturns"); + pDisassociateCurrentThreadFromCallback = + (void*)GetProcAddress(kernel32, "DisassociateCurrentThreadFromCallback"); + } + return TRUE; +} +#endif + +VOID SetEventWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE evt) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pSetEventWhenCallbackReturns) + { + pSetEventWhenCallbackReturns(pci, evt); + return; + } +#endif + /* No default implementation */ +} + +VOID ReleaseSemaphoreWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE sem, DWORD crel) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pReleaseSemaphoreWhenCallbackReturns) + { + pReleaseSemaphoreWhenCallbackReturns(pci, sem, crel); + return; + } +#endif + /* No default implementation */ +} + +VOID ReleaseMutexWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE mut) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pReleaseMutexWhenCallbackReturns) + { + pReleaseMutexWhenCallbackReturns(pci, mut); + return; + } +#endif + /* No default implementation */ +} + +VOID LeaveCriticalSectionWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, PCRITICAL_SECTION pcs) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pLeaveCriticalSectionWhenCallbackReturns) + { + pLeaveCriticalSectionWhenCallbackReturns(pci, pcs); + } +#endif + /* No default implementation */ +} + +VOID FreeLibraryWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HMODULE mod) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pFreeLibraryWhenCallbackReturns) + { + pFreeLibraryWhenCallbackReturns(pci, mod); + return; + } +#endif + /* No default implementation */ +} + +VOID DisassociateCurrentThreadFromCallback(PTP_CALLBACK_INSTANCE pci) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pDisassociateCurrentThreadFromCallback) + { + pDisassociateCurrentThreadFromCallback(pci); + return; + } +#endif + /* No default implementation */ +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/cleanup_group.c b/winpr/libwinpr/pool/cleanup_group.c new file mode 100644 index 0000000..9c2569c --- /dev/null +++ b/winpr/libwinpr/pool/cleanup_group.c @@ -0,0 +1,140 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Clean-up Group) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "pool.h" + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static PTP_CLEANUP_GROUP(WINAPI* pCreateThreadpoolCleanupGroup)(); +static VOID(WINAPI* pCloseThreadpoolCleanupGroupMembers)(PTP_CLEANUP_GROUP ptpcg, + BOOL fCancelPendingCallbacks, + PVOID pvCleanupContext); +static VOID(WINAPI* pCloseThreadpoolCleanupGroup)(PTP_CLEANUP_GROUP ptpcg); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + + if (kernel32) + { + pCreateThreadpoolCleanupGroup = + (void*)GetProcAddress(kernel32, "CreateThreadpoolCleanupGroup"); + pCloseThreadpoolCleanupGroupMembers = + (void*)GetProcAddress(kernel32, "CloseThreadpoolCleanupGroupMembers"); + pCloseThreadpoolCleanupGroup = + (void*)GetProcAddress(kernel32, "CloseThreadpoolCleanupGroup"); + } + + return TRUE; +} +#endif + +PTP_CLEANUP_GROUP winpr_CreateThreadpoolCleanupGroup(void) +{ + PTP_CLEANUP_GROUP cleanupGroup = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCreateThreadpoolCleanupGroup) + return pCreateThreadpoolCleanupGroup(); + + return cleanupGroup; +#else + cleanupGroup = (PTP_CLEANUP_GROUP)calloc(1, sizeof(TP_CLEANUP_GROUP)); + + if (!cleanupGroup) + return NULL; + + cleanupGroup->groups = ArrayList_New(FALSE); + + if (!cleanupGroup->groups) + { + free(cleanupGroup); + return NULL; + } + + return cleanupGroup; +#endif +} + +VOID winpr_SetThreadpoolCallbackCleanupGroup(PTP_CALLBACK_ENVIRON pcbe, PTP_CLEANUP_GROUP ptpcg, + PTP_CLEANUP_GROUP_CANCEL_CALLBACK pfng) +{ + pcbe->CleanupGroup = ptpcg; + pcbe->CleanupGroupCancelCallback = pfng; +#ifndef _WIN32 + pcbe->CleanupGroup->env = pcbe; +#endif +} + +VOID winpr_CloseThreadpoolCleanupGroupMembers(PTP_CLEANUP_GROUP ptpcg, BOOL fCancelPendingCallbacks, + PVOID pvCleanupContext) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCloseThreadpoolCleanupGroupMembers) + { + pCloseThreadpoolCleanupGroupMembers(ptpcg, fCancelPendingCallbacks, pvCleanupContext); + return; + } + +#else + + while (ArrayList_Count(ptpcg->groups) > 0) + { + PTP_WORK work = ArrayList_GetItem(ptpcg->groups, 0); + winpr_CloseThreadpoolWork(work); + } + +#endif +} + +VOID winpr_CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCloseThreadpoolCleanupGroup) + { + pCloseThreadpoolCleanupGroup(ptpcg); + return; + } + +#else + + if (ptpcg && ptpcg->groups) + ArrayList_Free(ptpcg->groups); + + if (ptpcg && ptpcg->env) + ptpcg->env->CleanupGroup = NULL; + + free(ptpcg); +#endif +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/io.c b/winpr/libwinpr/pool/io.c new file mode 100644 index 0000000..7442c37 --- /dev/null +++ b/winpr/libwinpr/pool/io.c @@ -0,0 +1,49 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (I/O) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#ifdef WINPR_THREAD_POOL + +PTP_IO winpr_CreateThreadpoolIo(HANDLE fl, PTP_WIN32_IO_CALLBACK pfnio, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe) +{ + return NULL; +} + +VOID winpr_CloseThreadpoolIo(PTP_IO pio) +{ +} + +VOID winpr_StartThreadpoolIo(PTP_IO pio) +{ +} + +VOID winpr_CancelThreadpoolIo(PTP_IO pio) +{ +} + +VOID winpr_WaitForThreadpoolIoCallbacks(PTP_IO pio, BOOL fCancelPendingCallbacks) +{ +} + +#endif diff --git a/winpr/libwinpr/pool/pool.c b/winpr/libwinpr/pool/pool.c new file mode 100644 index 0000000..6c66b42 --- /dev/null +++ b/winpr/libwinpr/pool/pool.c @@ -0,0 +1,251 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Pool) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "pool.h" + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static PTP_POOL(WINAPI* pCreateThreadpool)(PVOID reserved); +static VOID(WINAPI* pCloseThreadpool)(PTP_POOL ptpp); +static BOOL(WINAPI* pSetThreadpoolThreadMinimum)(PTP_POOL ptpp, DWORD cthrdMic); +static VOID(WINAPI* pSetThreadpoolThreadMaximum)(PTP_POOL ptpp, DWORD cthrdMost); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { + pCreateThreadpool = (void*)GetProcAddress(kernel32, "CreateThreadpool"); + pCloseThreadpool = (void*)GetProcAddress(kernel32, "CloseThreadpool"); + pSetThreadpoolThreadMinimum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMinimum"); + pSetThreadpoolThreadMaximum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMaximum"); + } + return TRUE; +} +#endif + +static TP_POOL DEFAULT_POOL = { + 0, /* DWORD Minimum */ + 500, /* DWORD Maximum */ + NULL, /* wArrayList* Threads */ + NULL, /* wQueue* PendingQueue */ + NULL, /* HANDLE TerminateEvent */ + NULL, /* wCountdownEvent* WorkComplete */ +}; + +static DWORD WINAPI thread_pool_work_func(LPVOID arg) +{ + DWORD status = 0; + PTP_POOL pool = NULL; + PTP_WORK work = NULL; + HANDLE events[2]; + PTP_CALLBACK_INSTANCE callbackInstance = NULL; + + pool = (PTP_POOL)arg; + + events[0] = pool->TerminateEvent; + events[1] = Queue_Event(pool->PendingQueue); + + while (1) + { + status = WaitForMultipleObjects(2, events, FALSE, INFINITE); + + if (status == WAIT_OBJECT_0) + break; + + if (status != (WAIT_OBJECT_0 + 1)) + break; + + callbackInstance = (PTP_CALLBACK_INSTANCE)Queue_Dequeue(pool->PendingQueue); + + if (callbackInstance) + { + work = callbackInstance->Work; + work->WorkCallback(callbackInstance, work->CallbackParameter, work); + CountdownEvent_Signal(pool->WorkComplete, 1); + free(callbackInstance); + } + } + + ExitThread(0); + return 0; +} + +static void threads_close(void* thread) +{ + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +} + +static BOOL InitializeThreadpool(PTP_POOL pool) +{ + BOOL rc = FALSE; + wObject* obj = NULL; + HANDLE thread = NULL; + + if (pool->Threads) + return TRUE; + + pool->Minimum = 0; + pool->Maximum = 500; + + if (!(pool->PendingQueue = Queue_New(TRUE, -1, -1))) + goto fail; + + if (!(pool->WorkComplete = CountdownEvent_New(0))) + goto fail; + + if (!(pool->TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + goto fail; + + if (!(pool->Threads = ArrayList_New(TRUE))) + goto fail; + + obj = ArrayList_Object(pool->Threads); + obj->fnObjectFree = threads_close; + + for (int index = 0; index < 4; index++) + { + if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)pool, 0, NULL))) + { + goto fail; + } + + if (!ArrayList_Append(pool->Threads, thread)) + { + CloseHandle(thread); + goto fail; + } + } + + rc = TRUE; + +fail: + return rc; +} + +PTP_POOL GetDefaultThreadpool(void) +{ + PTP_POOL pool = NULL; + + pool = &DEFAULT_POOL; + + if (!InitializeThreadpool(pool)) + return NULL; + + return pool; +} + +PTP_POOL winpr_CreateThreadpool(PVOID reserved) +{ + PTP_POOL pool = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pCreateThreadpool) + return pCreateThreadpool(reserved); +#else + WINPR_UNUSED(reserved); +#endif + if (!(pool = (PTP_POOL)calloc(1, sizeof(TP_POOL)))) + return NULL; + + if (!InitializeThreadpool(pool)) + { + winpr_CloseThreadpool(pool); + return NULL; + } + + return pool; +} + +VOID winpr_CloseThreadpool(PTP_POOL ptpp) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pCloseThreadpool) + { + pCloseThreadpool(ptpp); + return; + } +#endif + SetEvent(ptpp->TerminateEvent); + + ArrayList_Free(ptpp->Threads); + Queue_Free(ptpp->PendingQueue); + CountdownEvent_Free(ptpp->WorkComplete); + CloseHandle(ptpp->TerminateEvent); + + { + TP_POOL empty = { 0 }; + *ptpp = empty; + } + + if (ptpp != &DEFAULT_POOL) + free(ptpp); +} + +BOOL winpr_SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic) +{ + HANDLE thread = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pSetThreadpoolThreadMinimum) + return pSetThreadpoolThreadMinimum(ptpp, cthrdMic); +#endif + ptpp->Minimum = cthrdMic; + + while (ArrayList_Count(ptpp->Threads) < ptpp->Minimum) + { + if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)ptpp, 0, NULL))) + { + return FALSE; + } + + if (!ArrayList_Append(ptpp->Threads, thread)) + { + CloseHandle(thread); + return FALSE; + } + } + + return TRUE; +} + +VOID winpr_SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pSetThreadpoolThreadMaximum) + { + pSetThreadpoolThreadMaximum(ptpp, cthrdMost); + return; + } +#endif + ptpp->Maximum = cthrdMost; +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/pool.h b/winpr/libwinpr/pool/pool.h new file mode 100644 index 0000000..7e8cf4c --- /dev/null +++ b/winpr/libwinpr/pool/pool.h @@ -0,0 +1,122 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Pool) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_POOL_PRIVATE_H +#define WINPR_POOL_PRIVATE_H + +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#if (_WIN32_WINNT < _WIN32_WINNT_WIN6) || defined(__MINGW32__) +struct S_TP_CALLBACK_INSTANCE +{ + PTP_WORK Work; +}; + +struct S_TP_POOL +{ + DWORD Minimum; + DWORD Maximum; + wArrayList* Threads; + wQueue* PendingQueue; + HANDLE TerminateEvent; + wCountdownEvent* WorkComplete; +}; + +struct S_TP_WORK +{ + PVOID CallbackParameter; + PTP_WORK_CALLBACK WorkCallback; + PTP_CALLBACK_ENVIRON CallbackEnvironment; +}; + +struct S_TP_TIMER +{ + void* dummy; +}; + +struct S_TP_WAIT +{ + void* dummy; +}; + +struct S_TP_IO +{ + void* dummy; +}; + +struct S_TP_CLEANUP_GROUP +{ + void* dummy; +}; + +#endif +#else +struct S_TP_CALLBACK_INSTANCE +{ + PTP_WORK Work; +}; + +struct S_TP_POOL +{ + DWORD Minimum; + DWORD Maximum; + wArrayList* Threads; + wQueue* PendingQueue; + HANDLE TerminateEvent; + wCountdownEvent* WorkComplete; +}; + +struct S_TP_WORK +{ + PVOID CallbackParameter; + PTP_WORK_CALLBACK WorkCallback; + PTP_CALLBACK_ENVIRON CallbackEnvironment; +}; + +struct S_TP_TIMER +{ + void* dummy; +}; + +struct S_TP_WAIT +{ + void* dummy; +}; + +struct S_TP_IO +{ + void* dummy; +}; + +struct S_TP_CLEANUP_GROUP +{ + wArrayList* groups; + PTP_CALLBACK_ENVIRON env; +}; + +#endif + +PTP_POOL GetDefaultThreadpool(void); + +#endif /* WINPR_POOL_PRIVATE_H */ diff --git a/winpr/libwinpr/pool/synch.c b/winpr/libwinpr/pool/synch.c new file mode 100644 index 0000000..1586a0e --- /dev/null +++ b/winpr/libwinpr/pool/synch.c @@ -0,0 +1,44 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Synch) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#ifdef WINPR_THREAD_POOL + +PTP_WAIT winpr_CreateThreadpoolWait(PTP_WAIT_CALLBACK pfnwa, PVOID pv, PTP_CALLBACK_ENVIRON pcbe) +{ + return NULL; +} + +VOID winpr_CloseThreadpoolWait(PTP_WAIT pwa) +{ +} + +VOID winpr_SetThreadpoolWait(PTP_WAIT pwa, HANDLE h, PFILETIME pftTimeout) +{ +} + +VOID winpr_WaitForThreadpoolWaitCallbacks(PTP_WAIT pwa, BOOL fCancelPendingCallbacks) +{ +} + +#endif diff --git a/winpr/libwinpr/pool/test/CMakeLists.txt b/winpr/libwinpr/pool/test/CMakeLists.txt new file mode 100644 index 0000000..9758fff --- /dev/null +++ b/winpr/libwinpr/pool/test/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(MODULE_NAME "TestPool") +set(MODULE_PREFIX "TEST_POOL") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestPoolIO.c + TestPoolSynch.c + TestPoolThread.c + TestPoolTimer.c + TestPoolWork.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/pool/test/TestPoolIO.c b/winpr/libwinpr/pool/test/TestPoolIO.c new file mode 100644 index 0000000..d68586e --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolIO.c @@ -0,0 +1,8 @@ + +#include +#include + +int TestPoolIO(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolSynch.c b/winpr/libwinpr/pool/test/TestPoolSynch.c new file mode 100644 index 0000000..4d6f381 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolSynch.c @@ -0,0 +1,8 @@ + +#include +#include + +int TestPoolSynch(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolThread.c b/winpr/libwinpr/pool/test/TestPoolThread.c new file mode 100644 index 0000000..d006ae6 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolThread.c @@ -0,0 +1,41 @@ + +#include +#include + +/** + * Improve Scalability With New Thread Pool APIs: + * http://msdn.microsoft.com/en-us/magazine/cc16332.aspx + * + * Developing with Thread Pool Enhancements: + * http://msdn.microsoft.com/en-us/library/cc308561.aspx + * + * Introduction to the Windows Threadpool: + * http://blogs.msdn.com/b/harip/archive/2010/10/11/introduction-to-the-windows-threadpool-part-1.aspx + * http://blogs.msdn.com/b/harip/archive/2010/10/12/introduction-to-the-windows-threadpool-part-2.aspx + */ + +int TestPoolThread(int argc, char* argv[]) +{ + TP_POOL* pool = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!(pool = CreateThreadpool(NULL))) + { + printf("CreateThreadpool failed\n"); + return -1; + } + + if (!SetThreadpoolThreadMinimum(pool, 8)) /* default is 0 */ + { + printf("SetThreadpoolThreadMinimum failed\n"); + return -1; + } + + SetThreadpoolThreadMaximum(pool, 64); /* default is 500 */ + + CloseThreadpool(pool); + + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolTimer.c b/winpr/libwinpr/pool/test/TestPoolTimer.c new file mode 100644 index 0000000..c749bc7 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolTimer.c @@ -0,0 +1,8 @@ + +#include +#include + +int TestPoolTimer(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolWork.c b/winpr/libwinpr/pool/test/TestPoolWork.c new file mode 100644 index 0000000..ec50a22 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolWork.c @@ -0,0 +1,136 @@ + +#include +#include +#include +#include + +static LONG count = 0; + +static void CALLBACK test_WorkCallback(PTP_CALLBACK_INSTANCE instance, void* context, PTP_WORK work) +{ + printf("Hello %s: %03" PRId32 " (thread: 0x%08" PRIX32 ")\n", (char*)context, + InterlockedIncrement(&count), GetCurrentThreadId()); + + for (int index = 0; index < 100; index++) + { + BYTE a[1024]; + BYTE b[1024]; + BYTE c[1024] = { 0 }; + + FillMemory(a, ARRAYSIZE(a), 0xAA); + FillMemory(b, ARRAYSIZE(b), 0xBB); + + CopyMemory(c, a, ARRAYSIZE(a)); + CopyMemory(c, b, ARRAYSIZE(b)); + } +} + +static BOOL test1(void) +{ + PTP_WORK work = NULL; + printf("Global Thread Pool\n"); + work = CreateThreadpoolWork(test_WorkCallback, "world", NULL); + + if (!work) + { + printf("CreateThreadpoolWork failure\n"); + return FALSE; + } + + /** + * You can post a work object one or more times (up to MAXULONG) without waiting for prior + * callbacks to complete. The callbacks will execute in parallel. To improve efficiency, the + * thread pool may throttle the threads. + */ + + for (int index = 0; index < 10; index++) + SubmitThreadpoolWork(work); + + WaitForThreadpoolWorkCallbacks(work, FALSE); + CloseThreadpoolWork(work); + return TRUE; +} + +static BOOL test2(void) +{ + BOOL rc = FALSE; + PTP_POOL pool = NULL; + PTP_WORK work = NULL; + PTP_CLEANUP_GROUP cleanupGroup = NULL; + TP_CALLBACK_ENVIRON environment; + printf("Private Thread Pool\n"); + + if (!(pool = CreateThreadpool(NULL))) + { + printf("CreateThreadpool failure\n"); + return FALSE; + } + + if (!SetThreadpoolThreadMinimum(pool, 4)) + { + printf("SetThreadpoolThreadMinimum failure\n"); + goto fail; + } + + SetThreadpoolThreadMaximum(pool, 8); + InitializeThreadpoolEnvironment(&environment); + SetThreadpoolCallbackPool(&environment, pool); + cleanupGroup = CreateThreadpoolCleanupGroup(); + + if (!cleanupGroup) + { + printf("CreateThreadpoolCleanupGroup failure\n"); + goto fail; + } + + SetThreadpoolCallbackCleanupGroup(&environment, cleanupGroup, NULL); + work = CreateThreadpoolWork(test_WorkCallback, "world", &environment); + + if (!work) + { + printf("CreateThreadpoolWork failure\n"); + goto fail; + } + + for (int index = 0; index < 10; index++) + SubmitThreadpoolWork(work); + + WaitForThreadpoolWorkCallbacks(work, FALSE); + rc = TRUE; +fail: + + if (cleanupGroup) + { + CloseThreadpoolCleanupGroupMembers(cleanupGroup, TRUE, NULL); + CloseThreadpoolCleanupGroup(cleanupGroup); + DestroyThreadpoolEnvironment(&environment); + /** + * See Remarks at + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682043(v=vs.85).aspx If there + * is a cleanup group associated with the work object, it is not necessary to call + * CloseThreadpoolWork ! calling the CloseThreadpoolCleanupGroupMembers function releases + * the work, wait, and timer objects associated with the cleanup group. + */ +#if 0 + CloseThreadpoolWork(work); // this would segfault, see comment above. */ +#endif + } + + CloseThreadpool(pool); + return rc; +} + +int TestPoolWork(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test1()) + return -1; + + if (!test2()) + return -1; + + return 0; +} diff --git a/winpr/libwinpr/pool/timer.c b/winpr/libwinpr/pool/timer.c new file mode 100644 index 0000000..a8aa1a7 --- /dev/null +++ b/winpr/libwinpr/pool/timer.c @@ -0,0 +1,50 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Timer) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#ifdef WINPR_THREAD_POOL + +PTP_TIMER winpr_CreateThreadpoolTimer(PTP_TIMER_CALLBACK pfnti, PVOID pv, PTP_CALLBACK_ENVIRON pcbe) +{ + return NULL; +} + +VOID winpr_CloseThreadpoolTimer(PTP_TIMER pti) +{ +} + +BOOL winpr_IsThreadpoolTimerSet(PTP_TIMER pti) +{ + return FALSE; +} + +VOID winpr_SetThreadpoolTimer(PTP_TIMER pti, PFILETIME pftDueTime, DWORD msPeriod, + DWORD msWindowLength) +{ +} + +VOID winpr_WaitForThreadpoolTimerCallbacks(PTP_TIMER pti, BOOL fCancelPendingCallbacks) +{ +} + +#endif diff --git a/winpr/libwinpr/pool/work.c b/winpr/libwinpr/pool/work.c new file mode 100644 index 0000000..e83f417 --- /dev/null +++ b/winpr/libwinpr/pool/work.c @@ -0,0 +1,199 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Work) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include "pool.h" +#include "../log.h" +#define TAG WINPR_TAG("pool") + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static PTP_WORK(WINAPI* pCreateThreadpoolWork)(PTP_WORK_CALLBACK pfnwk, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); +static VOID(WINAPI* pCloseThreadpoolWork)(PTP_WORK pwk); +static VOID(WINAPI* pSubmitThreadpoolWork)(PTP_WORK pwk); +static BOOL(WINAPI* pTrySubmitThreadpoolCallback)(PTP_SIMPLE_CALLBACK pfns, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); +static VOID(WINAPI* pWaitForThreadpoolWorkCallbacks)(PTP_WORK pwk, BOOL fCancelPendingCallbacks); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + + if (kernel32) + { + pCreateThreadpoolWork = (void*)GetProcAddress(kernel32, "CreateThreadpoolWork"); + pCloseThreadpoolWork = (void*)GetProcAddress(kernel32, "CloseThreadpoolWork"); + pSubmitThreadpoolWork = (void*)GetProcAddress(kernel32, "SubmitThreadpoolWork"); + pTrySubmitThreadpoolCallback = + (void*)GetProcAddress(kernel32, "TrySubmitThreadpoolCallback"); + pWaitForThreadpoolWorkCallbacks = + (void*)GetProcAddress(kernel32, "WaitForThreadpoolWorkCallbacks"); + } + + return TRUE; +} +#endif + +static TP_CALLBACK_ENVIRON DEFAULT_CALLBACK_ENVIRONMENT = { + 1, /* Version */ + NULL, /* Pool */ + NULL, /* CleanupGroup */ + NULL, /* CleanupGroupCancelCallback */ + NULL, /* RaceDll */ + NULL, /* FinalizationCallback */ + { 0 } /* Flags */ +}; + +PTP_WORK winpr_CreateThreadpoolWork(PTP_WORK_CALLBACK pfnwk, PVOID pv, PTP_CALLBACK_ENVIRON pcbe) +{ + PTP_WORK work = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCreateThreadpoolWork) + return pCreateThreadpoolWork(pfnwk, pv, pcbe); + +#endif + work = (PTP_WORK)calloc(1, sizeof(TP_WORK)); + + if (work) + { + if (!pcbe) + { + pcbe = &DEFAULT_CALLBACK_ENVIRONMENT; + pcbe->Pool = GetDefaultThreadpool(); + } + + work->CallbackEnvironment = pcbe; + work->WorkCallback = pfnwk; + work->CallbackParameter = pv; +#ifndef _WIN32 + + if (pcbe->CleanupGroup) + ArrayList_Append(pcbe->CleanupGroup->groups, work); + +#endif + } + + return work; +} + +VOID winpr_CloseThreadpoolWork(PTP_WORK pwk) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCloseThreadpoolWork) + { + pCloseThreadpoolWork(pwk); + return; + } + +#else + + WINPR_ASSERT(pwk); + WINPR_ASSERT(pwk->CallbackEnvironment); + if (pwk->CallbackEnvironment->CleanupGroup) + ArrayList_Remove(pwk->CallbackEnvironment->CleanupGroup->groups, pwk); + +#endif + free(pwk); +} + +VOID winpr_SubmitThreadpoolWork(PTP_WORK pwk) +{ + PTP_POOL pool = NULL; + PTP_CALLBACK_INSTANCE callbackInstance = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pSubmitThreadpoolWork) + { + pSubmitThreadpoolWork(pwk); + return; + } + +#endif + + WINPR_ASSERT(pwk); + WINPR_ASSERT(pwk->CallbackEnvironment); + pool = pwk->CallbackEnvironment->Pool; + callbackInstance = (PTP_CALLBACK_INSTANCE)calloc(1, sizeof(TP_CALLBACK_INSTANCE)); + + if (callbackInstance) + { + callbackInstance->Work = pwk; + CountdownEvent_AddCount(pool->WorkComplete, 1); + if (!Queue_Enqueue(pool->PendingQueue, callbackInstance)) + free(callbackInstance); + } + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): Queue_Enqueue takes ownership of callbackInstance +} + +BOOL winpr_TrySubmitThreadpoolCallback(PTP_SIMPLE_CALLBACK pfns, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pTrySubmitThreadpoolCallback) + return pTrySubmitThreadpoolCallback(pfns, pv, pcbe); + +#endif + WLog_ERR(TAG, "TrySubmitThreadpoolCallback is not implemented"); + return FALSE; +} + +VOID winpr_WaitForThreadpoolWorkCallbacks(PTP_WORK pwk, BOOL fCancelPendingCallbacks) +{ + HANDLE event = NULL; + PTP_POOL pool = NULL; + +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pWaitForThreadpoolWorkCallbacks) + { + pWaitForThreadpoolWorkCallbacks(pwk, fCancelPendingCallbacks); + return; + } + +#endif + WINPR_ASSERT(pwk); + WINPR_ASSERT(pwk->CallbackEnvironment); + + pool = pwk->CallbackEnvironment->Pool; + WINPR_ASSERT(pool); + + event = CountdownEvent_WaitHandle(pool->WorkComplete); + + if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) + WLog_ERR(TAG, "error waiting on work completion"); +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/registry/CMakeLists.txt b/winpr/libwinpr/registry/CMakeLists.txt new file mode 100644 index 0000000..4400afd --- /dev/null +++ b/winpr/libwinpr/registry/CMakeLists.txt @@ -0,0 +1,21 @@ +# WinPR: Windows Portable Runtime +# libwinpr-registry cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + registry_reg.c + registry_reg.h + registry.c) diff --git a/winpr/libwinpr/registry/ModuleOptions.cmake b/winpr/libwinpr/registry/ModuleOptions.cmake new file mode 100644 index 0000000..a83eb09 --- /dev/null +++ b/winpr/libwinpr/registry/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "registry") +set(MINWIN_LONG_NAME "Registry Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/registry/registry.c b/winpr/libwinpr/registry/registry.c new file mode 100644 index 0000000..7422d8b --- /dev/null +++ b/winpr/libwinpr/registry/registry.c @@ -0,0 +1,554 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Registry + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/* + * Windows registry MSDN pages: + * Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724880/ + * Functions: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724875/ + */ + +#if !defined(_WIN32) || defined(_UWP) + +#include +#include +#include + +#include +#include + +#include "registry_reg.h" + +#include "../log.h" +#define TAG WINPR_TAG("registry") + +static Reg* instance = NULL; + +static Reg* RegGetInstance(void) +{ + if (!instance) + instance = reg_open(1); + + return instance; +} + +LONG RegCloseKey(HKEY hKey) +{ + WINPR_UNUSED(hKey); + return 0; +} + +LONG RegCopyTreeW(HKEY hKeySrc, LPCWSTR lpSubKey, HKEY hKeyDest) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegCopyTreeA(HKEY hKeySrc, LPCSTR lpSubKey, HKEY hKeyDest) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, + REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, + LPDWORD lpdwDisposition) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegCreateKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD Reserved, LPSTR lpClass, DWORD dwOptions, + REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, + LPDWORD lpdwDisposition) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDeleteKeyExW(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDeleteKeyExA(HKEY hKey, LPCSTR lpSubKey, REGSAM samDesired, DWORD Reserved) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDeleteTreeW(HKEY hKey, LPCWSTR lpSubKey) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDeleteTreeA(HKEY hKey, LPCSTR lpSubKey) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDeleteValueW(HKEY hKey, LPCWSTR lpValueName) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDeleteValueA(HKEY hKey, LPCSTR lpValueName) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegDisablePredefinedCacheEx(void) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegEnumKeyExW(HKEY hKey, DWORD dwIndex, LPWSTR lpName, LPDWORD lpcName, LPDWORD lpReserved, + LPWSTR lpClass, LPDWORD lpcClass, PFILETIME lpftLastWriteTime) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegEnumKeyExA(HKEY hKey, DWORD dwIndex, LPSTR lpName, LPDWORD lpcName, LPDWORD lpReserved, + LPSTR lpClass, LPDWORD lpcClass, PFILETIME lpftLastWriteTime) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegEnumValueW(HKEY hKey, DWORD dwIndex, LPWSTR lpValueName, LPDWORD lpcchValueName, + LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegEnumValueA(HKEY hKey, DWORD dwIndex, LPSTR lpValueName, LPDWORD lpcchValueName, + LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegFlushKey(HKEY hKey) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegGetKeySecurity(HKEY hKey, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, LPDWORD lpcbSecurityDescriptor) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegGetValueW(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, LPDWORD pdwType, + PVOID pvData, LPDWORD pcbData) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegGetValueA(HKEY hkey, LPCSTR lpSubKey, LPCSTR lpValue, DWORD dwFlags, LPDWORD pdwType, + PVOID pvData, LPDWORD pcbData) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegLoadAppKeyW(LPCWSTR lpFile, PHKEY phkResult, REGSAM samDesired, DWORD dwOptions, + DWORD Reserved) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegLoadAppKeyA(LPCSTR lpFile, PHKEY phkResult, REGSAM samDesired, DWORD dwOptions, + DWORD Reserved) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegLoadKeyW(HKEY hKey, LPCWSTR lpSubKey, LPCWSTR lpFile) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegLoadKeyA(HKEY hKey, LPCSTR lpSubKey, LPCSTR lpFile) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegLoadMUIStringW(HKEY hKey, LPCWSTR pszValue, LPWSTR pszOutBuf, DWORD cbOutBuf, + LPDWORD pcbData, DWORD Flags, LPCWSTR pszDirectory) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegLoadMUIStringA(HKEY hKey, LPCSTR pszValue, LPSTR pszOutBuf, DWORD cbOutBuf, LPDWORD pcbData, + DWORD Flags, LPCSTR pszDirectory) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegNotifyChangeKeyValue(HKEY hKey, BOOL bWatchSubtree, DWORD dwNotifyFilter, HANDLE hEvent, + BOOL fAsynchronous) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegOpenCurrentUser(REGSAM samDesired, PHKEY phkResult) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) +{ + LONG rc = 0; + char* str = ConvertWCharToUtf8Alloc(lpSubKey, NULL); + if (!str) + return ERROR_FILE_NOT_FOUND; + + rc = RegOpenKeyExA(hKey, str, ulOptions, samDesired, phkResult); + free(str); + return rc; +} + +LONG RegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) +{ + Reg* reg = RegGetInstance(); + + if (!reg) + return -1; + + if (hKey != HKEY_LOCAL_MACHINE) + { + WLog_WARN(TAG, "Registry emulation only supports HKEY_LOCAL_MACHINE"); + return ERROR_FILE_NOT_FOUND; + } + + WINPR_ASSERT(reg->root_key); + RegKey* pKey = reg->root_key->subkeys; + + while (pKey != NULL) + { + WINPR_ASSERT(lpSubKey); + + if (pKey->subname && (_stricmp(pKey->subname, lpSubKey) == 0)) + { + *phkResult = (HKEY)pKey; + return ERROR_SUCCESS; + } + + pKey = pKey->next; + } + + *phkResult = NULL; + + return ERROR_FILE_NOT_FOUND; +} + +LONG RegOpenUserClassesRoot(HANDLE hToken, DWORD dwOptions, REGSAM samDesired, PHKEY phkResult) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegQueryInfoKeyW(HKEY hKey, LPWSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, + LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen, LPDWORD lpcMaxClassLen, + LPDWORD lpcValues, LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, + LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegQueryInfoKeyA(HKEY hKey, LPSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, + LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen, LPDWORD lpcMaxClassLen, + LPDWORD lpcValues, LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, + LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +static LONG reg_read_int(const RegVal* pValue, LPBYTE lpData, LPDWORD lpcbData) +{ + const BYTE* ptr = NULL; + DWORD required = 0; + + WINPR_ASSERT(pValue); + + switch (pValue->type) + { + case REG_DWORD: + case REG_DWORD_BIG_ENDIAN: + required = sizeof(DWORD); + ptr = (const BYTE*)&pValue->data.dword; + break; + case REG_QWORD: + required = sizeof(UINT64); + ptr = (const BYTE*)&pValue->data.qword; + break; + default: + return ERROR_INTERNAL_ERROR; + } + + if (lpcbData) + { + DWORD size = *lpcbData; + *lpcbData = required; + if (lpData) + { + if (size < *lpcbData) + return ERROR_MORE_DATA; + } + } + + if (lpData != NULL) + { + DWORD size = 0; + WINPR_ASSERT(lpcbData); + + size = *lpcbData; + *lpcbData = required; + if (size < required) + return ERROR_MORE_DATA; + memcpy(lpData, ptr, required); + } + else if (lpcbData != NULL) + *lpcbData = required; + return ERROR_SUCCESS; +} + +LONG RegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, + LPBYTE lpData, LPDWORD lpcbData) +{ + LONG status = ERROR_FILE_NOT_FOUND; + RegKey* key = NULL; + RegVal* pValue = NULL; + char* valueName = NULL; + + WINPR_UNUSED(lpReserved); + + key = (RegKey*)hKey; + WINPR_ASSERT(key); + + valueName = ConvertWCharToUtf8Alloc(lpValueName, NULL); + if (!valueName) + goto end; + + pValue = key->values; + + while (pValue != NULL) + { + if (strcmp(pValue->name, valueName) == 0) + { + if (lpType) + *lpType = pValue->type; + + switch (pValue->type) + { + case REG_DWORD_BIG_ENDIAN: + case REG_QWORD: + case REG_DWORD: + return reg_read_int(pValue, lpData, lpcbData); + case REG_SZ: + { + const size_t length = strnlen(pValue->data.string, UINT32_MAX) * sizeof(WCHAR); + + if (lpData != NULL) + { + DWORD size = 0; + union + { + WCHAR* wc; + BYTE* b; + } cnv; + WINPR_ASSERT(lpcbData); + + cnv.b = lpData; + size = *lpcbData; + *lpcbData = (DWORD)length; + if (size < length) + return ERROR_MORE_DATA; + if (ConvertUtf8NToWChar(pValue->data.string, length, cnv.wc, length) < 0) + return ERROR_OUTOFMEMORY; + } + else if (lpcbData) + *lpcbData = (UINT32)length; + + status = ERROR_SUCCESS; + goto end; + } + default: + WLog_WARN(TAG, + "Registry emulation does not support value type %s [0x%08" PRIx32 "]", + reg_type_string(pValue->type), pValue->type); + break; + } + } + + pValue = pValue->next; + } + +end: + free(valueName); + return status; +} + +LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, + LPBYTE lpData, LPDWORD lpcbData) +{ + RegKey* key = NULL; + RegVal* pValue = NULL; + + WINPR_UNUSED(lpReserved); + + key = (RegKey*)hKey; + WINPR_ASSERT(key); + + pValue = key->values; + + while (pValue != NULL) + { + if (strcmp(pValue->name, lpValueName) == 0) + { + if (lpType) + *lpType = pValue->type; + + switch (pValue->type) + { + case REG_DWORD_BIG_ENDIAN: + case REG_QWORD: + case REG_DWORD: + return reg_read_int(pValue, lpData, lpcbData); + case REG_SZ: + { + const size_t length = strnlen(pValue->data.string, UINT32_MAX); + char* pData = (char*)lpData; + + if (pData != NULL) + { + DWORD size = 0; + WINPR_ASSERT(lpcbData); + + size = *lpcbData; + *lpcbData = (DWORD)length; + if (size < length) + return ERROR_MORE_DATA; + memcpy(pData, pValue->data.string, length); + pData[length] = '\0'; + } + else if (lpcbData) + *lpcbData = (UINT32)length; + + return ERROR_SUCCESS; + } + default: + WLog_WARN(TAG, + "Registry emulation does not support value type %s [0x%08" PRIx32 "]", + reg_type_string(pValue->type), pValue->type); + break; + } + } + + pValue = pValue->next; + } + + return ERROR_FILE_NOT_FOUND; +} + +LONG RegRestoreKeyW(HKEY hKey, LPCWSTR lpFile, DWORD dwFlags) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegRestoreKeyA(HKEY hKey, LPCSTR lpFile, DWORD dwFlags) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegSaveKeyExW(HKEY hKey, LPCWSTR lpFile, LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD Flags) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegSaveKeyExA(HKEY hKey, LPCSTR lpFile, LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD Flags) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegSetKeySecurity(HKEY hKey, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, + const BYTE* lpData, DWORD cbData) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegSetValueExA(HKEY hKey, LPCSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, + DWORD cbData) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegUnLoadKeyW(HKEY hKey, LPCWSTR lpSubKey) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +LONG RegUnLoadKeyA(HKEY hKey, LPCSTR lpSubKey) +{ + WLog_ERR(TAG, "TODO: Implement"); + return -1; +} + +#endif diff --git a/winpr/libwinpr/registry/registry_reg.c b/winpr/libwinpr/registry/registry_reg.c new file mode 100644 index 0000000..a1803b6 --- /dev/null +++ b/winpr/libwinpr/registry/registry_reg.c @@ -0,0 +1,570 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Registry (.reg file format) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "registry_reg.h" + +#include "../log.h" +#define TAG WINPR_TAG("registry") + +#define WINPR_HKLM_HIVE "/etc/winpr/HKLM.reg" + +struct reg_data_type +{ + char* tag; + size_t length; + DWORD type; +}; + +static struct reg_data_type REG_DATA_TYPE_TABLE[] = { { "\"", 1, REG_SZ }, + { "dword:", 6, REG_DWORD }, + { "str:\"", 5, REG_SZ }, + { "str(2):\"", 8, REG_EXPAND_SZ }, + { "str(7):\"", 8, REG_MULTI_SZ }, + { "hex:", 4, REG_BINARY }, + { "hex(2):\"", 8, REG_EXPAND_SZ }, + { "hex(7):\"", 8, REG_MULTI_SZ }, + { "hex(b):\"", 8, REG_QWORD } }; + +static char* reg_data_type_string(DWORD type) +{ + switch (type) + { + case REG_NONE: + return "REG_NONE"; + case REG_SZ: + return "REG_SZ"; + case REG_EXPAND_SZ: + return "REG_EXPAND_SZ"; + case REG_BINARY: + return "REG_BINARY"; + case REG_DWORD: + return "REG_DWORD"; + case REG_DWORD_BIG_ENDIAN: + return "REG_DWORD_BIG_ENDIAN"; + case REG_LINK: + return "REG_LINK"; + case REG_MULTI_SZ: + return "REG_MULTI_SZ"; + case REG_RESOURCE_LIST: + return "REG_RESOURCE_LIST"; + case REG_FULL_RESOURCE_DESCRIPTOR: + return "REG_FULL_RESOURCE_DESCRIPTOR"; + case REG_RESOURCE_REQUIREMENTS_LIST: + return "REG_RESOURCE_REQUIREMENTS_LIST"; + case REG_QWORD: + return "REG_QWORD"; + default: + return "REG_UNKNOWN"; + } +} + +static BOOL reg_load_start(Reg* reg) +{ + char* buffer = NULL; + INT64 file_size = 0; + + WINPR_ASSERT(reg); + WINPR_ASSERT(reg->fp); + + _fseeki64(reg->fp, 0, SEEK_END); + file_size = _ftelli64(reg->fp); + _fseeki64(reg->fp, 0, SEEK_SET); + reg->line = NULL; + reg->next_line = NULL; + + if (file_size < 1) + return FALSE; + + buffer = (char*)realloc(reg->buffer, (size_t)file_size + 2); + + if (!buffer) + return FALSE; + reg->buffer = buffer; + + if (fread(reg->buffer, (size_t)file_size, 1, reg->fp) != 1) + return FALSE; + + reg->buffer[file_size] = '\n'; + reg->buffer[file_size + 1] = '\0'; + reg->next_line = strtok(reg->buffer, "\n"); + return TRUE; +} + +static void reg_load_finish(Reg* reg) +{ + if (!reg) + return; + + if (reg->buffer) + { + free(reg->buffer); + reg->buffer = NULL; + } +} + +static RegVal* reg_load_value(const Reg* reg, RegKey* key) +{ + const char* p[5] = { 0 }; + size_t length = 0; + char* name = NULL; + const char* type = NULL; + const char* data = NULL; + RegVal* value = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + WINPR_ASSERT(reg->line); + + p[0] = reg->line + 1; + p[1] = strstr(p[0], "\"="); + if (!p[1]) + return NULL; + + p[2] = p[1] + 2; + type = p[2]; + + if (p[2][0] == '"') + p[3] = p[2]; + else + p[3] = strchr(p[2], ':'); + + if (!p[3]) + return NULL; + + data = p[3] + 1; + length = (size_t)(p[1] - p[0]); + if (length < 1) + goto fail; + + name = (char*)calloc(length + 1, sizeof(char)); + + if (!name) + goto fail; + + memcpy(name, p[0], length); + value = (RegVal*)calloc(1, sizeof(RegVal)); + + if (!value) + goto fail; + + value->name = name; + value->type = REG_NONE; + + for (size_t index = 0; index < ARRAYSIZE(REG_DATA_TYPE_TABLE); index++) + { + const struct reg_data_type* current = ®_DATA_TYPE_TABLE[index]; + WINPR_ASSERT(current->tag); + WINPR_ASSERT(current->length > 0); + WINPR_ASSERT(current->type != REG_NONE); + + if (strncmp(type, current->tag, current->length) == 0) + { + value->type = current->type; + break; + } + } + + switch (value->type) + { + case REG_DWORD: + { + unsigned long val = 0; + errno = 0; + val = strtoul(data, NULL, 0); + + if ((errno != 0) || (val > UINT32_MAX)) + { + WLog_WARN(TAG, "%s::%s value %s invalid", key->name, value->name, data); + goto fail; + } + value->data.dword = (DWORD)val; + } + break; + case REG_QWORD: + { + unsigned long long val = 0; + errno = 0; + val = strtoull(data, NULL, 0); + + if ((errno != 0) || (val > UINT64_MAX)) + { + WLog_WARN(TAG, "%s::%s value %s invalid", key->name, value->name, data); + goto fail; + } + + value->data.qword = (UINT64)val; + } + break; + case REG_SZ: + { + size_t len = 0; + size_t cmp = 0; + char* end = NULL; + char* start = strchr(data, '"'); + if (!start) + goto fail; + + /* Check for terminating quote, check it is the last symbol */ + len = strlen(start); + end = strchr(start + 1, '"'); + if (!end) + goto fail; + cmp = end - start + 1; + if (len != cmp) + goto fail; + if (start[0] == '"') + start++; + if (end[0] == '"') + end[0] = '\0'; + value->data.string = _strdup(start); + + if (!value->data.string) + goto fail; + } + break; + default: + WLog_ERR(TAG, "[%s] %s unimplemented format: %s", key->name, value->name, + reg_data_type_string(value->type)); + break; + } + + if (!key->values) + { + key->values = value; + } + else + { + RegVal* pValue = key->values; + + while (pValue->next != NULL) + { + pValue = pValue->next; + } + + pValue->next = value; + value->prev = pValue; + } + + return value; + +fail: + free(value); + free(name); + return NULL; +} + +static BOOL reg_load_has_next_line(Reg* reg) +{ + if (!reg) + return 0; + + return (reg->next_line != NULL) ? 1 : 0; +} + +static char* reg_load_get_next_line(Reg* reg) +{ + if (!reg) + return NULL; + + reg->line = reg->next_line; + reg->next_line = strtok(NULL, "\n"); + reg->line_length = strlen(reg->line); + return reg->line; +} + +static char* reg_load_peek_next_line(Reg* reg) +{ + WINPR_ASSERT(reg); + return reg->next_line; +} + +static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) +{ + char* name = NULL; + char* path = NULL; + char* save = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + WINPR_ASSERT(subkey); + WINPR_ASSERT(subkey->name); + + path = _strdup(subkey->name); + + if (!path) + return; + + name = strtok_s(path, "\\", &save); + + while (name != NULL) + { + if (strcmp(key->name, name) == 0) + { + if (save) + subkey->subname = _strdup(save); + + /* TODO: free allocated memory in error case */ + if (!subkey->subname) + { + free(path); + return; + } + } + + name = strtok_s(NULL, "\\", &save); + } + + free(path); +} + +static RegKey* reg_load_key(Reg* reg, RegKey* key) +{ + char* p[2]; + size_t length = 0; + RegKey* subkey = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + + WINPR_ASSERT(reg->line); + p[0] = reg->line + 1; + p[1] = strrchr(p[0], ']'); + if (!p[1]) + return NULL; + + subkey = (RegKey*)calloc(1, sizeof(RegKey)); + + if (!subkey) + return NULL; + + length = (size_t)(p[1] - p[0]); + subkey->name = (char*)malloc(length + 1); + + if (!subkey->name) + { + free(subkey); + return NULL; + } + + memcpy(subkey->name, p[0], length); + subkey->name[length] = '\0'; + + while (reg_load_has_next_line(reg)) + { + char* line = reg_load_peek_next_line(reg); + + if (line[0] == '[') + break; + + reg_load_get_next_line(reg); + + if (reg->line[0] == '"') + { + reg_load_value(reg, subkey); + } + } + + reg_insert_key(reg, key, subkey); + + if (!key->subkeys) + { + key->subkeys = subkey; + } + else + { + RegKey* pKey = key->subkeys; + + while (pKey->next != NULL) + { + pKey = pKey->next; + } + + pKey->next = subkey; + subkey->prev = pKey; + } + + return subkey; +} + +static void reg_load(Reg* reg) +{ + reg_load_start(reg); + + while (reg_load_has_next_line(reg)) + { + reg_load_get_next_line(reg); + + if (reg->line[0] == '[') + { + reg_load_key(reg, reg->root_key); + } + } + + reg_load_finish(reg); +} + +static void reg_unload_value(Reg* reg, RegVal* value) +{ + WINPR_ASSERT(reg); + WINPR_ASSERT(value); + + switch (value->type) + { + case REG_SZ: + free(value->data.string); + break; + default: + break; + } + + free(value); +} + +static void reg_unload_key(Reg* reg, RegKey* key) +{ + RegVal* pValue = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + + pValue = key->values; + + while (pValue != NULL) + { + RegVal* pValueNext = pValue->next; + reg_unload_value(reg, pValue); + pValue = pValueNext; + } + + free(key->name); + free(key); +} + +static void reg_unload(Reg* reg) +{ + WINPR_ASSERT(reg); + if (reg->root_key) + { + RegKey* pKey = reg->root_key->subkeys; + + while (pKey != NULL) + { + RegKey* pKeyNext = pKey->next; + reg_unload_key(reg, pKey); + pKey = pKeyNext; + } + + free(reg->root_key); + } +} + +Reg* reg_open(BOOL read_only) +{ + Reg* reg = (Reg*)calloc(1, sizeof(Reg)); + + if (!reg) + return NULL; + + reg->read_only = read_only; + reg->filename = WINPR_HKLM_HIVE; + + if (reg->read_only) + reg->fp = winpr_fopen(reg->filename, "r"); + else + { + reg->fp = winpr_fopen(reg->filename, "r+"); + + if (!reg->fp) + reg->fp = winpr_fopen(reg->filename, "w+"); + } + + if (!reg->fp) + goto fail; + + reg->root_key = (RegKey*)calloc(1, sizeof(RegKey)); + + if (!reg->root_key) + goto fail; + + reg->root_key->values = NULL; + reg->root_key->subkeys = NULL; + reg->root_key->name = "HKEY_LOCAL_MACHINE"; + reg_load(reg); + return reg; + +fail: + reg_close(reg); + return NULL; +} + +void reg_close(Reg* reg) +{ + if (reg) + { + reg_unload(reg); + if (reg->fp) + fclose(reg->fp); + free(reg); + } +} + +const char* reg_type_string(DWORD type) +{ + switch (type) + { + case REG_NONE: + return "REG_NONE"; + case REG_SZ: + return "REG_SZ"; + case REG_EXPAND_SZ: + return "REG_EXPAND_SZ"; + case REG_BINARY: + return "REG_BINARY"; + case REG_DWORD: + return "REG_DWORD"; + case REG_DWORD_BIG_ENDIAN: + return "REG_DWORD_BIG_ENDIAN"; + case REG_LINK: + return "REG_LINK"; + case REG_MULTI_SZ: + return "REG_MULTI_SZ"; + case REG_RESOURCE_LIST: + return "REG_RESOURCE_LIST"; + case REG_FULL_RESOURCE_DESCRIPTOR: + return "REG_FULL_RESOURCE_DESCRIPTOR"; + case REG_RESOURCE_REQUIREMENTS_LIST: + return "REG_RESOURCE_REQUIREMENTS_LIST"; + case REG_QWORD: + return "REG_QWORD"; + default: + return "REG_UNKNOWN"; + } +} diff --git a/winpr/libwinpr/registry/registry_reg.h b/winpr/libwinpr/registry/registry_reg.h new file mode 100644 index 0000000..4ef283e --- /dev/null +++ b/winpr/libwinpr/registry/registry_reg.h @@ -0,0 +1,72 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Registry (.reg file format) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef REGISTRY_REG_H_ +#define REGISTRY_REG_H_ + +#include + +typedef struct s_reg Reg; +typedef struct s_reg_key RegKey; +typedef struct s_reg_val RegVal; + +struct s_reg +{ + FILE* fp; + char* line; + char* next_line; + size_t line_length; + char* buffer; + char* filename; + BOOL read_only; + RegKey* root_key; +}; + +struct s_reg_val +{ + char* name; + DWORD type; + RegVal* prev; + RegVal* next; + + union reg_data + { + DWORD dword; + UINT64 qword; + char* string; + } data; +}; + +struct s_reg_key +{ + char* name; + DWORD type; + RegKey* prev; + RegKey* next; + + char* subname; + RegVal* values; + RegKey* subkeys; +}; + +Reg* reg_open(BOOL read_only); +void reg_close(Reg* reg); + +const char* reg_type_string(DWORD type); + +#endif diff --git a/winpr/libwinpr/rpc/CMakeLists.txt b/winpr/libwinpr/rpc/CMakeLists.txt new file mode 100644 index 0000000..e3e851a --- /dev/null +++ b/winpr/libwinpr/rpc/CMakeLists.txt @@ -0,0 +1,28 @@ +# WinPR: Windows Portable Runtime +# libwinpr-rpc cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + rpc.c +) + +winpr_include_directory_add(${OPENSSL_INCLUDE_DIR}) + +winpr_library_add_private(${OPENSSL_LIBRARIES}) + +if(WIN32) + winpr_library_add_public(ws2_32 rpcrt4) +endif() diff --git a/winpr/libwinpr/rpc/ModuleOptions.cmake b/winpr/libwinpr/rpc/ModuleOptions.cmake new file mode 100644 index 0000000..55987fe --- /dev/null +++ b/winpr/libwinpr/rpc/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "rpcrt4") +set(MINWIN_LONG_NAME "RPC NDR Engine") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/rpc/rpc.c b/winpr/libwinpr/rpc/rpc.c new file mode 100644 index 0000000..99c2c7e --- /dev/null +++ b/winpr/libwinpr/rpc/rpc.c @@ -0,0 +1,928 @@ +/** + * WinPR: Windows Portable Runtime + * Microsoft Remote Procedure Call (MSRPC) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#if !defined(_WIN32) || defined(_UWP) + +#include "../log.h" +#define TAG WINPR_TAG("rpc") + +RPC_STATUS RpcBindingCopy(RPC_BINDING_HANDLE SourceBinding, RPC_BINDING_HANDLE* DestinationBinding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingFree(RPC_BINDING_HANDLE* Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingSetOption(RPC_BINDING_HANDLE hBinding, unsigned long option, + ULONG_PTR optionValue) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqOption(RPC_BINDING_HANDLE hBinding, unsigned long option, + ULONG_PTR* pOptionValue) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingFromStringBindingA(RPC_CSTR StringBinding, RPC_BINDING_HANDLE* Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingFromStringBindingW(RPC_WSTR StringBinding, RPC_BINDING_HANDLE* Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcSsGetContextBinding(void* ContextHandle, RPC_BINDING_HANDLE* Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqObject(RPC_BINDING_HANDLE Binding, UUID* ObjectUuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingReset(RPC_BINDING_HANDLE Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingSetObject(RPC_BINDING_HANDLE Binding, UUID* ObjectUuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtInqDefaultProtectLevel(unsigned long AuthnSvc, unsigned long* AuthnLevel) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingToStringBindingA(RPC_BINDING_HANDLE Binding, RPC_CSTR* StringBinding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingToStringBindingW(RPC_BINDING_HANDLE Binding, RPC_WSTR* StringBinding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingVectorFree(RPC_BINDING_VECTOR** BindingVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcStringBindingComposeA(RPC_CSTR ObjUuid, RPC_CSTR Protseq, RPC_CSTR NetworkAddr, + RPC_CSTR Endpoint, RPC_CSTR Options, RPC_CSTR* StringBinding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcStringBindingComposeW(RPC_WSTR ObjUuid, RPC_WSTR Protseq, RPC_WSTR NetworkAddr, + RPC_WSTR Endpoint, RPC_WSTR Options, RPC_WSTR* StringBinding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcStringBindingParseA(RPC_CSTR StringBinding, RPC_CSTR* ObjUuid, RPC_CSTR* Protseq, + RPC_CSTR* NetworkAddr, RPC_CSTR* Endpoint, + RPC_CSTR* NetworkOptions) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcStringBindingParseW(RPC_WSTR StringBinding, RPC_WSTR* ObjUuid, RPC_WSTR* Protseq, + RPC_WSTR* NetworkAddr, RPC_WSTR* Endpoint, + RPC_WSTR* NetworkOptions) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcStringFreeA(RPC_CSTR* String) +{ + if (String) + free(*String); + + return RPC_S_OK; +} + +RPC_STATUS RpcStringFreeW(RPC_WSTR* String) +{ + if (String) + free(*String); + + return RPC_S_OK; +} + +RPC_STATUS RpcIfInqId(RPC_IF_HANDLE RpcIfHandle, RPC_IF_ID* RpcIfId) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcNetworkIsProtseqValidA(RPC_CSTR Protseq) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcNetworkIsProtseqValidW(RPC_WSTR Protseq) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtInqComTimeout(RPC_BINDING_HANDLE Binding, unsigned int* Timeout) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtSetComTimeout(RPC_BINDING_HANDLE Binding, unsigned int Timeout) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtSetCancelTimeout(long Timeout) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcNetworkInqProtseqsA(RPC_PROTSEQ_VECTORA** ProtseqVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcNetworkInqProtseqsW(RPC_PROTSEQ_VECTORW** ProtseqVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcObjectInqType(UUID* ObjUuid, UUID* TypeUuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcObjectSetInqFn(RPC_OBJECT_INQ_FN* InquiryFn) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcObjectSetType(UUID* ObjUuid, UUID* TypeUuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcProtseqVectorFreeA(RPC_PROTSEQ_VECTORA** ProtseqVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcProtseqVectorFreeW(RPC_PROTSEQ_VECTORW** ProtseqVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerInqBindings(RPC_BINDING_VECTOR** BindingVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerInqIf(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, RPC_MGR_EPV** MgrEpv) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerListen(unsigned int MinimumCallThreads, unsigned int MaxCalls, + unsigned int DontWait) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerRegisterIf(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, RPC_MGR_EPV* MgrEpv) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerRegisterIfEx(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, RPC_MGR_EPV* MgrEpv, + unsigned int Flags, unsigned int MaxCalls, + RPC_IF_CALLBACK_FN* IfCallback) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerRegisterIf2(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, RPC_MGR_EPV* MgrEpv, + unsigned int Flags, unsigned int MaxCalls, unsigned int MaxRpcSize, + RPC_IF_CALLBACK_FN* IfCallbackFn) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUnregisterIf(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + unsigned int WaitForCallsToComplete) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUnregisterIfEx(RPC_IF_HANDLE IfSpec, UUID* MgrTypeUuid, + int RundownContextHandles) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseAllProtseqs(unsigned int MaxCalls, void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseAllProtseqsEx(unsigned int MaxCalls, void* SecurityDescriptor, + PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseAllProtseqsIf(unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseAllProtseqsIfEx(unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor, PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqA(RPC_CSTR Protseq, unsigned int MaxCalls, void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqExA(RPC_CSTR Protseq, unsigned int MaxCalls, void* SecurityDescriptor, + PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqW(RPC_WSTR Protseq, unsigned int MaxCalls, void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqExW(RPC_WSTR Protseq, unsigned int MaxCalls, void* SecurityDescriptor, + PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqEpA(RPC_CSTR Protseq, unsigned int MaxCalls, RPC_CSTR Endpoint, + void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqEpExA(RPC_CSTR Protseq, unsigned int MaxCalls, RPC_CSTR Endpoint, + void* SecurityDescriptor, PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqEpW(RPC_WSTR Protseq, unsigned int MaxCalls, RPC_WSTR Endpoint, + void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqEpExW(RPC_WSTR Protseq, unsigned int MaxCalls, RPC_WSTR Endpoint, + void* SecurityDescriptor, PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqIfA(RPC_CSTR Protseq, unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqIfExA(RPC_CSTR Protseq, unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor, PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqIfW(RPC_WSTR Protseq, unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerUseProtseqIfExW(RPC_WSTR Protseq, unsigned int MaxCalls, RPC_IF_HANDLE IfSpec, + void* SecurityDescriptor, PRPC_POLICY Policy) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +void RpcServerYield(void) +{ + WLog_ERR(TAG, "Not implemented"); +} + +RPC_STATUS RpcMgmtStatsVectorFree(RPC_STATS_VECTOR** StatsVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtInqStats(RPC_BINDING_HANDLE Binding, RPC_STATS_VECTOR** Statistics) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtIsServerListening(RPC_BINDING_HANDLE Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtStopServerListening(RPC_BINDING_HANDLE Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtWaitServerListen(void) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtSetServerStackSize(unsigned long ThreadStackSize) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +void RpcSsDontSerializeContext(void) +{ + WLog_ERR(TAG, "Not implemented"); +} + +RPC_STATUS RpcMgmtEnableIdleCleanup(void) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtInqIfIds(RPC_BINDING_HANDLE Binding, RPC_IF_ID_VECTOR** IfIdVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcIfIdVectorFree(RPC_IF_ID_VECTOR** IfIdVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtInqServerPrincNameA(RPC_BINDING_HANDLE Binding, unsigned long AuthnSvc, + RPC_CSTR* ServerPrincName) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtInqServerPrincNameW(RPC_BINDING_HANDLE Binding, unsigned long AuthnSvc, + RPC_WSTR* ServerPrincName) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerInqDefaultPrincNameA(unsigned long AuthnSvc, RPC_CSTR* PrincName) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerInqDefaultPrincNameW(unsigned long AuthnSvc, RPC_WSTR* PrincName) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcEpResolveBinding(RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcNsBindingInqEntryNameA(RPC_BINDING_HANDLE Binding, unsigned long EntryNameSyntax, + RPC_CSTR* EntryName) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcNsBindingInqEntryNameW(RPC_BINDING_HANDLE Binding, unsigned long EntryNameSyntax, + RPC_WSTR* EntryName) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcImpersonateClient(RPC_BINDING_HANDLE BindingHandle) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcRevertToSelfEx(RPC_BINDING_HANDLE BindingHandle) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcRevertToSelf(void) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthClientA(RPC_BINDING_HANDLE ClientBinding, RPC_AUTHZ_HANDLE* Privs, + RPC_CSTR* ServerPrincName, unsigned long* AuthnLevel, + unsigned long* AuthnSvc, unsigned long* AuthzSvc) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthClientW(RPC_BINDING_HANDLE ClientBinding, RPC_AUTHZ_HANDLE* Privs, + RPC_WSTR* ServerPrincName, unsigned long* AuthnLevel, + unsigned long* AuthnSvc, unsigned long* AuthzSvc) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthClientExA(RPC_BINDING_HANDLE ClientBinding, RPC_AUTHZ_HANDLE* Privs, + RPC_CSTR* ServerPrincName, unsigned long* AuthnLevel, + unsigned long* AuthnSvc, unsigned long* AuthzSvc, + unsigned long Flags) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthClientExW(RPC_BINDING_HANDLE ClientBinding, RPC_AUTHZ_HANDLE* Privs, + RPC_WSTR* ServerPrincName, unsigned long* AuthnLevel, + unsigned long* AuthnSvc, unsigned long* AuthzSvc, + unsigned long Flags) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthInfoA(RPC_BINDING_HANDLE Binding, RPC_CSTR* ServerPrincName, + unsigned long* AuthnLevel, unsigned long* AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, unsigned long* AuthzSvc) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthInfoW(RPC_BINDING_HANDLE Binding, RPC_WSTR* ServerPrincName, + unsigned long* AuthnLevel, unsigned long* AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, unsigned long* AuthzSvc) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingSetAuthInfoA(RPC_BINDING_HANDLE Binding, RPC_CSTR ServerPrincName, + unsigned long AuthnLevel, unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, unsigned long AuthzSvc) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingSetAuthInfoExA(RPC_BINDING_HANDLE Binding, RPC_CSTR ServerPrincName, + unsigned long AuthnLevel, unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, unsigned long AuthzSvc, + RPC_SECURITY_QOS* SecurityQos) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingSetAuthInfoW(RPC_BINDING_HANDLE Binding, RPC_WSTR ServerPrincName, + unsigned long AuthnLevel, unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, unsigned long AuthzSvc) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingSetAuthInfoExW(RPC_BINDING_HANDLE Binding, RPC_WSTR ServerPrincName, + unsigned long AuthnLevel, unsigned long AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE AuthIdentity, unsigned long AuthzSvc, + RPC_SECURITY_QOS* SecurityQOS) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthInfoExA(RPC_BINDING_HANDLE Binding, RPC_CSTR* ServerPrincName, + unsigned long* AuthnLevel, unsigned long* AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, unsigned long* AuthzSvc, + unsigned long RpcQosVersion, RPC_SECURITY_QOS* SecurityQOS) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingInqAuthInfoExW(RPC_BINDING_HANDLE Binding, RPC_WSTR* ServerPrincName, + unsigned long* AuthnLevel, unsigned long* AuthnSvc, + RPC_AUTH_IDENTITY_HANDLE* AuthIdentity, unsigned long* AuthzSvc, + unsigned long RpcQosVersion, RPC_SECURITY_QOS* SecurityQOS) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerRegisterAuthInfoA(RPC_CSTR ServerPrincName, unsigned long AuthnSvc, + RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFn, void* Arg) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerRegisterAuthInfoW(RPC_WSTR ServerPrincName, unsigned long AuthnSvc, + RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFn, void* Arg) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcBindingServerFromClient(RPC_BINDING_HANDLE ClientBinding, + RPC_BINDING_HANDLE* ServerBinding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +void RpcRaiseException(RPC_STATUS exception) +{ + WLog_ERR(TAG, "RpcRaiseException: 0x%08luX", exception); + exit((int)exception); +} + +RPC_STATUS RpcTestCancel(void) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerTestCancel(RPC_BINDING_HANDLE BindingHandle) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcCancelThread(void* Thread) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcCancelThreadEx(void* Thread, long Timeout) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +/** + * UUID Functions + */ + +static UUID UUID_NIL = { + 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +RPC_STATUS UuidCreate(UUID* Uuid) +{ + winpr_RAND_pseudo(Uuid, 16); + return RPC_S_OK; +} + +RPC_STATUS UuidCreateSequential(UUID* Uuid) +{ + winpr_RAND_pseudo(Uuid, 16); + return RPC_S_OK; +} + +RPC_STATUS UuidToStringA(const UUID* Uuid, RPC_CSTR* StringUuid) +{ + *StringUuid = (RPC_CSTR)malloc(36 + 1); + + if (!(*StringUuid)) + return RPC_S_OUT_OF_MEMORY; + + if (!Uuid) + Uuid = &UUID_NIL; + + /** + * Format is 32 hex digits partitioned in 5 groups: + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + */ + sprintf_s((char*)*StringUuid, 36 + 1, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + Uuid->Data1, Uuid->Data2, Uuid->Data3, Uuid->Data4[0], Uuid->Data4[1], Uuid->Data4[2], + Uuid->Data4[3], Uuid->Data4[4], Uuid->Data4[5], Uuid->Data4[6], Uuid->Data4[7]); + return RPC_S_OK; +} + +RPC_STATUS UuidToStringW(const UUID* Uuid, RPC_WSTR* StringUuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS UuidFromStringA(RPC_CSTR StringUuid, UUID* Uuid) +{ + BYTE bin[36]; + + if (!StringUuid) + return UuidCreateNil(Uuid); + + if (strlen((char*)StringUuid) != 36) + return RPC_S_INVALID_STRING_UUID; + + if ((StringUuid[8] != '-') || (StringUuid[13] != '-') || (StringUuid[18] != '-') || + (StringUuid[23] != '-')) + { + return RPC_S_INVALID_STRING_UUID; + } + + for (int index = 0; index < 36; index++) + { + if ((index == 8) || (index == 13) || (index == 18) || (index == 23)) + continue; + + if ((StringUuid[index] >= '0') && (StringUuid[index] <= '9')) + bin[index] = StringUuid[index] - '0'; + else if ((StringUuid[index] >= 'a') && (StringUuid[index] <= 'f')) + bin[index] = StringUuid[index] - 'a' + 10; + else if ((StringUuid[index] >= 'A') && (StringUuid[index] <= 'F')) + bin[index] = StringUuid[index] - 'A' + 10; + else + return RPC_S_INVALID_STRING_UUID; + } + + Uuid->Data1 = ((bin[0] << 28) | (bin[1] << 24) | (bin[2] << 20) | (bin[3] << 16) | + (bin[4] << 12) | (bin[5] << 8) | (bin[6] << 4) | bin[7]); + Uuid->Data2 = ((bin[9] << 12) | (bin[10] << 8) | (bin[11] << 4) | bin[12]); + Uuid->Data3 = ((bin[14] << 12) | (bin[15] << 8) | (bin[16] << 4) | bin[17]); + Uuid->Data4[0] = ((bin[19] << 4) | bin[20]); + Uuid->Data4[1] = ((bin[21] << 4) | bin[22]); + Uuid->Data4[2] = ((bin[24] << 4) | bin[25]); + Uuid->Data4[3] = ((bin[26] << 4) | bin[27]); + Uuid->Data4[4] = ((bin[28] << 4) | bin[29]); + Uuid->Data4[5] = ((bin[30] << 4) | bin[31]); + Uuid->Data4[6] = ((bin[32] << 4) | bin[33]); + Uuid->Data4[7] = ((bin[34] << 4) | bin[35]); + return RPC_S_OK; +} + +RPC_STATUS UuidFromStringW(RPC_WSTR StringUuid, UUID* Uuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +signed int UuidCompare(const UUID* Uuid1, const UUID* Uuid2, RPC_STATUS* Status) +{ + *Status = RPC_S_OK; + + if (!Uuid1) + Uuid1 = &UUID_NIL; + + if (!Uuid2) + Uuid2 = &UUID_NIL; + + if (Uuid1->Data1 != Uuid2->Data1) + return (Uuid1->Data1 < Uuid2->Data1) ? -1 : 1; + + if (Uuid1->Data2 != Uuid2->Data2) + return (Uuid1->Data2 < Uuid2->Data2) ? -1 : 1; + + if (Uuid1->Data3 != Uuid2->Data3) + return (Uuid1->Data3 < Uuid2->Data3) ? -1 : 1; + + for (int index = 0; index < 8; index++) + { + if (Uuid1->Data4[index] != Uuid2->Data4[index]) + return (Uuid1->Data4[index] < Uuid2->Data4[index]) ? -1 : 1; + } + + return 0; +} + +RPC_STATUS UuidCreateNil(UUID* NilUuid) +{ + CopyMemory((void*)NilUuid, (void*)&UUID_NIL, 16); + return RPC_S_OK; +} + +int UuidEqual(const UUID* Uuid1, const UUID* Uuid2, RPC_STATUS* Status) +{ + return ((UuidCompare(Uuid1, Uuid2, Status) == 0) ? TRUE : FALSE); +} + +unsigned short UuidHash(const UUID* Uuid, RPC_STATUS* Status) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +int UuidIsNil(const UUID* Uuid, RPC_STATUS* Status) +{ + return UuidEqual(Uuid, &UUID_NIL, Status); +} + +RPC_STATUS RpcEpRegisterNoReplaceA(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_CSTR Annotation) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcEpRegisterNoReplaceW(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_WSTR Annotation) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcEpRegisterA(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_CSTR Annotation) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcEpRegisterW(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, RPC_WSTR Annotation) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcEpUnregister(RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS DceErrorInqTextA(RPC_STATUS RpcStatus, RPC_CSTR ErrorText) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS DceErrorInqTextW(RPC_STATUS RpcStatus, RPC_WSTR ErrorText) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtEpEltInqBegin(RPC_BINDING_HANDLE EpBinding, unsigned long InquiryType, + RPC_IF_ID* IfId, unsigned long VersOption, UUID* ObjectUuid, + RPC_EP_INQ_HANDLE* InquiryContext) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtEpEltInqDone(RPC_EP_INQ_HANDLE* InquiryContext) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtEpEltInqNextA(RPC_EP_INQ_HANDLE InquiryContext, RPC_IF_ID* IfId, + RPC_BINDING_HANDLE* Binding, UUID* ObjectUuid, RPC_CSTR* Annotation) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtEpEltInqNextW(RPC_EP_INQ_HANDLE InquiryContext, RPC_IF_ID* IfId, + RPC_BINDING_HANDLE* Binding, UUID* ObjectUuid, RPC_WSTR* Annotation) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtEpUnregister(RPC_BINDING_HANDLE EpBinding, RPC_IF_ID* IfId, + RPC_BINDING_HANDLE Binding, UUID* ObjectUuid) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcMgmtSetAuthorizationFn(RPC_MGMT_AUTHORIZATION_FN AuthorizationFn) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +RPC_STATUS RpcServerInqBindingHandle(RPC_BINDING_HANDLE* Binding) +{ + WLog_ERR(TAG, "Not implemented"); + return 0; +} + +#endif diff --git a/winpr/libwinpr/security/CMakeLists.txt b/winpr/libwinpr/security/CMakeLists.txt new file mode 100644 index 0000000..98141e1 --- /dev/null +++ b/winpr/libwinpr/security/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-security cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(security.c) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/security/ModuleOptions.cmake b/winpr/libwinpr/security/ModuleOptions.cmake new file mode 100644 index 0000000..66fa71a --- /dev/null +++ b/winpr/libwinpr/security/ModuleOptions.cmake @@ -0,0 +1,8 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "security") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "base") +set(MINWIN_LONG_NAME "Base Security Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") diff --git a/winpr/libwinpr/security/security.c b/winpr/libwinpr/security/security.c new file mode 100644 index 0000000..3806233 --- /dev/null +++ b/winpr/libwinpr/security/security.c @@ -0,0 +1,226 @@ +/** + * WinPR: Windows Portable Runtime + * Base Security Functions + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include + +#include "../handle/handle.h" + +/** + * api-ms-win-security-base-l1-2-0.dll: + * + * AccessCheck + * AccessCheckAndAuditAlarmW + * AccessCheckByType + * AccessCheckByTypeAndAuditAlarmW + * AccessCheckByTypeResultList + * AccessCheckByTypeResultListAndAuditAlarmByHandleW + * AccessCheckByTypeResultListAndAuditAlarmW + * AddAccessAllowedAce + * AddAccessAllowedAceEx + * AddAccessAllowedObjectAce + * AddAccessDeniedAce + * AddAccessDeniedAceEx + * AddAccessDeniedObjectAce + * AddAce + * AddAuditAccessAce + * AddAuditAccessAceEx + * AddAuditAccessObjectAce + * AddMandatoryAce + * AddResourceAttributeAce + * AddScopedPolicyIDAce + * AdjustTokenGroups + * AdjustTokenPrivileges + * AllocateAndInitializeSid + * AllocateLocallyUniqueId + * AreAllAccessesGranted + * AreAnyAccessesGranted + * CheckTokenCapability + * CheckTokenMembership + * CheckTokenMembershipEx + * ConvertToAutoInheritPrivateObjectSecurity + * CopySid + * CreatePrivateObjectSecurity + * CreatePrivateObjectSecurityEx + * CreatePrivateObjectSecurityWithMultipleInheritance + * CreateRestrictedToken + * CreateWellKnownSid + * DeleteAce + * DestroyPrivateObjectSecurity + * DuplicateToken + * DuplicateTokenEx + * EqualDomainSid + * EqualPrefixSid + * EqualSid + * FindFirstFreeAce + * FreeSid + * GetAce + * GetAclInformation + * GetAppContainerAce + * GetCachedSigningLevel + * GetFileSecurityW + * GetKernelObjectSecurity + * GetLengthSid + * GetPrivateObjectSecurity + * GetSidIdentifierAuthority + * GetSidLengthRequired + * GetSidSubAuthority + * GetSidSubAuthorityCount + * GetTokenInformation + * GetWindowsAccountDomainSid + * ImpersonateAnonymousToken + * ImpersonateLoggedOnUser + * ImpersonateSelf + * InitializeAcl + * InitializeSid + * IsTokenRestricted + * IsValidAcl + * IsValidSid + * IsWellKnownSid + * MakeAbsoluteSD + * MakeSelfRelativeSD + * MapGenericMask + * ObjectCloseAuditAlarmW + * ObjectDeleteAuditAlarmW + * ObjectOpenAuditAlarmW + * ObjectPrivilegeAuditAlarmW + * PrivilegeCheck + * PrivilegedServiceAuditAlarmW + * QuerySecurityAccessMask + * RevertToSelf + * SetAclInformation + * SetCachedSigningLevel + * SetFileSecurityW + * SetKernelObjectSecurity + * SetPrivateObjectSecurity + * SetPrivateObjectSecurityEx + * SetSecurityAccessMask + * SetTokenInformation + */ + +#ifndef _WIN32 + +#include "security.h" + +BOOL InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD dwRevision) +{ + return TRUE; +} + +DWORD GetSecurityDescriptorLength(PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + return 0; +} + +BOOL IsValidSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSECURITY_DESCRIPTOR_CONTROL pControl, LPDWORD lpdwRevision) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, + SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbDaclPresent, + PACL* pDacl, LPBOOL lpbDaclDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bDaclPresent, + PACL pDacl, BOOL bDaclDefaulted) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID* pGroup, + LPBOOL lpbGroupDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pGroup, + BOOL bGroupDefaulted) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID* pOwner, + LPBOOL lpbOwnerDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pOwner, + BOOL bOwnerDefaulted) +{ + return TRUE; +} + +DWORD GetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, PUCHAR RMControl) +{ + return 0; +} + +DWORD SetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, PUCHAR RMControl) +{ + return 0; +} + +BOOL GetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbSaclPresent, + PACL* pSacl, LPBOOL lpbSaclDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bSaclPresent, + PACL pSacl, BOOL bSaclDefaulted) +{ + return TRUE; +} + +#endif + +BOOL AccessTokenIsValid(HANDLE handle) +{ + WINPR_HANDLE* h = (WINPR_HANDLE*)handle; + + if (!h || (h->Type != HANDLE_TYPE_ACCESS_TOKEN)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + return TRUE; +} diff --git a/winpr/libwinpr/security/security.h b/winpr/libwinpr/security/security.h new file mode 100644 index 0000000..a80dfe1 --- /dev/null +++ b/winpr/libwinpr/security/security.h @@ -0,0 +1,45 @@ +/** + * WinPR: Windows Portable Runtime + * Base Security Functions + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SECURITY_PRIVATE_H +#define WINPR_SECURITY_PRIVATE_H + +#ifndef _WIN32 + +#include + +#include "../handle/handle.h" + +struct winpr_access_token +{ + WINPR_HANDLE common; + + LPSTR Username; + LPSTR Domain; + + DWORD UserId; + DWORD GroupId; +}; +typedef struct winpr_access_token WINPR_ACCESS_TOKEN; + +BOOL AccessTokenIsValid(HANDLE handle); + +#endif + +#endif /* WINPR_SECURITY_PRIVATE_H */ diff --git a/winpr/libwinpr/security/test/CMakeLists.txt b/winpr/libwinpr/security/test/CMakeLists.txt new file mode 100644 index 0000000..80be394 --- /dev/null +++ b/winpr/libwinpr/security/test/CMakeLists.txt @@ -0,0 +1,23 @@ + +set(MODULE_NAME "TestSecurity") +set(MODULE_PREFIX "TEST_SECURITY") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestSecurityToken.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/security/test/TestSecurityToken.c b/winpr/libwinpr/security/test/TestSecurityToken.c new file mode 100644 index 0000000..0d877b6 --- /dev/null +++ b/winpr/libwinpr/security/test/TestSecurityToken.c @@ -0,0 +1,9 @@ + +#include +#include +#include + +int TestSecurityToken(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/shell/CMakeLists.txt b/winpr/libwinpr/shell/CMakeLists.txt new file mode 100644 index 0000000..24b47e3 --- /dev/null +++ b/winpr/libwinpr/shell/CMakeLists.txt @@ -0,0 +1,20 @@ +# WinPR: Windows Portable Runtime +# libwinpr-shell cmake build script +# +# Copyright 2015 Dell Software +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if (NOT ANDROID) + winpr_module_add(shell.c) +endif() diff --git a/winpr/libwinpr/shell/ModuleOptions.cmake b/winpr/libwinpr/shell/ModuleOptions.cmake new file mode 100644 index 0000000..116680d --- /dev/null +++ b/winpr/libwinpr/shell/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "shell") +set(MINWIN_LONG_NAME "Shell Functions") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/shell/shell.c b/winpr/libwinpr/shell/shell.c new file mode 100644 index 0000000..244ab7a --- /dev/null +++ b/winpr/libwinpr/shell/shell.c @@ -0,0 +1,145 @@ +/** + * WinPR: Windows Portable Runtime + * Shell Functions + * + * Copyright 2015 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/** + * shell32.dll: + * + * GetUserProfileDirectoryA + * GetUserProfileDirectoryW + */ + +#ifndef _WIN32 + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "../handle/handle.h" + +#include "../security/security.h" + +BOOL GetUserProfileDirectoryA(HANDLE hToken, LPSTR lpProfileDir, LPDWORD lpcchSize) +{ + char* buf = NULL; + int buflen = 0; + int status = 0; + DWORD cchDirSize = 0; + struct passwd pwd; + struct passwd* pw = NULL; + WINPR_ACCESS_TOKEN* token = NULL; + token = (WINPR_ACCESS_TOKEN*)hToken; + + if (!AccessTokenIsValid(hToken)) + return FALSE; + + if (!lpcchSize) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (buflen == -1) + buflen = 8196; + + buf = (char*)malloc(buflen); + + if (!buf) + return FALSE; + + status = getpwnam_r(token->Username, &pwd, buf, buflen, &pw); + + if ((status != 0) || !pw) + { + SetLastError(ERROR_INVALID_PARAMETER); + free(buf); + return FALSE; + } + + cchDirSize = strlen(pw->pw_dir) + 1; + + if (!lpProfileDir || (*lpcchSize < cchDirSize)) + { + *lpcchSize = cchDirSize; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + free(buf); + return FALSE; + } + + ZeroMemory(lpProfileDir, *lpcchSize); + sprintf_s(lpProfileDir, *lpcchSize, "%s", pw->pw_dir); + *lpcchSize = cchDirSize; + free(buf); + return TRUE; +} + +BOOL GetUserProfileDirectoryW(HANDLE hToken, LPWSTR lpProfileDir, LPDWORD lpcchSize) +{ + BOOL bStatus = 0; + DWORD cchSizeA = 0; + LPSTR lpProfileDirA = NULL; + + if (!lpcchSize) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + cchSizeA = *lpcchSize; + lpProfileDirA = NULL; + + if (lpProfileDir) + { + lpProfileDirA = (LPSTR)malloc(cchSizeA); + + if (lpProfileDirA == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + } + + bStatus = GetUserProfileDirectoryA(hToken, lpProfileDirA, &cchSizeA); + + if (bStatus) + { + SSIZE_T size = ConvertUtf8NToWChar(lpProfileDirA, cchSizeA, lpProfileDir, *lpcchSize); + bStatus = size >= 0; + } + + if (lpProfileDirA) + { + free(lpProfileDirA); + } + + *lpcchSize = cchSizeA; + return bStatus; +} + +#endif diff --git a/winpr/libwinpr/smartcard/CMakeLists.txt b/winpr/libwinpr/smartcard/CMakeLists.txt new file mode 100644 index 0000000..68e5277 --- /dev/null +++ b/winpr/libwinpr/smartcard/CMakeLists.txt @@ -0,0 +1,60 @@ +# WinPR: Windows Portable Runtime +# libwinpr-smartcard cmake build script +# +# Copyright 2014 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_PREFIX "WINPR_SMARTCARD") + +if(PCSC_WINPR_FOUND) + winpr_definition_add(-DWITH_WINPR_PCSC) +endif() + +option(WITH_SMARTCARD_PCSC "Enable smartcard PCSC backend" ON) + + +set(${MODULE_PREFIX}_SRCS + smartcard.c + smartcard.h) + +if(WITH_SMARTCARD_PCSC) + winpr_definition_add(-DWITH_SMARTCARD_PCSC) + list(APPEND ${MODULE_PREFIX}_SRCS + smartcard_pcsc.c + smartcard_pcsc.h) +endif() + +if(WITH_SMARTCARD_INSPECT) + winpr_definition_add(-DWITH_SMARTCARD_INSPECT) + list(APPEND ${MODULE_PREFIX}_SRCS + smartcard_inspect.c + smartcard_inspect.h) +endif() + +if(WIN32) + list(APPEND ${MODULE_PREFIX}_SRCS + smartcard_windows.c + smartcard_windows.h) +endif() + +winpr_module_add(${${MODULE_PREFIX}_SRCS}) + +if(PCSC_WINPR_FOUND) + winpr_library_add_private(${PCSC_WINPR_LIBRARY}) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + diff --git a/winpr/libwinpr/smartcard/ModuleOptions.cmake b/winpr/libwinpr/smartcard/ModuleOptions.cmake new file mode 100644 index 0000000..afb4a1e --- /dev/null +++ b/winpr/libwinpr/smartcard/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "smartcard") +set(MINWIN_LONG_NAME "Smart Card API") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/smartcard/smartcard.c b/winpr/libwinpr/smartcard/smartcard.c new file mode 100644 index 0000000..0a79663 --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard.c @@ -0,0 +1,1283 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "../log.h" + +#include "smartcard.h" + +#if defined(WITH_SMARTCARD_INSPECT) +#include "smartcard_inspect.h" +#endif + +static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT; +static const SCardApiFunctionTable* g_SCardApi = NULL; + +#define TAG WINPR_TAG("smartcard") + +#define xstr(s) str(s) +#define str(s) #s + +#define SCARDAPI_STUB_CALL_LONG(_name, ...) \ + InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + return SCARD_E_NO_SERVICE; \ + } \ + return g_SCardApi->pfn##_name(__VA_ARGS__) + +#define SCARDAPI_STUB_CALL_HANDLE(_name, ...) \ + InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + return NULL; \ + } \ + return g_SCardApi->pfn##_name(__VA_ARGS__) + +#define SCARDAPI_STUB_CALL_VOID(_name, ...) \ + InitOnceExecuteOnce(&g_Initialized, InitializeSCardApiStubs, NULL, NULL); \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + return; \ + } \ + g_SCardApi->pfn##_name(__VA_ARGS__) + +/** + * Standard Windows Smart Card API + */ + +const SCARD_IO_REQUEST g_rgSCardT0Pci = { SCARD_PROTOCOL_T0, 8 }; +const SCARD_IO_REQUEST g_rgSCardT1Pci = { SCARD_PROTOCOL_T1, 8 }; +const SCARD_IO_REQUEST g_rgSCardRawPci = { SCARD_PROTOCOL_RAW, 8 }; + +static BOOL CALLBACK InitializeSCardApiStubs(PINIT_ONCE once, PVOID param, PVOID* context) +{ +#ifdef _WIN32 + if (Windows_InitializeSCardApi() >= 0) + g_SCardApi = Windows_GetSCardApiFunctionTable(); +#else +#if defined(WITH_SMARTCARD_PCSC) + if (PCSC_InitializeSCardApi() >= 0) + g_SCardApi = PCSC_GetSCardApiFunctionTable(); +#endif +#endif + +#if defined(WITH_SMARTCARD_INSPECT) + g_SCardApi = Inspect_RegisterSCardApi(g_SCardApi); +#endif + return TRUE; +} + +WINSCARDAPI LONG WINAPI SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, LPSCARDCONTEXT phContext) +{ + SCARDAPI_STUB_CALL_LONG(SCardEstablishContext, dwScope, pvReserved1, pvReserved2, phContext); +} + +WINSCARDAPI LONG WINAPI SCardReleaseContext(SCARDCONTEXT hContext) +{ + SCARDAPI_STUB_CALL_LONG(SCardReleaseContext, hContext); +} + +WINSCARDAPI LONG WINAPI SCardIsValidContext(SCARDCONTEXT hContext) +{ + SCARDAPI_STUB_CALL_LONG(SCardIsValidContext, hContext); +} + +WINSCARDAPI LONG WINAPI SCardListReaderGroupsA(SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups) +{ + SCARDAPI_STUB_CALL_LONG(SCardListReaderGroupsA, hContext, mszGroups, pcchGroups); +} + +WINSCARDAPI LONG WINAPI SCardListReaderGroupsW(SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups) +{ + SCARDAPI_STUB_CALL_LONG(SCardListReaderGroupsW, hContext, mszGroups, pcchGroups); +} + +WINSCARDAPI LONG WINAPI SCardListReadersA(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, + LPDWORD pcchReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardListReadersA, hContext, mszGroups, mszReaders, pcchReaders); +} + +WINSCARDAPI LONG WINAPI SCardListReadersW(SCARDCONTEXT hContext, LPCWSTR mszGroups, + LPWSTR mszReaders, LPDWORD pcchReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardListReadersW, hContext, mszGroups, mszReaders, pcchReaders); +} + +WINSCARDAPI LONG WINAPI SCardListCardsA(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + CHAR* mszCards, LPDWORD pcchCards) +{ + SCARDAPI_STUB_CALL_LONG(SCardListCardsA, hContext, pbAtr, rgquidInterfaces, cguidInterfaceCount, + mszCards, pcchCards); +} + +WINSCARDAPI LONG WINAPI SCardListCardsW(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + WCHAR* mszCards, LPDWORD pcchCards) +{ + SCARDAPI_STUB_CALL_LONG(SCardListCardsW, hContext, pbAtr, rgquidInterfaces, cguidInterfaceCount, + mszCards, pcchCards); +} + +WINSCARDAPI LONG WINAPI SCardListInterfacesA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + SCARDAPI_STUB_CALL_LONG(SCardListInterfacesA, hContext, szCard, pguidInterfaces, + pcguidInterfaces); +} + +WINSCARDAPI LONG WINAPI SCardListInterfacesW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + SCARDAPI_STUB_CALL_LONG(SCardListInterfacesW, hContext, szCard, pguidInterfaces, + pcguidInterfaces); +} + +WINSCARDAPI LONG WINAPI SCardGetProviderIdA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidProviderId) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetProviderIdA, hContext, szCard, pguidProviderId); +} + +WINSCARDAPI LONG WINAPI SCardGetProviderIdW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidProviderId) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetProviderIdW, hContext, szCard, pguidProviderId); +} + +WINSCARDAPI LONG WINAPI SCardGetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, CHAR* szProvider, + LPDWORD pcchProvider) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetCardTypeProviderNameA, hContext, szCardName, dwProviderId, + szProvider, pcchProvider); +} + +WINSCARDAPI LONG WINAPI SCardGetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetCardTypeProviderNameW, hContext, szCardName, dwProviderId, + szProvider, pcchProvider); +} + +WINSCARDAPI LONG WINAPI SCardIntroduceReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardIntroduceReaderGroupA, hContext, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardIntroduceReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardIntroduceReaderGroupW, hContext, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardForgetReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardForgetReaderGroupA, hContext, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardForgetReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardForgetReaderGroupW, hContext, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardIntroduceReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName) +{ + SCARDAPI_STUB_CALL_LONG(SCardIntroduceReaderA, hContext, szReaderName, szDeviceName); +} + +WINSCARDAPI LONG WINAPI SCardIntroduceReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName) +{ + SCARDAPI_STUB_CALL_LONG(SCardIntroduceReaderW, hContext, szReaderName, szDeviceName); +} + +WINSCARDAPI LONG WINAPI SCardForgetReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName) +{ + SCARDAPI_STUB_CALL_LONG(SCardForgetReaderA, hContext, szReaderName); +} + +WINSCARDAPI LONG WINAPI SCardForgetReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName) +{ + SCARDAPI_STUB_CALL_LONG(SCardForgetReaderW, hContext, szReaderName); +} + +WINSCARDAPI LONG WINAPI SCardAddReaderToGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardAddReaderToGroupA, hContext, szReaderName, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardAddReaderToGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardAddReaderToGroupW, hContext, szReaderName, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardRemoveReaderFromGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardRemoveReaderFromGroupA, hContext, szReaderName, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardRemoveReaderFromGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + SCARDAPI_STUB_CALL_LONG(SCardRemoveReaderFromGroupW, hContext, szReaderName, szGroupName); +} + +WINSCARDAPI LONG WINAPI SCardIntroduceCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardIntroduceCardTypeA, hContext, szCardName, pguidPrimaryProvider, + rgguidInterfaces, dwInterfaceCount, pbAtr, pbAtrMask, cbAtrLen); +} + +WINSCARDAPI LONG WINAPI SCardIntroduceCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardIntroduceCardTypeW, hContext, szCardName, pguidPrimaryProvider, + rgguidInterfaces, dwInterfaceCount, pbAtr, pbAtrMask, cbAtrLen); +} + +WINSCARDAPI LONG WINAPI SCardSetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, LPCSTR szProvider) +{ + SCARDAPI_STUB_CALL_LONG(SCardSetCardTypeProviderNameA, hContext, szCardName, dwProviderId, + szProvider); +} + +WINSCARDAPI LONG WINAPI SCardSetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, LPCWSTR szProvider) +{ + SCARDAPI_STUB_CALL_LONG(SCardSetCardTypeProviderNameW, hContext, szCardName, dwProviderId, + szProvider); +} + +WINSCARDAPI LONG WINAPI SCardForgetCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName) +{ + SCARDAPI_STUB_CALL_LONG(SCardForgetCardTypeA, hContext, szCardName); +} + +WINSCARDAPI LONG WINAPI SCardForgetCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName) +{ + SCARDAPI_STUB_CALL_LONG(SCardForgetCardTypeW, hContext, szCardName); +} + +WINSCARDAPI LONG WINAPI SCardFreeMemory(SCARDCONTEXT hContext, LPVOID pvMem) +{ + SCARDAPI_STUB_CALL_LONG(SCardFreeMemory, hContext, pvMem); +} + +WINSCARDAPI HANDLE WINAPI SCardAccessStartedEvent(void) +{ + SCARDAPI_STUB_CALL_HANDLE(SCardAccessStartedEvent); +} + +WINSCARDAPI void WINAPI SCardReleaseStartedEvent(void) +{ + SCARDAPI_STUB_CALL_VOID(SCardReleaseStartedEvent); +} + +WINSCARDAPI LONG WINAPI SCardLocateCardsA(SCARDCONTEXT hContext, LPCSTR mszCards, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardLocateCardsA, hContext, mszCards, rgReaderStates, cReaders); +} + +WINSCARDAPI LONG WINAPI SCardLocateCardsW(SCARDCONTEXT hContext, LPCWSTR mszCards, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardLocateCardsW, hContext, mszCards, rgReaderStates, cReaders); +} + +WINSCARDAPI LONG WINAPI SCardLocateCardsByATRA(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardLocateCardsByATRA, hContext, rgAtrMasks, cAtrs, rgReaderStates, + cReaders); +} + +WINSCARDAPI LONG WINAPI SCardLocateCardsByATRW(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardLocateCardsByATRW, hContext, rgAtrMasks, cAtrs, rgReaderStates, + cReaders); +} + +WINSCARDAPI LONG WINAPI SCardGetStatusChangeA(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetStatusChangeA, hContext, dwTimeout, rgReaderStates, cReaders); +} + +WINSCARDAPI LONG WINAPI SCardGetStatusChangeW(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetStatusChangeW, hContext, dwTimeout, rgReaderStates, cReaders); +} + +WINSCARDAPI LONG WINAPI SCardCancel(SCARDCONTEXT hContext) +{ + SCARDAPI_STUB_CALL_LONG(SCardCancel, hContext); +} + +WINSCARDAPI LONG WINAPI SCardConnectA(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol) +{ + SCARDAPI_STUB_CALL_LONG(SCardConnectA, hContext, szReader, dwShareMode, dwPreferredProtocols, + phCard, pdwActiveProtocol); +} + +WINSCARDAPI LONG WINAPI SCardConnectW(SCARDCONTEXT hContext, LPCWSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol) +{ + SCARDAPI_STUB_CALL_LONG(SCardConnectW, hContext, szReader, dwShareMode, dwPreferredProtocols, + phCard, pdwActiveProtocol); +} + +WINSCARDAPI LONG WINAPI SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, + DWORD dwPreferredProtocols, DWORD dwInitialization, + LPDWORD pdwActiveProtocol) +{ + SCARDAPI_STUB_CALL_LONG(SCardReconnect, hCard, dwShareMode, dwPreferredProtocols, + dwInitialization, pdwActiveProtocol); +} + +WINSCARDAPI LONG WINAPI SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition) +{ + SCARDAPI_STUB_CALL_LONG(SCardDisconnect, hCard, dwDisposition); +} + +WINSCARDAPI LONG WINAPI SCardBeginTransaction(SCARDHANDLE hCard) +{ + SCARDAPI_STUB_CALL_LONG(SCardBeginTransaction, hCard); +} + +WINSCARDAPI LONG WINAPI SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition) +{ + SCARDAPI_STUB_CALL_LONG(SCardEndTransaction, hCard, dwDisposition); +} + +WINSCARDAPI LONG WINAPI SCardCancelTransaction(SCARDHANDLE hCard) +{ + SCARDAPI_STUB_CALL_LONG(SCardCancelTransaction, hCard); +} + +WINSCARDAPI LONG WINAPI SCardState(SCARDHANDLE hCard, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardState, hCard, pdwState, pdwProtocol, pbAtr, pcbAtrLen); +} + +WINSCARDAPI LONG WINAPI SCardStatusA(SCARDHANDLE hCard, LPSTR mszReaderNames, LPDWORD pcchReaderLen, + LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, + LPDWORD pcbAtrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardStatusA, hCard, mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen); +} + +WINSCARDAPI LONG WINAPI SCardStatusW(SCARDHANDLE hCard, LPWSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardStatusW, hCard, mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen); +} + +WINSCARDAPI LONG WINAPI SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, + LPCBYTE pbSendBuffer, DWORD cbSendLength, + LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength) +{ + SCARDAPI_STUB_CALL_LONG(SCardTransmit, hCard, pioSendPci, pbSendBuffer, cbSendLength, + pioRecvPci, pbRecvBuffer, pcbRecvLength); +} + +WINSCARDAPI LONG WINAPI SCardGetTransmitCount(SCARDHANDLE hCard, LPDWORD pcTransmitCount) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetTransmitCount, hCard, pcTransmitCount); +} + +WINSCARDAPI LONG WINAPI SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, + DWORD cbInBufferSize, LPVOID lpOutBuffer, + DWORD cbOutBufferSize, LPDWORD lpBytesReturned) +{ + SCARDAPI_STUB_CALL_LONG(SCardControl, hCard, dwControlCode, lpInBuffer, cbInBufferSize, + lpOutBuffer, cbOutBufferSize, lpBytesReturned); +} + +WINSCARDAPI LONG WINAPI SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetAttrib, hCard, dwAttrId, pbAttr, pcbAttrLen); +} + +WINSCARDAPI LONG WINAPI SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, + DWORD cbAttrLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardSetAttrib, hCard, dwAttrId, pbAttr, cbAttrLen); +} + +WINSCARDAPI LONG WINAPI SCardUIDlgSelectCardA(LPOPENCARDNAMEA_EX pDlgStruc) +{ + SCARDAPI_STUB_CALL_LONG(SCardUIDlgSelectCardA, pDlgStruc); +} + +WINSCARDAPI LONG WINAPI SCardUIDlgSelectCardW(LPOPENCARDNAMEW_EX pDlgStruc) +{ + SCARDAPI_STUB_CALL_LONG(SCardUIDlgSelectCardW, pDlgStruc); +} + +WINSCARDAPI LONG WINAPI GetOpenCardNameA(LPOPENCARDNAMEA pDlgStruc) +{ + SCARDAPI_STUB_CALL_LONG(GetOpenCardNameA, pDlgStruc); +} + +WINSCARDAPI LONG WINAPI GetOpenCardNameW(LPOPENCARDNAMEW pDlgStruc) +{ + SCARDAPI_STUB_CALL_LONG(GetOpenCardNameW, pDlgStruc); +} + +WINSCARDAPI LONG WINAPI SCardDlgExtendedError(void) +{ + SCARDAPI_STUB_CALL_LONG(SCardDlgExtendedError); +} + +WINSCARDAPI LONG WINAPI SCardReadCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD* DataLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardReadCacheA, hContext, CardIdentifier, FreshnessCounter, LookupName, + Data, DataLen); +} + +WINSCARDAPI LONG WINAPI SCardReadCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD* DataLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardReadCacheW, hContext, CardIdentifier, FreshnessCounter, LookupName, + Data, DataLen); +} + +WINSCARDAPI LONG WINAPI SCardWriteCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD DataLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardWriteCacheA, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); +} + +WINSCARDAPI LONG WINAPI SCardWriteCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD DataLen) +{ + SCARDAPI_STUB_CALL_LONG(SCardWriteCacheW, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); +} + +WINSCARDAPI LONG WINAPI SCardGetReaderIconA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetReaderIconA, hContext, szReaderName, pbIcon, pcbIcon); +} + +WINSCARDAPI LONG WINAPI SCardGetReaderIconW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetReaderIconW, hContext, szReaderName, pbIcon, pcbIcon); +} + +WINSCARDAPI LONG WINAPI SCardGetDeviceTypeIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetDeviceTypeIdA, hContext, szReaderName, pdwDeviceTypeId); +} + +WINSCARDAPI LONG WINAPI SCardGetDeviceTypeIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetDeviceTypeIdW, hContext, szReaderName, pdwDeviceTypeId); +} + +WINSCARDAPI LONG WINAPI SCardGetReaderDeviceInstanceIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetReaderDeviceInstanceIdA, hContext, szReaderName, + szDeviceInstanceId, pcchDeviceInstanceId); +} + +WINSCARDAPI LONG WINAPI SCardGetReaderDeviceInstanceIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + SCARDAPI_STUB_CALL_LONG(SCardGetReaderDeviceInstanceIdW, hContext, szReaderName, + szDeviceInstanceId, pcchDeviceInstanceId); +} + +WINSCARDAPI LONG WINAPI SCardListReadersWithDeviceInstanceIdA(SCARDCONTEXT hContext, + LPCSTR szDeviceInstanceId, + LPSTR mszReaders, LPDWORD pcchReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardListReadersWithDeviceInstanceIdA, hContext, szDeviceInstanceId, + mszReaders, pcchReaders); +} + +WINSCARDAPI LONG WINAPI SCardListReadersWithDeviceInstanceIdW(SCARDCONTEXT hContext, + LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, + LPDWORD pcchReaders) +{ + SCARDAPI_STUB_CALL_LONG(SCardListReadersWithDeviceInstanceIdW, hContext, szDeviceInstanceId, + mszReaders, pcchReaders); +} + +WINSCARDAPI LONG WINAPI SCardAudit(SCARDCONTEXT hContext, DWORD dwEvent) +{ + SCARDAPI_STUB_CALL_LONG(SCardAudit, hContext, dwEvent); +} + +/** + * Extended API + */ + +WINSCARDAPI const char* WINAPI SCardGetErrorString(LONG errorCode) +{ + switch (errorCode) + { + case SCARD_S_SUCCESS: + return "SCARD_S_SUCCESS"; + + case SCARD_F_INTERNAL_ERROR: + return "SCARD_F_INTERNAL_ERROR"; + + case SCARD_E_CANCELLED: + return "SCARD_E_CANCELLED"; + + case SCARD_E_INVALID_HANDLE: + return "SCARD_E_INVALID_HANDLE"; + + case SCARD_E_INVALID_PARAMETER: + return "SCARD_E_INVALID_PARAMETER"; + + case SCARD_E_INVALID_TARGET: + return "SCARD_E_INVALID_TARGET"; + + case SCARD_E_NO_MEMORY: + return "SCARD_E_NO_MEMORY"; + + case SCARD_F_WAITED_TOO_LONG: + return "SCARD_F_WAITED_TOO_LONG"; + + case SCARD_E_INSUFFICIENT_BUFFER: + return "SCARD_E_INSUFFICIENT_BUFFER"; + + case SCARD_E_UNKNOWN_READER: + return "SCARD_E_UNKNOWN_READER"; + + case SCARD_E_TIMEOUT: + return "SCARD_E_TIMEOUT"; + + case SCARD_E_SHARING_VIOLATION: + return "SCARD_E_SHARING_VIOLATION"; + + case SCARD_E_NO_SMARTCARD: + return "SCARD_E_NO_SMARTCARD"; + + case SCARD_E_UNKNOWN_CARD: + return "SCARD_E_UNKNOWN_CARD"; + + case SCARD_E_CANT_DISPOSE: + return "SCARD_E_CANT_DISPOSE"; + + case SCARD_E_PROTO_MISMATCH: + return "SCARD_E_PROTO_MISMATCH"; + + case SCARD_E_NOT_READY: + return "SCARD_E_NOT_READY"; + + case SCARD_E_INVALID_VALUE: + return "SCARD_E_INVALID_VALUE"; + + case SCARD_E_SYSTEM_CANCELLED: + return "SCARD_E_SYSTEM_CANCELLED"; + + case SCARD_F_COMM_ERROR: + return "SCARD_F_COMM_ERROR"; + + case SCARD_F_UNKNOWN_ERROR: + return "SCARD_F_UNKNOWN_ERROR"; + + case SCARD_E_INVALID_ATR: + return "SCARD_E_INVALID_ATR"; + + case SCARD_E_NOT_TRANSACTED: + return "SCARD_E_NOT_TRANSACTED"; + + case SCARD_E_READER_UNAVAILABLE: + return "SCARD_E_READER_UNAVAILABLE"; + + case SCARD_P_SHUTDOWN: + return "SCARD_P_SHUTDOWN"; + + case SCARD_E_PCI_TOO_SMALL: + return "SCARD_E_PCI_TOO_SMALL"; + + case SCARD_E_READER_UNSUPPORTED: + return "SCARD_E_READER_UNSUPPORTED"; + + case SCARD_E_DUPLICATE_READER: + return "SCARD_E_DUPLICATE_READER"; + + case SCARD_E_CARD_UNSUPPORTED: + return "SCARD_E_CARD_UNSUPPORTED"; + + case SCARD_E_NO_SERVICE: + return "SCARD_E_NO_SERVICE"; + + case SCARD_E_SERVICE_STOPPED: + return "SCARD_E_SERVICE_STOPPED"; + + case SCARD_E_UNEXPECTED: + return "SCARD_E_UNEXPECTED"; + + case SCARD_E_ICC_INSTALLATION: + return "SCARD_E_ICC_INSTALLATION"; + + case SCARD_E_ICC_CREATEORDER: + return "SCARD_E_ICC_CREATEORDER"; + + case SCARD_E_UNSUPPORTED_FEATURE: + return "SCARD_E_UNSUPPORTED_FEATURE"; + + case SCARD_E_DIR_NOT_FOUND: + return "SCARD_E_DIR_NOT_FOUND"; + + case SCARD_E_FILE_NOT_FOUND: + return "SCARD_E_FILE_NOT_FOUND"; + + case SCARD_E_NO_DIR: + return "SCARD_E_NO_DIR"; + + case SCARD_E_NO_FILE: + return "SCARD_E_NO_FILE"; + + case SCARD_E_NO_ACCESS: + return "SCARD_E_NO_ACCESS"; + + case SCARD_E_WRITE_TOO_MANY: + return "SCARD_E_WRITE_TOO_MANY"; + + case SCARD_E_BAD_SEEK: + return "SCARD_E_BAD_SEEK"; + + case SCARD_E_INVALID_CHV: + return "SCARD_E_INVALID_CHV"; + + case SCARD_E_UNKNOWN_RES_MNG: + return "SCARD_E_UNKNOWN_RES_MNG"; + + case SCARD_E_NO_SUCH_CERTIFICATE: + return "SCARD_E_NO_SUCH_CERTIFICATE"; + + case SCARD_E_CERTIFICATE_UNAVAILABLE: + return "SCARD_E_CERTIFICATE_UNAVAILABLE"; + + case SCARD_E_NO_READERS_AVAILABLE: + return "SCARD_E_NO_READERS_AVAILABLE"; + + case SCARD_E_COMM_DATA_LOST: + return "SCARD_E_COMM_DATA_LOST"; + + case SCARD_E_NO_KEY_CONTAINER: + return "SCARD_E_NO_KEY_CONTAINER"; + + case SCARD_E_SERVER_TOO_BUSY: + return "SCARD_E_SERVER_TOO_BUSY"; + + case SCARD_E_PIN_CACHE_EXPIRED: + return "SCARD_E_PIN_CACHE_EXPIRED"; + + case SCARD_E_NO_PIN_CACHE: + return "SCARD_E_NO_PIN_CACHE"; + + case SCARD_E_READ_ONLY_CARD: + return "SCARD_E_READ_ONLY_CARD"; + + case SCARD_W_UNSUPPORTED_CARD: + return "SCARD_W_UNSUPPORTED_CARD"; + + case SCARD_W_UNRESPONSIVE_CARD: + return "SCARD_W_UNRESPONSIVE_CARD"; + + case SCARD_W_UNPOWERED_CARD: + return "SCARD_W_UNPOWERED_CARD"; + + case SCARD_W_RESET_CARD: + return "SCARD_W_RESET_CARD"; + + case SCARD_W_REMOVED_CARD: + return "SCARD_W_REMOVED_CARD"; + + case SCARD_W_SECURITY_VIOLATION: + return "SCARD_W_SECURITY_VIOLATION"; + + case SCARD_W_WRONG_CHV: + return "SCARD_W_WRONG_CHV"; + + case SCARD_W_CHV_BLOCKED: + return "SCARD_W_CHV_BLOCKED"; + + case SCARD_W_EOF: + return "SCARD_W_EOF"; + + case SCARD_W_CANCELLED_BY_USER: + return "SCARD_W_CANCELLED_BY_USER"; + + case SCARD_W_CARD_NOT_AUTHENTICATED: + return "SCARD_W_CARD_NOT_AUTHENTICATED"; + + case SCARD_W_CACHE_ITEM_NOT_FOUND: + return "SCARD_W_CACHE_ITEM_NOT_FOUND"; + + case SCARD_W_CACHE_ITEM_STALE: + return "SCARD_W_CACHE_ITEM_STALE"; + + case SCARD_W_CACHE_ITEM_TOO_BIG: + return "SCARD_W_CACHE_ITEM_TOO_BIG"; + + default: + return "SCARD_E_UNKNOWN"; + } +} + +WINSCARDAPI const char* WINAPI SCardGetAttributeString(DWORD dwAttrId) +{ + switch (dwAttrId) + { + case SCARD_ATTR_VENDOR_NAME: + return "SCARD_ATTR_VENDOR_NAME"; + + case SCARD_ATTR_VENDOR_IFD_TYPE: + return "SCARD_ATTR_VENDOR_IFD_TYPE"; + + case SCARD_ATTR_VENDOR_IFD_VERSION: + return "SCARD_ATTR_VENDOR_IFD_VERSION"; + + case SCARD_ATTR_VENDOR_IFD_SERIAL_NO: + return "SCARD_ATTR_VENDOR_IFD_SERIAL_NO"; + + case SCARD_ATTR_CHANNEL_ID: + return "SCARD_ATTR_CHANNEL_ID"; + + case SCARD_ATTR_PROTOCOL_TYPES: + return "SCARD_ATTR_PROTOCOL_TYPES"; + + case SCARD_ATTR_DEFAULT_CLK: + return "SCARD_ATTR_DEFAULT_CLK"; + + case SCARD_ATTR_MAX_CLK: + return "SCARD_ATTR_MAX_CLK"; + + case SCARD_ATTR_DEFAULT_DATA_RATE: + return "SCARD_ATTR_DEFAULT_DATA_RATE"; + + case SCARD_ATTR_MAX_DATA_RATE: + return "SCARD_ATTR_MAX_DATA_RATE"; + + case SCARD_ATTR_MAX_IFSD: + return "SCARD_ATTR_MAX_IFSD"; + + case SCARD_ATTR_POWER_MGMT_SUPPORT: + return "SCARD_ATTR_POWER_MGMT_SUPPORT"; + + case SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE: + return "SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE"; + + case SCARD_ATTR_USER_AUTH_INPUT_DEVICE: + return "SCARD_ATTR_USER_AUTH_INPUT_DEVICE"; + + case SCARD_ATTR_CHARACTERISTICS: + return "SCARD_ATTR_CHARACTERISTICS"; + + case SCARD_ATTR_CURRENT_PROTOCOL_TYPE: + return "SCARD_ATTR_CURRENT_PROTOCOL_TYPE"; + + case SCARD_ATTR_CURRENT_CLK: + return "SCARD_ATTR_CURRENT_CLK"; + + case SCARD_ATTR_CURRENT_F: + return "SCARD_ATTR_CURRENT_F"; + + case SCARD_ATTR_CURRENT_D: + return "SCARD_ATTR_CURRENT_D"; + + case SCARD_ATTR_CURRENT_N: + return "SCARD_ATTR_CURRENT_N"; + + case SCARD_ATTR_CURRENT_W: + return "SCARD_ATTR_CURRENT_W"; + + case SCARD_ATTR_CURRENT_IFSC: + return "SCARD_ATTR_CURRENT_IFSC"; + + case SCARD_ATTR_CURRENT_IFSD: + return "SCARD_ATTR_CURRENT_IFSD"; + + case SCARD_ATTR_CURRENT_BWT: + return "SCARD_ATTR_CURRENT_BWT"; + + case SCARD_ATTR_CURRENT_CWT: + return "SCARD_ATTR_CURRENT_CWT"; + + case SCARD_ATTR_CURRENT_EBC_ENCODING: + return "SCARD_ATTR_CURRENT_EBC_ENCODING"; + + case SCARD_ATTR_EXTENDED_BWT: + return "SCARD_ATTR_EXTENDED_BWT"; + + case SCARD_ATTR_ICC_PRESENCE: + return "SCARD_ATTR_ICC_PRESENCE"; + + case SCARD_ATTR_ICC_INTERFACE_STATUS: + return "SCARD_ATTR_ICC_INTERFACE_STATUS"; + + case SCARD_ATTR_CURRENT_IO_STATE: + return "SCARD_ATTR_CURRENT_IO_STATE"; + + case SCARD_ATTR_ATR_STRING: + return "SCARD_ATTR_ATR_STRING"; + + case SCARD_ATTR_ICC_TYPE_PER_ATR: + return "SCARD_ATTR_ICC_TYPE_PER_ATR"; + + case SCARD_ATTR_ESC_RESET: + return "SCARD_ATTR_ESC_RESET"; + + case SCARD_ATTR_ESC_CANCEL: + return "SCARD_ATTR_ESC_CANCEL"; + + case SCARD_ATTR_ESC_AUTHREQUEST: + return "SCARD_ATTR_ESC_AUTHREQUEST"; + + case SCARD_ATTR_MAXINPUT: + return "SCARD_ATTR_MAXINPUT"; + + case SCARD_ATTR_DEVICE_UNIT: + return "SCARD_ATTR_DEVICE_UNIT"; + + case SCARD_ATTR_DEVICE_IN_USE: + return "SCARD_ATTR_DEVICE_IN_USE"; + + case SCARD_ATTR_DEVICE_FRIENDLY_NAME_A: + return "SCARD_ATTR_DEVICE_FRIENDLY_NAME_A"; + + case SCARD_ATTR_DEVICE_SYSTEM_NAME_A: + return "SCARD_ATTR_DEVICE_SYSTEM_NAME_A"; + + case SCARD_ATTR_DEVICE_FRIENDLY_NAME_W: + return "SCARD_ATTR_DEVICE_FRIENDLY_NAME_W"; + + case SCARD_ATTR_DEVICE_SYSTEM_NAME_W: + return "SCARD_ATTR_DEVICE_SYSTEM_NAME_W"; + + case SCARD_ATTR_SUPRESS_T1_IFS_REQUEST: + return "SCARD_ATTR_SUPRESS_T1_IFS_REQUEST"; + + default: + return "SCARD_ATTR_UNKNOWN"; + } +} + +WINSCARDAPI const char* WINAPI SCardGetProtocolString(DWORD dwProtocols) +{ + if (dwProtocols == SCARD_PROTOCOL_UNDEFINED) + return "SCARD_PROTOCOL_UNDEFINED"; + + if (dwProtocols == SCARD_PROTOCOL_T0) + return "SCARD_PROTOCOL_T0"; + + if (dwProtocols == SCARD_PROTOCOL_T1) + return "SCARD_PROTOCOL_T1"; + + if (dwProtocols == SCARD_PROTOCOL_Tx) + return "SCARD_PROTOCOL_Tx"; + + if (dwProtocols == SCARD_PROTOCOL_RAW) + return "SCARD_PROTOCOL_RAW"; + + if (dwProtocols == SCARD_PROTOCOL_DEFAULT) + return "SCARD_PROTOCOL_DEFAULT"; + + if (dwProtocols == (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_RAW)) + return "SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_RAW"; + + if (dwProtocols == (SCARD_PROTOCOL_T1 | SCARD_PROTOCOL_RAW)) + return "SCARD_PROTOCOL_T1 | SCARD_PROTOCOL_RAW"; + + if (dwProtocols == (SCARD_PROTOCOL_Tx | SCARD_PROTOCOL_RAW)) + return "SCARD_PROTOCOL_Tx | SCARD_PROTOCOL_RAW"; + + return "SCARD_PROTOCOL_UNKNOWN"; +} + +WINSCARDAPI const char* WINAPI SCardGetShareModeString(DWORD dwShareMode) +{ + switch (dwShareMode) + { + case SCARD_SHARE_EXCLUSIVE: + return "SCARD_SHARE_EXCLUSIVE"; + + case SCARD_SHARE_SHARED: + return "SCARD_SHARE_SHARED"; + + case SCARD_SHARE_DIRECT: + return "SCARD_SHARE_DIRECT"; + + default: + return "SCARD_SHARE_UNKNOWN"; + } +} + +WINSCARDAPI const char* WINAPI SCardGetDispositionString(DWORD dwDisposition) +{ + switch (dwDisposition) + { + case SCARD_LEAVE_CARD: + return "SCARD_LEAVE_CARD"; + + case SCARD_RESET_CARD: + return "SCARD_RESET_CARD"; + + case SCARD_UNPOWER_CARD: + return "SCARD_UNPOWER_CARD"; + + default: + return "SCARD_UNKNOWN_CARD"; + } +} + +WINSCARDAPI const char* WINAPI SCardGetScopeString(DWORD dwScope) +{ + switch (dwScope) + { + case SCARD_SCOPE_USER: + return "SCARD_SCOPE_USER"; + + case SCARD_SCOPE_TERMINAL: + return "SCARD_SCOPE_TERMINAL"; + + case SCARD_SCOPE_SYSTEM: + return "SCARD_SCOPE_SYSTEM"; + + default: + return "SCARD_SCOPE_UNKNOWN"; + } +} + +WINSCARDAPI const char* WINAPI SCardGetCardStateString(DWORD dwCardState) +{ + switch (dwCardState) + { + case SCARD_UNKNOWN: + return "SCARD_UNKNOWN"; + + case SCARD_ABSENT: + return "SCARD_ABSENT"; + + case SCARD_PRESENT: + return "SCARD_PRESENT"; + + case SCARD_SWALLOWED: + return "SCARD_SWALLOWED"; + + case SCARD_POWERED: + return "SCARD_POWERED"; + + case SCARD_NEGOTIABLE: + return "SCARD_NEGOTIABLE"; + + case SCARD_SPECIFIC: + return "SCARD_SPECIFIC"; + + default: + return "SCARD_UNKNOWN"; + } +} + +WINSCARDAPI char* WINAPI SCardGetReaderStateString(DWORD dwReaderState) +{ + const size_t size = 512; + char* buffer = calloc(size, sizeof(char)); + + if (!buffer) + return NULL; + + if (dwReaderState & SCARD_STATE_IGNORE) + winpr_str_append("SCARD_STATE_IGNORE", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_CHANGED) + winpr_str_append("SCARD_STATE_CHANGED", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_UNKNOWN) + winpr_str_append("SCARD_STATE_UNKNOWN", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_UNAVAILABLE) + winpr_str_append("SCARD_STATE_UNAVAILABLE", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_EMPTY) + winpr_str_append("SCARD_STATE_EMPTY", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_PRESENT) + winpr_str_append("SCARD_STATE_PRESENT", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_ATRMATCH) + winpr_str_append("SCARD_STATE_ATRMATCH", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_EXCLUSIVE) + winpr_str_append("SCARD_STATE_EXCLUSIVE", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_INUSE) + winpr_str_append("SCARD_STATE_INUSE", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_MUTE) + winpr_str_append("SCARD_STATE_MUTE", buffer, size, "|"); + + if (dwReaderState & SCARD_STATE_UNPOWERED) + winpr_str_append("SCARD_STATE_UNPOWERED", buffer, size, "|"); + + if (!buffer[0]) + winpr_str_append("SCARD_STATE_UNAWARE", buffer, size, "|"); + + return buffer; +} + +#define WINSCARD_LOAD_PROC(_name, ...) \ + do \ + { \ + WINPR_PRAGMA_DIAG_PUSH \ + WINPR_PRAGMA_DIAG_IGNORED_PEDANTIC \ + pWinSCardApiTable->pfn##_name = (fn##_name)GetProcAddress(hWinSCardLibrary, #_name); \ + WINPR_PRAGMA_DIAG_POP \ + } while (0) + +BOOL WinSCard_LoadApiTableFunctions(PSCardApiFunctionTable pWinSCardApiTable, + HMODULE hWinSCardLibrary) +{ + WINPR_ASSERT(pWinSCardApiTable); + WINPR_ASSERT(hWinSCardLibrary); + + WINSCARD_LOAD_PROC(SCardEstablishContext); + WINSCARD_LOAD_PROC(SCardReleaseContext); + WINSCARD_LOAD_PROC(SCardIsValidContext); + WINSCARD_LOAD_PROC(SCardListReaderGroupsA); + WINSCARD_LOAD_PROC(SCardListReaderGroupsW); + WINSCARD_LOAD_PROC(SCardListReadersA); + WINSCARD_LOAD_PROC(SCardListReadersW); + WINSCARD_LOAD_PROC(SCardListCardsA); + WINSCARD_LOAD_PROC(SCardListCardsW); + WINSCARD_LOAD_PROC(SCardListInterfacesA); + WINSCARD_LOAD_PROC(SCardListInterfacesW); + WINSCARD_LOAD_PROC(SCardGetProviderIdA); + WINSCARD_LOAD_PROC(SCardGetProviderIdW); + WINSCARD_LOAD_PROC(SCardGetCardTypeProviderNameA); + WINSCARD_LOAD_PROC(SCardGetCardTypeProviderNameW); + WINSCARD_LOAD_PROC(SCardIntroduceReaderGroupA); + WINSCARD_LOAD_PROC(SCardIntroduceReaderGroupW); + WINSCARD_LOAD_PROC(SCardForgetReaderGroupA); + WINSCARD_LOAD_PROC(SCardForgetReaderGroupW); + WINSCARD_LOAD_PROC(SCardIntroduceReaderA); + WINSCARD_LOAD_PROC(SCardIntroduceReaderW); + WINSCARD_LOAD_PROC(SCardForgetReaderA); + WINSCARD_LOAD_PROC(SCardForgetReaderW); + WINSCARD_LOAD_PROC(SCardAddReaderToGroupA); + WINSCARD_LOAD_PROC(SCardAddReaderToGroupW); + WINSCARD_LOAD_PROC(SCardRemoveReaderFromGroupA); + WINSCARD_LOAD_PROC(SCardRemoveReaderFromGroupW); + WINSCARD_LOAD_PROC(SCardIntroduceCardTypeA); + WINSCARD_LOAD_PROC(SCardIntroduceCardTypeW); + WINSCARD_LOAD_PROC(SCardSetCardTypeProviderNameA); + WINSCARD_LOAD_PROC(SCardSetCardTypeProviderNameW); + WINSCARD_LOAD_PROC(SCardForgetCardTypeA); + WINSCARD_LOAD_PROC(SCardForgetCardTypeW); + WINSCARD_LOAD_PROC(SCardFreeMemory); + WINSCARD_LOAD_PROC(SCardAccessStartedEvent); + WINSCARD_LOAD_PROC(SCardReleaseStartedEvent); + WINSCARD_LOAD_PROC(SCardLocateCardsA); + WINSCARD_LOAD_PROC(SCardLocateCardsW); + WINSCARD_LOAD_PROC(SCardLocateCardsByATRA); + WINSCARD_LOAD_PROC(SCardLocateCardsByATRW); + WINSCARD_LOAD_PROC(SCardGetStatusChangeA); + WINSCARD_LOAD_PROC(SCardGetStatusChangeW); + WINSCARD_LOAD_PROC(SCardCancel); + WINSCARD_LOAD_PROC(SCardConnectA); + WINSCARD_LOAD_PROC(SCardConnectW); + WINSCARD_LOAD_PROC(SCardReconnect); + WINSCARD_LOAD_PROC(SCardDisconnect); + WINSCARD_LOAD_PROC(SCardBeginTransaction); + WINSCARD_LOAD_PROC(SCardEndTransaction); + WINSCARD_LOAD_PROC(SCardCancelTransaction); + WINSCARD_LOAD_PROC(SCardState); + WINSCARD_LOAD_PROC(SCardStatusA); + WINSCARD_LOAD_PROC(SCardStatusW); + WINSCARD_LOAD_PROC(SCardTransmit); + WINSCARD_LOAD_PROC(SCardGetTransmitCount); + WINSCARD_LOAD_PROC(SCardControl); + WINSCARD_LOAD_PROC(SCardGetAttrib); + WINSCARD_LOAD_PROC(SCardSetAttrib); + WINSCARD_LOAD_PROC(SCardUIDlgSelectCardA); + WINSCARD_LOAD_PROC(SCardUIDlgSelectCardW); + WINSCARD_LOAD_PROC(GetOpenCardNameA); + WINSCARD_LOAD_PROC(GetOpenCardNameW); + WINSCARD_LOAD_PROC(SCardDlgExtendedError); + WINSCARD_LOAD_PROC(SCardReadCacheA); + WINSCARD_LOAD_PROC(SCardReadCacheW); + WINSCARD_LOAD_PROC(SCardWriteCacheA); + WINSCARD_LOAD_PROC(SCardWriteCacheW); + WINSCARD_LOAD_PROC(SCardGetReaderIconA); + WINSCARD_LOAD_PROC(SCardGetReaderIconW); + WINSCARD_LOAD_PROC(SCardGetDeviceTypeIdA); + WINSCARD_LOAD_PROC(SCardGetDeviceTypeIdW); + WINSCARD_LOAD_PROC(SCardGetReaderDeviceInstanceIdA); + WINSCARD_LOAD_PROC(SCardGetReaderDeviceInstanceIdW); + WINSCARD_LOAD_PROC(SCardListReadersWithDeviceInstanceIdA); + WINSCARD_LOAD_PROC(SCardListReadersWithDeviceInstanceIdW); + WINSCARD_LOAD_PROC(SCardAudit); + + return TRUE; +} + +static const SCardApiFunctionTable WinPR_SCardApiFunctionTable = { + 0, /* dwVersion */ + 0, /* dwFlags */ + + SCardEstablishContext, /* SCardEstablishContext */ + SCardReleaseContext, /* SCardReleaseContext */ + SCardIsValidContext, /* SCardIsValidContext */ + SCardListReaderGroupsA, /* SCardListReaderGroupsA */ + SCardListReaderGroupsW, /* SCardListReaderGroupsW */ + SCardListReadersA, /* SCardListReadersA */ + SCardListReadersW, /* SCardListReadersW */ + SCardListCardsA, /* SCardListCardsA */ + SCardListCardsW, /* SCardListCardsW */ + SCardListInterfacesA, /* SCardListInterfacesA */ + SCardListInterfacesW, /* SCardListInterfacesW */ + SCardGetProviderIdA, /* SCardGetProviderIdA */ + SCardGetProviderIdW, /* SCardGetProviderIdW */ + SCardGetCardTypeProviderNameA, /* SCardGetCardTypeProviderNameA */ + SCardGetCardTypeProviderNameW, /* SCardGetCardTypeProviderNameW */ + SCardIntroduceReaderGroupA, /* SCardIntroduceReaderGroupA */ + SCardIntroduceReaderGroupW, /* SCardIntroduceReaderGroupW */ + SCardForgetReaderGroupA, /* SCardForgetReaderGroupA */ + SCardForgetReaderGroupW, /* SCardForgetReaderGroupW */ + SCardIntroduceReaderA, /* SCardIntroduceReaderA */ + SCardIntroduceReaderW, /* SCardIntroduceReaderW */ + SCardForgetReaderA, /* SCardForgetReaderA */ + SCardForgetReaderW, /* SCardForgetReaderW */ + SCardAddReaderToGroupA, /* SCardAddReaderToGroupA */ + SCardAddReaderToGroupW, /* SCardAddReaderToGroupW */ + SCardRemoveReaderFromGroupA, /* SCardRemoveReaderFromGroupA */ + SCardRemoveReaderFromGroupW, /* SCardRemoveReaderFromGroupW */ + SCardIntroduceCardTypeA, /* SCardIntroduceCardTypeA */ + SCardIntroduceCardTypeW, /* SCardIntroduceCardTypeW */ + SCardSetCardTypeProviderNameA, /* SCardSetCardTypeProviderNameA */ + SCardSetCardTypeProviderNameW, /* SCardSetCardTypeProviderNameW */ + SCardForgetCardTypeA, /* SCardForgetCardTypeA */ + SCardForgetCardTypeW, /* SCardForgetCardTypeW */ + SCardFreeMemory, /* SCardFreeMemory */ + SCardAccessStartedEvent, /* SCardAccessStartedEvent */ + SCardReleaseStartedEvent, /* SCardReleaseStartedEvent */ + SCardLocateCardsA, /* SCardLocateCardsA */ + SCardLocateCardsW, /* SCardLocateCardsW */ + SCardLocateCardsByATRA, /* SCardLocateCardsByATRA */ + SCardLocateCardsByATRW, /* SCardLocateCardsByATRW */ + SCardGetStatusChangeA, /* SCardGetStatusChangeA */ + SCardGetStatusChangeW, /* SCardGetStatusChangeW */ + SCardCancel, /* SCardCancel */ + SCardConnectA, /* SCardConnectA */ + SCardConnectW, /* SCardConnectW */ + SCardReconnect, /* SCardReconnect */ + SCardDisconnect, /* SCardDisconnect */ + SCardBeginTransaction, /* SCardBeginTransaction */ + SCardEndTransaction, /* SCardEndTransaction */ + SCardCancelTransaction, /* SCardCancelTransaction */ + SCardState, /* SCardState */ + SCardStatusA, /* SCardStatusA */ + SCardStatusW, /* SCardStatusW */ + SCardTransmit, /* SCardTransmit */ + SCardGetTransmitCount, /* SCardGetTransmitCount */ + SCardControl, /* SCardControl */ + SCardGetAttrib, /* SCardGetAttrib */ + SCardSetAttrib, /* SCardSetAttrib */ + SCardUIDlgSelectCardA, /* SCardUIDlgSelectCardA */ + SCardUIDlgSelectCardW, /* SCardUIDlgSelectCardW */ + GetOpenCardNameA, /* GetOpenCardNameA */ + GetOpenCardNameW, /* GetOpenCardNameW */ + SCardDlgExtendedError, /* SCardDlgExtendedError */ + SCardReadCacheA, /* SCardReadCacheA */ + SCardReadCacheW, /* SCardReadCacheW */ + SCardWriteCacheA, /* SCardWriteCacheA */ + SCardWriteCacheW, /* SCardWriteCacheW */ + SCardGetReaderIconA, /* SCardGetReaderIconA */ + SCardGetReaderIconW, /* SCardGetReaderIconW */ + SCardGetDeviceTypeIdA, /* SCardGetDeviceTypeIdA */ + SCardGetDeviceTypeIdW, /* SCardGetDeviceTypeIdW */ + SCardGetReaderDeviceInstanceIdA, /* SCardGetReaderDeviceInstanceIdA */ + SCardGetReaderDeviceInstanceIdW, /* SCardGetReaderDeviceInstanceIdW */ + SCardListReadersWithDeviceInstanceIdA, /* SCardListReadersWithDeviceInstanceIdA */ + SCardListReadersWithDeviceInstanceIdW, /* SCardListReadersWithDeviceInstanceIdW */ + SCardAudit /* SCardAudit */ +}; + +const SCardApiFunctionTable* WinPR_GetSCardApiFunctionTable(void) +{ + return &WinPR_SCardApiFunctionTable; +} diff --git a/winpr/libwinpr/smartcard/smartcard.h b/winpr/libwinpr/smartcard/smartcard.h new file mode 100644 index 0000000..e9279bc --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard.h @@ -0,0 +1,31 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_PRIVATE_H +#define WINPR_SMARTCARD_PRIVATE_H + +#include + +#ifndef _WIN32 +#include "smartcard_pcsc.h" +#else +#include "smartcard_windows.h" +#endif + +#endif /* WINPR_SMARTCARD_PRIVATE_H */ diff --git a/winpr/libwinpr/smartcard/smartcard_inspect.c b/winpr/libwinpr/smartcard/smartcard_inspect.c new file mode 100644 index 0000000..6b3ffff --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard_inspect.c @@ -0,0 +1,1363 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "smartcard_inspect.h" + +#include "../log.h" +#define TAG WINPR_TAG("smartcard.inspect") + +#define xstr(s) str(s) +#define str(s) #s + +#define SCARDAPI_STUB_CALL_LONG(status, _name, ...) \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + status = SCARD_E_NO_SERVICE; \ + } \ + else \ + status = g_SCardApi->pfn##_name(__VA_ARGS__) + +#define SCARDAPI_STUB_CALL_HANDLE(status, _name, ...) \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + status = NULL; \ + } \ + else \ + status = g_SCardApi->pfn##_name(__VA_ARGS__) + +#define SCARDAPI_STUB_CALL_VOID(_name, ...) \ + if (!g_SCardApi || !g_SCardApi->pfn##_name) \ + { \ + WLog_DBG(TAG, "Missing function pointer g_SCardApi=%p->" xstr(pfn##_name) "=%p", \ + g_SCardApi, g_SCardApi ? g_SCardApi->pfn##_name : NULL); \ + } \ + else \ + g_SCardApi->pfn##_name(__VA_ARGS__) + +static const DWORD g_LogLevel = WLOG_DEBUG; +static wLog* g_Log = NULL; + +static const SCardApiFunctionTable* g_SCardApi = NULL; + +/** + * Standard Windows Smart Card API + */ + +static LONG WINAPI Inspect_SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, LPSCARDCONTEXT phContext) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardEstablishContext { dwScope: %s (0x%08" PRIX32 ")", + SCardGetScopeString(dwScope), dwScope); + + SCARDAPI_STUB_CALL_LONG(status, SCardEstablishContext, dwScope, pvReserved1, pvReserved2, + phContext); + + WLog_Print(g_Log, g_LogLevel, "SCardEstablishContext } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardReleaseContext(SCARDCONTEXT hContext) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardReleaseContext { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardReleaseContext, hContext); + + WLog_Print(g_Log, g_LogLevel, "SCardReleaseContext } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIsValidContext(SCARDCONTEXT hContext) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIsValidContext { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIsValidContext, hContext); + + WLog_Print(g_Log, g_LogLevel, "SCardIsValidContext } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListReaderGroupsA(SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListReaderGroupsA, hContext, mszGroups, pcchGroups); + + WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListReaderGroupsW(SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListReaderGroupsW, hContext, mszGroups, pcchGroups); + + WLog_Print(g_Log, g_LogLevel, "SCardListReaderGroupsW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListReadersA(SCARDCONTEXT hContext, LPCSTR mszGroups, + LPSTR mszReaders, LPDWORD pcchReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListReadersA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersA, hContext, mszGroups, mszReaders, + pcchReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardListReadersA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListReadersW(SCARDCONTEXT hContext, LPCWSTR mszGroups, + LPWSTR mszReaders, LPDWORD pcchReaders) +{ + LONG status = 0; + WLog_Print(g_Log, g_LogLevel, "SCardListReadersW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersW, hContext, mszGroups, mszReaders, + pcchReaders); + WLog_Print(g_Log, g_LogLevel, "SCardListReadersW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListCardsA(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + CHAR* mszCards, LPDWORD pcchCards) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListCardsA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListCardsA, hContext, pbAtr, rgquidInterfaces, + cguidInterfaceCount, mszCards, pcchCards); + + WLog_Print(g_Log, g_LogLevel, "SCardListCardsA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListCardsW(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + WCHAR* mszCards, LPDWORD pcchCards) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListCardsW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListCardsW, hContext, pbAtr, rgquidInterfaces, + cguidInterfaceCount, mszCards, pcchCards); + + WLog_Print(g_Log, g_LogLevel, "SCardListCardsW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListInterfacesA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListInterfacesA, hContext, szCard, pguidInterfaces, + pcguidInterfaces); + + WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListInterfacesW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListInterfacesW, hContext, szCard, pguidInterfaces, + pcguidInterfaces); + + WLog_Print(g_Log, g_LogLevel, "SCardListInterfacesW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetProviderIdA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidProviderId) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetProviderIdA, hContext, szCard, pguidProviderId); + + WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetProviderIdW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidProviderId) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetProviderIdW, hContext, szCard, pguidProviderId); + + WLog_Print(g_Log, g_LogLevel, "SCardGetProviderIdW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, CHAR* szProvider, + LPDWORD pcchProvider) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetCardTypeProviderNameA, hContext, szCardName, + dwProviderId, szProvider, pcchProvider); + + WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetCardTypeProviderNameW, hContext, szCardName, + dwProviderId, szProvider, pcchProvider); + + WLog_Print(g_Log, g_LogLevel, "SCardGetCardTypeProviderNameW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIntroduceReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderGroupA, hContext, szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIntroduceReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderGroupW, hContext, szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardForgetReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderGroupA, hContext, szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardForgetReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderGroupW, hContext, szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIntroduceReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderA, hContext, szReaderName, szDeviceName); + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIntroduceReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceReaderW, hContext, szReaderName, szDeviceName); + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceReaderW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardForgetReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderA, hContext, szReaderName); + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardForgetReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardForgetReaderW, hContext, szReaderName); + + WLog_Print(g_Log, g_LogLevel, "SCardForgetReaderW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardAddReaderToGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardAddReaderToGroupA, hContext, szReaderName, szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardAddReaderToGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardAddReaderToGroupW, hContext, szReaderName, szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardAddReaderToGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardRemoveReaderFromGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardRemoveReaderFromGroupA, hContext, szReaderName, + szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardRemoveReaderFromGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardRemoveReaderFromGroupW, hContext, szReaderName, + szGroupName); + + WLog_Print(g_Log, g_LogLevel, "SCardRemoveReaderFromGroupW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIntroduceCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceCardTypeA, hContext, szCardName, + pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount, pbAtr, + pbAtrMask, cbAtrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardIntroduceCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardIntroduceCardTypeW, hContext, szCardName, + pguidPrimaryProvider, rgguidInterfaces, dwInterfaceCount, pbAtr, + pbAtrMask, cbAtrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardIntroduceCardTypeW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardSetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, LPCSTR szProvider) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardSetCardTypeProviderNameA, hContext, szCardName, + dwProviderId, szProvider); + + WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardSetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, LPCWSTR szProvider) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardSetCardTypeProviderNameW, hContext, szCardName, + dwProviderId, szProvider); + + WLog_Print(g_Log, g_LogLevel, "SCardSetCardTypeProviderNameW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardForgetCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardForgetCardTypeA, hContext, szCardName); + + WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardForgetCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardForgetCardTypeW, hContext, szCardName); + + WLog_Print(g_Log, g_LogLevel, "SCardForgetCardTypeW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardFreeMemory(SCARDCONTEXT hContext, LPVOID pvMem) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardFreeMemory { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardFreeMemory, hContext, pvMem); + + WLog_Print(g_Log, g_LogLevel, "SCardFreeMemory } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static HANDLE WINAPI Inspect_SCardAccessStartedEvent(void) +{ + HANDLE hEvent = NULL; + + WLog_Print(g_Log, g_LogLevel, "SCardAccessStartedEvent {"); + + SCARDAPI_STUB_CALL_HANDLE(hEvent, SCardAccessStartedEvent); + + WLog_Print(g_Log, g_LogLevel, "SCardAccessStartedEvent } hEvent: %p", hEvent); + + return hEvent; +} + +static void WINAPI Inspect_SCardReleaseStartedEvent(void) +{ + WLog_Print(g_Log, g_LogLevel, "SCardReleaseStartedEvent {"); + + SCARDAPI_STUB_CALL_VOID(SCardReleaseStartedEvent); + + WLog_Print(g_Log, g_LogLevel, "SCardReleaseStartedEvent }"); +} + +static LONG WINAPI Inspect_SCardLocateCardsA(SCARDCONTEXT hContext, LPCSTR mszCards, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsA, hContext, mszCards, rgReaderStates, + cReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardLocateCardsW(SCARDCONTEXT hContext, LPCWSTR mszCards, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsW, hContext, mszCards, rgReaderStates, + cReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardLocateCardsByATRA(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsByATRA, hContext, rgAtrMasks, cAtrs, + rgReaderStates, cReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardLocateCardsByATRW(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardLocateCardsByATRW, hContext, rgAtrMasks, cAtrs, + rgReaderStates, cReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardLocateCardsByATRW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetStatusChangeA(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetStatusChangeA, hContext, dwTimeout, rgReaderStates, + cReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetStatusChangeW(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetStatusChangeW, hContext, dwTimeout, rgReaderStates, + cReaders); + + WLog_Print(g_Log, g_LogLevel, "SCardGetStatusChangeW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardCancel(SCARDCONTEXT hContext) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardCancel { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardCancel, hContext); + + WLog_Print(g_Log, g_LogLevel, "SCardCancel } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardConnectA(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardConnectA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardConnectA, hContext, szReader, dwShareMode, + dwPreferredProtocols, phCard, pdwActiveProtocol); + + WLog_Print(g_Log, g_LogLevel, "SCardConnectA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardConnectW(SCARDCONTEXT hContext, LPCWSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardConnectW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardConnectW, hContext, szReader, dwShareMode, + dwPreferredProtocols, phCard, pdwActiveProtocol); + + WLog_Print(g_Log, g_LogLevel, "SCardConnectW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, + DWORD dwPreferredProtocols, DWORD dwInitialization, + LPDWORD pdwActiveProtocol) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardReconnect { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardReconnect, hCard, dwShareMode, dwPreferredProtocols, + dwInitialization, pdwActiveProtocol); + + WLog_Print(g_Log, g_LogLevel, "SCardReconnect } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardDisconnect { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardDisconnect, hCard, dwDisposition); + + WLog_Print(g_Log, g_LogLevel, "SCardDisconnect } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardBeginTransaction(SCARDHANDLE hCard) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardBeginTransaction { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardBeginTransaction, hCard); + + WLog_Print(g_Log, g_LogLevel, "SCardBeginTransaction } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardEndTransaction { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardEndTransaction, hCard, dwDisposition); + + WLog_Print(g_Log, g_LogLevel, "SCardEndTransaction } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardCancelTransaction(SCARDHANDLE hCard) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardCancelTransaction { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardCancelTransaction, hCard); + + WLog_Print(g_Log, g_LogLevel, "SCardCancelTransaction } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardState(SCARDHANDLE hCard, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardState { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardState, hCard, pdwState, pdwProtocol, pbAtr, pcbAtrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardState } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardStatusA(SCARDHANDLE hCard, LPSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardStatusA { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardStatusA, hCard, mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardStatusA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardStatusW(SCARDHANDLE hCard, LPWSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardStatusW { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardStatusW, hCard, mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardStatusW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, + LPCBYTE pbSendBuffer, DWORD cbSendLength, + LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardTransmit { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardTransmit, hCard, pioSendPci, pbSendBuffer, cbSendLength, + pioRecvPci, pbRecvBuffer, pcbRecvLength); + + WLog_Print(g_Log, g_LogLevel, "SCardTransmit } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetTransmitCount(SCARDHANDLE hCard, LPDWORD pcTransmitCount) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetTransmitCount { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetTransmitCount, hCard, pcTransmitCount); + + WLog_Print(g_Log, g_LogLevel, "SCardGetTransmitCount } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, + DWORD cbInBufferSize, LPVOID lpOutBuffer, + DWORD cbOutBufferSize, LPDWORD lpBytesReturned) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardControl { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardControl, hCard, dwControlCode, lpInBuffer, cbInBufferSize, + lpOutBuffer, cbOutBufferSize, lpBytesReturned); + + WLog_Print(g_Log, g_LogLevel, "SCardControl } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetAttrib { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetAttrib, hCard, dwAttrId, pbAttr, pcbAttrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardGetAttrib } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, + DWORD cbAttrLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardSetAttrib { hCard: %p", (void*)hCard); + + SCARDAPI_STUB_CALL_LONG(status, SCardSetAttrib, hCard, dwAttrId, pbAttr, cbAttrLen); + + WLog_Print(g_Log, g_LogLevel, "SCardSetAttrib } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardUIDlgSelectCardA(LPOPENCARDNAMEA_EX pDlgStruc) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardA {"); + + SCARDAPI_STUB_CALL_LONG(status, SCardUIDlgSelectCardA, pDlgStruc); + + WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardUIDlgSelectCardW(LPOPENCARDNAMEW_EX pDlgStruc) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardW {"); + + SCARDAPI_STUB_CALL_LONG(status, SCardUIDlgSelectCardW, pDlgStruc); + + WLog_Print(g_Log, g_LogLevel, "SCardUIDlgSelectCardW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_GetOpenCardNameA(LPOPENCARDNAMEA pDlgStruc) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameA {"); + + SCARDAPI_STUB_CALL_LONG(status, GetOpenCardNameA, pDlgStruc); + + WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_GetOpenCardNameW(LPOPENCARDNAMEW pDlgStruc) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameW {"); + + SCARDAPI_STUB_CALL_LONG(status, GetOpenCardNameW, pDlgStruc); + + WLog_Print(g_Log, g_LogLevel, "GetOpenCardNameW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardDlgExtendedError(void) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardDlgExtendedError {"); + + SCARDAPI_STUB_CALL_LONG(status, SCardDlgExtendedError); + + WLog_Print(g_Log, g_LogLevel, "SCardDlgExtendedError } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardReadCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD* DataLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardReadCacheA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardReadCacheA, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); + + WLog_Print(g_Log, g_LogLevel, "SCardReadCacheA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardReadCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD* DataLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardReadCacheW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardReadCacheW, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); + + WLog_Print(g_Log, g_LogLevel, "SCardReadCacheW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardWriteCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD DataLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardWriteCacheA, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); + + WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardWriteCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD DataLen) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardWriteCacheW, hContext, CardIdentifier, FreshnessCounter, + LookupName, Data, DataLen); + + WLog_Print(g_Log, g_LogLevel, "SCardWriteCacheW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetReaderIconA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderIconA, hContext, szReaderName, pbIcon, pcbIcon); + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetReaderIconW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderIconW, hContext, szReaderName, pbIcon, pcbIcon); + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderIconW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetDeviceTypeIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdA { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetDeviceTypeIdA, hContext, szReaderName, pdwDeviceTypeId); + + WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetDeviceTypeIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdW { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetDeviceTypeIdW, hContext, szReaderName, pdwDeviceTypeId); + + WLog_Print(g_Log, g_LogLevel, "SCardGetDeviceTypeIdW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetReaderDeviceInstanceIdA(SCARDCONTEXT hContext, + LPCSTR szReaderName, + LPSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdA { hContext: %p", + (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderDeviceInstanceIdA, hContext, szReaderName, + szDeviceInstanceId, pcchDeviceInstanceId); + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardGetReaderDeviceInstanceIdW(SCARDCONTEXT hContext, + LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdW { hContext: %p", + (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardGetReaderDeviceInstanceIdW, hContext, szReaderName, + szDeviceInstanceId, pcchDeviceInstanceId); + + WLog_Print(g_Log, g_LogLevel, "SCardGetReaderDeviceInstanceIdW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListReadersWithDeviceInstanceIdA(SCARDCONTEXT hContext, + LPCSTR szDeviceInstanceId, + LPSTR mszReaders, + LPDWORD pcchReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListReadersWithDeviceInstanceIdA { hContext: %p", + (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersWithDeviceInstanceIdA, hContext, + szDeviceInstanceId, mszReaders, pcchReaders); + + WLog_Print(g_Log, g_LogLevel, + "SCardListReadersWithDeviceInstanceIdA } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardListReadersWithDeviceInstanceIdW(SCARDCONTEXT hContext, + LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, + LPDWORD pcchReaders) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardListReadersWithDeviceInstanceIdW { hContext: %p", + (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardListReadersWithDeviceInstanceIdW, hContext, + szDeviceInstanceId, mszReaders, pcchReaders); + + WLog_Print(g_Log, g_LogLevel, + "SCardListReadersWithDeviceInstanceIdW } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +static LONG WINAPI Inspect_SCardAudit(SCARDCONTEXT hContext, DWORD dwEvent) +{ + LONG status = 0; + + WLog_Print(g_Log, g_LogLevel, "SCardAudit { hContext: %p", (void*)hContext); + + SCARDAPI_STUB_CALL_LONG(status, SCardAudit, hContext, dwEvent); + + WLog_Print(g_Log, g_LogLevel, "SCardAudit } status: %s (0x%08" PRIX32 ")", + SCardGetErrorString(status), status); + + return status; +} + +/** + * Extended API + */ + +static const SCardApiFunctionTable Inspect_SCardApiFunctionTable = { + 0, /* dwVersion */ + 0, /* dwFlags */ + + Inspect_SCardEstablishContext, /* SCardEstablishContext */ + Inspect_SCardReleaseContext, /* SCardReleaseContext */ + Inspect_SCardIsValidContext, /* SCardIsValidContext */ + Inspect_SCardListReaderGroupsA, /* SCardListReaderGroupsA */ + Inspect_SCardListReaderGroupsW, /* SCardListReaderGroupsW */ + Inspect_SCardListReadersA, /* SCardListReadersA */ + Inspect_SCardListReadersW, /* SCardListReadersW */ + Inspect_SCardListCardsA, /* SCardListCardsA */ + Inspect_SCardListCardsW, /* SCardListCardsW */ + Inspect_SCardListInterfacesA, /* SCardListInterfacesA */ + Inspect_SCardListInterfacesW, /* SCardListInterfacesW */ + Inspect_SCardGetProviderIdA, /* SCardGetProviderIdA */ + Inspect_SCardGetProviderIdW, /* SCardGetProviderIdW */ + Inspect_SCardGetCardTypeProviderNameA, /* SCardGetCardTypeProviderNameA */ + Inspect_SCardGetCardTypeProviderNameW, /* SCardGetCardTypeProviderNameW */ + Inspect_SCardIntroduceReaderGroupA, /* SCardIntroduceReaderGroupA */ + Inspect_SCardIntroduceReaderGroupW, /* SCardIntroduceReaderGroupW */ + Inspect_SCardForgetReaderGroupA, /* SCardForgetReaderGroupA */ + Inspect_SCardForgetReaderGroupW, /* SCardForgetReaderGroupW */ + Inspect_SCardIntroduceReaderA, /* SCardIntroduceReaderA */ + Inspect_SCardIntroduceReaderW, /* SCardIntroduceReaderW */ + Inspect_SCardForgetReaderA, /* SCardForgetReaderA */ + Inspect_SCardForgetReaderW, /* SCardForgetReaderW */ + Inspect_SCardAddReaderToGroupA, /* SCardAddReaderToGroupA */ + Inspect_SCardAddReaderToGroupW, /* SCardAddReaderToGroupW */ + Inspect_SCardRemoveReaderFromGroupA, /* SCardRemoveReaderFromGroupA */ + Inspect_SCardRemoveReaderFromGroupW, /* SCardRemoveReaderFromGroupW */ + Inspect_SCardIntroduceCardTypeA, /* SCardIntroduceCardTypeA */ + Inspect_SCardIntroduceCardTypeW, /* SCardIntroduceCardTypeW */ + Inspect_SCardSetCardTypeProviderNameA, /* SCardSetCardTypeProviderNameA */ + Inspect_SCardSetCardTypeProviderNameW, /* SCardSetCardTypeProviderNameW */ + Inspect_SCardForgetCardTypeA, /* SCardForgetCardTypeA */ + Inspect_SCardForgetCardTypeW, /* SCardForgetCardTypeW */ + Inspect_SCardFreeMemory, /* SCardFreeMemory */ + Inspect_SCardAccessStartedEvent, /* SCardAccessStartedEvent */ + Inspect_SCardReleaseStartedEvent, /* SCardReleaseStartedEvent */ + Inspect_SCardLocateCardsA, /* SCardLocateCardsA */ + Inspect_SCardLocateCardsW, /* SCardLocateCardsW */ + Inspect_SCardLocateCardsByATRA, /* SCardLocateCardsByATRA */ + Inspect_SCardLocateCardsByATRW, /* SCardLocateCardsByATRW */ + Inspect_SCardGetStatusChangeA, /* SCardGetStatusChangeA */ + Inspect_SCardGetStatusChangeW, /* SCardGetStatusChangeW */ + Inspect_SCardCancel, /* SCardCancel */ + Inspect_SCardConnectA, /* SCardConnectA */ + Inspect_SCardConnectW, /* SCardConnectW */ + Inspect_SCardReconnect, /* SCardReconnect */ + Inspect_SCardDisconnect, /* SCardDisconnect */ + Inspect_SCardBeginTransaction, /* SCardBeginTransaction */ + Inspect_SCardEndTransaction, /* SCardEndTransaction */ + Inspect_SCardCancelTransaction, /* SCardCancelTransaction */ + Inspect_SCardState, /* SCardState */ + Inspect_SCardStatusA, /* SCardStatusA */ + Inspect_SCardStatusW, /* SCardStatusW */ + Inspect_SCardTransmit, /* SCardTransmit */ + Inspect_SCardGetTransmitCount, /* SCardGetTransmitCount */ + Inspect_SCardControl, /* SCardControl */ + Inspect_SCardGetAttrib, /* SCardGetAttrib */ + Inspect_SCardSetAttrib, /* SCardSetAttrib */ + Inspect_SCardUIDlgSelectCardA, /* SCardUIDlgSelectCardA */ + Inspect_SCardUIDlgSelectCardW, /* SCardUIDlgSelectCardW */ + Inspect_GetOpenCardNameA, /* GetOpenCardNameA */ + Inspect_GetOpenCardNameW, /* GetOpenCardNameW */ + Inspect_SCardDlgExtendedError, /* SCardDlgExtendedError */ + Inspect_SCardReadCacheA, /* SCardReadCacheA */ + Inspect_SCardReadCacheW, /* SCardReadCacheW */ + Inspect_SCardWriteCacheA, /* SCardWriteCacheA */ + Inspect_SCardWriteCacheW, /* SCardWriteCacheW */ + Inspect_SCardGetReaderIconA, /* SCardGetReaderIconA */ + Inspect_SCardGetReaderIconW, /* SCardGetReaderIconW */ + Inspect_SCardGetDeviceTypeIdA, /* SCardGetDeviceTypeIdA */ + Inspect_SCardGetDeviceTypeIdW, /* SCardGetDeviceTypeIdW */ + Inspect_SCardGetReaderDeviceInstanceIdA, /* SCardGetReaderDeviceInstanceIdA */ + Inspect_SCardGetReaderDeviceInstanceIdW, /* SCardGetReaderDeviceInstanceIdW */ + Inspect_SCardListReadersWithDeviceInstanceIdA, /* SCardListReadersWithDeviceInstanceIdA */ + Inspect_SCardListReadersWithDeviceInstanceIdW, /* SCardListReadersWithDeviceInstanceIdW */ + Inspect_SCardAudit /* SCardAudit */ +}; + +static void Inspect_InitLog(void) +{ + if (g_Log) + return; + + if (!(g_Log = WLog_Get("WinSCard"))) + return; +} + +const SCardApiFunctionTable* Inspect_RegisterSCardApi(const SCardApiFunctionTable* pSCardApi) +{ + g_SCardApi = pSCardApi; + + Inspect_InitLog(); + + return &Inspect_SCardApiFunctionTable; +} diff --git a/winpr/libwinpr/smartcard/smartcard_inspect.h b/winpr/libwinpr/smartcard/smartcard_inspect.h new file mode 100644 index 0000000..482dd97 --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard_inspect.h @@ -0,0 +1,30 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_INSPECT_PRIVATE_H +#define WINPR_SMARTCARD_INSPECT_PRIVATE_H + +#include +#include + +const SCardApiFunctionTable* Inspect_RegisterSCardApi(const SCardApiFunctionTable* pSCardApi); + +#endif /* WINPR_SMARTCARD_INSPECT_PRIVATE_H */ diff --git a/winpr/libwinpr/smartcard/smartcard_pcsc.c b/winpr/libwinpr/smartcard/smartcard_pcsc.c new file mode 100644 index 0000000..fb04d56 --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard_pcsc.c @@ -0,0 +1,3357 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef _WIN32 + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "smartcard_pcsc.h" + +#include "../log.h" +#define TAG WINPR_TAG("smartcard") + +#define WINSCARD_LOAD_PROC_EX(module, pcsc, _fname, _name, ...) \ + do \ + { \ + WINPR_PRAGMA_DIAG_PUSH \ + WINPR_PRAGMA_DIAG_IGNORED_PEDANTIC \ + pcsc.pfn##_fname = (fnPCSC##_fname)GetProcAddress(module, #_name); \ + WINPR_PRAGMA_DIAG_POP \ + } while (0) + +#define WINSCARD_LOAD_PROC(module, pcsc, _name, ...) \ + WINSCARD_LOAD_PROC_EX(module, pcsc, _name, _name, ##__VA_ARGS__) + +/** + * PC/SC transactions: + * http://developersblog.wwpass.com/?p=180 + */ + +/** + * Smart Card Logon on Windows Vista: + * http://blogs.msdn.com/b/shivaram/archive/2007/02/26/smart-card-logon-on-windows-vista.aspx + */ + +/** + * The Smart Card Cryptographic Service Provider Cookbook: + * http://msdn.microsoft.com/en-us/library/ms953432.aspx + * + * SCARDCONTEXT + * + * The context is a communication channel with the smart card resource manager and + * all calls to the resource manager must go through this link. + * + * All functions that take a context as a parameter or a card handle as parameter, + * which is indirectly associated with a particular context, may be blocking calls. + * Examples of these are SCardGetStatusChange and SCardBeginTransaction, which takes + * a card handle as a parameter. If such a function blocks then all operations wanting + * to use the context are blocked as well. So, it is recommended that a CSP using + * monitoring establishes at least two contexts with the resource manager; one for + * monitoring (with SCardGetStatusChange) and one for other operations. + * + * If multiple cards are present, it is recommended that a separate context or pair + * of contexts be established for each card to prevent operations on one card from + * blocking operations on another. + * + * Example one + * + * The example below shows what can happen if a CSP using SCardGetStatusChange for + * monitoring does not establish two contexts with the resource manager. + * The context becomes unusable until SCardGetStatusChange unblocks. + * + * In this example, there is one process running called P1. + * P1 calls SCardEstablishContext, which returns the context hCtx. + * P1 calls SCardConnect (with the hCtx context) which returns a handle to the card, hCard. + * P1 calls SCardGetStatusChange (with the hCtx context) which blocks because + * there are no status changes to report. + * Until the thread running SCardGetStatusChange unblocks, another thread in P1 trying to + * perform an operation using the context hCtx (or the card hCard) will also be blocked. + * + * Example two + * + * The example below shows how transaction control ensures that operations meant to be + * performed without interruption can do so safely within a transaction. + * + * In this example, there are two different processes running; P1 and P2. + * P1 calls SCardEstablishContext, which returns the context hCtx1. + * P2 calls SCardEstablishContext, which returns the context hCtx2. + * P1 calls SCardConnect (with the hCtx1 context) which returns a handle to the card, hCard1. + * P2 calls SCardConnect (with the hCtx2 context) which returns a handle to the same card, hCard2. + * P1 calls SCardBeginTransaction (with the hCard 1 context). + * Until P1 calls SCardEndTransaction (with the hCard1 context), + * any operation using hCard2 will be blocked. + * Once an operation using hCard2 is blocked and until it's returning, + * any operation using hCtx2 (and hCard2) will also be blocked. + */ + +//#define DISABLE_PCSC_SCARD_AUTOALLOCATE +#include "smartcard_pcsc.h" + +#define PCSC_SCARD_PCI_T0 (&g_PCSC_rgSCardT0Pci) +#define PCSC_SCARD_PCI_T1 (&g_PCSC_rgSCardT1Pci) +#define PCSC_SCARD_PCI_RAW (&g_PCSC_rgSCardRawPci) + +typedef PCSC_LONG (*fnPCSCSCardEstablishContext)(PCSC_DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, LPSCARDCONTEXT phContext); +typedef PCSC_LONG (*fnPCSCSCardReleaseContext)(SCARDCONTEXT hContext); +typedef PCSC_LONG (*fnPCSCSCardIsValidContext)(SCARDCONTEXT hContext); +typedef PCSC_LONG (*fnPCSCSCardConnect)(SCARDCONTEXT hContext, LPCSTR szReader, + PCSC_DWORD dwShareMode, PCSC_DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, PCSC_LPDWORD pdwActiveProtocol); +typedef PCSC_LONG (*fnPCSCSCardReconnect)(SCARDHANDLE hCard, PCSC_DWORD dwShareMode, + PCSC_DWORD dwPreferredProtocols, + PCSC_DWORD dwInitialization, + PCSC_LPDWORD pdwActiveProtocol); +typedef PCSC_LONG (*fnPCSCSCardDisconnect)(SCARDHANDLE hCard, PCSC_DWORD dwDisposition); +typedef PCSC_LONG (*fnPCSCSCardBeginTransaction)(SCARDHANDLE hCard); +typedef PCSC_LONG (*fnPCSCSCardEndTransaction)(SCARDHANDLE hCard, PCSC_DWORD dwDisposition); +typedef PCSC_LONG (*fnPCSCSCardStatus)(SCARDHANDLE hCard, LPSTR mszReaderName, + PCSC_LPDWORD pcchReaderLen, PCSC_LPDWORD pdwState, + PCSC_LPDWORD pdwProtocol, LPBYTE pbAtr, + PCSC_LPDWORD pcbAtrLen); +typedef PCSC_LONG (*fnPCSCSCardGetStatusChange)(SCARDCONTEXT hContext, PCSC_DWORD dwTimeout, + PCSC_SCARD_READERSTATE* rgReaderStates, + PCSC_DWORD cReaders); +typedef PCSC_LONG (*fnPCSCSCardControl)(SCARDHANDLE hCard, PCSC_DWORD dwControlCode, + LPCVOID pbSendBuffer, PCSC_DWORD cbSendLength, + LPVOID pbRecvBuffer, PCSC_DWORD cbRecvLength, + PCSC_LPDWORD lpBytesReturned); +typedef PCSC_LONG (*fnPCSCSCardTransmit)(SCARDHANDLE hCard, const PCSC_SCARD_IO_REQUEST* pioSendPci, + LPCBYTE pbSendBuffer, PCSC_DWORD cbSendLength, + PCSC_SCARD_IO_REQUEST* pioRecvPci, LPBYTE pbRecvBuffer, + PCSC_LPDWORD pcbRecvLength); +typedef PCSC_LONG (*fnPCSCSCardListReaderGroups)(SCARDCONTEXT hContext, LPSTR mszGroups, + PCSC_LPDWORD pcchGroups); +typedef PCSC_LONG (*fnPCSCSCardListReaders)(SCARDCONTEXT hContext, LPCSTR mszGroups, + LPSTR mszReaders, PCSC_LPDWORD pcchReaders); +typedef PCSC_LONG (*fnPCSCSCardFreeMemory)(SCARDCONTEXT hContext, LPCVOID pvMem); +typedef PCSC_LONG (*fnPCSCSCardCancel)(SCARDCONTEXT hContext); +typedef PCSC_LONG (*fnPCSCSCardGetAttrib)(SCARDHANDLE hCard, PCSC_DWORD dwAttrId, LPBYTE pbAttr, + PCSC_LPDWORD pcbAttrLen); +typedef PCSC_LONG (*fnPCSCSCardSetAttrib)(SCARDHANDLE hCard, PCSC_DWORD dwAttrId, LPCBYTE pbAttr, + PCSC_DWORD cbAttrLen); + +typedef struct +{ + fnPCSCSCardEstablishContext pfnSCardEstablishContext; + fnPCSCSCardReleaseContext pfnSCardReleaseContext; + fnPCSCSCardIsValidContext pfnSCardIsValidContext; + fnPCSCSCardConnect pfnSCardConnect; + fnPCSCSCardReconnect pfnSCardReconnect; + fnPCSCSCardDisconnect pfnSCardDisconnect; + fnPCSCSCardBeginTransaction pfnSCardBeginTransaction; + fnPCSCSCardEndTransaction pfnSCardEndTransaction; + fnPCSCSCardStatus pfnSCardStatus; + fnPCSCSCardGetStatusChange pfnSCardGetStatusChange; + fnPCSCSCardControl pfnSCardControl; + fnPCSCSCardTransmit pfnSCardTransmit; + fnPCSCSCardListReaderGroups pfnSCardListReaderGroups; + fnPCSCSCardListReaders pfnSCardListReaders; + fnPCSCSCardFreeMemory pfnSCardFreeMemory; + fnPCSCSCardCancel pfnSCardCancel; + fnPCSCSCardGetAttrib pfnSCardGetAttrib; + fnPCSCSCardSetAttrib pfnSCardSetAttrib; +} PCSCFunctionTable; + +typedef struct +{ + DWORD len; + DWORD freshness; + BYTE* data; +} PCSC_CACHE_ITEM; + +typedef struct +{ + SCARDHANDLE owner; + CRITICAL_SECTION lock; + SCARDCONTEXT hContext; + DWORD dwCardHandleCount; + BOOL isTransactionLocked; + wHashTable* cache; +} PCSC_SCARDCONTEXT; + +typedef struct +{ + BOOL shared; + SCARDCONTEXT hSharedContext; +} PCSC_SCARDHANDLE; + +static HMODULE g_PCSCModule = NULL; +static PCSCFunctionTable g_PCSC = { 0 }; + +static HANDLE g_StartedEvent = NULL; +static int g_StartedEventRefCount = 0; + +static BOOL g_SCardAutoAllocate = FALSE; +static BOOL g_PnP_Notification = TRUE; + +#ifdef __MACOSX__ +static unsigned int OSXVersion = 0; +#endif + +static wListDictionary* g_CardHandles = NULL; +static wListDictionary* g_CardContexts = NULL; +static wListDictionary* g_MemoryBlocks = NULL; + +static const char SMARTCARD_PNP_NOTIFICATION_A[] = "\\\\?PnP?\\Notification"; + +static const PCSC_SCARD_IO_REQUEST g_PCSC_rgSCardT0Pci = { SCARD_PROTOCOL_T0, + sizeof(PCSC_SCARD_IO_REQUEST) }; +static const PCSC_SCARD_IO_REQUEST g_PCSC_rgSCardT1Pci = { SCARD_PROTOCOL_T1, + sizeof(PCSC_SCARD_IO_REQUEST) }; +static const PCSC_SCARD_IO_REQUEST g_PCSC_rgSCardRawPci = { PCSC_SCARD_PROTOCOL_RAW, + sizeof(PCSC_SCARD_IO_REQUEST) }; + +static LONG WINAPI PCSC_SCardFreeMemory_Internal(SCARDCONTEXT hContext, LPVOID pvMem); +static LONG WINAPI PCSC_SCardEstablishContext_Internal(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, + LPSCARDCONTEXT phContext); +static LONG WINAPI PCSC_SCardReleaseContext_Internal(SCARDCONTEXT hContext); + +static LONG PCSC_SCard_LogError(const char* what) +{ + WLog_WARN(TAG, "Missing function pointer %s=NULL", what); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG PCSC_MapErrorCodeToWinSCard(PCSC_LONG errorCode) +{ + /** + * pcsc-lite returns SCARD_E_UNEXPECTED when it + * should return SCARD_E_UNSUPPORTED_FEATURE. + * + * Additionally, the pcsc-lite headers incorrectly + * define SCARD_E_UNSUPPORTED_FEATURE to 0x8010001F, + * when the real value should be 0x80100022. + */ + if (errorCode != SCARD_S_SUCCESS) + { + if (errorCode == SCARD_E_UNEXPECTED) + errorCode = SCARD_E_UNSUPPORTED_FEATURE; + } + + return (LONG)errorCode; +} + +static DWORD PCSC_ConvertCardStateToWinSCard(DWORD dwCardState, PCSC_LONG status) +{ + /** + * pcsc-lite's SCardStatus returns a bit-field, not an enumerated value. + * + * State WinSCard pcsc-lite + * + * SCARD_UNKNOWN 0 0x0001 + * SCARD_ABSENT 1 0x0002 + * SCARD_PRESENT 2 0x0004 + * SCARD_SWALLOWED 3 0x0008 + * SCARD_POWERED 4 0x0010 + * SCARD_NEGOTIABLE 5 0x0020 + * SCARD_SPECIFIC 6 0x0040 + * + * pcsc-lite also never sets SCARD_SPECIFIC, + * which is expected by some windows applications. + */ + if (status == SCARD_S_SUCCESS) + { + if ((dwCardState & PCSC_SCARD_NEGOTIABLE) || (dwCardState & PCSC_SCARD_SPECIFIC)) + return SCARD_SPECIFIC; + } + + if (dwCardState & PCSC_SCARD_POWERED) + return SCARD_POWERED; + + if (dwCardState & PCSC_SCARD_NEGOTIABLE) + return SCARD_NEGOTIABLE; + + if (dwCardState & PCSC_SCARD_SPECIFIC) + return SCARD_SPECIFIC; + + if (dwCardState & PCSC_SCARD_ABSENT) + return SCARD_ABSENT; + + if (dwCardState & PCSC_SCARD_PRESENT) + return SCARD_PRESENT; + + if (dwCardState & PCSC_SCARD_SWALLOWED) + return SCARD_SWALLOWED; + + if (dwCardState & PCSC_SCARD_UNKNOWN) + return SCARD_UNKNOWN; + + return SCARD_UNKNOWN; +} + +static DWORD PCSC_ConvertProtocolsToWinSCard(PCSC_DWORD dwProtocols) +{ + /** + * pcsc-lite uses a different value for SCARD_PROTOCOL_RAW, + * and also has SCARD_PROTOCOL_T15 which is not in WinSCard. + */ + if (dwProtocols & PCSC_SCARD_PROTOCOL_RAW) + { + dwProtocols &= ~PCSC_SCARD_PROTOCOL_RAW; + dwProtocols |= SCARD_PROTOCOL_RAW; + } + + if (dwProtocols & PCSC_SCARD_PROTOCOL_T15) + { + dwProtocols &= ~PCSC_SCARD_PROTOCOL_T15; + } + + return (DWORD)dwProtocols; +} + +static DWORD PCSC_ConvertProtocolsFromWinSCard(DWORD dwProtocols) +{ + /** + * pcsc-lite uses a different value for SCARD_PROTOCOL_RAW, + * and it does not define WinSCard's SCARD_PROTOCOL_DEFAULT. + */ + if (dwProtocols & SCARD_PROTOCOL_RAW) + { + dwProtocols &= ~SCARD_PROTOCOL_RAW; + dwProtocols |= PCSC_SCARD_PROTOCOL_RAW; + } + + if (dwProtocols & SCARD_PROTOCOL_DEFAULT) + { + dwProtocols &= ~SCARD_PROTOCOL_DEFAULT; + } + + if (dwProtocols == SCARD_PROTOCOL_UNDEFINED) + { + dwProtocols = SCARD_PROTOCOL_Tx; + } + + return dwProtocols; +} + +static PCSC_SCARDCONTEXT* PCSC_GetCardContextData(SCARDCONTEXT hContext) +{ + PCSC_SCARDCONTEXT* pContext = NULL; + + if (!g_CardContexts) + return NULL; + + pContext = (PCSC_SCARDCONTEXT*)ListDictionary_GetItemValue(g_CardContexts, (void*)hContext); + + if (!pContext) + return NULL; + + return pContext; +} + +static void pcsc_cache_item_free(void* ptr) +{ + PCSC_CACHE_ITEM* data = ptr; + if (data) + free(data->data); + free(data); +} + +static PCSC_SCARDCONTEXT* PCSC_EstablishCardContext(SCARDCONTEXT hContext) +{ + PCSC_SCARDCONTEXT* pContext = NULL; + pContext = (PCSC_SCARDCONTEXT*)calloc(1, sizeof(PCSC_SCARDCONTEXT)); + + if (!pContext) + return NULL; + + pContext->hContext = hContext; + + if (!InitializeCriticalSectionAndSpinCount(&(pContext->lock), 4000)) + goto error_spinlock; + + pContext->cache = HashTable_New(FALSE); + if (!pContext->cache) + goto errors; + if (!HashTable_SetupForStringData(pContext->cache, FALSE)) + goto errors; + { + wObject* obj = HashTable_ValueObject(pContext->cache); + obj->fnObjectFree = pcsc_cache_item_free; + } + + if (!g_CardContexts) + { + g_CardContexts = ListDictionary_New(TRUE); + + if (!g_CardContexts) + goto errors; + } + + if (!ListDictionary_Add(g_CardContexts, (void*)hContext, (void*)pContext)) + goto errors; + + return pContext; +errors: + HashTable_Free(pContext->cache); + DeleteCriticalSection(&(pContext->lock)); +error_spinlock: + free(pContext); + return NULL; +} + +static void PCSC_ReleaseCardContext(SCARDCONTEXT hContext) +{ + PCSC_SCARDCONTEXT* pContext = NULL; + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + { + WLog_ERR(TAG, "PCSC_ReleaseCardContext: null pContext!"); + return; + } + + DeleteCriticalSection(&(pContext->lock)); + HashTable_Free(pContext->cache); + free(pContext); + + if (!g_CardContexts) + return; + + ListDictionary_Remove(g_CardContexts, (void*)hContext); +} + +static BOOL PCSC_LockCardContext(SCARDCONTEXT hContext) +{ + PCSC_SCARDCONTEXT* pContext = NULL; + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + { + WLog_ERR(TAG, "PCSC_LockCardContext: invalid context (%p)", (void*)hContext); + return FALSE; + } + + EnterCriticalSection(&(pContext->lock)); + return TRUE; +} + +static BOOL PCSC_UnlockCardContext(SCARDCONTEXT hContext) +{ + PCSC_SCARDCONTEXT* pContext = NULL; + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + { + WLog_ERR(TAG, "PCSC_UnlockCardContext: invalid context (%p)", (void*)hContext); + return FALSE; + } + + LeaveCriticalSection(&(pContext->lock)); + return TRUE; +} + +static PCSC_SCARDHANDLE* PCSC_GetCardHandleData(SCARDHANDLE hCard) +{ + PCSC_SCARDHANDLE* pCard = NULL; + + if (!g_CardHandles) + return NULL; + + pCard = (PCSC_SCARDHANDLE*)ListDictionary_GetItemValue(g_CardHandles, (void*)hCard); + + if (!pCard) + return NULL; + + return pCard; +} + +static SCARDCONTEXT PCSC_GetCardContextFromHandle(SCARDHANDLE hCard) +{ + PCSC_SCARDHANDLE* pCard = NULL; + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return 0; + + return pCard->hSharedContext; +} + +static BOOL PCSC_WaitForCardAccess(SCARDCONTEXT hContext, SCARDHANDLE hCard, BOOL shared) +{ + BOOL status = TRUE; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_SCARDCONTEXT* pContext = NULL; + + if (!hCard) + { + /* SCardConnect */ + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + return FALSE; + + if (!pContext->owner) + return TRUE; + + /* wait for card ownership */ + return TRUE; + } + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return FALSE; + + shared = pCard->shared; + hContext = pCard->hSharedContext; + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + return FALSE; + + if (!pContext->owner) + { + /* card is not owned */ + if (!shared) + pContext->owner = hCard; + + return TRUE; + } + + if (pContext->owner == hCard) + { + /* already card owner */ + } + else + { + /* wait for card ownership */ + } + + return status; +} + +static BOOL PCSC_ReleaseCardAccess(SCARDCONTEXT hContext, SCARDHANDLE hCard) +{ + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_SCARDCONTEXT* pContext = NULL; + + if (!hCard) + { + /* release current owner */ + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + return FALSE; + + hCard = pContext->owner; + + if (!hCard) + return TRUE; + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return FALSE; + + /* release card ownership */ + pContext->owner = 0; + return TRUE; + } + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return FALSE; + + hContext = pCard->hSharedContext; + pContext = PCSC_GetCardContextData(hContext); + + if (!pContext) + return FALSE; + + if (pContext->owner == hCard) + { + /* release card ownership */ + pContext->owner = 0; + } + + return TRUE; +} + +static PCSC_SCARDHANDLE* PCSC_ConnectCardHandle(SCARDCONTEXT hSharedContext, SCARDHANDLE hCard) +{ + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_SCARDCONTEXT* pContext = NULL; + pContext = PCSC_GetCardContextData(hSharedContext); + + if (!pContext) + { + WLog_ERR(TAG, "PCSC_ConnectCardHandle: null pContext!"); + return NULL; + } + + pCard = (PCSC_SCARDHANDLE*)calloc(1, sizeof(PCSC_SCARDHANDLE)); + + if (!pCard) + return NULL; + + pCard->hSharedContext = hSharedContext; + + if (!g_CardHandles) + { + g_CardHandles = ListDictionary_New(TRUE); + + if (!g_CardHandles) + goto error; + } + + if (!ListDictionary_Add(g_CardHandles, (void*)hCard, (void*)pCard)) + goto error; + + pContext->dwCardHandleCount++; + return pCard; +error: + free(pCard); + return NULL; +} + +static void PCSC_DisconnectCardHandle(SCARDHANDLE hCard) +{ + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_SCARDCONTEXT* pContext = NULL; + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return; + + pContext = PCSC_GetCardContextData(pCard->hSharedContext); + free(pCard); + + if (!g_CardHandles) + return; + + ListDictionary_Remove(g_CardHandles, (void*)hCard); + + if (!pContext) + { + WLog_ERR(TAG, "PCSC_DisconnectCardHandle: null pContext!"); + return; + } + + pContext->dwCardHandleCount--; +} + +static BOOL PCSC_AddMemoryBlock(SCARDCONTEXT hContext, void* pvMem) +{ + if (!g_MemoryBlocks) + { + g_MemoryBlocks = ListDictionary_New(TRUE); + + if (!g_MemoryBlocks) + return FALSE; + } + + return ListDictionary_Add(g_MemoryBlocks, pvMem, (void*)hContext); +} + +static void* PCSC_RemoveMemoryBlock(SCARDCONTEXT hContext, void* pvMem) +{ + WINPR_UNUSED(hContext); + + if (!g_MemoryBlocks) + return NULL; + + return ListDictionary_Take(g_MemoryBlocks, pvMem); +} + +/** + * Standard Windows Smart Card API (PCSC) + */ + +static LONG WINAPI PCSC_SCardEstablishContext_Internal(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, + LPSCARDCONTEXT phContext) +{ + WINPR_UNUSED(dwScope); /* SCARD_SCOPE_SYSTEM is the only scope supported by pcsc-lite */ + PCSC_LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardEstablishContext) + return PCSC_SCard_LogError("g_PCSC.pfnSCardEstablishContext"); + + status = + g_PCSC.pfnSCardEstablishContext(SCARD_SCOPE_SYSTEM, pvReserved1, pvReserved2, phContext); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardEstablishContext(DWORD dwScope, LPCVOID pvReserved1, + LPCVOID pvReserved2, LPSCARDCONTEXT phContext) +{ + LONG status = 0; + + status = PCSC_SCardEstablishContext_Internal(dwScope, pvReserved1, pvReserved2, phContext); + + if (status == SCARD_S_SUCCESS) + PCSC_EstablishCardContext(*phContext); + + return status; +} + +static LONG WINAPI PCSC_SCardReleaseContext_Internal(SCARDCONTEXT hContext) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardReleaseContext) + return PCSC_SCard_LogError("g_PCSC.pfnSCardReleaseContext"); + + if (!hContext) + { + WLog_ERR(TAG, "SCardReleaseContext: null hContext"); + return PCSC_MapErrorCodeToWinSCard(status); + } + + status = g_PCSC.pfnSCardReleaseContext(hContext); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardReleaseContext(SCARDCONTEXT hContext) +{ + LONG status = SCARD_S_SUCCESS; + + status = PCSC_SCardReleaseContext_Internal(hContext); + + if (status != SCARD_S_SUCCESS) + PCSC_ReleaseCardContext(hContext); + + return status; +} + +static LONG WINAPI PCSC_SCardIsValidContext(SCARDCONTEXT hContext) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardIsValidContext) + return PCSC_SCard_LogError("g_PCSC.pfnSCardIsValidContext"); + + status = g_PCSC.pfnSCardIsValidContext(hContext); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardListReaderGroups_Internal(SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + BOOL pcchGroupsAlloc = FALSE; + PCSC_DWORD pcsc_cchGroups = 0; + + if (!pcchGroups) + return SCARD_E_INVALID_PARAMETER; + + if (!g_PCSC.pfnSCardListReaderGroups) + return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaderGroups"); + + if (*pcchGroups == SCARD_AUTOALLOCATE) + pcchGroupsAlloc = TRUE; + + pcsc_cchGroups = pcchGroupsAlloc ? PCSC_SCARD_AUTOALLOCATE : (PCSC_DWORD)*pcchGroups; + + if (pcchGroupsAlloc && !g_SCardAutoAllocate) + { + pcsc_cchGroups = 0; + status = g_PCSC.pfnSCardListReaderGroups(hContext, NULL, &pcsc_cchGroups); + + if (status == SCARD_S_SUCCESS) + { + LPSTR tmp = calloc(1, pcsc_cchGroups); + + if (!tmp) + return SCARD_E_NO_MEMORY; + + status = g_PCSC.pfnSCardListReaderGroups(hContext, tmp, &pcsc_cchGroups); + + if (status != SCARD_S_SUCCESS) + { + free(tmp); + tmp = NULL; + } + else + PCSC_AddMemoryBlock(hContext, tmp); + + *(LPSTR*)mszGroups = tmp; + } + } + else + { + status = g_PCSC.pfnSCardListReaderGroups(hContext, mszGroups, &pcsc_cchGroups); + } + + *pcchGroups = (DWORD)pcsc_cchGroups; + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardListReaderGroupsA(SCARDCONTEXT hContext, LPSTR mszGroups, + LPDWORD pcchGroups) +{ + LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardListReaderGroups) + return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaderGroups"); + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + status = PCSC_SCardListReaderGroups_Internal(hContext, mszGroups, pcchGroups); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + return status; +} + +static LONG WINAPI PCSC_SCardListReaderGroupsW(SCARDCONTEXT hContext, LPWSTR mszGroups, + LPDWORD pcchGroups) +{ + LPSTR mszGroupsA = NULL; + LPSTR* pMszGroupsA = &mszGroupsA; + LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardListReaderGroups) + return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaderGroups"); + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + status = PCSC_SCardListReaderGroups_Internal(hContext, (LPSTR)&mszGroupsA, pcchGroups); + + if (status == SCARD_S_SUCCESS) + { + size_t size = 0; + WCHAR* str = ConvertMszUtf8NToWCharAlloc(*pMszGroupsA, *pcchGroups, &size); + if (!str) + return SCARD_E_NO_MEMORY; + *(WCHAR**)mszGroups = str; + *pcchGroups = (DWORD)size; + PCSC_AddMemoryBlock(hContext, str); + PCSC_SCardFreeMemory_Internal(hContext, *pMszGroupsA); + } + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + return status; +} + +static LONG WINAPI PCSC_SCardListReaders_Internal(SCARDCONTEXT hContext, LPCSTR mszGroups, + LPSTR mszReaders, LPDWORD pcchReaders) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + BOOL pcchReadersAlloc = FALSE; + PCSC_DWORD pcsc_cchReaders = 0; + if (!pcchReaders) + return SCARD_E_INVALID_PARAMETER; + + if (!g_PCSC.pfnSCardListReaders) + return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaders"); + + mszGroups = NULL; /* mszGroups is not supported by pcsc-lite */ + + if (*pcchReaders == SCARD_AUTOALLOCATE) + pcchReadersAlloc = TRUE; + + pcsc_cchReaders = pcchReadersAlloc ? PCSC_SCARD_AUTOALLOCATE : (PCSC_DWORD)*pcchReaders; + + if (pcchReadersAlloc && !g_SCardAutoAllocate) + { + pcsc_cchReaders = 0; + status = g_PCSC.pfnSCardListReaders(hContext, mszGroups, NULL, &pcsc_cchReaders); + + if (status == SCARD_S_SUCCESS) + { + char* tmp = calloc(1, pcsc_cchReaders); + + if (!tmp) + return SCARD_E_NO_MEMORY; + + status = g_PCSC.pfnSCardListReaders(hContext, mszGroups, tmp, &pcsc_cchReaders); + + if (status != SCARD_S_SUCCESS) + { + free(tmp); + tmp = NULL; + } + else + PCSC_AddMemoryBlock(hContext, tmp); + + *(char**)mszReaders = tmp; + } + } + else + { + status = g_PCSC.pfnSCardListReaders(hContext, mszGroups, mszReaders, &pcsc_cchReaders); + } + + *pcchReaders = (DWORD)pcsc_cchReaders; + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardListReadersA(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, + LPDWORD pcchReaders) +{ + BOOL nullCardContext = FALSE; + LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardListReaders) + return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaders"); + + if (!hContext) + { + status = PCSC_SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); + + if (status != SCARD_S_SUCCESS) + return status; + + nullCardContext = TRUE; + } + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + status = PCSC_SCardListReaders_Internal(hContext, mszGroups, mszReaders, pcchReaders); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + if (nullCardContext) + { + status = PCSC_SCardReleaseContext(hContext); + } + + return status; +} + +static LONG WINAPI PCSC_SCardListReadersW(SCARDCONTEXT hContext, LPCWSTR mszGroups, + LPWSTR mszReaders, LPDWORD pcchReaders) +{ + LPSTR mszGroupsA = NULL; + LPSTR mszReadersA = NULL; + LONG status = SCARD_S_SUCCESS; + BOOL nullCardContext = FALSE; + + if (!g_PCSC.pfnSCardListReaders) + return PCSC_SCard_LogError("g_PCSC.pfnSCardListReaders"); + + if (!hContext) + { + status = PCSC_SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); + + if (status != SCARD_S_SUCCESS) + return status; + + nullCardContext = TRUE; + } + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + mszGroups = NULL; /* mszGroups is not supported by pcsc-lite */ + + if (mszGroups) + { + mszGroupsA = ConvertWCharToUtf8Alloc(mszGroups, NULL); + if (!mszGroups) + return SCARD_E_NO_MEMORY; + } + + status = + PCSC_SCardListReaders_Internal(hContext, mszGroupsA, (LPSTR*)&mszReadersA, pcchReaders); + if (status == SCARD_S_SUCCESS) + { + size_t size = 0; + WCHAR* str = ConvertMszUtf8NToWCharAlloc(mszReadersA, *pcchReaders, &size); + PCSC_SCardFreeMemory_Internal(hContext, mszReadersA); + if (!str || (size > UINT32_MAX)) + { + free(mszGroupsA); + return SCARD_E_NO_MEMORY; + } + *(LPWSTR*)mszReaders = str; + *pcchReaders = (DWORD)size; + PCSC_AddMemoryBlock(hContext, str); + } + + free(mszGroupsA); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + if (nullCardContext) + { + status = PCSC_SCardReleaseContext(hContext); + } + + return status; +} + +typedef struct +{ + BYTE atr[64]; + size_t atrLen; + const char* cardName; +} PcscKnownAtr; + +static PcscKnownAtr knownAtrs[] = { + /* Yubico YubiKey 5 NFC (PKI) */ + { { 0x3B, 0xFD, 0x13, 0x00, 0x00, 0x81, 0x31, 0xFE, 0x15, 0x80, 0x73, 0xC0, + 0x21, 0xC0, 0x57, 0x59, 0x75, 0x62, 0x69, 0x4B, 0x65, 0x79, 0x40 }, + 23, + "NIST SP 800-73 [PIV]" }, + /* PIVKey C910 PKI Smart Card (eID) */ + { { 0x3B, 0xFC, 0x18, 0x00, 0x00, 0x81, 0x31, 0x80, 0x45, 0x90, 0x67, + 0x46, 0x4A, 0x00, 0x64, 0x16, 0x06, 0xF2, 0x72, 0x7E, 0x00, 0xE0 }, + 22, + "PIVKey Feitian (E0)" } +}; + +#ifndef ARRAY_LENGTH +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) +#endif + +static const char* findCardByAtr(LPCBYTE pbAtr) +{ + for (size_t i = 0; i < ARRAY_LENGTH(knownAtrs); i++) + { + if (memcmp(knownAtrs[i].atr, pbAtr, knownAtrs[i].atrLen) == 0) + return knownAtrs[i].cardName; + } + + return NULL; +} + +static LONG WINAPI PCSC_SCardListCardsA(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + CHAR* mszCards, LPDWORD pcchCards) +{ + const char* cardName = NULL; + DWORD outputLen = 1; + CHAR* output = NULL; + BOOL autoAllocate = 0; + + if (!pbAtr || rgquidInterfaces || cguidInterfaceCount) + return SCARD_E_UNSUPPORTED_FEATURE; + + if (!pcchCards) + return SCARD_E_INVALID_PARAMETER; + + autoAllocate = (*pcchCards == SCARD_AUTOALLOCATE); + + cardName = findCardByAtr(pbAtr); + if (cardName) + outputLen += strlen(cardName) + 1; + + *pcchCards = outputLen; + if (autoAllocate) + { + output = malloc(outputLen); + if (!output) + return SCARD_E_NO_MEMORY; + + *((LPSTR*)mszCards) = output; + } + else + { + if (!mszCards) + return SCARD_S_SUCCESS; + + if (*pcchCards < outputLen) + return SCARD_E_INSUFFICIENT_BUFFER; + + output = mszCards; + } + + if (cardName) + { + size_t toCopy = strlen(cardName) + 1; + memcpy(output, cardName, toCopy); + output += toCopy; + } + + *output = '\0'; + + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardListCardsW(SCARDCONTEXT hContext, LPCBYTE pbAtr, + LPCGUID rgquidInterfaces, DWORD cguidInterfaceCount, + WCHAR* mszCards, LPDWORD pcchCards) +{ + const char* cardName = NULL; + DWORD outputLen = 1; + WCHAR* output = NULL; + BOOL autoAllocate = 0; + + if (!pbAtr || rgquidInterfaces || cguidInterfaceCount) + return SCARD_E_UNSUPPORTED_FEATURE; + + if (!pcchCards) + return SCARD_E_INVALID_PARAMETER; + + autoAllocate = (*pcchCards == SCARD_AUTOALLOCATE); + + cardName = findCardByAtr(pbAtr); + if (cardName) + outputLen += strlen(cardName) + 1; + + *pcchCards = outputLen; + if (autoAllocate) + { + output = malloc(outputLen * 2); + if (!output) + return SCARD_E_NO_MEMORY; + + *((LPWSTR*)mszCards) = output; + } + else + { + if (!mszCards) + return SCARD_S_SUCCESS; + + if (*pcchCards < outputLen) + return SCARD_E_INSUFFICIENT_BUFFER; + + output = mszCards; + } + + if (cardName) + { + size_t toCopy = strlen(cardName) + 1; + if (ConvertUtf8ToWChar(cardName, output, toCopy) < 0) + return SCARD_F_INTERNAL_ERROR; + output += toCopy; + } + + *output = 0; + + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardListInterfacesA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidInterfaces); + WINPR_UNUSED(pcguidInterfaces); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardListInterfacesW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidInterfaces, LPDWORD pcguidInterfaces) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidInterfaces); + WINPR_UNUSED(pcguidInterfaces); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetProviderIdA(SCARDCONTEXT hContext, LPCSTR szCard, + LPGUID pguidProviderId) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidProviderId); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetProviderIdW(SCARDCONTEXT hContext, LPCWSTR szCard, + LPGUID pguidProviderId) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCard); + WINPR_UNUSED(pguidProviderId); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, CHAR* szProvider, + LPDWORD pcchProvider) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + WINPR_UNUSED(pcchProvider); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, WCHAR* szProvider, + LPDWORD pcchProvider) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + WINPR_UNUSED(pcchProvider); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardIntroduceReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardIntroduceReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardForgetReaderGroupA(SCARDCONTEXT hContext, LPCSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardForgetReaderGroupW(SCARDCONTEXT hContext, LPCWSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardIntroduceReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szDeviceName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szDeviceName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardIntroduceReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szDeviceName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szDeviceName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardForgetReaderA(SCARDCONTEXT hContext, LPCSTR szReaderName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardForgetReaderW(SCARDCONTEXT hContext, LPCWSTR szReaderName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardAddReaderToGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardAddReaderToGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardRemoveReaderFromGroupA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPCSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardRemoveReaderFromGroupW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPCWSTR szGroupName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szGroupName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardIntroduceCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + WINPR_UNUSED(pguidPrimaryProvider); + WINPR_UNUSED(rgguidInterfaces); + WINPR_UNUSED(dwInterfaceCount); + WINPR_UNUSED(pbAtr); + WINPR_UNUSED(pbAtrMask); + WINPR_UNUSED(cbAtrLen); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardIntroduceCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName, + LPCGUID pguidPrimaryProvider, + LPCGUID rgguidInterfaces, DWORD dwInterfaceCount, + LPCBYTE pbAtr, LPCBYTE pbAtrMask, DWORD cbAtrLen) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + WINPR_UNUSED(pguidPrimaryProvider); + WINPR_UNUSED(rgguidInterfaces); + WINPR_UNUSED(dwInterfaceCount); + WINPR_UNUSED(pbAtr); + WINPR_UNUSED(pbAtrMask); + WINPR_UNUSED(cbAtrLen); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardSetCardTypeProviderNameA(SCARDCONTEXT hContext, LPCSTR szCardName, + DWORD dwProviderId, LPCSTR szProvider) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardSetCardTypeProviderNameW(SCARDCONTEXT hContext, LPCWSTR szCardName, + DWORD dwProviderId, LPCWSTR szProvider) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + WINPR_UNUSED(dwProviderId); + WINPR_UNUSED(szProvider); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardForgetCardTypeA(SCARDCONTEXT hContext, LPCSTR szCardName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardForgetCardTypeW(SCARDCONTEXT hContext, LPCWSTR szCardName) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szCardName); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardFreeMemory_Internal(SCARDCONTEXT hContext, LPVOID pvMem) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + + if (PCSC_RemoveMemoryBlock(hContext, pvMem)) + { + free((void*)pvMem); + status = SCARD_S_SUCCESS; + } + else + { + if (g_PCSC.pfnSCardFreeMemory) + { + status = g_PCSC.pfnSCardFreeMemory(hContext, pvMem); + } + } + + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardFreeMemory(SCARDCONTEXT hContext, LPVOID pvMem) +{ + LONG status = SCARD_S_SUCCESS; + + if (hContext) + { + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + } + + status = PCSC_SCardFreeMemory_Internal(hContext, pvMem); + + if (hContext) + { + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + } + + return status; +} + +static HANDLE WINAPI PCSC_SCardAccessStartedEvent(void) +{ + LONG status = 0; + SCARDCONTEXT hContext = 0; + + status = PCSC_SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); + + if (status != SCARD_S_SUCCESS) + return NULL; + + status = PCSC_SCardReleaseContext(hContext); + + if (status != SCARD_S_SUCCESS) + return NULL; + + if (!g_StartedEvent) + { + if (!(g_StartedEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + return NULL; + + if (!SetEvent(g_StartedEvent)) + { + CloseHandle(g_StartedEvent); + return NULL; + } + } + + g_StartedEventRefCount++; + return g_StartedEvent; +} + +static void WINAPI PCSC_SCardReleaseStartedEvent(void) +{ + g_StartedEventRefCount--; + + if (g_StartedEventRefCount == 0) + { + if (g_StartedEvent) + { + CloseHandle(g_StartedEvent); + g_StartedEvent = NULL; + } + } +} + +static LONG WINAPI PCSC_SCardLocateCardsA(SCARDCONTEXT hContext, LPCSTR mszCards, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(mszCards); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardLocateCardsW(SCARDCONTEXT hContext, LPCWSTR mszCards, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(mszCards); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardLocateCardsByATRA(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(rgAtrMasks); + WINPR_UNUSED(cAtrs); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardLocateCardsByATRW(SCARDCONTEXT hContext, LPSCARD_ATRMASK rgAtrMasks, + DWORD cAtrs, LPSCARD_READERSTATEW rgReaderStates, + DWORD cReaders) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(rgAtrMasks); + WINPR_UNUSED(cAtrs); + WINPR_UNUSED(rgReaderStates); + WINPR_UNUSED(cReaders); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetStatusChange_Internal(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, + DWORD cReaders) +{ + INT64* map = NULL; + PCSC_DWORD cMappedReaders = 0; + PCSC_SCARD_READERSTATE* states = NULL; + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_DWORD pcsc_dwTimeout = (PCSC_DWORD)dwTimeout; + PCSC_DWORD pcsc_cReaders = (PCSC_DWORD)cReaders; + + if (!g_PCSC.pfnSCardGetStatusChange) + return PCSC_SCard_LogError("g_PCSC.pfnSCardGetStatusChange"); + + if (!cReaders) + return SCARD_S_SUCCESS; + + /* pcsc-lite interprets value 0 as INFINITE, work around the problem by using value 1 */ + pcsc_dwTimeout = pcsc_dwTimeout ? pcsc_dwTimeout : 1; + /** + * Apple's SmartCard Services (not vanilla pcsc-lite) appears to have trouble with the + * "\\\\?PnP?\\Notification" reader name. I am always getting EXC_BAD_ACCESS with it. + * + * The SmartCard Services tarballs can be found here: + * http://opensource.apple.com/tarballs/SmartCardServices/ + * + * The "\\\\?PnP?\\Notification" string cannot be found anywhere in the sources, + * while this string is present in the vanilla pcsc-lite sources. + * + * To work around this apparent lack of "\\\\?PnP?\\Notification" support, + * we have to filter rgReaderStates to exclude the special PnP reader name. + */ + map = (INT64*)calloc(pcsc_cReaders, sizeof(INT64)); + + if (!map) + return SCARD_E_NO_MEMORY; + + states = (PCSC_SCARD_READERSTATE*)calloc(pcsc_cReaders, sizeof(PCSC_SCARD_READERSTATE)); + + if (!states) + { + free(map); + return SCARD_E_NO_MEMORY; + } + + PCSC_DWORD j = 0; + for (PCSC_DWORD i = 0; i < pcsc_cReaders; i++) + { + if (!g_PnP_Notification) + { + LPSCARD_READERSTATEA reader = &rgReaderStates[i]; + if (!reader->szReader) + continue; + if (0 == _stricmp(reader->szReader, SMARTCARD_PNP_NOTIFICATION_A)) + { + map[i] = -1; /* unmapped */ + continue; + } + } + + map[i] = (INT64)j; + states[j].szReader = rgReaderStates[i].szReader; + states[j].dwCurrentState = rgReaderStates[i].dwCurrentState; + states[j].pvUserData = rgReaderStates[i].pvUserData; + states[j].dwEventState = rgReaderStates[i].dwEventState; + states[j].cbAtr = rgReaderStates[i].cbAtr; + CopyMemory(&(states[j].rgbAtr), &(rgReaderStates[i].rgbAtr), PCSC_MAX_ATR_SIZE); + j++; + } + + cMappedReaders = j; + + if (cMappedReaders > 0) + { + status = g_PCSC.pfnSCardGetStatusChange(hContext, pcsc_dwTimeout, states, cMappedReaders); + } + else + { + status = SCARD_S_SUCCESS; + } + + for (PCSC_DWORD i = 0; i < pcsc_cReaders; i++) + { + if (map[i] < 0) + continue; /* unmapped */ + + PCSC_DWORD j = (PCSC_DWORD)map[i]; + rgReaderStates[i].dwCurrentState = (DWORD)states[j].dwCurrentState; + rgReaderStates[i].cbAtr = (DWORD)states[j].cbAtr; + CopyMemory(&(rgReaderStates[i].rgbAtr), &(states[j].rgbAtr), PCSC_MAX_ATR_SIZE); + rgReaderStates[i].dwEventState = (DWORD)states[j].dwEventState; + } + + free(map); + free(states); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardGetStatusChangeA(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEA rgReaderStates, DWORD cReaders) +{ + LONG status = SCARD_S_SUCCESS; + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + status = PCSC_SCardGetStatusChange_Internal(hContext, dwTimeout, rgReaderStates, cReaders); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + return status; +} + +static LONG WINAPI PCSC_SCardGetStatusChangeW(SCARDCONTEXT hContext, DWORD dwTimeout, + LPSCARD_READERSTATEW rgReaderStates, DWORD cReaders) +{ + LPSCARD_READERSTATEA states = NULL; + LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardGetStatusChange) + return PCSC_SCard_LogError("g_PCSC.pfnSCardGetStatusChange"); + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + states = (LPSCARD_READERSTATEA)calloc(cReaders, sizeof(SCARD_READERSTATEA)); + + if (!states) + { + PCSC_UnlockCardContext(hContext); + return SCARD_E_NO_MEMORY; + } + + for (DWORD index = 0; index < cReaders; index++) + { + const LPSCARD_READERSTATEW curReader = &rgReaderStates[index]; + LPSCARD_READERSTATEA cur = &states[index]; + + cur->szReader = ConvertWCharToUtf8Alloc(curReader->szReader, NULL); + cur->pvUserData = curReader->pvUserData; + cur->dwCurrentState = curReader->dwCurrentState; + cur->dwEventState = curReader->dwEventState; + cur->cbAtr = curReader->cbAtr; + CopyMemory(&(cur->rgbAtr), &(curReader->rgbAtr), ARRAYSIZE(cur->rgbAtr)); + } + + status = PCSC_SCardGetStatusChange_Internal(hContext, dwTimeout, states, cReaders); + + for (DWORD index = 0; index < cReaders; index++) + { + free((void*)states[index].szReader); + rgReaderStates[index].pvUserData = states[index].pvUserData; + rgReaderStates[index].dwCurrentState = states[index].dwCurrentState; + rgReaderStates[index].dwEventState = states[index].dwEventState; + rgReaderStates[index].cbAtr = states[index].cbAtr; + CopyMemory(&(rgReaderStates[index].rgbAtr), &(states[index].rgbAtr), 36); + } + + free(states); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + return status; +} + +static LONG WINAPI PCSC_SCardCancel(SCARDCONTEXT hContext) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + + if (!g_PCSC.pfnSCardCancel) + return PCSC_SCard_LogError("g_PCSC.pfnSCardCancel"); + + status = g_PCSC.pfnSCardCancel(hContext); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardConnect_Internal(SCARDCONTEXT hContext, LPCSTR szReader, + DWORD dwShareMode, DWORD dwPreferredProtocols, + LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) +{ + BOOL shared = 0; + const char* szReaderPCSC = NULL; + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_DWORD pcsc_dwShareMode = (PCSC_DWORD)dwShareMode; + PCSC_DWORD pcsc_dwPreferredProtocols = 0; + PCSC_DWORD pcsc_dwActiveProtocol = 0; + + if (!g_PCSC.pfnSCardConnect) + return PCSC_SCard_LogError("g_PCSC.pfnSCardConnect"); + + shared = (dwShareMode == SCARD_SHARE_DIRECT) ? TRUE : FALSE; + PCSC_WaitForCardAccess(hContext, 0, shared); + szReaderPCSC = szReader; + + /** + * As stated here : + * https://pcsclite.alioth.debian.org/api/group__API.html#ga4e515829752e0a8dbc4d630696a8d6a5 + * SCARD_PROTOCOL_UNDEFINED is valid for dwPreferredProtocols (only) if dwShareMode == + * SCARD_SHARE_DIRECT and allows to send control commands to the reader (with SCardControl()) + * even if a card is not present in the reader + */ + if (pcsc_dwShareMode == SCARD_SHARE_DIRECT && dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) + pcsc_dwPreferredProtocols = SCARD_PROTOCOL_UNDEFINED; + else + pcsc_dwPreferredProtocols = + (PCSC_DWORD)PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols); + + status = g_PCSC.pfnSCardConnect(hContext, szReaderPCSC, pcsc_dwShareMode, + pcsc_dwPreferredProtocols, phCard, &pcsc_dwActiveProtocol); + + if (status == SCARD_S_SUCCESS) + { + pCard = PCSC_ConnectCardHandle(hContext, *phCard); + *pdwActiveProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwActiveProtocol); + pCard->shared = shared; + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ListDictionary_Add takes ownership of pCard + PCSC_WaitForCardAccess(hContext, pCard->hSharedContext, shared); + } + + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardConnectA(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol) +{ + LONG status = SCARD_S_SUCCESS; + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + status = PCSC_SCardConnect_Internal(hContext, szReader, dwShareMode, dwPreferredProtocols, + phCard, pdwActiveProtocol); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + return status; +} + +static LONG WINAPI PCSC_SCardConnectW(SCARDCONTEXT hContext, LPCWSTR szReader, DWORD dwShareMode, + DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, + LPDWORD pdwActiveProtocol) +{ + LPSTR szReaderA = NULL; + LONG status = SCARD_S_SUCCESS; + + if (!PCSC_LockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + if (szReader) + { + szReaderA = ConvertWCharToUtf8Alloc(szReader, NULL); + if (!szReaderA) + return SCARD_E_INSUFFICIENT_BUFFER; + } + + status = PCSC_SCardConnect_Internal(hContext, szReaderA, dwShareMode, dwPreferredProtocols, + phCard, pdwActiveProtocol); + free(szReaderA); + + if (!PCSC_UnlockCardContext(hContext)) + return SCARD_E_INVALID_HANDLE; + + return status; +} + +static LONG WINAPI PCSC_SCardReconnect(SCARDHANDLE hCard, DWORD dwShareMode, + DWORD dwPreferredProtocols, DWORD dwInitialization, + LPDWORD pdwActiveProtocol) +{ + BOOL shared = 0; + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_DWORD pcsc_dwShareMode = (PCSC_DWORD)dwShareMode; + PCSC_DWORD pcsc_dwPreferredProtocols = 0; + PCSC_DWORD pcsc_dwInitialization = (PCSC_DWORD)dwInitialization; + PCSC_DWORD pcsc_dwActiveProtocol = 0; + + if (!g_PCSC.pfnSCardReconnect) + return PCSC_SCard_LogError("g_PCSC.pfnSCardReconnect"); + + shared = (dwShareMode == SCARD_SHARE_DIRECT) ? TRUE : FALSE; + PCSC_WaitForCardAccess(0, hCard, shared); + pcsc_dwPreferredProtocols = (PCSC_DWORD)PCSC_ConvertProtocolsFromWinSCard(dwPreferredProtocols); + status = g_PCSC.pfnSCardReconnect(hCard, pcsc_dwShareMode, pcsc_dwPreferredProtocols, + pcsc_dwInitialization, &pcsc_dwActiveProtocol); + + *pdwActiveProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwActiveProtocol); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_DWORD pcsc_dwDisposition = (PCSC_DWORD)dwDisposition; + + if (!g_PCSC.pfnSCardDisconnect) + return PCSC_SCard_LogError("g_PCSC.pfnSCardDisconnect"); + + status = g_PCSC.pfnSCardDisconnect(hCard, pcsc_dwDisposition); + + if (status == SCARD_S_SUCCESS) + { + PCSC_DisconnectCardHandle(hCard); + } + + PCSC_ReleaseCardAccess(0, hCard); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardBeginTransaction(SCARDHANDLE hCard) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_SCARDCONTEXT* pContext = NULL; + + if (!g_PCSC.pfnSCardBeginTransaction) + return PCSC_SCard_LogError("g_PCSC.pfnSCardBeginTransaction"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_HANDLE; + + pContext = PCSC_GetCardContextData(pCard->hSharedContext); + + if (!pContext) + return SCARD_E_INVALID_HANDLE; + + if (pContext->isTransactionLocked) + return SCARD_S_SUCCESS; /* disable nested transactions */ + + status = g_PCSC.pfnSCardBeginTransaction(hCard); + + pContext->isTransactionLocked = TRUE; + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_SCARDCONTEXT* pContext = NULL; + PCSC_DWORD pcsc_dwDisposition = (PCSC_DWORD)dwDisposition; + + if (!g_PCSC.pfnSCardEndTransaction) + return PCSC_SCard_LogError("g_PCSC.pfnSCardEndTransaction"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_HANDLE; + + pContext = PCSC_GetCardContextData(pCard->hSharedContext); + + if (!pContext) + return SCARD_E_INVALID_HANDLE; + + PCSC_ReleaseCardAccess(0, hCard); + + if (!pContext->isTransactionLocked) + return SCARD_S_SUCCESS; /* disable nested transactions */ + + status = g_PCSC.pfnSCardEndTransaction(hCard, pcsc_dwDisposition); + + pContext->isTransactionLocked = FALSE; + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardCancelTransaction(SCARDHANDLE hCard) +{ + WINPR_UNUSED(hCard); + return SCARD_S_SUCCESS; +} + +/* + * PCSC returns a string but Windows SCardStatus requires the return to be a multi string. + * Therefore extra length checks and additional buffer allocation is required + */ +static LONG WINAPI PCSC_SCardStatus_Internal(SCARDHANDLE hCard, LPSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, + LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen, + BOOL unicode) +{ + PCSC_SCARDHANDLE* pCard = NULL; + SCARDCONTEXT hContext = 0; + PCSC_LONG status = 0; + PCSC_DWORD pcsc_cchReaderLen = 0; + PCSC_DWORD pcsc_cbAtrLen = 0; + PCSC_DWORD pcsc_dwState = 0; + PCSC_DWORD pcsc_dwProtocol = 0; + BOOL allocateReader = FALSE; + BOOL allocateAtr = FALSE; + LPSTR readerNames = mszReaderNames; + LPBYTE atr = pbAtr; + LPSTR tReader = NULL; + LPBYTE tATR = NULL; + + if (!g_PCSC.pfnSCardStatus) + return PCSC_SCard_LogError("g_PCSC.pfnSCardStatus"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + hContext = PCSC_GetCardContextFromHandle(hCard); + + if (!hContext) + return SCARD_E_INVALID_VALUE; + + status = + g_PCSC.pfnSCardStatus(hCard, NULL, &pcsc_cchReaderLen, NULL, NULL, NULL, &pcsc_cbAtrLen); + + if (status != STATUS_SUCCESS) + return PCSC_MapErrorCodeToWinSCard(status); + + pcsc_cchReaderLen++; + + if (unicode) + pcsc_cchReaderLen *= 2; + + if (pcchReaderLen) + { + if (*pcchReaderLen == SCARD_AUTOALLOCATE) + allocateReader = TRUE; + else if (mszReaderNames && (*pcchReaderLen < pcsc_cchReaderLen)) + return SCARD_E_INSUFFICIENT_BUFFER; + else + pcsc_cchReaderLen = *pcchReaderLen; + } + + if (pcbAtrLen) + { + if (*pcbAtrLen == SCARD_AUTOALLOCATE) + allocateAtr = TRUE; + else if (pbAtr && (*pcbAtrLen < pcsc_cbAtrLen)) + return SCARD_E_INSUFFICIENT_BUFFER; + else + pcsc_cbAtrLen = *pcbAtrLen; + } + + if (allocateReader && pcsc_cchReaderLen > 0 && mszReaderNames) + { +#ifdef __MACOSX__ + + /** + * Workaround for SCardStatus Bug in MAC OS X Yosemite + */ + if (OSXVersion == 0x10100000) + pcsc_cchReaderLen++; + +#endif + tReader = calloc(sizeof(CHAR), pcsc_cchReaderLen + 1); + + if (!tReader) + { + status = ERROR_NOT_ENOUGH_MEMORY; + goto out_fail; + } + + readerNames = tReader; + } + + if (allocateAtr && pcsc_cbAtrLen > 0 && pbAtr) + { + tATR = calloc(1, pcsc_cbAtrLen); + + if (!tATR) + { + status = ERROR_NOT_ENOUGH_MEMORY; + goto out_fail; + } + + atr = tATR; + } + + status = g_PCSC.pfnSCardStatus(hCard, readerNames, &pcsc_cchReaderLen, &pcsc_dwState, + &pcsc_dwProtocol, atr, &pcsc_cbAtrLen); + + if (status != STATUS_SUCCESS) + goto out_fail; + + if (tATR) + { + PCSC_AddMemoryBlock(hContext, tATR); + *(BYTE**)pbAtr = tATR; + } + + if (tReader) + { + if (unicode) + { + size_t size = 0; + WCHAR* tmp = ConvertMszUtf8NToWCharAlloc(tReader, pcsc_cchReaderLen + 1, &size); + + if (tmp == NULL) + { + status = ERROR_NOT_ENOUGH_MEMORY; + goto out_fail; + } + + free(tReader); + + PCSC_AddMemoryBlock(hContext, tmp); + *(WCHAR**)mszReaderNames = tmp; + } + else + { + tReader[pcsc_cchReaderLen - 1] = '\0'; + PCSC_AddMemoryBlock(hContext, tReader); + *(char**)mszReaderNames = tReader; + } + } + + pcsc_dwState &= 0xFFFF; + + if (pdwState) + *pdwState = PCSC_ConvertCardStateToWinSCard((DWORD)pcsc_dwState, status); + + if (pdwProtocol) + *pdwProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwProtocol); + + if (pcbAtrLen) + *pcbAtrLen = (DWORD)pcsc_cbAtrLen; + + if (pcchReaderLen) + { + WINPR_ASSERT(pcsc_cchReaderLen < UINT32_MAX); + *pcchReaderLen = (DWORD)pcsc_cchReaderLen + 1u; + } + + return (LONG)status; +out_fail: + free(tReader); + free(tATR); + return (LONG)status; +} + +static LONG WINAPI PCSC_SCardState(SCARDHANDLE hCard, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + DWORD cchReaderLen = 0; + SCARDCONTEXT hContext = 0; + LPSTR mszReaderNames = NULL; + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + DWORD pcsc_dwState = 0; + DWORD pcsc_dwProtocol = 0; + DWORD pcsc_cbAtrLen = 0; + + if (pcbAtrLen) + pcsc_cbAtrLen = (DWORD)*pcbAtrLen; + + if (!g_PCSC.pfnSCardStatus) + return PCSC_SCard_LogError("g_PCSC.pfnSCardStatus"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + hContext = PCSC_GetCardContextFromHandle(hCard); + + if (!hContext) + return SCARD_E_INVALID_VALUE; + + cchReaderLen = SCARD_AUTOALLOCATE; + status = PCSC_SCardStatus_Internal(hCard, (LPSTR)&mszReaderNames, &cchReaderLen, &pcsc_dwState, + &pcsc_dwProtocol, pbAtr, &pcsc_cbAtrLen, FALSE); + + if (mszReaderNames) + PCSC_SCardFreeMemory_Internal(hContext, mszReaderNames); + + *pdwState = (DWORD)pcsc_dwState; + *pdwProtocol = PCSC_ConvertProtocolsToWinSCard((DWORD)pcsc_dwProtocol); + if (pcbAtrLen) + *pcbAtrLen = (DWORD)pcsc_cbAtrLen; + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardStatusA(SCARDHANDLE hCard, LPSTR mszReaderNames, LPDWORD pcchReaderLen, + LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, + LPDWORD pcbAtrLen) +{ + + return PCSC_SCardStatus_Internal(hCard, mszReaderNames, pcchReaderLen, pdwState, pdwProtocol, + pbAtr, pcbAtrLen, FALSE); +} + +static LONG WINAPI PCSC_SCardStatusW(SCARDHANDLE hCard, LPWSTR mszReaderNames, + LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, + LPBYTE pbAtr, LPDWORD pcbAtrLen) +{ + + return PCSC_SCardStatus_Internal(hCard, (LPSTR)mszReaderNames, pcchReaderLen, pdwState, + pdwProtocol, pbAtr, pcbAtrLen, TRUE); +} + +static LONG WINAPI PCSC_SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, + LPCBYTE pbSendBuffer, DWORD cbSendLength, + LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, + LPDWORD pcbRecvLength) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_DWORD cbExtraBytes = 0; + BYTE* pbExtraBytes = NULL; + BYTE* pcsc_pbExtraBytes = NULL; + PCSC_DWORD pcsc_cbSendLength = (PCSC_DWORD)cbSendLength; + PCSC_DWORD pcsc_cbRecvLength = 0; + union + { + const PCSC_SCARD_IO_REQUEST* pcs; + PCSC_SCARD_IO_REQUEST* ps; + LPSCARD_IO_REQUEST lps; + LPCSCARD_IO_REQUEST lpcs; + BYTE* pb; + } sendPci, recvPci, inRecvPci, inSendPci; + + sendPci.ps = NULL; + recvPci.ps = NULL; + inRecvPci.lps = pioRecvPci; + inSendPci.lpcs = pioSendPci; + + if (!g_PCSC.pfnSCardTransmit) + return PCSC_SCard_LogError("g_PCSC.pfnSCardTransmit"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + + if (!pcbRecvLength) + return SCARD_E_INVALID_PARAMETER; + + if (*pcbRecvLength == SCARD_AUTOALLOCATE) + return SCARD_E_INVALID_PARAMETER; + + pcsc_cbRecvLength = (PCSC_DWORD)*pcbRecvLength; + + if (!inSendPci.lpcs) + { + PCSC_DWORD dwState = 0; + PCSC_DWORD cbAtrLen = 0; + PCSC_DWORD dwProtocol = 0; + PCSC_DWORD cchReaderLen = 0; + /** + * pcsc-lite cannot have a null pioSendPci parameter, unlike WinSCard. + * Query the current protocol and use default SCARD_IO_REQUEST for it. + */ + status = g_PCSC.pfnSCardStatus(hCard, NULL, &cchReaderLen, &dwState, &dwProtocol, NULL, + &cbAtrLen); + + if (status == SCARD_S_SUCCESS) + { + if (dwProtocol == SCARD_PROTOCOL_T0) + sendPci.pcs = PCSC_SCARD_PCI_T0; + else if (dwProtocol == SCARD_PROTOCOL_T1) + sendPci.pcs = PCSC_SCARD_PCI_T1; + else if (dwProtocol == PCSC_SCARD_PROTOCOL_RAW) + sendPci.pcs = PCSC_SCARD_PCI_RAW; + } + } + else + { + cbExtraBytes = inSendPci.lpcs->cbPciLength - sizeof(SCARD_IO_REQUEST); + sendPci.ps = (PCSC_SCARD_IO_REQUEST*)malloc(sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes); + + if (!sendPci.ps) + return SCARD_E_NO_MEMORY; + + sendPci.ps->dwProtocol = (PCSC_DWORD)inSendPci.lpcs->dwProtocol; + sendPci.ps->cbPciLength = sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes; + pbExtraBytes = &(inSendPci.pb)[sizeof(SCARD_IO_REQUEST)]; + pcsc_pbExtraBytes = &(sendPci.pb)[sizeof(PCSC_SCARD_IO_REQUEST)]; + CopyMemory(pcsc_pbExtraBytes, pbExtraBytes, cbExtraBytes); + } + + if (inRecvPci.lps) + { + cbExtraBytes = inRecvPci.lps->cbPciLength - sizeof(SCARD_IO_REQUEST); + recvPci.ps = (PCSC_SCARD_IO_REQUEST*)malloc(sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes); + + if (!recvPci.ps) + { + if (inSendPci.lpcs) + free(sendPci.ps); + + return SCARD_E_NO_MEMORY; + } + + recvPci.ps->dwProtocol = (PCSC_DWORD)inRecvPci.lps->dwProtocol; + recvPci.ps->cbPciLength = sizeof(PCSC_SCARD_IO_REQUEST) + cbExtraBytes; + pbExtraBytes = &(inRecvPci.pb)[sizeof(SCARD_IO_REQUEST)]; + pcsc_pbExtraBytes = &(recvPci.pb)[sizeof(PCSC_SCARD_IO_REQUEST)]; + CopyMemory(pcsc_pbExtraBytes, pbExtraBytes, cbExtraBytes); + } + + status = g_PCSC.pfnSCardTransmit(hCard, sendPci.ps, pbSendBuffer, pcsc_cbSendLength, recvPci.ps, + pbRecvBuffer, &pcsc_cbRecvLength); + + *pcbRecvLength = (DWORD)pcsc_cbRecvLength; + + if (inSendPci.lpcs) + free(sendPci.ps); /* pcsc_pioSendPci is dynamically allocated only when pioSendPci is + non null */ + + if (inRecvPci.lps) + { + cbExtraBytes = inRecvPci.lps->cbPciLength - sizeof(SCARD_IO_REQUEST); + pbExtraBytes = &(inRecvPci.pb)[sizeof(SCARD_IO_REQUEST)]; + pcsc_pbExtraBytes = &(recvPci.pb)[sizeof(PCSC_SCARD_IO_REQUEST)]; + CopyMemory(pbExtraBytes, pcsc_pbExtraBytes, cbExtraBytes); /* copy extra bytes */ + free(recvPci.ps); /* pcsc_pioRecvPci is dynamically allocated only when pioRecvPci is + non null */ + } + + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardGetTransmitCount(SCARDHANDLE hCard, LPDWORD pcTransmitCount) +{ + WINPR_UNUSED(pcTransmitCount); + PCSC_SCARDHANDLE* pCard = NULL; + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardControl(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, + DWORD cbInBufferSize, LPVOID lpOutBuffer, + DWORD cbOutBufferSize, LPDWORD lpBytesReturned) +{ + DWORD IoCtlFunction = 0; + DWORD IoCtlDeviceType = 0; + BOOL getFeatureRequest = FALSE; + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_DWORD pcsc_dwControlCode = 0; + PCSC_DWORD pcsc_cbInBufferSize = (PCSC_DWORD)cbInBufferSize; + PCSC_DWORD pcsc_cbOutBufferSize = (PCSC_DWORD)cbOutBufferSize; + PCSC_DWORD pcsc_BytesReturned = 0; + + if (!g_PCSC.pfnSCardControl) + return PCSC_SCard_LogError("g_PCSC.pfnSCardControl"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + /** + * PCSCv2 Part 10: + * http://www.pcscworkgroup.com/specifications/files/pcsc10_v2.02.09.pdf + * + * Smart Card Driver IOCTLs: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff548988/ + * + * Converting Windows Feature Request IOCTL code to the pcsc-lite control code: + * http://musclecard.996296.n3.nabble.com/Converting-Windows-Feature-Request-IOCTL-code-to-the-pcsc-lite-control-code-td4906.html + */ + IoCtlFunction = FUNCTION_FROM_CTL_CODE(dwControlCode); + IoCtlDeviceType = DEVICE_TYPE_FROM_CTL_CODE(dwControlCode); + + if (dwControlCode == IOCTL_SMARTCARD_GET_FEATURE_REQUEST) + getFeatureRequest = TRUE; + + if (IoCtlDeviceType == FILE_DEVICE_SMARTCARD) + dwControlCode = PCSC_SCARD_CTL_CODE(IoCtlFunction); + + pcsc_dwControlCode = (PCSC_DWORD)dwControlCode; + status = g_PCSC.pfnSCardControl(hCard, pcsc_dwControlCode, lpInBuffer, pcsc_cbInBufferSize, + lpOutBuffer, pcsc_cbOutBufferSize, &pcsc_BytesReturned); + + *lpBytesReturned = (DWORD)pcsc_BytesReturned; + + if (getFeatureRequest) + { + UINT32 count = 0; + PCSC_TLV_STRUCTURE* tlv = (PCSC_TLV_STRUCTURE*)lpOutBuffer; + + if ((*lpBytesReturned % sizeof(PCSC_TLV_STRUCTURE)) != 0) + return SCARD_E_UNEXPECTED; + + count = *lpBytesReturned / sizeof(PCSC_TLV_STRUCTURE); + + for (DWORD index = 0; index < count; index++) + { + if (tlv[index].length != 4) + return SCARD_E_UNEXPECTED; + } + } + + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardGetAttrib_Internal(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen) +{ + SCARDCONTEXT hContext = 0; + BOOL pcbAttrLenAlloc = FALSE; + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_DWORD pcsc_dwAttrId = (PCSC_DWORD)dwAttrId; + PCSC_DWORD pcsc_cbAttrLen = 0; + + if (!g_PCSC.pfnSCardGetAttrib) + return PCSC_SCard_LogError("g_PCSC.pfnSCardGetAttrib"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + hContext = PCSC_GetCardContextFromHandle(hCard); + + if (!hContext) + return SCARD_E_INVALID_HANDLE; + + if (!pcbAttrLen) + return SCARD_E_INVALID_PARAMETER; + + if (*pcbAttrLen == SCARD_AUTOALLOCATE) + { + if (!pbAttr) + return SCARD_E_INVALID_PARAMETER; + pcbAttrLenAlloc = TRUE; + } + + pcsc_cbAttrLen = pcbAttrLenAlloc ? PCSC_SCARD_AUTOALLOCATE : (PCSC_DWORD)*pcbAttrLen; + + if (pcbAttrLenAlloc && !g_SCardAutoAllocate) + { + pcsc_cbAttrLen = 0; + status = g_PCSC.pfnSCardGetAttrib(hCard, pcsc_dwAttrId, NULL, &pcsc_cbAttrLen); + + if (status == SCARD_S_SUCCESS) + { + BYTE* tmp = (BYTE*)calloc(1, pcsc_cbAttrLen); + + if (!tmp) + return SCARD_E_NO_MEMORY; + + status = g_PCSC.pfnSCardGetAttrib(hCard, pcsc_dwAttrId, tmp, &pcsc_cbAttrLen); + + if (status != SCARD_S_SUCCESS) + free(tmp); + else + PCSC_AddMemoryBlock(hContext, tmp); + *(BYTE**)pbAttr = tmp; + } + } + else + { + status = g_PCSC.pfnSCardGetAttrib(hCard, pcsc_dwAttrId, pbAttr, &pcsc_cbAttrLen); + } + + if (status == SCARD_S_SUCCESS) + *pcbAttrLen = (DWORD)pcsc_cbAttrLen; + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardGetAttrib_FriendlyName(SCARDHANDLE hCard, DWORD dwAttrId, + LPBYTE pbAttr, LPDWORD pcbAttrLen) +{ + size_t length = 0; + char* namePCSC = NULL; + char* pbAttrA = NULL; + DWORD cbAttrLen = 0; + WCHAR* pbAttrW = NULL; + SCARDCONTEXT hContext = 0; + LONG status = SCARD_S_SUCCESS; + + hContext = PCSC_GetCardContextFromHandle(hCard); + + if (!hContext) + return SCARD_E_INVALID_HANDLE; + + if (!pcbAttrLen) + return SCARD_E_INVALID_PARAMETER; + cbAttrLen = *pcbAttrLen; + *pcbAttrLen = SCARD_AUTOALLOCATE; + status = PCSC_SCardGetAttrib_Internal(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_A, + (LPBYTE)&pbAttrA, pcbAttrLen); + + if (status != SCARD_S_SUCCESS) + { + *pcbAttrLen = SCARD_AUTOALLOCATE; + status = PCSC_SCardGetAttrib_Internal(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_W, + (LPBYTE)&pbAttrW, pcbAttrLen); + + if (status != SCARD_S_SUCCESS) + return status; + + namePCSC = ConvertMszWCharNToUtf8Alloc(pbAttrW, *pcbAttrLen, NULL); + PCSC_SCardFreeMemory_Internal(hContext, pbAttrW); + } + else + { + namePCSC = _strdup(pbAttrA); + + if (!namePCSC) + return SCARD_E_NO_MEMORY; + + PCSC_SCardFreeMemory_Internal(hContext, pbAttrA); + } + + length = strlen(namePCSC); + + if (dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_W) + { + size_t size = 0; + WCHAR* friendlyNameW = ConvertUtf8ToWCharAlloc(namePCSC, &size); + /* length here includes null terminator */ + + if (!friendlyNameW) + status = SCARD_E_NO_MEMORY; + else + { + length = size; + + if (cbAttrLen == SCARD_AUTOALLOCATE) + { + WINPR_ASSERT(length <= UINT32_MAX / sizeof(WCHAR)); + *(WCHAR**)pbAttr = friendlyNameW; + *pcbAttrLen = (UINT32)length * sizeof(WCHAR); + PCSC_AddMemoryBlock(hContext, friendlyNameW); + } + else + { + if ((length * 2) > cbAttrLen) + status = SCARD_E_INSUFFICIENT_BUFFER; + else + { + WINPR_ASSERT(length <= UINT32_MAX / sizeof(WCHAR)); + CopyMemory(pbAttr, (BYTE*)friendlyNameW, (length * sizeof(WCHAR))); + *pcbAttrLen = (UINT32)length * sizeof(WCHAR); + } + free(friendlyNameW); + } + } + free(namePCSC); + } + else + { + if (cbAttrLen == SCARD_AUTOALLOCATE) + { + *(CHAR**)pbAttr = namePCSC; + WINPR_ASSERT(length <= UINT32_MAX); + *pcbAttrLen = (UINT32)length; + PCSC_AddMemoryBlock(hContext, namePCSC); + } + else + { + if ((length + 1) > cbAttrLen) + status = SCARD_E_INSUFFICIENT_BUFFER; + else + { + CopyMemory(pbAttr, namePCSC, length + 1); + WINPR_ASSERT(length <= UINT32_MAX); + *pcbAttrLen = (UINT32)length; + } + free(namePCSC); + } + } + + return status; +} + +static LONG WINAPI PCSC_SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, + LPDWORD pcbAttrLen) +{ + DWORD cbAttrLen = 0; + SCARDCONTEXT hContext = 0; + BOOL pcbAttrLenAlloc = FALSE; + LONG status = SCARD_S_SUCCESS; + + if (NULL == pcbAttrLen) + return SCARD_E_INVALID_PARAMETER; + + cbAttrLen = *pcbAttrLen; + + if (*pcbAttrLen == SCARD_AUTOALLOCATE) + { + if (NULL == pbAttr) + return SCARD_E_INVALID_PARAMETER; + + pcbAttrLenAlloc = TRUE; + *(BYTE**)pbAttr = NULL; + } + else + { + /** + * pcsc-lite returns SCARD_E_INSUFFICIENT_BUFFER if the given + * buffer size is larger than PCSC_MAX_BUFFER_SIZE (264) + */ + if (*pcbAttrLen > PCSC_MAX_BUFFER_SIZE) + *pcbAttrLen = PCSC_MAX_BUFFER_SIZE; + } + + hContext = PCSC_GetCardContextFromHandle(hCard); + + if (!hContext) + return SCARD_E_INVALID_HANDLE; + + if ((dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_A) || + (dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_W)) + { + status = PCSC_SCardGetAttrib_FriendlyName(hCard, dwAttrId, pbAttr, pcbAttrLen); + return status; + } + + status = PCSC_SCardGetAttrib_Internal(hCard, dwAttrId, pbAttr, pcbAttrLen); + + if (status == SCARD_S_SUCCESS) + { + if (dwAttrId == SCARD_ATTR_VENDOR_NAME) + { + if (pbAttr) + { + const char* vendorName = NULL; + + /** + * pcsc-lite adds a null terminator to the vendor name, + * while WinSCard doesn't. Strip the null terminator. + */ + + if (pcbAttrLenAlloc) + vendorName = (char*)*(BYTE**)pbAttr; + else + vendorName = (char*)pbAttr; + + if (vendorName) + { + size_t len = strnlen(vendorName, *pcbAttrLen); + WINPR_ASSERT(len <= UINT32_MAX); + *pcbAttrLen = (DWORD)len; + } + else + *pcbAttrLen = 0; + } + } + } + else + { + + if (dwAttrId == SCARD_ATTR_CURRENT_PROTOCOL_TYPE) + { + if (!pcbAttrLenAlloc) + { + PCSC_DWORD dwState = 0; + PCSC_DWORD cbAtrLen = 0; + PCSC_DWORD dwProtocol = 0; + PCSC_DWORD cchReaderLen = 0; + status = (LONG)g_PCSC.pfnSCardStatus(hCard, NULL, &cchReaderLen, &dwState, + &dwProtocol, NULL, &cbAtrLen); + + if (status == SCARD_S_SUCCESS) + { + if (cbAttrLen < sizeof(DWORD)) + return SCARD_E_INSUFFICIENT_BUFFER; + + *(DWORD*)pbAttr = PCSC_ConvertProtocolsToWinSCard(dwProtocol); + *pcbAttrLen = sizeof(DWORD); + } + } + } + else if (dwAttrId == SCARD_ATTR_CHANNEL_ID) + { + if (!pcbAttrLenAlloc) + { + UINT32 channelType = 0x20; /* USB */ + UINT32 channelNumber = 0; + + if (cbAttrLen < sizeof(DWORD)) + return SCARD_E_INSUFFICIENT_BUFFER; + + status = SCARD_S_SUCCESS; + *(DWORD*)pbAttr = (channelType << 16u) | channelNumber; + *pcbAttrLen = sizeof(DWORD); + } + } + else if (dwAttrId == SCARD_ATTR_VENDOR_IFD_TYPE) + { + } + else if (dwAttrId == SCARD_ATTR_DEFAULT_CLK) + { + } + else if (dwAttrId == SCARD_ATTR_DEFAULT_DATA_RATE) + { + } + else if (dwAttrId == SCARD_ATTR_MAX_CLK) + { + } + else if (dwAttrId == SCARD_ATTR_MAX_DATA_RATE) + { + } + else if (dwAttrId == SCARD_ATTR_MAX_IFSD) + { + } + else if (dwAttrId == SCARD_ATTR_CHARACTERISTICS) + { + } + else if (dwAttrId == SCARD_ATTR_DEVICE_SYSTEM_NAME_A) + { + } + else if (dwAttrId == SCARD_ATTR_DEVICE_UNIT) + { + } + else if (dwAttrId == SCARD_ATTR_POWER_MGMT_SUPPORT) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_CLK) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_F) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_D) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_N) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_CWT) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_BWT) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_IFSC) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_EBC_ENCODING) + { + } + else if (dwAttrId == SCARD_ATTR_CURRENT_IFSD) + { + } + else if (dwAttrId == SCARD_ATTR_ICC_TYPE_PER_ATR) + { + } + } + + return status; +} + +static LONG WINAPI PCSC_SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, + DWORD cbAttrLen) +{ + PCSC_LONG status = SCARD_S_SUCCESS; + PCSC_SCARDHANDLE* pCard = NULL; + PCSC_DWORD pcsc_dwAttrId = (PCSC_DWORD)dwAttrId; + PCSC_DWORD pcsc_cbAttrLen = (PCSC_DWORD)cbAttrLen; + + if (!g_PCSC.pfnSCardSetAttrib) + return PCSC_SCard_LogError("g_PCSC.pfnSCardSetAttrib"); + + pCard = PCSC_GetCardHandleData(hCard); + + if (!pCard) + return SCARD_E_INVALID_VALUE; + + PCSC_WaitForCardAccess(0, hCard, pCard->shared); + status = g_PCSC.pfnSCardSetAttrib(hCard, pcsc_dwAttrId, pbAttr, pcsc_cbAttrLen); + return PCSC_MapErrorCodeToWinSCard(status); +} + +static LONG WINAPI PCSC_SCardUIDlgSelectCardA(LPOPENCARDNAMEA_EX pDlgStruc) +{ + WINPR_UNUSED(pDlgStruc); + + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardUIDlgSelectCardW(LPOPENCARDNAMEW_EX pDlgStruc) +{ + WINPR_UNUSED(pDlgStruc); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_GetOpenCardNameA(LPOPENCARDNAMEA pDlgStruc) +{ + WINPR_UNUSED(pDlgStruc); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_GetOpenCardNameW(LPOPENCARDNAMEW pDlgStruc) +{ + WINPR_UNUSED(pDlgStruc); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardDlgExtendedError(void) +{ + + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static char* card_id_and_name_a(const UUID* CardIdentifier, LPCSTR LookupName) +{ + WINPR_ASSERT(CardIdentifier); + WINPR_ASSERT(LookupName); + + size_t len = strlen(LookupName) + 34; + char* id = malloc(len); + if (!id) + return NULL; + + snprintf(id, len, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X\\%s", CardIdentifier->Data1, + CardIdentifier->Data2, CardIdentifier->Data3, CardIdentifier->Data4[0], + CardIdentifier->Data4[1], CardIdentifier->Data4[2], CardIdentifier->Data4[3], + CardIdentifier->Data4[4], CardIdentifier->Data4[5], CardIdentifier->Data4[6], + CardIdentifier->Data4[7], LookupName); + return id; +} + +static char* card_id_and_name_w(const UUID* CardIdentifier, LPCWSTR LookupName) +{ + char* res = NULL; + char* tmp = ConvertWCharToUtf8Alloc(LookupName, NULL); + if (!tmp) + return NULL; + res = card_id_and_name_a(CardIdentifier, tmp); + free(tmp); + return res; +} + +static LONG WINAPI PCSC_SCardReadCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD* DataLen) +{ + PCSC_CACHE_ITEM* data = NULL; + PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); + char* id = card_id_and_name_a(CardIdentifier, LookupName); + + data = HashTable_GetItemValue(ctx->cache, id); + free(id); + if (!data) + { + *DataLen = 0; + return SCARD_W_CACHE_ITEM_NOT_FOUND; + } + + if (FreshnessCounter != data->freshness) + { + *DataLen = 0; + return SCARD_W_CACHE_ITEM_STALE; + } + + if (*DataLen == SCARD_AUTOALLOCATE) + { + BYTE* mem = calloc(1, data->len); + if (!mem) + return SCARD_E_NO_MEMORY; + + if (!PCSC_AddMemoryBlock(hContext, mem)) + { + free(mem); + return SCARD_E_NO_MEMORY; + } + + memcpy(mem, data->data, data->len); + *(BYTE**)Data = mem; + } + else + memcpy(Data, data->data, data->len); + *DataLen = data->len; + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardReadCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD* DataLen) +{ + PCSC_CACHE_ITEM* data = NULL; + PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); + char* id = card_id_and_name_w(CardIdentifier, LookupName); + + data = HashTable_GetItemValue(ctx->cache, id); + free(id); + + if (!data) + { + *DataLen = 0; + return SCARD_W_CACHE_ITEM_NOT_FOUND; + } + + if (FreshnessCounter != data->freshness) + { + *DataLen = 0; + return SCARD_W_CACHE_ITEM_STALE; + } + + if (*DataLen == SCARD_AUTOALLOCATE) + { + BYTE* mem = calloc(1, data->len); + if (!mem) + return SCARD_E_NO_MEMORY; + + if (!PCSC_AddMemoryBlock(hContext, mem)) + { + free(mem); + return SCARD_E_NO_MEMORY; + } + + memcpy(mem, data->data, data->len); + *(BYTE**)Data = mem; + } + else + memcpy(Data, data->data, data->len); + *DataLen = data->len; + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardWriteCacheA(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPSTR LookupName, PBYTE Data, + DWORD DataLen) +{ + PCSC_CACHE_ITEM* data = NULL; + PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); + char* id = NULL; + + if (!ctx) + return SCARD_E_FILE_NOT_FOUND; + + id = card_id_and_name_a(CardIdentifier, LookupName); + + if (!id) + return SCARD_E_NO_MEMORY; + + data = malloc(sizeof(PCSC_CACHE_ITEM)); + if (!data) + { + free(id); + return SCARD_E_NO_MEMORY; + } + data->data = calloc(DataLen, 1); + if (!data->data) + { + free(id); + free(data); + return SCARD_E_NO_MEMORY; + } + data->len = DataLen; + data->freshness = FreshnessCounter; + memcpy(data->data, Data, data->len); + + HashTable_Remove(ctx->cache, id); + const BOOL rc = HashTable_Insert(ctx->cache, id, data); + free(id); + + if (!rc) + { + pcsc_cache_item_free(data); + return SCARD_E_NO_MEMORY; + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns data + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardWriteCacheW(SCARDCONTEXT hContext, UUID* CardIdentifier, + DWORD FreshnessCounter, LPWSTR LookupName, PBYTE Data, + DWORD DataLen) +{ + PCSC_CACHE_ITEM* data = NULL; + PCSC_SCARDCONTEXT* ctx = PCSC_GetCardContextData(hContext); + char* id = NULL; + if (!ctx) + return SCARD_E_FILE_NOT_FOUND; + + id = card_id_and_name_w(CardIdentifier, LookupName); + + if (!id) + return SCARD_E_NO_MEMORY; + + data = malloc(sizeof(PCSC_CACHE_ITEM)); + if (!data) + { + free(id); + return SCARD_E_NO_MEMORY; + } + data->data = malloc(DataLen); + if (!data->data) + { + free(id); + free(data); + return SCARD_E_NO_MEMORY; + } + data->len = DataLen; + data->freshness = FreshnessCounter; + memcpy(data->data, Data, data->len); + + HashTable_Remove(ctx->cache, id); + const BOOL rc = HashTable_Insert(ctx->cache, id, data); + free(id); + + if (!rc) + { + pcsc_cache_item_free(data); + return SCARD_E_NO_MEMORY; + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert owns data + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardGetReaderIconA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(pbIcon); + WINPR_UNUSED(pcbIcon); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetReaderIconW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPBYTE pbIcon, LPDWORD pcbIcon) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(pbIcon); + WINPR_UNUSED(pcbIcon); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetDeviceTypeIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(pdwDeviceTypeId); + if (pdwDeviceTypeId) + *pdwDeviceTypeId = SCARD_READER_TYPE_USB; + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardGetDeviceTypeIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPDWORD pdwDeviceTypeId) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + if (pdwDeviceTypeId) + *pdwDeviceTypeId = SCARD_READER_TYPE_USB; + return SCARD_S_SUCCESS; +} + +static LONG WINAPI PCSC_SCardGetReaderDeviceInstanceIdA(SCARDCONTEXT hContext, LPCSTR szReaderName, + LPSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(pcchDeviceInstanceId); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardGetReaderDeviceInstanceIdW(SCARDCONTEXT hContext, LPCWSTR szReaderName, + LPWSTR szDeviceInstanceId, + LPDWORD pcchDeviceInstanceId) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szReaderName); + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(pcchDeviceInstanceId); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardListReadersWithDeviceInstanceIdA(SCARDCONTEXT hContext, + LPCSTR szDeviceInstanceId, + LPSTR mszReaders, LPDWORD pcchReaders) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(mszReaders); + WINPR_UNUSED(pcchReaders); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardListReadersWithDeviceInstanceIdW(SCARDCONTEXT hContext, + LPCWSTR szDeviceInstanceId, + LPWSTR mszReaders, + LPDWORD pcchReaders) +{ + WINPR_UNUSED(hContext); + WINPR_UNUSED(szDeviceInstanceId); + WINPR_UNUSED(mszReaders); + WINPR_UNUSED(pcchReaders); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG WINAPI PCSC_SCardAudit(SCARDCONTEXT hContext, DWORD dwEvent) +{ + + WINPR_UNUSED(hContext); + WINPR_UNUSED(dwEvent); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +#ifdef __MACOSX__ +unsigned int determineMacOSXVersion(void) +{ + int mib[2]; + size_t len = 0; + char* kernelVersion = NULL; + char* tok = NULL; + unsigned int version = 0; + long majorVersion = 0; + long minorVersion = 0; + long patchVersion = 0; + int count = 0; + char* context = NULL; + mib[0] = CTL_KERN; + mib[1] = KERN_OSRELEASE; + + if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0) + return 0; + + kernelVersion = calloc(len, sizeof(char)); + + if (!kernelVersion) + return 0; + + if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) != 0) + { + free(kernelVersion); + return 0; + } + + tok = strtok_s(kernelVersion, ".", &context); + errno = 0; + + while (tok) + { + switch (count) + { + case 0: + majorVersion = strtol(tok, NULL, 0); + + if (errno != 0) + goto fail; + + break; + + case 1: + minorVersion = strtol(tok, NULL, 0); + + if (errno != 0) + goto fail; + + break; + + case 2: + patchVersion = strtol(tok, NULL, 0); + + if (errno != 0) + goto fail; + + break; + } + + tok = strtok_s(NULL, ".", &context); + count++; + } + + /** + * Source : http://en.wikipedia.org/wiki/Darwin_(operating_system) + **/ + if (majorVersion < 5) + { + if (minorVersion < 4) + version = 0x10000000; + else + version = 0x10010000; + } + else + { + switch (majorVersion) + { + case 5: + version = 0x10010000; + break; + + case 6: + version = 0x10020000; + break; + + case 7: + version = 0x10030000; + break; + + case 8: + version = 0x10040000; + break; + + case 9: + version = 0x10050000; + break; + + case 10: + version = 0x10060000; + break; + + case 11: + version = 0x10070000; + break; + + case 12: + version = 0x10080000; + break; + + case 13: + version = 0x10090000; + break; + + default: + version = 0x10100000; + break; + } + + version |= (minorVersion << 8) | (patchVersion); + } + +fail: + free(kernelVersion); + return version; +} +#endif + +static const SCardApiFunctionTable PCSC_SCardApiFunctionTable = { + 0, /* dwVersion */ + 0, /* dwFlags */ + + PCSC_SCardEstablishContext, /* SCardEstablishContext */ + PCSC_SCardReleaseContext, /* SCardReleaseContext */ + PCSC_SCardIsValidContext, /* SCardIsValidContext */ + PCSC_SCardListReaderGroupsA, /* SCardListReaderGroupsA */ + PCSC_SCardListReaderGroupsW, /* SCardListReaderGroupsW */ + PCSC_SCardListReadersA, /* SCardListReadersA */ + PCSC_SCardListReadersW, /* SCardListReadersW */ + PCSC_SCardListCardsA, /* SCardListCardsA */ + PCSC_SCardListCardsW, /* SCardListCardsW */ + PCSC_SCardListInterfacesA, /* SCardListInterfacesA */ + PCSC_SCardListInterfacesW, /* SCardListInterfacesW */ + PCSC_SCardGetProviderIdA, /* SCardGetProviderIdA */ + PCSC_SCardGetProviderIdW, /* SCardGetProviderIdW */ + PCSC_SCardGetCardTypeProviderNameA, /* SCardGetCardTypeProviderNameA */ + PCSC_SCardGetCardTypeProviderNameW, /* SCardGetCardTypeProviderNameW */ + PCSC_SCardIntroduceReaderGroupA, /* SCardIntroduceReaderGroupA */ + PCSC_SCardIntroduceReaderGroupW, /* SCardIntroduceReaderGroupW */ + PCSC_SCardForgetReaderGroupA, /* SCardForgetReaderGroupA */ + PCSC_SCardForgetReaderGroupW, /* SCardForgetReaderGroupW */ + PCSC_SCardIntroduceReaderA, /* SCardIntroduceReaderA */ + PCSC_SCardIntroduceReaderW, /* SCardIntroduceReaderW */ + PCSC_SCardForgetReaderA, /* SCardForgetReaderA */ + PCSC_SCardForgetReaderW, /* SCardForgetReaderW */ + PCSC_SCardAddReaderToGroupA, /* SCardAddReaderToGroupA */ + PCSC_SCardAddReaderToGroupW, /* SCardAddReaderToGroupW */ + PCSC_SCardRemoveReaderFromGroupA, /* SCardRemoveReaderFromGroupA */ + PCSC_SCardRemoveReaderFromGroupW, /* SCardRemoveReaderFromGroupW */ + PCSC_SCardIntroduceCardTypeA, /* SCardIntroduceCardTypeA */ + PCSC_SCardIntroduceCardTypeW, /* SCardIntroduceCardTypeW */ + PCSC_SCardSetCardTypeProviderNameA, /* SCardSetCardTypeProviderNameA */ + PCSC_SCardSetCardTypeProviderNameW, /* SCardSetCardTypeProviderNameW */ + PCSC_SCardForgetCardTypeA, /* SCardForgetCardTypeA */ + PCSC_SCardForgetCardTypeW, /* SCardForgetCardTypeW */ + PCSC_SCardFreeMemory, /* SCardFreeMemory */ + PCSC_SCardAccessStartedEvent, /* SCardAccessStartedEvent */ + PCSC_SCardReleaseStartedEvent, /* SCardReleaseStartedEvent */ + PCSC_SCardLocateCardsA, /* SCardLocateCardsA */ + PCSC_SCardLocateCardsW, /* SCardLocateCardsW */ + PCSC_SCardLocateCardsByATRA, /* SCardLocateCardsByATRA */ + PCSC_SCardLocateCardsByATRW, /* SCardLocateCardsByATRW */ + PCSC_SCardGetStatusChangeA, /* SCardGetStatusChangeA */ + PCSC_SCardGetStatusChangeW, /* SCardGetStatusChangeW */ + PCSC_SCardCancel, /* SCardCancel */ + PCSC_SCardConnectA, /* SCardConnectA */ + PCSC_SCardConnectW, /* SCardConnectW */ + PCSC_SCardReconnect, /* SCardReconnect */ + PCSC_SCardDisconnect, /* SCardDisconnect */ + PCSC_SCardBeginTransaction, /* SCardBeginTransaction */ + PCSC_SCardEndTransaction, /* SCardEndTransaction */ + PCSC_SCardCancelTransaction, /* SCardCancelTransaction */ + PCSC_SCardState, /* SCardState */ + PCSC_SCardStatusA, /* SCardStatusA */ + PCSC_SCardStatusW, /* SCardStatusW */ + PCSC_SCardTransmit, /* SCardTransmit */ + PCSC_SCardGetTransmitCount, /* SCardGetTransmitCount */ + PCSC_SCardControl, /* SCardControl */ + PCSC_SCardGetAttrib, /* SCardGetAttrib */ + PCSC_SCardSetAttrib, /* SCardSetAttrib */ + PCSC_SCardUIDlgSelectCardA, /* SCardUIDlgSelectCardA */ + PCSC_SCardUIDlgSelectCardW, /* SCardUIDlgSelectCardW */ + PCSC_GetOpenCardNameA, /* GetOpenCardNameA */ + PCSC_GetOpenCardNameW, /* GetOpenCardNameW */ + PCSC_SCardDlgExtendedError, /* SCardDlgExtendedError */ + PCSC_SCardReadCacheA, /* SCardReadCacheA */ + PCSC_SCardReadCacheW, /* SCardReadCacheW */ + PCSC_SCardWriteCacheA, /* SCardWriteCacheA */ + PCSC_SCardWriteCacheW, /* SCardWriteCacheW */ + PCSC_SCardGetReaderIconA, /* SCardGetReaderIconA */ + PCSC_SCardGetReaderIconW, /* SCardGetReaderIconW */ + PCSC_SCardGetDeviceTypeIdA, /* SCardGetDeviceTypeIdA */ + PCSC_SCardGetDeviceTypeIdW, /* SCardGetDeviceTypeIdW */ + PCSC_SCardGetReaderDeviceInstanceIdA, /* SCardGetReaderDeviceInstanceIdA */ + PCSC_SCardGetReaderDeviceInstanceIdW, /* SCardGetReaderDeviceInstanceIdW */ + PCSC_SCardListReadersWithDeviceInstanceIdA, /* SCardListReadersWithDeviceInstanceIdA */ + PCSC_SCardListReadersWithDeviceInstanceIdW, /* SCardListReadersWithDeviceInstanceIdW */ + PCSC_SCardAudit /* SCardAudit */ +}; + +const SCardApiFunctionTable* PCSC_GetSCardApiFunctionTable(void) +{ + return &PCSC_SCardApiFunctionTable; +} + +int PCSC_InitializeSCardApi(void) +{ + /* Disable pcsc-lite's (poor) blocking so we can handle it ourselves */ + SetEnvironmentVariableA("PCSCLITE_NO_BLOCKING", "1"); +#ifdef __MACOSX__ + g_PCSCModule = LoadLibraryX("/System/Library/Frameworks/PCSC.framework/PCSC"); + OSXVersion = determineMacOSXVersion(); + + if (OSXVersion == 0) + return -1; + +#else + g_PCSCModule = LoadLibraryA("libpcsclite.so.1"); + + if (!g_PCSCModule) + g_PCSCModule = LoadLibraryA("libpcsclite.so"); + +#endif + + if (!g_PCSCModule) + return -1; + + /* symbols defined in winpr/smartcard.h, might pose an issue with the GetProcAddress macro + * below. therefore undefine them here */ +#undef SCardListReaderGroups +#undef SCardListReaders +#undef SCardListCards +#undef SCardListInterfaces +#undef SCardGetProviderId +#undef SCardGetCardTypeProviderName +#undef SCardIntroduceReaderGroup +#undef SCardForgetReaderGroup +#undef SCardIntroduceReader +#undef SCardForgetReader +#undef SCardAddReaderToGroup +#undef SCardRemoveReaderFromGroup +#undef SCardIntroduceCardType +#undef SCardSetCardTypeProviderName +#undef SCardForgetCardType +#undef SCardLocateCards +#undef SCardLocateCardsByATR +#undef SCardGetStatusChange +#undef SCardConnect +#undef SCardStatus +#undef SCardUIDlgSelectCard +#undef GetOpenCardName +#undef SCardReadCache +#undef SCardWriteCache +#undef SCardGetReaderIcon +#undef SCardGetDeviceTypeId +#undef SCardGetReaderDeviceInstanceId +#undef SCardListReadersWithDeviceInstanceId + + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardEstablishContext); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardReleaseContext); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardIsValidContext); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardConnect); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardReconnect); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardDisconnect); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardBeginTransaction); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardEndTransaction); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardStatus); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardGetStatusChange); + +#ifdef __MACOSX__ + + if (OSXVersion >= 0x10050600) + { + WINSCARD_LOAD_PROC_EX(g_PCSCModule, g_PCSC, SCardControl, SCardControl132); + } + else + { + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardControl); + } +#else + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardControl); +#endif + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardTransmit); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardListReaderGroups); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardListReaders); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardCancel); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardGetAttrib); + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardSetAttrib); + g_PCSC.pfnSCardFreeMemory = NULL; +#ifndef __APPLE__ + WINSCARD_LOAD_PROC(g_PCSCModule, g_PCSC, SCardFreeMemory); +#endif + + if (g_PCSC.pfnSCardFreeMemory) + g_SCardAutoAllocate = TRUE; + +#ifdef DISABLE_PCSC_SCARD_AUTOALLOCATE + g_PCSC.pfnSCardFreeMemory = NULL; + g_SCardAutoAllocate = FALSE; +#endif +#ifdef __APPLE__ + g_PnP_Notification = FALSE; +#endif + return 1; +} + +#endif diff --git a/winpr/libwinpr/smartcard/smartcard_pcsc.h b/winpr/libwinpr/smartcard/smartcard_pcsc.h new file mode 100644 index 0000000..9ff822d --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard_pcsc.h @@ -0,0 +1,175 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2020 Armin Novak + * Copyright 2020 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_PCSC_PRIVATE_H +#define WINPR_SMARTCARD_PCSC_PRIVATE_H + +#ifndef _WIN32 + +#include +#include + +/** + * On Windows, DWORD and ULONG are defined to unsigned long. + * However, 64-bit Windows uses the LLP64 model which defines + * unsigned long as a 4-byte type, while most non-Windows + * systems use the LP64 model where unsigned long is 8 bytes. + * + * WinPR correctly defines DWORD and ULONG to be 4-byte types + * regardless of LLP64/LP64, but this has the side effect of + * breaking compatibility with the broken pcsc-lite types. + * + * To make matters worse, pcsc-lite correctly defines + * the data types on OS X, but not on other platforms. + */ + +#ifdef __APPLE__ + +#include + +#ifndef BYTE +typedef uint8_t PCSC_BYTE; +#endif +typedef uint8_t PCSC_UCHAR; +typedef PCSC_UCHAR* PCSC_PUCHAR; +typedef uint16_t PCSC_USHORT; + +#ifndef __COREFOUNDATION_CFPLUGINCOM__ +typedef uint32_t PCSC_ULONG; +typedef void* PCSC_LPVOID; +typedef int16_t PCSC_BOOL; +#endif + +typedef PCSC_ULONG* PCSC_PULONG; +typedef const void* PCSC_LPCVOID; +typedef uint32_t PCSC_DWORD; +typedef PCSC_DWORD* PCSC_PDWORD; +typedef uint16_t PCSC_WORD; +typedef int32_t PCSC_LONG; +typedef const char* PCSC_LPCSTR; +typedef const PCSC_BYTE* PCSC_LPCBYTE; +typedef PCSC_BYTE* PCSC_LPBYTE; +typedef PCSC_DWORD* PCSC_LPDWORD; +typedef char* PCSC_LPSTR; + +#else + +#ifndef BYTE +typedef unsigned char PCSC_BYTE; +#endif +typedef unsigned char PCSC_UCHAR; +typedef PCSC_UCHAR* PCSC_PUCHAR; +typedef unsigned short PCSC_USHORT; + +#ifndef __COREFOUNDATION_CFPLUGINCOM__ +typedef unsigned long PCSC_ULONG; +typedef void* PCSC_LPVOID; +#endif + +typedef const void* PCSC_LPCVOID; +typedef unsigned long PCSC_DWORD; +typedef PCSC_DWORD* PCSC_PDWORD; +typedef long PCSC_LONG; +typedef const char* PCSC_LPCSTR; +typedef const PCSC_BYTE* PCSC_LPCBYTE; +typedef PCSC_BYTE* PCSC_LPBYTE; +typedef PCSC_DWORD* PCSC_LPDWORD; +typedef char* PCSC_LPSTR; + +/* these types were deprecated but still used by old drivers and + * applications. So just declare and use them. */ +typedef PCSC_LPSTR PCSC_LPTSTR; +typedef PCSC_LPCSTR PCSC_LPCTSTR; + +/* types unused by pcsc-lite */ +typedef short PCSC_BOOL; +typedef unsigned short PCSC_WORD; +typedef PCSC_ULONG* PCSC_PULONG; + +#endif + +#define PCSC_SCARD_UNKNOWN 0x0001 +#define PCSC_SCARD_ABSENT 0x0002 +#define PCSC_SCARD_PRESENT 0x0004 +#define PCSC_SCARD_SWALLOWED 0x0008 +#define PCSC_SCARD_POWERED 0x0010 +#define PCSC_SCARD_NEGOTIABLE 0x0020 +#define PCSC_SCARD_SPECIFIC 0x0040 + +#define PCSC_SCARD_PROTOCOL_RAW 0x00000004u +#define PCSC_SCARD_PROTOCOL_T15 0x00000008u + +#define PCSC_MAX_BUFFER_SIZE 264 +#define PCSC_MAX_BUFFER_SIZE_EXTENDED (4 + 3 + (1 << 16) + 3 + 2) + +#define PCSC_MAX_ATR_SIZE 33 + +#define PCSC_SCARD_AUTOALLOCATE (PCSC_DWORD)(-1) + +#define PCSC_SCARD_CTL_CODE(code) (0x42000000 + (code)) +#define PCSC_CM_IOCTL_GET_FEATURE_REQUEST PCSC_SCARD_CTL_CODE(3400) + +/** + * pcsc-lite defines SCARD_READERSTATE, SCARD_IO_REQUEST as packed + * on Mac OS X only and uses default packing everywhere else. + */ + +#ifdef __APPLE__ +#pragma pack(push, 1) +#endif + +typedef struct +{ + LPCSTR szReader; + LPVOID pvUserData; + PCSC_DWORD dwCurrentState; + PCSC_DWORD dwEventState; + PCSC_DWORD cbAtr; + BYTE rgbAtr[PCSC_MAX_ATR_SIZE]; /* WinSCard: 36, PCSC: 33 */ +} PCSC_SCARD_READERSTATE; + +typedef struct +{ + PCSC_DWORD dwProtocol; + PCSC_DWORD cbPciLength; +} PCSC_SCARD_IO_REQUEST; + +#ifdef __APPLE__ +#pragma pack(pop) +#endif + +#pragma pack(push, 1) + +typedef struct +{ + BYTE tag; + BYTE length; + UINT32 value; +} PCSC_TLV_STRUCTURE; + +#pragma pack(pop) + +int PCSC_InitializeSCardApi(void); +const SCardApiFunctionTable* PCSC_GetSCardApiFunctionTable(void); + +#endif + +#endif /* WINPR_SMARTCARD_PCSC_PRIVATE_H */ diff --git a/winpr/libwinpr/smartcard/smartcard_windows.c b/winpr/libwinpr/smartcard/smartcard_windows.c new file mode 100644 index 0000000..967a2a0 --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard_windows.c @@ -0,0 +1,126 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "smartcard_windows.h" + +static HMODULE g_WinSCardModule = NULL; + +static SCardApiFunctionTable Windows_SCardApiFunctionTable = { + 0, /* dwVersion */ + 0, /* dwFlags */ + + NULL, /* SCardEstablishContext */ + NULL, /* SCardReleaseContext */ + NULL, /* SCardIsValidContext */ + NULL, /* SCardListReaderGroupsA */ + NULL, /* SCardListReaderGroupsW */ + NULL, /* SCardListReadersA */ + NULL, /* SCardListReadersW */ + NULL, /* SCardListCardsA */ + NULL, /* SCardListCardsW */ + NULL, /* SCardListInterfacesA */ + NULL, /* SCardListInterfacesW */ + NULL, /* SCardGetProviderIdA */ + NULL, /* SCardGetProviderIdW */ + NULL, /* SCardGetCardTypeProviderNameA */ + NULL, /* SCardGetCardTypeProviderNameW */ + NULL, /* SCardIntroduceReaderGroupA */ + NULL, /* SCardIntroduceReaderGroupW */ + NULL, /* SCardForgetReaderGroupA */ + NULL, /* SCardForgetReaderGroupW */ + NULL, /* SCardIntroduceReaderA */ + NULL, /* SCardIntroduceReaderW */ + NULL, /* SCardForgetReaderA */ + NULL, /* SCardForgetReaderW */ + NULL, /* SCardAddReaderToGroupA */ + NULL, /* SCardAddReaderToGroupW */ + NULL, /* SCardRemoveReaderFromGroupA */ + NULL, /* SCardRemoveReaderFromGroupW */ + NULL, /* SCardIntroduceCardTypeA */ + NULL, /* SCardIntroduceCardTypeW */ + NULL, /* SCardSetCardTypeProviderNameA */ + NULL, /* SCardSetCardTypeProviderNameW */ + NULL, /* SCardForgetCardTypeA */ + NULL, /* SCardForgetCardTypeW */ + NULL, /* SCardFreeMemory */ + NULL, /* SCardAccessStartedEvent */ + NULL, /* SCardReleaseStartedEvent */ + NULL, /* SCardLocateCardsA */ + NULL, /* SCardLocateCardsW */ + NULL, /* SCardLocateCardsByATRA */ + NULL, /* SCardLocateCardsByATRW */ + NULL, /* SCardGetStatusChangeA */ + NULL, /* SCardGetStatusChangeW */ + NULL, /* SCardCancel */ + NULL, /* SCardConnectA */ + NULL, /* SCardConnectW */ + NULL, /* SCardReconnect */ + NULL, /* SCardDisconnect */ + NULL, /* SCardBeginTransaction */ + NULL, /* SCardEndTransaction */ + NULL, /* SCardCancelTransaction */ + NULL, /* SCardState */ + NULL, /* SCardStatusA */ + NULL, /* SCardStatusW */ + NULL, /* SCardTransmit */ + NULL, /* SCardGetTransmitCount */ + NULL, /* SCardControl */ + NULL, /* SCardGetAttrib */ + NULL, /* SCardSetAttrib */ + NULL, /* SCardUIDlgSelectCardA */ + NULL, /* SCardUIDlgSelectCardW */ + NULL, /* GetOpenCardNameA */ + NULL, /* GetOpenCardNameW */ + NULL, /* SCardDlgExtendedError */ + NULL, /* SCardReadCacheA */ + NULL, /* SCardReadCacheW */ + NULL, /* SCardWriteCacheA */ + NULL, /* SCardWriteCacheW */ + NULL, /* SCardGetReaderIconA */ + NULL, /* SCardGetReaderIconW */ + NULL, /* SCardGetDeviceTypeIdA */ + NULL, /* SCardGetDeviceTypeIdW */ + NULL, /* SCardGetReaderDeviceInstanceIdA */ + NULL, /* SCardGetReaderDeviceInstanceIdW */ + NULL, /* SCardListReadersWithDeviceInstanceIdA */ + NULL, /* SCardListReadersWithDeviceInstanceIdW */ + NULL /* SCardAudit */ +}; + +const SCardApiFunctionTable* Windows_GetSCardApiFunctionTable(void) +{ + return &Windows_SCardApiFunctionTable; +} + +int Windows_InitializeSCardApi(void) +{ + g_WinSCardModule = LoadLibraryA("WinSCard.dll"); + + if (!g_WinSCardModule) + return -1; + + WinSCard_LoadApiTableFunctions(&Windows_SCardApiFunctionTable, g_WinSCardModule); + return 1; +} diff --git a/winpr/libwinpr/smartcard/smartcard_windows.h b/winpr/libwinpr/smartcard/smartcard_windows.h new file mode 100644 index 0000000..4df72b0 --- /dev/null +++ b/winpr/libwinpr/smartcard/smartcard_windows.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * Smart Card API + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SMARTCARD_WINSCARD_PRIVATE_H +#define WINPR_SMARTCARD_WINSCARD_PRIVATE_H + +#include + +int Windows_InitializeSCardApi(void); +const SCardApiFunctionTable* Windows_GetSCardApiFunctionTable(void); + +#endif /* WINPR_SMARTCARD_WINSCARD_PRIVATE_H */ diff --git a/winpr/libwinpr/smartcard/test/CMakeLists.txt b/winpr/libwinpr/smartcard/test/CMakeLists.txt new file mode 100644 index 0000000..4d8e107 --- /dev/null +++ b/winpr/libwinpr/smartcard/test/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(MODULE_NAME "TestSmartCard") +set(MODULE_PREFIX "TEST_SMARTCARD") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestSmartCardListReaders.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/smartcard/test/TestSmartCardListReaders.c b/winpr/libwinpr/smartcard/test/TestSmartCardListReaders.c new file mode 100644 index 0000000..637200b --- /dev/null +++ b/winpr/libwinpr/smartcard/test/TestSmartCardListReaders.c @@ -0,0 +1,53 @@ + +#include +#include + +int TestSmartCardListReaders(int argc, char* argv[]) +{ + LONG lStatus = 0; + LPSTR pReader = NULL; + SCARDCONTEXT hSC = 0; + LPSTR mszReaders = NULL; + DWORD cchReaders = SCARD_AUTOALLOCATE; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + lStatus = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hSC); + + if (lStatus != SCARD_S_SUCCESS) + { + printf("SCardEstablishContext failure: %s (0x%08" PRIX32 ")\n", + SCardGetErrorString(lStatus), lStatus); + return 0; + } + + lStatus = SCardListReadersA(hSC, NULL, (LPSTR)&mszReaders, &cchReaders); + + if (lStatus != SCARD_S_SUCCESS) + { + if (lStatus == SCARD_E_NO_READERS_AVAILABLE) + printf("SCARD_E_NO_READERS_AVAILABLE\n"); + else + return -1; + } + else + { + pReader = mszReaders; + + while (*pReader) + { + printf("Reader: %s\n", pReader); + pReader = pReader + strlen((CHAR*)pReader) + 1; + } + + lStatus = SCardFreeMemory(hSC, mszReaders); + + if (lStatus != SCARD_S_SUCCESS) + printf("Failed SCardFreeMemory\n"); + } + + SCardReleaseContext(hSC); + + return 0; +} diff --git a/winpr/libwinpr/smartcard/test/TestSmartCardStatus.c b/winpr/libwinpr/smartcard/test/TestSmartCardStatus.c new file mode 100644 index 0000000..011f0ce --- /dev/null +++ b/winpr/libwinpr/smartcard/test/TestSmartCardStatus.c @@ -0,0 +1,160 @@ +// compile against PCSC gcc -o scardtest TestSmartCardStatus.c -DPCSC=1 -I /usr/include/PCSC +// -lpcsclite +#include +#include +#include +#if defined(__APPLE__) || defined(PCSC) +#include +#include +#elif defined(__linux__) +#include +#include +#include +#else +#include +#endif + +#if defined(PCSC) +int main(int argc, char* argv[]) +#else +int TestSmartCardStatus(int argc, char* argv[]) +#endif +{ + SCARDCONTEXT hContext; + LPSTR mszReaders; + DWORD cchReaders = 0; + DWORD err; + SCARDHANDLE hCard; + DWORD dwActiveProtocol; + char name[100]; + char* aname = NULL; + char* aatr = NULL; + DWORD len; + BYTE atr[32]; + DWORD atrlen = 32; + DWORD status = 0; + DWORD protocol = 0; + err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); + + if (err != SCARD_S_SUCCESS) + { + printf("ScardEstablishedContext: 0x%08x\n", err); + return -1; + } + + err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders); + + if (err != 0) + { + printf("ScardListReaders: 0x%08x\n", err); + return -1; + } + + mszReaders = calloc(cchReaders, sizeof(char)); + + if (!mszReaders) + { + printf("calloc\n"); + return -1; + } + + err = SCardListReaders(hContext, "SCard$AllReaders", mszReaders, &cchReaders); + + if (err != SCARD_S_SUCCESS) + { + printf("ScardListReaders: 0x%08x\n", err); + return -1; + } + + printf("Reader: %s\n", mszReaders); + err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); + + if (err != SCARD_S_SUCCESS) + { + printf("ScardConnect: 0x%08x\n", err); + return -1; + } + + free(mszReaders); + + printf("# test 1 - get reader length\n"); + err = SCardStatus(hCard, NULL, &len, NULL, NULL, NULL, NULL); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("reader name length: %u\n", len); + + printf("# test 2 - get reader name value\n"); + err = SCardStatus(hCard, name, &len, NULL, NULL, NULL, NULL); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("Reader name: %s (%ld)\n", name, strlen(name)); + + printf("# test 3 - get all values - pre allocated\n"); + err = SCardStatus(hCard, name, &len, &status, &protocol, atr, &atrlen); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("Reader name: %s (%ld/len %u)\n", name, strlen(name), len); + printf("status: 0x%08X\n", status); + printf("proto: 0x%08X\n", protocol); + printf("atrlen: %u\n", atrlen); + + printf("# test 4 - get all values - auto allocate\n"); + len = atrlen = SCARD_AUTOALLOCATE; + err = SCardStatus(hCard, (LPSTR)&aname, &len, &status, &protocol, (LPBYTE)&aatr, &atrlen); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("Reader name: %s (%ld/%u)\n", aname, strlen(aname), len); + printf("status: 0x%08X\n", status); + printf("proto: 0x%08X\n", protocol); + printf("atrlen: %u\n", atrlen); + SCardFreeMemory(hContext, aname); + SCardFreeMemory(hContext, aatr); + + printf("# test 5 - get status and protocol only\n"); + err = SCardStatus(hCard, NULL, NULL, &status, &protocol, NULL, NULL); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("status: 0x%08X\n", status); + printf("proto: 0x%08X\n", protocol); + + printf("# test 6 - get atr only auto allocated\n"); + atrlen = SCARD_AUTOALLOCATE; + err = SCardStatus(hCard, NULL, NULL, NULL, NULL, (LPBYTE)&aatr, &atrlen); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("atrlen: %u\n", atrlen); + SCardFreeMemory(hContext, aatr); + + printf("# test 7 - get atr only pre allocated\n"); + atrlen = 32; + err = SCardStatus(hCard, NULL, NULL, NULL, NULL, atr, &atrlen); + if (err != SCARD_S_SUCCESS) + { + printf("SCardStatus: 0x%08x\n", err); + return -1; + } + printf("atrlen: %u\n", atrlen); + SCardDisconnect(hCard, SCARD_LEAVE_CARD); + SCardReleaseContext(hContext); + + return 0; +} diff --git a/winpr/libwinpr/sspi/CMakeLists.txt b/winpr/libwinpr/sspi/CMakeLists.txt new file mode 100644 index 0000000..f77b38f --- /dev/null +++ b/winpr/libwinpr/sspi/CMakeLists.txt @@ -0,0 +1,129 @@ +# WinPR: Windows Portable Runtime +# libwinpr-sspi cmake build script +# +# Copyright 2011 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_PREFIX "WINPR_SSPI") + +set(${MODULE_PREFIX}_NTLM_SRCS + NTLM/ntlm_av_pairs.c + NTLM/ntlm_av_pairs.h + NTLM/ntlm_compute.c + NTLM/ntlm_compute.h + NTLM/ntlm_message.c + NTLM/ntlm_message.h + NTLM/ntlm.c + NTLM/ntlm.h + NTLM/ntlm_export.h) + +set(${MODULE_PREFIX}_KERBEROS_SRCS + Kerberos/kerberos.c + Kerberos/kerberos.h) + +set(${MODULE_PREFIX}_NEGOTIATE_SRCS + Negotiate/negotiate.c + Negotiate/negotiate.h) + +set(${MODULE_PREFIX}_SCHANNEL_SRCS + Schannel/schannel_openssl.c + Schannel/schannel_openssl.h + Schannel/schannel.c + Schannel/schannel.h) + +set(${MODULE_PREFIX}_CREDSSP_SRCS + CredSSP/credssp.c + CredSSP/credssp.h) + +set(${MODULE_PREFIX}_SRCS + sspi_winpr.c + sspi_winpr.h + sspi_export.c + sspi_gss.c + sspi_gss.h + sspi.c + sspi.h) + +set(KRB5_DEFAULT OFF) +if (NOT WIN32 AND NOT ANDROID AND NOT IOS AND NOT APPLE) + set(KRB5_DEFAULT ON) +endif() + +option(WITH_DEBUG_SCHANNEL "Compile support for SCHANNEL debug" ${DEFAULT_DEBUG_OPTION}) +if (WITH_DEBUG_SCHANNEL) + winpr_definition_add("-DWITH_DEBUG_SCHANNEL") +endif() + +option(WITH_KRB5 "Compile support for kerberos authentication." ${KRB5_DEFAULT}) +if (WITH_KRB5) + find_package(KRB5 REQUIRED) + + list(APPEND ${MODULE_PREFIX}_KERBEROS_SRCS + Kerberos/krb5glue.h) + + winpr_include_directory_add(${KRB5_INCLUDEDIR}) + winpr_include_directory_add(${KRB5_INCLUDE_DIRS}) + winpr_library_add_private(${KRB5_LIBRARIES}) + winpr_library_add_private(${KRB5_LIBRARY}) + winpr_library_add_compile_options(${KRB5_CFLAGS}) + winpr_library_add_link_options(${KRB5_LDFLAGS}) + winpr_library_add_link_directory(${KRB5_LIBRARY_DIRS}) + + winpr_definition_add("-DWITH_KRB5") + + if(KRB5_FLAVOUR STREQUAL "MIT") + winpr_definition_add("-DWITH_KRB5_MIT") + list(APPEND ${MODULE_PREFIX}_KERBEROS_SRCS + Kerberos/krb5glue_mit.c + ) + elseif(KRB5_FLAVOUR STREQUAL "Heimdal") + winpr_definition_add("-DWITH_KRB5_HEIMDAL") + list(APPEND ${MODULE_PREFIX}_KERBEROS_SRCS + Kerberos/krb5glue_heimdal.c + ) + else() + message(WARNING "Kerberos version not detected") + endif() + + include(CMakeDependentOption) + CMAKE_DEPENDENT_OPTION(WITH_KRB5_NO_NTLM_FALLBACK "Do not fall back to NTLM if no kerberos ticket available" OFF "WITH_KRB5" OFF) + if (WITH_KRB5_NO_NTLM_FALLBACK) + add_definitions("-DWITH_KRB5_NO_NTLM_FALLBACK") + endif() +endif() + +winpr_module_add(${${MODULE_PREFIX}_CREDSSP_SRCS} + ${${MODULE_PREFIX}_NTLM_SRCS} + ${${MODULE_PREFIX}_KERBEROS_SRCS} + ${${MODULE_PREFIX}_NEGOTIATE_SRCS} + ${${MODULE_PREFIX}_SCHANNEL_SRCS} + ${${MODULE_PREFIX}_SRCS}) + +if(OPENSSL_FOUND) + winpr_include_directory_add(${OPENSSL_INCLUDE_DIR}) + winpr_library_add_private(${OPENSSL_LIBRARIES}) +endif() + +if(MBEDTLS_FOUND) + winpr_include_directory_add(${MBEDTLS_INCLUDE_DIR}) + winpr_library_add_private(${MBEDTLS_LIBRARIES}) +endif() + +if(WIN32) + winpr_library_add_public(ws2_32) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/sspi/CredSSP/credssp.c b/winpr/libwinpr/sspi/CredSSP/credssp.c new file mode 100644 index 0000000..5581555 --- /dev/null +++ b/winpr/libwinpr/sspi/CredSSP/credssp.c @@ -0,0 +1,322 @@ +/** + * WinPR: Windows Portable Runtime + * Credential Security Support Provider (CredSSP) + * + * Copyright 2010-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "credssp.h" + +#include "../sspi.h" +#include "../../log.h" + +#define TAG WINPR_TAG("sspi.CredSSP") + +static const char* CREDSSP_PACKAGE_NAME = "CredSSP"; + +static SECURITY_STATUS SEC_ENTRY credssp_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + CREDSSP_CONTEXT* context = NULL; + SSPI_CREDENTIALS* credentials = NULL; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = (CREDSSP_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + { + union + { + const void* cpv; + void* pv; + } cnv; + context = credssp_ContextNew(); + + if (!context) + return SEC_E_INSUFFICIENT_MEMORY; + + credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + + if (!credentials) + { + credssp_ContextFree(context); + return SEC_E_INVALID_HANDLE; + } + + sspi_SecureHandleSetLowerPointer(phNewContext, context); + + cnv.cpv = CREDSSP_PACKAGE_NAME; + sspi_SecureHandleSetUpperPointer(phNewContext, cnv.pv); + } + + return SEC_E_OK; +} + +CREDSSP_CONTEXT* credssp_ContextNew(void) +{ + CREDSSP_CONTEXT* context = NULL; + context = (CREDSSP_CONTEXT*)calloc(1, sizeof(CREDSSP_CONTEXT)); + + if (!context) + return NULL; + + return context; +} + +void credssp_ContextFree(CREDSSP_CONTEXT* context) +{ + free(context); +} + +static SECURITY_STATUS SEC_ENTRY credssp_QueryContextAttributes(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + if (!phContext) + return SEC_E_INVALID_HANDLE; + + if (!pBuffer) + return SEC_E_INSUFFICIENT_MEMORY; + + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SSPI_CREDENTIALS* credentials = NULL; + SEC_WINNT_AUTH_IDENTITY* identity = NULL; + + if (fCredentialUse == SECPKG_CRED_OUTBOUND) + { + union + { + const void* cpv; + void* pv; + } cnv; + credentials = sspi_CredentialsNew(); + + if (!credentials) + return SEC_E_INSUFFICIENT_MEMORY; + + identity = (SEC_WINNT_AUTH_IDENTITY*)pAuthData; + CopyMemory(&(credentials->identity), identity, sizeof(SEC_WINNT_AUTH_IDENTITY)); + sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); + + cnv.cpv = CREDSSP_PACKAGE_NAME; + sspi_SecureHandleSetUpperPointer(phCredential, cnv.pv); + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + if (ulAttribute == SECPKG_CRED_ATTR_NAMES) + { + SSPI_CREDENTIALS* credentials = + (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + + if (!credentials) + return SEC_E_INVALID_HANDLE; + + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_FreeCredentialsHandle(PCredHandle phCredential) +{ + SSPI_CREDENTIALS* credentials = NULL; + + if (!phCredential) + return SEC_E_INVALID_HANDLE; + + credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + + if (!credentials) + return SEC_E_INVALID_HANDLE; + + sspi_CredentialsFree(credentials); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY credssp_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + ULONG* pfQOP) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY credssp_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +const SecurityFunctionTableA CREDSSP_SecurityFunctionTableA = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + credssp_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */ + credssp_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */ + credssp_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + credssp_InitializeSecurityContextA, /* InitializeSecurityContext */ + NULL, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + NULL, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + credssp_QueryContextAttributes, /* QueryContextAttributes */ + NULL, /* ImpersonateSecurityContext */ + NULL, /* RevertSecurityContext */ + credssp_MakeSignature, /* MakeSignature */ + credssp_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + credssp_EncryptMessage, /* EncryptMessage */ + credssp_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributes */ + NULL, /* SetCredentialsAttributes */ +}; + +const SecurityFunctionTableW CREDSSP_SecurityFunctionTableW = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + credssp_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */ + credssp_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */ + credssp_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + credssp_InitializeSecurityContextW, /* InitializeSecurityContext */ + NULL, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + NULL, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + credssp_QueryContextAttributes, /* QueryContextAttributes */ + NULL, /* ImpersonateSecurityContext */ + NULL, /* RevertSecurityContext */ + credssp_MakeSignature, /* MakeSignature */ + credssp_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + credssp_EncryptMessage, /* EncryptMessage */ + credssp_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributes */ + NULL, /* SetCredentialsAttributes */ +}; + +const SecPkgInfoA CREDSSP_SecPkgInfoA = { + 0x000110733, /* fCapabilities */ + 1, /* wVersion */ + 0xFFFF, /* wRPCID */ + 0x000090A8, /* cbMaxToken */ + "CREDSSP", /* Name */ + "Microsoft CredSSP Security Provider" /* Comment */ +}; + +static WCHAR CREDSSP_SecPkgInfoW_NameBuffer[128] = { 0 }; +static WCHAR CREDSSP_SecPkgInfoW_CommentBuffer[128] = { 0 }; + +const SecPkgInfoW CREDSSP_SecPkgInfoW = { + 0x000110733, /* fCapabilities */ + 1, /* wVersion */ + 0xFFFF, /* wRPCID */ + 0x000090A8, /* cbMaxToken */ + CREDSSP_SecPkgInfoW_NameBuffer, /* Name */ + CREDSSP_SecPkgInfoW_CommentBuffer /* Comment */ +}; + +BOOL CREDSSP_init(void) +{ + InitializeConstWCharFromUtf8(CREDSSP_SecPkgInfoA.Name, CREDSSP_SecPkgInfoW_NameBuffer, + ARRAYSIZE(CREDSSP_SecPkgInfoW_NameBuffer)); + InitializeConstWCharFromUtf8(CREDSSP_SecPkgInfoA.Comment, CREDSSP_SecPkgInfoW_CommentBuffer, + ARRAYSIZE(CREDSSP_SecPkgInfoW_CommentBuffer)); + return TRUE; +} diff --git a/winpr/libwinpr/sspi/CredSSP/credssp.h b/winpr/libwinpr/sspi/CredSSP/credssp.h new file mode 100644 index 0000000..39c8fe9 --- /dev/null +++ b/winpr/libwinpr/sspi/CredSSP/credssp.h @@ -0,0 +1,42 @@ +/** + * WinPR: Windows Portable Runtime + * Credential Security Support Provider (CredSSP) + * + * Copyright 2010-2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_CREDSSP_PRIVATE_H +#define WINPR_SSPI_CREDSSP_PRIVATE_H + +#include + +#include "../sspi.h" + +typedef struct +{ + BOOL server; +} CREDSSP_CONTEXT; + +CREDSSP_CONTEXT* credssp_ContextNew(void); +void credssp_ContextFree(CREDSSP_CONTEXT* context); + +extern const SecPkgInfoA CREDSSP_SecPkgInfoA; +extern const SecPkgInfoW CREDSSP_SecPkgInfoW; +extern const SecurityFunctionTableA CREDSSP_SecurityFunctionTableA; +extern const SecurityFunctionTableW CREDSSP_SecurityFunctionTableW; + +BOOL CREDSSP_init(void); + +#endif /* WINPR_SSPI_CREDSSP_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/Kerberos/kerberos.c b/winpr/libwinpr/sspi/Kerberos/kerberos.c new file mode 100644 index 0000000..b7b71f9 --- /dev/null +++ b/winpr/libwinpr/sspi/Kerberos/kerberos.c @@ -0,0 +1,1899 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Kerberos Auth Protocol + * + * Copyright 2015 ANSSI, Author Thomas Calderon + * Copyright 2017 Dorian Ducournau + * Copyright 2022 David Fort + * Copyright 2022 Isaac Klein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kerberos.h" + +#ifdef WITH_KRB5_MIT +#include "krb5glue.h" +#include +#endif + +#ifdef WITH_KRB5_HEIMDAL +#include "krb5glue.h" +#include +#endif + +#include "../sspi.h" +#include "../../log.h" +#define TAG WINPR_TAG("sspi.Kerberos") + +#define KRB_TGT_REQ 16 +#define KRB_TGT_REP 17 + +const SecPkgInfoA KERBEROS_SecPkgInfoA = { + 0x000F3BBF, /* fCapabilities */ + 1, /* wVersion */ + 0x0010, /* wRPCID */ + 0x0000BB80, /* cbMaxToken : 48k bytes maximum for Windows Server 2012 */ + "Kerberos", /* Name */ + "Kerberos Security Package" /* Comment */ +}; + +static WCHAR KERBEROS_SecPkgInfoW_NameBuffer[32] = { 0 }; +static WCHAR KERBEROS_SecPkgInfoW_CommentBuffer[32] = { 0 }; + +const SecPkgInfoW KERBEROS_SecPkgInfoW = { + 0x000F3BBF, /* fCapabilities */ + 1, /* wVersion */ + 0x0010, /* wRPCID */ + 0x0000BB80, /* cbMaxToken : 48k bytes maximum for Windows Server 2012 */ + KERBEROS_SecPkgInfoW_NameBuffer, /* Name */ + KERBEROS_SecPkgInfoW_CommentBuffer /* Comment */ +}; + +#ifdef WITH_KRB5 + +enum KERBEROS_STATE +{ + KERBEROS_STATE_INITIAL, + KERBEROS_STATE_TGT_REQ, + KERBEROS_STATE_TGT_REP, + KERBEROS_STATE_AP_REQ, + KERBEROS_STATE_AP_REP, + KERBEROS_STATE_FINAL +}; + +struct s_KRB_CONTEXT +{ + enum KERBEROS_STATE state; + krb5_context ctx; + krb5_auth_context auth_ctx; + BOOL acceptor; + uint32_t flags; + uint64_t local_seq; + uint64_t remote_seq; + struct krb5glue_keyset keyset; + BOOL u2u; +}; + +typedef struct KRB_CREDENTIALS_st +{ + char* kdc_url; + krb5_ccache ccache; + krb5_keytab keytab; + krb5_keytab client_keytab; + BOOL own_ccache; /**< Whether we created ccache, and must destroy it after use. */ +} KRB_CREDENTIALS; + +static const WinPrAsn1_OID kerberos_OID = { 9, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; +static const WinPrAsn1_OID kerberos_u2u_OID = { 10, + (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03" }; + +#define krb_log_exec(fkt, ctx, ...) \ + kerberos_log_msg(ctx, fkt(ctx, ##__VA_ARGS__), #fkt, __FILE__, __func__, __LINE__) +#define krb_log_exec_ptr(fkt, ctx, ...) \ + kerberos_log_msg(*ctx, fkt(ctx, ##__VA_ARGS__), #fkt, __FILE__, __func__, __LINE__) +static krb5_error_code kerberos_log_msg(krb5_context ctx, krb5_error_code code, const char* what, + const char* file, const char* fkt, size_t line) +{ + switch (code) + { + case 0: + case KRB5_KT_END: + break; + default: + { + const DWORD level = WLOG_ERROR; + + wLog* log = WLog_Get(TAG); + if (WLog_IsLevelActive(log, level)) + { + const char* msg = krb5_get_error_message(ctx, code); + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "%s (%s [%d])", + what, msg, code); + krb5_free_error_message(ctx, msg); + } + } + break; + } + return code; +} + +static void kerberos_ContextFree(KRB_CONTEXT* ctx, BOOL allocated) +{ + if (ctx && ctx->ctx) + { + krb5glue_keys_free(ctx->ctx, &ctx->keyset); + + if (ctx->auth_ctx) + krb5_auth_con_free(ctx->ctx, ctx->auth_ctx); + + krb5_free_context(ctx->ctx); + } + if (allocated) + free(ctx); +} + +static KRB_CONTEXT* kerberos_ContextNew(void) +{ + KRB_CONTEXT* context = NULL; + + context = (KRB_CONTEXT*)calloc(1, sizeof(KRB_CONTEXT)); + if (!context) + return NULL; + + return context; +} + +static krb5_error_code krb5_prompter(krb5_context context, void* data, const char* name, + const char* banner, int num_prompts, krb5_prompt prompts[]) +{ + for (int i = 0; i < num_prompts; i++) + { + krb5_prompt_type type = krb5glue_get_prompt_type(context, prompts, i); + if (type && (type == KRB5_PROMPT_TYPE_PREAUTH || type == KRB5_PROMPT_TYPE_PASSWORD) && data) + { + prompts[i].reply->data = _strdup((const char*)data); + prompts[i].reply->length = strlen((const char*)data); + } + } + return 0; +} + +static INLINE krb5glue_key get_key(struct krb5glue_keyset* keyset) +{ + return keyset->acceptor_key ? keyset->acceptor_key + : keyset->initiator_key ? keyset->initiator_key + : keyset->session_key; +} + +#endif /* WITH_KRB5 */ + +static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ +#ifdef WITH_KRB5 + SEC_WINPR_KERBEROS_SETTINGS* krb_settings = NULL; + KRB_CREDENTIALS* credentials = NULL; + krb5_context ctx = NULL; + krb5_ccache ccache = NULL; + krb5_keytab keytab = NULL; + krb5_principal principal = NULL; + char* domain = NULL; + char* username = NULL; + char* password = NULL; + BOOL own_ccache = FALSE; + const char* const default_ccache_type = "MEMORY"; + + if (pAuthData) + { + UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData); + + if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED) + krb_settings = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->kerberosSettings); + + if (!sspi_CopyAuthIdentityFieldsA((const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData, &username, + &domain, &password)) + { + WLog_ERR(TAG, "Failed to copy auth identity fields"); + goto cleanup; + } + + if (!pszPrincipal) + pszPrincipal = username; + } + + if (krb_log_exec_ptr(krb5_init_context, &ctx)) + goto cleanup; + + if (domain) + { + char* udomain = _strdup(domain); + if (!udomain) + goto cleanup; + + CharUpperA(udomain); + /* Will use domain if realm is not specified in username */ + krb5_error_code rv = krb_log_exec(krb5_set_default_realm, ctx, udomain); + free(udomain); + + if (rv) + goto cleanup; + } + + if (pszPrincipal) + { + char* cpszPrincipal = _strdup(pszPrincipal); + if (!cpszPrincipal) + goto cleanup; + + /* Find realm component if included and convert to uppercase */ + char* p = strchr(cpszPrincipal, '@'); + if (p) + CharUpperA(p); + + krb5_error_code rv = krb_log_exec(krb5_parse_name, ctx, cpszPrincipal, &principal); + free(cpszPrincipal); + + if (rv) + goto cleanup; + } + + if (krb_settings && krb_settings->cache) + { + if ((krb_log_exec(krb5_cc_set_default_name, ctx, krb_settings->cache))) + goto cleanup; + } + else + own_ccache = TRUE; + + if (principal) + { + /* Use the default cache if it's initialized with the right principal */ + if (krb5_cc_cache_match(ctx, principal, &ccache) == KRB5_CC_NOTFOUND) + { + if (own_ccache) + { + if (krb_log_exec(krb5_cc_new_unique, ctx, default_ccache_type, 0, &ccache)) + goto cleanup; + } + else + { + if (krb_log_exec(krb5_cc_resolve, ctx, krb_settings->cache, &ccache)) + goto cleanup; + } + + if (krb_log_exec(krb5_cc_initialize, ctx, ccache, principal)) + goto cleanup; + } + else + own_ccache = FALSE; + } + else if (fCredentialUse & SECPKG_CRED_OUTBOUND) + { + /* Use the default cache with it's default principal */ + if (krb_log_exec(krb5_cc_default, ctx, &ccache)) + goto cleanup; + if (krb_log_exec(krb5_cc_get_principal, ctx, ccache, &principal)) + goto cleanup; + own_ccache = FALSE; + } + else + { + if (own_ccache) + { + if (krb_log_exec(krb5_cc_new_unique, ctx, default_ccache_type, 0, &ccache)) + goto cleanup; + } + else + { + if (krb_log_exec(krb5_cc_resolve, ctx, krb_settings->cache, &ccache)) + goto cleanup; + } + } + + if (krb_settings && krb_settings->keytab) + { + if (krb_log_exec(krb5_kt_resolve, ctx, krb_settings->keytab, &keytab)) + goto cleanup; + } + else + { + if (fCredentialUse & SECPKG_CRED_INBOUND) + if (krb_log_exec(krb5_kt_default, ctx, &keytab)) + goto cleanup; + } + + /* Get initial credentials if required */ + if (fCredentialUse & SECPKG_CRED_OUTBOUND) + { + if (krb_log_exec(krb5glue_get_init_creds, ctx, principal, ccache, krb5_prompter, password, + krb_settings)) + goto cleanup; + } + + credentials = calloc(1, sizeof(KRB_CREDENTIALS)); + if (!credentials) + goto cleanup; + credentials->ccache = ccache; + credentials->keytab = keytab; + credentials->own_ccache = own_ccache; + +cleanup: + + free(domain); + free(username); + free(password); + + if (principal) + krb5_free_principal(ctx, principal); + if (ctx) + { + if (!credentials) + { + if (ccache) + { + if (own_ccache) + krb5_cc_destroy(ctx, ccache); + else + krb5_cc_close(ctx, ccache); + } + if (keytab) + krb5_kt_close(ctx, keytab); + } + krb5_free_context(ctx); + } + + /* If we managed to get credentials set the output */ + if (credentials) + { + sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); + sspi_SecureHandleSetUpperPointer(phCredential, (void*)KERBEROS_SSP_NAME); + return SEC_E_OK; + } + + return SEC_E_NO_CREDENTIALS; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + char* principal = NULL; + char* package = NULL; + + if (pszPrincipal) + { + principal = ConvertWCharToUtf8Alloc(pszPrincipal, NULL); + if (!principal) + return SEC_E_INSUFFICIENT_MEMORY; + } + if (pszPackage) + { + package = ConvertWCharToUtf8Alloc(pszPackage, NULL); + if (!package) + return SEC_E_INSUFFICIENT_MEMORY; + } + + status = + kerberos_AcquireCredentialsHandleA(principal, package, fCredentialUse, pvLogonID, pAuthData, + pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry); + + if (principal) + free(principal); + if (package) + free(package); + + return status; +} + +static SECURITY_STATUS SEC_ENTRY kerberos_FreeCredentialsHandle(PCredHandle phCredential) +{ +#ifdef WITH_KRB5 + KRB_CREDENTIALS* credentials = NULL; + krb5_context ctx = NULL; + + credentials = sspi_SecureHandleGetLowerPointer(phCredential); + if (!credentials) + return SEC_E_INVALID_HANDLE; + + if (krb5_init_context(&ctx)) + return SEC_E_INTERNAL_ERROR; + + free(credentials->kdc_url); + + if (credentials->ccache) + { + if (credentials->own_ccache) + krb5_cc_destroy(ctx, credentials->ccache); + else + krb5_cc_close(ctx, credentials->ccache); + } + if (credentials->keytab) + krb5_kt_close(ctx, credentials->keytab); + + krb5_free_context(ctx); + + free(credentials); + + sspi_SecureHandleInvalidate(phCredential); + return SEC_E_OK; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ +#ifdef WITH_KRB5 + if (ulAttribute == SECPKG_CRED_ATTR_NAMES) + { + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute); + return SEC_E_UNSUPPORTED_FUNCTION; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + return kerberos_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer); +} + +#ifdef WITH_KRB5 + +static BOOL kerberos_mk_tgt_token(SecBuffer* buf, int msg_type, char* sname, char* host, + const krb5_data* ticket) +{ + WinPrAsn1Encoder* enc = NULL; + WinPrAsn1_MemoryChunk data; + wStream s; + size_t len = 0; + sspi_gss_data token; + BOOL ret = FALSE; + + WINPR_ASSERT(buf); + + if (msg_type != KRB_TGT_REQ && msg_type != KRB_TGT_REP) + return FALSE; + if (msg_type == KRB_TGT_REP && !ticket) + return FALSE; + + enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); + if (!enc) + return FALSE; + + /* KERB-TGT-REQUEST (SEQUENCE) */ + if (!WinPrAsn1EncSeqContainer(enc)) + goto cleanup; + + /* pvno [0] INTEGER */ + if (!WinPrAsn1EncContextualInteger(enc, 0, 5)) + goto cleanup; + + /* msg-type [1] INTEGER */ + if (!WinPrAsn1EncContextualInteger(enc, 1, msg_type)) + goto cleanup; + + if (msg_type == KRB_TGT_REQ && sname) + { + /* server-name [2] PrincipalName (SEQUENCE) */ + if (!WinPrAsn1EncContextualSeqContainer(enc, 2)) + goto cleanup; + + /* name-type [0] INTEGER */ + if (!WinPrAsn1EncContextualInteger(enc, 0, KRB5_NT_SRV_HST)) + goto cleanup; + + /* name-string [1] SEQUENCE OF GeneralString */ + if (!WinPrAsn1EncContextualSeqContainer(enc, 1)) + goto cleanup; + + if (!WinPrAsn1EncGeneralString(enc, sname)) + goto cleanup; + + if (host && !WinPrAsn1EncGeneralString(enc, host)) + goto cleanup; + + if (!WinPrAsn1EncEndContainer(enc) || !WinPrAsn1EncEndContainer(enc)) + goto cleanup; + } + else if (msg_type == KRB_TGT_REP) + { + /* ticket [2] Ticket */ + data.data = (BYTE*)ticket->data; + data.len = ticket->length; + if (!WinPrAsn1EncContextualRawContent(enc, 2, &data)) + goto cleanup; + } + + if (!WinPrAsn1EncEndContainer(enc)) + goto cleanup; + + if (!WinPrAsn1EncStreamSize(enc, &len) || len > buf->cbBuffer) + goto cleanup; + + Stream_StaticInit(&s, buf->pvBuffer, len); + if (!WinPrAsn1EncToStream(enc, &s)) + goto cleanup; + + token.data = buf->pvBuffer; + token.length = (UINT)len; + if (sspi_gss_wrap_token(buf, &kerberos_u2u_OID, + msg_type == KRB_TGT_REQ ? TOK_ID_TGT_REQ : TOK_ID_TGT_REP, &token)) + ret = TRUE; + +cleanup: + WinPrAsn1Encoder_Free(&enc); + return ret; +} + +static BOOL kerberos_rd_tgt_token(const sspi_gss_data* token, char** target, krb5_data* ticket) +{ + WinPrAsn1Decoder dec; + WinPrAsn1Decoder dec2; + BOOL error = 0; + WinPrAsn1_tagId tag = 0; + WinPrAsn1_INTEGER val = 0; + size_t len = 0; + wStream s; + char* buf = NULL; + char* str = NULL; + + WINPR_ASSERT(token); + + WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, (BYTE*)token->data, token->length); + + /* KERB-TGT-REQUEST (SEQUENCE) */ + if (!WinPrAsn1DecReadSequence(&dec, &dec2)) + return FALSE; + dec = dec2; + + /* pvno [0] INTEGER */ + if (!WinPrAsn1DecReadContextualInteger(&dec, 0, &error, &val) || val != 5) + return FALSE; + + /* msg-type [1] INTEGER */ + if (!WinPrAsn1DecReadContextualInteger(&dec, 1, &error, &val)) + return FALSE; + + if (val == KRB_TGT_REQ) + { + if (!target) + return FALSE; + *target = NULL; + + s = WinPrAsn1DecGetStream(&dec); + len = Stream_Length(&s); + if (len == 0) + return TRUE; + + buf = malloc(len); + if (!buf) + return FALSE; + + *buf = 0; + *target = buf; + + if (!WinPrAsn1DecReadContextualTag(&dec, &tag, &dec2)) + goto fail; + + if (tag == 2) + { + WinPrAsn1Decoder seq; + /* server-name [2] PrincipalName (SEQUENCE) */ + if (!WinPrAsn1DecReadSequence(&dec2, &seq)) + goto fail; + + /* name-type [0] INTEGER */ + if (!WinPrAsn1DecReadContextualInteger(&seq, 0, &error, &val)) + goto fail; + + /* name-string [1] SEQUENCE OF GeneralString */ + if (!WinPrAsn1DecReadContextualSequence(&seq, 1, &error, &dec2)) + goto fail; + + while (WinPrAsn1DecPeekTag(&dec2, &tag)) + { + if (!WinPrAsn1DecReadGeneralString(&dec2, &str)) + goto fail; + + if (buf != *target) + *buf++ = '/'; + buf = stpcpy(buf, str); + free(str); + } + + if (!WinPrAsn1DecReadContextualTag(&dec, &tag, &dec2)) + return TRUE; + } + + /* realm [3] Realm */ + if (tag != 3 || !WinPrAsn1DecReadGeneralString(&dec2, &str)) + goto fail; + + *buf++ = '@'; + strcpy(buf, str); + return TRUE; + } + else if (val == KRB_TGT_REP) + { + if (!ticket) + return FALSE; + + /* ticket [2] Ticket */ + if (!WinPrAsn1DecReadContextualTag(&dec, &tag, &dec2) || tag != 2) + return FALSE; + + s = WinPrAsn1DecGetStream(&dec2); + ticket->data = (char*)Stream_Buffer(&s); + ticket->length = Stream_Length(&s); + return TRUE; + } + else + return FALSE; + +fail: + free(buf); + if (target) + *target = NULL; + return FALSE; +} + +#endif /* WITH_KRB5 */ + +static BOOL kerberos_hash_channel_bindings(WINPR_DIGEST_CTX* md5, SEC_CHANNEL_BINDINGS* bindings) +{ + BYTE buf[4]; + + Data_Write_UINT32(buf, bindings->dwInitiatorAddrType); + if (!winpr_Digest_Update(md5, buf, 4)) + return FALSE; + + Data_Write_UINT32(buf, bindings->cbInitiatorLength); + if (!winpr_Digest_Update(md5, buf, 4)) + return FALSE; + + if (bindings->cbInitiatorLength && + !winpr_Digest_Update(md5, (BYTE*)bindings + bindings->dwInitiatorOffset, + bindings->cbInitiatorLength)) + return FALSE; + + Data_Write_UINT32(buf, bindings->dwAcceptorAddrType); + if (!winpr_Digest_Update(md5, buf, 4)) + return FALSE; + + Data_Write_UINT32(buf, bindings->cbAcceptorLength); + if (!winpr_Digest_Update(md5, buf, 4)) + return FALSE; + + if (bindings->cbAcceptorLength && + !winpr_Digest_Update(md5, (BYTE*)bindings + bindings->dwAcceptorOffset, + bindings->cbAcceptorLength)) + return FALSE; + + Data_Write_UINT32(buf, bindings->cbApplicationDataLength); + if (!winpr_Digest_Update(md5, buf, 4)) + return FALSE; + + if (bindings->cbApplicationDataLength && + !winpr_Digest_Update(md5, (BYTE*)bindings + bindings->dwApplicationDataOffset, + bindings->cbApplicationDataLength)) + return FALSE; + + return TRUE; +} + +static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, PTimeStamp ptsExpiry) +{ +#ifdef WITH_KRB5 + KRB_CREDENTIALS* credentials = NULL; + KRB_CONTEXT* context = NULL; + KRB_CONTEXT new_context = { 0 }; + PSecBuffer input_buffer = NULL; + PSecBuffer output_buffer = NULL; + PSecBuffer bindings_buffer = NULL; + WINPR_DIGEST_CTX* md5 = NULL; + char* target = NULL; + char* sname = NULL; + char* host = NULL; + krb5_data input_token = { 0 }; + krb5_data output_token = { 0 }; + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + WinPrAsn1_OID oid = { 0 }; + uint16_t tok_id = 0; + krb5_ap_rep_enc_part* reply = NULL; + krb5_flags ap_flags = AP_OPTS_USE_SUBKEY; + char cksum_contents[24] = { 0 }; + krb5_data cksum = { 0 }; + krb5_creds in_creds = { 0 }; + krb5_creds* creds = NULL; + + credentials = sspi_SecureHandleGetLowerPointer(phCredential); + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = sspi_SecureHandleGetLowerPointer(phContext); + + if (!credentials) + return SEC_E_NO_CREDENTIALS; + + if (pInput) + { + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS); + } + if (pOutput) + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (fContextReq & ISC_REQ_MUTUAL_AUTH) + ap_flags |= AP_OPTS_MUTUAL_REQUIRED; + + if (fContextReq & ISC_REQ_USE_SESSION_KEY) + ap_flags |= AP_OPTS_USE_SESSION_KEY; + + if (!context) + { + context = &new_context; + + if (krb_log_exec_ptr(krb5_init_context, &context->ctx)) + return SEC_E_INTERNAL_ERROR; + + if (fContextReq & ISC_REQ_USE_SESSION_KEY) + { + context->state = KERBEROS_STATE_TGT_REQ; + context->u2u = TRUE; + } + else + context->state = KERBEROS_STATE_AP_REQ; + } + else + { + if (!input_buffer || !sspi_gss_unwrap_token(input_buffer, &oid, &tok_id, &input_token)) + goto bad_token; + if ((context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) || + (!context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_OID))) + goto bad_token; + } + + /* Split target name into service/hostname components */ + if (pszTargetName) + { + target = _strdup(pszTargetName); + if (!target) + { + status = SEC_E_INSUFFICIENT_MEMORY; + goto cleanup; + } + host = strchr(target, '/'); + if (host) + { + *host++ = 0; + sname = target; + } + else + host = target; + } + + /* SSPI flags are compatible with GSS flags except INTEG_FLAG */ + context->flags |= (fContextReq & 0x1F); + if (fContextReq & ISC_REQ_INTEGRITY && !(fContextReq & ISC_REQ_NO_INTEGRITY)) + context->flags |= SSPI_GSS_C_INTEG_FLAG; + + switch (context->state) + { + case KERBEROS_STATE_TGT_REQ: + + if (!kerberos_mk_tgt_token(output_buffer, KRB_TGT_REQ, sname, host, NULL)) + goto cleanup; + + context->state = KERBEROS_STATE_TGT_REP; + + status = SEC_I_CONTINUE_NEEDED; + + break; + + case KERBEROS_STATE_TGT_REP: + + if (tok_id != TOK_ID_TGT_REP) + goto bad_token; + + if (!kerberos_rd_tgt_token(&input_token, NULL, &in_creds.second_ticket)) + goto bad_token; + + /* Continue to AP-REQ */ + /* fall through */ + WINPR_FALLTHROUGH + + case KERBEROS_STATE_AP_REQ: + + /* Set auth_context options */ + if (krb_log_exec(krb5_auth_con_init, context->ctx, &context->auth_ctx)) + goto cleanup; + if (krb_log_exec(krb5_auth_con_setflags, context->ctx, context->auth_ctx, + KRB5_AUTH_CONTEXT_DO_SEQUENCE | KRB5_AUTH_CONTEXT_USE_SUBKEY)) + goto cleanup; + if (krb_log_exec(krb5glue_auth_con_set_cksumtype, context->ctx, context->auth_ctx, + GSS_CHECKSUM_TYPE)) + goto cleanup; + + /* Get a service ticket */ + if (krb_log_exec(krb5_sname_to_principal, context->ctx, host, sname, KRB5_NT_SRV_HST, + &in_creds.server)) + goto cleanup; + + if (krb_log_exec(krb5_cc_get_principal, context->ctx, credentials->ccache, + &in_creds.client)) + goto cleanup; + + if (krb_log_exec(krb5_get_credentials, context->ctx, + context->u2u ? KRB5_GC_USER_USER : 0, credentials->ccache, &in_creds, + &creds)) + goto cleanup; + + /* Write the checksum (delegation not implemented) */ + cksum.data = cksum_contents; + cksum.length = sizeof(cksum_contents); + Data_Write_UINT32(cksum_contents, 16); + Data_Write_UINT32((cksum_contents + 20), context->flags); + + if (bindings_buffer) + { + SEC_CHANNEL_BINDINGS* bindings = bindings_buffer->pvBuffer; + + /* Sanity checks */ + if (bindings_buffer->cbBuffer < sizeof(SEC_CHANNEL_BINDINGS) || + (bindings->cbInitiatorLength + bindings->dwInitiatorOffset) > + bindings_buffer->cbBuffer || + (bindings->cbAcceptorLength + bindings->dwAcceptorOffset) > + bindings_buffer->cbBuffer || + (bindings->cbApplicationDataLength + bindings->dwApplicationDataOffset) > + bindings_buffer->cbBuffer) + { + status = SEC_E_BAD_BINDINGS; + goto cleanup; + } + + md5 = winpr_Digest_New(); + if (!md5) + goto cleanup; + + if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) + goto cleanup; + + if (!kerberos_hash_channel_bindings(md5, bindings)) + goto cleanup; + + if (!winpr_Digest_Final(md5, (BYTE*)cksum_contents + 4, 16)) + goto cleanup; + } + + /* Make the AP_REQ message */ + if (krb_log_exec(krb5_mk_req_extended, context->ctx, &context->auth_ctx, ap_flags, + &cksum, creds, &output_token)) + goto cleanup; + + if (!sspi_gss_wrap_token(output_buffer, + context->u2u ? &kerberos_u2u_OID : &kerberos_OID, + TOK_ID_AP_REQ, &output_token)) + goto cleanup; + + if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG) + { + if (krb_log_exec(krb5_auth_con_getlocalseqnumber, context->ctx, context->auth_ctx, + (INT32*)&context->local_seq)) + goto cleanup; + context->remote_seq ^= context->local_seq; + } + + if (krb_log_exec(krb5glue_update_keyset, context->ctx, context->auth_ctx, FALSE, + &context->keyset)) + goto cleanup; + + context->state = KERBEROS_STATE_AP_REP; + + if (context->flags & SSPI_GSS_C_MUTUAL_FLAG) + status = SEC_I_CONTINUE_NEEDED; + else + status = SEC_E_OK; + + break; + + case KERBEROS_STATE_AP_REP: + + if (tok_id == TOK_ID_AP_REP) + { + if (krb_log_exec(krb5_rd_rep, context->ctx, context->auth_ctx, &input_token, + &reply)) + goto cleanup; + krb5_free_ap_rep_enc_part(context->ctx, reply); + } + else if (tok_id == TOK_ID_ERROR) + { + krb5glue_log_error(context->ctx, &input_token, TAG); + goto cleanup; + } + else + goto bad_token; + + if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG) + { + if (krb_log_exec(krb5_auth_con_getremoteseqnumber, context->ctx, context->auth_ctx, + (INT32*)&context->remote_seq)) + goto cleanup; + } + + if (krb_log_exec(krb5glue_update_keyset, context->ctx, context->auth_ctx, FALSE, + &context->keyset)) + goto cleanup; + + context->state = KERBEROS_STATE_FINAL; + + if (output_buffer) + output_buffer->cbBuffer = 0; + status = SEC_E_OK; + + break; + + case KERBEROS_STATE_FINAL: + default: + WLog_ERR(TAG, "Kerberos in invalid state!"); + goto cleanup; + } + + /* On first call allocate a new context */ + if (new_context.ctx) + { + const KRB_CONTEXT empty = { 0 }; + + context = kerberos_ContextNew(); + if (!context) + { + status = SEC_E_INSUFFICIENT_MEMORY; + goto cleanup; + } + *context = new_context; + new_context = empty; + + sspi_SecureHandleSetLowerPointer(phNewContext, context); + sspi_SecureHandleSetUpperPointer(phNewContext, KERBEROS_SSP_NAME); + } + +cleanup: + +{ + /* second_ticket is not allocated */ + krb5_data edata = { 0 }; + in_creds.second_ticket = edata; + krb5_free_cred_contents(context->ctx, &in_creds); +} + + krb5_free_creds(context->ctx, creds); + if (output_token.data) + krb5glue_free_data_contents(context->ctx, &output_token); + + winpr_Digest_Free(md5); + + free(target); + kerberos_ContextFree(&new_context, FALSE); + + return status; + +bad_token: + status = SEC_E_INVALID_TOKEN; + goto cleanup; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif /* WITH_KRB5 */ +} + +static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + char* target_name = NULL; + + if (pszTargetName) + { + target_name = ConvertWCharToUtf8Alloc(pszTargetName, NULL); + if (!target_name) + return SEC_E_INSUFFICIENT_MEMORY; + } + + status = kerberos_InitializeSecurityContextA(phCredential, phContext, target_name, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); + + if (target_name) + free(target_name); + + return status; +} + +static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext( + PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq, + ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, + PTimeStamp ptsExpity) +{ +#ifdef WITH_KRB5 + KRB_CREDENTIALS* credentials = NULL; + KRB_CONTEXT* context = NULL; + KRB_CONTEXT new_context = { 0 }; + PSecBuffer input_buffer = NULL; + PSecBuffer output_buffer = NULL; + WinPrAsn1_OID oid = { 0 }; + uint16_t tok_id = 0; + krb5_data input_token = { 0 }; + krb5_data output_token = { 0 }; + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + krb5_flags ap_flags = 0; + krb5glue_authenticator authenticator = NULL; + char* target = NULL; + char* sname = NULL; + char* realm = NULL; + krb5_kt_cursor cur = { 0 }; + krb5_keytab_entry entry = { 0 }; + krb5_principal principal = NULL; + krb5_creds creds = { 0 }; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = sspi_SecureHandleGetLowerPointer(phContext); + credentials = sspi_SecureHandleGetLowerPointer(phCredential); + + if (pInput) + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + if (pOutput) + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!input_buffer) + return SEC_E_INVALID_TOKEN; + + if (!sspi_gss_unwrap_token(input_buffer, &oid, &tok_id, &input_token)) + return SEC_E_INVALID_TOKEN; + + if (!context) + { + context = &new_context; + + if (krb_log_exec_ptr(krb5_init_context, &context->ctx)) + return SEC_E_INTERNAL_ERROR; + + if (sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) + { + context->u2u = TRUE; + context->state = KERBEROS_STATE_TGT_REQ; + } + else if (sspi_gss_oid_compare(&oid, &kerberos_OID)) + context->state = KERBEROS_STATE_AP_REQ; + else + goto bad_token; + } + else + { + if ((context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_u2u_OID)) || + (!context->u2u && !sspi_gss_oid_compare(&oid, &kerberos_OID))) + goto bad_token; + } + + if (context->state == KERBEROS_STATE_TGT_REQ && tok_id == TOK_ID_TGT_REQ) + { + if (!kerberos_rd_tgt_token(&input_token, &target, NULL)) + goto bad_token; + + if (target) + { + if (*target != 0 && *target != '@') + sname = target; + realm = strchr(target, '@'); + if (realm) + realm++; + } + + if (krb_log_exec(krb5_parse_name_flags, context->ctx, sname ? sname : "", + KRB5_PRINCIPAL_PARSE_NO_REALM, &principal)) + goto cleanup; + + if (realm) + { + if (krb_log_exec(krb5glue_set_principal_realm, context->ctx, principal, realm)) + goto cleanup; + } + + if (krb_log_exec(krb5_kt_start_seq_get, context->ctx, credentials->keytab, &cur)) + goto cleanup; + + do + { + krb5_error_code rv = + krb_log_exec(krb5_kt_next_entry, context->ctx, credentials->keytab, &entry, &cur); + if (rv == KRB5_KT_END) + break; + if (rv != 0) + goto cleanup; + + if ((!sname || krb_log_exec(krb5_principal_compare_any_realm, context->ctx, principal, + entry.principal)) && + (!realm || + krb_log_exec(krb5_realm_compare, context->ctx, principal, entry.principal))) + break; + if (krb_log_exec(krb5glue_free_keytab_entry_contents, context->ctx, &entry)) + goto cleanup; + } while (1); + + if (krb_log_exec(krb5_kt_end_seq_get, context->ctx, credentials->keytab, &cur)) + goto cleanup; + + if (!entry.principal) + goto cleanup; + + /* Get the TGT */ + if (krb_log_exec(krb5_get_init_creds_keytab, context->ctx, &creds, entry.principal, + credentials->keytab, 0, NULL, NULL)) + goto cleanup; + + if (!kerberos_mk_tgt_token(output_buffer, KRB_TGT_REP, NULL, NULL, &creds.ticket)) + goto cleanup; + + if (krb_log_exec(krb5_auth_con_init, context->ctx, &context->auth_ctx)) + goto cleanup; + + if (krb_log_exec(krb5glue_auth_con_setuseruserkey, context->ctx, context->auth_ctx, + &krb5glue_creds_getkey(creds))) + goto cleanup; + + context->state = KERBEROS_STATE_AP_REQ; + } + else if (context->state == KERBEROS_STATE_AP_REQ && tok_id == TOK_ID_AP_REQ) + { + if (krb_log_exec(krb5_rd_req, context->ctx, &context->auth_ctx, &input_token, NULL, + credentials->keytab, &ap_flags, NULL)) + goto cleanup; + + if (krb_log_exec(krb5_auth_con_setflags, context->ctx, context->auth_ctx, + KRB5_AUTH_CONTEXT_DO_SEQUENCE | KRB5_AUTH_CONTEXT_USE_SUBKEY)) + goto cleanup; + + /* Retrieve and validate the checksum */ + if (krb_log_exec(krb5_auth_con_getauthenticator, context->ctx, context->auth_ctx, + &authenticator)) + goto cleanup; + if (!krb5glue_authenticator_validate_chksum(authenticator, GSS_CHECKSUM_TYPE, + &context->flags)) + goto bad_token; + + if (ap_flags & AP_OPTS_MUTUAL_REQUIRED && context->flags & SSPI_GSS_C_MUTUAL_FLAG) + { + if (!output_buffer) + goto bad_token; + if (krb_log_exec(krb5_mk_rep, context->ctx, context->auth_ctx, &output_token)) + goto cleanup; + if (!sspi_gss_wrap_token(output_buffer, + context->u2u ? &kerberos_u2u_OID : &kerberos_OID, + TOK_ID_AP_REP, &output_token)) + goto cleanup; + } + else + { + if (output_buffer) + output_buffer->cbBuffer = 0; + } + + *pfContextAttr = context->flags & 0x1F; + if (context->flags & SSPI_GSS_C_INTEG_FLAG) + *pfContextAttr |= ASC_RET_INTEGRITY; + + if (context->flags & SSPI_GSS_C_SEQUENCE_FLAG) + { + if (krb_log_exec(krb5_auth_con_getlocalseqnumber, context->ctx, context->auth_ctx, + (INT32*)&context->local_seq)) + goto cleanup; + if (krb_log_exec(krb5_auth_con_getremoteseqnumber, context->ctx, context->auth_ctx, + (INT32*)&context->remote_seq)) + goto cleanup; + } + + if (krb_log_exec(krb5glue_update_keyset, context->ctx, context->auth_ctx, TRUE, + &context->keyset)) + goto cleanup; + + context->state = KERBEROS_STATE_FINAL; + } + else + goto bad_token; + + /* On first call allocate new context */ + if (new_context.ctx) + { + const KRB_CONTEXT empty = { 0 }; + + context = kerberos_ContextNew(); + if (!context) + { + status = SEC_E_INSUFFICIENT_MEMORY; + goto cleanup; + } + *context = new_context; + new_context = empty; + context->acceptor = TRUE; + + sspi_SecureHandleSetLowerPointer(phNewContext, context); + sspi_SecureHandleSetUpperPointer(phNewContext, KERBEROS_SSP_NAME); + } + + if (context->state == KERBEROS_STATE_FINAL) + status = SEC_E_OK; + else + status = SEC_I_CONTINUE_NEEDED; + +cleanup: + + free(target); + if (output_token.data) + krb5glue_free_data_contents(context->ctx, &output_token); + if (entry.principal) + krb5glue_free_keytab_entry_contents(context->ctx, &entry); + + kerberos_ContextFree(&new_context, FALSE); + return status; + +bad_token: + status = SEC_E_INVALID_TOKEN; + goto cleanup; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif /* WITH_KRB5 */ +} + +static KRB_CONTEXT* get_context(PCtxtHandle phContext) +{ + if (!phContext) + return NULL; + + TCHAR* name = sspi_SecureHandleGetUpperPointer(phContext); + if (_tcscmp(KERBEROS_SSP_NAME, name) != 0) + return NULL; + return sspi_SecureHandleGetLowerPointer(phContext); +} + +static SECURITY_STATUS SEC_ENTRY kerberos_DeleteSecurityContext(PCtxtHandle phContext) +{ +#ifdef WITH_KRB5 + KRB_CONTEXT* context = get_context(phContext); + if (!context) + return SEC_E_INVALID_HANDLE; + + kerberos_ContextFree(context, TRUE); + + return SEC_E_OK; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ +#ifdef WITH_KRB5 + if (!phContext) + return SEC_E_INVALID_HANDLE; + + if (!pBuffer) + return SEC_E_INSUFFICIENT_MEMORY; + + if (ulAttribute == SECPKG_ATTR_SIZES) + { + UINT header = 0; + UINT pad = 0; + UINT trailer = 0; + krb5glue_key key = NULL; + KRB_CONTEXT* context = get_context(phContext); + SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->ctx); + WINPR_ASSERT(context->auth_ctx); + + /* The MaxTokenSize by default is 12,000 bytes. This has been the default value + * since Windows 2000 SP2 and still remains in Windows 7 and Windows 2008 R2. + * For Windows Server 2012, the default value of the MaxTokenSize registry + * entry is 48,000 bytes.*/ + ContextSizes->cbMaxToken = KERBEROS_SecPkgInfoA.cbMaxToken; + ContextSizes->cbMaxSignature = 0; + ContextSizes->cbBlockSize = 1; + ContextSizes->cbSecurityTrailer = 0; + + key = get_key(&context->keyset); + + if (context->flags & SSPI_GSS_C_CONF_FLAG) + { + krb5_error_code rv = krb_log_exec(krb5glue_crypto_length, context->ctx, key, + KRB5_CRYPTO_TYPE_HEADER, &header); + if (rv) + return rv; + rv = krb_log_exec(krb5glue_crypto_length, context->ctx, key, KRB5_CRYPTO_TYPE_PADDING, + &pad); + if (rv) + return rv; + rv = krb_log_exec(krb5glue_crypto_length, context->ctx, key, KRB5_CRYPTO_TYPE_TRAILER, + &trailer); + if (rv) + return rv; + /* GSS header (= 16 bytes) + encrypted header = 32 bytes */ + ContextSizes->cbSecurityTrailer = header + pad + trailer + 32; + } + if (context->flags & SSPI_GSS_C_INTEG_FLAG) + { + krb5_error_code rv = + krb_log_exec(krb5glue_crypto_length, context->ctx, key, KRB5_CRYPTO_TYPE_CHECKSUM, + &ContextSizes->cbMaxSignature); + if (rv) + return rv; + ContextSizes->cbMaxSignature += 16; + } + + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute); + return SEC_E_UNSUPPORTED_FUNCTION; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + return kerberos_QueryContextAttributesA(phContext, ulAttribute, pBuffer); +} + +static SECURITY_STATUS SEC_ENTRY kerberos_SetContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY kerberos_SetContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY kerberos_SetCredentialsAttributesX(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer, + BOOL unicode) +{ +#ifdef WITH_KRB5 + KRB_CREDENTIALS* credentials = NULL; + + if (!phCredential) + return SEC_E_INVALID_HANDLE; + + credentials = sspi_SecureHandleGetLowerPointer(phCredential); + + if (!credentials) + return SEC_E_INVALID_HANDLE; + + if (!pBuffer) + return SEC_E_INSUFFICIENT_MEMORY; + + if (ulAttribute == SECPKG_CRED_ATTR_KDC_PROXY_SETTINGS) + { + SecPkgCredentials_KdcProxySettingsW* kdc_settings = pBuffer; + + /* Sanity checks */ + if (cbBuffer < sizeof(SecPkgCredentials_KdcProxySettingsW) || + kdc_settings->Version != KDC_PROXY_SETTINGS_V1 || + kdc_settings->ProxyServerOffset < sizeof(SecPkgCredentials_KdcProxySettingsW) || + cbBuffer < sizeof(SecPkgCredentials_KdcProxySettingsW) + + kdc_settings->ProxyServerOffset + kdc_settings->ProxyServerLength) + return SEC_E_INVALID_TOKEN; + + if (credentials->kdc_url) + { + free(credentials->kdc_url); + credentials->kdc_url = NULL; + } + + if (kdc_settings->ProxyServerLength > 0) + { + WCHAR* proxy = (WCHAR*)((BYTE*)pBuffer + kdc_settings->ProxyServerOffset); + + credentials->kdc_url = ConvertWCharNToUtf8Alloc( + proxy, kdc_settings->ProxyServerLength / sizeof(WCHAR), NULL); + if (!credentials->kdc_url) + return SEC_E_INSUFFICIENT_MEMORY; + } + + return SEC_E_OK; + } + + return SEC_E_UNSUPPORTED_FUNCTION; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_SetCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + return kerberos_SetCredentialsAttributesX(phCredential, ulAttribute, pBuffer, cbBuffer, TRUE); +} + +static SECURITY_STATUS SEC_ENTRY kerberos_SetCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + return kerberos_SetCredentialsAttributesX(phCredential, ulAttribute, pBuffer, cbBuffer, FALSE); +} + +static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, + ULONG MessageSeqNo) +{ +#ifdef WITH_KRB5 + KRB_CONTEXT* context = get_context(phContext); + PSecBuffer sig_buffer = NULL; + PSecBuffer data_buffer = NULL; + BYTE* header = NULL; + BYTE flags = 0; + krb5glue_key key = NULL; + krb5_keyusage usage = 0; + krb5_crypto_iov encrypt_iov[] = { { KRB5_CRYPTO_TYPE_HEADER, { 0 } }, + { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_PADDING, { 0 } }, + { KRB5_CRYPTO_TYPE_TRAILER, { 0 } } }; + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (!(context->flags & SSPI_GSS_C_CONF_FLAG)) + return SEC_E_UNSUPPORTED_FUNCTION; + + sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN); + data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA); + + if (!sig_buffer || !data_buffer) + return SEC_E_INVALID_TOKEN; + + if (fQOP) + return SEC_E_QOP_NOT_SUPPORTED; + + flags |= context->acceptor ? FLAG_SENDER_IS_ACCEPTOR : 0; + flags |= FLAG_WRAP_CONFIDENTIAL; + + key = get_key(&context->keyset); + if (!key) + return SEC_E_INTERNAL_ERROR; + + flags |= context->keyset.acceptor_key == key ? FLAG_ACCEPTOR_SUBKEY : 0; + + usage = context->acceptor ? KG_USAGE_ACCEPTOR_SEAL : KG_USAGE_INITIATOR_SEAL; + + /* Set the lengths of the data (plaintext + header) */ + encrypt_iov[1].data.length = data_buffer->cbBuffer; + encrypt_iov[2].data.length = 16; + + /* Get the lengths of the header, trailer, and padding and ensure sig_buffer is large enough */ + if (krb_log_exec(krb5glue_crypto_length_iov, context->ctx, key, encrypt_iov, + ARRAYSIZE(encrypt_iov))) + return SEC_E_INTERNAL_ERROR; + if (sig_buffer->cbBuffer < + encrypt_iov[0].data.length + encrypt_iov[3].data.length + encrypt_iov[4].data.length + 32) + return SEC_E_INSUFFICIENT_MEMORY; + + /* Set up the iov array in sig_buffer */ + header = sig_buffer->pvBuffer; + encrypt_iov[2].data.data = header + 16; + encrypt_iov[3].data.data = (BYTE*)encrypt_iov[2].data.data + encrypt_iov[2].data.length; + encrypt_iov[4].data.data = (BYTE*)encrypt_iov[3].data.data + encrypt_iov[3].data.length; + encrypt_iov[0].data.data = (BYTE*)encrypt_iov[4].data.data + encrypt_iov[4].data.length; + encrypt_iov[1].data.data = data_buffer->pvBuffer; + + /* Write the GSS header with 0 in RRC */ + Data_Write_UINT16_BE(header, TOK_ID_WRAP); + header[2] = flags; + header[3] = 0xFF; + Data_Write_UINT32(header + 4, 0); + Data_Write_UINT64_BE(header + 8, (context->local_seq + MessageSeqNo)); + + /* Copy header to be encrypted */ + CopyMemory(encrypt_iov[2].data.data, header, 16); + + /* Set the correct RRC */ + Data_Write_UINT16_BE(header + 6, 16 + encrypt_iov[3].data.length + encrypt_iov[4].data.length); + + if (krb_log_exec(krb5glue_encrypt_iov, context->ctx, key, usage, encrypt_iov, + ARRAYSIZE(encrypt_iov))) + return SEC_E_INTERNAL_ERROR; + + return SEC_E_OK; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ +#ifdef WITH_KRB5 + KRB_CONTEXT* context = get_context(phContext); + PSecBuffer sig_buffer = NULL; + PSecBuffer data_buffer = NULL; + krb5glue_key key = NULL; + krb5_keyusage usage = 0; + char* header = NULL; + uint16_t tok_id = 0; + BYTE flags = 0; + uint16_t ec = 0; + uint16_t rrc = 0; + uint64_t seq_no = 0; + krb5_crypto_iov iov[] = { { KRB5_CRYPTO_TYPE_HEADER, { 0 } }, + { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_PADDING, { 0 } }, + { KRB5_CRYPTO_TYPE_TRAILER, { 0 } } }; + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (!(context->flags & SSPI_GSS_C_CONF_FLAG)) + return SEC_E_UNSUPPORTED_FUNCTION; + + sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN); + data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA); + + if (!sig_buffer || !data_buffer || sig_buffer->cbBuffer < 16) + return SEC_E_INVALID_TOKEN; + + /* Read in header information */ + header = sig_buffer->pvBuffer; + Data_Read_UINT16_BE(header, tok_id); + flags = header[2]; + Data_Read_UINT16_BE((header + 4), ec); + Data_Read_UINT16_BE((header + 6), rrc); + Data_Read_UINT64_BE((header + 8), seq_no); + + /* Check that the header is valid */ + if (tok_id != TOK_ID_WRAP || (BYTE)header[3] != 0xFF) + return SEC_E_INVALID_TOKEN; + + if ((flags & FLAG_SENDER_IS_ACCEPTOR) == context->acceptor) + return SEC_E_INVALID_TOKEN; + + if (context->flags & ISC_REQ_SEQUENCE_DETECT && seq_no != context->remote_seq + MessageSeqNo) + return SEC_E_OUT_OF_SEQUENCE; + + if (!(flags & FLAG_WRAP_CONFIDENTIAL)) + return SEC_E_INVALID_TOKEN; + + /* We don't expect a trailer buffer; the encrypted header must be rotated */ + if (rrc < 16) + return SEC_E_INVALID_TOKEN; + + /* Find the proper key and key usage */ + key = get_key(&context->keyset); + if (!key || (flags & FLAG_ACCEPTOR_SUBKEY && context->keyset.acceptor_key != key)) + return SEC_E_INTERNAL_ERROR; + usage = context->acceptor ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL; + + /* Fill in the lengths of the iov array */ + iov[1].data.length = data_buffer->cbBuffer; + iov[2].data.length = 16; + if (krb_log_exec(krb5glue_crypto_length_iov, context->ctx, key, iov, ARRAYSIZE(iov))) + return SEC_E_INTERNAL_ERROR; + + /* We don't expect a trailer buffer; everything must be in sig_buffer */ + if (rrc != 16 + iov[3].data.length + iov[4].data.length) + return SEC_E_INVALID_TOKEN; + if (sig_buffer->cbBuffer != 16 + rrc + iov[0].data.length) + return SEC_E_INVALID_TOKEN; + + /* Locate the parts of the message */ + iov[0].data.data = header + 16 + rrc + ec; + iov[1].data.data = data_buffer->pvBuffer; + iov[2].data.data = header + 16 + ec; + iov[3].data.data = (BYTE*)iov[2].data.data + iov[2].data.length; + iov[4].data.data = (BYTE*)iov[3].data.data + iov[3].data.length; + + if (krb_log_exec(krb5glue_decrypt_iov, context->ctx, key, usage, iov, ARRAYSIZE(iov))) + return SEC_E_INTERNAL_ERROR; + + /* Validate the encrypted header */ + Data_Write_UINT16_BE(iov[2].data.data + 4, ec); + Data_Write_UINT16_BE(iov[2].data.data + 6, rrc); + if (memcmp(iov[2].data.data, header, 16) != 0) + return SEC_E_MESSAGE_ALTERED; + + *pfQOP = 0; + + return SEC_E_OK; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ +#ifdef WITH_KRB5 + KRB_CONTEXT* context = get_context(phContext); + PSecBuffer sig_buffer = NULL; + PSecBuffer data_buffer = NULL; + krb5glue_key key = NULL; + krb5_keyusage usage = 0; + char* header = NULL; + BYTE flags = 0; + krb5_crypto_iov iov[] = { { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_CHECKSUM, { 0 } } }; + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (!(context->flags & SSPI_GSS_C_INTEG_FLAG)) + return SEC_E_UNSUPPORTED_FUNCTION; + + sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN); + data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA); + + if (!sig_buffer || !data_buffer) + return SEC_E_INVALID_TOKEN; + + flags |= context->acceptor ? FLAG_SENDER_IS_ACCEPTOR : 0; + + key = get_key(&context->keyset); + if (!key) + return SEC_E_INTERNAL_ERROR; + usage = context->acceptor ? KG_USAGE_ACCEPTOR_SIGN : KG_USAGE_INITIATOR_SIGN; + + flags |= context->keyset.acceptor_key == key ? FLAG_ACCEPTOR_SUBKEY : 0; + + /* Fill in the lengths of the iov array */ + iov[0].data.length = data_buffer->cbBuffer; + iov[1].data.length = 16; + if (krb_log_exec(krb5glue_crypto_length_iov, context->ctx, key, iov, ARRAYSIZE(iov))) + return SEC_E_INTERNAL_ERROR; + + /* Ensure the buffer is big enough */ + if (sig_buffer->cbBuffer < iov[2].data.length + 16) + return SEC_E_INSUFFICIENT_MEMORY; + + /* Write the header */ + header = sig_buffer->pvBuffer; + Data_Write_UINT16_BE(header, TOK_ID_MIC); + header[2] = flags; + memset(header + 3, 0xFF, 5); + Data_Write_UINT64_BE(header + 8, (context->local_seq + MessageSeqNo)); + + /* Set up the iov array */ + iov[0].data.data = data_buffer->pvBuffer; + iov[1].data.data = header; + iov[2].data.data = header + 16; + + if (krb_log_exec(krb5glue_make_checksum_iov, context->ctx, key, usage, iov, ARRAYSIZE(iov))) + return SEC_E_INTERNAL_ERROR; + + sig_buffer->cbBuffer = iov[2].data.length + 16; + + return SEC_E_OK; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +static SECURITY_STATUS SEC_ENTRY kerberos_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ +#ifdef WITH_KRB5 + PSecBuffer sig_buffer = NULL; + PSecBuffer data_buffer = NULL; + krb5glue_key key = NULL; + krb5_keyusage usage = 0; + char* header = NULL; + BYTE flags = 0; + uint16_t tok_id = 0; + uint64_t seq_no = 0; + krb5_boolean is_valid = 0; + krb5_crypto_iov iov[] = { { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_DATA, { 0 } }, + { KRB5_CRYPTO_TYPE_CHECKSUM, { 0 } } }; + BYTE cmp_filler[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + KRB_CONTEXT* context = get_context(phContext); + if (!context) + return SEC_E_INVALID_HANDLE; + + if (!(context->flags & SSPI_GSS_C_INTEG_FLAG)) + return SEC_E_UNSUPPORTED_FUNCTION; + + sig_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_TOKEN); + data_buffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA); + + if (!sig_buffer || !data_buffer || sig_buffer->cbBuffer < 16) + return SEC_E_INVALID_TOKEN; + + /* Read in header info */ + header = sig_buffer->pvBuffer; + Data_Read_UINT16_BE(header, tok_id); + flags = header[2]; + Data_Read_UINT64_BE((header + 8), seq_no); + + /* Validate header */ + if (tok_id != TOK_ID_MIC) + return SEC_E_INVALID_TOKEN; + + if ((flags & FLAG_SENDER_IS_ACCEPTOR) == context->acceptor || flags & FLAG_WRAP_CONFIDENTIAL) + return SEC_E_INVALID_TOKEN; + + if (memcmp(header + 3, cmp_filler, sizeof(cmp_filler))) + return SEC_E_INVALID_TOKEN; + + if (context->flags & ISC_REQ_SEQUENCE_DETECT && seq_no != context->remote_seq + MessageSeqNo) + return SEC_E_OUT_OF_SEQUENCE; + + /* Find the proper key and usage */ + key = get_key(&context->keyset); + if (!key || (flags & FLAG_ACCEPTOR_SUBKEY && context->keyset.acceptor_key != key)) + return SEC_E_INTERNAL_ERROR; + usage = context->acceptor ? KG_USAGE_INITIATOR_SIGN : KG_USAGE_ACCEPTOR_SIGN; + + /* Fill in the iov array lengths */ + iov[0].data.length = data_buffer->cbBuffer; + iov[1].data.length = 16; + if (krb_log_exec(krb5glue_crypto_length_iov, context->ctx, key, iov, ARRAYSIZE(iov))) + return SEC_E_INTERNAL_ERROR; + + if (sig_buffer->cbBuffer != iov[2].data.length + 16) + return SEC_E_INTERNAL_ERROR; + + /* Set up the iov array */ + iov[0].data.data = data_buffer->pvBuffer; + iov[1].data.data = header; + iov[2].data.data = header + 16; + + if (krb_log_exec(krb5glue_verify_checksum_iov, context->ctx, key, usage, iov, ARRAYSIZE(iov), + &is_valid)) + return SEC_E_INTERNAL_ERROR; + + if (!is_valid) + return SEC_E_MESSAGE_ALTERED; + + return SEC_E_OK; +#else + return SEC_E_UNSUPPORTED_FUNCTION; +#endif +} + +const SecurityFunctionTableA KERBEROS_SecurityFunctionTableA = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + kerberos_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */ + kerberos_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */ + kerberos_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + kerberos_InitializeSecurityContextA, /* InitializeSecurityContext */ + kerberos_AcceptSecurityContext, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + kerberos_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + kerberos_QueryContextAttributesA, /* QueryContextAttributes */ + NULL, /* ImpersonateSecurityContext */ + NULL, /* RevertSecurityContext */ + kerberos_MakeSignature, /* MakeSignature */ + kerberos_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + kerberos_EncryptMessage, /* EncryptMessage */ + kerberos_DecryptMessage, /* DecryptMessage */ + kerberos_SetContextAttributesA, /* SetContextAttributes */ + kerberos_SetCredentialsAttributesA, /* SetCredentialsAttributes */ +}; + +const SecurityFunctionTableW KERBEROS_SecurityFunctionTableW = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + kerberos_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */ + kerberos_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */ + kerberos_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + kerberos_InitializeSecurityContextW, /* InitializeSecurityContext */ + kerberos_AcceptSecurityContext, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + kerberos_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + kerberos_QueryContextAttributesW, /* QueryContextAttributes */ + NULL, /* ImpersonateSecurityContext */ + NULL, /* RevertSecurityContext */ + kerberos_MakeSignature, /* MakeSignature */ + kerberos_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + kerberos_EncryptMessage, /* EncryptMessage */ + kerberos_DecryptMessage, /* DecryptMessage */ + kerberos_SetContextAttributesW, /* SetContextAttributes */ + kerberos_SetCredentialsAttributesW, /* SetCredentialsAttributes */ +}; + +BOOL KERBEROS_init(void) +{ + InitializeConstWCharFromUtf8(KERBEROS_SecPkgInfoA.Name, KERBEROS_SecPkgInfoW_NameBuffer, + ARRAYSIZE(KERBEROS_SecPkgInfoW_NameBuffer)); + InitializeConstWCharFromUtf8(KERBEROS_SecPkgInfoA.Comment, KERBEROS_SecPkgInfoW_CommentBuffer, + ARRAYSIZE(KERBEROS_SecPkgInfoW_CommentBuffer)); + return TRUE; +} diff --git a/winpr/libwinpr/sspi/Kerberos/kerberos.h b/winpr/libwinpr/sspi/Kerberos/kerberos.h new file mode 100644 index 0000000..aa4b86d --- /dev/null +++ b/winpr/libwinpr/sspi/Kerberos/kerberos.h @@ -0,0 +1,39 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Kerberos Auth Protocol + * + * Copyright 2015 ANSSI, Author Thomas Calderon + * Copyright 2017 Dorian Ducournau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_KERBEROS_PRIVATE_H +#define WINPR_SSPI_KERBEROS_PRIVATE_H + +#include +#include + +#include "../sspi.h" +#include "../../log.h" + +typedef struct s_KRB_CONTEXT KRB_CONTEXT; + +extern const SecPkgInfoA KERBEROS_SecPkgInfoA; +extern const SecPkgInfoW KERBEROS_SecPkgInfoW; +extern const SecurityFunctionTableA KERBEROS_SecurityFunctionTableA; +extern const SecurityFunctionTableW KERBEROS_SecurityFunctionTableW; + +BOOL KERBEROS_init(void); + +#endif /* WINPR_SSPI_KERBEROS_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/Kerberos/krb5glue.h b/winpr/libwinpr/sspi/Kerberos/krb5glue.h new file mode 100644 index 0000000..2874688 --- /dev/null +++ b/winpr/libwinpr/sspi/Kerberos/krb5glue.h @@ -0,0 +1,104 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Kerberos Auth Protocol + * + * Copyright 2022 Isaac Klein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_KERBEROS_GLUE_PRIVATE_H +#define WINPR_SSPI_KERBEROS_GLUE_PRIVATE_H + +#include +#include + +#include + +#if defined(WITH_KRB5_MIT) +typedef krb5_key krb5glue_key; +typedef krb5_authenticator* krb5glue_authenticator; + +#define krb5glue_crypto_length(ctx, key, type, size) \ + krb5_c_crypto_length(ctx, krb5_k_key_enctype(ctx, key), type, size) +#define krb5glue_crypto_length_iov(ctx, key, iov, size) \ + krb5_c_crypto_length_iov(ctx, krb5_k_key_enctype(ctx, key), iov, size) +#define krb5glue_encrypt_iov(ctx, key, usage, iov, size) \ + krb5_k_encrypt_iov(ctx, key, usage, NULL, iov, size) +#define krb5glue_decrypt_iov(ctx, key, usage, iov, size) \ + krb5_k_decrypt_iov(ctx, key, usage, NULL, iov, size) +#define krb5glue_make_checksum_iov(ctx, key, usage, iov, size) \ + krb5_k_make_checksum_iov(ctx, 0, key, usage, iov, size) +#define krb5glue_verify_checksum_iov(ctx, key, usage, iov, size, is_valid) \ + krb5_k_verify_checksum_iov(ctx, 0, key, usage, iov, size, is_valid) +#define krb5glue_auth_con_set_cksumtype(ctx, auth_ctx, cksumtype) \ + krb5_auth_con_set_req_cksumtype(ctx, auth_ctx, cksumtype) +#define krb5glue_set_principal_realm(ctx, principal, realm) \ + krb5_set_principal_realm(ctx, principal, realm) +#define krb5glue_free_keytab_entry_contents(ctx, entry) krb5_free_keytab_entry_contents(ctx, entry) +#define krb5glue_auth_con_setuseruserkey(ctx, auth_ctx, keytab) \ + krb5_auth_con_setuseruserkey(ctx, auth_ctx, keytab) +#define krb5glue_free_data_contents(ctx, data) krb5_free_data_contents(ctx, data) +krb5_prompt_type krb5glue_get_prompt_type(krb5_context ctx, krb5_prompt prompts[], int index); + +#define krb5glue_creds_getkey(creds) creds.keyblock + +#elif defined(WITH_KRB5_HEIMDAL) +typedef krb5_crypto krb5glue_key; +typedef krb5_authenticator krb5glue_authenticator; + +krb5_error_code krb5glue_crypto_length(krb5_context ctx, krb5glue_key key, int type, + unsigned int* size); +#define krb5glue_crypto_length_iov(ctx, key, iov, size) krb5_crypto_length_iov(ctx, key, iov, size) +#define krb5glue_encrypt_iov(ctx, key, usage, iov, size) \ + krb5_encrypt_iov_ivec(ctx, key, usage, iov, size, NULL) +#define krb5glue_decrypt_iov(ctx, key, usage, iov, size) \ + krb5_decrypt_iov_ivec(ctx, key, usage, iov, size, NULL) +#define krb5glue_make_checksum_iov(ctx, key, usage, iov, size) \ + krb5_create_checksum_iov(ctx, key, usage, iov, size, NULL) +krb5_error_code krb5glue_verify_checksum_iov(krb5_context ctx, krb5glue_key key, unsigned usage, + krb5_crypto_iov* iov, unsigned int iov_size, + krb5_boolean* is_valid); +#define krb5glue_auth_con_set_cksumtype(ctx, auth_ctx, cksumtype) \ + krb5_auth_con_setcksumtype(ctx, auth_ctx, cksumtype) +#define krb5glue_set_principal_realm(ctx, principal, realm) \ + krb5_principal_set_realm(ctx, principal, realm) +#define krb5glue_free_keytab_entry_contents(ctx, entry) krb5_kt_free_entry(ctx, entry) +#define krb5glue_auth_con_setuseruserkey(ctx, auth_ctx, keytab) \ + krb5_auth_con_setuserkey(ctx, auth_ctx, keytab) +#define krb5glue_free_data_contents(ctx, data) krb5_data_free(data) +#define krb5glue_get_prompt_type(ctx, prompts, index) prompts[index].type + +#define krb5glue_creds_getkey(creds) creds.session +#else +#error "Missing implementation for KRB5 provider" +#endif + +struct krb5glue_keyset +{ + krb5glue_key session_key; + krb5glue_key initiator_key; + krb5glue_key acceptor_key; +}; + +void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset); +krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor, + struct krb5glue_keyset* keyset); +krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag); +BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype, + uint32_t* flags); +krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache, + krb5_prompter_fct prompter, char* password, + SEC_WINPR_KERBEROS_SETTINGS* krb_settings); + +#endif /* WINPR_SSPI_KERBEROS_GLUE_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c b/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c new file mode 100644 index 0000000..8e01b55 --- /dev/null +++ b/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c @@ -0,0 +1,215 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Kerberos Auth Protocol + * + * Copyright 2022 Isaac Klein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WITH_KRB5_HEIMDAL +#error "This file must ony be included with HEIMDAL kerberos" +#endif + +#include +#include +#include +#include "krb5glue.h" + +void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset) +{ + if (!ctx || !keyset) + return; + if (keyset->session_key) + krb5_crypto_destroy(ctx, keyset->session_key); + if (keyset->initiator_key) + krb5_crypto_destroy(ctx, keyset->initiator_key); + if (keyset->acceptor_key) + krb5_crypto_destroy(ctx, keyset->acceptor_key); +} + +krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor, + struct krb5glue_keyset* keyset) +{ + krb5_keyblock* keyblock = NULL; + krb5_error_code rv = 0; + + WINPR_ASSERT(ctx); + WINPR_ASSERT(auth_ctx); + WINPR_ASSERT(keyset); + + krb5glue_keys_free(ctx, keyset); + + if (!(rv = krb5_auth_con_getkey(ctx, auth_ctx, &keyblock))) + { + krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->session_key); + krb5_free_keyblock(ctx, keyblock); + keyblock = NULL; + } + + if (acceptor) + rv = krb5_auth_con_getremotesubkey(ctx, auth_ctx, &keyblock); + else + rv = krb5_auth_con_getlocalsubkey(ctx, auth_ctx, &keyblock); + + if (!rv && keyblock) + { + krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->initiator_key); + krb5_free_keyblock(ctx, keyblock); + keyblock = NULL; + } + + if (acceptor) + rv = krb5_auth_con_getlocalsubkey(ctx, auth_ctx, &keyblock); + else + rv = krb5_auth_con_getremotesubkey(ctx, auth_ctx, &keyblock); + + if (!rv && keyblock) + { + krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->acceptor_key); + krb5_free_keyblock(ctx, keyblock); + } + + return rv; +} + +krb5_error_code krb5glue_verify_checksum_iov(krb5_context ctx, krb5glue_key key, unsigned usage, + krb5_crypto_iov* iov, unsigned int iov_size, + krb5_boolean* is_valid) +{ + krb5_error_code rv = 0; + + WINPR_ASSERT(ctx); + WINPR_ASSERT(key); + WINPR_ASSERT(is_valid); + + rv = krb5_verify_checksum_iov(ctx, key, usage, iov, iov_size, NULL); + *is_valid = (rv == 0); + return rv; +} + +krb5_error_code krb5glue_crypto_length(krb5_context ctx, krb5glue_key key, int type, + unsigned int* size) +{ + krb5_error_code rv = 0; + size_t s = 0; + + WINPR_ASSERT(ctx); + WINPR_ASSERT(key); + WINPR_ASSERT(size); + + rv = krb5_crypto_length(ctx, key, type, &s); + *size = (UINT)s; + return rv; +} + +krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag) +{ + krb5_error error = { 0 }; + krb5_error_code rv = 0; + + WINPR_ASSERT(ctx); + WINPR_ASSERT(msg); + WINPR_ASSERT(tag); + + if (!(rv = krb5_rd_error(ctx, msg, &error))) + { + WLog_ERR(tag, "KRB_ERROR: %" PRIx32, error.error_code); + krb5_free_error_contents(ctx, &error); + } + return rv; +} + +BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype, + uint32_t* flags) +{ + WINPR_ASSERT(flags); + + if (!authenticator || !authenticator->cksum || authenticator->cksum->cksumtype != cksumtype || + authenticator->cksum->checksum.length < 24) + return FALSE; + + const BYTE* data = authenticator->cksum->checksum.data; + Data_Read_UINT32((data + 20), (*flags)); + return TRUE; +} + +krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache, + krb5_prompter_fct prompter, char* password, + SEC_WINPR_KERBEROS_SETTINGS* krb_settings) +{ + krb5_error_code rv = 0; + krb5_deltat start_time = 0; + krb5_get_init_creds_opt* gic_opt = NULL; + krb5_init_creds_context creds_ctx = NULL; + krb5_creds creds = { 0 }; + + WINPR_ASSERT(ctx); + + do + { + if ((rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt)) != 0) + break; + + krb5_get_init_creds_opt_set_forwardable(gic_opt, 0); + krb5_get_init_creds_opt_set_proxiable(gic_opt, 0); + + if (krb_settings) + { + if (krb_settings->startTime) + start_time = krb_settings->startTime; + if (krb_settings->lifeTime) + krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime); + if (krb_settings->renewLifeTime) + krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime); + if (krb_settings->withPac) + krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE); + if (krb_settings->pkinitX509Anchors || krb_settings->pkinitX509Identity) + { + if ((rv = krb5_get_init_creds_opt_set_pkinit( + ctx, gic_opt, princ, krb_settings->pkinitX509Identity, + krb_settings->pkinitX509Anchors, NULL, NULL, 0, prompter, password, + password)) != 0) + break; + } + } + + if ((rv = krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt, + &creds_ctx)) != 0) + break; + if ((rv = krb5_init_creds_set_password(ctx, creds_ctx, password)) != 0) + break; + if (krb_settings && krb_settings->armorCache) + { + krb5_ccache armor_cc = NULL; + if ((rv = krb5_cc_resolve(ctx, krb_settings->armorCache, &armor_cc)) != 0) + break; + if ((rv = krb5_init_creds_set_fast_ccache(ctx, creds_ctx, armor_cc)) != 0) + break; + krb5_cc_close(ctx, armor_cc); + } + if ((rv = krb5_init_creds_get(ctx, creds_ctx)) != 0) + break; + if ((rv = krb5_init_creds_get_creds(ctx, creds_ctx, &creds)) != 0) + break; + if ((rv = krb5_cc_store_cred(ctx, ccache, &creds)) != 0) + break; + } while (0); + + krb5_free_cred_contents(ctx, &creds); + krb5_init_creds_free(ctx, creds_ctx); + krb5_get_init_creds_opt_free(ctx, gic_opt); + + return rv; +} + diff --git a/winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c b/winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c new file mode 100644 index 0000000..2638b22 --- /dev/null +++ b/winpr/libwinpr/sspi/Kerberos/krb5glue_mit.c @@ -0,0 +1,248 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Kerberos Auth Protocol + * + * Copyright 2022 Isaac Klein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WITH_KRB5_MIT +#error "This file must only be included with MIT kerberos" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "krb5glue.h" +#include + +static char* create_temporary_file(void) +{ + BYTE buffer[32]; + char* hex = NULL; + char* path = NULL; + + winpr_RAND(buffer, sizeof(buffer)); + hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE); + path = GetKnownSubPath(KNOWN_PATH_TEMP, hex); + free(hex); + return path; +} + +void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset) +{ + WINPR_ASSERT(ctx); + WINPR_ASSERT(keyset); + + krb5_k_free_key(ctx, keyset->session_key); + krb5_k_free_key(ctx, keyset->initiator_key); + krb5_k_free_key(ctx, keyset->acceptor_key); +} + +krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor, + struct krb5glue_keyset* keyset) +{ + WINPR_ASSERT(ctx); + WINPR_ASSERT(auth_ctx); + WINPR_ASSERT(keyset); + + krb5glue_keys_free(ctx, keyset); + krb5_auth_con_getkey_k(ctx, auth_ctx, &keyset->session_key); + if (acceptor) + { + krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->acceptor_key); + krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->initiator_key); + } + else + { + krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->initiator_key); + krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->acceptor_key); + } + return 0; +} + +krb5_prompt_type krb5glue_get_prompt_type(krb5_context ctx, krb5_prompt prompts[], int index) +{ + WINPR_ASSERT(ctx); + WINPR_ASSERT(prompts); + + krb5_prompt_type* types = krb5_get_prompt_types(ctx); + return types ? types[index] : 0; +} + +krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag) +{ + krb5_error* error = NULL; + krb5_error_code rv = 0; + + WINPR_ASSERT(ctx); + WINPR_ASSERT(msg); + WINPR_ASSERT(tag); + + if (!(rv = krb5_rd_error(ctx, msg, &error))) + { + WLog_ERR(tag, "KRB_ERROR: %s", error->text.data); + krb5_free_error(ctx, error); + } + + return rv; +} + +BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype, + uint32_t* flags) +{ + WINPR_ASSERT(flags); + + if (!authenticator || !authenticator->checksum || + authenticator->checksum->checksum_type != cksumtype || authenticator->checksum->length < 24) + return FALSE; + Data_Read_UINT32((authenticator->checksum->contents + 20), (*flags)); + return TRUE; +} + +krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache, + krb5_prompter_fct prompter, char* password, + SEC_WINPR_KERBEROS_SETTINGS* krb_settings) +{ + krb5_error_code rv = 0; + krb5_deltat start_time = 0; + krb5_get_init_creds_opt* gic_opt = NULL; + krb5_init_creds_context creds_ctx = NULL; + char* tmp_profile_path = create_temporary_file(); + profile_t profile = NULL; + BOOL is_temp_ctx = FALSE; + + WINPR_ASSERT(ctx); + + rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt); + if (rv) + goto cleanup; + + krb5_get_init_creds_opt_set_forwardable(gic_opt, 0); + krb5_get_init_creds_opt_set_proxiable(gic_opt, 0); + + if (krb_settings) + { + if (krb_settings->startTime) + start_time = krb_settings->startTime; + if (krb_settings->lifeTime) + krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime); + if (krb_settings->renewLifeTime) + krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime); + if (krb_settings->withPac) + { + rv = krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE); + if (rv) + goto cleanup; + } + if (krb_settings->armorCache) + { + rv = krb5_get_init_creds_opt_set_fast_ccache_name(ctx, gic_opt, + krb_settings->armorCache); + if (rv) + goto cleanup; + } + if (krb_settings->pkinitX509Identity) + { + rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_user_identity", + krb_settings->pkinitX509Identity); + if (rv) + goto cleanup; + } + if (krb_settings->pkinitX509Anchors) + { + rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_anchors", + krb_settings->pkinitX509Anchors); + if (rv) + goto cleanup; + } + if (krb_settings->kdcUrl) + { + const char* names[4] = { 0 }; + char* realm = NULL; + char* kdc_url = NULL; + size_t size = 0; + + if ((rv = krb5_get_profile(ctx, &profile))) + goto cleanup; + + rv = ENOMEM; + if (winpr_asprintf(&kdc_url, &size, "https://%s/KdcProxy", krb_settings->kdcUrl) <= 0) + goto cleanup; + + realm = calloc(princ->realm.length + 1, 1); + if (!realm) + { + free(kdc_url); + goto cleanup; + } + CopyMemory(realm, princ->realm.data, princ->realm.length); + + names[0] = "realms"; + names[1] = realm; + names[2] = "kdc"; + + profile_clear_relation(profile, names); + profile_add_relation(profile, names, kdc_url); + + /* Since we know who the KDC is, tell krb5 that its certificate is valid for pkinit */ + names[2] = "pkinit_kdc_hostname"; + profile_add_relation(profile, names, krb_settings->kdcUrl); + + free(kdc_url); + free(realm); + + if ((rv = profile_flush_to_file(profile, tmp_profile_path))) + goto cleanup; + + profile_release(profile); + profile = NULL; + if ((rv = profile_init_path(tmp_profile_path, &profile))) + goto cleanup; + + if ((rv = krb5_init_context_profile(profile, 0, &ctx))) + goto cleanup; + is_temp_ctx = TRUE; + } + } + + if ((rv = krb5_get_init_creds_opt_set_in_ccache(ctx, gic_opt, ccache))) + goto cleanup; + + if ((rv = krb5_get_init_creds_opt_set_out_ccache(ctx, gic_opt, ccache))) + goto cleanup; + + if ((rv = + krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt, &creds_ctx))) + goto cleanup; + + if ((rv = krb5_init_creds_get(ctx, creds_ctx))) + goto cleanup; + +cleanup: + krb5_init_creds_free(ctx, creds_ctx); + krb5_get_init_creds_opt_free(ctx, gic_opt); + if (is_temp_ctx) + krb5_free_context(ctx); + profile_release(profile); + winpr_DeleteFile(tmp_profile_path); + free(tmp_profile_path); + + return rv; +} + diff --git a/winpr/libwinpr/sspi/ModuleOptions.cmake b/winpr/libwinpr/sspi/ModuleOptions.cmake new file mode 100644 index 0000000..b947e30 --- /dev/null +++ b/winpr/libwinpr/sspi/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "sspi") +set(MINWIN_LONG_NAME "Security Support Provider Interface") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c new file mode 100644 index 0000000..6a2ee6a --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm.c @@ -0,0 +1,1526 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ntlm.h" +#include "ntlm_export.h" +#include "../sspi.h" + +#include "ntlm_message.h" + +#include "../../log.h" +#define TAG WINPR_TAG("sspi.NTLM") + +#define WINPR_KEY "Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\WinPR\\NTLM" + +static char* NTLM_PACKAGE_NAME = "NTLM"; + +#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__) +static BOOL check_context_(NTLM_CONTEXT* context, const char* file, const char* fkt, size_t line) +{ + BOOL rc = TRUE; + wLog* log = WLog_Get(TAG); + const DWORD log_level = WLOG_ERROR; + + if (!context) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context"); + + return FALSE; + } + + if (!context->RecvRc4Seal) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context->RecvRc4Seal"); + rc = FALSE; + } + if (!context->SendRc4Seal) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context->SendRc4Seal"); + rc = FALSE; + } + + if (!context->SendSigningKey) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context->SendSigningKey"); + rc = FALSE; + } + if (!context->RecvSigningKey) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context->RecvSigningKey"); + rc = FALSE; + } + if (!context->SendSealingKey) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context->SendSealingKey"); + rc = FALSE; + } + if (!context->RecvSealingKey) + { + if (WLog_IsLevelActive(log, log_level)) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt, + "invalid context->RecvSealingKey"); + rc = FALSE; + } + return rc; +} + +static int ntlm_SetContextWorkstation(NTLM_CONTEXT* context, char* Workstation) +{ + char* ws = Workstation; + DWORD nSize = 0; + CHAR* computerName = NULL; + + WINPR_ASSERT(context); + + if (!Workstation) + { + if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || + GetLastError() != ERROR_MORE_DATA) + return -1; + + computerName = calloc(nSize, sizeof(CHAR)); + + if (!computerName) + return -1; + + if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize)) + { + free(computerName); + return -1; + } + + if (nSize > MAX_COMPUTERNAME_LENGTH) + computerName[MAX_COMPUTERNAME_LENGTH] = '\0'; + + ws = computerName; + + if (!ws) + return -1; + } + + size_t len = 0; + context->Workstation.Buffer = ConvertUtf8ToWCharAlloc(ws, &len); + + if (!Workstation) + free(ws); + + if (!context->Workstation.Buffer || (len > UINT16_MAX / sizeof(WCHAR))) + return -1; + + context->Workstation.Length = (USHORT)(len * sizeof(WCHAR)); + return 1; +} + +static int ntlm_SetContextServicePrincipalNameW(NTLM_CONTEXT* context, LPWSTR ServicePrincipalName) +{ + WINPR_ASSERT(context); + + if (!ServicePrincipalName) + { + context->ServicePrincipalName.Buffer = NULL; + context->ServicePrincipalName.Length = 0; + return 1; + } + + context->ServicePrincipalName.Length = (USHORT)(_wcslen(ServicePrincipalName) * 2); + context->ServicePrincipalName.Buffer = (PWSTR)malloc(context->ServicePrincipalName.Length + 2); + + if (!context->ServicePrincipalName.Buffer) + return -1; + + memcpy(context->ServicePrincipalName.Buffer, ServicePrincipalName, + context->ServicePrincipalName.Length + 2); + return 1; +} + +static int ntlm_SetContextTargetName(NTLM_CONTEXT* context, char* TargetName) +{ + char* name = TargetName; + DWORD nSize = 0; + CHAR* computerName = NULL; + + WINPR_ASSERT(context); + + if (!name) + { + if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || + GetLastError() != ERROR_MORE_DATA) + return -1; + + computerName = calloc(nSize, sizeof(CHAR)); + + if (!computerName) + return -1; + + if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize)) + { + free(computerName); + return -1; + } + + if (nSize > MAX_COMPUTERNAME_LENGTH) + computerName[MAX_COMPUTERNAME_LENGTH] = '\0'; + + name = computerName; + + if (!name) + return -1; + + CharUpperA(name); + } + + size_t len = 0; + context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len); + + if (!context->TargetName.pvBuffer || (len > UINT16_MAX / sizeof(WCHAR))) + { + free(context->TargetName.pvBuffer); + context->TargetName.pvBuffer = NULL; + + if (!TargetName) + free(name); + + return -1; + } + + context->TargetName.cbBuffer = (USHORT)(len * sizeof(WCHAR)); + + if (!TargetName) + free(name); + + return 1; +} + +static NTLM_CONTEXT* ntlm_ContextNew(void) +{ + HKEY hKey = 0; + LONG status = 0; + DWORD dwType = 0; + DWORD dwSize = 0; + DWORD dwValue = 0; + NTLM_CONTEXT* context = (NTLM_CONTEXT*)calloc(1, sizeof(NTLM_CONTEXT)); + + if (!context) + return NULL; + + context->NTLMv2 = TRUE; + context->UseMIC = FALSE; + context->SendVersionInfo = TRUE; + context->SendSingleHostData = FALSE; + context->SendWorkstationName = TRUE; + context->NegotiateKeyExchange = TRUE; + context->UseSamFileDatabase = TRUE; + status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WINPR_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); + + if (status == ERROR_SUCCESS) + { + if (RegQueryValueEx(hKey, _T("NTLMv2"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) == + ERROR_SUCCESS) + context->NTLMv2 = dwValue ? 1 : 0; + + if (RegQueryValueEx(hKey, _T("UseMIC"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) == + ERROR_SUCCESS) + context->UseMIC = dwValue ? 1 : 0; + + if (RegQueryValueEx(hKey, _T("SendVersionInfo"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) == + ERROR_SUCCESS) + context->SendVersionInfo = dwValue ? 1 : 0; + + if (RegQueryValueEx(hKey, _T("SendSingleHostData"), NULL, &dwType, (BYTE*)&dwValue, + &dwSize) == ERROR_SUCCESS) + context->SendSingleHostData = dwValue ? 1 : 0; + + if (RegQueryValueEx(hKey, _T("SendWorkstationName"), NULL, &dwType, (BYTE*)&dwValue, + &dwSize) == ERROR_SUCCESS) + context->SendWorkstationName = dwValue ? 1 : 0; + + if (RegQueryValueEx(hKey, _T("WorkstationName"), NULL, &dwType, NULL, &dwSize) == + ERROR_SUCCESS) + { + char* workstation = (char*)malloc(dwSize + 1); + + if (!workstation) + { + free(context); + return NULL; + } + + status = RegQueryValueExA(hKey, "WorkstationName", NULL, &dwType, (BYTE*)workstation, + &dwSize); + if (status != ERROR_SUCCESS) + WLog_WARN(TAG, "Key ''WorkstationName' not found"); + workstation[dwSize] = '\0'; + + if (ntlm_SetContextWorkstation(context, workstation) < 0) + { + free(workstation); + free(context); + return NULL; + } + + free(workstation); + } + + RegCloseKey(hKey); + } + + /* + * Extended Protection is enabled by default in Windows 7, + * but enabling it in WinPR breaks TS Gateway at this point + */ + context->SuppressExtendedProtection = FALSE; + status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("System\\CurrentControlSet\\Control\\LSA"), 0, + KEY_READ | KEY_WOW64_64KEY, &hKey); + + if (status == ERROR_SUCCESS) + { + if (RegQueryValueEx(hKey, _T("SuppressExtendedProtection"), NULL, &dwType, (BYTE*)&dwValue, + &dwSize) == ERROR_SUCCESS) + context->SuppressExtendedProtection = dwValue ? 1 : 0; + + RegCloseKey(hKey); + } + + context->NegotiateFlags = 0; + context->LmCompatibilityLevel = 3; + ntlm_change_state(context, NTLM_STATE_INITIAL); + FillMemory(context->MachineID, sizeof(context->MachineID), 0xAA); + + if (context->NTLMv2) + context->UseMIC = TRUE; + + return context; +} + +static void ntlm_ContextFree(NTLM_CONTEXT* context) +{ + if (!context) + return; + + winpr_RC4_Free(context->SendRc4Seal); + winpr_RC4_Free(context->RecvRc4Seal); + sspi_SecBufferFree(&context->NegotiateMessage); + sspi_SecBufferFree(&context->ChallengeMessage); + sspi_SecBufferFree(&context->AuthenticateMessage); + sspi_SecBufferFree(&context->ChallengeTargetInfo); + sspi_SecBufferFree(&context->TargetName); + sspi_SecBufferFree(&context->NtChallengeResponse); + sspi_SecBufferFree(&context->LmChallengeResponse); + free(context->ServicePrincipalName.Buffer); + free(context->Workstation.Buffer); + free(context); +} + +static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SEC_WINPR_NTLM_SETTINGS* settings = NULL; + + if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) && + (fCredentialUse != SECPKG_CRED_BOTH)) + { + return SEC_E_INVALID_PARAMETER; + } + + SSPI_CREDENTIALS* credentials = sspi_CredentialsNew(); + + if (!credentials) + return SEC_E_INTERNAL_ERROR; + + credentials->fCredentialUse = fCredentialUse; + credentials->pGetKeyFn = pGetKeyFn; + credentials->pvGetKeyArgument = pvGetKeyArgument; + + if (pAuthData) + { + UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData); + + sspi_CopyAuthIdentity(&(credentials->identity), + (const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData); + + if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED) + settings = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->ntlmSettings); + } + + if (settings) + { + if (settings->samFile) + { + credentials->ntlmSettings.samFile = _strdup(settings->samFile); + if (!credentials->ntlmSettings.samFile) + { + sspi_CredentialsFree(credentials); + return SEC_E_INSUFFICIENT_MEMORY; + } + } + credentials->ntlmSettings.hashCallback = settings->hashCallback; + credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg; + } + + sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); + sspi_SecureHandleSetUpperPointer(phCredential, (void*)NTLM_PACKAGE_NAME); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY; + SEC_WCHAR* principal = NULL; + SEC_WCHAR* package = NULL; + + if (pszPrincipal) + { + principal = ConvertUtf8ToWCharAlloc(pszPrincipal, NULL); + if (!principal) + goto fail; + } + if (pszPackage) + { + package = ConvertUtf8ToWCharAlloc(pszPackage, NULL); + if (!package) + goto fail; + } + + status = + ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData, + pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry); + +fail: + free(principal); + free(package); + + return status; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle phCredential) +{ + if (!phCredential) + return SEC_E_INVALID_HANDLE; + + SSPI_CREDENTIALS* credentials = + (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + + if (!credentials) + return SEC_E_INVALID_HANDLE; + + sspi_CredentialsFree(credentials); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer) +{ + if (ulAttribute == SECPKG_CRED_ATTR_NAMES) + { + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer) +{ + return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer); +} + +/** + * @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa374707 + */ +static SECURITY_STATUS SEC_ENTRY +ntlm_AcceptSecurityContext(PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, + ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp) +{ + SECURITY_STATUS status = 0; + SSPI_CREDENTIALS* credentials = NULL; + PSecBuffer input_buffer = NULL; + PSecBuffer output_buffer = NULL; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + { + context = ntlm_ContextNew(); + + if (!context) + return SEC_E_INSUFFICIENT_MEMORY; + + context->server = TRUE; + + if (fContextReq & ASC_REQ_CONFIDENTIALITY) + context->confidentiality = TRUE; + + credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + context->credentials = credentials; + context->SamFile = credentials->ntlmSettings.samFile; + context->HashCallback = credentials->ntlmSettings.hashCallback; + context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg; + + ntlm_SetContextTargetName(context, NULL); + sspi_SecureHandleSetLowerPointer(phNewContext, context); + sspi_SecureHandleSetUpperPointer(phNewContext, (void*)NTLM_PACKAGE_NAME); + } + + switch (ntlm_get_state(context)) + { + case NTLM_STATE_INITIAL: + { + ntlm_change_state(context, NTLM_STATE_NEGOTIATE); + + if (!pInput) + return SEC_E_INVALID_TOKEN; + + if (pInput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + + if (!input_buffer) + return SEC_E_INVALID_TOKEN; + + if (input_buffer->cbBuffer < 1) + return SEC_E_INVALID_TOKEN; + + status = ntlm_read_NegotiateMessage(context, input_buffer); + if (status != SEC_I_CONTINUE_NEEDED) + return status; + + if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE) + { + if (!pOutput) + return SEC_E_INVALID_TOKEN; + + if (pOutput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!output_buffer->BufferType) + return SEC_E_INVALID_TOKEN; + + if (output_buffer->cbBuffer < 1) + return SEC_E_INSUFFICIENT_MEMORY; + + return ntlm_write_ChallengeMessage(context, output_buffer); + } + + return SEC_E_OUT_OF_SEQUENCE; + } + + case NTLM_STATE_AUTHENTICATE: + { + if (!pInput) + return SEC_E_INVALID_TOKEN; + + if (pInput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + + if (!input_buffer) + return SEC_E_INVALID_TOKEN; + + if (input_buffer->cbBuffer < 1) + return SEC_E_INVALID_TOKEN; + + status = ntlm_read_AuthenticateMessage(context, input_buffer); + + if (pOutput) + { + for (ULONG i = 0; i < pOutput->cBuffers; i++) + { + pOutput->pBuffers[i].cbBuffer = 0; + pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN; + } + } + + return status; + } + + default: + return SEC_E_OUT_OF_SEQUENCE; + } +} + +static SECURITY_STATUS SEC_ENTRY ntlm_ImpersonateSecurityContext(PCtxtHandle phContext) +{ + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + SSPI_CREDENTIALS* credentials = NULL; + PSecBuffer input_buffer = NULL; + PSecBuffer output_buffer = NULL; + PSecBuffer channel_bindings = NULL; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (pInput) + { + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS); + } + + if (!context) + { + context = ntlm_ContextNew(); + + if (!context) + return SEC_E_INSUFFICIENT_MEMORY; + + if (fContextReq & ISC_REQ_CONFIDENTIALITY) + context->confidentiality = TRUE; + + credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + context->credentials = credentials; + + if (context->Workstation.Length < 1) + { + if (ntlm_SetContextWorkstation(context, NULL) < 0) + { + ntlm_ContextFree(context); + return SEC_E_INTERNAL_ERROR; + } + } + + if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0) + { + ntlm_ContextFree(context); + return SEC_E_INTERNAL_ERROR; + } + + sspi_SecureHandleSetLowerPointer(phNewContext, context); + sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME); + } + + if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)) + { + if (!pOutput) + return SEC_E_INVALID_TOKEN; + + if (pOutput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!output_buffer) + return SEC_E_INVALID_TOKEN; + + if (output_buffer->cbBuffer < 1) + return SEC_E_INVALID_TOKEN; + + if (ntlm_get_state(context) == NTLM_STATE_INITIAL) + ntlm_change_state(context, NTLM_STATE_NEGOTIATE); + + if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE) + return ntlm_write_NegotiateMessage(context, output_buffer); + + return SEC_E_OUT_OF_SEQUENCE; + } + else + { + if (!input_buffer) + return SEC_E_INVALID_TOKEN; + + if (input_buffer->cbBuffer < 1) + return SEC_E_INVALID_TOKEN; + + channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS); + + if (channel_bindings) + { + context->Bindings.BindingsLength = channel_bindings->cbBuffer; + context->Bindings.Bindings = (SEC_CHANNEL_BINDINGS*)channel_bindings->pvBuffer; + } + + if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE) + { + status = ntlm_read_ChallengeMessage(context, input_buffer); + + if (status != SEC_I_CONTINUE_NEEDED) + return status; + + if (!pOutput) + return SEC_E_INVALID_TOKEN; + + if (pOutput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!output_buffer) + return SEC_E_INVALID_TOKEN; + + if (output_buffer->cbBuffer < 1) + return SEC_E_INSUFFICIENT_MEMORY; + + if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE) + return ntlm_write_AuthenticateMessage(context, output_buffer); + } + + return SEC_E_OUT_OF_SEQUENCE; + } + + return SEC_E_OUT_OF_SEQUENCE; +} + +/** + * @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa375512%28v=vs.85%29.aspx + */ +static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + SEC_WCHAR* pszTargetNameW = NULL; + + if (pszTargetName) + { + pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL); + if (!pszTargetNameW) + return SEC_E_INTERNAL_ERROR; + } + + status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); + free(pszTargetNameW); + return status; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375354 */ + +static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext) +{ + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + ntlm_ContextFree(context); + return SEC_E_OK; +} + +SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof) +{ + BYTE* blob = NULL; + SecBuffer* target = NULL; + + WINPR_ASSERT(ntlm); + WINPR_ASSERT(ntproof); + + target = &ntlm->ChallengeTargetInfo; + + if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer)) + return SEC_E_INSUFFICIENT_MEMORY; + + blob = (BYTE*)ntproof->pvBuffer; + CopyMemory(blob, ntlm->ServerChallenge, 8); /* Server challenge. */ + blob[8] = 1; /* Response version. */ + blob[9] = 1; /* Highest response version understood by the client. */ + /* Reserved 6B. */ + CopyMemory(&blob[16], ntlm->Timestamp, 8); /* Time. */ + CopyMemory(&blob[24], ntlm->ClientChallenge, 8); /* Client challenge. */ + /* Reserved 4B. */ + /* Server name. */ + CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer); + return SEC_E_OK; +} + +SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue) +{ + BYTE* blob = NULL; + ULONG msgSize = 0; + + WINPR_ASSERT(ntlm); + WINPR_ASSERT(micvalue); + + msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer + + ntlm->AuthenticateMessage.cbBuffer; + + if (!sspi_SecBufferAlloc(micvalue, msgSize)) + return SEC_E_INSUFFICIENT_MEMORY; + + blob = (BYTE*)micvalue->pvBuffer; + CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer); + blob += ntlm->NegotiateMessage.cbBuffer; + CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer); + blob += ntlm->ChallengeMessage.cbBuffer; + CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer); + blob += ntlm->MessageIntegrityCheckOffset; + ZeroMemory(blob, 16); + return SEC_E_OK; +} + +/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */ + +static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + if (!phContext) + return SEC_E_INVALID_HANDLE; + + if (!pBuffer) + return SEC_E_INSUFFICIENT_MEMORY; + + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + if (!check_context(context)) + return SEC_E_INVALID_HANDLE; + + if (ulAttribute == SECPKG_ATTR_SIZES) + { + SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer; + ContextSizes->cbMaxToken = 2010; + ContextSizes->cbMaxSignature = 16; /* the size of expected signature is 16 bytes */ + ContextSizes->cbBlockSize = 0; /* no padding */ + ContextSizes->cbSecurityTrailer = 16; /* no security trailer appended in NTLM + contrary to Kerberos */ + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY) + { + SSPI_CREDENTIALS* credentials = NULL; + const SecPkgContext_AuthIdentity empty = { 0 }; + SecPkgContext_AuthIdentity* AuthIdentity = (SecPkgContext_AuthIdentity*)pBuffer; + + WINPR_ASSERT(AuthIdentity); + *AuthIdentity = empty; + + context->UseSamFileDatabase = FALSE; + credentials = context->credentials; + + if (credentials->identity.UserLength > 0) + { + if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength, + AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0) + return SEC_E_INTERNAL_ERROR; + } + + if (credentials->identity.DomainLength > 0) + { + if (ConvertWCharNToUtf8(credentials->identity.Domain, + credentials->identity.DomainLength, AuthIdentity->Domain, + ARRAYSIZE(AuthIdentity->Domain)) <= 0) + return SEC_E_INTERNAL_ERROR; + } + + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE) + { + return ntlm_computeProofValue(context, (SecBuffer*)pBuffer); + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY) + { + SecBuffer* randkey = NULL; + randkey = (SecBuffer*)pBuffer; + + if (!sspi_SecBufferAlloc(randkey, 16)) + return (SEC_E_INSUFFICIENT_MEMORY); + + CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16); + return (SEC_E_OK); + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC) + { + SecBuffer* mic = (SecBuffer*)pBuffer; + NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE; + + if (!sspi_SecBufferAlloc(mic, 16)) + return (SEC_E_INSUFFICIENT_MEMORY); + + CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16); + return (SEC_E_OK); + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE) + { + return ntlm_computeMicValue(context, (SecBuffer*)pBuffer); + } + + WLog_ERR(TAG, "TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer); +} + +static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + if (!phContext) + return SEC_E_INVALID_HANDLE; + + if (!pBuffer) + return SEC_E_INVALID_PARAMETER; + + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + if (!context) + return SEC_E_INVALID_HANDLE; + + if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH) + { + SecPkgContext_AuthNtlmHash* AuthNtlmHash = (SecPkgContext_AuthNtlmHash*)pBuffer; + + if (cbBuffer < sizeof(SecPkgContext_AuthNtlmHash)) + return SEC_E_INVALID_PARAMETER; + + if (AuthNtlmHash->Version == 1) + CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16); + else if (AuthNtlmHash->Version == 2) + CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16); + + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE) + { + SecPkgContext_AuthNtlmMessage* AuthNtlmMessage = (SecPkgContext_AuthNtlmMessage*)pBuffer; + + if (cbBuffer < sizeof(SecPkgContext_AuthNtlmMessage)) + return SEC_E_INVALID_PARAMETER; + + if (AuthNtlmMessage->type == 1) + { + sspi_SecBufferFree(&context->NegotiateMessage); + + if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length)) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer, + AuthNtlmMessage->length); + } + else if (AuthNtlmMessage->type == 2) + { + sspi_SecBufferFree(&context->ChallengeMessage); + + if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length)) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer, + AuthNtlmMessage->length); + } + else if (AuthNtlmMessage->type == 3) + { + sspi_SecBufferFree(&context->AuthenticateMessage); + + if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length)) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer, + AuthNtlmMessage->length); + } + + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP) + { + SecPkgContext_AuthNtlmTimestamp* AuthNtlmTimestamp = + (SecPkgContext_AuthNtlmTimestamp*)pBuffer; + + if (cbBuffer < sizeof(SecPkgContext_AuthNtlmTimestamp)) + return SEC_E_INVALID_PARAMETER; + + if (AuthNtlmTimestamp->ChallengeOrResponse) + CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8); + else + CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8); + + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE) + { + SecPkgContext_AuthNtlmClientChallenge* AuthNtlmClientChallenge = + (SecPkgContext_AuthNtlmClientChallenge*)pBuffer; + + if (cbBuffer < sizeof(SecPkgContext_AuthNtlmClientChallenge)) + return SEC_E_INVALID_PARAMETER; + + CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8); + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE) + { + SecPkgContext_AuthNtlmServerChallenge* AuthNtlmServerChallenge = + (SecPkgContext_AuthNtlmServerChallenge*)pBuffer; + + if (cbBuffer < sizeof(SecPkgContext_AuthNtlmServerChallenge)) + return SEC_E_INVALID_PARAMETER; + + CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8); + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer); +} + +static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(PCtxtHandle phContext) +{ + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + const UINT32 SeqNo = MessageSeqNo; + UINT32 value = 0; + BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + BYTE checksum[8] = { 0 }; + ULONG version = 1; + PSecBuffer data_buffer = NULL; + PSecBuffer signature_buffer = NULL; + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + if (!check_context(context)) + return SEC_E_INVALID_HANDLE; + + for (ULONG index = 0; index < pMessage->cBuffers; index++) + { + SecBuffer* cur = &pMessage->pBuffers[index]; + + if (cur->BufferType & SECBUFFER_DATA) + data_buffer = cur; + else if (cur->BufferType & SECBUFFER_TOKEN) + signature_buffer = cur; + } + + if (!data_buffer) + return SEC_E_INVALID_TOKEN; + + if (!signature_buffer) + return SEC_E_INVALID_TOKEN; + + /* Copy original data buffer */ + ULONG length = data_buffer->cbBuffer; + void* data = malloc(length); + + if (!data) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(data, data_buffer->pvBuffer, length); + /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */ + WINPR_HMAC_CTX* hmac = winpr_HMAC_New(); + + if (hmac && + winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH)) + { + Data_Write_UINT32(&value, SeqNo); + winpr_HMAC_Update(hmac, (void*)&value, 4); + winpr_HMAC_Update(hmac, (void*)data, length); + winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH); + winpr_HMAC_Free(hmac); + } + else + { + winpr_HMAC_Free(hmac); + free(data); + return SEC_E_INSUFFICIENT_MEMORY; + } + + /* Encrypt message using with RC4, result overwrites original buffer */ + if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0) + { + if (context->confidentiality) + winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data, + (BYTE*)data_buffer->pvBuffer); + else + CopyMemory(data_buffer->pvBuffer, data, length); + } + +#ifdef WITH_DEBUG_NTLM + WLog_DBG(TAG, "Data Buffer (length = %" PRIuz ")", length); + winpr_HexDump(TAG, WLOG_DEBUG, data, length); + WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer); + winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer); +#endif + free(data); + /* RC4-encrypt first 8 bytes of digest */ + winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum); + if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0) + { + BYTE* signature = signature_buffer->pvBuffer; + /* Concatenate version, ciphertext and sequence number to build signature */ + Data_Write_UINT32(signature, version); + CopyMemory(&signature[4], (void*)checksum, 8); + Data_Write_UINT32(&signature[12], SeqNo); + } + context->SendSeqNum++; +#ifdef WITH_DEBUG_NTLM + WLog_DBG(TAG, "Signature (length = %" PRIu32 ")", signature_buffer->cbBuffer); + winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer); +#endif + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage, + ULONG MessageSeqNo, PULONG pfQOP) +{ + const UINT32 SeqNo = (UINT32)MessageSeqNo; + UINT32 value = 0; + BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + BYTE checksum[8] = { 0 }; + UINT32 version = 1; + BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + PSecBuffer data_buffer = NULL; + PSecBuffer signature_buffer = NULL; + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + if (!check_context(context)) + return SEC_E_INVALID_HANDLE; + + for (ULONG index = 0; index < pMessage->cBuffers; index++) + { + if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA) + data_buffer = &pMessage->pBuffers[index]; + else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN) + signature_buffer = &pMessage->pBuffers[index]; + } + + if (!data_buffer) + return SEC_E_INVALID_TOKEN; + + if (!signature_buffer) + return SEC_E_INVALID_TOKEN; + + /* Copy original data buffer */ + const ULONG length = data_buffer->cbBuffer; + void* data = malloc(length); + + if (!data) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(data, data_buffer->pvBuffer, length); + + /* Decrypt message using with RC4, result overwrites original buffer */ + + if (context->confidentiality) + winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data, (BYTE*)data_buffer->pvBuffer); + else + CopyMemory(data_buffer->pvBuffer, data, length); + + /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */ + WINPR_HMAC_CTX* hmac = winpr_HMAC_New(); + + if (hmac && + winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH)) + { + Data_Write_UINT32(&value, SeqNo); + winpr_HMAC_Update(hmac, (void*)&value, 4); + winpr_HMAC_Update(hmac, (void*)data_buffer->pvBuffer, data_buffer->cbBuffer); + winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH); + winpr_HMAC_Free(hmac); + } + else + { + winpr_HMAC_Free(hmac); + free(data); + return SEC_E_INSUFFICIENT_MEMORY; + } + +#ifdef WITH_DEBUG_NTLM + WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIuz ")", length); + winpr_HexDump(TAG, WLOG_DEBUG, data, length); + WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer); + winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer); +#endif + free(data); + /* RC4-encrypt first 8 bytes of digest */ + winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum); + /* Concatenate version, ciphertext and sequence number to build signature */ + Data_Write_UINT32(expected_signature, version); + CopyMemory(&expected_signature[4], (void*)checksum, 8); + Data_Write_UINT32(&expected_signature[12], SeqNo); + context->RecvSeqNum++; + + if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0) + { + /* signature verification failed! */ + WLog_ERR(TAG, "signature verification failed, something nasty is going on!"); +#ifdef WITH_DEBUG_NTLM + WLog_ERR(TAG, "Expected Signature:"); + winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16); + WLog_ERR(TAG, "Actual Signature:"); + winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16); +#endif + return SEC_E_MESSAGE_ALTERED; + } + + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + PSecBuffer data_buffer = NULL; + PSecBuffer sig_buffer = NULL; + UINT32 seq_no = 0; + BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + BYTE checksum[8] = { 0 }; + + NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext); + if (!check_context(context)) + return SEC_E_INVALID_HANDLE; + + for (ULONG i = 0; i < pMessage->cBuffers; i++) + { + if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA) + data_buffer = &pMessage->pBuffers[i]; + else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN) + sig_buffer = &pMessage->pBuffers[i]; + } + + if (!data_buffer || !sig_buffer) + return SEC_E_INVALID_TOKEN; + + WINPR_HMAC_CTX* hmac = winpr_HMAC_New(); + + if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH)) + return SEC_E_INTERNAL_ERROR; + + Data_Write_UINT32(&seq_no, MessageSeqNo); + winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4); + winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer); + winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH); + winpr_HMAC_Free(hmac); + + winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum); + + BYTE* signature = sig_buffer->pvBuffer; + Data_Write_UINT32(signature, 1L); + CopyMemory(&signature[4], checksum, 8); + Data_Write_UINT32(&signature[12], seq_no); + sig_buffer->cbBuffer = 16; + + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP) +{ + PSecBuffer data_buffer = NULL; + PSecBuffer sig_buffer = NULL; + UINT32 seq_no = 0; + BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + BYTE checksum[8] = { 0 }; + BYTE signature[16] = { 0 }; + + NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext); + if (!check_context(context)) + return SEC_E_INVALID_HANDLE; + + for (ULONG i = 0; i < pMessage->cBuffers; i++) + { + if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA) + data_buffer = &pMessage->pBuffers[i]; + else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN) + sig_buffer = &pMessage->pBuffers[i]; + } + + if (!data_buffer || !sig_buffer) + return SEC_E_INVALID_TOKEN; + + WINPR_HMAC_CTX* hmac = winpr_HMAC_New(); + + if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH)) + return SEC_E_INTERNAL_ERROR; + + Data_Write_UINT32(&seq_no, MessageSeqNo); + winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4); + winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer); + winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH); + winpr_HMAC_Free(hmac); + + winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum); + + Data_Write_UINT32(signature, 1L); + CopyMemory(&signature[4], checksum, 8); + Data_Write_UINT32(&signature[12], seq_no); + + if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0) + return SEC_E_MESSAGE_ALTERED; + + return SEC_E_OK; +} + +const SecurityFunctionTableA NTLM_SecurityFunctionTableA = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */ + ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */ + ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + ntlm_InitializeSecurityContextA, /* InitializeSecurityContext */ + ntlm_AcceptSecurityContext, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + ntlm_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + ntlm_QueryContextAttributesA, /* QueryContextAttributes */ + ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + ntlm_RevertSecurityContext, /* RevertSecurityContext */ + ntlm_MakeSignature, /* MakeSignature */ + ntlm_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + ntlm_EncryptMessage, /* EncryptMessage */ + ntlm_DecryptMessage, /* DecryptMessage */ + ntlm_SetContextAttributesA, /* SetContextAttributes */ + ntlm_SetCredentialsAttributesA, /* SetCredentialsAttributes */ +}; + +const SecurityFunctionTableW NTLM_SecurityFunctionTableW = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */ + ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */ + ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + ntlm_InitializeSecurityContextW, /* InitializeSecurityContext */ + ntlm_AcceptSecurityContext, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + ntlm_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + ntlm_QueryContextAttributesW, /* QueryContextAttributes */ + ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + ntlm_RevertSecurityContext, /* RevertSecurityContext */ + ntlm_MakeSignature, /* MakeSignature */ + ntlm_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + ntlm_EncryptMessage, /* EncryptMessage */ + ntlm_DecryptMessage, /* DecryptMessage */ + ntlm_SetContextAttributesW, /* SetContextAttributes */ + ntlm_SetCredentialsAttributesW, /* SetCredentialsAttributes */ +}; + +const SecPkgInfoA NTLM_SecPkgInfoA = { + 0x00082B37, /* fCapabilities */ + 1, /* wVersion */ + 0x000A, /* wRPCID */ + 0x00000B48, /* cbMaxToken */ + "NTLM", /* Name */ + "NTLM Security Package" /* Comment */ +}; + +static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = { 0 }; +static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = { 0 }; + +const SecPkgInfoW NTLM_SecPkgInfoW = { + 0x00082B37, /* fCapabilities */ + 1, /* wVersion */ + 0x000A, /* wRPCID */ + 0x00000B48, /* cbMaxToken */ + NTLM_SecPkgInfoW_NameBuffer, /* Name */ + NTLM_SecPkgInfoW_CommentBuffer /* Comment */ +}; + +char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags) +{ + if (!buffer || (size == 0)) + return buffer; + + _snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags); + + for (int x = 0; x < 31; x++) + { + const UINT32 mask = 1 << x; + size_t len = strnlen(buffer, size); + if (flags & mask) + { + const char* str = ntlm_get_negotiate_string(mask); + const size_t flen = strlen(str); + + if ((len > 0) && (buffer[len - 1] != ' ')) + { + if (size - len < 1) + break; + winpr_str_append("|", buffer, size, NULL); + len++; + } + + if (size - len < flen) + break; + winpr_str_append(str, buffer, size, NULL); + } + } + + return buffer; +} + +const char* ntlm_message_type_string(UINT32 messageType) +{ + switch (messageType) + { + case MESSAGE_TYPE_NEGOTIATE: + return "MESSAGE_TYPE_NEGOTIATE"; + case MESSAGE_TYPE_CHALLENGE: + return "MESSAGE_TYPE_CHALLENGE"; + case MESSAGE_TYPE_AUTHENTICATE: + return "MESSAGE_TYPE_AUTHENTICATE"; + default: + return "MESSAGE_TYPE_UNKNOWN"; + } +} + +const char* ntlm_state_string(NTLM_STATE state) +{ + switch (state) + { + case NTLM_STATE_INITIAL: + return "NTLM_STATE_INITIAL"; + case NTLM_STATE_NEGOTIATE: + return "NTLM_STATE_NEGOTIATE"; + case NTLM_STATE_CHALLENGE: + return "NTLM_STATE_CHALLENGE"; + case NTLM_STATE_AUTHENTICATE: + return "NTLM_STATE_AUTHENTICATE"; + case NTLM_STATE_FINAL: + return "NTLM_STATE_FINAL"; + default: + return "NTLM_STATE_UNKNOWN"; + } +} +void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state) +{ + WINPR_ASSERT(ntlm); + WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state), + ntlm_state_string(state)); + ntlm->state = state; +} + +NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm) +{ + WINPR_ASSERT(ntlm); + return ntlm->state; +} + +BOOL ntlm_reset_cipher_state(PSecHandle phContext) +{ + NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext); + + if (context) + { + check_context(context); + winpr_RC4_Free(context->SendRc4Seal); + winpr_RC4_Free(context->RecvRc4Seal); + context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16); + context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16); + + if (!context->SendRc4Seal) + { + WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal"); + return FALSE; + } + if (!context->RecvRc4Seal) + { + WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal"); + return FALSE; + } + } + + return TRUE; +} + +BOOL NTLM_init(void) +{ + InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer, + ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer)); + InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer, + ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer)); + + return TRUE; +} diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.h b/winpr/libwinpr/sspi/NTLM/ntlm.h new file mode 100644 index 0000000..4eac436 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm.h @@ -0,0 +1,301 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_NTLM_PRIVATE_H +#define WINPR_SSPI_NTLM_PRIVATE_H + +#include +#include + +#include +#include + +#include "../sspi.h" + +#define MESSAGE_TYPE_NEGOTIATE 1 +#define MESSAGE_TYPE_CHALLENGE 2 +#define MESSAGE_TYPE_AUTHENTICATE 3 + +#define NTLMSSP_NEGOTIATE_56 0x80000000 /* W (0) */ +#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 /* V (1) */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 /* U (2) */ +#define NTLMSSP_RESERVED1 0x10000000 /* r1 (3) */ +#define NTLMSSP_RESERVED2 0x08000000 /* r2 (4) */ +#define NTLMSSP_RESERVED3 0x04000000 /* r3 (5) */ +#define NTLMSSP_NEGOTIATE_VERSION 0x02000000 /* T (6) */ +#define NTLMSSP_RESERVED4 0x01000000 /* r4 (7) */ +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* S (8) */ +#define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 /* R (9) */ +#define NTLMSSP_RESERVED5 0x00200000 /* r5 (10) */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 /* Q (11) */ +#define NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY 0x00080000 /* P (12) */ +#define NTLMSSP_RESERVED6 0x00040000 /* r6 (13) */ +#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* O (14) */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* N (15) */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* M (16) */ +#define NTLMSSP_RESERVED7 0x00004000 /* r7 (17) */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 /* L (18) */ +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 /* K (19) */ +#define NTLMSSP_NEGOTIATE_ANONYMOUS 0x00000800 /* J (20) */ +#define NTLMSSP_RESERVED8 0x00000400 /* r8 (21) */ +#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* H (22) */ +#define NTLMSSP_RESERVED9 0x00000100 /* r9 (23) */ +#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* G (24) */ +#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* F (25) */ +#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* E (26) */ +#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* D (27) */ +#define NTLMSSP_RESERVED10 0x00000008 /* r10 (28) */ +#define NTLMSSP_REQUEST_TARGET 0x00000004 /* C (29) */ +#define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* B (30) */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* A (31) */ + +typedef enum +{ + NTLM_STATE_INITIAL, + NTLM_STATE_NEGOTIATE, + NTLM_STATE_CHALLENGE, + NTLM_STATE_AUTHENTICATE, + NTLM_STATE_FINAL +} NTLM_STATE; + +#ifdef __MINGW32__ +typedef MSV1_0_AVID NTLM_AV_ID; + +#if __MINGW64_VERSION_MAJOR < 9 +enum +{ + MsvAvTimestamp = MsvAvFlags + 1, + MsvAvRestrictions, + MsvAvTargetName, + MsvAvChannelBindings, + MsvAvSingleHost = MsvAvRestrictions +}; + +#else +#ifndef MsvAvSingleHost +#define MsvAvSingleHost MsvAvRestrictions +#endif +#endif +#else +typedef enum +{ + MsvAvEOL, + MsvAvNbComputerName, + MsvAvNbDomainName, + MsvAvDnsComputerName, + MsvAvDnsDomainName, + MsvAvDnsTreeName, + MsvAvFlags, + MsvAvTimestamp, + MsvAvSingleHost, + MsvAvTargetName, + MsvAvChannelBindings +} NTLM_AV_ID; +#endif /* __MINGW32__ */ + +typedef struct +{ + UINT16 AvId; + UINT16 AvLen; +} NTLM_AV_PAIR; + +#define MSV_AV_FLAGS_AUTHENTICATION_CONSTRAINED 0x00000001 +#define MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK 0x00000002 +#define MSV_AV_FLAGS_TARGET_SPN_UNTRUSTED_SOURCE 0x00000004 + +#define WINDOWS_MAJOR_VERSION_5 0x05 +#define WINDOWS_MAJOR_VERSION_6 0x06 +#define WINDOWS_MINOR_VERSION_0 0x00 +#define WINDOWS_MINOR_VERSION_1 0x01 +#define WINDOWS_MINOR_VERSION_2 0x02 +#define NTLMSSP_REVISION_W2K3 0x0F + +typedef struct +{ + UINT8 ProductMajorVersion; + UINT8 ProductMinorVersion; + UINT16 ProductBuild; + BYTE Reserved[3]; + UINT8 NTLMRevisionCurrent; +} NTLM_VERSION_INFO; + +typedef struct +{ + UINT32 Size; + UINT32 Z4; + UINT32 DataPresent; + UINT32 CustomData; + BYTE MachineID[32]; +} NTLM_SINGLE_HOST_DATA; + +typedef struct +{ + BYTE Response[24]; +} NTLM_RESPONSE; + +typedef struct +{ + UINT8 RespType; + UINT8 HiRespType; + UINT16 Reserved1; + UINT32 Reserved2; + BYTE Timestamp[8]; + BYTE ClientChallenge[8]; + UINT32 Reserved3; + NTLM_AV_PAIR* AvPairs; + UINT32 cbAvPairs; +} NTLMv2_CLIENT_CHALLENGE; + +typedef struct +{ + BYTE Response[16]; + NTLMv2_CLIENT_CHALLENGE Challenge; +} NTLMv2_RESPONSE; + +typedef struct +{ + UINT16 Len; + UINT16 MaxLen; + PBYTE Buffer; + UINT32 BufferOffset; +} NTLM_MESSAGE_FIELDS; + +typedef struct +{ + BYTE Signature[8]; + UINT32 MessageType; +} NTLM_MESSAGE_HEADER; + +typedef struct +{ + NTLM_MESSAGE_HEADER header; + UINT32 NegotiateFlags; + NTLM_VERSION_INFO Version; + NTLM_MESSAGE_FIELDS DomainName; + NTLM_MESSAGE_FIELDS Workstation; +} NTLM_NEGOTIATE_MESSAGE; + +typedef struct +{ + NTLM_MESSAGE_HEADER header; + UINT32 NegotiateFlags; + BYTE ServerChallenge[8]; + BYTE Reserved[8]; + NTLM_VERSION_INFO Version; + NTLM_MESSAGE_FIELDS TargetName; + NTLM_MESSAGE_FIELDS TargetInfo; +} NTLM_CHALLENGE_MESSAGE; + +typedef struct +{ + NTLM_MESSAGE_HEADER header; + UINT32 NegotiateFlags; + NTLM_VERSION_INFO Version; + NTLM_MESSAGE_FIELDS DomainName; + NTLM_MESSAGE_FIELDS UserName; + NTLM_MESSAGE_FIELDS Workstation; + NTLM_MESSAGE_FIELDS LmChallengeResponse; + NTLM_MESSAGE_FIELDS NtChallengeResponse; + NTLM_MESSAGE_FIELDS EncryptedRandomSessionKey; + BYTE MessageIntegrityCheck[16]; +} NTLM_AUTHENTICATE_MESSAGE; + +typedef struct +{ + BOOL server; + BOOL NTLMv2; + BOOL UseMIC; + NTLM_STATE state; + int SendSeqNum; + int RecvSeqNum; + char* SamFile; + BYTE NtlmHash[16]; + BYTE NtlmV2Hash[16]; + BYTE MachineID[32]; + BOOL SendVersionInfo; + BOOL confidentiality; + WINPR_RC4_CTX* SendRc4Seal; + WINPR_RC4_CTX* RecvRc4Seal; + BYTE* SendSigningKey; + BYTE* RecvSigningKey; + BYTE* SendSealingKey; + BYTE* RecvSealingKey; + UINT32 NegotiateFlags; + BOOL UseSamFileDatabase; + int LmCompatibilityLevel; + int SuppressExtendedProtection; + BOOL SendWorkstationName; + UNICODE_STRING Workstation; + UNICODE_STRING ServicePrincipalName; + SSPI_CREDENTIALS* credentials; + BYTE* ChannelBindingToken; + BYTE ChannelBindingsHash[16]; + SecPkgContext_Bindings Bindings; + BOOL SendSingleHostData; + BOOL NegotiateKeyExchange; + NTLM_SINGLE_HOST_DATA SingleHostData; + NTLM_NEGOTIATE_MESSAGE NEGOTIATE_MESSAGE; + NTLM_CHALLENGE_MESSAGE CHALLENGE_MESSAGE; + NTLM_AUTHENTICATE_MESSAGE AUTHENTICATE_MESSAGE; + size_t MessageIntegrityCheckOffset; + SecBuffer NegotiateMessage; + SecBuffer ChallengeMessage; + SecBuffer AuthenticateMessage; + SecBuffer ChallengeTargetInfo; + SecBuffer AuthenticateTargetInfo; + SecBuffer TargetName; + SecBuffer NtChallengeResponse; + SecBuffer LmChallengeResponse; + NTLMv2_RESPONSE NTLMv2Response; + BYTE NtProofString[16]; + BYTE Timestamp[8]; + BYTE ChallengeTimestamp[8]; + BYTE ServerChallenge[8]; + BYTE ClientChallenge[8]; + BYTE SessionBaseKey[16]; + BYTE KeyExchangeKey[16]; + BYTE RandomSessionKey[16]; + BYTE ExportedSessionKey[16]; + BYTE EncryptedRandomSessionKey[16]; + BYTE ClientSigningKey[16]; + BYTE ClientSealingKey[16]; + BYTE ServerSigningKey[16]; + BYTE ServerSealingKey[16]; + psSspiNtlmHashCallback HashCallback; + void* HashCallbackArg; +} NTLM_CONTEXT; + +char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags); +const char* ntlm_message_type_string(UINT32 messageType); + +const char* ntlm_state_string(NTLM_STATE state); +void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state); +NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm); +BOOL ntlm_reset_cipher_state(PSecHandle phContext); + +SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof); +SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue); + +#ifdef WITH_DEBUG_NLA +#define WITH_DEBUG_NTLM +#endif + +BOOL NTLM_init(void); + +#endif /* WINPR_SSPI_NTLM_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c new file mode 100644 index 0000000..881a743 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c @@ -0,0 +1,775 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package (AV_PAIRs) + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "ntlm.h" +#include "../sspi.h" + +#include +#include +#include +#include +#include + +#include "ntlm_compute.h" + +#include "ntlm_av_pairs.h" + +#include "../../log.h" +#define TAG WINPR_TAG("sspi.NTLM") + +static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset); + +static BOOL ntlm_av_pair_check_data(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair, size_t size) +{ + size_t offset = 0; + if (!pAvPair || cbAvPair < sizeof(NTLM_AV_PAIR) + size) + return FALSE; + if (!ntlm_av_pair_get_next_offset(pAvPair, cbAvPair, &offset)) + return FALSE; + return cbAvPair >= offset; +} + +static const char* get_av_pair_string(UINT16 pair) +{ + switch (pair) + { + case MsvAvEOL: + return "MsvAvEOL"; + case MsvAvNbComputerName: + return "MsvAvNbComputerName"; + case MsvAvNbDomainName: + return "MsvAvNbDomainName"; + case MsvAvDnsComputerName: + return "MsvAvDnsComputerName"; + case MsvAvDnsDomainName: + return "MsvAvDnsDomainName"; + case MsvAvDnsTreeName: + return "MsvAvDnsTreeName"; + case MsvAvFlags: + return "MsvAvFlags"; + case MsvAvTimestamp: + return "MsvAvTimestamp"; + case MsvAvSingleHost: + return "MsvAvSingleHost"; + case MsvAvTargetName: + return "MsvAvTargetName"; + case MsvAvChannelBindings: + return "MsvAvChannelBindings"; + default: + return "UNKNOWN"; + } +} + +static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair); +static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPairList, size_t* pcbAvPairList); + +static INLINE void ntlm_av_pair_set_id(NTLM_AV_PAIR* pAvPair, UINT16 id) +{ + WINPR_ASSERT(pAvPair); + Data_Write_UINT16(&pAvPair->AvId, id); +} + +static INLINE void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len) +{ + WINPR_ASSERT(pAvPair); + Data_Write_UINT16(&pAvPair->AvLen, len); +} + +static BOOL ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) +{ + NTLM_AV_PAIR* pAvPair = pAvPairList; + + if (!pAvPair || (cbAvPairList < sizeof(NTLM_AV_PAIR))) + return FALSE; + + ntlm_av_pair_set_id(pAvPair, MsvAvEOL); + ntlm_av_pair_set_len(pAvPair, 0); + return TRUE; +} + +static INLINE BOOL ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair, size_t size, UINT16* pair) +{ + UINT16 AvId = 0; + if (!pAvPair || !pair) + return FALSE; + + if (size < sizeof(NTLM_AV_PAIR)) + return FALSE; + + Data_Read_UINT16(&pAvPair->AvId, AvId); + + *pair = AvId; + return TRUE; +} + +ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) +{ + size_t size = 0; + size_t cbAvPair = 0; + NTLM_AV_PAIR* pAvPair = NULL; + + pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair); + if (!pAvPair) + return 0; + + size = ((PBYTE)pAvPair - (PBYTE)pAvPairList) + sizeof(NTLM_AV_PAIR); + WINPR_ASSERT(size <= ULONG_MAX); + return (ULONG)size; +} + +static INLINE BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pAvLen) +{ + UINT16 AvLen = 0; + if (!pAvPair) + return FALSE; + + if (size < sizeof(NTLM_AV_PAIR)) + return FALSE; + + Data_Read_UINT16(&pAvPair->AvLen, AvLen); + + *pAvLen = AvLen; + return TRUE; +} + +#ifdef WITH_DEBUG_NTLM +void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) +{ + UINT16 pair = 0; + size_t cbAvPair = cbAvPairList; + NTLM_AV_PAIR* pAvPair = pAvPairList; + + if (!ntlm_av_pair_check(pAvPair, cbAvPair)) + return; + + WLog_VRB(TAG, "AV_PAIRs ="); + + while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL)) + { + size_t cbLen = 0; + ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen); + + WLog_VRB(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "", get_av_pair_string(pair), pair); + winpr_HexDump(TAG, WLOG_TRACE, ntlm_av_pair_get_value_pointer(pAvPair), cbLen); + + pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair); + } +} +#endif + +static ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength) +{ + /* size of headers + value lengths + terminating MsvAvEOL AV_PAIR */ + return ((AvPairsCount + 1) * 4) + AvPairsValueLength; +} + +PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair) +{ + WINPR_ASSERT(pAvPair); + return (PBYTE)pAvPair + sizeof(NTLM_AV_PAIR); +} + +static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset) +{ + size_t avLen = 0; + if (!pOffset) + return FALSE; + + if (!ntlm_av_pair_get_len(pAvPair, size, &avLen)) + return FALSE; + *pOffset = avLen + sizeof(NTLM_AV_PAIR); + return TRUE; +} + +static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair) +{ + return ntlm_av_pair_check_data(pAvPair, cbAvPair, 0); +} + +static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPair, size_t* pcbAvPair) +{ + size_t offset = 0; + + if (!pcbAvPair) + return NULL; + if (!ntlm_av_pair_check(pAvPair, *pcbAvPair)) + return NULL; + + if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset)) + return NULL; + + *pcbAvPair -= offset; + return (NTLM_AV_PAIR*)((PBYTE)pAvPair + offset); +} + +NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId, + size_t* pcbAvPairListRemaining) +{ + UINT16 id = 0; + size_t cbAvPair = cbAvPairList; + NTLM_AV_PAIR* pAvPair = pAvPairList; + + if (!ntlm_av_pair_check(pAvPair, cbAvPair)) + pAvPair = NULL; + + while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &id)) + { + if (id == AvId) + break; + if (id == MsvAvEOL) + { + pAvPair = NULL; + break; + } + + pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair); + } + + if (!pAvPair) + cbAvPair = 0; + if (pcbAvPairListRemaining) + *pcbAvPairListRemaining = cbAvPair; + + return pAvPair; +} + +static BOOL ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId, + PBYTE Value, UINT16 AvLen) +{ + size_t cbAvPair = 0; + NTLM_AV_PAIR* pAvPair = NULL; + + pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair); + + /* size of header + value length + terminating MsvAvEOL AV_PAIR */ + if (!pAvPair || cbAvPair < 2 * sizeof(NTLM_AV_PAIR) + AvLen) + return FALSE; + + ntlm_av_pair_set_id(pAvPair, (UINT16)AvId); + ntlm_av_pair_set_len(pAvPair, AvLen); + if (AvLen) + { + WINPR_ASSERT(Value != NULL); + CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair), Value, AvLen); + } + + pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair); + return ntlm_av_pair_list_init(pAvPair, cbAvPair); +} + +static BOOL ntlm_av_pair_add_copy(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, + NTLM_AV_PAIR* pAvPair, size_t cbAvPair) +{ + UINT16 pair = 0; + size_t avLen = 0; + + if (!ntlm_av_pair_check(pAvPair, cbAvPair)) + return FALSE; + + if (!ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair)) + return FALSE; + + if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen)) + return FALSE; + + WINPR_ASSERT(avLen <= UINT16_MAX); + return ntlm_av_pair_add(pAvPairList, cbAvPairList, pair, + ntlm_av_pair_get_value_pointer(pAvPair), (UINT16)avLen); +} + +static int ntlm_get_target_computer_name(PUNICODE_STRING pName, COMPUTER_NAME_FORMAT type) +{ + char* name = NULL; + int status = -1; + DWORD nSize = 0; + CHAR* computerName = NULL; + + WINPR_ASSERT(pName); + + if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || GetLastError() != ERROR_MORE_DATA) + return -1; + + computerName = calloc(nSize, sizeof(CHAR)); + + if (!computerName) + return -1; + + if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize)) + { + free(computerName); + return -1; + } + + if (nSize > MAX_COMPUTERNAME_LENGTH) + computerName[MAX_COMPUTERNAME_LENGTH] = '\0'; + + name = computerName; + + if (!name) + return -1; + + if (type == ComputerNameNetBIOS) + CharUpperA(name); + + size_t len = 0; + pName->Buffer = ConvertUtf8ToWCharAlloc(name, &len); + + if (!pName->Buffer || (len == 0) || (len > UINT16_MAX / sizeof(WCHAR))) + { + free(pName->Buffer); + pName->Buffer = NULL; + free(name); + return status; + } + + pName->Length = (USHORT)((len) * sizeof(WCHAR)); + pName->MaximumLength = pName->Length; + free(name); + return 1; +} + +static void ntlm_free_unicode_string(PUNICODE_STRING string) +{ + if (string) + { + if (string->Length > 0) + { + free(string->Buffer); + string->Buffer = NULL; + string->Length = 0; + string->MaximumLength = 0; + } + } +} + +/** + * From http://www.ietf.org/proceedings/72/slides/sasl-2.pdf: + * + * tls-server-end-point: + * + * The hash of the TLS server's end entity certificate as it appears, octet for octet, + * in the server's Certificate message (note that the Certificate message contains a + * certificate_list, the first element of which is the server's end entity certificate.) + * The hash function to be selected is as follows: if the certificate's signature hash + * algorithm is either MD5 or SHA-1, then use SHA-256, otherwise use the certificate's + * signature hash algorithm. + */ + +/** + * Channel Bindings sample usage: + * https://raw.github.com/mozilla/mozilla-central/master/extensions/auth/nsAuthSSPI.cpp + */ + +/* +typedef struct gss_channel_bindings_struct { + OM_uint32 initiator_addrtype; + gss_buffer_desc initiator_address; + OM_uint32 acceptor_addrtype; + gss_buffer_desc acceptor_address; + gss_buffer_desc application_data; +} *gss_channel_bindings_t; + */ + +static BOOL ntlm_md5_update_uint32_be(WINPR_DIGEST_CTX* md5, UINT32 num) +{ + BYTE be32[4]; + be32[0] = (num >> 0) & 0xFF; + be32[1] = (num >> 8) & 0xFF; + be32[2] = (num >> 16) & 0xFF; + be32[3] = (num >> 24) & 0xFF; + return winpr_Digest_Update(md5, be32, 4); +} + +static void ntlm_compute_channel_bindings(NTLM_CONTEXT* context) +{ + WINPR_DIGEST_CTX* md5 = NULL; + BYTE* ChannelBindingToken = NULL; + UINT32 ChannelBindingTokenLength = 0; + SEC_CHANNEL_BINDINGS* ChannelBindings = NULL; + + WINPR_ASSERT(context); + + ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH); + ChannelBindings = context->Bindings.Bindings; + + if (!ChannelBindings) + return; + + if (!(md5 = winpr_Digest_New())) + return; + + if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) + goto out; + + ChannelBindingTokenLength = context->Bindings.BindingsLength - sizeof(SEC_CHANNEL_BINDINGS); + ChannelBindingToken = &((BYTE*)ChannelBindings)[ChannelBindings->dwApplicationDataOffset]; + + if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwInitiatorAddrType)) + goto out; + + if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbInitiatorLength)) + goto out; + + if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwAcceptorAddrType)) + goto out; + + if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbAcceptorLength)) + goto out; + + if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbApplicationDataLength)) + goto out; + + if (!winpr_Digest_Update(md5, (void*)ChannelBindingToken, ChannelBindingTokenLength)) + goto out; + + if (!winpr_Digest_Final(md5, context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH)) + goto out; + +out: + winpr_Digest_Free(md5); +} + +static void ntlm_compute_single_host_data(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + /** + * The Single_Host_Data structure allows a client to send machine-specific information + * within an authentication exchange to services on the same machine. The client can + * produce additional information to be processed in an implementation-specific way when + * the client and server are on the same host. If the server and client platforms are + * different or if they are on different hosts, then the information MUST be ignored. + * Any fields after the MachineID field MUST be ignored on receipt. + */ + Data_Write_UINT32(&context->SingleHostData.Size, 48); + Data_Write_UINT32(&context->SingleHostData.Z4, 0); + Data_Write_UINT32(&context->SingleHostData.DataPresent, 1); + Data_Write_UINT32(&context->SingleHostData.CustomData, SECURITY_MANDATORY_MEDIUM_RID); + FillMemory(context->SingleHostData.MachineID, 32, 0xAA); +} + +BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context) +{ + BOOL rc = FALSE; + ULONG length = 0; + ULONG AvPairsCount = 0; + ULONG AvPairsLength = 0; + NTLM_AV_PAIR* pAvPairList = NULL; + size_t cbAvPairList = 0; + UNICODE_STRING NbDomainName = { 0 }; + UNICODE_STRING NbComputerName = { 0 }; + UNICODE_STRING DnsDomainName = { 0 }; + UNICODE_STRING DnsComputerName = { 0 }; + + WINPR_ASSERT(context); + + if (ntlm_get_target_computer_name(&NbDomainName, ComputerNameNetBIOS) < 0) + goto fail; + + NbComputerName.Buffer = NULL; + + if (ntlm_get_target_computer_name(&NbComputerName, ComputerNameNetBIOS) < 0) + goto fail; + + DnsDomainName.Buffer = NULL; + + if (ntlm_get_target_computer_name(&DnsDomainName, ComputerNameDnsDomain) < 0) + goto fail; + + DnsComputerName.Buffer = NULL; + + if (ntlm_get_target_computer_name(&DnsComputerName, ComputerNameDnsHostname) < 0) + goto fail; + + AvPairsCount = 5; + AvPairsLength = NbDomainName.Length + NbComputerName.Length + DnsDomainName.Length + + DnsComputerName.Length + 8; + length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength); + + if (!sspi_SecBufferAlloc(&context->ChallengeTargetInfo, length)) + goto fail; + + pAvPairList = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer; + cbAvPairList = context->ChallengeTargetInfo.cbBuffer; + + if (!ntlm_av_pair_list_init(pAvPairList, cbAvPairList)) + goto fail; + + if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbDomainName, (PBYTE)NbDomainName.Buffer, + NbDomainName.Length)) + goto fail; + + if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbComputerName, + (PBYTE)NbComputerName.Buffer, NbComputerName.Length)) + goto fail; + + if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsDomainName, + (PBYTE)DnsDomainName.Buffer, DnsDomainName.Length)) + goto fail; + + if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsComputerName, + (PBYTE)DnsComputerName.Buffer, DnsComputerName.Length)) + goto fail; + + if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvTimestamp, context->Timestamp, + sizeof(context->Timestamp))) + goto fail; + + rc = TRUE; +fail: + ntlm_free_unicode_string(&NbDomainName); + ntlm_free_unicode_string(&NbComputerName); + ntlm_free_unicode_string(&DnsDomainName); + ntlm_free_unicode_string(&DnsComputerName); + return rc; +} + +BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) +{ + ULONG size = 0; + ULONG AvPairsCount = 0; + ULONG AvPairsValueLength = 0; + NTLM_AV_PAIR* AvTimestamp = NULL; + NTLM_AV_PAIR* AvNbDomainName = NULL; + NTLM_AV_PAIR* AvNbComputerName = NULL; + NTLM_AV_PAIR* AvDnsDomainName = NULL; + NTLM_AV_PAIR* AvDnsComputerName = NULL; + NTLM_AV_PAIR* AvDnsTreeName = NULL; + NTLM_AV_PAIR* ChallengeTargetInfo = NULL; + NTLM_AV_PAIR* AuthenticateTargetInfo = NULL; + size_t cbAvTimestamp = 0; + size_t cbAvNbDomainName = 0; + size_t cbAvNbComputerName = 0; + size_t cbAvDnsDomainName = 0; + size_t cbAvDnsComputerName = 0; + size_t cbAvDnsTreeName = 0; + size_t cbChallengeTargetInfo = 0; + size_t cbAuthenticateTargetInfo = 0; + + WINPR_ASSERT(context); + + AvPairsCount = 1; + AvPairsValueLength = 0; + ChallengeTargetInfo = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer; + cbChallengeTargetInfo = context->ChallengeTargetInfo.cbBuffer; + AvNbDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbDomainName, + &cbAvNbDomainName); + AvNbComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, + MsvAvNbComputerName, &cbAvNbComputerName); + AvDnsDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, + MsvAvDnsDomainName, &cbAvDnsDomainName); + AvDnsComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, + MsvAvDnsComputerName, &cbAvDnsComputerName); + AvDnsTreeName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsTreeName, + &cbAvDnsTreeName); + AvTimestamp = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvTimestamp, + &cbAvTimestamp); + + if (AvNbDomainName) + { + size_t avLen = 0; + if (!ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName, &avLen)) + goto fail; + AvPairsCount++; /* MsvAvNbDomainName */ + AvPairsValueLength += avLen; + } + + if (AvNbComputerName) + { + size_t avLen = 0; + if (!ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName, &avLen)) + goto fail; + AvPairsCount++; /* MsvAvNbComputerName */ + AvPairsValueLength += avLen; + } + + if (AvDnsDomainName) + { + size_t avLen = 0; + if (!ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName, &avLen)) + goto fail; + AvPairsCount++; /* MsvAvDnsDomainName */ + AvPairsValueLength += avLen; + } + + if (AvDnsComputerName) + { + size_t avLen = 0; + if (!ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName, &avLen)) + goto fail; + AvPairsCount++; /* MsvAvDnsComputerName */ + AvPairsValueLength += avLen; + } + + if (AvDnsTreeName) + { + size_t avLen = 0; + if (!ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName, &avLen)) + goto fail; + AvPairsCount++; /* MsvAvDnsTreeName */ + AvPairsValueLength += avLen; + } + + AvPairsCount++; /* MsvAvTimestamp */ + AvPairsValueLength += 8; + + if (context->UseMIC) + { + AvPairsCount++; /* MsvAvFlags */ + AvPairsValueLength += 4; + } + + if (context->SendSingleHostData) + { + AvPairsCount++; /* MsvAvSingleHost */ + ntlm_compute_single_host_data(context); + AvPairsValueLength += context->SingleHostData.Size; + } + + /** + * Extended Protection for Authentication: + * http://blogs.technet.com/b/srd/archive/2009/12/08/extended-protection-for-authentication.aspx + */ + + if (!context->SuppressExtendedProtection) + { + /** + * SEC_CHANNEL_BINDINGS structure + * http://msdn.microsoft.com/en-us/library/windows/desktop/dd919963/ + */ + AvPairsCount++; /* MsvAvChannelBindings */ + AvPairsValueLength += 16; + ntlm_compute_channel_bindings(context); + + if (context->ServicePrincipalName.Length > 0) + { + AvPairsCount++; /* MsvAvTargetName */ + AvPairsValueLength += context->ServicePrincipalName.Length; + } + } + + size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength); + + if (context->NTLMv2) + size += 8; /* unknown 8-byte padding */ + + if (!sspi_SecBufferAlloc(&context->AuthenticateTargetInfo, size)) + goto fail; + + AuthenticateTargetInfo = (NTLM_AV_PAIR*)context->AuthenticateTargetInfo.pvBuffer; + cbAuthenticateTargetInfo = context->AuthenticateTargetInfo.cbBuffer; + + if (!ntlm_av_pair_list_init(AuthenticateTargetInfo, cbAuthenticateTargetInfo)) + goto fail; + + if (AvNbDomainName) + { + if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvNbDomainName, + cbAvNbDomainName)) + goto fail; + } + + if (AvNbComputerName) + { + if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, + AvNbComputerName, cbAvNbComputerName)) + goto fail; + } + + if (AvDnsDomainName) + { + if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, + AvDnsDomainName, cbAvDnsDomainName)) + goto fail; + } + + if (AvDnsComputerName) + { + if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, + AvDnsComputerName, cbAvDnsComputerName)) + goto fail; + } + + if (AvDnsTreeName) + { + if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvDnsTreeName, + cbAvDnsTreeName)) + goto fail; + } + + if (AvTimestamp) + { + if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvTimestamp, + cbAvTimestamp)) + goto fail; + } + + if (context->UseMIC) + { + UINT32 flags = 0; + Data_Write_UINT32(&flags, MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK); + + if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvFlags, + (PBYTE)&flags, 4)) + goto fail; + } + + if (context->SendSingleHostData) + { + WINPR_ASSERT(context->SingleHostData.Size <= UINT16_MAX); + if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost, + (PBYTE)&context->SingleHostData, + (UINT16)context->SingleHostData.Size)) + goto fail; + } + + if (!context->SuppressExtendedProtection) + { + if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, + MsvAvChannelBindings, context->ChannelBindingsHash, 16)) + goto fail; + + if (context->ServicePrincipalName.Length > 0) + { + if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvTargetName, + (PBYTE)context->ServicePrincipalName.Buffer, + context->ServicePrincipalName.Length)) + goto fail; + } + } + + if (context->NTLMv2) + { + NTLM_AV_PAIR* AvEOL = NULL; + AvEOL = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, NULL); + + if (!AvEOL) + goto fail; + + ZeroMemory(AvEOL, sizeof(NTLM_AV_PAIR)); + } + + return TRUE; +fail: + sspi_SecBufferFree(&context->AuthenticateTargetInfo); + return FALSE; +} diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h new file mode 100644 index 0000000..ab9da43 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h @@ -0,0 +1,42 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package (AV_PAIRs) + * + * Copyright 2011-2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_NTLM_AV_PAIRS_H +#define WINPR_SSPI_NTLM_AV_PAIRS_H + +#include + +#include "ntlm.h" + +#include + +ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList); + +#ifdef WITH_DEBUG_NTLM +void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList); +#endif + +PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair); +NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId, + size_t* pcbAvPairListRemaining); + +BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context); +BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context); + +#endif /* WINPR_SSPI_NTLM_AV_PAIRS_H */ diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c new file mode 100644 index 0000000..9c6e818 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c @@ -0,0 +1,887 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package (Compute) + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "ntlm.h" +#include "../sspi.h" + +#include +#include +#include +#include +#include +#include + +#include "ntlm_compute.h" + +#include "../../log.h" +#define TAG WINPR_TAG("sspi.NTLM") + +#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what) \ + Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \ + __func__, __FILE__, (size_t)__LINE__) + +static char NTLM_CLIENT_SIGN_MAGIC[] = "session key to client-to-server signing key magic constant"; +static char NTLM_SERVER_SIGN_MAGIC[] = "session key to server-to-client signing key magic constant"; +static char NTLM_CLIENT_SEAL_MAGIC[] = "session key to client-to-server sealing key magic constant"; +static char NTLM_SERVER_SEAL_MAGIC[] = "session key to server-to-client sealing key magic constant"; + +static const BYTE NTLM_NULL_BUFFER[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/** + * Populate VERSION structure msdn{cc236654} + * @param versionInfo A pointer to the version struct + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo) +{ + WINPR_ASSERT(versionInfo); + +#if defined(WITH_WINPR_DEPRECATED) + OSVERSIONINFOA osVersionInfo = { 0 }; + osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA(&osVersionInfo)) + return FALSE; + versionInfo->ProductMajorVersion = (UINT8)osVersionInfo.dwMajorVersion; + versionInfo->ProductMinorVersion = (UINT8)osVersionInfo.dwMinorVersion; + versionInfo->ProductBuild = (UINT16)osVersionInfo.dwBuildNumber; +#else + /* Always return fixed version number. + * + * ProductVersion is fixed since windows 10 to Major 10, Minor 0 + * ProductBuild taken from https://en.wikipedia.org/wiki/Windows_11_version_history + * with most recent (pre) release build number + */ + versionInfo->ProductMajorVersion = 10; + versionInfo->ProductMinorVersion = 0; + versionInfo->ProductBuild = 22631; +#endif + ZeroMemory(versionInfo->Reserved, sizeof(versionInfo->Reserved)); + versionInfo->NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; + return TRUE; +} + +/** + * Read VERSION structure. msdn{cc236654} + * @param s A pointer to a stream to read + * @param versionInfo A pointer to the struct to read data to + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(versionInfo); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return FALSE; + + Stream_Read_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */ + Stream_Read_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */ + Stream_Read_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */ + Stream_Read(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */ + Stream_Read_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */ + return TRUE; +} + +/** + * Write VERSION structure. msdn{cc236654} + * @param s A pointer to the stream to write to + * @param versionInfo A pointer to the buffer to read the data from + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(versionInfo); + + if (!Stream_CheckAndLogRequiredCapacityEx( + TAG, WLOG_WARN, s, 5ull + sizeof(versionInfo->Reserved), 1ull, + "%s(%s:%" PRIuz ") NTLM_VERSION_INFO", __func__, __FILE__, (size_t)__LINE__)) + return FALSE; + + Stream_Write_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */ + Stream_Write_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */ + Stream_Write_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */ + Stream_Write(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */ + Stream_Write_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */ + return TRUE; +} + +/** + * Print VERSION structure. msdn{cc236654} + * @param versionInfo A pointer to the struct containing the data to print + */ +#ifdef WITH_DEBUG_NTLM +void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo) +{ + WINPR_ASSERT(versionInfo); + + WLog_VRB(TAG, "VERSION ={"); + WLog_VRB(TAG, "\tProductMajorVersion: %" PRIu8 "", versionInfo->ProductMajorVersion); + WLog_VRB(TAG, "\tProductMinorVersion: %" PRIu8 "", versionInfo->ProductMinorVersion); + WLog_VRB(TAG, "\tProductBuild: %" PRIu16 "", versionInfo->ProductBuild); + WLog_VRB(TAG, "\tReserved: 0x%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "", versionInfo->Reserved[0], + versionInfo->Reserved[1], versionInfo->Reserved[2]); + WLog_VRB(TAG, "\tNTLMRevisionCurrent: 0x%02" PRIX8 "", versionInfo->NTLMRevisionCurrent); +} +#endif + +static BOOL ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge) +{ + size_t size = 0; + WINPR_ASSERT(s); + WINPR_ASSERT(challenge); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 28)) + return FALSE; + + Stream_Read_UINT8(s, challenge->RespType); + Stream_Read_UINT8(s, challenge->HiRespType); + Stream_Read_UINT16(s, challenge->Reserved1); + Stream_Read_UINT32(s, challenge->Reserved2); + Stream_Read(s, challenge->Timestamp, 8); + Stream_Read(s, challenge->ClientChallenge, 8); + Stream_Read_UINT32(s, challenge->Reserved3); + size = Stream_Length(s) - Stream_GetPosition(s); + + if (size > UINT32_MAX) + { + WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::cbAvPairs too large, got %" PRIuz "bytes", size); + return FALSE; + } + + challenge->cbAvPairs = (UINT32)size; + challenge->AvPairs = (NTLM_AV_PAIR*)malloc(challenge->cbAvPairs); + + if (!challenge->AvPairs) + { + WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::AvPairs failed to allocate %" PRIu32 "bytes", + challenge->cbAvPairs); + return FALSE; + } + + Stream_Read(s, challenge->AvPairs, size); + return TRUE; +} + +static BOOL ntlm_write_ntlm_v2_client_challenge(wStream* s, + const NTLMv2_CLIENT_CHALLENGE* challenge) +{ + ULONG length = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(challenge); + + if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 28, "NTLMv2_CLIENT_CHALLENGE")) + return FALSE; + + Stream_Write_UINT8(s, challenge->RespType); + Stream_Write_UINT8(s, challenge->HiRespType); + Stream_Write_UINT16(s, challenge->Reserved1); + Stream_Write_UINT32(s, challenge->Reserved2); + Stream_Write(s, challenge->Timestamp, 8); + Stream_Write(s, challenge->ClientChallenge, 8); + Stream_Write_UINT32(s, challenge->Reserved3); + length = ntlm_av_pair_list_length(challenge->AvPairs, challenge->cbAvPairs); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) + return FALSE; + + Stream_Write(s, challenge->AvPairs, length); + return TRUE; +} + +BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(response); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return FALSE; + + Stream_Read(s, response->Response, 16); + return ntlm_read_ntlm_v2_client_challenge(s, &(response->Challenge)); +} + +BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(response); + + if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16ull, "NTLMv2_RESPONSE")) + return FALSE; + + Stream_Write(s, response->Response, 16); + return ntlm_write_ntlm_v2_client_challenge(s, &(response->Challenge)); +} + +/** + * Get current time, in tenths of microseconds since midnight of January 1, 1601. + * @param[out] timestamp 64-bit little-endian timestamp + */ + +void ntlm_current_time(BYTE* timestamp) +{ + FILETIME filetime = { 0 }; + ULARGE_INTEGER time64 = { 0 }; + + WINPR_ASSERT(timestamp); + + GetSystemTimeAsFileTime(&filetime); + time64.u.LowPart = filetime.dwLowDateTime; + time64.u.HighPart = filetime.dwHighDateTime; + CopyMemory(timestamp, &(time64.QuadPart), 8); +} + +/** + * Generate timestamp for AUTHENTICATE_MESSAGE. + * + * @param context A pointer to the NTLM context + */ + +void ntlm_generate_timestamp(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + if (memcmp(context->ChallengeTimestamp, NTLM_NULL_BUFFER, 8) != 0) + CopyMemory(context->Timestamp, context->ChallengeTimestamp, 8); + else + ntlm_current_time(context->Timestamp); +} + +static BOOL ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) +{ + BOOL rc = FALSE; + WINPR_SAM* sam = NULL; + WINPR_SAM_ENTRY* entry = NULL; + SSPI_CREDENTIALS* credentials = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(hash); + + credentials = context->credentials; + sam = SamOpen(context->SamFile, TRUE); + + if (!sam) + goto fail; + + entry = SamLookupUserW( + sam, (LPWSTR)credentials->identity.User, credentials->identity.UserLength * sizeof(WCHAR), + (LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * sizeof(WCHAR)); + + if (!entry) + { + entry = SamLookupUserW(sam, (LPWSTR)credentials->identity.User, + credentials->identity.UserLength * sizeof(WCHAR), NULL, 0); + } + + if (!entry) + goto fail; + +#ifdef WITH_DEBUG_NTLM + WLog_VRB(TAG, "NTLM Hash:"); + winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16); +#endif + NTOWFv2FromHashW(entry->NtHash, (LPWSTR)credentials->identity.User, + credentials->identity.UserLength * sizeof(WCHAR), + (LPWSTR)credentials->identity.Domain, + credentials->identity.DomainLength * sizeof(WCHAR), (BYTE*)hash); + + rc = TRUE; + +fail: + SamFreeEntry(sam, entry); + SamClose(sam); + if (!rc) + WLog_ERR(TAG, "Error: Could not find user in SAM database"); + + return rc; +} + +static int ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash) +{ + char PasswordHash[32] = { 0 }; + INT64 PasswordHashLength = 0; + SSPI_CREDENTIALS* credentials = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(hash); + + credentials = context->credentials; + /* Password contains a password hash of length (PasswordLength - + * SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) */ + PasswordHashLength = credentials->identity.PasswordLength - SSPI_CREDENTIALS_HASH_LENGTH_OFFSET; + + WINPR_ASSERT(PasswordHashLength >= 0); + WINPR_ASSERT((size_t)PasswordHashLength < ARRAYSIZE(PasswordHash)); + if (ConvertWCharNToUtf8(credentials->identity.Password, PasswordHashLength, PasswordHash, + ARRAYSIZE(PasswordHash)) <= 0) + return -1; + + CharUpperBuffA(PasswordHash, (DWORD)PasswordHashLength); + + for (size_t i = 0; i < ARRAYSIZE(PasswordHash); i += 2) + { + BYTE hn = + (BYTE)(PasswordHash[i] > '9' ? PasswordHash[i] - 'A' + 10 : PasswordHash[i] - '0'); + BYTE ln = (BYTE)(PasswordHash[i + 1] > '9' ? PasswordHash[i + 1] - 'A' + 10 + : PasswordHash[i + 1] - '0'); + hash[i / 2] = (BYTE)((hn << 4) | ln); + } + + return 1; +} + +static BOOL ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) +{ + SSPI_CREDENTIALS* credentials = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(hash); + + credentials = context->credentials; +#ifdef WITH_DEBUG_NTLM + + if (credentials) + { + WLog_VRB(TAG, "Password (length = %" PRIu32 ")", credentials->identity.PasswordLength * 2); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Password, + credentials->identity.PasswordLength * 2); + WLog_VRB(TAG, "Username (length = %" PRIu32 ")", credentials->identity.UserLength * 2); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.User, + credentials->identity.UserLength * 2); + WLog_VRB(TAG, "Domain (length = %" PRIu32 ")", credentials->identity.DomainLength * 2); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Domain, + credentials->identity.DomainLength * 2); + } + else + WLog_VRB(TAG, "Strange, NTLM_CONTEXT is missing valid credentials..."); + + WLog_VRB(TAG, "Workstation (length = %" PRIu16 ")", context->Workstation.Length); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)context->Workstation.Buffer, context->Workstation.Length); + WLog_VRB(TAG, "NTOWFv2, NTLMv2 Hash"); + winpr_HexDump(TAG, WLOG_TRACE, context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH); +#endif + + if (memcmp(context->NtlmV2Hash, NTLM_NULL_BUFFER, 16) != 0) + return TRUE; + + if (!credentials) + return FALSE; + else if (memcmp(context->NtlmHash, NTLM_NULL_BUFFER, 16) != 0) + { + NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User, + credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain, + credentials->identity.DomainLength * 2, (BYTE*)hash); + } + else if (credentials->identity.PasswordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) + { + /* Special case for WinPR: password hash */ + if (ntlm_convert_password_hash(context, context->NtlmHash) < 0) + return FALSE; + + NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User, + credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain, + credentials->identity.DomainLength * 2, (BYTE*)hash); + } + else if (credentials->identity.Password) + { + NTOWFv2W((LPWSTR)credentials->identity.Password, credentials->identity.PasswordLength * 2, + (LPWSTR)credentials->identity.User, credentials->identity.UserLength * 2, + (LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * 2, + (BYTE*)hash); + } + else if (context->HashCallback) + { + int ret = 0; + SecBuffer proofValue; + SecBuffer micValue; + + if (ntlm_computeProofValue(context, &proofValue) != SEC_E_OK) + return FALSE; + + if (ntlm_computeMicValue(context, &micValue) != SEC_E_OK) + { + sspi_SecBufferFree(&proofValue); + return FALSE; + } + + ret = context->HashCallback(context->HashCallbackArg, &credentials->identity, &proofValue, + context->EncryptedRandomSessionKey, + context->AUTHENTICATE_MESSAGE.MessageIntegrityCheck, &micValue, + hash); + sspi_SecBufferFree(&proofValue); + sspi_SecBufferFree(&micValue); + return ret ? TRUE : FALSE; + } + else if (context->UseSamFileDatabase) + { + return ntlm_fetch_ntlm_v2_hash(context, hash); + } + + return TRUE; +} + +BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) +{ + BYTE* response = NULL; + BYTE value[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + + WINPR_ASSERT(context); + + if (context->LmCompatibilityLevel < 2) + { + if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24)) + return FALSE; + + ZeroMemory(context->LmChallengeResponse.pvBuffer, 24); + return TRUE; + } + + /* Compute the NTLMv2 hash */ + + if (!ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash)) + return FALSE; + + /* Concatenate the server and client challenges */ + CopyMemory(value, context->ServerChallenge, 8); + CopyMemory(&value[8], context->ClientChallenge, 8); + + if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24)) + return FALSE; + + response = (BYTE*)context->LmChallengeResponse.pvBuffer; + /* Compute the HMAC-MD5 hash of the resulting value using the NTLMv2 hash as the key */ + winpr_HMAC(WINPR_MD_MD5, (void*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, (BYTE*)value, + WINPR_MD5_DIGEST_LENGTH, (BYTE*)response, WINPR_MD5_DIGEST_LENGTH); + /* Concatenate the resulting HMAC-MD5 hash and the client challenge, giving us the LMv2 response + * (24 bytes) */ + CopyMemory(&response[16], context->ClientChallenge, 8); + return TRUE; +} + +/** + * Compute NTLMv2 Response. + * + * NTLMv2_RESPONSE msdn{cc236653} + * NTLMv2 Authentication msdn{cc236700} + * + * @param context A pointer to the NTLM context + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) +{ + BYTE* blob = NULL; + SecBuffer ntlm_v2_temp = { 0 }; + SecBuffer ntlm_v2_temp_chal = { 0 }; + PSecBuffer TargetInfo = NULL; + + WINPR_ASSERT(context); + + TargetInfo = &context->ChallengeTargetInfo; + BOOL ret = FALSE; + + if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28)) + goto exit; + + ZeroMemory(ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); + blob = (BYTE*)ntlm_v2_temp.pvBuffer; + + /* Compute the NTLMv2 hash */ + if (!ntlm_compute_ntlm_v2_hash(context, (BYTE*)context->NtlmV2Hash)) + goto exit; + + /* Construct temp */ + blob[0] = 1; /* RespType (1 byte) */ + blob[1] = 1; /* HighRespType (1 byte) */ + /* Reserved1 (2 bytes) */ + /* Reserved2 (4 bytes) */ + CopyMemory(&blob[8], context->Timestamp, 8); /* Timestamp (8 bytes) */ + CopyMemory(&blob[16], context->ClientChallenge, 8); /* ClientChallenge (8 bytes) */ + /* Reserved3 (4 bytes) */ + CopyMemory(&blob[28], TargetInfo->pvBuffer, TargetInfo->cbBuffer); +#ifdef WITH_DEBUG_NTLM + WLog_VRB(TAG, "NTLMv2 Response Temp Blob"); + winpr_HexDump(TAG, WLOG_TRACE, ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); +#endif + + /* Concatenate server challenge with temp */ + + if (!sspi_SecBufferAlloc(&ntlm_v2_temp_chal, ntlm_v2_temp.cbBuffer + 8)) + goto exit; + + blob = (BYTE*)ntlm_v2_temp_chal.pvBuffer; + CopyMemory(blob, context->ServerChallenge, 8); + CopyMemory(&blob[8], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); + winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, + (BYTE*)ntlm_v2_temp_chal.pvBuffer, ntlm_v2_temp_chal.cbBuffer, + context->NtProofString, WINPR_MD5_DIGEST_LENGTH); + + /* NtChallengeResponse, Concatenate NTProofStr with temp */ + + if (!sspi_SecBufferAlloc(&context->NtChallengeResponse, ntlm_v2_temp.cbBuffer + 16)) + goto exit; + + blob = (BYTE*)context->NtChallengeResponse.pvBuffer; + CopyMemory(blob, context->NtProofString, WINPR_MD5_DIGEST_LENGTH); + CopyMemory(&blob[16], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); + /* Compute SessionBaseKey, the HMAC-MD5 hash of NTProofStr using the NTLMv2 hash as the key */ + winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, + context->NtProofString, WINPR_MD5_DIGEST_LENGTH, context->SessionBaseKey, + WINPR_MD5_DIGEST_LENGTH); + ret = TRUE; +exit: + sspi_SecBufferFree(&ntlm_v2_temp); + sspi_SecBufferFree(&ntlm_v2_temp_chal); + return ret; +} + +/** + * Encrypt the given plain text using RC4 and the given key. + * @param key RC4 key + * @param length text length + * @param plaintext plain text + * @param ciphertext cipher text + */ + +void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext) +{ + WINPR_RC4_CTX* rc4 = winpr_RC4_New(key, 16); + + if (rc4) + { + winpr_RC4_Update(rc4, length, plaintext, ciphertext); + winpr_RC4_Free(rc4); + } +} + +/** + * Generate client challenge (8-byte nonce). + * @param context A pointer to the NTLM context + */ + +void ntlm_generate_client_challenge(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + /* ClientChallenge is used in computation of LMv2 and NTLMv2 responses */ + if (memcmp(context->ClientChallenge, NTLM_NULL_BUFFER, sizeof(context->ClientChallenge)) == 0) + winpr_RAND(context->ClientChallenge, sizeof(context->ClientChallenge)); +} + +/** + * Generate server challenge (8-byte nonce). + * @param context A pointer to the NTLM context + */ + +void ntlm_generate_server_challenge(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, sizeof(context->ServerChallenge)) == 0) + winpr_RAND(context->ServerChallenge, sizeof(context->ServerChallenge)); +} + +/** + * Generate KeyExchangeKey (the 128-bit SessionBaseKey). msdn{cc236710} + * @param context A pointer to the NTLM context + */ + +void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + WINPR_ASSERT(sizeof(context->KeyExchangeKey) == sizeof(context->SessionBaseKey)); + + /* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */ + CopyMemory(context->KeyExchangeKey, context->SessionBaseKey, sizeof(context->KeyExchangeKey)); +} + +/** + * Generate RandomSessionKey (16-byte nonce). + * @param context A pointer to the NTLM context + */ + +void ntlm_generate_random_session_key(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + winpr_RAND(context->RandomSessionKey, sizeof(context->RandomSessionKey)); +} + +/** + * Generate ExportedSessionKey (the RandomSessionKey, exported) + * @param context A pointer to the NTLM context + */ + +void ntlm_generate_exported_session_key(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + CopyMemory(context->ExportedSessionKey, context->RandomSessionKey, + sizeof(context->ExportedSessionKey)); +} + +/** + * Encrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key). + * @param context A pointer to the NTLM context + */ + +void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context) +{ + /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the + * KeyExchangeKey */ + WINPR_ASSERT(context); + ntlm_rc4k(context->KeyExchangeKey, 16, context->RandomSessionKey, + context->EncryptedRandomSessionKey); +} + +/** + * Decrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key). + * @param context A pointer to the NTLM context + */ + +void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the + * KeyExchangeKey */ + + /** + * if (NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) + * Set RandomSessionKey to RC4K(KeyExchangeKey, + * AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey) else Set RandomSessionKey to KeyExchangeKey + */ + if (context->NegotiateKeyExchange) + { + WINPR_ASSERT(sizeof(context->EncryptedRandomSessionKey) == + sizeof(context->RandomSessionKey)); + ntlm_rc4k(context->KeyExchangeKey, sizeof(context->EncryptedRandomSessionKey), + context->EncryptedRandomSessionKey, context->RandomSessionKey); + } + else + { + WINPR_ASSERT(sizeof(context->RandomSessionKey) == sizeof(context->KeyExchangeKey)); + CopyMemory(context->RandomSessionKey, context->KeyExchangeKey, + sizeof(context->RandomSessionKey)); + } +} + +/** + * Generate signing key msdn{cc236711} + * + * @param exported_session_key ExportedSessionKey + * @param sign_magic Sign magic string + * @param signing_key Destination signing key + * + * @return \b TRUE for success, \b FALSE for failure + */ + +static BOOL ntlm_generate_signing_key(BYTE* exported_session_key, const SecBuffer* sign_magic, + BYTE* signing_key) +{ + BOOL rc = FALSE; + size_t length = 0; + BYTE* value = NULL; + + WINPR_ASSERT(exported_session_key); + WINPR_ASSERT(sign_magic); + WINPR_ASSERT(signing_key); + + length = WINPR_MD5_DIGEST_LENGTH + sign_magic->cbBuffer; + value = (BYTE*)malloc(length); + + if (!value) + goto out; + + /* Concatenate ExportedSessionKey with sign magic */ + CopyMemory(value, exported_session_key, WINPR_MD5_DIGEST_LENGTH); + CopyMemory(&value[WINPR_MD5_DIGEST_LENGTH], sign_magic->pvBuffer, sign_magic->cbBuffer); + + rc = winpr_Digest(WINPR_MD_MD5, value, length, signing_key, WINPR_MD5_DIGEST_LENGTH); + +out: + free(value); + return rc; +} + +/** + * Generate client signing key (ClientSigningKey). msdn{cc236711} + * @param context A pointer to the NTLM context + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context) +{ + const SecBuffer signMagic = { sizeof(NTLM_CLIENT_SIGN_MAGIC), 0, NTLM_CLIENT_SIGN_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, + context->ClientSigningKey); +} + +/** + * Generate server signing key (ServerSigningKey). msdn{cc236711} + * @param context A pointer to the NTLM context + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context) +{ + const SecBuffer signMagic = { sizeof(NTLM_SERVER_SIGN_MAGIC), 0, NTLM_SERVER_SIGN_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, + context->ServerSigningKey); +} + +/** + * Generate client sealing key (ClientSealingKey). msdn{cc236712} + * @param context A pointer to the NTLM context + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context) +{ + const SecBuffer sealMagic = { sizeof(NTLM_CLIENT_SEAL_MAGIC), 0, NTLM_CLIENT_SEAL_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, + context->ClientSealingKey); +} + +/** + * Generate server sealing key (ServerSealingKey). msdn{cc236712} + * @param context A pointer to the NTLM context + * + * @return \b TRUE for success, \b FALSE for failure + */ + +BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context) +{ + const SecBuffer sealMagic = { sizeof(NTLM_SERVER_SEAL_MAGIC), 0, NTLM_SERVER_SEAL_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, + context->ServerSealingKey); +} + +/** + * Initialize RC4 stream cipher states for sealing. + * @param context A pointer to the NTLM context + */ + +BOOL ntlm_init_rc4_seal_states(NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + if (context->server) + { + context->SendSigningKey = context->ServerSigningKey; + context->RecvSigningKey = context->ClientSigningKey; + context->SendSealingKey = context->ClientSealingKey; + context->RecvSealingKey = context->ServerSealingKey; + context->SendRc4Seal = + winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey)); + context->RecvRc4Seal = + winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey)); + } + else + { + context->SendSigningKey = context->ClientSigningKey; + context->RecvSigningKey = context->ServerSigningKey; + context->SendSealingKey = context->ServerSealingKey; + context->RecvSealingKey = context->ClientSealingKey; + context->SendRc4Seal = + winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey)); + context->RecvRc4Seal = + winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey)); + } + if (!context->SendRc4Seal) + { + WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal"); + return FALSE; + } + if (!context->RecvRc4Seal) + { + WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal"); + return FALSE; + } + return TRUE; +} + +BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size) +{ + BOOL rc = FALSE; + /* + * Compute the HMAC-MD5 hash of ConcatenationOf(NEGOTIATE_MESSAGE, + * CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE) using the ExportedSessionKey + */ + WINPR_HMAC_CTX* hmac = winpr_HMAC_New(); + + WINPR_ASSERT(context); + WINPR_ASSERT(mic); + WINPR_ASSERT(size >= WINPR_MD5_DIGEST_LENGTH); + + memset(mic, 0, size); + if (!hmac) + return FALSE; + + if (winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->ExportedSessionKey, WINPR_MD5_DIGEST_LENGTH)) + { + winpr_HMAC_Update(hmac, (BYTE*)context->NegotiateMessage.pvBuffer, + context->NegotiateMessage.cbBuffer); + winpr_HMAC_Update(hmac, (BYTE*)context->ChallengeMessage.pvBuffer, + context->ChallengeMessage.cbBuffer); + + if (context->MessageIntegrityCheckOffset > 0) + { + const BYTE* auth = (BYTE*)context->AuthenticateMessage.pvBuffer; + const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + const size_t rest = context->MessageIntegrityCheckOffset + sizeof(data); + + WINPR_ASSERT(rest <= context->AuthenticateMessage.cbBuffer); + winpr_HMAC_Update(hmac, &auth[0], context->MessageIntegrityCheckOffset); + winpr_HMAC_Update(hmac, data, sizeof(data)); + winpr_HMAC_Update(hmac, &auth[rest], context->AuthenticateMessage.cbBuffer - rest); + } + else + { + winpr_HMAC_Update(hmac, (BYTE*)context->AuthenticateMessage.pvBuffer, + context->AuthenticateMessage.cbBuffer); + } + winpr_HMAC_Final(hmac, mic, WINPR_MD5_DIGEST_LENGTH); + rc = TRUE; + } + + winpr_HMAC_Free(hmac); + return rc; +} diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.h b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h new file mode 100644 index 0000000..006a449 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h @@ -0,0 +1,64 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package (Compute) + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_NTLM_COMPUTE_H +#define WINPR_SSPI_NTLM_COMPUTE_H + +#include "ntlm.h" + +#include "ntlm_av_pairs.h" + +BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo); +BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo); +BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo); + +#ifdef WITH_DEBUG_NTLM +void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo); +#endif + +BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response); +BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response); + +void ntlm_output_target_name(NTLM_CONTEXT* context); +void ntlm_output_channel_bindings(NTLM_CONTEXT* context); + +void ntlm_current_time(BYTE* timestamp); +void ntlm_generate_timestamp(NTLM_CONTEXT* context); + +BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context); +BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context); + +void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext); +void ntlm_generate_client_challenge(NTLM_CONTEXT* context); +void ntlm_generate_server_challenge(NTLM_CONTEXT* context); +void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context); +void ntlm_generate_random_session_key(NTLM_CONTEXT* context); +void ntlm_generate_exported_session_key(NTLM_CONTEXT* context); +void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context); +void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context); + +BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context); +BOOL ntlm_init_rc4_seal_states(NTLM_CONTEXT* context); + +BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size); + +#endif /* WINPR_AUTH_NTLM_COMPUTE_H */ diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_export.h b/winpr/libwinpr/sspi/NTLM/ntlm_export.h new file mode 100644 index 0000000..5249be2 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_export.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_NTLM_EXPORT_H +#define WINPR_SSPI_NTLM_EXPORT_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern const SecPkgInfoA NTLM_SecPkgInfoA; + extern const SecPkgInfoW NTLM_SecPkgInfoW; + extern const SecurityFunctionTableA NTLM_SecurityFunctionTableA; + extern const SecurityFunctionTableW NTLM_SecurityFunctionTableW; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.c b/winpr/libwinpr/sspi/NTLM/ntlm_message.c new file mode 100644 index 0000000..511ca47 --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.c @@ -0,0 +1,1397 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package (Message) + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ntlm.h" +#include "../sspi.h" + +#include +#include +#include +#include +#include + +#include "ntlm_compute.h" + +#include "ntlm_message.h" + +#include "../../log.h" +#define TAG WINPR_TAG("sspi.NTLM") + +#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what) \ + Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \ + __func__, __FILE__, (size_t)__LINE__) + +static const char NTLM_SIGNATURE[8] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' }; + +static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields); + +const char* ntlm_get_negotiate_string(UINT32 flag) +{ + if (flag & NTLMSSP_NEGOTIATE_56) + return "NTLMSSP_NEGOTIATE_56"; + if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH) + return "NTLMSSP_NEGOTIATE_KEY_EXCH"; + if (flag & NTLMSSP_NEGOTIATE_128) + return "NTLMSSP_NEGOTIATE_128"; + if (flag & NTLMSSP_RESERVED1) + return "NTLMSSP_RESERVED1"; + if (flag & NTLMSSP_RESERVED2) + return "NTLMSSP_RESERVED2"; + if (flag & NTLMSSP_RESERVED3) + return "NTLMSSP_RESERVED3"; + if (flag & NTLMSSP_NEGOTIATE_VERSION) + return "NTLMSSP_NEGOTIATE_VERSION"; + if (flag & NTLMSSP_RESERVED4) + return "NTLMSSP_RESERVED4"; + if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO) + return "NTLMSSP_NEGOTIATE_TARGET_INFO"; + if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY) + return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY"; + if (flag & NTLMSSP_RESERVED5) + return "NTLMSSP_RESERVED5"; + if (flag & NTLMSSP_NEGOTIATE_IDENTIFY) + return "NTLMSSP_NEGOTIATE_IDENTIFY"; + if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY) + return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY"; + if (flag & NTLMSSP_RESERVED6) + return "NTLMSSP_RESERVED6"; + if (flag & NTLMSSP_TARGET_TYPE_SERVER) + return "NTLMSSP_TARGET_TYPE_SERVER"; + if (flag & NTLMSSP_TARGET_TYPE_DOMAIN) + return "NTLMSSP_TARGET_TYPE_DOMAIN"; + if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN"; + if (flag & NTLMSSP_RESERVED7) + return "NTLMSSP_RESERVED7"; + if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED"; + if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED"; + if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS) + return "NTLMSSP_NEGOTIATE_ANONYMOUS"; + if (flag & NTLMSSP_RESERVED8) + return "NTLMSSP_RESERVED8"; + if (flag & NTLMSSP_NEGOTIATE_NTLM) + return "NTLMSSP_NEGOTIATE_NTLM"; + if (flag & NTLMSSP_RESERVED9) + return "NTLMSSP_RESERVED9"; + if (flag & NTLMSSP_NEGOTIATE_LM_KEY) + return "NTLMSSP_NEGOTIATE_LM_KEY"; + if (flag & NTLMSSP_NEGOTIATE_DATAGRAM) + return "NTLMSSP_NEGOTIATE_DATAGRAM"; + if (flag & NTLMSSP_NEGOTIATE_SEAL) + return "NTLMSSP_NEGOTIATE_SEAL"; + if (flag & NTLMSSP_NEGOTIATE_SIGN) + return "NTLMSSP_NEGOTIATE_SIGN"; + if (flag & NTLMSSP_RESERVED10) + return "NTLMSSP_RESERVED10"; + if (flag & NTLMSSP_REQUEST_TARGET) + return "NTLMSSP_REQUEST_TARGET"; + if (flag & NTLMSSP_NEGOTIATE_OEM) + return "NTLMSSP_NEGOTIATE_OEM"; + if (flag & NTLMSSP_NEGOTIATE_UNICODE) + return "NTLMSSP_NEGOTIATE_UNICODE"; + return "NTLMSSP_NEGOTIATE_UNKNOWN"; +} + +#if defined(WITH_DEBUG_NTLM) +static void ntlm_print_message_fields(const NTLM_MESSAGE_FIELDS* fields, const char* name) +{ + WINPR_ASSERT(fields); + WINPR_ASSERT(name); + + WLog_VRB(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name, + fields->Len, fields->MaxLen, fields->BufferOffset); + + if (fields->Len > 0) + winpr_HexDump(TAG, WLOG_TRACE, fields->Buffer, fields->Len); +} + +static void ntlm_print_negotiate_flags(UINT32 flags) +{ + WLog_VRB(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags); + + for (int i = 31; i >= 0; i--) + { + if ((flags >> i) & 1) + { + const char* str = ntlm_get_negotiate_string(1 << i); + WLog_VRB(TAG, "\t%s (%d),", str, (31 - i)); + } + } +} + +static void ntlm_print_negotiate_message(const SecBuffer* NegotiateMessage, + const NTLM_NEGOTIATE_MESSAGE* message) +{ + WINPR_ASSERT(NegotiateMessage); + WINPR_ASSERT(message); + + WLog_VRB(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", NegotiateMessage->cbBuffer); + winpr_HexDump(TAG, WLOG_TRACE, NegotiateMessage->pvBuffer, NegotiateMessage->cbBuffer); + ntlm_print_negotiate_flags(message->NegotiateFlags); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_print_version_info(&(message->Version)); +} + +static void ntlm_print_challenge_message(const SecBuffer* ChallengeMessage, + const NTLM_CHALLENGE_MESSAGE* message, + const SecBuffer* ChallengeTargetInfo) +{ + WINPR_ASSERT(ChallengeMessage); + WINPR_ASSERT(message); + + WLog_VRB(TAG, "CHALLENGE_MESSAGE (length = %" PRIu32 ")", ChallengeMessage->cbBuffer); + winpr_HexDump(TAG, WLOG_TRACE, ChallengeMessage->pvBuffer, ChallengeMessage->cbBuffer); + ntlm_print_negotiate_flags(message->NegotiateFlags); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_print_version_info(&(message->Version)); + + ntlm_print_message_fields(&(message->TargetName), "TargetName"); + ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo"); + + if (ChallengeTargetInfo && (ChallengeTargetInfo->cbBuffer > 0)) + { + WLog_VRB(TAG, "ChallengeTargetInfo (%" PRIu32 "):", ChallengeTargetInfo->cbBuffer); + ntlm_print_av_pair_list(ChallengeTargetInfo->pvBuffer, ChallengeTargetInfo->cbBuffer); + } +} + +static void ntlm_print_authenticate_message(const SecBuffer* AuthenticateMessage, + const NTLM_AUTHENTICATE_MESSAGE* message, UINT32 flags, + const SecBuffer* AuthenticateTargetInfo) +{ + WINPR_ASSERT(AuthenticateMessage); + WINPR_ASSERT(message); + + WLog_VRB(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")", AuthenticateMessage->cbBuffer); + winpr_HexDump(TAG, WLOG_TRACE, AuthenticateMessage->pvBuffer, AuthenticateMessage->cbBuffer); + ntlm_print_negotiate_flags(message->NegotiateFlags); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_print_version_info(&(message->Version)); + + if (AuthenticateTargetInfo && (AuthenticateTargetInfo->cbBuffer > 0)) + { + WLog_VRB(TAG, "AuthenticateTargetInfo (%" PRIu32 "):", AuthenticateTargetInfo->cbBuffer); + ntlm_print_av_pair_list(AuthenticateTargetInfo->pvBuffer, AuthenticateTargetInfo->cbBuffer); + } + + ntlm_print_message_fields(&(message->DomainName), "DomainName"); + ntlm_print_message_fields(&(message->UserName), "UserName"); + ntlm_print_message_fields(&(message->Workstation), "Workstation"); + ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse"); + ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse"); + ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey"); + + if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) + { + WLog_VRB(TAG, "MessageIntegrityCheck (length = 16)"); + winpr_HexDump(TAG, WLOG_TRACE, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)); + } +} + +static void ntlm_print_authentication_complete(const NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + WLog_VRB(TAG, "ClientChallenge"); + winpr_HexDump(TAG, WLOG_TRACE, context->ClientChallenge, 8); + WLog_VRB(TAG, "ServerChallenge"); + winpr_HexDump(TAG, WLOG_TRACE, context->ServerChallenge, 8); + WLog_VRB(TAG, "SessionBaseKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->SessionBaseKey, 16); + WLog_VRB(TAG, "KeyExchangeKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->KeyExchangeKey, 16); + WLog_VRB(TAG, "ExportedSessionKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ExportedSessionKey, 16); + WLog_VRB(TAG, "RandomSessionKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->RandomSessionKey, 16); + WLog_VRB(TAG, "ClientSigningKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ClientSigningKey, 16); + WLog_VRB(TAG, "ClientSealingKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ClientSealingKey, 16); + WLog_VRB(TAG, "ServerSigningKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ServerSigningKey, 16); + WLog_VRB(TAG, "ServerSealingKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ServerSealingKey, 16); + WLog_VRB(TAG, "Timestamp"); + winpr_HexDump(TAG, WLOG_TRACE, context->Timestamp, 8); +} +#endif + +static BOOL ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header, UINT32 expected) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(header); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return FALSE; + + Stream_Read(s, header->Signature, 8); + Stream_Read_UINT32(s, header->MessageType); + + if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0) + { + WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s", + header->Signature, NTLM_SIGNATURE); + return FALSE; + } + + if (header->MessageType != expected) + { + WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message tyep, got %s, expected %s", + ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected)); + return FALSE; + } + + return TRUE; +} + +static BOOL ntlm_write_message_header(wStream* s, const NTLM_MESSAGE_HEADER* header) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(header); + + if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, sizeof(NTLM_SIGNATURE) + 4ull, + "NTLM_MESSAGE_HEADER::header")) + return FALSE; + + Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE)); + Stream_Write_UINT32(s, header->MessageType); + + return TRUE; +} + +static BOOL ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType) +{ + WINPR_ASSERT(header); + + CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); + header->MessageType = MessageType; + return TRUE; +} + +static BOOL ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return FALSE; + + ntlm_free_message_fields_buffer(fields); + + Stream_Read_UINT16(s, fields->Len); /* Len (2 bytes) */ + Stream_Read_UINT16(s, fields->MaxLen); /* MaxLen (2 bytes) */ + Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */ + return TRUE; +} + +static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields) +{ + UINT16 MaxLen = 0; + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + + MaxLen = fields->MaxLen; + if (fields->MaxLen < 1) + MaxLen = fields->Len; + + if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), 8, "NTLM_MESSAGE_FIELDS::header")) + return FALSE; + + Stream_Write_UINT16(s, fields->Len); /* Len (2 bytes) */ + Stream_Write_UINT16(s, MaxLen); /* MaxLen (2 bytes) */ + Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */ + return TRUE; +} + +static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + + if (fields->Len > 0) + { + const UINT32 offset = fields->BufferOffset + fields->Len; + + if (fields->BufferOffset > UINT32_MAX - fields->Len) + { + WLog_ERR(TAG, + "NTLM_MESSAGE_FIELDS::BufferOffset %" PRIu32 + " too large, maximum allowed is %" PRIu32, + fields->BufferOffset, UINT32_MAX - fields->Len); + return FALSE; + } + + if (offset > Stream_Length(s)) + { + WLog_ERR(TAG, + "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIu32 " beyond received data %" PRIuz, + offset, Stream_Length(s)); + return FALSE; + } + + fields->Buffer = (PBYTE)malloc(fields->Len); + + if (!fields->Buffer) + { + WLog_ERR(TAG, "NTLM_MESSAGE_FIELDS::Buffer allocation of %" PRIu16 "bytes failed", + fields->Len); + return FALSE; + } + + Stream_SetPosition(s, fields->BufferOffset); + Stream_Read(s, fields->Buffer, fields->Len); + } + + return TRUE; +} + +static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + + if (fields->Len > 0) + { + Stream_SetPosition(s, fields->BufferOffset); + if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), fields->Len, "NTLM_MESSAGE_FIELDS::Len")) + return FALSE; + + Stream_Write(s, fields->Buffer, fields->Len); + } + return TRUE; +} + +void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields) +{ + if (fields) + { + if (fields->Buffer) + { + free(fields->Buffer); + fields->Len = 0; + fields->MaxLen = 0; + fields->Buffer = NULL; + fields->BufferOffset = 0; + } + } +} + +static BOOL ntlm_read_negotiate_flags(wStream* s, UINT32* flags, UINT32 required, const char* name) +{ + UINT32 NegotiateFlags = 0; + char buffer[1024] = { 0 }; + WINPR_ASSERT(s); + WINPR_ASSERT(flags); + WINPR_ASSERT(name); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return FALSE; + + Stream_Read_UINT32(s, NegotiateFlags); /* NegotiateFlags (4 bytes) */ + + if ((NegotiateFlags & required) != required) + { + WLog_ERR(TAG, "%s::NegotiateFlags invalid flags 0x08%" PRIx32 ", 0x%08" PRIx32 " required", + name, NegotiateFlags, required); + return FALSE; + } + + WLog_DBG(TAG, "Read flags %s", + ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), NegotiateFlags)); + *flags = NegotiateFlags; + return TRUE; +} + +static BOOL ntlm_write_negotiate_flags(wStream* s, UINT32 flags, const char* name) +{ + char buffer[1024] = { 0 }; + WINPR_ASSERT(s); + WINPR_ASSERT(name); + + if (!Stream_CheckAndLogRequiredCapacityEx(TAG, WLOG_WARN, s, 4ull, 1ull, + "%s(%s:%" PRIuz ") %s::NegotiateFlags", __func__, + __FILE__, (size_t)__LINE__, name)) + return FALSE; + + WLog_DBG(TAG, "Write flags %s", ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), flags)); + Stream_Write_UINT32(s, flags); /* NegotiateFlags (4 bytes) */ + return TRUE; +} + +static BOOL ntlm_read_message_integrity_check(wStream* s, size_t* offset, BYTE* data, size_t size, + const char* name) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(offset); + WINPR_ASSERT(data); + WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH); + WINPR_ASSERT(name); + + *offset = Stream_GetPosition(s); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, size)) + return FALSE; + + Stream_Read(s, data, size); + return TRUE; +} + +static BOOL ntlm_write_message_integrity_check(wStream* s, size_t offset, const BYTE* data, + size_t size, const char* name) +{ + size_t pos = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(data); + WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH); + WINPR_ASSERT(name); + + pos = Stream_GetPosition(s); + + if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, offset, "MessageIntegrityCheck::offset")) + return FALSE; + + Stream_SetPosition(s, offset); + if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, size, "MessageIntegrityCheck::size")) + return FALSE; + + Stream_Write(s, data, size); + Stream_SetPosition(s, pos); + return TRUE; +} + +SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer) +{ + wStream sbuffer; + wStream* s = NULL; + size_t length = 0; + const NTLM_NEGOTIATE_MESSAGE empty = { 0 }; + NTLM_NEGOTIATE_MESSAGE* message = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + message = &context->NEGOTIATE_MESSAGE; + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); + + if (!s) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE)) + return SEC_E_INVALID_TOKEN; + + if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, + NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_UNICODE, + "NTLM_NEGOTIATE_MESSAGE")) + return SEC_E_INVALID_TOKEN; + + context->NegotiateFlags = message->NegotiateFlags; + + /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */ + // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + { + if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */ + return SEC_E_INVALID_TOKEN; + } + + /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */ + // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + { + if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */ + return SEC_E_INVALID_TOKEN; + } + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + { + if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */ + return SEC_E_INVALID_TOKEN; + } + + if (!ntlm_read_message_fields_buffer(s, &message->DomainName)) + return SEC_E_INVALID_TOKEN; + + if (!ntlm_read_message_fields_buffer(s, &message->Workstation)) + return SEC_E_INVALID_TOKEN; + + length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); + buffer->cbBuffer = (ULONG)length; + + if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length)) + return SEC_E_INTERNAL_ERROR; + + CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer); + context->NegotiateMessage.BufferType = buffer->BufferType; +#if defined(WITH_DEBUG_NTLM) + ntlm_print_negotiate_message(&context->NegotiateMessage, message); +#endif + ntlm_change_state(context, NTLM_STATE_CHALLENGE); + return SEC_I_CONTINUE_NEEDED; +} + +SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer) +{ + wStream sbuffer; + wStream* s = NULL; + size_t length = 0; + const NTLM_NEGOTIATE_MESSAGE empty = { 0 }; + NTLM_NEGOTIATE_MESSAGE* message = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + message = &context->NEGOTIATE_MESSAGE; + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); + + if (!s) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_NEGOTIATE)) + return SEC_E_INTERNAL_ERROR; + + if (context->NTLMv2) + { + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM; + } + + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN; + message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE; + + if (context->confidentiality) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL; + + if (context->SendVersionInfo) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_get_version_info(&(message->Version)); + + context->NegotiateFlags = message->NegotiateFlags; + /* Message Header (12 bytes) */ + if (!ntlm_write_message_header(s, &message->header)) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_NEGOTIATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + + /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */ + /* DomainNameFields (8 bytes) */ + if (!ntlm_write_message_fields(s, &(message->DomainName))) + return SEC_E_INTERNAL_ERROR; + + /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */ + /* WorkstationFields (8 bytes) */ + if (!ntlm_write_message_fields(s, &(message->Workstation))) + return SEC_E_INTERNAL_ERROR; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + { + if (!ntlm_write_version_info(s, &(message->Version))) + return SEC_E_INTERNAL_ERROR; + } + + length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); + buffer->cbBuffer = (ULONG)length; + + if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length)) + return SEC_E_INTERNAL_ERROR; + + CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer); + context->NegotiateMessage.BufferType = buffer->BufferType; +#if defined(WITH_DEBUG_NTLM) + ntlm_print_negotiate_message(&context->NegotiateMessage, message); +#endif + ntlm_change_state(context, NTLM_STATE_CHALLENGE); + return SEC_I_CONTINUE_NEEDED; +} + +SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer) +{ + SECURITY_STATUS status = SEC_E_INVALID_TOKEN; + wStream sbuffer; + wStream* s = NULL; + size_t length = 0; + size_t StartOffset = 0; + size_t PayloadOffset = 0; + NTLM_AV_PAIR* AvTimestamp = NULL; + const NTLM_CHALLENGE_MESSAGE empty = { 0 }; + NTLM_CHALLENGE_MESSAGE* message = NULL; + + if (!context || !buffer) + return SEC_E_INTERNAL_ERROR; + + ntlm_generate_client_challenge(context); + message = &context->CHALLENGE_MESSAGE; + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); + + if (!s) + return SEC_E_INTERNAL_ERROR; + + StartOffset = Stream_GetPosition(s); + + if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE)) + goto fail; + + if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */ + goto fail; + + if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE")) + goto fail; + + context->NegotiateFlags = message->NegotiateFlags; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + goto fail; + + Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */ + CopyMemory(context->ServerChallenge, message->ServerChallenge, 8); + Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */ + + if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */ + goto fail; + + if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + { + if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */ + goto fail; + } + + /* Payload (variable) */ + PayloadOffset = Stream_GetPosition(s); + + status = SEC_E_INTERNAL_ERROR; + if (message->TargetName.Len > 0) + { + if (!ntlm_read_message_fields_buffer(s, &(message->TargetName))) + goto fail; + } + + if (message->TargetInfo.Len > 0) + { + size_t cbAvTimestamp = 0; + + if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo))) + goto fail; + + context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer; + context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len; + AvTimestamp = ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer, + message->TargetInfo.Len, MsvAvTimestamp, &cbAvTimestamp); + + if (AvTimestamp) + { + PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp); + + if (!ptr) + goto fail; + + if (context->NTLMv2) + context->UseMIC = TRUE; + + CopyMemory(context->ChallengeTimestamp, ptr, 8); + } + } + + length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len; + if (length > buffer->cbBuffer) + goto fail; + + if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length)) + goto fail; + + if (context->ChallengeMessage.pvBuffer) + CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s) + StartOffset, length); +#if defined(WITH_DEBUG_NTLM) + ntlm_print_challenge_message(&context->ChallengeMessage, message, NULL); +#endif + /* AV_PAIRs */ + + if (context->NTLMv2) + { + if (!ntlm_construct_authenticate_target_info(context)) + goto fail; + + sspi_SecBufferFree(&context->ChallengeTargetInfo); + context->ChallengeTargetInfo.pvBuffer = context->AuthenticateTargetInfo.pvBuffer; + context->ChallengeTargetInfo.cbBuffer = context->AuthenticateTargetInfo.cbBuffer; + } + + ntlm_generate_timestamp(context); /* Timestamp */ + + if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */ + goto fail; + + if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */ + goto fail; + + ntlm_generate_key_exchange_key(context); /* KeyExchangeKey */ + ntlm_generate_random_session_key(context); /* RandomSessionKey */ + ntlm_generate_exported_session_key(context); /* ExportedSessionKey */ + ntlm_encrypt_random_session_key(context); /* EncryptedRandomSessionKey */ + /* Generate signing keys */ + if (!ntlm_generate_client_signing_key(context)) + goto fail; + if (!ntlm_generate_server_signing_key(context)) + goto fail; + /* Generate sealing keys */ + if (!ntlm_generate_client_sealing_key(context)) + goto fail; + if (!ntlm_generate_server_sealing_key(context)) + goto fail; + /* Initialize RC4 seal state using client sealing key */ + if (!ntlm_init_rc4_seal_states(context)) + goto fail; +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authentication_complete(context); +#endif + ntlm_change_state(context, NTLM_STATE_AUTHENTICATE); + status = SEC_I_CONTINUE_NEEDED; +fail: + ntlm_free_message_fields_buffer(&(message->TargetName)); + return status; +} + +SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, const PSecBuffer buffer) +{ + wStream sbuffer; + wStream* s = NULL; + size_t length = 0; + UINT32 PayloadOffset = 0; + const NTLM_CHALLENGE_MESSAGE empty = { 0 }; + NTLM_CHALLENGE_MESSAGE* message = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + message = &context->CHALLENGE_MESSAGE; + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); + + if (!s) + return SEC_E_INTERNAL_ERROR; + + ntlm_get_version_info(&(message->Version)); /* Version */ + ntlm_generate_server_challenge(context); /* Server Challenge */ + ntlm_generate_timestamp(context); /* Timestamp */ + + if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */ + return SEC_E_INTERNAL_ERROR; + + CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */ + message->NegotiateFlags = context->NegotiateFlags; + if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE)) + return SEC_E_INTERNAL_ERROR; + + /* Message Header (12 bytes) */ + if (!ntlm_write_message_header(s, &message->header)) + return SEC_E_INTERNAL_ERROR; + + if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET) + { + message->TargetName.Len = (UINT16)context->TargetName.cbBuffer; + message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer; + } + + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO) + { + message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer; + message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer; + } + + PayloadOffset = 48; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + PayloadOffset += 8; + + message->TargetName.BufferOffset = PayloadOffset; + message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len; + /* TargetNameFields (8 bytes) */ + if (!ntlm_write_message_fields(s, &(message->TargetName))) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + + if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16, "NTLM_CHALLENGE_MESSAGE::ServerChallenge")) + return SEC_E_INTERNAL_ERROR; + + Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */ + Stream_Write(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */ + + /* TargetInfoFields (8 bytes) */ + if (!ntlm_write_message_fields(s, &(message->TargetInfo))) + return SEC_E_INTERNAL_ERROR; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + { + if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + } + + /* Payload (variable) */ + if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET) + { + if (!ntlm_write_message_fields_buffer(s, &(message->TargetName))) + return SEC_E_INTERNAL_ERROR; + } + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO) + { + if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo))) + return SEC_E_INTERNAL_ERROR; + } + + length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); + buffer->cbBuffer = (ULONG)length; + + if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length)) + return SEC_E_INTERNAL_ERROR; + + CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s), length); +#if defined(WITH_DEBUG_NTLM) + ntlm_print_challenge_message(&context->ChallengeMessage, message, + &context->ChallengeTargetInfo); +#endif + ntlm_change_state(context, NTLM_STATE_AUTHENTICATE); + return SEC_I_CONTINUE_NEEDED; +} + +SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer) +{ + SECURITY_STATUS status = SEC_E_INVALID_TOKEN; + wStream sbuffer; + wStream* s = NULL; + size_t length = 0; + UINT32 flags = 0; + NTLM_AV_PAIR* AvFlags = NULL; + size_t PayloadBufferOffset = 0; + const NTLM_AUTHENTICATE_MESSAGE empty = { 0 }; + NTLM_AUTHENTICATE_MESSAGE* message = NULL; + SSPI_CREDENTIALS* credentials = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + credentials = context->credentials; + WINPR_ASSERT(credentials); + + message = &context->AUTHENTICATE_MESSAGE; + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); + + if (!s) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE)) + goto fail; + + if (!ntlm_read_message_fields( + s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */ + goto fail; + + if (!ntlm_read_message_fields( + s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */ + goto fail; + + if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */ + goto fail; + + if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */ + goto fail; + + if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */ + goto fail; + + if (!ntlm_read_message_fields( + s, + &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */ + goto fail; + + if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE")) + goto fail; + + context->NegotiateKeyExchange = + (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) ? TRUE : FALSE; + + if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) || + (!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len)) + goto fail; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + { + if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */ + goto fail; + } + + PayloadBufferOffset = Stream_GetPosition(s); + + status = SEC_E_INTERNAL_ERROR; + if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */ + goto fail; + + if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */ + goto fail; + + if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */ + goto fail; + + if (!ntlm_read_message_fields_buffer(s, + &(message->LmChallengeResponse))) /* LmChallengeResponse */ + goto fail; + + if (!ntlm_read_message_fields_buffer(s, + &(message->NtChallengeResponse))) /* NtChallengeResponse */ + goto fail; + + if (message->NtChallengeResponse.Len > 0) + { + size_t cbAvFlags = 0; + wStream ssbuffer; + wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer, + message->NtChallengeResponse.Len); + + if (!snt) + goto fail; + + status = SEC_E_INVALID_TOKEN; + if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response))) + goto fail; + status = SEC_E_INTERNAL_ERROR; + + context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer; + context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len; + sspi_SecBufferFree(&(context->ChallengeTargetInfo)); + context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs; + context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16); + CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8); + AvFlags = + ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs, + context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags); + + if (AvFlags) + Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags); + } + + if (!ntlm_read_message_fields_buffer( + s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */ + goto fail; + + if (message->EncryptedRandomSessionKey.Len > 0) + { + if (message->EncryptedRandomSessionKey.Len != 16) + goto fail; + + CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer, + 16); + } + + length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); + + if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length)) + goto fail; + + CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length); + buffer->cbBuffer = (ULONG)length; + Stream_SetPosition(s, PayloadBufferOffset); + + if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) + { + status = SEC_E_INVALID_TOKEN; + if (!ntlm_read_message_integrity_check( + s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE")) + goto fail; + } + + status = SEC_E_INTERNAL_ERROR; + +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authenticate_message(&context->AuthenticateMessage, message, flags, NULL); +#endif + + if (message->UserName.Len > 0) + { + credentials->identity.User = (UINT16*)malloc(message->UserName.Len); + + if (!credentials->identity.User) + goto fail; + + CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len); + credentials->identity.UserLength = message->UserName.Len / 2; + } + + if (message->DomainName.Len > 0) + { + credentials->identity.Domain = (UINT16*)malloc(message->DomainName.Len); + + if (!credentials->identity.Domain) + goto fail; + + CopyMemory(credentials->identity.Domain, message->DomainName.Buffer, + message->DomainName.Len); + credentials->identity.DomainLength = message->DomainName.Len / 2; + } + + if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) + { + if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */ + return SEC_E_INTERNAL_ERROR; + } + + if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */ + return SEC_E_INTERNAL_ERROR; + + /* KeyExchangeKey */ + ntlm_generate_key_exchange_key(context); + /* EncryptedRandomSessionKey */ + ntlm_decrypt_random_session_key(context); + /* ExportedSessionKey */ + ntlm_generate_exported_session_key(context); + + if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) + { + BYTE messageIntegrityCheck[16] = { 0 }; + + ntlm_compute_message_integrity_check(context, messageIntegrityCheck, + sizeof(messageIntegrityCheck)); + CopyMemory( + &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset], + message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck)); + + if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)) != 0) + { + WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!"); +#ifdef WITH_DEBUG_NTLM + WLog_ERR(TAG, "Expected MIC:"); + winpr_HexDump(TAG, WLOG_ERROR, messageIntegrityCheck, sizeof(messageIntegrityCheck)); + WLog_ERR(TAG, "Actual MIC:"); + winpr_HexDump(TAG, WLOG_ERROR, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)); +#endif + return SEC_E_MESSAGE_ALTERED; + } + } + else + { + /* no mic message was present + + https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/f9e6fbc4-a953-4f24-b229-ccdcc213b9ec + the mic is optional, as not supported in Windows NT, Windows 2000, Windows XP, and + Windows Server 2003 and, as it seems, in the NTLMv2 implementation of Qt5. + + now check the NtProofString, to detect if the entered client password matches the + expected password. + */ + +#ifdef WITH_DEBUG_NTLM + WLog_VRB(TAG, "No MIC present, using NtProofString for verification."); +#endif + + if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0) + { + WLog_ERR(TAG, "NtProofString verification failed!"); +#ifdef WITH_DEBUG_NTLM + WLog_ERR(TAG, "Expected NtProofString:"); + winpr_HexDump(TAG, WLOG_ERROR, context->NtProofString, sizeof(context->NtProofString)); + WLog_ERR(TAG, "Actual NtProofString:"); + winpr_HexDump(TAG, WLOG_ERROR, context->NTLMv2Response.Response, + sizeof(context->NTLMv2Response)); +#endif + return SEC_E_LOGON_DENIED; + } + } + + /* Generate signing keys */ + if (!ntlm_generate_client_signing_key(context)) + return SEC_E_INTERNAL_ERROR; + if (!ntlm_generate_server_signing_key(context)) + return SEC_E_INTERNAL_ERROR; + /* Generate sealing keys */ + if (!ntlm_generate_client_sealing_key(context)) + return SEC_E_INTERNAL_ERROR; + if (!ntlm_generate_server_sealing_key(context)) + return SEC_E_INTERNAL_ERROR; + /* Initialize RC4 seal state */ + if (!ntlm_init_rc4_seal_states(context)) + return SEC_E_INTERNAL_ERROR; +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authentication_complete(context); +#endif + ntlm_change_state(context, NTLM_STATE_FINAL); + ntlm_free_message_fields_buffer(&(message->DomainName)); + ntlm_free_message_fields_buffer(&(message->UserName)); + ntlm_free_message_fields_buffer(&(message->Workstation)); + ntlm_free_message_fields_buffer(&(message->LmChallengeResponse)); + ntlm_free_message_fields_buffer(&(message->NtChallengeResponse)); + ntlm_free_message_fields_buffer(&(message->EncryptedRandomSessionKey)); + return SEC_E_OK; + +fail: + return status; +} + +/** + * Send NTLMSSP AUTHENTICATE_MESSAGE. msdn{cc236643} + * + * @param context Pointer to the NTLM context + * @param buffer The buffer to write + */ + +SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer) +{ + wStream sbuffer; + wStream* s = NULL; + size_t length = 0; + UINT32 PayloadBufferOffset = 0; + const NTLM_AUTHENTICATE_MESSAGE empty = { 0 }; + NTLM_AUTHENTICATE_MESSAGE* message = NULL; + SSPI_CREDENTIALS* credentials = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + credentials = context->credentials; + WINPR_ASSERT(credentials); + + message = &context->AUTHENTICATE_MESSAGE; + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); + + if (!s) + return SEC_E_INTERNAL_ERROR; + + if (context->NTLMv2) + { + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56; + + if (context->SendVersionInfo) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION; + } + + if (context->UseMIC) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (context->SendWorkstationName) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; + + if (context->confidentiality) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL; + + if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH; + + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN; + message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET; + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_get_version_info(&(message->Version)); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + { + message->Workstation.Len = context->Workstation.Length; + message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer; + } + + if (credentials->identity.DomainLength > 0) + { + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED; + message->DomainName.Len = (UINT16)credentials->identity.DomainLength * 2; + message->DomainName.Buffer = (BYTE*)credentials->identity.Domain; + } + + message->UserName.Len = (UINT16)credentials->identity.UserLength * 2; + message->UserName.Buffer = (BYTE*)credentials->identity.User; + message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer; + message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer; + message->NtChallengeResponse.Len = (UINT16)context->NtChallengeResponse.cbBuffer; + message->NtChallengeResponse.Buffer = (BYTE*)context->NtChallengeResponse.pvBuffer; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) + { + message->EncryptedRandomSessionKey.Len = 16; + message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey; + } + + PayloadBufferOffset = 64; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + PayloadBufferOffset += 8; /* Version (8 bytes) */ + + if (context->UseMIC) + PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */ + + message->DomainName.BufferOffset = PayloadBufferOffset; + message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len; + message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len; + message->LmChallengeResponse.BufferOffset = + message->Workstation.BufferOffset + message->Workstation.Len; + message->NtChallengeResponse.BufferOffset = + message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len; + message->EncryptedRandomSessionKey.BufferOffset = + message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len; + if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE)) + return SEC_E_INVALID_TOKEN; + if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields( + s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields( + s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields( + s, + &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + { + if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + } + + if (context->UseMIC) + { + const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + + context->MessageIntegrityCheckOffset = Stream_GetPosition(s); + if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data), + "NTLM_AUTHENTICATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + } + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + { + if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */ + return SEC_E_INTERNAL_ERROR; + } + + if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */ + return SEC_E_INTERNAL_ERROR; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + { + if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */ + return SEC_E_INTERNAL_ERROR; + } + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) + { + if (!ntlm_write_message_fields_buffer( + s, &(message->LmChallengeResponse))) /* LmChallengeResponse */ + return SEC_E_INTERNAL_ERROR; + } + if (!ntlm_write_message_fields_buffer( + s, &(message->NtChallengeResponse))) /* NtChallengeResponse */ + return SEC_E_INTERNAL_ERROR; + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) + { + if (!ntlm_write_message_fields_buffer( + s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */ + return SEC_E_INTERNAL_ERROR; + } + + length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); + + if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length)) + return SEC_E_INTERNAL_ERROR; + + CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length); + buffer->cbBuffer = (ULONG)length; + + if (context->UseMIC) + { + /* Message Integrity Check */ + ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)); + if (!ntlm_write_message_integrity_check( + s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + } + +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authenticate_message(&context->AuthenticateMessage, message, + context->UseMIC ? MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK : 0, + &context->AuthenticateTargetInfo); +#endif + ntlm_change_state(context, NTLM_STATE_FINAL); + return SEC_E_OK; +} diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.h b/winpr/libwinpr/sspi/NTLM/ntlm_message.h new file mode 100644 index 0000000..58ff35d --- /dev/null +++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.h @@ -0,0 +1,36 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Security Package (Message) + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_NTLM_MESSAGE_H +#define WINPR_SSPI_NTLM_MESSAGE_H + +#include "ntlm.h" + +SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer); +SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer); +SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer); +SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, const PSecBuffer buffer); +SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer); +SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer); + +SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context); + +const char* ntlm_get_negotiate_string(UINT32 flag); + +#endif /* WINPR_SSPI_NTLM_MESSAGE_H */ diff --git a/winpr/libwinpr/sspi/Negotiate/negotiate.c b/winpr/libwinpr/sspi/Negotiate/negotiate.c new file mode 100644 index 0000000..7249399 --- /dev/null +++ b/winpr/libwinpr/sspi/Negotiate/negotiate.c @@ -0,0 +1,1660 @@ +/** + * WinPR: Windows Portable Runtime + * Negotiate Security Package + * + * Copyright 2011-2014 Marc-Andre Moreau + * Copyright 2017 Dorian Ducournau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "negotiate.h" + +#include "../NTLM/ntlm.h" +#include "../NTLM/ntlm_export.h" +#include "../Kerberos/kerberos.h" +#include "../sspi.h" +#include "../../log.h" +#define TAG WINPR_TAG("negotiate") + +static const char NEGO_REG_KEY[] = + "Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\SSPI\\Negotiate"; + +typedef struct +{ + const TCHAR* name; + const SecurityFunctionTableA* table; + const SecurityFunctionTableW* table_w; +} SecPkg; + +struct Mech_st +{ + const WinPrAsn1_OID* oid; + const SecPkg* pkg; + const UINT flags; + const BOOL preferred; +}; + +typedef struct +{ + const Mech* mech; + CredHandle cred; + BOOL valid; +} MechCred; + +const SecPkgInfoA NEGOTIATE_SecPkgInfoA = { + 0x00083BB3, /* fCapabilities */ + 1, /* wVersion */ + 0x0009, /* wRPCID */ + 0x00002FE0, /* cbMaxToken */ + "Negotiate", /* Name */ + "Microsoft Package Negotiator" /* Comment */ +}; + +static WCHAR NEGOTIATE_SecPkgInfoW_NameBuffer[32] = { 0 }; +static WCHAR NEGOTIATE_SecPkgInfoW_CommentBuffer[32] = { 0 }; + +const SecPkgInfoW NEGOTIATE_SecPkgInfoW = { + 0x00083BB3, /* fCapabilities */ + 1, /* wVersion */ + 0x0009, /* wRPCID */ + 0x00002FE0, /* cbMaxToken */ + NEGOTIATE_SecPkgInfoW_NameBuffer, /* Name */ + NEGOTIATE_SecPkgInfoW_CommentBuffer /* Comment */ +}; + +static const WinPrAsn1_OID spnego_OID = { 6, (BYTE*)"\x2b\x06\x01\x05\x05\x02" }; +static const WinPrAsn1_OID kerberos_u2u_OID = { 10, + (BYTE*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03" }; +static const WinPrAsn1_OID kerberos_OID = { 9, (BYTE*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; +static const WinPrAsn1_OID kerberos_wrong_OID = { 9, + (BYTE*)"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02" }; +static const WinPrAsn1_OID ntlm_OID = { 10, (BYTE*)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" }; + +static const WinPrAsn1_OID negoex_OID = { 10, (BYTE*)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e" }; + +#ifdef WITH_KRB5 +static const SecPkg SecPkgTable[] = { + { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW }, + { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, &NTLM_SecurityFunctionTableW } +}; + +static const Mech MechTable[] = { + { &kerberos_u2u_OID, &SecPkgTable[0], ISC_REQ_INTEGRITY | ISC_REQ_USE_SESSION_KEY, TRUE }, + { &kerberos_OID, &SecPkgTable[0], ISC_REQ_INTEGRITY, TRUE }, + { &ntlm_OID, &SecPkgTable[1], 0, FALSE }, +}; +#else +static const SecPkg SecPkgTable[] = { { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, + &NTLM_SecurityFunctionTableW } }; + +static const Mech MechTable[] = { + { &ntlm_OID, &SecPkgTable[0], 0, FALSE }, +}; +#endif + +static const size_t MECH_COUNT = sizeof(MechTable) / sizeof(Mech); + +enum NegState +{ + NOSTATE = -1, + ACCEPT_COMPLETED, + ACCEPT_INCOMPLETE, + REJECT, + REQUEST_MIC +}; + +typedef struct +{ + enum NegState negState; + BOOL init; + WinPrAsn1_OID supportedMech; + SecBuffer mechTypes; + SecBuffer mechToken; + SecBuffer mic; +} NegToken; + +static const NegToken empty_neg_token = { NOSTATE, FALSE, { 0, NULL }, + { 0, 0, NULL }, { 0, 0, NULL }, { 0, 0, NULL } }; + +static NEGOTIATE_CONTEXT* negotiate_ContextNew(NEGOTIATE_CONTEXT* init_context) +{ + NEGOTIATE_CONTEXT* context = NULL; + + WINPR_ASSERT(init_context); + + context = calloc(1, sizeof(NEGOTIATE_CONTEXT)); + if (!context) + return NULL; + + if (init_context->spnego) + { + init_context->mechTypes.pvBuffer = malloc(init_context->mechTypes.cbBuffer); + if (!init_context->mechTypes.pvBuffer) + { + free(context); + return NULL; + } + } + + *context = *init_context; + + return context; +} + +static void negotiate_ContextFree(NEGOTIATE_CONTEXT* context) +{ + WINPR_ASSERT(context); + + if (context->mechTypes.pvBuffer) + free(context->mechTypes.pvBuffer); + free(context); +} + +static const char* negotiate_mech_name(const WinPrAsn1_OID* oid) +{ + if (sspi_gss_oid_compare(oid, &spnego_OID)) + return "SPNEGO (1.3.6.1.5.5.2)"; + else if (sspi_gss_oid_compare(oid, &kerberos_u2u_OID)) + return "Kerberos user to user (1.2.840.113554.1.2.2.3)"; + else if (sspi_gss_oid_compare(oid, &kerberos_OID)) + return "Kerberos (1.2.840.113554.1.2.2)"; + else if (sspi_gss_oid_compare(oid, &kerberos_wrong_OID)) + return "Kerberos [wrong OID] (1.2.840.48018.1.2.2)"; + else if (sspi_gss_oid_compare(oid, &ntlm_OID)) + return "NTLM (1.3.6.1.4.1.311.2.2.10)"; + else if (sspi_gss_oid_compare(oid, &negoex_OID)) + return "NegoEx (1.3.6.1.4.1.311.2.2.30)"; + else + return "Unknown mechanism"; +} + +static const Mech* negotiate_GetMechByOID(const WinPrAsn1_OID* oid) +{ + WINPR_ASSERT(oid); + + WinPrAsn1_OID testOid = *oid; + + if (sspi_gss_oid_compare(&testOid, &kerberos_wrong_OID)) + { + testOid.len = kerberos_OID.len; + testOid.data = kerberos_OID.data; + } + + for (size_t i = 0; i < MECH_COUNT; i++) + { + if (sspi_gss_oid_compare(&testOid, MechTable[i].oid)) + return &MechTable[i]; + } + return NULL; +} + +static PSecHandle negotiate_FindCredential(MechCred* creds, const Mech* mech) +{ + WINPR_ASSERT(creds); + + if (!mech) + return NULL; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + MechCred* cred = &creds[i]; + + if (cred->mech == mech) + { + if (cred->valid) + return &cred->cred; + return NULL; + } + } + + return NULL; +} + +static BOOL negotiate_get_dword(HKEY hKey, const char* subkey, DWORD* pdwValue) +{ + DWORD dwValue = 0; + DWORD dwType = 0; + DWORD dwSize = sizeof(dwValue); + LONG rc = RegQueryValueExA(hKey, subkey, NULL, &dwType, (BYTE*)&dwValue, &dwSize); + + if (rc != ERROR_SUCCESS) + return FALSE; + if (dwType != REG_DWORD) + return FALSE; + + *pdwValue = dwValue; + return TRUE; +} + +static BOOL negotiate_get_config_from_auth_package_list(void* pAuthData, BOOL* kerberos, BOOL* ntlm) +{ + char* tok_ctx = NULL; + char* tok_ptr = NULL; + char* PackageList = NULL; + + if (!sspi_CopyAuthPackageListA((const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData, &PackageList)) + return FALSE; + + tok_ptr = strtok_s(PackageList, ",", &tok_ctx); + + while (tok_ptr) + { + char* PackageName = tok_ptr; + BOOL PackageInclude = TRUE; + + if (PackageName[0] == '!') + { + PackageName = &PackageName[1]; + PackageInclude = FALSE; + } + + if (!_stricmp(PackageName, "ntlm")) + { + *ntlm = PackageInclude; + } + else if (!_stricmp(PackageName, "kerberos")) + { + *kerberos = PackageInclude; + } + else + { + WLog_WARN(TAG, "Unknown authentication package name: %s", PackageName); + } + + tok_ptr = strtok_s(NULL, ",", &tok_ctx); + } + + free(PackageList); + return TRUE; +} + +static BOOL negotiate_get_config(void* pAuthData, BOOL* kerberos, BOOL* ntlm) +{ + HKEY hKey = NULL; + LONG rc = 0; + + WINPR_ASSERT(kerberos); + WINPR_ASSERT(ntlm); + +#if !defined(WITH_KRB5_NO_NTLM_FALLBACK) + *ntlm = TRUE; +#else + *ntlm = FALSE; +#endif + *kerberos = TRUE; + + if (negotiate_get_config_from_auth_package_list(pAuthData, kerberos, ntlm)) + { + return TRUE; // use explicit authentication package list + } + + rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, NEGO_REG_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); + if (rc == ERROR_SUCCESS) + { + DWORD dwValue = 0; + + if (negotiate_get_dword(hKey, "kerberos", &dwValue)) + *kerberos = (dwValue != 0) ? TRUE : FALSE; + +#if !defined(WITH_KRB5_NO_NTLM_FALLBACK) + if (negotiate_get_dword(hKey, "ntlm", &dwValue)) + *ntlm = (dwValue != 0) ? TRUE : FALSE; +#endif + + RegCloseKey(hKey); + } + + return TRUE; +} + +static BOOL negotiate_write_neg_token(PSecBuffer output_buffer, NegToken* token) +{ + WINPR_ASSERT(output_buffer); + WINPR_ASSERT(token); + + BOOL ret = FALSE; + WinPrAsn1Encoder* enc = NULL; + WinPrAsn1_MemoryChunk mechTypes = { token->mechTypes.cbBuffer, token->mechTypes.pvBuffer }; + WinPrAsn1_OctetString mechToken = { token->mechToken.cbBuffer, token->mechToken.pvBuffer }; + WinPrAsn1_OctetString mechListMic = { token->mic.cbBuffer, token->mic.pvBuffer }; + wStream s; + size_t len = 0; + + enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); + if (!enc) + return FALSE; + + /* For NegTokenInit wrap in an initialContextToken */ + if (token->init) + { + /* InitialContextToken [APPLICATION 0] IMPLICIT SEQUENCE */ + if (!WinPrAsn1EncAppContainer(enc, 0)) + goto cleanup; + + /* thisMech MechType OID */ + if (!WinPrAsn1EncOID(enc, &spnego_OID)) + goto cleanup; + } + + /* innerContextToken [0] NegTokenInit or [1] NegTokenResp */ + if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1)) + goto cleanup; + + WLog_DBG(TAG, token->init ? "Writing negTokenInit..." : "Writing negTokenResp..."); + + /* mechTypes [0] MechTypeList (mechTypes already contains the SEQUENCE tag) */ + if (token->init) + { + if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes)) + goto cleanup; + WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer); + } + /* negState [0] ENUMERATED */ + else if (token->negState != NOSTATE) + { + if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState)) + goto cleanup; + WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState); + } + + /* supportedMech [1] OID */ + if (token->supportedMech.len) + { + if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech)) + goto cleanup; + WLog_DBG(TAG, "\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech)); + } + + /* mechToken [2] OCTET STRING */ + if (token->mechToken.cbBuffer) + { + if (WinPrAsn1EncContextualOctetString(enc, 2, &mechToken) == 0) + goto cleanup; + WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer); + } + + /* mechListMIC [3] OCTET STRING */ + if (token->mic.cbBuffer) + { + if (WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic) == 0) + goto cleanup; + WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer); + } + + /* NegTokenInit or NegTokenResp */ + if (!WinPrAsn1EncEndContainer(enc)) + goto cleanup; + + if (token->init) + { + /* initialContextToken */ + if (!WinPrAsn1EncEndContainer(enc)) + goto cleanup; + } + + if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer) + goto cleanup; + + Stream_StaticInit(&s, output_buffer->pvBuffer, len); + + if (WinPrAsn1EncToStream(enc, &s)) + { + output_buffer->cbBuffer = len; + ret = TRUE; + } + +cleanup: + WinPrAsn1Encoder_Free(&enc); + return ret; +} + +static BOOL negotiate_read_neg_token(PSecBuffer input, NegToken* token) +{ + WinPrAsn1Decoder dec; + WinPrAsn1Decoder dec2; + WinPrAsn1_OID oid; + WinPrAsn1_tagId contextual = 0; + WinPrAsn1_tag tag = 0; + size_t len = 0; + WinPrAsn1_OctetString octet_string; + BOOL err = 0; + + WINPR_ASSERT(input); + WINPR_ASSERT(token); + + WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer); + + if (!WinPrAsn1DecPeekTag(&dec, &tag)) + return FALSE; + + if (tag == 0x60) + { + /* initialContextToken [APPLICATION 0] */ + if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0) + return FALSE; + dec = dec2; + + /* thisMech OID */ + if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE)) + return FALSE; + + if (!sspi_gss_oid_compare(&spnego_OID, &oid)) + return FALSE; + + /* [0] NegTokenInit */ + if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2)) + return FALSE; + + token->init = TRUE; + } + /* [1] NegTokenResp */ + else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2)) + return FALSE; + dec = dec2; + + WLog_DBG(TAG, token->init ? "Reading negTokenInit..." : "Reading negTokenResp..."); + + /* Read NegTokenResp sequence members */ + do + { + if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2)) + return FALSE; + + switch (contextual) + { + case 0: + if (token->init) + { + /* mechTypes [0] MechTypeList */ + wStream s = WinPrAsn1DecGetStream(&dec2); + token->mechTypes.BufferType = SECBUFFER_TOKEN; + token->mechTypes.cbBuffer = Stream_Length(&s); + token->mechTypes.pvBuffer = Stream_Buffer(&s); + WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer); + } + else + { + /* negState [0] ENUMERATED */ + WinPrAsn1_ENUMERATED rd = 0; + if (!WinPrAsn1DecReadEnumerated(&dec2, &rd)) + return FALSE; + token->negState = rd; + WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState); + } + break; + case 1: + if (token->init) + { + /* reqFlags [1] ContextFlags BIT STRING (ignored) */ + if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING)) + return FALSE; + WLog_DBG(TAG, "\treqFlags [1] (%li bytes)", len); + } + else + { + /* supportedMech [1] MechType */ + if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE)) + return FALSE; + WLog_DBG(TAG, "\tsupportedMech [1] (%s)", + negotiate_mech_name(&token->supportedMech)); + } + break; + case 2: + /* mechToken [2] OCTET STRING */ + if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE)) + return FALSE; + token->mechToken.cbBuffer = octet_string.len; + token->mechToken.pvBuffer = octet_string.data; + token->mechToken.BufferType = SECBUFFER_TOKEN; + WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", octet_string.len); + break; + case 3: + /* mechListMic [3] OCTET STRING */ + if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE)) + return FALSE; + token->mic.cbBuffer = octet_string.len; + token->mic.pvBuffer = octet_string.data; + token->mic.BufferType = SECBUFFER_TOKEN; + WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", octet_string.len); + break; + default: + WLog_ERR(TAG, "unknown contextual item %d", contextual); + return FALSE; + } + } while (WinPrAsn1DecPeekTag(&dec, &tag)); + + return TRUE; +} + +static SECURITY_STATUS negotiate_mic_exchange(NEGOTIATE_CONTEXT* context, NegToken* input_token, + NegToken* output_token, PSecBuffer output_buffer) +{ + SecBuffer mic_buffers[2] = { 0 }; + SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers }; + SECURITY_STATUS status = 0; + + WINPR_ASSERT(context); + WINPR_ASSERT(input_token); + WINPR_ASSERT(output_token); + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + + const SecurityFunctionTableA* table = context->mech->pkg->table; + WINPR_ASSERT(table); + + mic_buffers[0] = context->mechTypes; + + /* Verify MIC if we received one */ + if (input_token->mic.cbBuffer > 0) + { + mic_buffers[1] = input_token->mic; + + status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, 0); + if (status != SEC_E_OK) + return status; + + output_token->negState = ACCEPT_COMPLETED; + } + + /* If peer expects a MIC then generate it */ + if (input_token->negState != ACCEPT_COMPLETED) + { + /* Store the mic token after the mech token in the output buffer */ + output_token->mic.BufferType = SECBUFFER_TOKEN; + if (output_buffer) + { + output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer; + output_token->mic.pvBuffer = + (BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer; + } + mic_buffers[1] = output_token->mic; + + status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0); + if (status != SEC_E_OK) + return status; + + output_token->mic = mic_buffers[1]; + } + + /* When using NTLM cipher states need to be reset after mic exchange */ + if (_tcscmp(sspi_SecureHandleGetUpperPointer(&context->sub_context), NTLM_SSP_NAME) == 0) + { + if (!ntlm_reset_cipher_state(&context->sub_context)) + return SEC_E_INTERNAL_ERROR; + } + + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + NEGOTIATE_CONTEXT* context = NULL; + NEGOTIATE_CONTEXT init_context = { 0 }; + MechCred* creds = NULL; + PCtxtHandle sub_context = NULL; + PCredHandle sub_cred = NULL; + NegToken input_token = empty_neg_token; + NegToken output_token = empty_neg_token; + PSecBuffer input_buffer = NULL; + PSecBuffer output_buffer = NULL; + PSecBuffer bindings_buffer = NULL; + SecBuffer mech_input_buffers[2] = { 0 }; + SecBufferDesc mech_input = { SECBUFFER_VERSION, 2, mech_input_buffers }; + SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken }; + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + SECURITY_STATUS sub_status = SEC_E_INTERNAL_ERROR; + WinPrAsn1Encoder* enc = NULL; + wStream s; + const Mech* mech = NULL; + + if (!phCredential || !SecIsValidHandle(phCredential)) + return SEC_E_NO_CREDENTIALS; + + creds = sspi_SecureHandleGetLowerPointer(phCredential); + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = sspi_SecureHandleGetLowerPointer(phContext); + + if (pInput) + { + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS); + } + if (pOutput) + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!context) + { + enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); + if (!enc) + return SEC_E_INSUFFICIENT_MEMORY; + + if (!WinPrAsn1EncSeqContainer(enc)) + goto cleanup; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + MechCred* cred = &creds[i]; + const SecPkg* pkg = MechTable[i].pkg; + WINPR_ASSERT(pkg); + WINPR_ASSERT(pkg->table_w); + + if (!cred->valid) + continue; + + /* Send an optimistic token for the first valid mechanism */ + if (!init_context.mech) + { + /* Use the output buffer to store the optimistic token */ + CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer)); + + if (bindings_buffer) + mech_input_buffers[0] = *bindings_buffer; + + WINPR_ASSERT(pkg->table_w->InitializeSecurityContextW); + sub_status = pkg->table_w->InitializeSecurityContextW( + &cred->cred, NULL, pszTargetName, fContextReq | cred->mech->flags, Reserved1, + TargetDataRep, &mech_input, Reserved2, &init_context.sub_context, &mech_output, + pfContextAttr, ptsExpiry); + + /* If the mechanism failed we can't use it; skip */ + if (IsSecurityStatusError(sub_status)) + { + if (SecIsValidHandle(&init_context.sub_context)) + { + WINPR_ASSERT(pkg->table_w->DeleteSecurityContext); + pkg->table_w->DeleteSecurityContext(&init_context.sub_context); + } + cred->valid = FALSE; + continue; + } + + init_context.mech = cred->mech; + } + + if (!WinPrAsn1EncOID(enc, cred->mech->oid)) + goto cleanup; + WLog_DBG(TAG, "Available mechanism: %s", negotiate_mech_name(cred->mech->oid)); + } + + /* No usable mechanisms were found */ + if (!init_context.mech) + goto cleanup; + + /* If the only available mech is NTLM use it directly otherwise use spnego */ + if (init_context.mech->oid == &ntlm_OID) + { + init_context.spnego = FALSE; + output_buffer->cbBuffer = output_token.mechToken.cbBuffer; + WLog_DBG(TAG, "Using direct NTLM"); + } + else + { + init_context.spnego = TRUE; + init_context.mechTypes.BufferType = SECBUFFER_DATA; + init_context.mechTypes.cbBuffer = WinPrAsn1EncEndContainer(enc); + } + + /* Allocate memory for the new context */ + context = negotiate_ContextNew(&init_context); + if (!context) + { + init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context); + WinPrAsn1Encoder_Free(&enc); + return SEC_E_INSUFFICIENT_MEMORY; + } + + sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME); + sspi_SecureHandleSetLowerPointer(phNewContext, context); + + if (!context->spnego) + { + status = sub_status; + goto cleanup; + } + + /* Write mechTypesList */ + Stream_StaticInit(&s, context->mechTypes.pvBuffer, context->mechTypes.cbBuffer); + if (!WinPrAsn1EncToStream(enc, &s)) + goto cleanup; + + output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer; + output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer; + output_token.init = TRUE; + + if (sub_status == SEC_E_OK) + context->state = NEGOTIATE_STATE_FINAL_OPTIMISTIC; + } + else + { + if (!input_buffer) + return SEC_E_INVALID_TOKEN; + + sub_context = &context->sub_context; + sub_cred = negotiate_FindCredential(creds, context->mech); + + if (!context->spnego) + { + return context->mech->pkg->table_w->InitializeSecurityContextW( + sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1, + TargetDataRep, pInput, Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry); + } + + if (!negotiate_read_neg_token(input_buffer, &input_token)) + return SEC_E_INVALID_TOKEN; + + /* On first response check if the server doesn't like out prefered mech */ + if (context->state < NEGOTIATE_STATE_NEGORESP && input_token.supportedMech.len && + !sspi_gss_oid_compare(&input_token.supportedMech, context->mech->oid)) + { + mech = negotiate_GetMechByOID(&input_token.supportedMech); + if (!mech) + return SEC_E_INVALID_TOKEN; + + /* Make sure the specified mech is supported and get the appropriate credential */ + sub_cred = negotiate_FindCredential(creds, mech); + if (!sub_cred) + return SEC_E_INVALID_TOKEN; + + /* Clean up the optimistic mech */ + context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context); + sub_context = NULL; + + context->mech = mech; + context->mic = TRUE; + } + + /* Check neg_state (required on first response) */ + if (context->state < NEGOTIATE_STATE_NEGORESP) + { + switch (input_token.negState) + { + case NOSTATE: + return SEC_E_INVALID_TOKEN; + case REJECT: + return SEC_E_LOGON_DENIED; + case REQUEST_MIC: + context->mic = TRUE; + /* fallthrough */ + WINPR_FALLTHROUGH + case ACCEPT_INCOMPLETE: + context->state = NEGOTIATE_STATE_NEGORESP; + break; + case ACCEPT_COMPLETED: + if (context->state == NEGOTIATE_STATE_INITIAL) + context->state = NEGOTIATE_STATE_NEGORESP; + else + context->state = NEGOTIATE_STATE_FINAL; + break; + } + + WLog_DBG(TAG, "Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid)); + } + + if (context->state == NEGOTIATE_STATE_NEGORESP) + { + /* Store the mech token in the output buffer */ + CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer)); + + mech_input_buffers[0] = input_token.mechToken; + if (bindings_buffer) + mech_input_buffers[1] = *bindings_buffer; + + status = context->mech->pkg->table_w->InitializeSecurityContextW( + sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1, + TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : NULL, Reserved2, + &context->sub_context, &mech_output, pfContextAttr, ptsExpiry); + + if (IsSecurityStatusError(status)) + return status; + } + + if (status == SEC_E_OK) + { + if (output_token.mechToken.cbBuffer > 0) + context->state = NEGOTIATE_STATE_MIC; + else + context->state = NEGOTIATE_STATE_FINAL; + } + + /* Check if the acceptor sent its final token without a mic */ + if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0) + { + if (context->mic || input_token.negState != ACCEPT_COMPLETED) + return SEC_E_INVALID_TOKEN; + + if (output_buffer) + output_buffer->cbBuffer = 0; + return SEC_E_OK; + } + + if ((context->state == NEGOTIATE_STATE_MIC && context->mic) || + context->state == NEGOTIATE_STATE_FINAL) + { + status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer); + if (status != SEC_E_OK) + return status; + } + } + + if (input_token.negState == ACCEPT_COMPLETED) + { + if (output_buffer) + output_buffer->cbBuffer = 0; + return SEC_E_OK; + } + + if (output_token.negState == ACCEPT_COMPLETED) + status = SEC_E_OK; + else + status = SEC_I_CONTINUE_NEEDED; + + if (!negotiate_write_neg_token(output_buffer, &output_token)) + status = SEC_E_INTERNAL_ERROR; + +cleanup: + WinPrAsn1Encoder_Free(&enc); + return status; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + SEC_WCHAR* pszTargetNameW = NULL; + + if (pszTargetName) + { + pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL); + if (!pszTargetNameW) + return SEC_E_INTERNAL_ERROR; + } + + status = negotiate_InitializeSecurityContextW( + phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput, + Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry); + free(pszTargetNameW); + return status; +} + +static const Mech* guessMech(PSecBuffer input_buffer, BOOL* spNego, WinPrAsn1_OID* oid) +{ + WinPrAsn1Decoder decoder; + WinPrAsn1Decoder appDecoder; + WinPrAsn1_tagId tag = 0; + + *spNego = FALSE; + + /* Check for NTLM token */ + if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, "NTLMSSP", 8) == 0) + { + *oid = ntlm_OID; + return negotiate_GetMechByOID(&ntlm_OID); + } + + /* Read initialContextToken or raw Kerberos token */ + WinPrAsn1Decoder_InitMem(&decoder, WINPR_ASN1_DER, input_buffer->pvBuffer, + input_buffer->cbBuffer); + + if (!WinPrAsn1DecReadApp(&decoder, &tag, &appDecoder) || tag != 0) + return NULL; + + if (!WinPrAsn1DecReadOID(&appDecoder, oid, FALSE)) + return NULL; + + if (sspi_gss_oid_compare(oid, &spnego_OID)) + { + *spNego = TRUE; + return NULL; + } + + return negotiate_GetMechByOID(oid); +} + +static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext( + PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq, + ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, + PTimeStamp ptsTimeStamp) +{ + NEGOTIATE_CONTEXT* context = NULL; + NEGOTIATE_CONTEXT init_context = { 0 }; + MechCred* creds = NULL; + PCredHandle sub_cred = NULL; + NegToken input_token = empty_neg_token; + NegToken output_token = empty_neg_token; + PSecBuffer input_buffer = NULL; + PSecBuffer output_buffer = NULL; + SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken }; + SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken }; + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + WinPrAsn1Decoder dec; + WinPrAsn1Decoder dec2; + WinPrAsn1_tagId tag = 0; + WinPrAsn1_OID oid = { 0 }; + const Mech* first_mech = NULL; + + if (!phCredential || !SecIsValidHandle(phCredential)) + return SEC_E_NO_CREDENTIALS; + + creds = sspi_SecureHandleGetLowerPointer(phCredential); + + if (!pInput) + return SEC_E_INVALID_TOKEN; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = sspi_SecureHandleGetLowerPointer(phContext); + + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + if (pOutput) + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!context) + { + init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid); + if (!init_context.mech && !init_context.spnego) + return SEC_E_INVALID_TOKEN; + + WLog_DBG(TAG, "Mechanism: %s", negotiate_mech_name(&oid)); + + if (init_context.spnego) + { + /* Process spnego token */ + if (!negotiate_read_neg_token(input_buffer, &input_token)) + return SEC_E_INVALID_TOKEN; + + /* First token must be negoTokenInit and must contain a mechList */ + if (!input_token.init || input_token.mechTypes.cbBuffer == 0) + return SEC_E_INVALID_TOKEN; + + init_context.mechTypes.BufferType = SECBUFFER_DATA; + init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer; + + /* Prepare to read mechList */ + WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer, + input_token.mechTypes.cbBuffer); + + if (!WinPrAsn1DecReadSequence(&dec, &dec2)) + return SEC_E_INVALID_TOKEN; + dec = dec2; + + /* If an optimistic token was provided pass it into the first mech */ + if (input_token.mechToken.cbBuffer) + { + if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE)) + return SEC_E_INVALID_TOKEN; + + init_context.mech = negotiate_GetMechByOID(&oid); + + if (init_context.mech) + { + if (output_buffer) + output_token.mechToken = *output_buffer; + WLog_DBG(TAG, "Requested mechanism: %s", + negotiate_mech_name(init_context.mech->oid)); + } + } + } + + if (init_context.mech) + { + sub_cred = negotiate_FindCredential(creds, init_context.mech); + + status = init_context.mech->pkg->table->AcceptSecurityContext( + sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq, + TargetDataRep, &init_context.sub_context, + init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp); + } + + if (IsSecurityStatusError(status)) + { + if (!init_context.spnego) + return status; + + init_context.mic = TRUE; + first_mech = init_context.mech; + init_context.mech = NULL; + output_token.mechToken.cbBuffer = 0; + } + + while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag)) + { + /* Read each mechanism */ + if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE)) + return SEC_E_INVALID_TOKEN; + + init_context.mech = negotiate_GetMechByOID(&oid); + WLog_DBG(TAG, "Requested mechanism: %s", negotiate_mech_name(&oid)); + + /* Microsoft may send two versions of the kerberos OID */ + if (init_context.mech == first_mech) + init_context.mech = NULL; + + if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech)) + init_context.mech = NULL; + } + + if (!init_context.mech) + return SEC_E_INTERNAL_ERROR; + + context = negotiate_ContextNew(&init_context); + if (!context) + { + if (!IsSecurityStatusError(status)) + init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context); + return SEC_E_INSUFFICIENT_MEMORY; + } + + sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME); + sspi_SecureHandleSetLowerPointer(phNewContext, context); + + if (!init_context.spnego) + return status; + + CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer, + input_token.mechTypes.cbBuffer); + + if (!context->mech->preferred) + { + output_token.negState = REQUEST_MIC; + context->mic = TRUE; + } + else + { + output_token.negState = ACCEPT_INCOMPLETE; + } + + if (status == SEC_E_OK) + context->state = NEGOTIATE_STATE_FINAL; + else + context->state = NEGOTIATE_STATE_NEGORESP; + + output_token.supportedMech = oid; + WLog_DBG(TAG, "Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech)); + } + else + { + sub_cred = negotiate_FindCredential(creds, context->mech); + if (!sub_cred) + return SEC_E_NO_CREDENTIALS; + + if (!context->spnego) + { + return context->mech->pkg->table->AcceptSecurityContext( + sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep, + &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp); + } + + if (!negotiate_read_neg_token(input_buffer, &input_token)) + return SEC_E_INVALID_TOKEN; + + /* Process the mechanism token */ + if (input_token.mechToken.cbBuffer > 0) + { + if (context->state != NEGOTIATE_STATE_NEGORESP) + return SEC_E_INVALID_TOKEN; + + /* Use the output buffer to store the optimistic token */ + CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer)); + + status = context->mech->pkg->table->AcceptSecurityContext( + sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags, + TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp); + + if (IsSecurityStatusError(status)) + return status; + + if (status == SEC_E_OK) + context->state = NEGOTIATE_STATE_FINAL; + } + else if (context->state == NEGOTIATE_STATE_NEGORESP) + return SEC_E_INVALID_TOKEN; + } + + if (context->state == NEGOTIATE_STATE_FINAL) + { + /* Check if initiator sent the last mech token without a mic and a mic was required */ + if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0) + return SEC_E_INVALID_TOKEN; + + if (context->mic || input_token.mic.cbBuffer > 0) + { + status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer); + if (status != SEC_E_OK) + return status; + } + else + output_token.negState = ACCEPT_COMPLETED; + } + + if (input_token.negState == ACCEPT_COMPLETED) + { + if (output_buffer) + output_buffer->cbBuffer = 0; + return SEC_E_OK; + } + + if (output_token.negState == ACCEPT_COMPLETED) + status = SEC_E_OK; + else + status = SEC_I_CONTINUE_NEEDED; + + if (!negotiate_write_neg_token(output_buffer, &output_token)) + return SEC_E_INTERNAL_ERROR; + + return status; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(PCtxtHandle phContext, + PSecBufferDesc pToken) +{ + NEGOTIATE_CONTEXT* context = NULL; + SECURITY_STATUS status = SEC_E_OK; + context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->CompleteAuthToken) + status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken); + + return status; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(PCtxtHandle phContext) +{ + NEGOTIATE_CONTEXT* context = NULL; + SECURITY_STATUS status = SEC_E_OK; + context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + const SecPkg* pkg = NULL; + + if (!context) + return SEC_E_INVALID_HANDLE; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + pkg = context->mech->pkg; + + if (pkg->table->DeleteSecurityContext) + status = pkg->table->DeleteSecurityContext(&context->sub_context); + + negotiate_ContextFree(context); + return status; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_ImpersonateSecurityContext(PCtxtHandle phContext) +{ + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_RevertSecurityContext(PCtxtHandle phContext) +{ + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table_w); + if (context->mech->pkg->table_w->QueryContextAttributesW) + return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context, + ulAttribute, pBuffer); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->QueryContextAttributesA) + return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context, + ulAttribute, pBuffer); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table_w); + if (context->mech->pkg->table_w->SetContextAttributesW) + return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context, + ulAttribute, pBuffer, cbBuffer); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->SetContextAttributesA) + return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute, + pBuffer, cbBuffer); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + MechCred* creds = NULL; + BOOL success = FALSE; + SECURITY_STATUS secStatus = 0; + + creds = sspi_SecureHandleGetLowerPointer(phCredential); + + if (!creds) + return SEC_E_INVALID_HANDLE; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + MechCred* cred = &creds[i]; + + WINPR_ASSERT(cred->mech); + WINPR_ASSERT(cred->mech->pkg); + WINPR_ASSERT(cred->mech->pkg->table); + WINPR_ASSERT(cred->mech->pkg->table_w->SetCredentialsAttributesW); + secStatus = cred->mech->pkg->table_w->SetCredentialsAttributesW(&cred->cred, ulAttribute, + pBuffer, cbBuffer); + + if (secStatus == SEC_E_OK) + { + success = TRUE; + } + } + + // return success if at least one submodule accepts the credential attribute + return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION); +} + +static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + MechCred* creds = NULL; + BOOL success = FALSE; + SECURITY_STATUS secStatus = 0; + + creds = sspi_SecureHandleGetLowerPointer(phCredential); + + if (!creds) + return SEC_E_INVALID_HANDLE; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + MechCred* cred = &creds[i]; + + if (!cred->valid) + continue; + + WINPR_ASSERT(cred->mech); + WINPR_ASSERT(cred->mech->pkg); + WINPR_ASSERT(cred->mech->pkg->table); + WINPR_ASSERT(cred->mech->pkg->table->SetCredentialsAttributesA); + secStatus = cred->mech->pkg->table->SetCredentialsAttributesA(&cred->cred, ulAttribute, + pBuffer, cbBuffer); + + if (secStatus == SEC_E_OK) + { + success = TRUE; + } + } + + // return success if at least one submodule accepts the credential attribute + return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION); +} + +static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + BOOL kerberos = 0; + BOOL ntlm = 0; + + if (!negotiate_get_config(pAuthData, &kerberos, &ntlm)) + return SEC_E_INTERNAL_ERROR; + + MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred)); + + if (!creds) + return SEC_E_INTERNAL_ERROR; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + MechCred* cred = &creds[i]; + const SecPkg* pkg = MechTable[i].pkg; + cred->mech = &MechTable[i]; + + if (!kerberos && _tcscmp(pkg->name, KERBEROS_SSP_NAME) == 0) + continue; + if (!ntlm && _tcscmp(SecPkgTable[i].name, NTLM_SSP_NAME) == 0) + continue; + + WINPR_ASSERT(pkg->table_w); + WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW); + if (pkg->table_w->AcquireCredentialsHandleW( + pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn, + pvGetKeyArgument, &cred->cred, ptsExpiry) != SEC_E_OK) + continue; + + cred->valid = TRUE; + } + + sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds); + sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + BOOL kerberos = 0; + BOOL ntlm = 0; + + if (!negotiate_get_config(pAuthData, &kerberos, &ntlm)) + return SEC_E_INTERNAL_ERROR; + + MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred)); + + if (!creds) + return SEC_E_INTERNAL_ERROR; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + const SecPkg* pkg = MechTable[i].pkg; + MechCred* cred = &creds[i]; + + cred->mech = &MechTable[i]; + + if (!kerberos && _tcscmp(pkg->name, KERBEROS_SSP_NAME) == 0) + continue; + if (!ntlm && _tcscmp(SecPkgTable[i].name, NTLM_SSP_NAME) == 0) + continue; + + WINPR_ASSERT(pkg->table); + WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA); + if (pkg->table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse, + pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument, + &cred->cred, ptsExpiry) != SEC_E_OK) + continue; + + cred->valid = TRUE; + } + + sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds); + sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + WLog_ERR(TAG, "TODO: Implement"); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(PCredHandle phCredential) +{ + MechCred* creds = NULL; + + creds = sspi_SecureHandleGetLowerPointer(phCredential); + if (!creds) + return SEC_E_INVALID_HANDLE; + + for (size_t i = 0; i < MECH_COUNT; i++) + { + MechCred* cred = &creds[i]; + + WINPR_ASSERT(cred->mech); + WINPR_ASSERT(cred->mech->pkg); + WINPR_ASSERT(cred->mech->pkg->table); + WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle); + cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred); + } + free(creds); + + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, + ULONG MessageSeqNo) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (context->mic) + MessageSeqNo++; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->EncryptMessage) + return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage, + MessageSeqNo); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (context->mic) + MessageSeqNo++; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->DecryptMessage) + return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage, + MessageSeqNo, pfQOP); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, + ULONG MessageSeqNo) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (context->mic) + MessageSeqNo++; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->MakeSignature) + return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage, + MessageSeqNo); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ + NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + if (context->mic) + MessageSeqNo++; + + WINPR_ASSERT(context->mech); + WINPR_ASSERT(context->mech->pkg); + WINPR_ASSERT(context->mech->pkg->table); + if (context->mech->pkg->table->VerifySignature) + return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage, + MessageSeqNo, pfQOP); + + return SEC_E_UNSUPPORTED_FUNCTION; +} + +const SecurityFunctionTableA NEGOTIATE_SecurityFunctionTableA = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + negotiate_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */ + negotiate_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */ + negotiate_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + negotiate_InitializeSecurityContextA, /* InitializeSecurityContext */ + negotiate_AcceptSecurityContext, /* AcceptSecurityContext */ + negotiate_CompleteAuthToken, /* CompleteAuthToken */ + negotiate_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + negotiate_QueryContextAttributesA, /* QueryContextAttributes */ + negotiate_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + negotiate_RevertSecurityContext, /* RevertSecurityContext */ + negotiate_MakeSignature, /* MakeSignature */ + negotiate_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + negotiate_EncryptMessage, /* EncryptMessage */ + negotiate_DecryptMessage, /* DecryptMessage */ + negotiate_SetContextAttributesA, /* SetContextAttributes */ + negotiate_SetCredentialsAttributesA, /* SetCredentialsAttributes */ +}; + +const SecurityFunctionTableW NEGOTIATE_SecurityFunctionTableW = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + negotiate_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */ + negotiate_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */ + negotiate_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + negotiate_InitializeSecurityContextW, /* InitializeSecurityContext */ + negotiate_AcceptSecurityContext, /* AcceptSecurityContext */ + negotiate_CompleteAuthToken, /* CompleteAuthToken */ + negotiate_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + negotiate_QueryContextAttributesW, /* QueryContextAttributes */ + negotiate_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + negotiate_RevertSecurityContext, /* RevertSecurityContext */ + negotiate_MakeSignature, /* MakeSignature */ + negotiate_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + negotiate_EncryptMessage, /* EncryptMessage */ + negotiate_DecryptMessage, /* DecryptMessage */ + negotiate_SetContextAttributesW, /* SetContextAttributes */ + negotiate_SetCredentialsAttributesW, /* SetCredentialsAttributes */ +}; + +BOOL NEGOTIATE_init(void) +{ + InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Name, NEGOTIATE_SecPkgInfoW_NameBuffer, + ARRAYSIZE(NEGOTIATE_SecPkgInfoW_NameBuffer)); + InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Comment, NEGOTIATE_SecPkgInfoW_CommentBuffer, + ARRAYSIZE(NEGOTIATE_SecPkgInfoW_CommentBuffer)); + + return TRUE; +} diff --git a/winpr/libwinpr/sspi/Negotiate/negotiate.h b/winpr/libwinpr/sspi/Negotiate/negotiate.h new file mode 100644 index 0000000..767e30a --- /dev/null +++ b/winpr/libwinpr/sspi/Negotiate/negotiate.h @@ -0,0 +1,57 @@ +/** + * WinPR: Windows Portable Runtime + * Negotiate Security Package + * + * Copyright 2011-2012 Jiten Pathy + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_NEGOTIATE_PRIVATE_H +#define WINPR_SSPI_NEGOTIATE_PRIVATE_H + +#include + +#include "../sspi.h" + +#define NTLM_OID "1.3.6.1.4.1.311.2.2.10" + +typedef enum +{ + NEGOTIATE_STATE_INITIAL, + NEGOTIATE_STATE_FINAL_OPTIMISTIC, + NEGOTIATE_STATE_NEGORESP, + NEGOTIATE_STATE_MIC, + NEGOTIATE_STATE_FINAL, +} NEGOTIATE_STATE; + +typedef struct Mech_st Mech; + +typedef struct +{ + NEGOTIATE_STATE state; + CtxtHandle sub_context; + SecBuffer mechTypes; + const Mech* mech; + BOOL mic; + BOOL spnego; +} NEGOTIATE_CONTEXT; + +extern const SecPkgInfoA NEGOTIATE_SecPkgInfoA; +extern const SecPkgInfoW NEGOTIATE_SecPkgInfoW; +extern const SecurityFunctionTableA NEGOTIATE_SecurityFunctionTableA; +extern const SecurityFunctionTableW NEGOTIATE_SecurityFunctionTableW; + +BOOL NEGOTIATE_init(void); + +#endif /* WINPR_SSPI_NEGOTIATE_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/Schannel/schannel.c b/winpr/libwinpr/sspi/Schannel/schannel.c new file mode 100644 index 0000000..45fba37 --- /dev/null +++ b/winpr/libwinpr/sspi/Schannel/schannel.c @@ -0,0 +1,467 @@ +/** + * WinPR: Windows Portable Runtime + * Schannel Security Package + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "schannel.h" + +#include "../sspi.h" +#include "../../log.h" + +static char* SCHANNEL_PACKAGE_NAME = "Schannel"; + +#define TAG WINPR_TAG("sspi.Schannel") + +SCHANNEL_CONTEXT* schannel_ContextNew(void) +{ + SCHANNEL_CONTEXT* context = NULL; + context = (SCHANNEL_CONTEXT*)calloc(1, sizeof(SCHANNEL_CONTEXT)); + + if (!context) + return NULL; + + context->openssl = schannel_openssl_new(); + + if (!context->openssl) + { + free(context); + return NULL; + } + + return context; +} + +void schannel_ContextFree(SCHANNEL_CONTEXT* context) +{ + if (!context) + return; + + schannel_openssl_free(context->openssl); + free(context); +} + +static SCHANNEL_CREDENTIALS* schannel_CredentialsNew(void) +{ + SCHANNEL_CREDENTIALS* credentials = NULL; + credentials = (SCHANNEL_CREDENTIALS*)calloc(1, sizeof(SCHANNEL_CREDENTIALS)); + return credentials; +} + +static void schannel_CredentialsFree(SCHANNEL_CREDENTIALS* credentials) +{ + free(credentials); +} + +static ALG_ID schannel_SupportedAlgs[] = { CALG_AES_128, + CALG_AES_256, + CALG_RC4, + CALG_DES, + CALG_3DES, + CALG_MD5, + CALG_SHA1, + CALG_SHA_256, + CALG_SHA_384, + CALG_SHA_512, + CALG_RSA_SIGN, + CALG_DH_EPHEM, + (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RESERVED7 | + 6), /* what is this? */ + CALG_DSS_SIGN, + CALG_ECDSA }; + +static SECURITY_STATUS SEC_ENTRY schannel_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + if (ulAttribute == SECPKG_ATTR_SUPPORTED_ALGS) + { + PSecPkgCred_SupportedAlgs SupportedAlgs = (PSecPkgCred_SupportedAlgs)pBuffer; + SupportedAlgs->cSupportedAlgs = sizeof(schannel_SupportedAlgs) / sizeof(ALG_ID); + SupportedAlgs->palgSupportedAlgs = (ALG_ID*)schannel_SupportedAlgs; + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_CIPHER_STRENGTHS) + { + PSecPkgCred_CipherStrengths CipherStrengths = (PSecPkgCred_CipherStrengths)pBuffer; + CipherStrengths->dwMinimumCipherStrength = 40; + CipherStrengths->dwMaximumCipherStrength = 256; + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_SUPPORTED_PROTOCOLS) + { + PSecPkgCred_SupportedProtocols SupportedProtocols = (PSecPkgCred_SupportedProtocols)pBuffer; + /* Observed SupportedProtocols: 0x208A0 */ + SupportedProtocols->grbitProtocol = (SP_PROT_CLIENTS | SP_PROT_SERVERS); + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY schannel_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, + void* pBuffer) +{ + return schannel_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer); +} + +static SECURITY_STATUS SEC_ENTRY schannel_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SCHANNEL_CREDENTIALS* credentials = NULL; + + if (fCredentialUse == SECPKG_CRED_OUTBOUND) + { + SCHANNEL_CRED* cred = NULL; + credentials = schannel_CredentialsNew(); + credentials->fCredentialUse = fCredentialUse; + cred = (SCHANNEL_CRED*)pAuthData; + + if (cred) + { + CopyMemory(&credentials->cred, cred, sizeof(SCHANNEL_CRED)); + } + + sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); + sspi_SecureHandleSetUpperPointer(phCredential, (void*)SCHANNEL_PACKAGE_NAME); + return SEC_E_OK; + } + else if (fCredentialUse == SECPKG_CRED_INBOUND) + { + credentials = schannel_CredentialsNew(); + credentials->fCredentialUse = fCredentialUse; + sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials); + sspi_SecureHandleSetUpperPointer(phCredential, (void*)SCHANNEL_PACKAGE_NAME); + return SEC_E_OK; + } + + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY schannel_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + SEC_WCHAR* pszPrincipalW = NULL; + SEC_WCHAR* pszPackageW = NULL; + if (pszPrincipal) + pszPrincipalW = ConvertUtf8ToWCharAlloc(pszPrincipal, NULL); + if (pszPackage) + pszPackageW = ConvertUtf8ToWCharAlloc(pszPackage, NULL); + + status = schannel_AcquireCredentialsHandleW(pszPrincipalW, pszPackageW, fCredentialUse, + pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument, + phCredential, ptsExpiry); + free(pszPrincipalW); + free(pszPackageW); + return status; +} + +static SECURITY_STATUS SEC_ENTRY schannel_FreeCredentialsHandle(PCredHandle phCredential) +{ + SCHANNEL_CREDENTIALS* credentials = NULL; + + if (!phCredential) + return SEC_E_INVALID_HANDLE; + + credentials = (SCHANNEL_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + + if (!credentials) + return SEC_E_INVALID_HANDLE; + + schannel_CredentialsFree(credentials); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY schannel_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + SCHANNEL_CONTEXT* context = NULL; + SCHANNEL_CREDENTIALS* credentials = NULL; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + { + context = schannel_ContextNew(); + + if (!context) + return SEC_E_INSUFFICIENT_MEMORY; + + credentials = (SCHANNEL_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); + context->server = FALSE; + CopyMemory(&context->cred, &credentials->cred, sizeof(SCHANNEL_CRED)); + sspi_SecureHandleSetLowerPointer(phNewContext, context); + sspi_SecureHandleSetUpperPointer(phNewContext, (void*)SCHANNEL_PACKAGE_NAME); + schannel_openssl_client_init(context->openssl); + } + + status = schannel_openssl_client_process_tokens(context->openssl, pInput, pOutput); + return status; +} + +static SECURITY_STATUS SEC_ENTRY schannel_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + SEC_WCHAR* pszTargetNameW = NULL; + + if (pszTargetName != NULL) + { + pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL); + if (!pszTargetNameW) + return SEC_E_INSUFFICIENT_MEMORY; + } + + status = schannel_InitializeSecurityContextW( + phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput, + Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry); + free(pszTargetNameW); + return status; +} + +static SECURITY_STATUS SEC_ENTRY schannel_AcceptSecurityContext( + PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq, + ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, + PTimeStamp ptsTimeStamp) +{ + SECURITY_STATUS status = 0; + SCHANNEL_CONTEXT* context = NULL; + + /* behave like windows SSPIs that don't want empty context */ + if (phContext && !phContext->dwLower && !phContext->dwUpper) + return SEC_E_INVALID_HANDLE; + + context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + { + context = schannel_ContextNew(); + + if (!context) + return SEC_E_INSUFFICIENT_MEMORY; + + context->server = TRUE; + sspi_SecureHandleSetLowerPointer(phNewContext, context); + sspi_SecureHandleSetUpperPointer(phNewContext, (void*)SCHANNEL_PACKAGE_NAME); + schannel_openssl_server_init(context->openssl); + } + + status = schannel_openssl_server_process_tokens(context->openssl, pInput, pOutput); + return status; +} + +static SECURITY_STATUS SEC_ENTRY schannel_DeleteSecurityContext(PCtxtHandle phContext) +{ + SCHANNEL_CONTEXT* context = NULL; + context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + schannel_ContextFree(context); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY schannel_QueryContextAttributes(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + if (!phContext) + return SEC_E_INVALID_HANDLE; + + if (!pBuffer) + return SEC_E_INSUFFICIENT_MEMORY; + + if (ulAttribute == SECPKG_ATTR_SIZES) + { + SecPkgContext_Sizes* Sizes = (SecPkgContext_Sizes*)pBuffer; + Sizes->cbMaxToken = 0x6000; + Sizes->cbMaxSignature = 16; + Sizes->cbBlockSize = 0; + Sizes->cbSecurityTrailer = 16; + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_STREAM_SIZES) + { + SecPkgContext_StreamSizes* StreamSizes = (SecPkgContext_StreamSizes*)pBuffer; + StreamSizes->cbHeader = 5; + StreamSizes->cbTrailer = 36; + StreamSizes->cbMaximumMessage = 0x4000; + StreamSizes->cBuffers = 4; + StreamSizes->cbBlockSize = 16; + return SEC_E_OK; + } + + WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute); + return SEC_E_UNSUPPORTED_FUNCTION; +} + +static SECURITY_STATUS SEC_ENTRY schannel_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY schannel_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY schannel_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, + ULONG MessageSeqNo) +{ + SECURITY_STATUS status = 0; + SCHANNEL_CONTEXT* context = NULL; + context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + status = schannel_openssl_encrypt_message(context->openssl, pMessage); + return status; +} + +static SECURITY_STATUS SEC_ENTRY schannel_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, + ULONG MessageSeqNo, ULONG* pfQOP) +{ + SECURITY_STATUS status = 0; + SCHANNEL_CONTEXT* context = NULL; + context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; + + status = schannel_openssl_decrypt_message(context->openssl, pMessage); + return status; +} + +const SecurityFunctionTableA SCHANNEL_SecurityFunctionTableA = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + schannel_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */ + schannel_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */ + schannel_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + schannel_InitializeSecurityContextA, /* InitializeSecurityContext */ + schannel_AcceptSecurityContext, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + schannel_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + schannel_QueryContextAttributes, /* QueryContextAttributes */ + NULL, /* ImpersonateSecurityContext */ + NULL, /* RevertSecurityContext */ + schannel_MakeSignature, /* MakeSignature */ + schannel_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + schannel_EncryptMessage, /* EncryptMessage */ + schannel_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributes */ + NULL, /* SetCredentialsAttributes */ +}; + +const SecurityFunctionTableW SCHANNEL_SecurityFunctionTableW = { + 3, /* dwVersion */ + NULL, /* EnumerateSecurityPackages */ + schannel_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */ + schannel_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */ + schannel_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + schannel_InitializeSecurityContextW, /* InitializeSecurityContext */ + schannel_AcceptSecurityContext, /* AcceptSecurityContext */ + NULL, /* CompleteAuthToken */ + schannel_DeleteSecurityContext, /* DeleteSecurityContext */ + NULL, /* ApplyControlToken */ + schannel_QueryContextAttributes, /* QueryContextAttributes */ + NULL, /* ImpersonateSecurityContext */ + NULL, /* RevertSecurityContext */ + schannel_MakeSignature, /* MakeSignature */ + schannel_VerifySignature, /* VerifySignature */ + NULL, /* FreeContextBuffer */ + NULL, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + NULL, /* ExportSecurityContext */ + NULL, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + NULL, /* QuerySecurityContextToken */ + schannel_EncryptMessage, /* EncryptMessage */ + schannel_DecryptMessage, /* DecryptMessage */ + NULL, /* SetContextAttributes */ + NULL, /* SetCredentialsAttributes */ +}; + +const SecPkgInfoA SCHANNEL_SecPkgInfoA = { + 0x000107B3, /* fCapabilities */ + 1, /* wVersion */ + 0x000E, /* wRPCID */ + SCHANNEL_CB_MAX_TOKEN, /* cbMaxToken */ + "Schannel", /* Name */ + "Schannel Security Package" /* Comment */ +}; + +static WCHAR SCHANNEL_SecPkgInfoW_NameBuffer[32] = { 0 }; +static WCHAR SCHANNEL_SecPkgInfoW_CommentBuffer[32] = { 0 }; + +const SecPkgInfoW SCHANNEL_SecPkgInfoW = { + 0x000107B3, /* fCapabilities */ + 1, /* wVersion */ + 0x000E, /* wRPCID */ + SCHANNEL_CB_MAX_TOKEN, /* cbMaxToken */ + SCHANNEL_SecPkgInfoW_NameBuffer, /* Name */ + SCHANNEL_SecPkgInfoW_CommentBuffer /* Comment */ +}; + +BOOL SCHANNEL_init(void) +{ + InitializeConstWCharFromUtf8(SCHANNEL_SecPkgInfoA.Name, SCHANNEL_SecPkgInfoW_NameBuffer, + ARRAYSIZE(SCHANNEL_SecPkgInfoW_NameBuffer)); + InitializeConstWCharFromUtf8(SCHANNEL_SecPkgInfoA.Comment, SCHANNEL_SecPkgInfoW_CommentBuffer, + ARRAYSIZE(SCHANNEL_SecPkgInfoW_CommentBuffer)); + return TRUE; +} diff --git a/winpr/libwinpr/sspi/Schannel/schannel.h b/winpr/libwinpr/sspi/Schannel/schannel.h new file mode 100644 index 0000000..e4d170a --- /dev/null +++ b/winpr/libwinpr/sspi/Schannel/schannel.h @@ -0,0 +1,53 @@ +/** + * WinPR: Windows Portable Runtime + * Schannel Security Package + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_SCHANNEL_PRIVATE_H +#define WINPR_SSPI_SCHANNEL_PRIVATE_H + +#include +#include + +#include "../sspi.h" + +#include "schannel_openssl.h" + +typedef struct +{ + SCHANNEL_CRED cred; + ULONG fCredentialUse; +} SCHANNEL_CREDENTIALS; + +typedef struct +{ + BOOL server; + SCHANNEL_CRED cred; + SCHANNEL_OPENSSL* openssl; +} SCHANNEL_CONTEXT; + +SCHANNEL_CONTEXT* schannel_ContextNew(void); +void schannel_ContextFree(SCHANNEL_CONTEXT* context); + +extern const SecPkgInfoA SCHANNEL_SecPkgInfoA; +extern const SecPkgInfoW SCHANNEL_SecPkgInfoW; +extern const SecurityFunctionTableA SCHANNEL_SecurityFunctionTableA; +extern const SecurityFunctionTableW SCHANNEL_SecurityFunctionTableW; + +BOOL SCHANNEL_init(void); + +#endif /* WINPR_SSPI_SCHANNEL_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/Schannel/schannel_openssl.c b/winpr/libwinpr/sspi/Schannel/schannel_openssl.c new file mode 100644 index 0000000..63c17d7 --- /dev/null +++ b/winpr/libwinpr/sspi/Schannel/schannel_openssl.c @@ -0,0 +1,649 @@ +/** + * WinPR: Windows Portable Runtime + * Schannel Security Package (OpenSSL) + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "schannel_openssl.h" + +#ifdef WITH_OPENSSL + +#include +#include +#include +#include +#include + +#include +#include +#include + +struct S_SCHANNEL_OPENSSL +{ + SSL* ssl; + SSL_CTX* ctx; + BOOL connected; + BIO* bioRead; + BIO* bioWrite; + BYTE* ReadBuffer; + BYTE* WriteBuffer; +}; + +#include "../../log.h" +#define TAG WINPR_TAG("sspi.schannel") + +static char* openssl_get_ssl_error_string(int ssl_error) +{ + switch (ssl_error) + { + case SSL_ERROR_ZERO_RETURN: + return "SSL_ERROR_ZERO_RETURN"; + + case SSL_ERROR_WANT_READ: + return "SSL_ERROR_WANT_READ"; + + case SSL_ERROR_WANT_WRITE: + return "SSL_ERROR_WANT_WRITE"; + + case SSL_ERROR_SYSCALL: + return "SSL_ERROR_SYSCALL"; + + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + } + + return "SSL_ERROR_UNKNOWN"; +} + +static void schannel_context_cleanup(SCHANNEL_OPENSSL* context) +{ + WINPR_ASSERT(context); + + free(context->ReadBuffer); + context->ReadBuffer = NULL; + + if (context->bioWrite) + BIO_free_all(context->bioWrite); + context->bioWrite = NULL; + + if (context->bioRead) + BIO_free_all(context->bioRead); + context->bioRead = NULL; + + if (context->ssl) + SSL_free(context->ssl); + context->ssl = NULL; + + if (context->ctx) + SSL_CTX_free(context->ctx); + context->ctx = NULL; +} + +static const SSL_METHOD* get_method(BOOL server) +{ + if (server) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + return SSLv23_server_method(); +#else + return TLS_server_method(); +#endif + } + else + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + return SSLv23_client_method(); +#else + return TLS_client_method(); +#endif + } +} +int schannel_openssl_client_init(SCHANNEL_OPENSSL* context) +{ + int status = 0; + long options = 0; + context->ctx = SSL_CTX_new(get_method(FALSE)); + + if (!context->ctx) + { + WLog_ERR(TAG, "SSL_CTX_new failed"); + return -1; + } + + /** + * SSL_OP_NO_COMPRESSION: + * + * The Microsoft RDP server does not advertise support + * for TLS compression, but alternative servers may support it. + * This was observed between early versions of the FreeRDP server + * and the FreeRDP client, and caused major performance issues, + * which is why we're disabling it. + */ +#ifdef SSL_OP_NO_COMPRESSION + options |= SSL_OP_NO_COMPRESSION; +#endif + /** + * SSL_OP_TLS_BLOCK_PADDING_BUG: + * + * The Microsoft RDP server does *not* support TLS padding. + * It absolutely needs to be disabled otherwise it won't work. + */ + options |= SSL_OP_TLS_BLOCK_PADDING_BUG; + /** + * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: + * + * Just like TLS padding, the Microsoft RDP server does not + * support empty fragments. This needs to be disabled. + */ + options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + SSL_CTX_set_options(context->ctx, options); + context->ssl = SSL_new(context->ctx); + + if (!context->ssl) + { + WLog_ERR(TAG, "SSL_new failed"); + goto fail; + } + + context->bioRead = BIO_new(BIO_s_mem()); + + if (!context->bioRead) + { + WLog_ERR(TAG, "BIO_new failed"); + goto fail; + } + + status = BIO_set_write_buf_size(context->bioRead, SCHANNEL_CB_MAX_TOKEN); + + if (status != 1) + { + WLog_ERR(TAG, "BIO_set_write_buf_size on bioRead failed"); + goto fail; + } + + context->bioWrite = BIO_new(BIO_s_mem()); + + if (!context->bioWrite) + { + WLog_ERR(TAG, "BIO_new failed"); + goto fail; + } + + status = BIO_set_write_buf_size(context->bioWrite, SCHANNEL_CB_MAX_TOKEN); + + if (status != 1) + { + WLog_ERR(TAG, "BIO_set_write_buf_size on bioWrite failed"); + goto fail; + } + + status = BIO_make_bio_pair(context->bioRead, context->bioWrite); + + if (status != 1) + { + WLog_ERR(TAG, "BIO_make_bio_pair failed"); + goto fail; + } + + SSL_set_bio(context->ssl, context->bioRead, context->bioWrite); + context->ReadBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN); + + if (!context->ReadBuffer) + { + WLog_ERR(TAG, "Failed to allocate ReadBuffer"); + goto fail; + } + + context->WriteBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN); + + if (!context->WriteBuffer) + { + WLog_ERR(TAG, "Failed to allocate ReadBuffer"); + goto fail; + } + + return 0; +fail: + schannel_context_cleanup(context); + return -1; +} + +int schannel_openssl_server_init(SCHANNEL_OPENSSL* context) +{ + int status = 0; + unsigned long options = 0; + + context->ctx = SSL_CTX_new(get_method(TRUE)); + + if (!context->ctx) + { + WLog_ERR(TAG, "SSL_CTX_new failed"); + return -1; + } + + /* + * SSL_OP_NO_SSLv2: + * + * We only want SSLv3 and TLSv1, so disable SSLv2. + * SSLv3 is used by, eg. Microsoft RDC for Mac OS X. + */ + options |= SSL_OP_NO_SSLv2; + /** + * SSL_OP_NO_COMPRESSION: + * + * The Microsoft RDP server does not advertise support + * for TLS compression, but alternative servers may support it. + * This was observed between early versions of the FreeRDP server + * and the FreeRDP client, and caused major performance issues, + * which is why we're disabling it. + */ +#ifdef SSL_OP_NO_COMPRESSION + options |= SSL_OP_NO_COMPRESSION; +#endif + /** + * SSL_OP_TLS_BLOCK_PADDING_BUG: + * + * The Microsoft RDP server does *not* support TLS padding. + * It absolutely needs to be disabled otherwise it won't work. + */ + options |= SSL_OP_TLS_BLOCK_PADDING_BUG; + /** + * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: + * + * Just like TLS padding, the Microsoft RDP server does not + * support empty fragments. This needs to be disabled. + */ + options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + SSL_CTX_set_options(context->ctx, options); + +#if defined(WITH_DEBUG_SCHANNEL) + if (SSL_CTX_use_RSAPrivateKey_file(context->ctx, "/tmp/localhost.key", SSL_FILETYPE_PEM) <= 0) + { + WLog_ERR(TAG, "SSL_CTX_use_RSAPrivateKey_file failed"); + goto fail; + } +#endif + + context->ssl = SSL_new(context->ctx); + + if (!context->ssl) + { + WLog_ERR(TAG, "SSL_new failed"); + goto fail; + } + + if (SSL_use_certificate_file(context->ssl, "/tmp/localhost.crt", SSL_FILETYPE_PEM) <= 0) + { + WLog_ERR(TAG, "SSL_use_certificate_file failed"); + goto fail; + } + + context->bioRead = BIO_new(BIO_s_mem()); + + if (!context->bioRead) + { + WLog_ERR(TAG, "BIO_new failed"); + goto fail; + } + + status = BIO_set_write_buf_size(context->bioRead, SCHANNEL_CB_MAX_TOKEN); + + if (status != 1) + { + WLog_ERR(TAG, "BIO_set_write_buf_size failed for bioRead"); + goto fail; + } + + context->bioWrite = BIO_new(BIO_s_mem()); + + if (!context->bioWrite) + { + WLog_ERR(TAG, "BIO_new failed"); + goto fail; + } + + status = BIO_set_write_buf_size(context->bioWrite, SCHANNEL_CB_MAX_TOKEN); + + if (status != 1) + { + WLog_ERR(TAG, "BIO_set_write_buf_size failed for bioWrite"); + goto fail; + } + + status = BIO_make_bio_pair(context->bioRead, context->bioWrite); + + if (status != 1) + { + WLog_ERR(TAG, "BIO_make_bio_pair failed"); + goto fail; + } + + SSL_set_bio(context->ssl, context->bioRead, context->bioWrite); + context->ReadBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN); + + if (!context->ReadBuffer) + { + WLog_ERR(TAG, "Failed to allocate memory for ReadBuffer"); + goto fail; + } + + context->WriteBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN); + + if (!context->WriteBuffer) + { + WLog_ERR(TAG, "Failed to allocate memory for WriteBuffer"); + goto fail; + } + + return 0; +fail: + schannel_context_cleanup(context); + return -1; +} + +SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context, + PSecBufferDesc pInput, + PSecBufferDesc pOutput) +{ + int status = 0; + int ssl_error = 0; + PSecBuffer pBuffer = NULL; + + if (!context->connected) + { + if (pInput) + { + if (pInput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + pBuffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + + if (!pBuffer) + return SEC_E_INVALID_TOKEN; + + ERR_clear_error(); + status = BIO_write(context->bioRead, pBuffer->pvBuffer, pBuffer->cbBuffer); + if (status < 0) + return SEC_E_INVALID_TOKEN; + } + + status = SSL_connect(context->ssl); + + if (status < 0) + { + ssl_error = SSL_get_error(context->ssl, status); + WLog_ERR(TAG, "SSL_connect error: %s", openssl_get_ssl_error_string(ssl_error)); + } + + if (status == 1) + context->connected = TRUE; + + ERR_clear_error(); + status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN); + + if (pOutput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + pBuffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!pBuffer) + return SEC_E_INVALID_TOKEN; + + if (status > 0) + { + if (pBuffer->cbBuffer < (unsigned long)status) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(pBuffer->pvBuffer, context->ReadBuffer, status); + pBuffer->cbBuffer = status; + return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED; + } + else + { + pBuffer->cbBuffer = 0; + return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED; + } + } + + return SEC_E_OK; +} + +SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context, + PSecBufferDesc pInput, + PSecBufferDesc pOutput) +{ + int status = 0; + int ssl_error = 0; + PSecBuffer pBuffer = NULL; + + if (!context->connected) + { + if (pInput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + pBuffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + + if (!pBuffer) + return SEC_E_INVALID_TOKEN; + + ERR_clear_error(); + status = BIO_write(context->bioRead, pBuffer->pvBuffer, pBuffer->cbBuffer); + if (status >= 0) + status = SSL_accept(context->ssl); + + if (status < 0) + { + ssl_error = SSL_get_error(context->ssl, status); + WLog_ERR(TAG, "SSL_accept error: %s", openssl_get_ssl_error_string(ssl_error)); + return SEC_E_INVALID_TOKEN; + } + + if (status == 1) + context->connected = TRUE; + + ERR_clear_error(); + status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN); + if (status < 0) + { + ssl_error = SSL_get_error(context->ssl, status); + WLog_ERR(TAG, "BIO_read: %s", openssl_get_ssl_error_string(ssl_error)); + return SEC_E_INVALID_TOKEN; + } + + if (pOutput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; + + pBuffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + + if (!pBuffer) + return SEC_E_INVALID_TOKEN; + + if (status > 0) + { + if (pBuffer->cbBuffer < (unsigned long)status) + return SEC_E_INSUFFICIENT_MEMORY; + + CopyMemory(pBuffer->pvBuffer, context->ReadBuffer, status); + pBuffer->cbBuffer = status; + return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED; + } + else + { + pBuffer->cbBuffer = 0; + return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED; + } + } + + return SEC_E_OK; +} + +SECURITY_STATUS schannel_openssl_encrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage) +{ + int status = 0; + int ssl_error = 0; + PSecBuffer pStreamBodyBuffer = NULL; + PSecBuffer pStreamHeaderBuffer = NULL; + PSecBuffer pStreamTrailerBuffer = NULL; + pStreamHeaderBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_STREAM_HEADER); + pStreamBodyBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA); + pStreamTrailerBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_STREAM_TRAILER); + + if ((!pStreamHeaderBuffer) || (!pStreamBodyBuffer) || (!pStreamTrailerBuffer)) + return SEC_E_INVALID_TOKEN; + + status = SSL_write(context->ssl, pStreamBodyBuffer->pvBuffer, pStreamBodyBuffer->cbBuffer); + + if (status < 0) + { + ssl_error = SSL_get_error(context->ssl, status); + WLog_ERR(TAG, "SSL_write: %s", openssl_get_ssl_error_string(ssl_error)); + } + + ERR_clear_error(); + status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN); + + if (status > 0) + { + size_t ustatus = (size_t)status; + size_t length = 0; + size_t offset = 0; + + length = + (pStreamHeaderBuffer->cbBuffer > ustatus) ? ustatus : pStreamHeaderBuffer->cbBuffer; + CopyMemory(pStreamHeaderBuffer->pvBuffer, &context->ReadBuffer[offset], length); + ustatus -= length; + offset += length; + length = (pStreamBodyBuffer->cbBuffer > ustatus) ? ustatus : pStreamBodyBuffer->cbBuffer; + CopyMemory(pStreamBodyBuffer->pvBuffer, &context->ReadBuffer[offset], length); + ustatus -= length; + offset += length; + length = + (pStreamTrailerBuffer->cbBuffer > ustatus) ? ustatus : pStreamTrailerBuffer->cbBuffer; + CopyMemory(pStreamTrailerBuffer->pvBuffer, &context->ReadBuffer[offset], length); + } + + return SEC_E_OK; +} + +SECURITY_STATUS schannel_openssl_decrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage) +{ + int status = 0; + int length = 0; + BYTE* buffer = NULL; + int ssl_error = 0; + PSecBuffer pBuffer = NULL; + pBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA); + + if (!pBuffer) + return SEC_E_INVALID_TOKEN; + + ERR_clear_error(); + status = BIO_write(context->bioRead, pBuffer->pvBuffer, pBuffer->cbBuffer); + if (status > 0) + status = SSL_read(context->ssl, pBuffer->pvBuffer, pBuffer->cbBuffer); + + if (status < 0) + { + ssl_error = SSL_get_error(context->ssl, status); + WLog_ERR(TAG, "SSL_read: %s", openssl_get_ssl_error_string(ssl_error)); + } + + length = status; + buffer = pBuffer->pvBuffer; + pMessage->pBuffers[0].BufferType = SECBUFFER_STREAM_HEADER; + pMessage->pBuffers[0].cbBuffer = 5; + pMessage->pBuffers[1].BufferType = SECBUFFER_DATA; + pMessage->pBuffers[1].pvBuffer = buffer; + pMessage->pBuffers[1].cbBuffer = length; + pMessage->pBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER; + pMessage->pBuffers[2].cbBuffer = 36; + pMessage->pBuffers[3].BufferType = SECBUFFER_EMPTY; + pMessage->pBuffers[3].cbBuffer = 0; + return SEC_E_OK; +} + +SCHANNEL_OPENSSL* schannel_openssl_new(void) +{ + SCHANNEL_OPENSSL* context = NULL; + context = (SCHANNEL_OPENSSL*)calloc(1, sizeof(SCHANNEL_OPENSSL)); + + if (context != NULL) + { + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + context->connected = FALSE; + } + + return context; +} + +void schannel_openssl_free(SCHANNEL_OPENSSL* context) +{ + if (context) + { + free(context->ReadBuffer); + free(context->WriteBuffer); + free(context); + } +} + +#else + +int schannel_openssl_client_init(SCHANNEL_OPENSSL* context) +{ + return 0; +} + +int schannel_openssl_server_init(SCHANNEL_OPENSSL* context) +{ + return 0; +} + +SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context, + PSecBufferDesc pInput, + PSecBufferDesc pOutput) +{ + return SEC_E_OK; +} + +SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context, + PSecBufferDesc pInput, + PSecBufferDesc pOutput) +{ + return SEC_E_OK; +} + +SECURITY_STATUS schannel_openssl_encrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage) +{ + return SEC_E_OK; +} + +SECURITY_STATUS schannel_openssl_decrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage) +{ + return SEC_E_OK; +} + +SCHANNEL_OPENSSL* schannel_openssl_new(void) +{ + return NULL; +} + +void schannel_openssl_free(SCHANNEL_OPENSSL* context) +{ +} + +#endif diff --git a/winpr/libwinpr/sspi/Schannel/schannel_openssl.h b/winpr/libwinpr/sspi/Schannel/schannel_openssl.h new file mode 100644 index 0000000..31993e9 --- /dev/null +++ b/winpr/libwinpr/sspi/Schannel/schannel_openssl.h @@ -0,0 +1,52 @@ +/** + * WinPR: Windows Portable Runtime + * Schannel Security Package (OpenSSL) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_SCHANNEL_OPENSSL_H +#define WINPR_SSPI_SCHANNEL_OPENSSL_H + +#include + +#include "../sspi.h" + +/* OpenSSL includes windows.h */ +#include + +typedef struct S_SCHANNEL_OPENSSL SCHANNEL_OPENSSL; + +int schannel_openssl_client_init(SCHANNEL_OPENSSL* context); +int schannel_openssl_server_init(SCHANNEL_OPENSSL* context); + +SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context, + PSecBufferDesc pInput, + PSecBufferDesc pOutput); +SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context, + PSecBufferDesc pInput, + PSecBufferDesc pOutput); + +SECURITY_STATUS schannel_openssl_encrypt_message(SCHANNEL_OPENSSL* context, + PSecBufferDesc pMessage); +SECURITY_STATUS schannel_openssl_decrypt_message(SCHANNEL_OPENSSL* context, + PSecBufferDesc pMessage); + +void schannel_openssl_free(SCHANNEL_OPENSSL* context); + +WINPR_ATTR_MALLOC(schannel_openssl_free, 1) +SCHANNEL_OPENSSL* schannel_openssl_new(void); + +#endif /* WINPR_SSPI_SCHANNEL_OPENSSL_H */ diff --git a/winpr/libwinpr/sspi/sspi.c b/winpr/libwinpr/sspi/sspi.c new file mode 100644 index 0000000..acecb5b --- /dev/null +++ b/winpr/libwinpr/sspi/sspi.c @@ -0,0 +1,1129 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Security Support Provider Interface (SSPI) + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#define _NO_KSECDD_IMPORT_ 1 + +WINPR_PRAGMA_DIAG_POP + +#include + +#include +#include +#include +#include +#include + +#include "sspi.h" + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES + +static wLog* g_Log = NULL; + +static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT; +#if defined(WITH_NATIVE_SSPI) +static HMODULE g_SspiModule = NULL; +static SecurityFunctionTableW windows_SecurityFunctionTableW = { 0 }; +static SecurityFunctionTableA windows_SecurityFunctionTableA = { 0 }; +#endif + +static SecurityFunctionTableW* g_SspiW = NULL; +static SecurityFunctionTableA* g_SspiA = NULL; + +#if defined(WITH_NATIVE_SSPI) +static BOOL ShouldUseNativeSspi(void); +static BOOL InitializeSspiModule_Native(void); +#endif + +#if defined(WITH_NATIVE_SSPI) +BOOL ShouldUseNativeSspi(void) +{ + BOOL status = FALSE; +#ifdef _WIN32 + LPCSTR sspi = "WINPR_NATIVE_SSPI"; + DWORD nSize; + char* env = NULL; + nSize = GetEnvironmentVariableA(sspi, NULL, 0); + + if (!nSize) + return TRUE; + + env = (LPSTR)malloc(nSize); + + if (!env) + return TRUE; + + if (GetEnvironmentVariableA(sspi, env, nSize) != nSize - 1) + { + free(env); + return TRUE; + } + + if (strcmp(env, "0") == 0) + status = FALSE; + else + status = TRUE; + + free(env); +#endif + return status; +} +#endif + +#if defined(WITH_NATIVE_SSPI) +BOOL InitializeSspiModule_Native(void) +{ + SecurityFunctionTableW* pSspiW = NULL; + SecurityFunctionTableA* pSspiA = NULL; + INIT_SECURITY_INTERFACE_W pInitSecurityInterfaceW; + INIT_SECURITY_INTERFACE_A pInitSecurityInterfaceA; + g_SspiModule = LoadLibraryA("secur32.dll"); + + if (!g_SspiModule) + g_SspiModule = LoadLibraryA("sspicli.dll"); + + if (!g_SspiModule) + return FALSE; + + pInitSecurityInterfaceW = + (INIT_SECURITY_INTERFACE_W)GetProcAddress(g_SspiModule, "InitSecurityInterfaceW"); + pInitSecurityInterfaceA = + (INIT_SECURITY_INTERFACE_A)GetProcAddress(g_SspiModule, "InitSecurityInterfaceA"); + + if (pInitSecurityInterfaceW) + { + pSspiW = pInitSecurityInterfaceW(); + + if (pSspiW) + { + g_SspiW = &windows_SecurityFunctionTableW; + CopyMemory(g_SspiW, pSspiW, + FIELD_OFFSET(SecurityFunctionTableW, SetContextAttributesW)); + + g_SspiW->dwVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_3; + + g_SspiW->SetContextAttributesW = + (SET_CONTEXT_ATTRIBUTES_FN_W)GetProcAddress(g_SspiModule, "SetContextAttributesW"); + + g_SspiW->SetCredentialsAttributesW = (SET_CREDENTIALS_ATTRIBUTES_FN_W)GetProcAddress( + g_SspiModule, "SetCredentialsAttributesW"); + } + } + + if (pInitSecurityInterfaceA) + { + pSspiA = pInitSecurityInterfaceA(); + + if (pSspiA) + { + g_SspiA = &windows_SecurityFunctionTableA; + CopyMemory(g_SspiA, pSspiA, + FIELD_OFFSET(SecurityFunctionTableA, SetContextAttributesA)); + + g_SspiA->dwVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_3; + + g_SspiA->SetContextAttributesA = + (SET_CONTEXT_ATTRIBUTES_FN_W)GetProcAddress(g_SspiModule, "SetContextAttributesA"); + + g_SspiA->SetCredentialsAttributesA = (SET_CREDENTIALS_ATTRIBUTES_FN_W)GetProcAddress( + g_SspiModule, "SetCredentialsAttributesA"); + } + } + + return TRUE; +} +#endif + +static BOOL CALLBACK InitializeSspiModuleInt(PINIT_ONCE once, PVOID param, PVOID* context) +{ + BOOL status = FALSE; +#if defined(WITH_NATIVE_SSPI) + DWORD flags = 0; + + if (param) + flags = *(DWORD*)param; + +#endif + sspi_GlobalInit(); + g_Log = WLog_Get("com.winpr.sspi"); +#if defined(WITH_NATIVE_SSPI) + + if (flags && (flags & SSPI_INTERFACE_NATIVE)) + { + status = InitializeSspiModule_Native(); + } + else if (flags && (flags & SSPI_INTERFACE_WINPR)) + { + g_SspiW = winpr_InitSecurityInterfaceW(); + g_SspiA = winpr_InitSecurityInterfaceA(); + status = TRUE; + } + + if (!status && ShouldUseNativeSspi()) + { + status = InitializeSspiModule_Native(); + } + +#endif + + if (!status) + { + g_SspiW = winpr_InitSecurityInterfaceW(); + g_SspiA = winpr_InitSecurityInterfaceA(); + } + + return TRUE; +} + +const char* GetSecurityStatusString(SECURITY_STATUS status) +{ + switch (status) + { + case SEC_E_OK: + return "SEC_E_OK"; + + case SEC_E_INSUFFICIENT_MEMORY: + return "SEC_E_INSUFFICIENT_MEMORY"; + + case SEC_E_INVALID_HANDLE: + return "SEC_E_INVALID_HANDLE"; + + case SEC_E_UNSUPPORTED_FUNCTION: + return "SEC_E_UNSUPPORTED_FUNCTION"; + + case SEC_E_TARGET_UNKNOWN: + return "SEC_E_TARGET_UNKNOWN"; + + case SEC_E_INTERNAL_ERROR: + return "SEC_E_INTERNAL_ERROR"; + + case SEC_E_SECPKG_NOT_FOUND: + return "SEC_E_SECPKG_NOT_FOUND"; + + case SEC_E_NOT_OWNER: + return "SEC_E_NOT_OWNER"; + + case SEC_E_CANNOT_INSTALL: + return "SEC_E_CANNOT_INSTALL"; + + case SEC_E_INVALID_TOKEN: + return "SEC_E_INVALID_TOKEN"; + + case SEC_E_CANNOT_PACK: + return "SEC_E_CANNOT_PACK"; + + case SEC_E_QOP_NOT_SUPPORTED: + return "SEC_E_QOP_NOT_SUPPORTED"; + + case SEC_E_NO_IMPERSONATION: + return "SEC_E_NO_IMPERSONATION"; + + case SEC_E_LOGON_DENIED: + return "SEC_E_LOGON_DENIED"; + + case SEC_E_UNKNOWN_CREDENTIALS: + return "SEC_E_UNKNOWN_CREDENTIALS"; + + case SEC_E_NO_CREDENTIALS: + return "SEC_E_NO_CREDENTIALS"; + + case SEC_E_MESSAGE_ALTERED: + return "SEC_E_MESSAGE_ALTERED"; + + case SEC_E_OUT_OF_SEQUENCE: + return "SEC_E_OUT_OF_SEQUENCE"; + + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + return "SEC_E_NO_AUTHENTICATING_AUTHORITY"; + + case SEC_E_BAD_PKGID: + return "SEC_E_BAD_PKGID"; + + case SEC_E_CONTEXT_EXPIRED: + return "SEC_E_CONTEXT_EXPIRED"; + + case SEC_E_INCOMPLETE_MESSAGE: + return "SEC_E_INCOMPLETE_MESSAGE"; + + case SEC_E_INCOMPLETE_CREDENTIALS: + return "SEC_E_INCOMPLETE_CREDENTIALS"; + + case SEC_E_BUFFER_TOO_SMALL: + return "SEC_E_BUFFER_TOO_SMALL"; + + case SEC_E_WRONG_PRINCIPAL: + return "SEC_E_WRONG_PRINCIPAL"; + + case SEC_E_TIME_SKEW: + return "SEC_E_TIME_SKEW"; + + case SEC_E_UNTRUSTED_ROOT: + return "SEC_E_UNTRUSTED_ROOT"; + + case SEC_E_ILLEGAL_MESSAGE: + return "SEC_E_ILLEGAL_MESSAGE"; + + case SEC_E_CERT_UNKNOWN: + return "SEC_E_CERT_UNKNOWN"; + + case SEC_E_CERT_EXPIRED: + return "SEC_E_CERT_EXPIRED"; + + case SEC_E_ENCRYPT_FAILURE: + return "SEC_E_ENCRYPT_FAILURE"; + + case SEC_E_DECRYPT_FAILURE: + return "SEC_E_DECRYPT_FAILURE"; + + case SEC_E_ALGORITHM_MISMATCH: + return "SEC_E_ALGORITHM_MISMATCH"; + + case SEC_E_SECURITY_QOS_FAILED: + return "SEC_E_SECURITY_QOS_FAILED"; + + case SEC_E_UNFINISHED_CONTEXT_DELETED: + return "SEC_E_UNFINISHED_CONTEXT_DELETED"; + + case SEC_E_NO_TGT_REPLY: + return "SEC_E_NO_TGT_REPLY"; + + case SEC_E_NO_IP_ADDRESSES: + return "SEC_E_NO_IP_ADDRESSES"; + + case SEC_E_WRONG_CREDENTIAL_HANDLE: + return "SEC_E_WRONG_CREDENTIAL_HANDLE"; + + case SEC_E_CRYPTO_SYSTEM_INVALID: + return "SEC_E_CRYPTO_SYSTEM_INVALID"; + + case SEC_E_MAX_REFERRALS_EXCEEDED: + return "SEC_E_MAX_REFERRALS_EXCEEDED"; + + case SEC_E_MUST_BE_KDC: + return "SEC_E_MUST_BE_KDC"; + + case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED: + return "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED"; + + case SEC_E_TOO_MANY_PRINCIPALS: + return "SEC_E_TOO_MANY_PRINCIPALS"; + + case SEC_E_NO_PA_DATA: + return "SEC_E_NO_PA_DATA"; + + case SEC_E_PKINIT_NAME_MISMATCH: + return "SEC_E_PKINIT_NAME_MISMATCH"; + + case SEC_E_SMARTCARD_LOGON_REQUIRED: + return "SEC_E_SMARTCARD_LOGON_REQUIRED"; + + case SEC_E_SHUTDOWN_IN_PROGRESS: + return "SEC_E_SHUTDOWN_IN_PROGRESS"; + + case SEC_E_KDC_INVALID_REQUEST: + return "SEC_E_KDC_INVALID_REQUEST"; + + case SEC_E_KDC_UNABLE_TO_REFER: + return "SEC_E_KDC_UNABLE_TO_REFER"; + + case SEC_E_KDC_UNKNOWN_ETYPE: + return "SEC_E_KDC_UNKNOWN_ETYPE"; + + case SEC_E_UNSUPPORTED_PREAUTH: + return "SEC_E_UNSUPPORTED_PREAUTH"; + + case SEC_E_DELEGATION_REQUIRED: + return "SEC_E_DELEGATION_REQUIRED"; + + case SEC_E_BAD_BINDINGS: + return "SEC_E_BAD_BINDINGS"; + + case SEC_E_MULTIPLE_ACCOUNTS: + return "SEC_E_MULTIPLE_ACCOUNTS"; + + case SEC_E_NO_KERB_KEY: + return "SEC_E_NO_KERB_KEY"; + + case SEC_E_CERT_WRONG_USAGE: + return "SEC_E_CERT_WRONG_USAGE"; + + case SEC_E_DOWNGRADE_DETECTED: + return "SEC_E_DOWNGRADE_DETECTED"; + + case SEC_E_SMARTCARD_CERT_REVOKED: + return "SEC_E_SMARTCARD_CERT_REVOKED"; + + case SEC_E_ISSUING_CA_UNTRUSTED: + return "SEC_E_ISSUING_CA_UNTRUSTED"; + + case SEC_E_REVOCATION_OFFLINE_C: + return "SEC_E_REVOCATION_OFFLINE_C"; + + case SEC_E_PKINIT_CLIENT_FAILURE: + return "SEC_E_PKINIT_CLIENT_FAILURE"; + + case SEC_E_SMARTCARD_CERT_EXPIRED: + return "SEC_E_SMARTCARD_CERT_EXPIRED"; + + case SEC_E_NO_S4U_PROT_SUPPORT: + return "SEC_E_NO_S4U_PROT_SUPPORT"; + + case SEC_E_CROSSREALM_DELEGATION_FAILURE: + return "SEC_E_CROSSREALM_DELEGATION_FAILURE"; + + case SEC_E_REVOCATION_OFFLINE_KDC: + return "SEC_E_REVOCATION_OFFLINE_KDC"; + + case SEC_E_ISSUING_CA_UNTRUSTED_KDC: + return "SEC_E_ISSUING_CA_UNTRUSTED_KDC"; + + case SEC_E_KDC_CERT_EXPIRED: + return "SEC_E_KDC_CERT_EXPIRED"; + + case SEC_E_KDC_CERT_REVOKED: + return "SEC_E_KDC_CERT_REVOKED"; + + case SEC_E_INVALID_PARAMETER: + return "SEC_E_INVALID_PARAMETER"; + + case SEC_E_DELEGATION_POLICY: + return "SEC_E_DELEGATION_POLICY"; + + case SEC_E_POLICY_NLTM_ONLY: + return "SEC_E_POLICY_NLTM_ONLY"; + + case SEC_E_NO_CONTEXT: + return "SEC_E_NO_CONTEXT"; + + case SEC_E_PKU2U_CERT_FAILURE: + return "SEC_E_PKU2U_CERT_FAILURE"; + + case SEC_E_MUTUAL_AUTH_FAILED: + return "SEC_E_MUTUAL_AUTH_FAILED"; + + case SEC_I_CONTINUE_NEEDED: + return "SEC_I_CONTINUE_NEEDED"; + + case SEC_I_COMPLETE_NEEDED: + return "SEC_I_COMPLETE_NEEDED"; + + case SEC_I_COMPLETE_AND_CONTINUE: + return "SEC_I_COMPLETE_AND_CONTINUE"; + + case SEC_I_LOCAL_LOGON: + return "SEC_I_LOCAL_LOGON"; + + case SEC_I_CONTEXT_EXPIRED: + return "SEC_I_CONTEXT_EXPIRED"; + + case SEC_I_INCOMPLETE_CREDENTIALS: + return "SEC_I_INCOMPLETE_CREDENTIALS"; + + case SEC_I_RENEGOTIATE: + return "SEC_I_RENEGOTIATE"; + + case SEC_I_NO_LSA_CONTEXT: + return "SEC_I_NO_LSA_CONTEXT"; + + case SEC_I_SIGNATURE_NEEDED: + return "SEC_I_SIGNATURE_NEEDED"; + + case SEC_I_NO_RENEGOTIATION: + return "SEC_I_NO_RENEGOTIATION"; + } + + return NtStatus2Tag((DWORD)status); +} + +BOOL IsSecurityStatusError(SECURITY_STATUS status) +{ + BOOL error = TRUE; + + switch (status) + { + case SEC_E_OK: + case SEC_I_CONTINUE_NEEDED: + case SEC_I_COMPLETE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + case SEC_I_LOCAL_LOGON: + case SEC_I_CONTEXT_EXPIRED: + case SEC_I_INCOMPLETE_CREDENTIALS: + case SEC_I_RENEGOTIATE: + case SEC_I_NO_LSA_CONTEXT: + case SEC_I_SIGNATURE_NEEDED: + case SEC_I_NO_RENEGOTIATION: + error = FALSE; + break; + } + + return error; +} + +SecurityFunctionTableW* SEC_ENTRY InitSecurityInterfaceExW(DWORD flags) +{ + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, &flags, NULL); + WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceExW"); + return g_SspiW; +} + +SecurityFunctionTableA* SEC_ENTRY InitSecurityInterfaceExA(DWORD flags) +{ + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, &flags, NULL); + WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceExA"); + return g_SspiA; +} + +/** + * Standard SSPI API + */ + +/* Package Management */ + +SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesW(ULONG* pcPackages, + PSecPkgInfoW* ppPackageInfo) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->EnumerateSecurityPackagesW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->EnumerateSecurityPackagesW(pcPackages, ppPackageInfo); + WLog_Print(g_Log, WLOG_DEBUG, "EnumerateSecurityPackagesW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesA(ULONG* pcPackages, + PSecPkgInfoA* ppPackageInfo) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->EnumerateSecurityPackagesA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->EnumerateSecurityPackagesA(pcPackages, ppPackageInfo); + WLog_Print(g_Log, WLOG_DEBUG, "EnumerateSecurityPackagesA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SecurityFunctionTableW* SEC_ENTRY sspi_InitSecurityInterfaceW(void) +{ + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceW"); + return g_SspiW; +} + +SecurityFunctionTableA* SEC_ENTRY sspi_InitSecurityInterfaceA(void) +{ + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceA"); + return g_SspiA; +} + +SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoW(SEC_WCHAR* pszPackageName, + PSecPkgInfoW* ppPackageInfo) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->QuerySecurityPackageInfoW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->QuerySecurityPackageInfoW(pszPackageName, ppPackageInfo); + WLog_Print(g_Log, WLOG_DEBUG, "QuerySecurityPackageInfoW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoA(SEC_CHAR* pszPackageName, + PSecPkgInfoA* ppPackageInfo) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->QuerySecurityPackageInfoA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->QuerySecurityPackageInfoA(pszPackageName, ppPackageInfo); + WLog_Print(g_Log, WLOG_DEBUG, "QuerySecurityPackageInfoA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +/* Credential Management */ + +SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->AcquireCredentialsHandleW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->AcquireCredentialsHandleW(pszPrincipal, pszPackage, fCredentialUse, pvLogonID, + pAuthData, pGetKeyFn, pvGetKeyArgument, + phCredential, ptsExpiry); + WLog_Print(g_Log, WLOG_DEBUG, "AcquireCredentialsHandleW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->AcquireCredentialsHandleA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse, pvLogonID, + pAuthData, pGetKeyFn, pvGetKeyArgument, + phCredential, ptsExpiry); + WLog_Print(g_Log, WLOG_DEBUG, "AcquireCredentialsHandleA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_ExportSecurityContext(PCtxtHandle phContext, ULONG fFlags, + PSecBuffer pPackedContext, HANDLE* pToken) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->ExportSecurityContext)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->ExportSecurityContext(phContext, fFlags, pPackedContext, pToken); + WLog_Print(g_Log, WLOG_DEBUG, "ExportSecurityContext: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_FreeCredentialsHandle(PCredHandle phCredential) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->FreeCredentialsHandle)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->FreeCredentialsHandle(phCredential); + WLog_Print(g_Log, WLOG_DEBUG, "FreeCredentialsHandle: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextW(SEC_WCHAR* pszPackage, + PSecBuffer pPackedContext, HANDLE pToken, + PCtxtHandle phContext) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->ImportSecurityContextW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->ImportSecurityContextW(pszPackage, pPackedContext, pToken, phContext); + WLog_Print(g_Log, WLOG_DEBUG, "ImportSecurityContextW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextA(SEC_CHAR* pszPackage, + PSecBuffer pPackedContext, HANDLE pToken, + PCtxtHandle phContext) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->ImportSecurityContextA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->ImportSecurityContextA(pszPackage, pPackedContext, pToken, phContext); + WLog_Print(g_Log, WLOG_DEBUG, "ImportSecurityContextA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->QueryCredentialsAttributesW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "QueryCredentialsAttributesW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->QueryCredentialsAttributesA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->QueryCredentialsAttributesA(phCredential, ulAttribute, pBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "QueryCredentialsAttributesA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +/* Context Management */ + +SECURITY_STATUS SEC_ENTRY sspi_AcceptSecurityContext(PCredHandle phCredential, + PCtxtHandle phContext, PSecBufferDesc pInput, + ULONG fContextReq, ULONG TargetDataRep, + PCtxtHandle phNewContext, + PSecBufferDesc pOutput, PULONG pfContextAttr, + PTimeStamp ptsTimeStamp) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->AcceptSecurityContext)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = + g_SspiW->AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep, + phNewContext, pOutput, pfContextAttr, ptsTimeStamp); + WLog_Print(g_Log, WLOG_DEBUG, "AcceptSecurityContext: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_ApplyControlToken(PCtxtHandle phContext, PSecBufferDesc pInput) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->ApplyControlToken)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->ApplyControlToken(phContext, pInput); + WLog_Print(g_Log, WLOG_DEBUG, "ApplyControlToken: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_CompleteAuthToken(PCtxtHandle phContext, PSecBufferDesc pToken) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->CompleteAuthToken)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->CompleteAuthToken(phContext, pToken); + WLog_Print(g_Log, WLOG_DEBUG, "CompleteAuthToken: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_DeleteSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->DeleteSecurityContext)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->DeleteSecurityContext(phContext); + WLog_Print(g_Log, WLOG_DEBUG, "DeleteSecurityContext: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_FreeContextBuffer(void* pvContextBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->FreeContextBuffer)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->FreeContextBuffer(pvContextBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "FreeContextBuffer: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_ImpersonateSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->ImpersonateSecurityContext)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->ImpersonateSecurityContext(phContext); + WLog_Print(g_Log, WLOG_DEBUG, "ImpersonateSecurityContext: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->InitializeSecurityContextW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->InitializeSecurityContextW( + phCredential, phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep, pInput, + Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry); + WLog_Print(g_Log, WLOG_DEBUG, "InitializeSecurityContextW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->InitializeSecurityContextA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->InitializeSecurityContextA( + phCredential, phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep, pInput, + Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry); + WLog_Print(g_Log, WLOG_DEBUG, "InitializeSecurityContextA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesW(PCtxtHandle phContext, ULONG ulAttribute, + void* pBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->QueryContextAttributesW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->QueryContextAttributesW(phContext, ulAttribute, pBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "QueryContextAttributesW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesA(PCtxtHandle phContext, ULONG ulAttribute, + void* pBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->QueryContextAttributesA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->QueryContextAttributesA(phContext, ulAttribute, pBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "QueryContextAttributesA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityContextToken(PCtxtHandle phContext, HANDLE* phToken) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->QuerySecurityContextToken)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->QuerySecurityContextToken(phContext, phToken); + WLog_Print(g_Log, WLOG_DEBUG, "QuerySecurityContextToken: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesW(PCtxtHandle phContext, ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->SetContextAttributesW)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "SetContextAttributesW: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesA(PCtxtHandle phContext, ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiA && g_SspiA->SetContextAttributesA)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiA->SetContextAttributesA(phContext, ulAttribute, pBuffer, cbBuffer); + WLog_Print(g_Log, WLOG_DEBUG, "SetContextAttributesA: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_RevertSecurityContext(PCtxtHandle phContext) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->RevertSecurityContext)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->RevertSecurityContext(phContext); + WLog_Print(g_Log, WLOG_DEBUG, "RevertSecurityContext: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +/* Message Support */ + +SECURITY_STATUS SEC_ENTRY sspi_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage, + ULONG MessageSeqNo, PULONG pfQOP) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->DecryptMessage)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->DecryptMessage(phContext, pMessage, MessageSeqNo, pfQOP); + WLog_Print(g_Log, WLOG_DEBUG, "DecryptMessage: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->EncryptMessage)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->EncryptMessage(phContext, fQOP, pMessage, MessageSeqNo); + WLog_Print(g_Log, WLOG_DEBUG, "EncryptMessage: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->MakeSignature)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->MakeSignature(phContext, fQOP, pMessage, MessageSeqNo); + WLog_Print(g_Log, WLOG_DEBUG, "MakeSignature: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +SECURITY_STATUS SEC_ENTRY sspi_VerifySignature(PCtxtHandle phContext, PSecBufferDesc pMessage, + ULONG MessageSeqNo, PULONG pfQOP) +{ + SECURITY_STATUS status = 0; + InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, NULL, NULL); + + if (!(g_SspiW && g_SspiW->VerifySignature)) + { + WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation"); + + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = g_SspiW->VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP); + WLog_Print(g_Log, WLOG_DEBUG, "VerifySignature: %s (0x%08" PRIX32 ")", + GetSecurityStatusString(status), status); + return status; +} + +WINPR_PRAGMA_DIAG_POP + +void sspi_FreeAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity) +{ + if (!identity) + return; + free(identity->User); + identity->UserLength = (UINT32)0; + identity->User = NULL; + + free(identity->Domain); + identity->DomainLength = (UINT32)0; + identity->Domain = NULL; + + if (identity->PasswordLength > 0) + memset(identity->Password, 0, identity->PasswordLength); + free(identity->Password); + identity->Password = NULL; + identity->PasswordLength = (UINT32)0; +} diff --git a/winpr/libwinpr/sspi/sspi.h b/winpr/libwinpr/sspi/sspi.h new file mode 100644 index 0000000..f6791f9 --- /dev/null +++ b/winpr/libwinpr/sspi/sspi.h @@ -0,0 +1,93 @@ +/** + * WinPR: Windows Portable Runtime + * Security Support Provider Interface (SSPI) + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_PRIVATE_H +#define WINPR_SSPI_PRIVATE_H + +#include + +#define SCHANNEL_CB_MAX_TOKEN 0x00006000 + +#define SSPI_CREDENTIALS_PASSWORD_HASH 0x00000001 + +#define SSPI_CREDENTIALS_HASH_LENGTH_OFFSET 512 + +typedef struct +{ + DWORD flags; + ULONG fCredentialUse; + SEC_GET_KEY_FN pGetKeyFn; + void* pvGetKeyArgument; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINPR_NTLM_SETTINGS ntlmSettings; + SEC_WINPR_KERBEROS_SETTINGS kerbSettings; +} SSPI_CREDENTIALS; + +SSPI_CREDENTIALS* sspi_CredentialsNew(void); +void sspi_CredentialsFree(SSPI_CREDENTIALS* credentials); + +PSecBuffer sspi_FindSecBuffer(PSecBufferDesc pMessage, ULONG BufferType); + +SecHandle* sspi_SecureHandleAlloc(void); +void sspi_SecureHandleInvalidate(SecHandle* handle); +void* sspi_SecureHandleGetLowerPointer(SecHandle* handle); +void sspi_SecureHandleSetLowerPointer(SecHandle* handle, void* pointer); +void* sspi_SecureHandleGetUpperPointer(SecHandle* handle); +void sspi_SecureHandleSetUpperPointer(SecHandle* handle, void* pointer); +void sspi_SecureHandleFree(SecHandle* handle); + +enum SecurityFunctionTableIndex +{ + EnumerateSecurityPackagesIndex = 1, + Reserved1Index = 2, + QueryCredentialsAttributesIndex = 3, + AcquireCredentialsHandleIndex = 4, + FreeCredentialsHandleIndex = 5, + Reserved2Index = 6, + InitializeSecurityContextIndex = 7, + AcceptSecurityContextIndex = 8, + CompleteAuthTokenIndex = 9, + DeleteSecurityContextIndex = 10, + ApplyControlTokenIndex = 11, + QueryContextAttributesIndex = 12, + ImpersonateSecurityContextIndex = 13, + RevertSecurityContextIndex = 14, + MakeSignatureIndex = 15, + VerifySignatureIndex = 16, + FreeContextBufferIndex = 17, + QuerySecurityPackageInfoIndex = 18, + Reserved3Index = 19, + Reserved4Index = 20, + ExportSecurityContextIndex = 21, + ImportSecurityContextIndex = 22, + AddCredentialsIndex = 23, + Reserved8Index = 24, + QuerySecurityContextTokenIndex = 25, + EncryptMessageIndex = 26, + DecryptMessageIndex = 27, + SetContextAttributesIndex = 28, + SetCredentialsAttributesIndex = 29 +}; + +BOOL IsSecurityStatusError(SECURITY_STATUS status); + +#include "sspi_gss.h" +#include "sspi_winpr.h" + +#endif /* WINPR_SSPI_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/sspi_export.c b/winpr/libwinpr/sspi/sspi_export.c new file mode 100644 index 0000000..32258cf --- /dev/null +++ b/winpr/libwinpr/sspi/sspi_export.c @@ -0,0 +1,345 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Security Support Provider Interface (SSPI) + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#ifdef _WIN32 +#define SEC_ENTRY __stdcall +#define SSPI_EXPORT __declspec(dllexport) +#else +#include +#define SEC_ENTRY +#define SSPI_EXPORT WINPR_API +#endif + +#ifdef _WIN32 +typedef long LONG; +typedef unsigned long ULONG; +#endif +typedef LONG SECURITY_STATUS; + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES + +#ifdef SSPI_DLL + +/** + * Standard SSPI API + */ + +/* Package Management */ + +extern SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesW(void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesW(void* pcPackages, + void* ppPackageInfo) +{ + return sspi_EnumerateSecurityPackagesW(pcPackages, ppPackageInfo); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesA(void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesA(void* pcPackages, + void* ppPackageInfo) +{ + return sspi_EnumerateSecurityPackagesA(pcPackages, ppPackageInfo); +} + +extern void* SEC_ENTRY sspi_InitSecurityInterfaceW(void); + +SSPI_EXPORT void* SEC_ENTRY InitSecurityInterfaceW(void) +{ + return sspi_InitSecurityInterfaceW(); +} + +extern void* SEC_ENTRY sspi_InitSecurityInterfaceA(void); + +SSPI_EXPORT void* SEC_ENTRY InitSecurityInterfaceA(void) +{ + return sspi_InitSecurityInterfaceA(); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoW(void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoW(void* pszPackageName, + void* ppPackageInfo) +{ + return sspi_QuerySecurityPackageInfoW(pszPackageName, ppPackageInfo); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoA(void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoA(void* pszPackageName, + void* ppPackageInfo) +{ + return sspi_QuerySecurityPackageInfoA(pszPackageName, ppPackageInfo); +} + +/* Credential Management */ + +extern SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleW(void*, void*, ULONG, void*, void*, + void*, void*, void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleW( + void* pszPrincipal, void* pszPackage, ULONG fCredentialUse, void* pvLogonID, void* pAuthData, + void* pGetKeyFn, void* pvGetKeyArgument, void* phCredential, void* ptsExpiry) +{ + return sspi_AcquireCredentialsHandleW(pszPrincipal, pszPackage, fCredentialUse, pvLogonID, + pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, + ptsExpiry); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleA(void*, void*, ULONG, void*, void*, + void*, void*, void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleA( + void* pszPrincipal, void* pszPackage, ULONG fCredentialUse, void* pvLogonID, void* pAuthData, + void* pGetKeyFn, void* pvGetKeyArgument, void* phCredential, void* ptsExpiry) +{ + return sspi_AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse, pvLogonID, + pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, + ptsExpiry); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_ExportSecurityContext(void*, ULONG, void*, void**); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ExportSecurityContext(void* phContext, ULONG fFlags, + void* pPackedContext, void** pToken) +{ + return sspi_ExportSecurityContext(phContext, fFlags, pPackedContext, pToken); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_FreeCredentialsHandle(void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY FreeCredentialsHandle(void* phCredential) +{ + return sspi_FreeCredentialsHandle(phCredential); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextW(void*, void*, void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ImportSecurityContextW(void* pszPackage, void* pPackedContext, + void* pToken, void* phContext) +{ + return sspi_ImportSecurityContextW(pszPackage, pPackedContext, pToken, phContext); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextA(void*, void*, void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ImportSecurityContextA(void* pszPackage, void* pPackedContext, + void* pToken, void* phContext) +{ + return sspi_ImportSecurityContextA(pszPackage, pPackedContext, pToken, phContext); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesW(void*, ULONG, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesW(void* phCredential, + ULONG ulAttribute, void* pBuffer) +{ + return sspi_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesA(void*, ULONG, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesA(void* phCredential, + ULONG ulAttribute, void* pBuffer) +{ + return sspi_QueryCredentialsAttributesA(phCredential, ulAttribute, pBuffer); +} + +/* Context Management */ + +extern SECURITY_STATUS SEC_ENTRY sspi_AcceptSecurityContext(void*, void*, void*, ULONG, ULONG, + void*, void*, void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY AcceptSecurityContext(void* phCredential, void* phContext, + void* pInput, ULONG fContextReq, + ULONG TargetDataRep, void* phNewContext, + void* pOutput, void* pfContextAttr, + void* ptsTimeStamp) +{ + return sspi_AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep, + phNewContext, pOutput, pfContextAttr, ptsTimeStamp); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_ApplyControlToken(void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ApplyControlToken(void* phContext, void* pInput) +{ + return sspi_ApplyControlToken(phContext, pInput); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_CompleteAuthToken(void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY CompleteAuthToken(void* phContext, void* pToken) +{ + return sspi_CompleteAuthToken(phContext, pToken); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_DeleteSecurityContext(void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY DeleteSecurityContext(void* phContext) +{ + return sspi_DeleteSecurityContext(phContext); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_FreeContextBuffer(void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY FreeContextBuffer(void* pvContextBuffer) +{ + return sspi_FreeContextBuffer(pvContextBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_ImpersonateSecurityContext(void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext(void* phContext) +{ + return sspi_ImpersonateSecurityContext(phContext); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_InitializeSecurityContextW(void*, void*, void*, ULONG, ULONG, + ULONG, void*, ULONG, void*, void*, + void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY InitializeSecurityContextW( + void* phCredential, void* phContext, void* pszTargetName, ULONG fContextReq, ULONG Reserved1, + ULONG TargetDataRep, void* pInput, ULONG Reserved2, void* phNewContext, void* pOutput, + void* pfContextAttr, void* ptsExpiry) +{ + return sspi_InitializeSecurityContextW(phCredential, phContext, pszTargetName, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_InitializeSecurityContextA(void*, void*, void*, ULONG, ULONG, + ULONG, void*, ULONG, void*, void*, + void*, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY InitializeSecurityContextA( + void* phCredential, void* phContext, void* pszTargetName, ULONG fContextReq, ULONG Reserved1, + ULONG TargetDataRep, void* pInput, ULONG Reserved2, void* phNewContext, void* pOutput, + void* pfContextAttr, void* ptsExpiry) +{ + return sspi_InitializeSecurityContextA(phCredential, phContext, pszTargetName, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesW(void*, ULONG, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryContextAttributesW(void* phContext, ULONG ulAttribute, + void* pBuffer) +{ + return sspi_QueryContextAttributesW(phContext, ulAttribute, pBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesA(void*, ULONG, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryContextAttributesA(void* phContext, ULONG ulAttribute, + void* pBuffer) +{ + return sspi_QueryContextAttributesA(phContext, ulAttribute, pBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityContextToken(void*, void**); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QuerySecurityContextToken(void* phContext, void** phToken) +{ + return sspi_QuerySecurityContextToken(phContext, phToken); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesW(void*, ULONG, void*, ULONG); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY SetContextAttributesW(void* phContext, ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + return sspi_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesA(void*, ULONG, void*, ULONG); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY SetContextAttributesA(void* phContext, ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + return sspi_SetContextAttributesA(phContext, ulAttribute, pBuffer, cbBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_SetCredentialsAttributesW(void*, ULONG, void*, ULONG); + +static SECURITY_STATUS SEC_ENTRY SetCredentialsAttributesW(void* phCredential, ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + return sspi_SetCredentialsAttributesW(phCredential, ulAttribute, pBuffer, cbBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_SetCredentialsAttributesA(void*, ULONG, void*, ULONG); + +static SECURITY_STATUS SEC_ENTRY SetCredentialsAttributesA(void* phCredential, ULONG ulAttribute, + void* pBuffer, ULONG cbBuffer) +{ + return sspi_SetCredentialsAttributesA(phCredential, ulAttribute, pBuffer, cbBuffer); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_RevertSecurityContext(void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY RevertSecurityContext(void* phContext) +{ + return sspi_RevertSecurityContext(phContext); +} + +/* Message Support */ + +extern SECURITY_STATUS SEC_ENTRY sspi_DecryptMessage(void*, void*, ULONG, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY DecryptMessage(void* phContext, void* pMessage, + ULONG MessageSeqNo, void* pfQOP) +{ + return sspi_DecryptMessage(phContext, pMessage, MessageSeqNo, pfQOP); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_EncryptMessage(void*, ULONG, void*, ULONG); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY EncryptMessage(void* phContext, ULONG fQOP, void* pMessage, + ULONG MessageSeqNo) +{ + return sspi_EncryptMessage(phContext, fQOP, pMessage, MessageSeqNo); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_MakeSignature(void*, ULONG, void*, ULONG); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY MakeSignature(void* phContext, ULONG fQOP, void* pMessage, + ULONG MessageSeqNo) +{ + return sspi_MakeSignature(phContext, fQOP, pMessage, MessageSeqNo); +} + +extern SECURITY_STATUS SEC_ENTRY sspi_VerifySignature(void*, void*, ULONG, void*); + +SSPI_EXPORT SECURITY_STATUS SEC_ENTRY VerifySignature(void* phContext, void* pMessage, + ULONG MessageSeqNo, void* pfQOP) +{ + return sspi_VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP); +} + +#endif /* SSPI_DLL */ + +WINPR_PRAGMA_DIAG_POP diff --git a/winpr/libwinpr/sspi/sspi_gss.c b/winpr/libwinpr/sspi/sspi_gss.c new file mode 100644 index 0000000..749973d --- /dev/null +++ b/winpr/libwinpr/sspi/sspi_gss.c @@ -0,0 +1,120 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Generic Security Service Application Program Interface (GSSAPI) + * + * Copyright 2015 ANSSI, Author Thomas Calderon + * Copyright 2015 Marc-Andre Moreau + * Copyright 2017 Dorian Ducournau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "sspi_gss.h" + +BOOL sspi_gss_wrap_token(SecBuffer* buf, const WinPrAsn1_OID* oid, uint16_t tok_id, + const sspi_gss_data* token) +{ + WinPrAsn1Encoder* enc = NULL; + BYTE tok_id_buf[2]; + WinPrAsn1_MemoryChunk mc = { 2, tok_id_buf }; + wStream s; + size_t len = 0; + BOOL ret = FALSE; + + WINPR_ASSERT(buf); + WINPR_ASSERT(oid); + WINPR_ASSERT(token); + + Data_Write_UINT16_BE(tok_id_buf, tok_id); + + enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); + if (!enc) + return FALSE; + + /* initialContextToken [APPLICATION 0] */ + if (!WinPrAsn1EncAppContainer(enc, 0)) + goto cleanup; + + /* thisMech OID */ + if (!WinPrAsn1EncOID(enc, oid)) + goto cleanup; + + /* TOK_ID */ + if (!WinPrAsn1EncRawContent(enc, &mc)) + goto cleanup; + + /* innerToken */ + mc.data = (BYTE*)token->data; + mc.len = token->length; + if (!WinPrAsn1EncRawContent(enc, &mc)) + goto cleanup; + + if (!WinPrAsn1EncEndContainer(enc)) + goto cleanup; + + if (!WinPrAsn1EncStreamSize(enc, &len) || len > buf->cbBuffer) + goto cleanup; + + Stream_StaticInit(&s, buf->pvBuffer, len); + if (WinPrAsn1EncToStream(enc, &s)) + { + buf->cbBuffer = len; + ret = TRUE; + } + +cleanup: + WinPrAsn1Encoder_Free(&enc); + return ret; +} + +BOOL sspi_gss_unwrap_token(const SecBuffer* buf, WinPrAsn1_OID* oid, uint16_t* tok_id, + sspi_gss_data* token) +{ + WinPrAsn1Decoder dec; + WinPrAsn1Decoder dec2; + WinPrAsn1_tagId tag = 0; + wStream sbuffer = { 0 }; + wStream* s = NULL; + + WINPR_ASSERT(buf); + WINPR_ASSERT(oid); + WINPR_ASSERT(token); + + WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, buf->pvBuffer, buf->cbBuffer); + + if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0) + return FALSE; + + if (!WinPrAsn1DecReadOID(&dec2, oid, FALSE)) + return FALSE; + + sbuffer = WinPrAsn1DecGetStream(&dec2); + s = &sbuffer; + + if (Stream_Length(s) < 2) + return FALSE; + + if (tok_id) + Stream_Read_INT16_BE(s, *tok_id); + + token->data = Stream_Pointer(s); + token->length = (UINT)Stream_GetRemainingLength(s); + + return TRUE; +} diff --git a/winpr/libwinpr/sspi/sspi_gss.h b/winpr/libwinpr/sspi/sspi_gss.h new file mode 100644 index 0000000..205f86a --- /dev/null +++ b/winpr/libwinpr/sspi/sspi_gss.h @@ -0,0 +1,85 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Generic Security Service Application Program Interface (GSSAPI) + * + * Copyright 2015 ANSSI, Author Thomas Calderon + * Copyright 2015 Marc-Andre Moreau + * Copyright 2017 Dorian Ducournau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_GSS_PRIVATE_H +#define WINPR_SSPI_GSS_PRIVATE_H + +#include +#include + +#ifdef WITH_KRB5_MIT +#include +typedef krb5_data sspi_gss_data; +#elif defined(WITH_KRB5_HEIMDAL) +#include +typedef krb5_data sspi_gss_data; +#else +typedef struct +{ + int32_t magic; + unsigned int length; + char* data; +} sspi_gss_data; +#endif + +#define SSPI_GSS_C_DELEG_FLAG 1 +#define SSPI_GSS_C_MUTUAL_FLAG 2 +#define SSPI_GSS_C_REPLAY_FLAG 4 +#define SSPI_GSS_C_SEQUENCE_FLAG 8 +#define SSPI_GSS_C_CONF_FLAG 16 +#define SSPI_GSS_C_INTEG_FLAG 32 + +#define FLAG_SENDER_IS_ACCEPTOR 0x01 +#define FLAG_WRAP_CONFIDENTIAL 0x02 +#define FLAG_ACCEPTOR_SUBKEY 0x04 + +#define KG_USAGE_ACCEPTOR_SEAL 22 +#define KG_USAGE_ACCEPTOR_SIGN 23 +#define KG_USAGE_INITIATOR_SEAL 24 +#define KG_USAGE_INITIATOR_SIGN 25 + +#define TOK_ID_AP_REQ 0x0100 +#define TOK_ID_AP_REP 0x0200 +#define TOK_ID_ERROR 0x0300 +#define TOK_ID_TGT_REQ 0x0400 +#define TOK_ID_TGT_REP 0x0401 + +#define TOK_ID_MIC 0x0404 +#define TOK_ID_WRAP 0x0504 +#define TOK_ID_MIC_V1 0x0101 +#define TOK_ID_WRAP_V1 0x0201 + +#define GSS_CHECKSUM_TYPE 0x8003 + +static INLINE BOOL sspi_gss_oid_compare(const WinPrAsn1_OID* oid1, const WinPrAsn1_OID* oid2) +{ + WINPR_ASSERT(oid1); + WINPR_ASSERT(oid2); + + return (oid1->len == oid2->len) && (memcmp(oid1->data, oid2->data, oid1->len) == 0); +} + +BOOL sspi_gss_wrap_token(SecBuffer* buf, const WinPrAsn1_OID* oid, uint16_t tok_id, + const sspi_gss_data* token); +BOOL sspi_gss_unwrap_token(const SecBuffer* buf, WinPrAsn1_OID* oid, uint16_t* tok_id, + sspi_gss_data* token); + +#endif /* WINPR_SSPI_GSS_PRIVATE_H */ diff --git a/winpr/libwinpr/sspi/sspi_winpr.c b/winpr/libwinpr/sspi/sspi_winpr.c new file mode 100644 index 0000000..1978650 --- /dev/null +++ b/winpr/libwinpr/sspi/sspi_winpr.c @@ -0,0 +1,2226 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Security Support Provider Interface (SSPI) + * + * Copyright 2012-2014 Marc-Andre Moreau + * Copyright 2017 Dorian Ducournau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "sspi.h" + +#include "sspi_winpr.h" + +#include "../log.h" +#define TAG WINPR_TAG("sspi") + +/* Authentication Functions: http://msdn.microsoft.com/en-us/library/windows/desktop/aa374731/ */ + +#include "NTLM/ntlm.h" +#include "NTLM/ntlm_export.h" +#include "CredSSP/credssp.h" +#include "Kerberos/kerberos.h" +#include "Negotiate/negotiate.h" +#include "Schannel/schannel.h" + +static const SecPkgInfoA* SecPkgInfoA_LIST[] = { &NTLM_SecPkgInfoA, &KERBEROS_SecPkgInfoA, + &NEGOTIATE_SecPkgInfoA, &CREDSSP_SecPkgInfoA, + &SCHANNEL_SecPkgInfoA }; + +static const SecPkgInfoW* SecPkgInfoW_LIST[] = { &NTLM_SecPkgInfoW, &KERBEROS_SecPkgInfoW, + &NEGOTIATE_SecPkgInfoW, &CREDSSP_SecPkgInfoW, + &SCHANNEL_SecPkgInfoW }; + +static SecurityFunctionTableA winpr_SecurityFunctionTableA; +static SecurityFunctionTableW winpr_SecurityFunctionTableW; + +typedef struct +{ + const SEC_CHAR* Name; + const SecurityFunctionTableA* SecurityFunctionTable; +} SecurityFunctionTableA_NAME; + +typedef struct +{ + const SEC_WCHAR* Name; + const SecurityFunctionTableW* SecurityFunctionTable; +} SecurityFunctionTableW_NAME; + +static const SecurityFunctionTableA_NAME SecurityFunctionTableA_NAME_LIST[] = { + { "NTLM", &NTLM_SecurityFunctionTableA }, + { "Kerberos", &KERBEROS_SecurityFunctionTableA }, + { "Negotiate", &NEGOTIATE_SecurityFunctionTableA }, + { "CREDSSP", &CREDSSP_SecurityFunctionTableA }, + { "Schannel", &SCHANNEL_SecurityFunctionTableA } +}; + +static WCHAR BUFFER_NAME_LIST_W[5][32] = { 0 }; + +static const SecurityFunctionTableW_NAME SecurityFunctionTableW_NAME_LIST[] = { + { BUFFER_NAME_LIST_W[0], &NTLM_SecurityFunctionTableW }, + { BUFFER_NAME_LIST_W[1], &KERBEROS_SecurityFunctionTableW }, + { BUFFER_NAME_LIST_W[2], &NEGOTIATE_SecurityFunctionTableW }, + { BUFFER_NAME_LIST_W[3], &CREDSSP_SecurityFunctionTableW }, + { BUFFER_NAME_LIST_W[4], &SCHANNEL_SecurityFunctionTableW } +}; + +#define SecHandle_LOWER_MAX 0xFFFFFFFF +#define SecHandle_UPPER_MAX 0xFFFFFFFE + +typedef struct +{ + void* contextBuffer; + UINT32 allocatorIndex; +} CONTEXT_BUFFER_ALLOC_ENTRY; + +typedef struct +{ + UINT32 cEntries; + UINT32 cMaxEntries; + CONTEXT_BUFFER_ALLOC_ENTRY* entries; +} CONTEXT_BUFFER_ALLOC_TABLE; + +static CONTEXT_BUFFER_ALLOC_TABLE ContextBufferAllocTable = { 0 }; + +static int sspi_ContextBufferAllocTableNew(void) +{ + size_t size = 0; + ContextBufferAllocTable.entries = NULL; + ContextBufferAllocTable.cEntries = 0; + ContextBufferAllocTable.cMaxEntries = 4; + size = sizeof(CONTEXT_BUFFER_ALLOC_ENTRY) * ContextBufferAllocTable.cMaxEntries; + ContextBufferAllocTable.entries = (CONTEXT_BUFFER_ALLOC_ENTRY*)calloc(1, size); + + if (!ContextBufferAllocTable.entries) + return -1; + + return 1; +} + +static int sspi_ContextBufferAllocTableGrow(void) +{ + size_t size = 0; + CONTEXT_BUFFER_ALLOC_ENTRY* entries = NULL; + ContextBufferAllocTable.cEntries = 0; + ContextBufferAllocTable.cMaxEntries *= 2; + size = sizeof(CONTEXT_BUFFER_ALLOC_ENTRY) * ContextBufferAllocTable.cMaxEntries; + + if (!size) + return -1; + + entries = (CONTEXT_BUFFER_ALLOC_ENTRY*)realloc(ContextBufferAllocTable.entries, size); + + if (!entries) + { + free(ContextBufferAllocTable.entries); + return -1; + } + + ContextBufferAllocTable.entries = entries; + ZeroMemory((void*)&ContextBufferAllocTable.entries[ContextBufferAllocTable.cMaxEntries / 2], + size / 2); + return 1; +} + +static void sspi_ContextBufferAllocTableFree(void) +{ + if (ContextBufferAllocTable.cEntries != 0) + WLog_ERR(TAG, "ContextBufferAllocTable.entries == %" PRIu32, + ContextBufferAllocTable.cEntries); + + ContextBufferAllocTable.cEntries = ContextBufferAllocTable.cMaxEntries = 0; + free(ContextBufferAllocTable.entries); + ContextBufferAllocTable.entries = NULL; +} + +static void* sspi_ContextBufferAlloc(UINT32 allocatorIndex, size_t size) +{ + void* contextBuffer = NULL; + + for (UINT32 index = 0; index < ContextBufferAllocTable.cMaxEntries; index++) + { + if (!ContextBufferAllocTable.entries[index].contextBuffer) + { + contextBuffer = calloc(1, size); + + if (!contextBuffer) + return NULL; + + ContextBufferAllocTable.cEntries++; + ContextBufferAllocTable.entries[index].contextBuffer = contextBuffer; + ContextBufferAllocTable.entries[index].allocatorIndex = allocatorIndex; + return ContextBufferAllocTable.entries[index].contextBuffer; + } + } + + /* no available entry was found, the table needs to be grown */ + + if (sspi_ContextBufferAllocTableGrow() < 0) + return NULL; + + /* the next call to sspi_ContextBufferAlloc() should now succeed */ + return sspi_ContextBufferAlloc(allocatorIndex, size); +} + +SSPI_CREDENTIALS* sspi_CredentialsNew(void) +{ + SSPI_CREDENTIALS* credentials = NULL; + credentials = (SSPI_CREDENTIALS*)calloc(1, sizeof(SSPI_CREDENTIALS)); + return credentials; +} + +void sspi_CredentialsFree(SSPI_CREDENTIALS* credentials) +{ + size_t userLength = 0; + size_t domainLength = 0; + size_t passwordLength = 0; + + if (!credentials) + return; + + if (credentials->ntlmSettings.samFile) + free(credentials->ntlmSettings.samFile); + + userLength = credentials->identity.UserLength; + domainLength = credentials->identity.DomainLength; + passwordLength = credentials->identity.PasswordLength; + + if (passwordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) /* [pth] */ + passwordLength -= SSPI_CREDENTIALS_HASH_LENGTH_OFFSET; + + if (credentials->identity.Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) + { + userLength *= 2; + domainLength *= 2; + passwordLength *= 2; + } + + if (credentials->identity.User) + memset(credentials->identity.User, 0, userLength); + if (credentials->identity.Domain) + memset(credentials->identity.Domain, 0, domainLength); + if (credentials->identity.Password) + memset(credentials->identity.Password, 0, passwordLength); + free(credentials->identity.User); + free(credentials->identity.Domain); + free(credentials->identity.Password); + free(credentials); +} + +void* sspi_SecBufferAlloc(PSecBuffer SecBuffer, ULONG size) +{ + if (!SecBuffer) + return NULL; + + SecBuffer->pvBuffer = calloc(1, size); + + if (!SecBuffer->pvBuffer) + return NULL; + + SecBuffer->cbBuffer = size; + return SecBuffer->pvBuffer; +} + +void sspi_SecBufferFree(PSecBuffer SecBuffer) +{ + if (!SecBuffer) + return; + + if (SecBuffer->pvBuffer) + memset(SecBuffer->pvBuffer, 0, SecBuffer->cbBuffer); + + free(SecBuffer->pvBuffer); + SecBuffer->pvBuffer = NULL; + SecBuffer->cbBuffer = 0; +} + +SecHandle* sspi_SecureHandleAlloc(void) +{ + SecHandle* handle = (SecHandle*)calloc(1, sizeof(SecHandle)); + + if (!handle) + return NULL; + + SecInvalidateHandle(handle); + return handle; +} + +void* sspi_SecureHandleGetLowerPointer(SecHandle* handle) +{ + void* pointer = NULL; + + if (!handle || !SecIsValidHandle(handle) || !handle->dwLower) + return NULL; + + pointer = (void*)~((size_t)handle->dwLower); + return pointer; +} + +void sspi_SecureHandleInvalidate(SecHandle* handle) +{ + if (!handle) + return; + + handle->dwLower = 0; + handle->dwUpper = 0; +} + +void sspi_SecureHandleSetLowerPointer(SecHandle* handle, void* pointer) +{ + if (!handle) + return; + + handle->dwLower = (ULONG_PTR)(~((size_t)pointer)); +} + +void* sspi_SecureHandleGetUpperPointer(SecHandle* handle) +{ + void* pointer = NULL; + + if (!handle || !SecIsValidHandle(handle) || !handle->dwUpper) + return NULL; + + pointer = (void*)~((size_t)handle->dwUpper); + return pointer; +} + +void sspi_SecureHandleSetUpperPointer(SecHandle* handle, void* pointer) +{ + if (!handle) + return; + + handle->dwUpper = (ULONG_PTR)(~((size_t)pointer)); +} + +void sspi_SecureHandleFree(SecHandle* handle) +{ + free(handle); +} + +int sspi_SetAuthIdentityW(SEC_WINNT_AUTH_IDENTITY* identity, const WCHAR* user, const WCHAR* domain, + const WCHAR* password) +{ + return sspi_SetAuthIdentityWithLengthW(identity, user, user ? _wcslen(user) : 0, domain, + domain ? _wcslen(domain) : 0, password, + password ? _wcslen(password) : 0); +} + +static BOOL copy(WCHAR** dst, ULONG* dstLen, const WCHAR* what, size_t len) +{ + WINPR_ASSERT(dst); + WINPR_ASSERT(dstLen); + + *dst = NULL; + *dstLen = 0; + + *dst = calloc(sizeof(WCHAR), len + 1); + if (!*dst) + return FALSE; + memcpy(*dst, what, len * sizeof(WCHAR)); + *dstLen = len; + return TRUE; +} + +int sspi_SetAuthIdentityWithLengthW(SEC_WINNT_AUTH_IDENTITY* identity, const WCHAR* user, + size_t userLen, const WCHAR* domain, size_t domainLen, + const WCHAR* password, size_t passwordLen) +{ + WINPR_ASSERT(identity); + sspi_FreeAuthIdentity(identity); + identity->Flags &= ~SEC_WINNT_AUTH_IDENTITY_ANSI; + identity->Flags |= SEC_WINNT_AUTH_IDENTITY_UNICODE; + + if (!copy(&identity->User, &identity->UserLength, user, userLen)) + return -1; + + if (!copy(&identity->Domain, &identity->DomainLength, domain, domainLen)) + return -1; + + if (!copy(&identity->Password, &identity->PasswordLength, password, passwordLen)) + return -1; + + return 1; +} + +static void zfree(WCHAR* str, size_t len) +{ + if (str) + memset(str, 0, len * sizeof(WCHAR)); + free(str); +} + +int sspi_SetAuthIdentityA(SEC_WINNT_AUTH_IDENTITY* identity, const char* user, const char* domain, + const char* password) +{ + int rc = 0; + size_t unicodeUserLenW = 0; + size_t unicodeDomainLenW = 0; + size_t unicodePasswordLenW = 0; + LPWSTR unicodeUser = ConvertUtf8ToWCharAlloc(user, &unicodeUserLenW); + LPWSTR unicodeDomain = ConvertUtf8ToWCharAlloc(domain, &unicodeDomainLenW); + LPWSTR unicodePassword = ConvertUtf8ToWCharAlloc(password, &unicodePasswordLenW); + + rc = sspi_SetAuthIdentityWithLengthW(identity, unicodeUser, unicodeUserLenW, unicodeDomain, + unicodeDomainLenW, unicodePassword, unicodePasswordLenW); + + zfree(unicodeUser, unicodeUserLenW); + zfree(unicodeDomain, unicodeDomainLenW); + zfree(unicodePassword, unicodePasswordLenW); + return rc; +} + +UINT32 sspi_GetAuthIdentityVersion(const void* identity) +{ + UINT32 version = 0; + + if (!identity) + return 0; + + version = *((const UINT32*)identity); + + if ((version == SEC_WINNT_AUTH_IDENTITY_VERSION) || + (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2)) + { + return version; + } + + return 0; // SEC_WINNT_AUTH_IDENTITY (no version) +} + +UINT32 sspi_GetAuthIdentityFlags(const void* identity) +{ + UINT32 version = 0; + UINT32 flags = 0; + + if (!identity) + return 0; + + version = sspi_GetAuthIdentityVersion(identity); + + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + flags = ((const SEC_WINNT_AUTH_IDENTITY_EX*)identity)->Flags; + } + else if (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2) + { + flags = ((const SEC_WINNT_AUTH_IDENTITY_EX2*)identity)->Flags; + } + else // SEC_WINNT_AUTH_IDENTITY + { + flags = ((const SEC_WINNT_AUTH_IDENTITY*)identity)->Flags; + } + + return flags; +} + +BOOL sspi_GetAuthIdentityUserDomainW(const void* identity, const WCHAR** pUser, UINT32* pUserLength, + const WCHAR** pDomain, UINT32* pDomainLength) +{ + UINT32 version = 0; + + if (!identity) + return FALSE; + + version = sspi_GetAuthIdentityVersion(identity); + + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + const SEC_WINNT_AUTH_IDENTITY_EXW* id = (const SEC_WINNT_AUTH_IDENTITY_EXW*)identity; + *pUser = (const WCHAR*)id->User; + *pUserLength = id->UserLength; + *pDomain = (const WCHAR*)id->Domain; + *pDomainLength = id->DomainLength; + } + else if (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2) + { + const SEC_WINNT_AUTH_IDENTITY_EX2* id = (const SEC_WINNT_AUTH_IDENTITY_EX2*)identity; + UINT32 UserOffset = id->UserOffset; + UINT32 DomainOffset = id->DomainOffset; + *pUser = (const WCHAR*)&((const uint8_t*)identity)[UserOffset]; + *pUserLength = id->UserLength / 2; + *pDomain = (const WCHAR*)&((const uint8_t*)identity)[DomainOffset]; + *pDomainLength = id->DomainLength / 2; + } + else // SEC_WINNT_AUTH_IDENTITY + { + const SEC_WINNT_AUTH_IDENTITY_W* id = (const SEC_WINNT_AUTH_IDENTITY_W*)identity; + *pUser = (const WCHAR*)id->User; + *pUserLength = id->UserLength; + *pDomain = (const WCHAR*)id->Domain; + *pDomainLength = id->DomainLength; + } + + return TRUE; +} + +BOOL sspi_GetAuthIdentityUserDomainA(const void* identity, const char** pUser, UINT32* pUserLength, + const char** pDomain, UINT32* pDomainLength) +{ + UINT32 version = 0; + + if (!identity) + return FALSE; + + version = sspi_GetAuthIdentityVersion(identity); + + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + const SEC_WINNT_AUTH_IDENTITY_EXA* id = (const SEC_WINNT_AUTH_IDENTITY_EXA*)identity; + *pUser = (const char*)id->User; + *pUserLength = id->UserLength; + *pDomain = (const char*)id->Domain; + *pDomainLength = id->DomainLength; + } + else if (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2) + { + const SEC_WINNT_AUTH_IDENTITY_EX2* id = (const SEC_WINNT_AUTH_IDENTITY_EX2*)identity; + UINT32 UserOffset = id->UserOffset; + UINT32 DomainOffset = id->DomainOffset; + *pUser = (const char*)&((const uint8_t*)identity)[UserOffset]; + *pUserLength = id->UserLength; + *pDomain = (const char*)&((const uint8_t*)identity)[DomainOffset]; + *pDomainLength = id->DomainLength; + } + else // SEC_WINNT_AUTH_IDENTITY + { + const SEC_WINNT_AUTH_IDENTITY_A* id = (const SEC_WINNT_AUTH_IDENTITY_A*)identity; + *pUser = (const char*)id->User; + *pUserLength = id->UserLength; + *pDomain = (const char*)id->Domain; + *pDomainLength = id->DomainLength; + } + + return TRUE; +} + +BOOL sspi_GetAuthIdentityPasswordW(const void* identity, const WCHAR** pPassword, + UINT32* pPasswordLength) +{ + UINT32 version = 0; + + if (!identity) + return FALSE; + + version = sspi_GetAuthIdentityVersion(identity); + + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + const SEC_WINNT_AUTH_IDENTITY_EXW* id = (const SEC_WINNT_AUTH_IDENTITY_EXW*)identity; + *pPassword = (const WCHAR*)id->Password; + *pPasswordLength = id->PasswordLength; + } + else if (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2) + { + return FALSE; // TODO: packed credentials + } + else // SEC_WINNT_AUTH_IDENTITY + { + const SEC_WINNT_AUTH_IDENTITY_W* id = (const SEC_WINNT_AUTH_IDENTITY_W*)identity; + *pPassword = (const WCHAR*)id->Password; + *pPasswordLength = id->PasswordLength; + } + + return TRUE; +} + +BOOL sspi_GetAuthIdentityPasswordA(const void* identity, const char** pPassword, + UINT32* pPasswordLength) +{ + UINT32 version = 0; + + if (!identity) + return FALSE; + + version = sspi_GetAuthIdentityVersion(identity); + + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + const SEC_WINNT_AUTH_IDENTITY_EXA* id = (const SEC_WINNT_AUTH_IDENTITY_EXA*)identity; + *pPassword = (const char*)id->Password; + *pPasswordLength = id->PasswordLength; + } + else if (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2) + { + return FALSE; // TODO: packed credentials + } + else // SEC_WINNT_AUTH_IDENTITY + { + const SEC_WINNT_AUTH_IDENTITY_A* id = (const SEC_WINNT_AUTH_IDENTITY_A*)identity; + *pPassword = (const char*)id->Password; + *pPasswordLength = id->PasswordLength; + } + + return TRUE; +} + +BOOL sspi_CopyAuthIdentityFieldsA(const SEC_WINNT_AUTH_IDENTITY_INFO* identity, char** pUser, + char** pDomain, char** pPassword) +{ + BOOL success = FALSE; + const char* UserA = NULL; + const char* DomainA = NULL; + const char* PasswordA = NULL; + const WCHAR* UserW = NULL; + const WCHAR* DomainW = NULL; + const WCHAR* PasswordW = NULL; + UINT32 UserLength = 0; + UINT32 DomainLength = 0; + UINT32 PasswordLength = 0; + + if (!identity || !pUser || !pDomain || !pPassword) + return FALSE; + + *pUser = *pDomain = *pPassword = NULL; + + UINT32 identityFlags = sspi_GetAuthIdentityFlags(identity); + + if (identityFlags & SEC_WINNT_AUTH_IDENTITY_ANSI) + { + if (!sspi_GetAuthIdentityUserDomainA(identity, &UserA, &UserLength, &DomainA, + &DomainLength)) + goto cleanup; + + if (!sspi_GetAuthIdentityPasswordA(identity, &PasswordA, &PasswordLength)) + goto cleanup; + + if (UserA && UserLength) + { + *pUser = _strdup(UserA); + + if (!(*pUser)) + goto cleanup; + } + + if (DomainA && DomainLength) + { + *pDomain = _strdup(DomainA); + + if (!(*pDomain)) + goto cleanup; + } + + if (PasswordA && PasswordLength) + { + *pPassword = _strdup(PasswordA); + + if (!(*pPassword)) + goto cleanup; + } + + success = TRUE; + } + else + { + if (!sspi_GetAuthIdentityUserDomainW(identity, &UserW, &UserLength, &DomainW, + &DomainLength)) + goto cleanup; + + if (!sspi_GetAuthIdentityPasswordW(identity, &PasswordW, &PasswordLength)) + goto cleanup; + + if (UserW && (UserLength > 0)) + { + *pUser = ConvertWCharNToUtf8Alloc(UserW, UserLength, NULL); + if (!(*pUser)) + goto cleanup; + } + + if (DomainW && (DomainLength > 0)) + { + *pDomain = ConvertWCharNToUtf8Alloc(DomainW, DomainLength, NULL); + if (!(*pDomain)) + goto cleanup; + } + + if (PasswordW && (PasswordLength > 0)) + { + *pPassword = ConvertWCharNToUtf8Alloc(PasswordW, PasswordLength, NULL); + if (!(*pPassword)) + goto cleanup; + } + + success = TRUE; + } + +cleanup: + return success; +} + +BOOL sspi_CopyAuthIdentityFieldsW(const SEC_WINNT_AUTH_IDENTITY_INFO* identity, WCHAR** pUser, + WCHAR** pDomain, WCHAR** pPassword) +{ + BOOL success = FALSE; + const char* UserA = NULL; + const char* DomainA = NULL; + const char* PasswordA = NULL; + const WCHAR* UserW = NULL; + const WCHAR* DomainW = NULL; + const WCHAR* PasswordW = NULL; + UINT32 UserLength = 0; + UINT32 DomainLength = 0; + UINT32 PasswordLength = 0; + + if (!identity || !pUser || !pDomain || !pPassword) + return FALSE; + + *pUser = *pDomain = *pPassword = NULL; + + UINT32 identityFlags = sspi_GetAuthIdentityFlags(identity); + + if (identityFlags & SEC_WINNT_AUTH_IDENTITY_ANSI) + { + if (!sspi_GetAuthIdentityUserDomainA(identity, &UserA, &UserLength, &DomainA, + &DomainLength)) + goto cleanup; + + if (!sspi_GetAuthIdentityPasswordA(identity, &PasswordA, &PasswordLength)) + goto cleanup; + + if (UserA && (UserLength > 0)) + { + WCHAR* ptr = ConvertUtf8NToWCharAlloc(UserA, UserLength, NULL); + *pUser = ptr; + + if (!ptr) + goto cleanup; + } + + if (DomainA && (DomainLength > 0)) + { + WCHAR* ptr = ConvertUtf8NToWCharAlloc(DomainA, DomainLength, NULL); + *pDomain = ptr; + if (!ptr) + goto cleanup; + } + + if (PasswordA && (PasswordLength > 0)) + { + WCHAR* ptr = ConvertUtf8NToWCharAlloc(PasswordA, PasswordLength, NULL); + + *pPassword = ptr; + if (!ptr) + goto cleanup; + } + + success = TRUE; + } + else + { + if (!sspi_GetAuthIdentityUserDomainW(identity, &UserW, &UserLength, &DomainW, + &DomainLength)) + goto cleanup; + + if (!sspi_GetAuthIdentityPasswordW(identity, &PasswordW, &PasswordLength)) + goto cleanup; + + if (UserW && UserLength) + { + *pUser = _wcsdup(UserW); + + if (!(*pUser)) + goto cleanup; + } + + if (DomainW && DomainLength) + { + *pDomain = _wcsdup(DomainW); + + if (!(*pDomain)) + goto cleanup; + } + + if (PasswordW && PasswordLength) + { + *pPassword = _wcsdup(PasswordW); + + if (!(*pPassword)) + goto cleanup; + } + + success = TRUE; + } + +cleanup: + return success; +} + +BOOL sspi_CopyAuthPackageListA(const SEC_WINNT_AUTH_IDENTITY_INFO* identity, char** pPackageList) +{ + UINT32 version = 0; + UINT32 identityFlags = 0; + char* PackageList = NULL; + const char* PackageListA = NULL; + const WCHAR* PackageListW = NULL; + UINT32 PackageListLength = 0; + UINT32 PackageListOffset = 0; + const void* pAuthData = (const void*)identity; + + if (!pAuthData) + return FALSE; + + version = sspi_GetAuthIdentityVersion(pAuthData); + identityFlags = sspi_GetAuthIdentityFlags(pAuthData); + + if (identityFlags & SEC_WINNT_AUTH_IDENTITY_ANSI) + { + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + const SEC_WINNT_AUTH_IDENTITY_EXA* ad = (const SEC_WINNT_AUTH_IDENTITY_EXA*)pAuthData; + PackageListA = (const char*)ad->PackageList; + PackageListLength = ad->PackageListLength; + } + + if (PackageListA && PackageListLength) + { + PackageList = _strdup(PackageListA); + } + } + else + { + if (version == SEC_WINNT_AUTH_IDENTITY_VERSION) + { + const SEC_WINNT_AUTH_IDENTITY_EXW* ad = (const SEC_WINNT_AUTH_IDENTITY_EXW*)pAuthData; + PackageListW = (const WCHAR*)ad->PackageList; + PackageListLength = ad->PackageListLength; + } + else if (version == SEC_WINNT_AUTH_IDENTITY_VERSION_2) + { + const SEC_WINNT_AUTH_IDENTITY_EX2* ad = (const SEC_WINNT_AUTH_IDENTITY_EX2*)pAuthData; + PackageListOffset = ad->PackageListOffset; + PackageListW = (const WCHAR*)&((const uint8_t*)pAuthData)[PackageListOffset]; + PackageListLength = ad->PackageListLength / 2; + } + + if (PackageListW && (PackageListLength > 0)) + PackageList = ConvertWCharNToUtf8Alloc(PackageListW, PackageListLength, NULL); + } + + if (PackageList) + { + *pPackageList = PackageList; + return TRUE; + } + + return FALSE; +} + +int sspi_CopyAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity, + const SEC_WINNT_AUTH_IDENTITY_INFO* srcIdentity) +{ + int status = 0; + UINT32 identityFlags = 0; + const char* UserA = NULL; + const char* DomainA = NULL; + const char* PasswordA = NULL; + const WCHAR* UserW = NULL; + const WCHAR* DomainW = NULL; + const WCHAR* PasswordW = NULL; + UINT32 UserLength = 0; + UINT32 DomainLength = 0; + UINT32 PasswordLength = 0; + + sspi_FreeAuthIdentity(identity); + + identityFlags = sspi_GetAuthIdentityFlags(srcIdentity); + + identity->Flags = identityFlags; + + if (identityFlags & SEC_WINNT_AUTH_IDENTITY_ANSI) + { + if (!sspi_GetAuthIdentityUserDomainA(srcIdentity, &UserA, &UserLength, &DomainA, + &DomainLength)) + { + return -1; + } + + if (!sspi_GetAuthIdentityPasswordA(srcIdentity, &PasswordA, &PasswordLength)) + { + return -1; + } + + status = sspi_SetAuthIdentity(identity, UserA, DomainA, PasswordA); + + if (status <= 0) + return -1; + + identity->Flags &= ~SEC_WINNT_AUTH_IDENTITY_ANSI; + identity->Flags |= SEC_WINNT_AUTH_IDENTITY_UNICODE; + return 1; + } + + identity->Flags |= SEC_WINNT_AUTH_IDENTITY_UNICODE; + + if (!sspi_GetAuthIdentityUserDomainW(srcIdentity, &UserW, &UserLength, &DomainW, &DomainLength)) + { + return -1; + } + + if (!sspi_GetAuthIdentityPasswordW(srcIdentity, &PasswordW, &PasswordLength)) + { + return -1; + } + + /* login/password authentication */ + identity->UserLength = UserLength; + + if (identity->UserLength > 0) + { + identity->User = (UINT16*)calloc((identity->UserLength + 1), sizeof(WCHAR)); + + if (!identity->User) + return -1; + + CopyMemory(identity->User, UserW, identity->UserLength * sizeof(WCHAR)); + identity->User[identity->UserLength] = 0; + } + + identity->DomainLength = DomainLength; + + if (identity->DomainLength > 0) + { + identity->Domain = (UINT16*)calloc((identity->DomainLength + 1), sizeof(WCHAR)); + + if (!identity->Domain) + return -1; + + CopyMemory(identity->Domain, DomainW, identity->DomainLength * sizeof(WCHAR)); + identity->Domain[identity->DomainLength] = 0; + } + + identity->PasswordLength = PasswordLength; + + if (identity->PasswordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) + identity->PasswordLength -= SSPI_CREDENTIALS_HASH_LENGTH_OFFSET; + + if (PasswordW) + { + identity->Password = (UINT16*)calloc((identity->PasswordLength + 1), sizeof(WCHAR)); + + if (!identity->Password) + return -1; + + CopyMemory(identity->Password, PasswordW, identity->PasswordLength * sizeof(WCHAR)); + identity->Password[identity->PasswordLength] = 0; + } + + identity->PasswordLength = PasswordLength; + /* End of login/password authentication */ + return 1; +} + +PSecBuffer sspi_FindSecBuffer(PSecBufferDesc pMessage, ULONG BufferType) +{ + PSecBuffer pSecBuffer = NULL; + + for (UINT32 index = 0; index < pMessage->cBuffers; index++) + { + if (pMessage->pBuffers[index].BufferType == BufferType) + { + pSecBuffer = &pMessage->pBuffers[index]; + break; + } + } + + return pSecBuffer; +} + +static BOOL WINPR_init(void) +{ + + for (size_t x = 0; x < ARRAYSIZE(SecurityFunctionTableA_NAME_LIST); x++) + { + const SecurityFunctionTableA_NAME* const cur = &SecurityFunctionTableA_NAME_LIST[x]; + InitializeConstWCharFromUtf8(cur->Name, BUFFER_NAME_LIST_W[x], + ARRAYSIZE(BUFFER_NAME_LIST_W[x])); + } + return TRUE; +} + +static BOOL CALLBACK sspi_init(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +{ + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + sspi_ContextBufferAllocTableNew(); + if (!SCHANNEL_init()) + return FALSE; + if (!KERBEROS_init()) + return FALSE; + if (!NTLM_init()) + return FALSE; + if (!CREDSSP_init()) + return FALSE; + if (!NEGOTIATE_init()) + return FALSE; + return WINPR_init(); +} + +void sspi_GlobalInit(void) +{ + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; + DWORD flags = 0; + InitOnceExecuteOnce(&once, sspi_init, &flags, NULL); +} + +void sspi_GlobalFinish(void) +{ + sspi_ContextBufferAllocTableFree(); +} + +static const SecurityFunctionTableA* sspi_GetSecurityFunctionTableAByNameA(const SEC_CHAR* Name) +{ + size_t cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST)); + + for (size_t index = 0; index < cPackages; index++) + { + if (strcmp(Name, SecurityFunctionTableA_NAME_LIST[index].Name) == 0) + { + return (const SecurityFunctionTableA*)SecurityFunctionTableA_NAME_LIST[index] + .SecurityFunctionTable; + } + } + + return NULL; +} + +static const SecurityFunctionTableW* sspi_GetSecurityFunctionTableWByNameW(const SEC_WCHAR* Name) +{ + size_t cPackages = sizeof(SecPkgInfoW_LIST) / sizeof(*(SecPkgInfoW_LIST)); + + for (size_t index = 0; index < cPackages; index++) + { + if (_wcscmp(Name, SecurityFunctionTableW_NAME_LIST[index].Name) == 0) + { + return (const SecurityFunctionTableW*)SecurityFunctionTableW_NAME_LIST[index] + .SecurityFunctionTable; + } + } + + return NULL; +} + +static const SecurityFunctionTableW* sspi_GetSecurityFunctionTableWByNameA(const SEC_CHAR* Name) +{ + SEC_WCHAR* NameW = NULL; + const SecurityFunctionTableW* table = NULL; + + if (!Name) + return NULL; + + NameW = ConvertUtf8ToWCharAlloc(Name, NULL); + + if (!NameW) + return NULL; + + table = sspi_GetSecurityFunctionTableWByNameW(NameW); + free(NameW); + return table; +} + +static void FreeContextBuffer_EnumerateSecurityPackages(void* contextBuffer); +static void FreeContextBuffer_QuerySecurityPackageInfo(void* contextBuffer); + +static void sspi_ContextBufferFree(void* contextBuffer) +{ + UINT32 allocatorIndex = 0; + + for (size_t index = 0; index < ContextBufferAllocTable.cMaxEntries; index++) + { + if (contextBuffer == ContextBufferAllocTable.entries[index].contextBuffer) + { + contextBuffer = ContextBufferAllocTable.entries[index].contextBuffer; + allocatorIndex = ContextBufferAllocTable.entries[index].allocatorIndex; + ContextBufferAllocTable.cEntries--; + ContextBufferAllocTable.entries[index].allocatorIndex = 0; + ContextBufferAllocTable.entries[index].contextBuffer = NULL; + + switch (allocatorIndex) + { + case EnumerateSecurityPackagesIndex: + FreeContextBuffer_EnumerateSecurityPackages(contextBuffer); + break; + + case QuerySecurityPackageInfoIndex: + FreeContextBuffer_QuerySecurityPackageInfo(contextBuffer); + break; + } + } + } +} + +/** + * Standard SSPI API + */ + +/* Package Management */ + +static SECURITY_STATUS SEC_ENTRY winpr_EnumerateSecurityPackagesW(ULONG* pcPackages, + PSecPkgInfoW* ppPackageInfo) +{ + size_t size = 0; + UINT32 cPackages = 0; + SecPkgInfoW* pPackageInfo = NULL; + cPackages = sizeof(SecPkgInfoW_LIST) / sizeof(*(SecPkgInfoW_LIST)); + size = sizeof(SecPkgInfoW) * cPackages; + pPackageInfo = (SecPkgInfoW*)sspi_ContextBufferAlloc(EnumerateSecurityPackagesIndex, size); + + if (!pPackageInfo) + return SEC_E_INSUFFICIENT_MEMORY; + + for (size_t index = 0; index < cPackages; index++) + { + pPackageInfo[index].fCapabilities = SecPkgInfoW_LIST[index]->fCapabilities; + pPackageInfo[index].wVersion = SecPkgInfoW_LIST[index]->wVersion; + pPackageInfo[index].wRPCID = SecPkgInfoW_LIST[index]->wRPCID; + pPackageInfo[index].cbMaxToken = SecPkgInfoW_LIST[index]->cbMaxToken; + pPackageInfo[index].Name = _wcsdup(SecPkgInfoW_LIST[index]->Name); + pPackageInfo[index].Comment = _wcsdup(SecPkgInfoW_LIST[index]->Comment); + } + + *(pcPackages) = cPackages; + *(ppPackageInfo) = pPackageInfo; + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY winpr_EnumerateSecurityPackagesA(ULONG* pcPackages, + PSecPkgInfoA* ppPackageInfo) +{ + size_t size = 0; + UINT32 cPackages = 0; + SecPkgInfoA* pPackageInfo = NULL; + cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST)); + size = sizeof(SecPkgInfoA) * cPackages; + pPackageInfo = (SecPkgInfoA*)sspi_ContextBufferAlloc(EnumerateSecurityPackagesIndex, size); + + if (!pPackageInfo) + return SEC_E_INSUFFICIENT_MEMORY; + + for (size_t index = 0; index < cPackages; index++) + { + pPackageInfo[index].fCapabilities = SecPkgInfoA_LIST[index]->fCapabilities; + pPackageInfo[index].wVersion = SecPkgInfoA_LIST[index]->wVersion; + pPackageInfo[index].wRPCID = SecPkgInfoA_LIST[index]->wRPCID; + pPackageInfo[index].cbMaxToken = SecPkgInfoA_LIST[index]->cbMaxToken; + pPackageInfo[index].Name = _strdup(SecPkgInfoA_LIST[index]->Name); + pPackageInfo[index].Comment = _strdup(SecPkgInfoA_LIST[index]->Comment); + + if (!pPackageInfo[index].Name || !pPackageInfo[index].Comment) + { + sspi_ContextBufferFree(pPackageInfo); + return SEC_E_INSUFFICIENT_MEMORY; + } + } + + *(pcPackages) = cPackages; + *(ppPackageInfo) = pPackageInfo; + return SEC_E_OK; +} + +static void FreeContextBuffer_EnumerateSecurityPackages(void* contextBuffer) +{ + UINT32 cPackages = 0; + SecPkgInfoA* pPackageInfo = (SecPkgInfoA*)contextBuffer; + cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST)); + + if (!pPackageInfo) + return; + + for (size_t index = 0; index < cPackages; index++) + { + free(pPackageInfo[index].Name); + free(pPackageInfo[index].Comment); + } + + free(pPackageInfo); +} + +SecurityFunctionTableW* SEC_ENTRY winpr_InitSecurityInterfaceW(void) +{ + return &winpr_SecurityFunctionTableW; +} + +SecurityFunctionTableA* SEC_ENTRY winpr_InitSecurityInterfaceA(void) +{ + return &winpr_SecurityFunctionTableA; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QuerySecurityPackageInfoW(SEC_WCHAR* pszPackageName, + PSecPkgInfoW* ppPackageInfo) +{ + size_t size = 0; + SecPkgInfoW* pPackageInfo = NULL; + size_t cPackages = sizeof(SecPkgInfoW_LIST) / sizeof(*(SecPkgInfoW_LIST)); + + for (size_t index = 0; index < cPackages; index++) + { + if (_wcscmp(pszPackageName, SecPkgInfoW_LIST[index]->Name) == 0) + { + size = sizeof(SecPkgInfoW); + pPackageInfo = + (SecPkgInfoW*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size); + + if (!pPackageInfo) + return SEC_E_INSUFFICIENT_MEMORY; + + pPackageInfo->fCapabilities = SecPkgInfoW_LIST[index]->fCapabilities; + pPackageInfo->wVersion = SecPkgInfoW_LIST[index]->wVersion; + pPackageInfo->wRPCID = SecPkgInfoW_LIST[index]->wRPCID; + pPackageInfo->cbMaxToken = SecPkgInfoW_LIST[index]->cbMaxToken; + pPackageInfo->Name = _wcsdup(SecPkgInfoW_LIST[index]->Name); + pPackageInfo->Comment = _wcsdup(SecPkgInfoW_LIST[index]->Comment); + *(ppPackageInfo) = pPackageInfo; + return SEC_E_OK; + } + } + + *(ppPackageInfo) = NULL; + return SEC_E_SECPKG_NOT_FOUND; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QuerySecurityPackageInfoA(SEC_CHAR* pszPackageName, + PSecPkgInfoA* ppPackageInfo) +{ + size_t size = 0; + SecPkgInfoA* pPackageInfo = NULL; + size_t cPackages = sizeof(SecPkgInfoA_LIST) / sizeof(*(SecPkgInfoA_LIST)); + + for (size_t index = 0; index < cPackages; index++) + { + if (strcmp(pszPackageName, SecPkgInfoA_LIST[index]->Name) == 0) + { + size = sizeof(SecPkgInfoA); + pPackageInfo = + (SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size); + + if (!pPackageInfo) + return SEC_E_INSUFFICIENT_MEMORY; + + pPackageInfo->fCapabilities = SecPkgInfoA_LIST[index]->fCapabilities; + pPackageInfo->wVersion = SecPkgInfoA_LIST[index]->wVersion; + pPackageInfo->wRPCID = SecPkgInfoA_LIST[index]->wRPCID; + pPackageInfo->cbMaxToken = SecPkgInfoA_LIST[index]->cbMaxToken; + pPackageInfo->Name = _strdup(SecPkgInfoA_LIST[index]->Name); + pPackageInfo->Comment = _strdup(SecPkgInfoA_LIST[index]->Comment); + + if (!pPackageInfo->Name || !pPackageInfo->Comment) + { + sspi_ContextBufferFree(pPackageInfo); + return SEC_E_INSUFFICIENT_MEMORY; + } + + *(ppPackageInfo) = pPackageInfo; + return SEC_E_OK; + } + } + + *(ppPackageInfo) = NULL; + return SEC_E_SECPKG_NOT_FOUND; +} + +void FreeContextBuffer_QuerySecurityPackageInfo(void* contextBuffer) +{ + SecPkgInfo* pPackageInfo = (SecPkgInfo*)contextBuffer; + + if (!pPackageInfo) + return; + + free(pPackageInfo->Name); + free(pPackageInfo->Comment); + free(pPackageInfo); +} + +/* Credential Management */ + +static SECURITY_STATUS SEC_ENTRY winpr_AcquireCredentialsHandleW( + SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = sspi_GetSecurityFunctionTableWByNameW(pszPackage); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->AcquireCredentialsHandleW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->AcquireCredentialsHandleW(pszPrincipal, pszPackage, fCredentialUse, pvLogonID, + pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, + ptsExpiry); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "AcquireCredentialsHandleW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_AcquireCredentialsHandleA( + SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID, + void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, + PTimeStamp ptsExpiry) +{ + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = sspi_GetSecurityFunctionTableAByNameA(pszPackage); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->AcquireCredentialsHandleA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse, pvLogonID, + pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, + ptsExpiry); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "AcquireCredentialsHandleA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_ExportSecurityContext(PCtxtHandle phContext, ULONG fFlags, + PSecBuffer pPackedContext, + HANDLE* pToken) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->ExportSecurityContext) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->ExportSecurityContext(phContext, fFlags, pPackedContext, pToken); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "ExportSecurityContext status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_FreeCredentialsHandle(PCredHandle phCredential) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->FreeCredentialsHandle) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->FreeCredentialsHandle(phCredential); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "FreeCredentialsHandle status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_ImportSecurityContextW(SEC_WCHAR* pszPackage, + PSecBuffer pPackedContext, + HANDLE pToken, PCtxtHandle phContext) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->ImportSecurityContextW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->ImportSecurityContextW(pszPackage, pPackedContext, pToken, phContext); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "ImportSecurityContextW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_ImportSecurityContextA(SEC_CHAR* pszPackage, + PSecBuffer pPackedContext, + HANDLE pToken, PCtxtHandle phContext) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->ImportSecurityContextA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->ImportSecurityContextA(pszPackage, pPackedContext, pToken, phContext); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "ImportSecurityContextA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QueryCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer) +{ + SEC_WCHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_WCHAR*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameW(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->QueryCredentialsAttributesW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "QueryCredentialsAttributesW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QueryCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->QueryCredentialsAttributesA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->QueryCredentialsAttributesA(phCredential, ulAttribute, pBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "QueryCredentialsAttributesA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_SetCredentialsAttributesW(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + SEC_WCHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_WCHAR*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameW(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->SetCredentialsAttributesW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->SetCredentialsAttributesW(phCredential, ulAttribute, pBuffer, cbBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "SetCredentialsAttributesW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_SetCredentialsAttributesA(PCredHandle phCredential, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->SetCredentialsAttributesA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->SetCredentialsAttributesA(phCredential, ulAttribute, pBuffer, cbBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "SetCredentialsAttributesA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +/* Context Management */ + +static SECURITY_STATUS SEC_ENTRY +winpr_AcceptSecurityContext(PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, + ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, + PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->AcceptSecurityContext) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = + table->AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep, + phNewContext, pOutput, pfContextAttr, ptsTimeStamp); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "AcceptSecurityContext status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_ApplyControlToken(PCtxtHandle phContext, + PSecBufferDesc pInput) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->ApplyControlToken) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->ApplyControlToken(phContext, pInput); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "ApplyControlToken status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_CompleteAuthToken(PCtxtHandle phContext, + PSecBufferDesc pToken) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->CompleteAuthToken) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->CompleteAuthToken(phContext, pToken); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "CompleteAuthToken status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_DeleteSecurityContext(PCtxtHandle phContext) +{ + const char* Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + const SecurityFunctionTableA* table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->DeleteSecurityContext) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + const UINT32 status = table->DeleteSecurityContext(phContext); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "DeleteSecurityContext status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_FreeContextBuffer(void* pvContextBuffer) +{ + if (!pvContextBuffer) + return SEC_E_INVALID_HANDLE; + + sspi_ContextBufferFree(pvContextBuffer); + return SEC_E_OK; +} + +static SECURITY_STATUS SEC_ENTRY winpr_ImpersonateSecurityContext(PCtxtHandle phContext) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->ImpersonateSecurityContext) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->ImpersonateSecurityContext(phContext); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "ImpersonateSecurityContext status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_InitializeSecurityContextW( + PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->InitializeSecurityContextW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->InitializeSecurityContextW(phCredential, phContext, pszTargetName, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "InitializeSecurityContextW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_InitializeSecurityContextA( + PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq, + ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, + PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phCredential); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->InitializeSecurityContextA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->InitializeSecurityContextA(phCredential, phContext, pszTargetName, fContextReq, + Reserved1, TargetDataRep, pInput, Reserved2, + phNewContext, pOutput, pfContextAttr, ptsExpiry); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "InitializeSecurityContextA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QueryContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->QueryContextAttributesW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->QueryContextAttributesW(phContext, ulAttribute, pBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "QueryContextAttributesW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QueryContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->QueryContextAttributesA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->QueryContextAttributesA(phContext, ulAttribute, pBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "QueryContextAttributesA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_QuerySecurityContextToken(PCtxtHandle phContext, + HANDLE* phToken) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->QuerySecurityContextToken) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->QuerySecurityContextToken(phContext, phToken); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "QuerySecurityContextToken status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_SetContextAttributesW(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->SetContextAttributesW) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "SetContextAttributesW status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_SetContextAttributesA(PCtxtHandle phContext, + ULONG ulAttribute, void* pBuffer, + ULONG cbBuffer) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->SetContextAttributesA) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->SetContextAttributesA(phContext, ulAttribute, pBuffer, cbBuffer); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "SetContextAttributesA status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_RevertSecurityContext(PCtxtHandle phContext) +{ + SEC_CHAR* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableW* table = NULL; + Name = (SEC_CHAR*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableWByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->RevertSecurityContext) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->RevertSecurityContext(phContext); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "RevertSecurityContext status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +/* Message Support */ + +static SECURITY_STATUS SEC_ENTRY winpr_DecryptMessage(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->DecryptMessage) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->DecryptMessage(phContext, pMessage, MessageSeqNo, pfQOP); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "DecryptMessage status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_EncryptMessage(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->EncryptMessage) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->EncryptMessage(phContext, fQOP, pMessage, MessageSeqNo); + + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "EncryptMessage status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status), + status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_MakeSignature(PCtxtHandle phContext, ULONG fQOP, + PSecBufferDesc pMessage, ULONG MessageSeqNo) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->MakeSignature) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->MakeSignature(phContext, fQOP, pMessage, MessageSeqNo); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "MakeSignature status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status), + status); + } + + return status; +} + +static SECURITY_STATUS SEC_ENTRY winpr_VerifySignature(PCtxtHandle phContext, + PSecBufferDesc pMessage, ULONG MessageSeqNo, + PULONG pfQOP) +{ + char* Name = NULL; + SECURITY_STATUS status = 0; + const SecurityFunctionTableA* table = NULL; + Name = (char*)sspi_SecureHandleGetUpperPointer(phContext); + + if (!Name) + return SEC_E_SECPKG_NOT_FOUND; + + table = sspi_GetSecurityFunctionTableAByNameA(Name); + + if (!table) + return SEC_E_SECPKG_NOT_FOUND; + + if (!table->VerifySignature) + { + WLog_WARN(TAG, "Security module does not provide an implementation"); + return SEC_E_UNSUPPORTED_FUNCTION; + } + + status = table->VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP); + + if (IsSecurityStatusError(status)) + { + WLog_WARN(TAG, "VerifySignature status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + + return status; +} + +static SecurityFunctionTableA winpr_SecurityFunctionTableA = { + 3, /* dwVersion */ + winpr_EnumerateSecurityPackagesA, /* EnumerateSecurityPackages */ + winpr_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */ + winpr_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */ + winpr_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + winpr_InitializeSecurityContextA, /* InitializeSecurityContext */ + winpr_AcceptSecurityContext, /* AcceptSecurityContext */ + winpr_CompleteAuthToken, /* CompleteAuthToken */ + winpr_DeleteSecurityContext, /* DeleteSecurityContext */ + winpr_ApplyControlToken, /* ApplyControlToken */ + winpr_QueryContextAttributesA, /* QueryContextAttributes */ + winpr_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + winpr_RevertSecurityContext, /* RevertSecurityContext */ + winpr_MakeSignature, /* MakeSignature */ + winpr_VerifySignature, /* VerifySignature */ + winpr_FreeContextBuffer, /* FreeContextBuffer */ + winpr_QuerySecurityPackageInfoA, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + winpr_ExportSecurityContext, /* ExportSecurityContext */ + winpr_ImportSecurityContextA, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + winpr_QuerySecurityContextToken, /* QuerySecurityContextToken */ + winpr_EncryptMessage, /* EncryptMessage */ + winpr_DecryptMessage, /* DecryptMessage */ + winpr_SetContextAttributesA, /* SetContextAttributes */ + winpr_SetCredentialsAttributesA, /* SetCredentialsAttributes */ +}; + +static SecurityFunctionTableW winpr_SecurityFunctionTableW = { + 3, /* dwVersion */ + winpr_EnumerateSecurityPackagesW, /* EnumerateSecurityPackages */ + winpr_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */ + winpr_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */ + winpr_FreeCredentialsHandle, /* FreeCredentialsHandle */ + NULL, /* Reserved2 */ + winpr_InitializeSecurityContextW, /* InitializeSecurityContext */ + winpr_AcceptSecurityContext, /* AcceptSecurityContext */ + winpr_CompleteAuthToken, /* CompleteAuthToken */ + winpr_DeleteSecurityContext, /* DeleteSecurityContext */ + winpr_ApplyControlToken, /* ApplyControlToken */ + winpr_QueryContextAttributesW, /* QueryContextAttributes */ + winpr_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ + winpr_RevertSecurityContext, /* RevertSecurityContext */ + winpr_MakeSignature, /* MakeSignature */ + winpr_VerifySignature, /* VerifySignature */ + winpr_FreeContextBuffer, /* FreeContextBuffer */ + winpr_QuerySecurityPackageInfoW, /* QuerySecurityPackageInfo */ + NULL, /* Reserved3 */ + NULL, /* Reserved4 */ + winpr_ExportSecurityContext, /* ExportSecurityContext */ + winpr_ImportSecurityContextW, /* ImportSecurityContext */ + NULL, /* AddCredentials */ + NULL, /* Reserved8 */ + winpr_QuerySecurityContextToken, /* QuerySecurityContextToken */ + winpr_EncryptMessage, /* EncryptMessage */ + winpr_DecryptMessage, /* DecryptMessage */ + winpr_SetContextAttributesW, /* SetContextAttributes */ + winpr_SetCredentialsAttributesW, /* SetCredentialsAttributes */ +}; diff --git a/winpr/libwinpr/sspi/sspi_winpr.h b/winpr/libwinpr/sspi/sspi_winpr.h new file mode 100644 index 0000000..2f7b55a --- /dev/null +++ b/winpr/libwinpr/sspi/sspi_winpr.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * Security Support Provider Interface (SSPI) + * + * Copyright 2012-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SSPI_WINPR_H +#define WINPR_SSPI_WINPR_H + +#include + +SecurityFunctionTableW* SEC_ENTRY winpr_InitSecurityInterfaceW(void); +SecurityFunctionTableA* SEC_ENTRY winpr_InitSecurityInterfaceA(void); + +#endif /* WINPR_SSPI_WINPR_H */ diff --git a/winpr/libwinpr/sspi/test/CMakeLists.txt b/winpr/libwinpr/sspi/test/CMakeLists.txt new file mode 100644 index 0000000..5e0f15d --- /dev/null +++ b/winpr/libwinpr/sspi/test/CMakeLists.txt @@ -0,0 +1,38 @@ + +set(MODULE_NAME "TestSspi") +set(MODULE_PREFIX "TEST_SSPI") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestQuerySecurityPackageInfo.c + TestEnumerateSecurityPackages.c + TestInitializeSecurityContext.c + TestAcquireCredentialsHandle.c + TestCredSSP.c + #TestSchannel.c + TestNTLM.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +include_directories(${OPENSSL_INCLUDE_DIR}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +if(WIN32) + set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} secur32 crypt32) +endif() + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c b/winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c new file mode 100644 index 0000000..05466c8 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c @@ -0,0 +1,60 @@ + +#include +#include +#include +#include + +static const char* test_User = "User"; +static const char* test_Domain = "Domain"; +static const char* test_Password = "Password"; + +int TestAcquireCredentialsHandle(int argc, char* argv[]) +{ + int rc = -1; + SECURITY_STATUS status = 0; + CredHandle credentials = { 0 }; + TimeStamp expiration; + SEC_WINNT_AUTH_IDENTITY identity; + SecurityFunctionTable* table = NULL; + SecPkgCredentials_Names credential_names; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + sspi_GlobalInit(); + table = InitSecurityInterfaceEx(0); + identity.User = (UINT16*)_strdup(test_User); + identity.Domain = (UINT16*)_strdup(test_Domain); + identity.Password = (UINT16*)_strdup(test_Password); + + if (!identity.User || !identity.Domain || !identity.Password) + goto fail; + + identity.UserLength = strlen(test_User); + identity.DomainLength = strlen(test_Domain); + identity.PasswordLength = strlen(test_Password); + identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + status = table->AcquireCredentialsHandle(NULL, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND, NULL, + &identity, NULL, NULL, &credentials, &expiration); + + if (status != SEC_E_OK) + goto fail; + + status = + table->QueryCredentialsAttributes(&credentials, SECPKG_CRED_ATTR_NAMES, &credential_names); + + if (status != SEC_E_OK) + goto fail; + + rc = 0; +fail: + + if (SecIsValidHandle(&credentials)) + table->FreeCredentialsHandle(&credentials); + + free(identity.User); + free(identity.Domain); + free(identity.Password); + sspi_GlobalFinish(); + return rc; +} diff --git a/winpr/libwinpr/sspi/test/TestCredSSP.c b/winpr/libwinpr/sspi/test/TestCredSSP.c new file mode 100644 index 0000000..b56d9e2 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestCredSSP.c @@ -0,0 +1,8 @@ + +#include +#include + +int TestCredSSP(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c b/winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c new file mode 100644 index 0000000..9de23c0 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c @@ -0,0 +1,40 @@ + +#include +#include +#include +#include +#include + +int TestEnumerateSecurityPackages(int argc, char* argv[]) +{ + ULONG cPackages = 0; + SECURITY_STATUS status = 0; + SecPkgInfo* pPackageInfo = NULL; + SecurityFunctionTable* table = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + sspi_GlobalInit(); + table = InitSecurityInterfaceEx(0); + + status = table->EnumerateSecurityPackages(&cPackages, &pPackageInfo); + + if (status != SEC_E_OK) + { + sspi_GlobalFinish(); + return -1; + } + + _tprintf(_T("\nEnumerateSecurityPackages (%") _T(PRIu32) _T("):\n"), cPackages); + + for (size_t index = 0; index < cPackages; index++) + { + _tprintf(_T("\"%s\", \"%s\"\n"), pPackageInfo[index].Name, pPackageInfo[index].Comment); + } + + table->FreeContextBuffer(pPackageInfo); + sspi_GlobalFinish(); + + return 0; +} diff --git a/winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c b/winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c new file mode 100644 index 0000000..88f5a7c --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c @@ -0,0 +1,115 @@ + +#include +#include +#include +#include + +static const char* test_User = "User"; +static const char* test_Domain = "Domain"; +static const char* test_Password = "Password"; + +int TestInitializeSecurityContext(int argc, char* argv[]) +{ + int rc = -1; + UINT32 cbMaxLen = 0; + UINT32 fContextReq = 0; + void* output_buffer = NULL; + CtxtHandle context; + ULONG pfContextAttr = 0; + SECURITY_STATUS status = 0; + CredHandle credentials = { 0 }; + TimeStamp expiration; + PSecPkgInfo pPackageInfo = NULL; + SEC_WINNT_AUTH_IDENTITY identity = { 0 }; + SecurityFunctionTable* table = NULL; + PSecBuffer p_SecBuffer = NULL; + SecBuffer output_SecBuffer; + SecBufferDesc output_SecBuffer_desc; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + sspi_GlobalInit(); + table = InitSecurityInterfaceEx(0); + status = table->QuerySecurityPackageInfo(NTLM_SSP_NAME, &pPackageInfo); + + if (status != SEC_E_OK) + { + printf("QuerySecurityPackageInfo status: 0x%08" PRIX32 "\n", status); + goto fail; + } + + cbMaxLen = pPackageInfo->cbMaxToken; + identity.User = (UINT16*)_strdup(test_User); + identity.Domain = (UINT16*)_strdup(test_Domain); + identity.Password = (UINT16*)_strdup(test_Password); + + if (!identity.User || !identity.Domain || !identity.Password) + goto fail; + + identity.UserLength = strlen(test_User); + identity.DomainLength = strlen(test_Domain); + identity.PasswordLength = strlen(test_Password); + identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + status = table->AcquireCredentialsHandle(NULL, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND, NULL, + &identity, NULL, NULL, &credentials, &expiration); + + if (status != SEC_E_OK) + { + printf("AcquireCredentialsHandle status: 0x%08" PRIX32 "\n", status); + goto fail; + } + + fContextReq = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | + ISC_REQ_DELEGATE; + output_buffer = malloc(cbMaxLen); + + if (!output_buffer) + { + printf("Memory allocation failed\n"); + goto fail; + } + + output_SecBuffer_desc.ulVersion = 0; + output_SecBuffer_desc.cBuffers = 1; + output_SecBuffer_desc.pBuffers = &output_SecBuffer; + output_SecBuffer.cbBuffer = cbMaxLen; + output_SecBuffer.BufferType = SECBUFFER_TOKEN; + output_SecBuffer.pvBuffer = output_buffer; + status = table->InitializeSecurityContext(&credentials, NULL, NULL, fContextReq, 0, 0, NULL, 0, + &context, &output_SecBuffer_desc, &pfContextAttr, + &expiration); + + if (status != SEC_I_CONTINUE_NEEDED) + { + printf("InitializeSecurityContext status: 0x%08" PRIX32 "\n", status); + goto fail; + } + + printf("cBuffers: %" PRIu32 " ulVersion: %" PRIu32 "\n", output_SecBuffer_desc.cBuffers, + output_SecBuffer_desc.ulVersion); + p_SecBuffer = &output_SecBuffer_desc.pBuffers[0]; + printf("BufferType: 0x%08" PRIX32 " cbBuffer: %" PRIu32 "\n", p_SecBuffer->BufferType, + p_SecBuffer->cbBuffer); + status = table->DeleteSecurityContext(&context); + + if (status != SEC_E_OK) + { + printf("DeleteSecurityContext status: 0x%08" PRIX32 "\n", status); + goto fail; + } + + rc = 0; +fail: + free(identity.User); + free(identity.Domain); + free(identity.Password); + free(output_buffer); + + if (SecIsValidHandle(&credentials)) + table->FreeCredentialsHandle(&credentials); + + table->FreeContextBuffer(pPackageInfo); + sspi_GlobalFinish(); + return rc; +} diff --git a/winpr/libwinpr/sspi/test/TestNTLM.c b/winpr/libwinpr/sspi/test/TestNTLM.c new file mode 100644 index 0000000..2ab3373 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestNTLM.c @@ -0,0 +1,694 @@ + +#include +#include +#include +#include +#include + +static BYTE TEST_NTLM_TIMESTAMP[8] = { 0x33, 0x57, 0xbd, 0xb1, 0x07, 0x8b, 0xcf, 0x01 }; + +static BYTE TEST_NTLM_CLIENT_CHALLENGE[8] = { 0x20, 0xc0, 0x2b, 0x3d, 0xc0, 0x61, 0xa7, 0x73 }; + +static BYTE TEST_NTLM_SERVER_CHALLENGE[8] = { 0xa4, 0xf1, 0xba, 0xa6, 0x7c, 0xdc, 0x1a, 0x12 }; + +static BYTE TEST_NTLM_NEGOTIATE[] = + "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x06\x03\x80\x25\x00\x00\x00\x0f"; + +static BYTE TEST_NTLM_CHALLENGE[] = + "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x00\x00\x00\x00" + "\x38\x00\x00\x00\x07\x82\x88\xa2\xa4\xf1\xba\xa6\x7c\xdc\x1a\x12" + "\x00\x00\x00\x00\x00\x00\x00\x00\x66\x00\x66\x00\x38\x00\x00\x00" + "\x06\x03\x80\x25\x00\x00\x00\x0f\x02\x00\x0e\x00\x4e\x00\x45\x00" + "\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00" + "\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00" + "\x6c\x00\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00" + "\x2e\x00\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00" + "\x6e\x00\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00" + "\x08\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x00\x00\x00\x00"; + +static BYTE TEST_NTLM_AUTHENTICATE[] = + "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00" + "\x82\x00\x00\x00\x08\x01\x08\x01\x9a\x00\x00\x00\x0c\x00\x0c\x00" + "\x58\x00\x00\x00\x10\x00\x10\x00\x64\x00\x00\x00\x0e\x00\x0e\x00" + "\x74\x00\x00\x00\x00\x00\x00\x00\xa2\x01\x00\x00\x05\x82\x88\xa2" + "\x06\x03\x80\x25\x00\x00\x00\x0f\x12\xe5\x5a\xf5\x80\xee\x3f\x29" + "\xe1\xde\x90\x4d\x73\x77\x06\x25\x44\x00\x6f\x00\x6d\x00\x61\x00" + "\x69\x00\x6e\x00\x55\x00\x73\x00\x65\x00\x72\x00\x6e\x00\x61\x00" + "\x6d\x00\x65\x00\x4e\x00\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00" + "\x52\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x62\x14\x68\xc8\x98\x12" + "\xe7\x39\xd8\x76\x1b\xe9\xf7\x54\xb5\xe3\x01\x01\x00\x00\x00\x00" + "\x00\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x20\xc0\x2b\x3d\xc0\x61" + "\xa7\x73\x00\x00\x00\x00\x02\x00\x0e\x00\x4e\x00\x45\x00\x57\x00" + "\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00\x45\x00" + "\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00\x6c\x00" + "\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00\x2e\x00" + "\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00\x6e\x00" + "\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00\x08\x00" + "\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x06\x00\x04\x00\x02\x00\x00\x00" + "\x08\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + "\x00\x20\x00\x00\x1e\x10\xf5\x2c\x54\x2f\x2e\x77\x1c\x13\xbf\xc3" + "\x3f\xe1\x7b\x28\x7e\x0b\x93\x5a\x39\xd2\xce\x12\xd7\xbd\x8c\x4e" + "\x2b\xb5\x0b\xf5\x0a\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x1a\x00\x48\x00\x54\x00" + "\x54\x00\x50\x00\x2f\x00\x72\x00\x77\x00\x2e\x00\x6c\x00\x6f\x00" + "\x63\x00\x61\x00\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00"; + +#define TEST_SSPI_INTERFACE SSPI_INTERFACE_WINPR + +static const char* TEST_NTLM_USER = "Username"; +static const char* TEST_NTLM_DOMAIN = "Domain"; +static const char* TEST_NTLM_PASSWORD = "P4ss123!"; + +// static const char* TEST_NTLM_HASH_STRING = "d5922a65c4d5c082ca444af1be0001db"; + +static const BYTE TEST_NTLM_HASH[16] = { 0xd5, 0x92, 0x2a, 0x65, 0xc4, 0xd5, 0xc0, 0x82, + 0xca, 0x44, 0x4a, 0xf1, 0xbe, 0x00, 0x01, 0xdb }; + +// static const char* TEST_NTLM_HASH_V2_STRING = "4c7f706f7dde05a9d1a0f4e7ffe3bfb8"; + +static const BYTE TEST_NTLM_V2_HASH[16] = { 0x4c, 0x7f, 0x70, 0x6f, 0x7d, 0xde, 0x05, 0xa9, + 0xd1, 0xa0, 0xf4, 0xe7, 0xff, 0xe3, 0xbf, 0xb8 }; + +#define NTLM_PACKAGE_NAME NTLM_SSP_NAME + +typedef struct +{ + CtxtHandle context; + ULONG cbMaxToken; + ULONG fContextReq; + ULONG pfContextAttr; + TimeStamp expiration; + PSecBuffer pBuffer; + SecBuffer inputBuffer[2]; + SecBuffer outputBuffer[2]; + BOOL haveContext; + BOOL haveInputBuffer; + LPTSTR ServicePrincipalName; + SecBufferDesc inputBufferDesc; + SecBufferDesc outputBufferDesc; + CredHandle credentials; + BOOL confidentiality; + SecPkgInfo* pPackageInfo; + SecurityFunctionTable* table; + SEC_WINNT_AUTH_IDENTITY identity; +} TEST_NTLM_CLIENT; + +static int test_ntlm_client_init(TEST_NTLM_CLIENT* ntlm, const char* user, const char* domain, + const char* password) +{ + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + + WINPR_ASSERT(ntlm); + + SecInvalidateHandle(&(ntlm->context)); + ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE); + sspi_SetAuthIdentity(&(ntlm->identity), user, domain, password); + status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo); + + if (status != SEC_E_OK) + { + fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; + } + + ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken; + status = ntlm->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, + NULL, &ntlm->identity, NULL, NULL, + &ntlm->credentials, &ntlm->expiration); + + if (status != SEC_E_OK) + { + fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; + } + + ntlm->haveContext = FALSE; + ntlm->haveInputBuffer = FALSE; + ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer)); + ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer)); + ntlm->fContextReq = 0; +#if 0 + /* HTTP authentication flags */ + ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY; +#endif + /* NLA authentication flags */ + ntlm->fContextReq |= ISC_REQ_MUTUAL_AUTH; + ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY; + ntlm->fContextReq |= ISC_REQ_USE_SESSION_KEY; + return 1; +} + +static void test_ntlm_client_uninit(TEST_NTLM_CLIENT* ntlm) +{ + if (!ntlm) + return; + + if (ntlm->outputBuffer[0].pvBuffer) + { + free(ntlm->outputBuffer[0].pvBuffer); + ntlm->outputBuffer[0].pvBuffer = NULL; + } + + free(ntlm->identity.User); + free(ntlm->identity.Domain); + free(ntlm->identity.Password); + free(ntlm->ServicePrincipalName); + + if (ntlm->table) + { + ntlm->table->FreeCredentialsHandle(&ntlm->credentials); + ntlm->table->FreeContextBuffer(ntlm->pPackageInfo); + ntlm->table->DeleteSecurityContext(&ntlm->context); + } +} + +/** + * SSPI Client Ceremony + * + * -------------- + * ( Client Begin ) + * -------------- + * | + * | + * \|/ + * -----------+-------------- + * | AcquireCredentialsHandle | + * -------------------------- + * | + * | + * \|/ + * -------------+-------------- + * +---------------> / InitializeSecurityContext / + * | ---------------------------- + * | | + * | | + * | \|/ + * --------------------------- ---------+------------- ---------------------- + * / Receive blob from server / < Received security blob? > --Yes-> / Send blob to server / + * -------------+------------- ----------------------- ---------------------- + * /|\ | | + * | No | + * Yes \|/ | + * | ------------+----------- | + * +---------------- < Received Continue Needed > <-----------------+ + * ------------------------ + * | + * No + * \|/ + * ------+------- + * ( Client End ) + * -------------- + */ + +static int test_ntlm_client_authenticate(TEST_NTLM_CLIENT* ntlm) +{ + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + + WINPR_ASSERT(ntlm); + if (ntlm->outputBuffer[0].pvBuffer) + { + free(ntlm->outputBuffer[0].pvBuffer); + ntlm->outputBuffer[0].pvBuffer = NULL; + } + + ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION; + ntlm->outputBufferDesc.cBuffers = 1; + ntlm->outputBufferDesc.pBuffers = ntlm->outputBuffer; + ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN; + ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken; + ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer); + + if (!ntlm->outputBuffer[0].pvBuffer) + return -1; + + if (ntlm->haveInputBuffer) + { + ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION; + ntlm->inputBufferDesc.cBuffers = 1; + ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer; + ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN; + } + + if ((!ntlm) || (!ntlm->table)) + { + fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n"); + return -1; + } + + status = ntlm->table->InitializeSecurityContext( + &ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : NULL, + (ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : NULL, ntlm->fContextReq, 0, + SECURITY_NATIVE_DREP, (ntlm->haveInputBuffer) ? &ntlm->inputBufferDesc : NULL, 0, + &ntlm->context, &ntlm->outputBufferDesc, &ntlm->pfContextAttr, &ntlm->expiration); + + if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED)) + { + if (ntlm->table->CompleteAuthToken) + ntlm->table->CompleteAuthToken(&ntlm->context, &ntlm->outputBufferDesc); + + if (status == SEC_I_COMPLETE_NEEDED) + status = SEC_E_OK; + else if (status == SEC_I_COMPLETE_AND_CONTINUE) + status = SEC_I_CONTINUE_NEEDED; + } + + if (ntlm->haveInputBuffer) + { + free(ntlm->inputBuffer[0].pvBuffer); + } + + ntlm->haveInputBuffer = TRUE; + ntlm->haveContext = TRUE; + return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0; +} + +static TEST_NTLM_CLIENT* test_ntlm_client_new(void) +{ + TEST_NTLM_CLIENT* ntlm = (TEST_NTLM_CLIENT*)calloc(1, sizeof(TEST_NTLM_CLIENT)); + + if (!ntlm) + return NULL; + + return ntlm; +} + +static void test_ntlm_client_free(TEST_NTLM_CLIENT* ntlm) +{ + if (!ntlm) + return; + + test_ntlm_client_uninit(ntlm); + free(ntlm); +} + +typedef struct +{ + CtxtHandle context; + ULONG cbMaxToken; + ULONG fContextReq; + ULONG pfContextAttr; + TimeStamp expiration; + PSecBuffer pBuffer; + SecBuffer inputBuffer[2]; + SecBuffer outputBuffer[2]; + BOOL haveContext; + BOOL haveInputBuffer; + BOOL UseNtlmV2Hash; + LPTSTR ServicePrincipalName; + SecBufferDesc inputBufferDesc; + SecBufferDesc outputBufferDesc; + CredHandle credentials; + BOOL confidentiality; + SecPkgInfo* pPackageInfo; + SecurityFunctionTable* table; + SEC_WINNT_AUTH_IDENTITY identity; +} TEST_NTLM_SERVER; + +static int test_ntlm_server_init(TEST_NTLM_SERVER* ntlm) +{ + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + + WINPR_ASSERT(ntlm); + + ntlm->UseNtlmV2Hash = TRUE; + SecInvalidateHandle(&(ntlm->context)); + ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE); + status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo); + + if (status != SEC_E_OK) + { + fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; + } + + ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken; + status = ntlm->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME, SECPKG_CRED_INBOUND, + NULL, NULL, NULL, NULL, &ntlm->credentials, + &ntlm->expiration); + + if (status != SEC_E_OK) + { + fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; + } + + ntlm->haveContext = FALSE; + ntlm->haveInputBuffer = FALSE; + ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer)); + ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer)); + ntlm->fContextReq = 0; + /* NLA authentication flags */ + ntlm->fContextReq |= ASC_REQ_MUTUAL_AUTH; + ntlm->fContextReq |= ASC_REQ_CONFIDENTIALITY; + ntlm->fContextReq |= ASC_REQ_CONNECTION; + ntlm->fContextReq |= ASC_REQ_USE_SESSION_KEY; + ntlm->fContextReq |= ASC_REQ_REPLAY_DETECT; + ntlm->fContextReq |= ASC_REQ_SEQUENCE_DETECT; + ntlm->fContextReq |= ASC_REQ_EXTENDED_ERROR; + return 1; +} + +static void test_ntlm_server_uninit(TEST_NTLM_SERVER* ntlm) +{ + if (!ntlm) + return; + + if (ntlm->outputBuffer[0].pvBuffer) + { + free(ntlm->outputBuffer[0].pvBuffer); + ntlm->outputBuffer[0].pvBuffer = NULL; + } + + free(ntlm->identity.User); + free(ntlm->identity.Domain); + free(ntlm->identity.Password); + free(ntlm->ServicePrincipalName); + + if (ntlm->table) + { + ntlm->table->FreeCredentialsHandle(&ntlm->credentials); + ntlm->table->FreeContextBuffer(ntlm->pPackageInfo); + ntlm->table->DeleteSecurityContext(&ntlm->context); + } +} + +static int test_ntlm_server_authenticate(TEST_NTLM_SERVER* ntlm) +{ + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + + WINPR_ASSERT(ntlm); + + ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION; + ntlm->inputBufferDesc.cBuffers = 1; + ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer; + ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN; + ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION; + ntlm->outputBufferDesc.cBuffers = 1; + ntlm->outputBufferDesc.pBuffers = &ntlm->outputBuffer[0]; + ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN; + ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken; + ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer); + BOOL hash_set = FALSE; + + if (!ntlm->outputBuffer[0].pvBuffer) + return -1; + + status = ntlm->table->AcceptSecurityContext( + &ntlm->credentials, ntlm->haveContext ? &ntlm->context : NULL, &ntlm->inputBufferDesc, + ntlm->fContextReq, SECURITY_NATIVE_DREP, &ntlm->context, &ntlm->outputBufferDesc, + &ntlm->pfContextAttr, &ntlm->expiration); + + if (!hash_set && status == SEC_I_CONTINUE_NEEDED) + { + SecPkgContext_AuthNtlmHash AuthNtlmHash = { 0 }; + + if (ntlm->UseNtlmV2Hash) + { + AuthNtlmHash.Version = 2; + CopyMemory(AuthNtlmHash.NtlmHash, TEST_NTLM_V2_HASH, 16); + } + else + { + AuthNtlmHash.Version = 1; + CopyMemory(AuthNtlmHash.NtlmHash, TEST_NTLM_HASH, 16); + } + + status = + ntlm->table->SetContextAttributes(&ntlm->context, SECPKG_ATTR_AUTH_NTLM_HASH, + &AuthNtlmHash, sizeof(SecPkgContext_AuthNtlmHash)); + + hash_set = TRUE; + } + + if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED)) + { + fprintf(stderr, "AcceptSecurityContext status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; /* Access Denied */ + } + + ntlm->haveContext = TRUE; + return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0; +} + +static TEST_NTLM_SERVER* test_ntlm_server_new(void) +{ + TEST_NTLM_SERVER* ntlm = (TEST_NTLM_SERVER*)calloc(1, sizeof(TEST_NTLM_SERVER)); + + if (!ntlm) + return NULL; + + return ntlm; +} + +static void test_ntlm_server_free(TEST_NTLM_SERVER* ntlm) +{ + if (!ntlm) + return; + + test_ntlm_server_uninit(ntlm); + free(ntlm); +} + +static BOOL test_default(void) +{ + int status = 0; + BOOL rc = FALSE; + PSecBuffer pSecBuffer = NULL; + TEST_NTLM_CLIENT* client = NULL; + TEST_NTLM_SERVER* server = NULL; + BOOL DynamicTest = TRUE; + + /** + * Client Initialization + */ + client = test_ntlm_client_new(); + + if (!client) + { + printf("Memory allocation failed"); + goto fail; + } + + status = test_ntlm_client_init(client, TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD); + + if (status < 0) + { + printf("test_ntlm_client_init failure\n"); + goto fail; + } + + /** + * Server Initialization + */ + server = test_ntlm_server_new(); + + if (!server) + { + printf("Memory allocation failed\n"); + goto fail; + } + + status = test_ntlm_server_init(server); + + if (status < 0) + { + printf("test_ntlm_server_init failure\n"); + goto fail; + } + + /** + * Client -> Negotiate Message + */ + status = test_ntlm_client_authenticate(client); + + if (status < 0) + { + printf("test_ntlm_client_authenticate failure\n"); + goto fail; + } + + if (!DynamicTest) + { + SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp; + SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge; + SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge; + CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8); + AuthNtlmTimestamp.ChallengeOrResponse = TRUE; + client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + AuthNtlmTimestamp.ChallengeOrResponse = FALSE; + client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8); + CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8); + client->table->SetContextAttributes( + &client->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge, + sizeof(SecPkgContext_AuthNtlmClientChallenge)); + client->table->SetContextAttributes( + &client->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge, + sizeof(SecPkgContext_AuthNtlmServerChallenge)); + } + + pSecBuffer = &(client->outputBuffer[0]); + + if (!DynamicTest) + { + pSecBuffer->cbBuffer = sizeof(TEST_NTLM_NEGOTIATE) - 1; + pSecBuffer->pvBuffer = (void*)malloc(pSecBuffer->cbBuffer); + + if (!pSecBuffer->pvBuffer) + { + printf("Memory allocation failed\n"); + goto fail; + } + + CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_NEGOTIATE, pSecBuffer->cbBuffer); + } + + fprintf(stderr, "NTLM_NEGOTIATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer); + winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer); + /** + * Server <- Negotiate Message + * Server -> Challenge Message + */ + server->haveInputBuffer = TRUE; + server->inputBuffer[0].BufferType = SECBUFFER_TOKEN; + server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer; + server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer; + status = test_ntlm_server_authenticate(server); + + if (status < 0) + { + printf("test_ntlm_server_authenticate failure\n"); + goto fail; + } + + if (!DynamicTest) + { + SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp; + SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge; + SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge; + CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8); + AuthNtlmTimestamp.ChallengeOrResponse = TRUE; + client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + AuthNtlmTimestamp.ChallengeOrResponse = FALSE; + client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8); + CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8); + server->table->SetContextAttributes( + &server->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge, + sizeof(SecPkgContext_AuthNtlmClientChallenge)); + server->table->SetContextAttributes( + &server->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge, + sizeof(SecPkgContext_AuthNtlmServerChallenge)); + } + + pSecBuffer = &(server->outputBuffer[0]); + + if (!DynamicTest) + { + SecPkgContext_AuthNtlmMessage AuthNtlmMessage = { 0 }; + pSecBuffer->cbBuffer = sizeof(TEST_NTLM_CHALLENGE) - 1; + pSecBuffer->pvBuffer = (void*)malloc(pSecBuffer->cbBuffer); + + if (!pSecBuffer->pvBuffer) + { + printf("Memory allocation failed\n"); + goto fail; + } + + CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_CHALLENGE, pSecBuffer->cbBuffer); + AuthNtlmMessage.type = 2; + AuthNtlmMessage.length = pSecBuffer->cbBuffer; + AuthNtlmMessage.buffer = (BYTE*)pSecBuffer->pvBuffer; + server->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_MESSAGE, + &AuthNtlmMessage, + sizeof(SecPkgContext_AuthNtlmMessage)); + } + + fprintf(stderr, "NTLM_CHALLENGE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer); + winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer); + /** + * Client <- Challenge Message + * Client -> Authenticate Message + */ + client->haveInputBuffer = TRUE; + client->inputBuffer[0].BufferType = SECBUFFER_TOKEN; + client->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer; + client->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer; + status = test_ntlm_client_authenticate(client); + + if (status < 0) + { + printf("test_ntlm_client_authenticate failure\n"); + goto fail; + } + + pSecBuffer = &(client->outputBuffer[0]); + + if (!DynamicTest) + { + pSecBuffer->cbBuffer = sizeof(TEST_NTLM_AUTHENTICATE) - 1; + pSecBuffer->pvBuffer = (void*)malloc(pSecBuffer->cbBuffer); + + if (!pSecBuffer->pvBuffer) + { + printf("Memory allocation failed\n"); + goto fail; + } + + CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_AUTHENTICATE, pSecBuffer->cbBuffer); + } + + fprintf(stderr, "NTLM_AUTHENTICATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer); + winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer); + /** + * Server <- Authenticate Message + */ + server->haveInputBuffer = TRUE; + server->inputBuffer[0].BufferType = SECBUFFER_TOKEN; + server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer; + server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer; + status = test_ntlm_server_authenticate(server); + + if (status < 0) + { + printf("test_ntlm_server_authenticate failure\n"); + goto fail; + } + + rc = TRUE; + +fail: + /** + * Cleanup & Termination + */ + test_ntlm_client_free(client); + test_ntlm_server_free(server); + return rc; +} + +int TestNTLM(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_default()) + return -1; + return 0; +} diff --git a/winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c b/winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c new file mode 100644 index 0000000..5d1ca00 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c @@ -0,0 +1,34 @@ + +#include +#include +#include +#include + +int TestQuerySecurityPackageInfo(int argc, char* argv[]) +{ + int rc = 0; + SECURITY_STATUS status = 0; + SecPkgInfo* pPackageInfo = NULL; + SecurityFunctionTable* table = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + sspi_GlobalInit(); + table = InitSecurityInterfaceEx(0); + + status = table->QuerySecurityPackageInfo(NTLM_SSP_NAME, &pPackageInfo); + + if (status != SEC_E_OK) + rc = -1; + else + { + _tprintf(_T("\nQuerySecurityPackageInfo:\n")); + _tprintf(_T("\"%s\", \"%s\"\n"), pPackageInfo->Name, pPackageInfo->Comment); + rc = 0; + } + + table->FreeContextBuffer(pPackageInfo); + sspi_GlobalFinish(); + return rc; +} diff --git a/winpr/libwinpr/sspi/test/TestSchannel.c b/winpr/libwinpr/sspi/test/TestSchannel.c new file mode 100644 index 0000000..3f9751a --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestSchannel.c @@ -0,0 +1,854 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static BOOL g_ClientWait = FALSE; +static BOOL g_ServerWait = FALSE; + +static HANDLE g_ClientReadPipe = NULL; +static HANDLE g_ClientWritePipe = NULL; +static HANDLE g_ServerReadPipe = NULL; +static HANDLE g_ServerWritePipe = NULL; + +static const BYTE test_localhost_crt[1029] = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, + 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x43, + 0x79, 0x6A, 0x43, 0x43, 0x41, 0x62, 0x4B, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x45, + 0x63, 0x61, 0x64, 0x63, 0x72, 0x7A, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47, + 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x55, 0x46, 0x41, 0x44, 0x41, 0x55, 0x4D, 0x52, 0x49, 0x77, + 0x45, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x45, 0x77, 0x6C, 0x73, 0x0A, 0x62, 0x32, 0x4E, + 0x68, 0x62, 0x47, 0x68, 0x76, 0x63, 0x33, 0x51, 0x77, 0x48, 0x68, 0x63, 0x4E, 0x4D, 0x54, 0x4D, + 0x78, 0x4D, 0x44, 0x45, 0x78, 0x4D, 0x44, 0x59, 0x78, 0x4E, 0x7A, 0x55, 0x31, 0x57, 0x68, 0x63, + 0x4E, 0x4D, 0x54, 0x51, 0x78, 0x4D, 0x44, 0x45, 0x78, 0x4D, 0x44, 0x59, 0x78, 0x4E, 0x7A, 0x55, + 0x31, 0x57, 0x6A, 0x41, 0x55, 0x4D, 0x52, 0x49, 0x77, 0x45, 0x41, 0x59, 0x44, 0x0A, 0x56, 0x51, + 0x51, 0x44, 0x45, 0x77, 0x6C, 0x73, 0x62, 0x32, 0x4E, 0x68, 0x62, 0x47, 0x68, 0x76, 0x63, 0x33, + 0x51, 0x77, 0x67, 0x67, 0x45, 0x69, 0x4D, 0x41, 0x30, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49, + 0x62, 0x33, 0x44, 0x51, 0x45, 0x42, 0x41, 0x51, 0x55, 0x41, 0x41, 0x34, 0x49, 0x42, 0x44, 0x77, + 0x41, 0x77, 0x67, 0x67, 0x45, 0x4B, 0x41, 0x6F, 0x49, 0x42, 0x41, 0x51, 0x43, 0x33, 0x0A, 0x65, + 0x6E, 0x33, 0x68, 0x5A, 0x4F, 0x53, 0x33, 0x6B, 0x51, 0x2F, 0x55, 0x54, 0x30, 0x53, 0x45, 0x6C, + 0x30, 0x48, 0x6E, 0x50, 0x79, 0x64, 0x48, 0x75, 0x35, 0x39, 0x61, 0x69, 0x71, 0x64, 0x73, 0x64, + 0x53, 0x55, 0x74, 0x6E, 0x43, 0x41, 0x37, 0x46, 0x66, 0x74, 0x30, 0x4F, 0x36, 0x51, 0x79, 0x68, + 0x49, 0x71, 0x58, 0x7A, 0x30, 0x47, 0x32, 0x53, 0x76, 0x77, 0x4C, 0x54, 0x62, 0x79, 0x68, 0x0A, + 0x59, 0x54, 0x68, 0x31, 0x36, 0x78, 0x31, 0x72, 0x45, 0x48, 0x68, 0x31, 0x57, 0x47, 0x5A, 0x6D, + 0x36, 0x77, 0x64, 0x2B, 0x4B, 0x76, 0x38, 0x6B, 0x31, 0x6B, 0x2F, 0x36, 0x6F, 0x41, 0x2F, 0x4F, + 0x51, 0x76, 0x65, 0x61, 0x38, 0x6B, 0x63, 0x45, 0x64, 0x53, 0x72, 0x54, 0x64, 0x75, 0x71, 0x4A, + 0x33, 0x65, 0x66, 0x74, 0x48, 0x4A, 0x4A, 0x6E, 0x43, 0x4B, 0x30, 0x41, 0x62, 0x68, 0x34, 0x39, + 0x0A, 0x41, 0x47, 0x41, 0x50, 0x39, 0x79, 0x58, 0x77, 0x77, 0x59, 0x41, 0x6A, 0x51, 0x49, 0x52, + 0x6E, 0x38, 0x2B, 0x4F, 0x63, 0x63, 0x48, 0x74, 0x6F, 0x4E, 0x75, 0x75, 0x79, 0x52, 0x63, 0x6B, + 0x49, 0x50, 0x71, 0x75, 0x70, 0x78, 0x79, 0x31, 0x4A, 0x5A, 0x4B, 0x39, 0x64, 0x76, 0x76, 0x62, + 0x34, 0x79, 0x53, 0x6B, 0x49, 0x75, 0x7A, 0x62, 0x79, 0x50, 0x6F, 0x54, 0x41, 0x79, 0x61, 0x55, + 0x2B, 0x0A, 0x51, 0x72, 0x70, 0x34, 0x78, 0x67, 0x64, 0x4B, 0x46, 0x54, 0x70, 0x6B, 0x50, 0x46, + 0x34, 0x33, 0x6A, 0x32, 0x4D, 0x6D, 0x5A, 0x72, 0x46, 0x63, 0x42, 0x76, 0x79, 0x6A, 0x69, 0x35, + 0x6A, 0x4F, 0x37, 0x74, 0x66, 0x6F, 0x56, 0x61, 0x6B, 0x59, 0x47, 0x53, 0x2F, 0x4C, 0x63, 0x78, + 0x77, 0x47, 0x2B, 0x77, 0x51, 0x77, 0x63, 0x4F, 0x43, 0x54, 0x42, 0x45, 0x78, 0x2F, 0x7A, 0x31, + 0x53, 0x30, 0x0A, 0x37, 0x49, 0x2F, 0x6A, 0x62, 0x44, 0x79, 0x53, 0x4E, 0x68, 0x44, 0x35, 0x63, + 0x61, 0x63, 0x54, 0x75, 0x4E, 0x36, 0x50, 0x68, 0x33, 0x58, 0x30, 0x71, 0x70, 0x47, 0x73, 0x37, + 0x79, 0x50, 0x6B, 0x4E, 0x79, 0x69, 0x4A, 0x33, 0x57, 0x52, 0x69, 0x6C, 0x35, 0x75, 0x57, 0x73, + 0x4B, 0x65, 0x79, 0x63, 0x64, 0x71, 0x42, 0x4E, 0x72, 0x34, 0x75, 0x32, 0x62, 0x49, 0x52, 0x6E, + 0x63, 0x54, 0x51, 0x0A, 0x46, 0x72, 0x68, 0x73, 0x58, 0x39, 0x69, 0x77, 0x37, 0x35, 0x76, 0x75, + 0x53, 0x64, 0x35, 0x46, 0x39, 0x37, 0x56, 0x70, 0x41, 0x67, 0x4D, 0x42, 0x41, 0x41, 0x47, 0x6A, + 0x4A, 0x44, 0x41, 0x69, 0x4D, 0x42, 0x4D, 0x47, 0x41, 0x31, 0x55, 0x64, 0x4A, 0x51, 0x51, 0x4D, + 0x4D, 0x41, 0x6F, 0x47, 0x43, 0x43, 0x73, 0x47, 0x41, 0x51, 0x55, 0x46, 0x42, 0x77, 0x4D, 0x42, + 0x4D, 0x41, 0x73, 0x47, 0x0A, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, 0x51, 0x45, 0x41, 0x77, 0x49, + 0x45, 0x4D, 0x44, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x39, 0x77, 0x30, + 0x42, 0x41, 0x51, 0x55, 0x46, 0x41, 0x41, 0x4F, 0x43, 0x41, 0x51, 0x45, 0x41, 0x49, 0x51, 0x66, + 0x75, 0x2F, 0x77, 0x39, 0x45, 0x34, 0x4C, 0x6F, 0x67, 0x30, 0x71, 0x35, 0x4B, 0x53, 0x38, 0x71, + 0x46, 0x78, 0x62, 0x36, 0x6F, 0x0A, 0x36, 0x31, 0x62, 0x35, 0x37, 0x6F, 0x6D, 0x6E, 0x46, 0x59, + 0x52, 0x34, 0x47, 0x43, 0x67, 0x33, 0x6F, 0x6A, 0x4F, 0x4C, 0x54, 0x66, 0x38, 0x7A, 0x6A, 0x4D, + 0x43, 0x52, 0x6D, 0x75, 0x59, 0x32, 0x76, 0x30, 0x4E, 0x34, 0x78, 0x66, 0x68, 0x69, 0x35, 0x4B, + 0x69, 0x59, 0x67, 0x64, 0x76, 0x4E, 0x4C, 0x4F, 0x33, 0x52, 0x42, 0x6D, 0x4E, 0x50, 0x76, 0x59, + 0x58, 0x50, 0x52, 0x46, 0x41, 0x76, 0x0A, 0x66, 0x61, 0x76, 0x66, 0x57, 0x75, 0x6C, 0x44, 0x31, + 0x64, 0x50, 0x36, 0x31, 0x69, 0x35, 0x62, 0x36, 0x59, 0x66, 0x56, 0x6C, 0x78, 0x62, 0x31, 0x61, + 0x57, 0x46, 0x37, 0x4C, 0x5A, 0x44, 0x32, 0x55, 0x6E, 0x63, 0x41, 0x6A, 0x37, 0x4E, 0x38, 0x78, + 0x38, 0x2B, 0x36, 0x58, 0x6B, 0x30, 0x6B, 0x63, 0x70, 0x58, 0x46, 0x38, 0x6C, 0x77, 0x58, 0x48, + 0x55, 0x57, 0x57, 0x55, 0x6D, 0x73, 0x2B, 0x0A, 0x4B, 0x56, 0x44, 0x34, 0x34, 0x39, 0x68, 0x6F, + 0x4D, 0x2B, 0x77, 0x4E, 0x4A, 0x49, 0x61, 0x4F, 0x52, 0x39, 0x4C, 0x46, 0x2B, 0x6B, 0x6F, 0x32, + 0x32, 0x37, 0x7A, 0x74, 0x37, 0x54, 0x41, 0x47, 0x64, 0x56, 0x35, 0x4A, 0x75, 0x7A, 0x71, 0x38, + 0x32, 0x2F, 0x6B, 0x75, 0x73, 0x6F, 0x65, 0x32, 0x69, 0x75, 0x57, 0x77, 0x54, 0x65, 0x42, 0x6C, + 0x53, 0x5A, 0x6E, 0x6B, 0x42, 0x38, 0x63, 0x64, 0x0A, 0x77, 0x4D, 0x30, 0x5A, 0x42, 0x58, 0x6D, + 0x34, 0x35, 0x48, 0x38, 0x6F, 0x79, 0x75, 0x36, 0x4A, 0x71, 0x59, 0x71, 0x45, 0x6D, 0x75, 0x4A, + 0x51, 0x64, 0x67, 0x79, 0x52, 0x2B, 0x63, 0x53, 0x53, 0x41, 0x7A, 0x2B, 0x4F, 0x32, 0x6D, 0x61, + 0x62, 0x68, 0x50, 0x5A, 0x65, 0x49, 0x76, 0x78, 0x65, 0x67, 0x6A, 0x6A, 0x61, 0x5A, 0x61, 0x46, + 0x4F, 0x71, 0x74, 0x73, 0x2B, 0x64, 0x33, 0x72, 0x39, 0x0A, 0x79, 0x71, 0x4A, 0x78, 0x67, 0x75, + 0x39, 0x43, 0x38, 0x39, 0x5A, 0x69, 0x33, 0x39, 0x57, 0x34, 0x38, 0x46, 0x66, 0x46, 0x63, 0x49, + 0x58, 0x4A, 0x4F, 0x6B, 0x39, 0x43, 0x4E, 0x46, 0x41, 0x2F, 0x69, 0x70, 0x54, 0x57, 0x6A, 0x74, + 0x74, 0x4E, 0x2F, 0x6B, 0x4F, 0x6B, 0x5A, 0x42, 0x70, 0x6F, 0x6A, 0x2F, 0x32, 0x6A, 0x4E, 0x45, + 0x62, 0x4F, 0x59, 0x7A, 0x7A, 0x6E, 0x4B, 0x77, 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x45, 0x4E, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, + 0x2D, 0x2D, 0x2D, 0x2D, 0x0A +}; + +static const BYTE test_localhost_key[1704] = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, + 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x45, + 0x76, 0x51, 0x49, 0x42, 0x41, 0x44, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47, + 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43, 0x42, 0x4B, 0x63, 0x77, + 0x67, 0x67, 0x53, 0x6A, 0x41, 0x67, 0x45, 0x41, 0x41, 0x6F, 0x49, 0x42, 0x41, 0x51, 0x43, 0x33, + 0x65, 0x6E, 0x33, 0x68, 0x5A, 0x4F, 0x53, 0x33, 0x6B, 0x51, 0x2F, 0x55, 0x0A, 0x54, 0x30, 0x53, + 0x45, 0x6C, 0x30, 0x48, 0x6E, 0x50, 0x79, 0x64, 0x48, 0x75, 0x35, 0x39, 0x61, 0x69, 0x71, 0x64, + 0x73, 0x64, 0x53, 0x55, 0x74, 0x6E, 0x43, 0x41, 0x37, 0x46, 0x66, 0x74, 0x30, 0x4F, 0x36, 0x51, + 0x79, 0x68, 0x49, 0x71, 0x58, 0x7A, 0x30, 0x47, 0x32, 0x53, 0x76, 0x77, 0x4C, 0x54, 0x62, 0x79, + 0x68, 0x59, 0x54, 0x68, 0x31, 0x36, 0x78, 0x31, 0x72, 0x45, 0x48, 0x68, 0x31, 0x0A, 0x57, 0x47, + 0x5A, 0x6D, 0x36, 0x77, 0x64, 0x2B, 0x4B, 0x76, 0x38, 0x6B, 0x31, 0x6B, 0x2F, 0x36, 0x6F, 0x41, + 0x2F, 0x4F, 0x51, 0x76, 0x65, 0x61, 0x38, 0x6B, 0x63, 0x45, 0x64, 0x53, 0x72, 0x54, 0x64, 0x75, + 0x71, 0x4A, 0x33, 0x65, 0x66, 0x74, 0x48, 0x4A, 0x4A, 0x6E, 0x43, 0x4B, 0x30, 0x41, 0x62, 0x68, + 0x34, 0x39, 0x41, 0x47, 0x41, 0x50, 0x39, 0x79, 0x58, 0x77, 0x77, 0x59, 0x41, 0x6A, 0x0A, 0x51, + 0x49, 0x52, 0x6E, 0x38, 0x2B, 0x4F, 0x63, 0x63, 0x48, 0x74, 0x6F, 0x4E, 0x75, 0x75, 0x79, 0x52, + 0x63, 0x6B, 0x49, 0x50, 0x71, 0x75, 0x70, 0x78, 0x79, 0x31, 0x4A, 0x5A, 0x4B, 0x39, 0x64, 0x76, + 0x76, 0x62, 0x34, 0x79, 0x53, 0x6B, 0x49, 0x75, 0x7A, 0x62, 0x79, 0x50, 0x6F, 0x54, 0x41, 0x79, + 0x61, 0x55, 0x2B, 0x51, 0x72, 0x70, 0x34, 0x78, 0x67, 0x64, 0x4B, 0x46, 0x54, 0x70, 0x6B, 0x0A, + 0x50, 0x46, 0x34, 0x33, 0x6A, 0x32, 0x4D, 0x6D, 0x5A, 0x72, 0x46, 0x63, 0x42, 0x76, 0x79, 0x6A, + 0x69, 0x35, 0x6A, 0x4F, 0x37, 0x74, 0x66, 0x6F, 0x56, 0x61, 0x6B, 0x59, 0x47, 0x53, 0x2F, 0x4C, + 0x63, 0x78, 0x77, 0x47, 0x2B, 0x77, 0x51, 0x77, 0x63, 0x4F, 0x43, 0x54, 0x42, 0x45, 0x78, 0x2F, + 0x7A, 0x31, 0x53, 0x30, 0x37, 0x49, 0x2F, 0x6A, 0x62, 0x44, 0x79, 0x53, 0x4E, 0x68, 0x44, 0x35, + 0x0A, 0x63, 0x61, 0x63, 0x54, 0x75, 0x4E, 0x36, 0x50, 0x68, 0x33, 0x58, 0x30, 0x71, 0x70, 0x47, + 0x73, 0x37, 0x79, 0x50, 0x6B, 0x4E, 0x79, 0x69, 0x4A, 0x33, 0x57, 0x52, 0x69, 0x6C, 0x35, 0x75, + 0x57, 0x73, 0x4B, 0x65, 0x79, 0x63, 0x64, 0x71, 0x42, 0x4E, 0x72, 0x34, 0x75, 0x32, 0x62, 0x49, + 0x52, 0x6E, 0x63, 0x54, 0x51, 0x46, 0x72, 0x68, 0x73, 0x58, 0x39, 0x69, 0x77, 0x37, 0x35, 0x76, + 0x75, 0x0A, 0x53, 0x64, 0x35, 0x46, 0x39, 0x37, 0x56, 0x70, 0x41, 0x67, 0x4D, 0x42, 0x41, 0x41, + 0x45, 0x43, 0x67, 0x67, 0x45, 0x41, 0x42, 0x36, 0x6A, 0x6C, 0x65, 0x48, 0x4E, 0x74, 0x32, 0x50, + 0x77, 0x46, 0x58, 0x53, 0x65, 0x79, 0x42, 0x4A, 0x63, 0x4C, 0x2B, 0x55, 0x74, 0x35, 0x71, 0x46, + 0x54, 0x38, 0x34, 0x68, 0x72, 0x48, 0x77, 0x6F, 0x39, 0x68, 0x62, 0x66, 0x59, 0x47, 0x6F, 0x6E, + 0x44, 0x59, 0x0A, 0x66, 0x70, 0x47, 0x2B, 0x32, 0x52, 0x30, 0x50, 0x62, 0x43, 0x63, 0x4B, 0x35, + 0x30, 0x46, 0x61, 0x4A, 0x46, 0x36, 0x71, 0x63, 0x56, 0x4A, 0x4E, 0x75, 0x52, 0x36, 0x48, 0x71, + 0x2B, 0x43, 0x55, 0x4A, 0x74, 0x48, 0x35, 0x39, 0x48, 0x48, 0x37, 0x62, 0x68, 0x6A, 0x39, 0x62, + 0x64, 0x78, 0x45, 0x6D, 0x6F, 0x48, 0x30, 0x4A, 0x76, 0x68, 0x45, 0x76, 0x67, 0x4D, 0x2F, 0x55, + 0x38, 0x42, 0x51, 0x0A, 0x65, 0x57, 0x4F, 0x4E, 0x68, 0x78, 0x50, 0x73, 0x69, 0x73, 0x6D, 0x57, + 0x6B, 0x78, 0x61, 0x5A, 0x6F, 0x6C, 0x72, 0x32, 0x69, 0x44, 0x56, 0x72, 0x7A, 0x54, 0x37, 0x55, + 0x4A, 0x71, 0x6A, 0x74, 0x59, 0x49, 0x74, 0x67, 0x2B, 0x37, 0x59, 0x43, 0x32, 0x70, 0x55, 0x58, + 0x6B, 0x64, 0x49, 0x35, 0x4A, 0x4D, 0x67, 0x6C, 0x44, 0x47, 0x4D, 0x52, 0x5A, 0x35, 0x55, 0x5A, + 0x48, 0x75, 0x63, 0x7A, 0x0A, 0x41, 0x56, 0x2B, 0x71, 0x77, 0x77, 0x33, 0x65, 0x45, 0x52, 0x74, + 0x78, 0x44, 0x50, 0x61, 0x61, 0x61, 0x34, 0x54, 0x39, 0x50, 0x64, 0x33, 0x44, 0x31, 0x6D, 0x62, + 0x71, 0x58, 0x66, 0x75, 0x45, 0x68, 0x42, 0x6D, 0x33, 0x51, 0x6F, 0x2B, 0x75, 0x7A, 0x51, 0x32, + 0x36, 0x76, 0x73, 0x66, 0x48, 0x75, 0x56, 0x76, 0x61, 0x39, 0x38, 0x32, 0x4F, 0x6A, 0x41, 0x55, + 0x6A, 0x6E, 0x64, 0x30, 0x70, 0x0A, 0x77, 0x43, 0x53, 0x6E, 0x42, 0x49, 0x48, 0x67, 0x70, 0x73, + 0x30, 0x79, 0x61, 0x45, 0x50, 0x63, 0x37, 0x46, 0x78, 0x39, 0x71, 0x45, 0x63, 0x6D, 0x33, 0x70, + 0x7A, 0x41, 0x56, 0x31, 0x69, 0x72, 0x31, 0x4E, 0x4E, 0x63, 0x51, 0x47, 0x55, 0x45, 0x75, 0x45, + 0x6C, 0x4A, 0x78, 0x76, 0x2B, 0x69, 0x57, 0x34, 0x6D, 0x35, 0x70, 0x7A, 0x4C, 0x6A, 0x64, 0x53, + 0x63, 0x49, 0x30, 0x59, 0x45, 0x73, 0x0A, 0x4D, 0x61, 0x33, 0x78, 0x32, 0x79, 0x48, 0x74, 0x6E, + 0x77, 0x79, 0x65, 0x4C, 0x4D, 0x54, 0x4B, 0x6C, 0x72, 0x46, 0x4B, 0x70, 0x55, 0x4E, 0x4A, 0x62, + 0x78, 0x73, 0x35, 0x32, 0x62, 0x5A, 0x4B, 0x71, 0x49, 0x56, 0x33, 0x33, 0x4A, 0x53, 0x34, 0x41, + 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x73, 0x4C, 0x54, 0x49, 0x68, 0x35, 0x59, 0x38, 0x4C, 0x2F, + 0x48, 0x33, 0x64, 0x74, 0x68, 0x63, 0x62, 0x0A, 0x53, 0x43, 0x45, 0x77, 0x32, 0x64, 0x42, 0x49, + 0x76, 0x49, 0x79, 0x54, 0x7A, 0x39, 0x53, 0x72, 0x62, 0x33, 0x58, 0x37, 0x37, 0x41, 0x77, 0x57, + 0x45, 0x4C, 0x53, 0x4D, 0x49, 0x57, 0x53, 0x50, 0x55, 0x43, 0x4B, 0x54, 0x49, 0x70, 0x6A, 0x4D, + 0x73, 0x6E, 0x7A, 0x6B, 0x46, 0x67, 0x32, 0x32, 0x59, 0x32, 0x53, 0x75, 0x47, 0x38, 0x4C, 0x72, + 0x50, 0x6D, 0x76, 0x73, 0x46, 0x4A, 0x34, 0x30, 0x0A, 0x32, 0x67, 0x35, 0x44, 0x55, 0x6C, 0x59, + 0x33, 0x59, 0x6D, 0x53, 0x4F, 0x46, 0x61, 0x45, 0x4A, 0x54, 0x70, 0x55, 0x47, 0x44, 0x4D, 0x79, + 0x65, 0x33, 0x74, 0x36, 0x4F, 0x30, 0x6C, 0x63, 0x51, 0x41, 0x66, 0x79, 0x6D, 0x58, 0x66, 0x41, + 0x38, 0x74, 0x50, 0x42, 0x48, 0x6A, 0x5A, 0x78, 0x56, 0x61, 0x38, 0x78, 0x78, 0x52, 0x5A, 0x6E, + 0x56, 0x43, 0x31, 0x41, 0x62, 0x75, 0x49, 0x49, 0x52, 0x0A, 0x6E, 0x77, 0x72, 0x4E, 0x46, 0x2B, + 0x42, 0x6F, 0x53, 0x4B, 0x55, 0x41, 0x73, 0x78, 0x2B, 0x46, 0x75, 0x35, 0x5A, 0x4A, 0x4B, 0x4F, + 0x66, 0x79, 0x4D, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x47, 0x34, 0x50, 0x52, 0x39, 0x2F, 0x58, + 0x58, 0x6B, 0x51, 0x54, 0x36, 0x6B, 0x7A, 0x4B, 0x64, 0x34, 0x50, 0x6C, 0x50, 0x4D, 0x63, 0x2B, + 0x4B, 0x51, 0x79, 0x4C, 0x45, 0x6C, 0x4B, 0x39, 0x71, 0x47, 0x0A, 0x41, 0x6D, 0x6E, 0x2F, 0x31, + 0x68, 0x64, 0x69, 0x57, 0x57, 0x4F, 0x52, 0x57, 0x46, 0x62, 0x32, 0x38, 0x30, 0x4D, 0x77, 0x76, + 0x77, 0x41, 0x64, 0x78, 0x72, 0x66, 0x65, 0x4C, 0x57, 0x4D, 0x57, 0x32, 0x66, 0x76, 0x4C, 0x59, + 0x4B, 0x66, 0x6C, 0x4F, 0x35, 0x50, 0x51, 0x44, 0x59, 0x67, 0x4B, 0x4A, 0x78, 0x35, 0x79, 0x50, + 0x37, 0x52, 0x64, 0x38, 0x2F, 0x64, 0x50, 0x79, 0x5A, 0x59, 0x36, 0x0A, 0x7A, 0x56, 0x37, 0x47, + 0x47, 0x6B, 0x51, 0x5A, 0x42, 0x4B, 0x36, 0x79, 0x74, 0x61, 0x66, 0x32, 0x35, 0x44, 0x50, 0x67, + 0x50, 0x72, 0x32, 0x77, 0x73, 0x59, 0x4D, 0x43, 0x6C, 0x53, 0x74, 0x6C, 0x56, 0x74, 0x72, 0x6D, + 0x4F, 0x78, 0x59, 0x55, 0x56, 0x77, 0x42, 0x59, 0x4F, 0x69, 0x36, 0x45, 0x62, 0x50, 0x69, 0x6B, + 0x78, 0x47, 0x48, 0x5A, 0x70, 0x59, 0x6F, 0x5A, 0x5A, 0x70, 0x68, 0x4A, 0x0A, 0x4E, 0x61, 0x38, + 0x4F, 0x4C, 0x31, 0x69, 0x77, 0x75, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x42, 0x55, 0x55, 0x31, + 0x54, 0x79, 0x5A, 0x2B, 0x4A, 0x5A, 0x43, 0x64, 0x79, 0x72, 0x33, 0x58, 0x43, 0x63, 0x77, 0x77, + 0x58, 0x2F, 0x48, 0x49, 0x73, 0x31, 0x34, 0x6B, 0x4B, 0x42, 0x48, 0x68, 0x44, 0x79, 0x33, 0x78, + 0x37, 0x74, 0x50, 0x38, 0x2F, 0x6F, 0x48, 0x54, 0x6F, 0x72, 0x76, 0x79, 0x74, 0x0A, 0x41, 0x68, + 0x38, 0x4B, 0x36, 0x4B, 0x72, 0x43, 0x41, 0x75, 0x65, 0x50, 0x6D, 0x79, 0x32, 0x6D, 0x4F, 0x54, + 0x31, 0x54, 0x39, 0x6F, 0x31, 0x61, 0x47, 0x55, 0x49, 0x6C, 0x66, 0x38, 0x72, 0x76, 0x33, 0x2F, + 0x30, 0x45, 0x78, 0x67, 0x53, 0x6B, 0x57, 0x50, 0x6D, 0x4F, 0x41, 0x38, 0x35, 0x49, 0x32, 0x2F, + 0x58, 0x48, 0x65, 0x66, 0x71, 0x54, 0x6F, 0x45, 0x48, 0x30, 0x44, 0x65, 0x41, 0x4E, 0x0A, 0x7A, + 0x6C, 0x4B, 0x4C, 0x71, 0x79, 0x44, 0x56, 0x30, 0x42, 0x56, 0x4E, 0x76, 0x48, 0x42, 0x57, 0x79, + 0x32, 0x49, 0x51, 0x35, 0x62, 0x50, 0x42, 0x57, 0x76, 0x30, 0x37, 0x63, 0x34, 0x2B, 0x6A, 0x39, + 0x4E, 0x62, 0x57, 0x67, 0x64, 0x44, 0x43, 0x43, 0x35, 0x52, 0x6B, 0x4F, 0x6A, 0x70, 0x33, 0x4D, + 0x4E, 0x45, 0x58, 0x47, 0x56, 0x43, 0x69, 0x51, 0x51, 0x4B, 0x42, 0x67, 0x43, 0x7A, 0x4D, 0x0A, + 0x77, 0x65, 0x61, 0x62, 0x73, 0x50, 0x48, 0x68, 0x44, 0x4B, 0x5A, 0x38, 0x2F, 0x34, 0x43, 0x6A, + 0x73, 0x61, 0x62, 0x4E, 0x75, 0x41, 0x7A, 0x62, 0x57, 0x4B, 0x52, 0x42, 0x38, 0x37, 0x44, 0x61, + 0x58, 0x46, 0x78, 0x6F, 0x4D, 0x73, 0x35, 0x52, 0x79, 0x6F, 0x38, 0x55, 0x4D, 0x6B, 0x72, 0x67, + 0x30, 0x35, 0x4C, 0x6F, 0x67, 0x37, 0x4D, 0x78, 0x62, 0x33, 0x76, 0x61, 0x42, 0x34, 0x63, 0x2F, + 0x0A, 0x52, 0x57, 0x77, 0x7A, 0x38, 0x72, 0x34, 0x39, 0x70, 0x48, 0x64, 0x71, 0x68, 0x4F, 0x6D, + 0x63, 0x6C, 0x45, 0x77, 0x79, 0x4D, 0x34, 0x51, 0x79, 0x6A, 0x39, 0x52, 0x6D, 0x57, 0x62, 0x51, + 0x58, 0x54, 0x54, 0x45, 0x63, 0x2B, 0x35, 0x67, 0x54, 0x4B, 0x50, 0x4E, 0x53, 0x33, 0x6D, 0x70, + 0x4D, 0x54, 0x36, 0x39, 0x46, 0x45, 0x74, 0x2F, 0x35, 0x72, 0x4D, 0x52, 0x70, 0x4B, 0x2B, 0x52, + 0x68, 0x0A, 0x49, 0x32, 0x42, 0x58, 0x6B, 0x51, 0x71, 0x31, 0x36, 0x6E, 0x72, 0x31, 0x61, 0x45, + 0x4D, 0x6D, 0x64, 0x51, 0x42, 0x51, 0x79, 0x4B, 0x59, 0x4A, 0x6C, 0x30, 0x6C, 0x50, 0x68, 0x69, + 0x42, 0x2F, 0x75, 0x6C, 0x5A, 0x63, 0x72, 0x67, 0x4C, 0x70, 0x41, 0x6F, 0x47, 0x41, 0x65, 0x30, + 0x65, 0x74, 0x50, 0x4A, 0x77, 0x6D, 0x51, 0x46, 0x6B, 0x6A, 0x4D, 0x70, 0x66, 0x4D, 0x44, 0x61, + 0x4E, 0x34, 0x0A, 0x70, 0x7A, 0x71, 0x45, 0x51, 0x72, 0x52, 0x35, 0x4B, 0x35, 0x4D, 0x6E, 0x54, + 0x48, 0x76, 0x47, 0x67, 0x2F, 0x70, 0x6A, 0x57, 0x6A, 0x43, 0x57, 0x58, 0x56, 0x48, 0x67, 0x35, + 0x76, 0x36, 0x46, 0x6F, 0x5A, 0x48, 0x35, 0x6E, 0x59, 0x2B, 0x56, 0x2F, 0x57, 0x75, 0x57, 0x38, + 0x38, 0x6A, 0x6C, 0x4B, 0x53, 0x50, 0x6C, 0x77, 0x6A, 0x50, 0x7A, 0x41, 0x67, 0x7A, 0x47, 0x33, + 0x45, 0x41, 0x55, 0x0A, 0x71, 0x57, 0x6B, 0x42, 0x67, 0x30, 0x71, 0x75, 0x50, 0x4D, 0x72, 0x54, + 0x6B, 0x73, 0x69, 0x6E, 0x58, 0x50, 0x2B, 0x58, 0x6B, 0x51, 0x65, 0x46, 0x66, 0x58, 0x61, 0x33, + 0x38, 0x6A, 0x72, 0x70, 0x62, 0x4B, 0x46, 0x4F, 0x72, 0x7A, 0x49, 0x6F, 0x6A, 0x69, 0x65, 0x6C, + 0x4B, 0x55, 0x4D, 0x50, 0x4D, 0x78, 0x2F, 0x78, 0x70, 0x53, 0x6A, 0x63, 0x55, 0x42, 0x68, 0x62, + 0x4E, 0x34, 0x45, 0x54, 0x0A, 0x4F, 0x30, 0x66, 0x63, 0x57, 0x47, 0x6F, 0x61, 0x56, 0x50, 0x72, + 0x63, 0x6E, 0x38, 0x62, 0x58, 0x4D, 0x54, 0x45, 0x4E, 0x53, 0x31, 0x41, 0x3D, 0x0A, 0x2D, 0x2D, + 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, + 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A +}; + +static const BYTE test_DummyMessage[64] = { + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD +}; + +static const BYTE test_LastDummyMessage[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int schannel_send(PSecurityFunctionTable table, HANDLE hPipe, PCtxtHandle phContext, + BYTE* buffer, UINT32 length) +{ + BYTE* ioBuffer; + UINT32 ioBufferLength; + BYTE* pMessageBuffer; + SecBuffer Buffers[4] = { 0 }; + SecBufferDesc Message; + SECURITY_STATUS status; + DWORD NumberOfBytesWritten; + SecPkgContext_StreamSizes StreamSizes = { 0 }; + + status = table->QueryContextAttributes(phContext, SECPKG_ATTR_STREAM_SIZES, &StreamSizes); + ioBufferLength = StreamSizes.cbHeader + StreamSizes.cbMaximumMessage + StreamSizes.cbTrailer; + ioBuffer = (BYTE*)calloc(1, ioBufferLength); + if (!ioBuffer) + return -1; + pMessageBuffer = ioBuffer + StreamSizes.cbHeader; + CopyMemory(pMessageBuffer, buffer, length); + Buffers[0].pvBuffer = ioBuffer; + Buffers[0].cbBuffer = StreamSizes.cbHeader; + Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; + Buffers[1].pvBuffer = pMessageBuffer; + Buffers[1].cbBuffer = length; + Buffers[1].BufferType = SECBUFFER_DATA; + Buffers[2].pvBuffer = pMessageBuffer + length; + Buffers[2].cbBuffer = StreamSizes.cbTrailer; + Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; + Buffers[3].pvBuffer = NULL; + Buffers[3].cbBuffer = 0; + Buffers[3].BufferType = SECBUFFER_EMPTY; + Message.ulVersion = SECBUFFER_VERSION; + Message.cBuffers = 4; + Message.pBuffers = Buffers; + ioBufferLength = + Message.pBuffers[0].cbBuffer + Message.pBuffers[1].cbBuffer + Message.pBuffers[2].cbBuffer; + status = table->EncryptMessage(phContext, 0, &Message, 0); + printf("EncryptMessage status: 0x%08" PRIX32 "\n", status); + printf("EncryptMessage output: cBuffers: %" PRIu32 " [0]: %" PRIu32 " / %" PRIu32 + " [1]: %" PRIu32 " / %" PRIu32 " [2]: %" PRIu32 " / %" PRIu32 " [3]: %" PRIu32 + " / %" PRIu32 "\n", + Message.cBuffers, Message.pBuffers[0].cbBuffer, Message.pBuffers[0].BufferType, + Message.pBuffers[1].cbBuffer, Message.pBuffers[1].BufferType, + Message.pBuffers[2].cbBuffer, Message.pBuffers[2].BufferType, + Message.pBuffers[3].cbBuffer, Message.pBuffers[3].BufferType); + + if (status != SEC_E_OK) + return -1; + + printf("Client > Server (%" PRIu32 ")\n", ioBufferLength); + winpr_HexDump("sspi.test", WLOG_DEBUG, ioBuffer, ioBufferLength); + + if (!WriteFile(hPipe, ioBuffer, ioBufferLength, &NumberOfBytesWritten, NULL)) + { + printf("schannel_send: failed to write to pipe\n"); + return -1; + } + + return 0; +} + +static int schannel_recv(PSecurityFunctionTable table, HANDLE hPipe, PCtxtHandle phContext) +{ + BYTE* ioBuffer; + UINT32 ioBufferLength; + // BYTE* pMessageBuffer; + SecBuffer Buffers[4] = { 0 }; + SecBufferDesc Message; + SECURITY_STATUS status; + DWORD NumberOfBytesRead; + SecPkgContext_StreamSizes StreamSizes = { 0 }; + + status = table->QueryContextAttributes(phContext, SECPKG_ATTR_STREAM_SIZES, &StreamSizes); + ioBufferLength = StreamSizes.cbHeader + StreamSizes.cbMaximumMessage + StreamSizes.cbTrailer; + ioBuffer = (BYTE*)calloc(1, ioBufferLength); + if (!ioBuffer) + return -1; + + if (!ReadFile(hPipe, ioBuffer, ioBufferLength, &NumberOfBytesRead, NULL)) + { + printf("schannel_recv: failed to read from pipe\n"); + return -1; + } + + Buffers[0].pvBuffer = ioBuffer; + Buffers[0].cbBuffer = NumberOfBytesRead; + Buffers[0].BufferType = SECBUFFER_DATA; + Buffers[1].pvBuffer = NULL; + Buffers[1].cbBuffer = 0; + Buffers[1].BufferType = SECBUFFER_EMPTY; + Buffers[2].pvBuffer = NULL; + Buffers[2].cbBuffer = 0; + Buffers[2].BufferType = SECBUFFER_EMPTY; + Buffers[3].pvBuffer = NULL; + Buffers[3].cbBuffer = 0; + Buffers[3].BufferType = SECBUFFER_EMPTY; + Message.ulVersion = SECBUFFER_VERSION; + Message.cBuffers = 4; + Message.pBuffers = Buffers; + status = table->DecryptMessage(phContext, &Message, 0, NULL); + printf("DecryptMessage status: 0x%08" PRIX32 "\n", status); + printf("DecryptMessage output: cBuffers: %" PRIu32 " [0]: %" PRIu32 " / %" PRIu32 + " [1]: %" PRIu32 " / %" PRIu32 " [2]: %" PRIu32 " / %" PRIu32 " [3]: %" PRIu32 + " / %" PRIu32 "\n", + Message.cBuffers, Message.pBuffers[0].cbBuffer, Message.pBuffers[0].BufferType, + Message.pBuffers[1].cbBuffer, Message.pBuffers[1].BufferType, + Message.pBuffers[2].cbBuffer, Message.pBuffers[2].BufferType, + Message.pBuffers[3].cbBuffer, Message.pBuffers[3].BufferType); + + if (status != SEC_E_OK) + return -1; + + printf("Decrypted Message (%" PRIu32 ")\n", Message.pBuffers[1].cbBuffer); + winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)Message.pBuffers[1].pvBuffer, + Message.pBuffers[1].cbBuffer); + + if (memcmp(Message.pBuffers[1].pvBuffer, test_LastDummyMessage, + sizeof(test_LastDummyMessage)) == 0) + return -1; + + return 0; +} + +static DWORD WINAPI schannel_test_server_thread(LPVOID arg) +{ + BOOL extraData; + BYTE* lpTokenIn; + BYTE* lpTokenOut; + TimeStamp expiry; + UINT32 cbMaxToken; + UINT32 fContextReq; + ULONG fContextAttr; + SCHANNEL_CRED cred = { 0 }; + CtxtHandle context; + CredHandle credentials; + DWORD cchNameString; + LPTSTR pszNameString; + HCERTSTORE hCertStore; + PCCERT_CONTEXT pCertContext; + PSecBuffer pSecBuffer; + SecBuffer SecBuffer_in[2] = { 0 }; + SecBuffer SecBuffer_out[2] = { 0 }; + SecBufferDesc SecBufferDesc_in; + SecBufferDesc SecBufferDesc_out; + DWORD NumberOfBytesRead; + SECURITY_STATUS status; + PSecPkgInfo pPackageInfo; + PSecurityFunctionTable table; + DWORD NumberOfBytesWritten; + printf("Starting Server\n"); + SecInvalidateHandle(&context); + SecInvalidateHandle(&credentials); + table = InitSecurityInterface(); + status = QuerySecurityPackageInfo(SCHANNEL_NAME, &pPackageInfo); + + if (status != SEC_E_OK) + { + printf("QuerySecurityPackageInfo failure: 0x%08" PRIX32 "\n", status); + return 0; + } + + cbMaxToken = pPackageInfo->cbMaxToken; + hCertStore = CertOpenSystemStore(0, _T("MY")); + + if (!hCertStore) + { + printf("Error opening system store\n"); + // return NULL; + } + +#ifdef CERT_FIND_HAS_PRIVATE_KEY + pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, + CERT_FIND_HAS_PRIVATE_KEY, NULL, NULL); +#else + pCertContext = + CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); +#endif + + if (!pCertContext) + { + printf("Error finding certificate in store\n"); + // return NULL; + } + + cchNameString = + CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); + pszNameString = (LPTSTR)malloc(cchNameString * sizeof(TCHAR)); + if (!pszNameString) + { + printf("Memory allocation failed\n"); + return 0; + } + cchNameString = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, + pszNameString, cchNameString); + _tprintf(_T("Certificate Name: %s\n"), pszNameString); + cred.dwVersion = SCHANNEL_CRED_VERSION; + cred.cCreds = 1; + cred.paCred = &pCertContext; + cred.cSupportedAlgs = 0; + cred.palgSupportedAlgs = NULL; + cred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER; + cred.dwFlags = SCH_CRED_NO_SYSTEM_MAPPER; + status = table->AcquireCredentialsHandle(NULL, SCHANNEL_NAME, SECPKG_CRED_INBOUND, NULL, &cred, + NULL, NULL, &credentials, NULL); + + if (status != SEC_E_OK) + { + printf("AcquireCredentialsHandle failure: 0x%08" PRIX32 "\n", status); + return 0; + } + + extraData = FALSE; + g_ServerWait = TRUE; + if (!(lpTokenIn = (BYTE*)malloc(cbMaxToken))) + { + printf("Memory allocation failed\n"); + return 0; + } + if (!(lpTokenOut = (BYTE*)malloc(cbMaxToken))) + { + printf("Memory allocation failed\n"); + free(lpTokenIn); + return 0; + } + fContextReq = ASC_REQ_STREAM | ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | + ASC_REQ_CONFIDENTIALITY | ASC_REQ_EXTENDED_ERROR; + + do + { + if (!extraData) + { + if (g_ServerWait) + { + if (!ReadFile(g_ServerReadPipe, lpTokenIn, cbMaxToken, &NumberOfBytesRead, NULL)) + { + printf("Failed to read from server pipe\n"); + return NULL; + } + } + else + { + NumberOfBytesRead = 0; + } + } + + extraData = FALSE; + g_ServerWait = TRUE; + SecBuffer_in[0].BufferType = SECBUFFER_TOKEN; + SecBuffer_in[0].pvBuffer = lpTokenIn; + SecBuffer_in[0].cbBuffer = NumberOfBytesRead; + SecBuffer_in[1].BufferType = SECBUFFER_EMPTY; + SecBuffer_in[1].pvBuffer = NULL; + SecBuffer_in[1].cbBuffer = 0; + SecBufferDesc_in.ulVersion = SECBUFFER_VERSION; + SecBufferDesc_in.cBuffers = 2; + SecBufferDesc_in.pBuffers = SecBuffer_in; + SecBuffer_out[0].BufferType = SECBUFFER_TOKEN; + SecBuffer_out[0].pvBuffer = lpTokenOut; + SecBuffer_out[0].cbBuffer = cbMaxToken; + SecBufferDesc_out.ulVersion = SECBUFFER_VERSION; + SecBufferDesc_out.cBuffers = 1; + SecBufferDesc_out.pBuffers = SecBuffer_out; + status = table->AcceptSecurityContext( + &credentials, SecIsValidHandle(&context) ? &context : NULL, &SecBufferDesc_in, + fContextReq, 0, &context, &SecBufferDesc_out, &fContextAttr, &expiry); + + if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED) && + (status != SEC_E_INCOMPLETE_MESSAGE)) + { + printf("AcceptSecurityContext unexpected status: 0x%08" PRIX32 "\n", status); + return NULL; + } + + NumberOfBytesWritten = 0; + + if (status == SEC_E_OK) + printf("AcceptSecurityContext status: SEC_E_OK\n"); + else if (status == SEC_I_CONTINUE_NEEDED) + printf("AcceptSecurityContext status: SEC_I_CONTINUE_NEEDED\n"); + else if (status == SEC_E_INCOMPLETE_MESSAGE) + printf("AcceptSecurityContext status: SEC_E_INCOMPLETE_MESSAGE\n"); + + printf("Server cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32 "\n", + SecBufferDesc_out.cBuffers, SecBufferDesc_out.pBuffers[0].cbBuffer, + SecBufferDesc_out.pBuffers[0].BufferType); + printf("Server Input cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32 + " pBuffers[1]: %" PRIu32 " type: %" PRIu32 "\n", + SecBufferDesc_in.cBuffers, SecBufferDesc_in.pBuffers[0].cbBuffer, + SecBufferDesc_in.pBuffers[0].BufferType, SecBufferDesc_in.pBuffers[1].cbBuffer, + SecBufferDesc_in.pBuffers[1].BufferType); + + if (SecBufferDesc_in.pBuffers[1].BufferType == SECBUFFER_EXTRA) + { + printf("AcceptSecurityContext SECBUFFER_EXTRA\n"); + pSecBuffer = &SecBufferDesc_in.pBuffers[1]; + CopyMemory(lpTokenIn, &lpTokenIn[NumberOfBytesRead - pSecBuffer->cbBuffer], + pSecBuffer->cbBuffer); + NumberOfBytesRead = pSecBuffer->cbBuffer; + continue; + } + + if (status != SEC_E_INCOMPLETE_MESSAGE) + { + pSecBuffer = &SecBufferDesc_out.pBuffers[0]; + + if (pSecBuffer->cbBuffer > 0) + { + printf("Server > Client (%" PRIu32 ")\n", pSecBuffer->cbBuffer); + winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, + pSecBuffer->cbBuffer); + + if (!WriteFile(g_ClientWritePipe, pSecBuffer->pvBuffer, pSecBuffer->cbBuffer, + &NumberOfBytesWritten, NULL)) + { + printf("failed to write to client pipe\n"); + return NULL; + } + } + } + + if (status == SEC_E_OK) + { + printf("Server Handshake Complete\n"); + break; + } + } while (1); + + do + { + if (schannel_recv(table, g_ServerReadPipe, &context) < 0) + break; + } while (1); + + return 0; +} + +static int dump_test_certificate_files(void) +{ + FILE* fp; + char* fullpath = NULL; + int ret = -1; + + /* + * Output Certificate File + */ + fullpath = GetCombinedPath("/tmp", "localhost.crt"); + if (!fullpath) + return -1; + + fp = winpr_fopen(fullpath, "w+"); + if (fp) + { + if (fwrite((void*)test_localhost_crt, sizeof(test_localhost_crt), 1, fp) != 1) + goto out_fail; + fclose(fp); + fp = NULL; + } + free(fullpath); + + /* + * Output Private Key File + */ + fullpath = GetCombinedPath("/tmp", "localhost.key"); + if (!fullpath) + return -1; + fp = winpr_fopen(fullpath, "w+"); + if (fp && fwrite((void*)test_localhost_key, sizeof(test_localhost_key), 1, fp) != 1) + goto out_fail; + + ret = 1; +out_fail: + free(fullpath); + if (fp) + fclose(fp); + return ret; +} + +int TestSchannel(int argc, char* argv[]) +{ + int count; + ALG_ID algId; + HANDLE thread; + BYTE* lpTokenIn; + BYTE* lpTokenOut; + TimeStamp expiry; + UINT32 cbMaxToken; + SCHANNEL_CRED cred = { 0 }; + UINT32 fContextReq; + ULONG fContextAttr; + CtxtHandle context; + CredHandle credentials; + SECURITY_STATUS status; + PSecPkgInfo pPackageInfo; + PSecBuffer pSecBuffer; + PSecurityFunctionTable table; + DWORD NumberOfBytesRead; + DWORD NumberOfBytesWritten; + SecPkgCred_SupportedAlgs SupportedAlgs = { 0 }; + SecPkgCred_CipherStrengths CipherStrengths = { 0 }; + SecPkgCred_SupportedProtocols SupportedProtocols = { 0 }; + return 0; /* disable by default - causes crash */ + sspi_GlobalInit(); + dump_test_certificate_files(); + SecInvalidateHandle(&context); + SecInvalidateHandle(&credentials); + + if (!CreatePipe(&g_ClientReadPipe, &g_ClientWritePipe, NULL, 0)) + { + printf("Failed to create client pipe\n"); + return -1; + } + + if (!CreatePipe(&g_ServerReadPipe, &g_ServerWritePipe, NULL, 0)) + { + printf("Failed to create server pipe\n"); + return -1; + } + + if (!(thread = CreateThread(NULL, 0, schannel_test_server_thread, NULL, 0, NULL))) + { + printf("Failed to create server thread\n"); + return -1; + } + + table = InitSecurityInterface(); + status = QuerySecurityPackageInfo(SCHANNEL_NAME, &pPackageInfo); + + if (status != SEC_E_OK) + { + printf("QuerySecurityPackageInfo failure: 0x%08" PRIX32 "\n", status); + return -1; + } + + cbMaxToken = pPackageInfo->cbMaxToken; + cred.dwVersion = SCHANNEL_CRED_VERSION; + cred.cCreds = 0; + cred.paCred = NULL; + cred.cSupportedAlgs = 0; + cred.palgSupportedAlgs = NULL; + cred.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS; + cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS; + cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; + cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; + status = table->AcquireCredentialsHandle(NULL, SCHANNEL_NAME, SECPKG_CRED_OUTBOUND, NULL, &cred, + NULL, NULL, &credentials, NULL); + + if (status != SEC_E_OK) + { + printf("AcquireCredentialsHandle failure: 0x%08" PRIX32 "\n", status); + return -1; + } + + status = + table->QueryCredentialsAttributes(&credentials, SECPKG_ATTR_SUPPORTED_ALGS, &SupportedAlgs); + + if (status != SEC_E_OK) + { + printf("QueryCredentialsAttributes SECPKG_ATTR_SUPPORTED_ALGS failure: 0x%08" PRIX32 "\n", + status); + return -1; + } + + /** + * SupportedAlgs: 15 + * 0x660E 0x6610 0x6801 0x6603 0x6601 0x8003 0x8004 + * 0x800C 0x800D 0x800E 0x2400 0xAA02 0xAE06 0x2200 0x2203 + */ + printf("SupportedAlgs: %" PRIu32 "\n", SupportedAlgs.cSupportedAlgs); + + for (DWORD index = 0; index < SupportedAlgs.cSupportedAlgs; index++) + { + algId = SupportedAlgs.palgSupportedAlgs[index]; + printf("\t0x%08" PRIX32 " CLASS: %" PRIu32 " TYPE: %" PRIu32 " SID: %" PRIu32 "\n", algId, + ((GET_ALG_CLASS(algId)) >> 13), ((GET_ALG_TYPE(algId)) >> 9), GET_ALG_SID(algId)); + } + + printf("\n"); + status = table->QueryCredentialsAttributes(&credentials, SECPKG_ATTR_CIPHER_STRENGTHS, + &CipherStrengths); + + if (status != SEC_E_OK) + { + printf("QueryCredentialsAttributes SECPKG_ATTR_CIPHER_STRENGTHS failure: 0x%08" PRIX32 "\n", + status); + return -1; + } + + /* CipherStrengths: Minimum: 40 Maximum: 256 */ + printf("CipherStrengths: Minimum: %" PRIu32 " Maximum: %" PRIu32 "\n", + CipherStrengths.dwMinimumCipherStrength, CipherStrengths.dwMaximumCipherStrength); + status = table->QueryCredentialsAttributes(&credentials, SECPKG_ATTR_SUPPORTED_PROTOCOLS, + &SupportedProtocols); + + if (status != SEC_E_OK) + { + printf("QueryCredentialsAttributes SECPKG_ATTR_SUPPORTED_PROTOCOLS failure: 0x%08" PRIX32 + "\n", + status); + return -1; + } + + /* SupportedProtocols: 0x208A0 */ + printf("SupportedProtocols: 0x%08" PRIX32 "\n", SupportedProtocols.grbitProtocol); + fContextReq = ISC_REQ_STREAM | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | + ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY; + if (!(lpTokenIn = (BYTE*)malloc(cbMaxToken))) + { + printf("Memory allocation failed\n"); + return -1; + } + if (!(lpTokenOut = (BYTE*)malloc(cbMaxToken))) + { + printf("Memory allocation failed\n"); + return -1; + } + g_ClientWait = FALSE; + + do + { + SecBuffer SecBuffer_in[2] = { 0 }; + SecBuffer SecBuffer_out[1] = { 0 }; + SecBufferDesc SecBufferDesc_in = { 0 }; + SecBufferDesc SecBufferDesc_out = { 0 }; + if (g_ClientWait) + { + if (!ReadFile(g_ClientReadPipe, lpTokenIn, cbMaxToken, &NumberOfBytesRead, NULL)) + { + printf("failed to read from server pipe\n"); + return -1; + } + } + else + { + NumberOfBytesRead = 0; + } + + g_ClientWait = TRUE; + printf("NumberOfBytesRead: %" PRIu32 "\n", NumberOfBytesRead); + SecBuffer_in[0].BufferType = SECBUFFER_TOKEN; + SecBuffer_in[0].pvBuffer = lpTokenIn; + SecBuffer_in[0].cbBuffer = NumberOfBytesRead; + SecBuffer_in[1].pvBuffer = NULL; + SecBuffer_in[1].cbBuffer = 0; + SecBuffer_in[1].BufferType = SECBUFFER_EMPTY; + SecBufferDesc_in.ulVersion = SECBUFFER_VERSION; + SecBufferDesc_in.cBuffers = 2; + SecBufferDesc_in.pBuffers = SecBuffer_in; + SecBuffer_out[0].BufferType = SECBUFFER_TOKEN; + SecBuffer_out[0].pvBuffer = lpTokenOut; + SecBuffer_out[0].cbBuffer = cbMaxToken; + SecBufferDesc_out.ulVersion = SECBUFFER_VERSION; + SecBufferDesc_out.cBuffers = 1; + SecBufferDesc_out.pBuffers = SecBuffer_out; + status = table->InitializeSecurityContext( + &credentials, SecIsValidHandle(&context) ? &context : NULL, _T("localhost"), + fContextReq, 0, 0, &SecBufferDesc_in, 0, &context, &SecBufferDesc_out, &fContextAttr, + &expiry); + + if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED) && + (status != SEC_E_INCOMPLETE_MESSAGE)) + { + printf("InitializeSecurityContext unexpected status: 0x%08" PRIX32 "\n", status); + return -1; + } + + NumberOfBytesWritten = 0; + + if (status == SEC_E_OK) + printf("InitializeSecurityContext status: SEC_E_OK\n"); + else if (status == SEC_I_CONTINUE_NEEDED) + printf("InitializeSecurityContext status: SEC_I_CONTINUE_NEEDED\n"); + else if (status == SEC_E_INCOMPLETE_MESSAGE) + printf("InitializeSecurityContext status: SEC_E_INCOMPLETE_MESSAGE\n"); + + printf("Client Output cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32 "\n", + SecBufferDesc_out.cBuffers, SecBufferDesc_out.pBuffers[0].cbBuffer, + SecBufferDesc_out.pBuffers[0].BufferType); + printf("Client Input cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32 + " pBuffers[1]: %" PRIu32 " type: %" PRIu32 "\n", + SecBufferDesc_in.cBuffers, SecBufferDesc_in.pBuffers[0].cbBuffer, + SecBufferDesc_in.pBuffers[0].BufferType, SecBufferDesc_in.pBuffers[1].cbBuffer, + SecBufferDesc_in.pBuffers[1].BufferType); + + if (status != SEC_E_INCOMPLETE_MESSAGE) + { + pSecBuffer = &SecBufferDesc_out.pBuffers[0]; + + if (pSecBuffer->cbBuffer > 0) + { + printf("Client > Server (%" PRIu32 ")\n", pSecBuffer->cbBuffer); + winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, + pSecBuffer->cbBuffer); + + if (!WriteFile(g_ServerWritePipe, pSecBuffer->pvBuffer, pSecBuffer->cbBuffer, + &NumberOfBytesWritten, NULL)) + { + printf("failed to write to server pipe\n"); + return -1; + } + } + } + + if (status == SEC_E_OK) + { + printf("Client Handshake Complete\n"); + break; + } + } while (1); + + count = 0; + + do + { + if (schannel_send(table, g_ServerWritePipe, &context, test_DummyMessage, + sizeof(test_DummyMessage)) < 0) + break; + + for (DWORD index = 0; index < sizeof(test_DummyMessage); index++) + { + BYTE b, ln, hn; + b = test_DummyMessage[index]; + ln = (b & 0x0F); + hn = ((b & 0xF0) >> 4); + ln = (ln + 1) % 0xF; + hn = (ln + 1) % 0xF; + b = (ln | (hn << 4)); + test_DummyMessage[index] = b; + } + + Sleep(100); + count++; + } while (count < 3); + + schannel_send(table, g_ServerWritePipe, &context, test_LastDummyMessage, + sizeof(test_LastDummyMessage)); + WaitForSingleObject(thread, INFINITE); + sspi_GlobalFinish(); + return 0; +} diff --git a/winpr/libwinpr/sspicli/CMakeLists.txt b/winpr/libwinpr/sspicli/CMakeLists.txt new file mode 100644 index 0000000..f50585e --- /dev/null +++ b/winpr/libwinpr/sspicli/CMakeLists.txt @@ -0,0 +1,18 @@ +# WinPR: Windows Portable Runtime +# libwinpr-sspicli cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(sspicli.c) diff --git a/winpr/libwinpr/sspicli/ModuleOptions.cmake b/winpr/libwinpr/sspicli/ModuleOptions.cmake new file mode 100644 index 0000000..3a356c7 --- /dev/null +++ b/winpr/libwinpr/sspicli/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "sspicli") +set(MINWIN_LONG_NAME "Authentication Functions") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/sspicli/sspicli.c b/winpr/libwinpr/sspicli/sspicli.c new file mode 100644 index 0000000..f3e922c --- /dev/null +++ b/winpr/libwinpr/sspicli/sspicli.c @@ -0,0 +1,275 @@ +/** + * WinPR: Windows Portable Runtime + * Security Support Provider Interface + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +/** + * sspicli.dll: + * + * EnumerateSecurityPackagesA + * EnumerateSecurityPackagesW + * GetUserNameExW + * ImportSecurityContextA + * LogonUser + * LogonUserEx + * LogonUserExExW + * SspiCompareAuthIdentities + * SspiCopyAuthIdentity + * SspiDecryptAuthIdentity + * SspiEncodeAuthIdentityAsStrings + * SspiEncodeStringsAsAuthIdentity + * SspiEncryptAuthIdentity + * SspiExcludePackage + * SspiFreeAuthIdentity + * SspiGetTargetHostName + * SspiIsAuthIdentityEncrypted + * SspiLocalFree + * SspiMarshalAuthIdentity + * SspiPrepareForCredRead + * SspiPrepareForCredWrite + * SspiUnmarshalAuthIdentity + * SspiValidateAuthIdentity + * SspiZeroAuthIdentity + */ + +#ifndef _WIN32 + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#if defined(WINPR_HAVE_GETPWUID_R) +#include +#endif + +#include + +#include +#include + +#include "../handle/handle.h" + +#include "../security/security.h" + +static BOOL LogonUserCloseHandle(HANDLE handle); + +static BOOL LogonUserIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_ACCESS_TOKEN, FALSE); +} + +static int LogonUserGetFd(HANDLE handle) +{ + WINPR_ACCESS_TOKEN* pLogonUser = (WINPR_ACCESS_TOKEN*)handle; + + if (!LogonUserIsHandled(handle)) + return -1; + + /* TODO: File fd not supported */ + (void)pLogonUser; + return -1; +} + +BOOL LogonUserCloseHandle(HANDLE handle) +{ + WINPR_ACCESS_TOKEN* token = (WINPR_ACCESS_TOKEN*)handle; + + if (!handle || !LogonUserIsHandled(handle)) + return FALSE; + + free(token->Username); + free(token->Domain); + free(token); + return TRUE; +} + +static HANDLE_OPS ops = { LogonUserIsHandled, + LogonUserCloseHandle, + LogonUserGetFd, + NULL, /* CleanupHandle */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +BOOL LogonUserA(LPCSTR lpszUsername, LPCSTR lpszDomain, LPCSTR lpszPassword, DWORD dwLogonType, + DWORD dwLogonProvider, PHANDLE phToken) +{ + struct passwd* pw = NULL; + WINPR_ACCESS_TOKEN* token = NULL; + + if (!lpszUsername) + return FALSE; + + token = (WINPR_ACCESS_TOKEN*)calloc(1, sizeof(WINPR_ACCESS_TOKEN)); + + if (!token) + return FALSE; + + WINPR_HANDLE_SET_TYPE_AND_MODE(token, HANDLE_TYPE_ACCESS_TOKEN, WINPR_FD_READ); + token->common.ops = &ops; + token->Username = _strdup(lpszUsername); + + if (!token->Username) + { + free(token); + return FALSE; + } + + if (lpszDomain) + { + token->Domain = _strdup(lpszDomain); + + if (!token->Domain) + { + free(token->Username); + free(token); + return FALSE; + } + } + + pw = getpwnam(lpszUsername); + + if (pw) + { + token->UserId = (DWORD)pw->pw_uid; + token->GroupId = (DWORD)pw->pw_gid; + } + + *((ULONG_PTR*)phToken) = (ULONG_PTR)token; + return TRUE; +} + +BOOL LogonUserW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword, DWORD dwLogonType, + DWORD dwLogonProvider, PHANDLE phToken) +{ + return TRUE; +} + +BOOL LogonUserExA(LPCSTR lpszUsername, LPCSTR lpszDomain, LPCSTR lpszPassword, DWORD dwLogonType, + DWORD dwLogonProvider, PHANDLE phToken, PSID* ppLogonSid, PVOID* ppProfileBuffer, + LPDWORD pdwProfileLength, PQUOTA_LIMITS pQuotaLimits) +{ + return TRUE; +} + +BOOL LogonUserExW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword, DWORD dwLogonType, + DWORD dwLogonProvider, PHANDLE phToken, PSID* ppLogonSid, PVOID* ppProfileBuffer, + LPDWORD pdwProfileLength, PQUOTA_LIMITS pQuotaLimits) +{ + return TRUE; +} + +BOOL GetUserNameExA(EXTENDED_NAME_FORMAT NameFormat, LPSTR lpNameBuffer, PULONG nSize) +{ + WINPR_ASSERT(lpNameBuffer); + WINPR_ASSERT(nSize); + + switch (NameFormat) + { + case NameSamCompatible: +#if defined(WINPR_HAVE_GETPWUID_R) + { + int rc = 0; + struct passwd pwd = { 0 }; + struct passwd* result = NULL; + uid_t uid = getuid(); + + rc = getpwuid_r(uid, &pwd, lpNameBuffer, *nSize, &result); + if (rc != 0) + return FALSE; + if (result == NULL) + return FALSE; + } +#elif defined(WINPR_HAVE_GETLOGIN_R) + if (getlogin_r(lpNameBuffer, *nSize) != 0) + return FALSE; +#else + { + const char* name = getlogin(); + if (!name) + return FALSE; + strncpy(lpNameBuffer, name, strnlen(name, *nSize)); + } +#endif + *nSize = strnlen(lpNameBuffer, *nSize); + return TRUE; + + case NameFullyQualifiedDN: + case NameDisplay: + case NameUniqueId: + case NameCanonical: + case NameUserPrincipal: + case NameCanonicalEx: + case NameServicePrincipal: + case NameDnsDomain: + break; + + default: + break; + } + + return FALSE; +} + +BOOL GetUserNameExW(EXTENDED_NAME_FORMAT NameFormat, LPWSTR lpNameBuffer, PULONG nSize) +{ + BOOL rc = FALSE; + char* name = NULL; + + WINPR_ASSERT(nSize); + WINPR_ASSERT(lpNameBuffer); + + name = calloc(1, *nSize + 1); + if (!name) + goto fail; + + if (!GetUserNameExA(NameFormat, name, nSize)) + goto fail; + + const SSIZE_T res = ConvertUtf8ToWChar(name, lpNameBuffer, *nSize); + if (res < 0) + goto fail; + + *nSize = res + 1; + rc = TRUE; +fail: + free(name); + return rc; +} + +#endif diff --git a/winpr/libwinpr/synch/CMakeLists.txt b/winpr/libwinpr/synch/CMakeLists.txt new file mode 100644 index 0000000..84053aa --- /dev/null +++ b/winpr/libwinpr/synch/CMakeLists.txt @@ -0,0 +1,40 @@ +# WinPR: Windows Portable Runtime +# libwinpr-synch cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + address.c + barrier.c + critical.c + event.c + init.c + mutex.c + pollset.c + pollset.h + semaphore.c + sleep.c + synch.h + timer.c + wait.c) + +if(FREEBSD) + winpr_include_directory_add(${EPOLLSHIM_INCLUDE_DIR}) + winpr_library_add_private(${EPOLLSHIM_LIBS}) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/synch/ModuleOptions.cmake b/winpr/libwinpr/synch/ModuleOptions.cmake new file mode 100644 index 0000000..1aac06d --- /dev/null +++ b/winpr/libwinpr/synch/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "synch") +set(MINWIN_LONG_NAME "Synchronization Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/synch/address.c b/winpr/libwinpr/synch/address.c new file mode 100644 index 0000000..d6d074b --- /dev/null +++ b/winpr/libwinpr/synch/address.c @@ -0,0 +1,46 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +/** + * WakeByAddressAll + * WakeByAddressSingle + * WaitOnAddress + */ + +#ifndef _WIN32 + +VOID WakeByAddressAll(PVOID Address) +{ +} + +VOID WakeByAddressSingle(PVOID Address) +{ +} + +BOOL WaitOnAddress(VOID volatile* Address, PVOID CompareAddress, SIZE_T AddressSize, + DWORD dwMilliseconds) +{ + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/synch/barrier.c b/winpr/libwinpr/synch/barrier.c new file mode 100644 index 0000000..0021408 --- /dev/null +++ b/winpr/libwinpr/synch/barrier.c @@ -0,0 +1,263 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "synch.h" + +#include + +#ifdef WINPR_SYNCHRONIZATION_BARRIER + +#include +#include +#include +#include + +/** + * WinPR uses the internal RTL_BARRIER struct members exactly like Windows: + * + * DWORD Reserved1: number of threads that have not yet entered the barrier + * DWORD Reserved2: number of threads required to enter the barrier + * ULONG_PTR Reserved3[2]; two synchronization events (manual reset events) + * DWORD Reserved4; number of processors + * DWORD Reserved5; spincount + */ + +#ifdef _WIN32 + +static HMODULE g_Kernel32 = NULL; +static BOOL g_NativeBarrier = FALSE; +static INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; + +typedef BOOL(WINAPI* fnInitializeSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier, + LONG lTotalThreads, LONG lSpinCount); +typedef BOOL(WINAPI* fnEnterSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier, + DWORD dwFlags); +typedef BOOL(WINAPI* fnDeleteSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier); + +static fnInitializeSynchronizationBarrier pfnInitializeSynchronizationBarrier = NULL; +static fnEnterSynchronizationBarrier pfnEnterSynchronizationBarrier = NULL; +static fnDeleteSynchronizationBarrier pfnDeleteSynchronizationBarrier = NULL; + +static BOOL CALLBACK InitOnce_Barrier(PINIT_ONCE once, PVOID param, PVOID* context) +{ + g_Kernel32 = LoadLibraryA("kernel32.dll"); + + if (!g_Kernel32) + return TRUE; + + pfnInitializeSynchronizationBarrier = (fnInitializeSynchronizationBarrier)GetProcAddress( + g_Kernel32, "InitializeSynchronizationBarrier"); + + pfnEnterSynchronizationBarrier = + (fnEnterSynchronizationBarrier)GetProcAddress(g_Kernel32, "EnterSynchronizationBarrier"); + + pfnDeleteSynchronizationBarrier = + (fnDeleteSynchronizationBarrier)GetProcAddress(g_Kernel32, "DeleteSynchronizationBarrier"); + + if (pfnInitializeSynchronizationBarrier && pfnEnterSynchronizationBarrier && + pfnDeleteSynchronizationBarrier) + { + g_NativeBarrier = TRUE; + } + + return TRUE; +} + +#endif + +BOOL WINAPI winpr_InitializeSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier, + LONG lTotalThreads, LONG lSpinCount) +{ + SYSTEM_INFO sysinfo; + HANDLE hEvent0 = NULL; + HANDLE hEvent1 = NULL; + +#ifdef _WIN32 + InitOnceExecuteOnce(&g_InitOnce, InitOnce_Barrier, NULL, NULL); + + if (g_NativeBarrier) + return pfnInitializeSynchronizationBarrier(lpBarrier, lTotalThreads, lSpinCount); +#endif + + if (!lpBarrier || lTotalThreads < 1 || lSpinCount < -1) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + ZeroMemory(lpBarrier, sizeof(SYNCHRONIZATION_BARRIER)); + + if (lSpinCount == -1) + lSpinCount = 2000; + + if (!(hEvent0 = CreateEvent(NULL, TRUE, FALSE, NULL))) + return FALSE; + + if (!(hEvent1 = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + CloseHandle(hEvent0); + return FALSE; + } + + GetNativeSystemInfo(&sysinfo); + + WINPR_ASSERT(lTotalThreads >= 0); + lpBarrier->Reserved1 = (DWORD)lTotalThreads; + lpBarrier->Reserved2 = (DWORD)lTotalThreads; + lpBarrier->Reserved3[0] = (ULONG_PTR)hEvent0; + lpBarrier->Reserved3[1] = (ULONG_PTR)hEvent1; + lpBarrier->Reserved4 = sysinfo.dwNumberOfProcessors; + WINPR_ASSERT(lSpinCount >= 0); + lpBarrier->Reserved5 = (DWORD)lSpinCount; + + return TRUE; +} + +BOOL WINAPI winpr_EnterSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier, DWORD dwFlags) +{ + LONG remainingThreads = 0; + HANDLE hCurrentEvent = NULL; + HANDLE hDormantEvent = NULL; + +#ifdef _WIN32 + if (g_NativeBarrier) + return pfnEnterSynchronizationBarrier(lpBarrier, dwFlags); +#endif + + if (!lpBarrier) + return FALSE; + + /** + * dwFlags according to + * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706889(v=vs.85).aspx + * + * SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY (0x01) + * Specifies that the thread entering the barrier should block + * immediately until the last thread enters the barrier. + * + * SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY (0x02) + * Specifies that the thread entering the barrier should spin until the + * last thread enters the barrier, even if the spinning thread exceeds + * the barrier's maximum spin count. + * + * SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE (0x04) + * Specifies that the function can skip the work required to ensure + * that it is safe to delete the barrier, which can improve + * performance. All threads that enter this barrier must specify the + * flag; otherwise, the flag is ignored. This flag should be used only + * if the barrier will never be deleted. + */ + + hCurrentEvent = (HANDLE)lpBarrier->Reserved3[0]; + hDormantEvent = (HANDLE)lpBarrier->Reserved3[1]; + + remainingThreads = InterlockedDecrement((LONG*)&lpBarrier->Reserved1); + + WINPR_ASSERT(remainingThreads >= 0); + + if (remainingThreads > 0) + { + DWORD dwProcessors = lpBarrier->Reserved4; + BOOL spinOnly = (dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY) ? TRUE : FALSE; + BOOL blockOnly = (dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) ? TRUE : FALSE; + BOOL block = TRUE; + + /** + * If SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY is set we will + * always spin and trust that the user knows what he/she/it + * is doing. Otherwise we'll only spin if the flag + * SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY is not set and + * the number of remaining threads is less than the number + * of processors. + */ + + if (spinOnly || (((ULONG)remainingThreads < dwProcessors) && !blockOnly)) + { + DWORD dwSpinCount = lpBarrier->Reserved5; + DWORD sp = 0; + /** + * nb: we must let the compiler know that our comparand + * can change between the iterations in the loop below + */ + volatile ULONG_PTR* cmp = &lpBarrier->Reserved3[0]; + /* we spin until the last thread _completed_ the event switch */ + while ((block = (*cmp == (ULONG_PTR)hCurrentEvent))) + if (!spinOnly && ++sp > dwSpinCount) + break; + } + + if (block) + WaitForSingleObject(hCurrentEvent, INFINITE); + + return FALSE; + } + + /* reset the dormant event first */ + ResetEvent(hDormantEvent); + + /* reset the remaining counter */ + lpBarrier->Reserved1 = lpBarrier->Reserved2; + + /* switch events - this will also unblock the spinning threads */ + lpBarrier->Reserved3[1] = (ULONG_PTR)hCurrentEvent; + lpBarrier->Reserved3[0] = (ULONG_PTR)hDormantEvent; + + /* signal the blocked threads */ + SetEvent(hCurrentEvent); + + return TRUE; +} + +BOOL WINAPI winpr_DeleteSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier) +{ +#ifdef _WIN32 + if (g_NativeBarrier) + return pfnDeleteSynchronizationBarrier(lpBarrier); +#endif + + /** + * According to https://msdn.microsoft.com/en-us/library/windows/desktop/hh706887(v=vs.85).aspx + * Return value: + * The DeleteSynchronizationBarrier function always returns TRUE. + */ + + if (!lpBarrier) + return TRUE; + + while (lpBarrier->Reserved1 != lpBarrier->Reserved2) + SwitchToThread(); + + if (lpBarrier->Reserved3[0]) + CloseHandle((HANDLE)lpBarrier->Reserved3[0]); + + if (lpBarrier->Reserved3[1]) + CloseHandle((HANDLE)lpBarrier->Reserved3[1]); + + ZeroMemory(lpBarrier, sizeof(SYNCHRONIZATION_BARRIER)); + + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/synch/critical.c b/winpr/libwinpr/synch/critical.c new file mode 100644 index 0000000..795d93a --- /dev/null +++ b/winpr/libwinpr/synch/critical.c @@ -0,0 +1,272 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2013 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "synch.h" + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#if defined(__APPLE__) +#include +#include +#include +#endif + +#ifndef _WIN32 + +#include "../log.h" +#define TAG WINPR_TAG("synch.critical") + +VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + InitializeCriticalSectionEx(lpCriticalSection, 0, 0); +} + +BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, + DWORD Flags) +{ + WINPR_ASSERT(lpCriticalSection); + /** + * See http://msdn.microsoft.com/en-us/library/ff541979(v=vs.85).aspx + * - The LockCount field indicates the number of times that any thread has + * called the EnterCriticalSection routine for this critical section, + * minus one. This field starts at -1 for an unlocked critical section. + * Each call of EnterCriticalSection increments this value; each call of + * LeaveCriticalSection decrements it. + * - The RecursionCount field indicates the number of times that the owning + * thread has called EnterCriticalSection for this critical section. + */ + if (Flags != 0) + { + WLog_WARN(TAG, "Flags unimplemented"); + } + + lpCriticalSection->DebugInfo = NULL; + lpCriticalSection->LockCount = -1; + lpCriticalSection->SpinCount = 0; + lpCriticalSection->RecursionCount = 0; + lpCriticalSection->OwningThread = NULL; + lpCriticalSection->LockSemaphore = (winpr_sem_t*)malloc(sizeof(winpr_sem_t)); + + if (!lpCriticalSection->LockSemaphore) + return FALSE; + +#if defined(__APPLE__) + + if (semaphore_create(mach_task_self(), lpCriticalSection->LockSemaphore, SYNC_POLICY_FIFO, 0) != + KERN_SUCCESS) + goto out_fail; + +#else + + if (sem_init(lpCriticalSection->LockSemaphore, 0, 0) != 0) + goto out_fail; + +#endif + SetCriticalSectionSpinCount(lpCriticalSection, dwSpinCount); + return TRUE; +out_fail: + free(lpCriticalSection->LockSemaphore); + return FALSE; +} + +BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount) +{ + return InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, 0); +} + +DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount) +{ + WINPR_ASSERT(lpCriticalSection); +#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT) + SYSTEM_INFO sysinfo; + DWORD dwPreviousSpinCount = lpCriticalSection->SpinCount; + + if (dwSpinCount) + { + /* Don't spin on uniprocessor systems! */ + GetNativeSystemInfo(&sysinfo); + + if (sysinfo.dwNumberOfProcessors < 2) + dwSpinCount = 0; + } + + lpCriticalSection->SpinCount = dwSpinCount; + return dwPreviousSpinCount; +#else + return 0; +#endif +} + +static VOID _WaitForCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + WINPR_ASSERT(lpCriticalSection); +#if defined(__APPLE__) + semaphore_wait(*((winpr_sem_t*)lpCriticalSection->LockSemaphore)); +#else + sem_wait((winpr_sem_t*)lpCriticalSection->LockSemaphore); +#endif +} + +static VOID _UnWaitCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + WINPR_ASSERT(lpCriticalSection); +#if defined __APPLE__ + semaphore_signal(*((winpr_sem_t*)lpCriticalSection->LockSemaphore)); +#else + sem_post((winpr_sem_t*)lpCriticalSection->LockSemaphore); +#endif +} + +VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + WINPR_ASSERT(lpCriticalSection); +#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT) + ULONG SpinCount = lpCriticalSection->SpinCount; + + /* If we're lucky or if the current thread is already owner we can return early */ + if (SpinCount && TryEnterCriticalSection(lpCriticalSection)) + return; + + /* Spin requested times but don't compete with another waiting thread */ + while (SpinCount-- && lpCriticalSection->LockCount < 1) + { + /* Atomically try to acquire and check the if the section is free. */ + if (InterlockedCompareExchange(&lpCriticalSection->LockCount, 0, -1) == -1) + { + lpCriticalSection->RecursionCount = 1; + lpCriticalSection->OwningThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId(); + return; + } + + /* Failed to get the lock. Let the scheduler know that we're spinning. */ + if (sched_yield() != 0) + { + /** + * On some operating systems sched_yield is a stub. + * usleep should at least trigger a context switch if any thread is waiting. + * A ThreadYield() would be nice in winpr ... + */ + usleep(1); + } + } + +#endif + + /* First try the fastest possible path to get the lock. */ + if (InterlockedIncrement(&lpCriticalSection->LockCount)) + { + /* Section is already locked. Check if it is owned by the current thread. */ + if (lpCriticalSection->OwningThread == (HANDLE)(ULONG_PTR)GetCurrentThreadId()) + { + /* Recursion. No need to wait. */ + lpCriticalSection->RecursionCount++; + return; + } + + /* Section is locked by another thread. We have to wait. */ + _WaitForCriticalSection(lpCriticalSection); + } + + /* We got the lock. Own it ... */ + lpCriticalSection->RecursionCount = 1; + lpCriticalSection->OwningThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId(); +} + +BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + HANDLE current_thread = (HANDLE)(ULONG_PTR)GetCurrentThreadId(); + + WINPR_ASSERT(lpCriticalSection); + + /* Atomically acquire the the lock if the section is free. */ + if (InterlockedCompareExchange(&lpCriticalSection->LockCount, 0, -1) == -1) + { + lpCriticalSection->RecursionCount = 1; + lpCriticalSection->OwningThread = current_thread; + return TRUE; + } + + /* Section is already locked. Check if it is owned by the current thread. */ + if (lpCriticalSection->OwningThread == current_thread) + { + /* Recursion, return success */ + lpCriticalSection->RecursionCount++; + InterlockedIncrement(&lpCriticalSection->LockCount); + return TRUE; + } + + return FALSE; +} + +VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + WINPR_ASSERT(lpCriticalSection); + + /* Decrement RecursionCount and check if this is the last LeaveCriticalSection call ...*/ + if (--lpCriticalSection->RecursionCount < 1) + { + /* Last recursion, clear owner, unlock and if there are other waiting threads ... */ + lpCriticalSection->OwningThread = NULL; + + if (InterlockedDecrement(&lpCriticalSection->LockCount) >= 0) + { + /* ...signal the semaphore to unblock the next waiting thread */ + _UnWaitCriticalSection(lpCriticalSection); + } + } + else + { + InterlockedDecrement(&lpCriticalSection->LockCount); + } +} + +VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) +{ + WINPR_ASSERT(lpCriticalSection); + + lpCriticalSection->LockCount = -1; + lpCriticalSection->SpinCount = 0; + lpCriticalSection->RecursionCount = 0; + lpCriticalSection->OwningThread = NULL; + + if (lpCriticalSection->LockSemaphore != NULL) + { +#if defined __APPLE__ + semaphore_destroy(mach_task_self(), *((winpr_sem_t*)lpCriticalSection->LockSemaphore)); +#else + sem_destroy((winpr_sem_t*)lpCriticalSection->LockSemaphore); +#endif + free(lpCriticalSection->LockSemaphore); + lpCriticalSection->LockSemaphore = NULL; + } +} + +#endif diff --git a/winpr/libwinpr/synch/event.c b/winpr/libwinpr/synch/event.c new file mode 100644 index 0000000..7204add --- /dev/null +++ b/winpr/libwinpr/synch/event.c @@ -0,0 +1,584 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2017 Armin Novak + * Copyright 2017 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +#ifndef _WIN32 + +#include "synch.h" + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifdef WINPR_HAVE_SYS_EVENTFD_H +#include +#endif + +#include +#include + +#include "../handle/handle.h" +#include "../pipe/pipe.h" + +#include "../log.h" +#include "event.h" +#define TAG WINPR_TAG("synch.event") + +#if defined(WITH_DEBUG_EVENTS) +static wArrayList* global_event_list = NULL; + +static void dump_event(WINPR_EVENT* event, size_t index) +{ + char** msg = NULL; + size_t used = 0; +#if 0 + void* stack = winpr_backtrace(20); + WLog_DBG(TAG, "Called from:"); + msg = winpr_backtrace_symbols(stack, &used); + + for (size_t i = 0; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); + winpr_backtrace_free(stack); +#endif + WLog_DBG(TAG, "Event handle created still not closed! [%" PRIuz ", %p]", index, event); + msg = winpr_backtrace_symbols(event->create_stack, &used); + + for (size_t i = 2; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); +} +#endif /* WITH_DEBUG_EVENTS */ + +#ifdef WINPR_HAVE_SYS_EVENTFD_H +#if !defined(WITH_EVENTFD_READ_WRITE) +static int eventfd_read(int fd, eventfd_t* value) +{ + return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1; +} + +static int eventfd_write(int fd, eventfd_t value) +{ + return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1; +} +#endif +#endif + +#ifndef WINPR_HAVE_SYS_EVENTFD_H +static BOOL set_non_blocking_fd(int fd) +{ + int flags; + flags = fcntl(fd, F_GETFL); + if (flags < 0) + return FALSE; + + return fcntl(fd, F_SETFL, flags | O_NONBLOCK) >= 0; +} +#endif /* !WINPR_HAVE_SYS_EVENTFD_H */ + +BOOL winpr_event_init(WINPR_EVENT_IMPL* event) +{ +#ifdef WINPR_HAVE_SYS_EVENTFD_H + event->fds[1] = -1; + event->fds[0] = eventfd(0, EFD_NONBLOCK); + + return event->fds[0] >= 0; +#else + int flags; + + if (pipe(event->fds) < 0) + return FALSE; + + if (!set_non_blocking_fd(event->fds[0]) || !set_non_blocking_fd(event->fds[1])) + goto out_error; + + return TRUE; + +out_error: + winpr_event_uninit(event); + return FALSE; +#endif +} + +void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd) +{ + event->fds[0] = fd; +#ifndef WINPR_HAVE_SYS_EVENTFD_H + event->fds[1] = fd; +#endif +} + +BOOL winpr_event_set(WINPR_EVENT_IMPL* event) +{ + int ret = 0; + do + { +#ifdef WINPR_HAVE_SYS_EVENTFD_H + eventfd_t value = 1; + ret = eventfd_write(event->fds[0], value); +#else + ret = write(event->fds[1], "-", 1); +#endif + } while (ret < 0 && errno == EINTR); + + return ret >= 0; +} + +BOOL winpr_event_reset(WINPR_EVENT_IMPL* event) +{ + int ret = 0; + do + { + do + { +#ifdef WINPR_HAVE_SYS_EVENTFD_H + eventfd_t value = 1; + ret = eventfd_read(event->fds[0], &value); +#else + char value; + ret = read(event->fds[0], &value, 1); +#endif + } while (ret < 0 && errno == EINTR); + } while (ret >= 0); + + return (errno == EAGAIN); +} + +void winpr_event_uninit(WINPR_EVENT_IMPL* event) +{ + if (event->fds[0] != -1) + { + close(event->fds[0]); + event->fds[0] = -1; + } + + if (event->fds[1] != -1) + { + close(event->fds[1]); + event->fds[1] = -1; + } +} + +static BOOL EventCloseHandle(HANDLE handle); + +static BOOL EventIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE); +} + +static int EventGetFd(HANDLE handle) +{ + WINPR_EVENT* event = (WINPR_EVENT*)handle; + + if (!EventIsHandled(handle)) + return -1; + + return event->impl.fds[0]; +} + +static BOOL EventCloseHandle_(WINPR_EVENT* event) +{ + if (!event) + return FALSE; + + if (event->bAttached) + { + // don't close attached file descriptor + event->impl.fds[0] = -1; // mark as invalid + } + + winpr_event_uninit(&event->impl); + +#if defined(WITH_DEBUG_EVENTS) + if (global_event_list) + { + ArrayList_Remove(global_event_list, event); + if (ArrayList_Count(global_event_list) < 1) + { + ArrayList_Free(global_event_list); + global_event_list = NULL; + } + } + + winpr_backtrace_free(event->create_stack); +#endif + free(event->name); + free(event); + return TRUE; +} + +static BOOL EventCloseHandle(HANDLE handle) +{ + WINPR_EVENT* event = (WINPR_EVENT*)handle; + + if (!EventIsHandled(handle)) + return FALSE; + + return EventCloseHandle_(event); +} + +static HANDLE_OPS ops = { EventIsHandled, + EventCloseHandle, + EventGetFd, + NULL, /* CleanupHandle */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, + LPCWSTR lpName) +{ + HANDLE handle = NULL; + char* name = NULL; + + if (lpName) + { + name = ConvertWCharToUtf8Alloc(lpName, NULL); + if (!name) + return NULL; + } + + handle = CreateEventA(lpEventAttributes, bManualReset, bInitialState, name); + free(name); + return handle; +} + +HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, + LPCSTR lpName) +{ + WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT)); + + if (lpEventAttributes) + WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName); + + if (!event) + return NULL; + + if (lpName) + event->name = strdup(lpName); + + event->impl.fds[0] = -1; + event->impl.fds[1] = -1; + event->bAttached = FALSE; + event->bManualReset = bManualReset; + event->common.ops = &ops; + WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ); + + if (!event->bManualReset) + WLog_ERR(TAG, "auto-reset events not yet implemented"); + + if (!winpr_event_init(&event->impl)) + goto fail; + + if (bInitialState) + { + if (!SetEvent(event)) + goto fail; + } + +#if defined(WITH_DEBUG_EVENTS) + event->create_stack = winpr_backtrace(20); + if (!global_event_list) + global_event_list = ArrayList_New(TRUE); + + if (global_event_list) + ArrayList_Append(global_event_list, event); +#endif + return (HANDLE)event; +fail: + EventCloseHandle_(event); + return NULL; +} + +HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags, + DWORD dwDesiredAccess) +{ + BOOL initial = FALSE; + BOOL manual = FALSE; + + if (dwFlags & CREATE_EVENT_INITIAL_SET) + initial = TRUE; + + if (dwFlags & CREATE_EVENT_MANUAL_RESET) + manual = TRUE; + + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName, + dwDesiredAccess); + + return CreateEventW(lpEventAttributes, manual, initial, lpName); +} + +HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, + DWORD dwDesiredAccess) +{ + BOOL initial = FALSE; + BOOL manual = FALSE; + + if (dwFlags & CREATE_EVENT_INITIAL_SET) + initial = TRUE; + + if (dwFlags & CREATE_EVENT_MANUAL_RESET) + manual = TRUE; + + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName, + dwDesiredAccess); + + return CreateEventA(lpEventAttributes, manual, initial, lpName); +} + +HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) +{ + /* TODO: Implement */ + WINPR_UNUSED(dwDesiredAccess); + WINPR_UNUSED(bInheritHandle); + WINPR_UNUSED(lpName); + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) +{ + /* TODO: Implement */ + WINPR_UNUSED(dwDesiredAccess); + WINPR_UNUSED(bInheritHandle); + WINPR_UNUSED(lpName); + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +BOOL SetEvent(HANDLE hEvent) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_EVENT* event = NULL; + + if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT) + { + WLog_ERR(TAG, "SetEvent: hEvent is not an event"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + event = (WINPR_EVENT*)Object; + return winpr_event_set(&event->impl); +} + +BOOL ResetEvent(HANDLE hEvent) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_EVENT* event = NULL; + + if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT) + { + WLog_ERR(TAG, "ResetEvent: hEvent is not an event"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + event = (WINPR_EVENT*)Object; + return winpr_event_reset(&event->impl); +} + +#endif + +HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, int FileDescriptor, ULONG mode) +{ +#ifndef _WIN32 + WINPR_EVENT* event = NULL; + HANDLE handle = NULL; + event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT)); + + if (event) + { + event->impl.fds[0] = -1; + event->impl.fds[1] = -1; + event->bAttached = TRUE; + event->bManualReset = bManualReset; + winpr_event_init_from_fd(&event->impl, FileDescriptor); + event->common.ops = &ops; + WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode); + handle = (HANDLE)event; + } + + return handle; +#else + return NULL; +#endif +} + +HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, int FileDescriptor, ULONG mode) +{ + return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, + FileDescriptor, mode); +} + +/** + * Returns an event based on the handle returned by GetEventWaitObject() + */ +HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, void* pObject) +{ +#ifndef _WIN32 + return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, + (int)(ULONG_PTR)pObject, WINPR_FD_READ); +#else + HANDLE hEvent = NULL; + DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE, + DUPLICATE_SAME_ACCESS); + return hEvent; +#endif +} + +/* + * Returns inner file descriptor for usage with select() + * This file descriptor is not usable on Windows + */ + +int GetEventFileDescriptor(HANDLE hEvent) +{ +#ifndef _WIN32 + return winpr_Handle_getFd(hEvent); +#else + return -1; +#endif +} + +/* + * Set inner file descriptor for usage with select() + * This file descriptor is not usable on Windows + */ + +int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode) +{ +#ifndef _WIN32 + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_EVENT* event = NULL; + + if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT) + { + WLog_ERR(TAG, "SetEventFileDescriptor: hEvent is not an event"); + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + event = (WINPR_EVENT*)Object; + + if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor) + close(event->impl.fds[0]); + + event->bAttached = TRUE; + event->common.Mode = mode; + event->impl.fds[0] = FileDescriptor; + return 0; +#else + return -1; +#endif +} + +/** + * Returns platform-specific wait object as a void pointer + * + * On Windows, the returned object is the same as the hEvent + * argument and is an event HANDLE usable in WaitForMultipleObjects + * + * On other platforms, the returned object can be cast to an int + * to obtain a file descriptor usable in select() + */ + +void* GetEventWaitObject(HANDLE hEvent) +{ +#ifndef _WIN32 + int fd = 0; + void* obj = NULL; + fd = GetEventFileDescriptor(hEvent); + obj = ((void*)(long)fd); + return obj; +#else + return hEvent; +#endif +} +#if defined(WITH_DEBUG_EVENTS) +#include +#include +#include +#include + +static BOOL dump_handle_list(void* data, size_t index, va_list ap) +{ + WINPR_EVENT* event = data; + dump_event(event, index); + return TRUE; +} + +void DumpEventHandles_(const char* fkt, const char* file, size_t line) +{ + struct rlimit r = { 0 }; + int rc = getrlimit(RLIMIT_NOFILE, &r); + if (rc >= 0) + { + size_t count = 0; + for (rlim_t x = 0; x < r.rlim_cur; x++) + { + int flags = fcntl(x, F_GETFD); + if (flags >= 0) + count++; + } + WLog_INFO(TAG, "------- limits [%d/%d] open files %" PRIuz, r.rlim_cur, r.rlim_max, count); + } + WLog_DBG(TAG, "--------- Start dump [%s %s:%" PRIuz "]", fkt, file, line); + if (global_event_list) + { + ArrayList_Lock(global_event_list); + ArrayList_ForEach(global_event_list, dump_handle_list); + ArrayList_Unlock(global_event_list); + } + WLog_DBG(TAG, "--------- End dump [%s %s:%" PRIuz "]", fkt, file, line); +} +#endif diff --git a/winpr/libwinpr/synch/event.h b/winpr/libwinpr/synch/event.h new file mode 100644 index 0000000..0f57374 --- /dev/null +++ b/winpr/libwinpr/synch/event.h @@ -0,0 +1,57 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * event implementation + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WINPR_LIBWINPR_SYNCH_EVENT_H_ +#define WINPR_LIBWINPR_SYNCH_EVENT_H_ + +#include "../handle/handle.h" + +#include + +#ifdef WINPR_HAVE_SYS_EVENTFD_H +#include +#endif + +struct winpr_event_impl +{ + int fds[2]; +}; + +typedef struct winpr_event_impl WINPR_EVENT_IMPL; + +struct winpr_event +{ + WINPR_HANDLE common; + + WINPR_EVENT_IMPL impl; + BOOL bAttached; + BOOL bManualReset; + char* name; +#if defined(WITH_DEBUG_EVENTS) + void* create_stack; +#endif +}; +typedef struct winpr_event WINPR_EVENT; + +BOOL winpr_event_init(WINPR_EVENT_IMPL* event); +void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd); +BOOL winpr_event_set(WINPR_EVENT_IMPL* event); +BOOL winpr_event_reset(WINPR_EVENT_IMPL* event); +void winpr_event_uninit(WINPR_EVENT_IMPL* event); + +#endif /* WINPR_LIBWINPR_SYNCH_EVENT_H_ */ diff --git a/winpr/libwinpr/synch/init.c b/winpr/libwinpr/synch/init.c new file mode 100644 index 0000000..0e383eb --- /dev/null +++ b/winpr/libwinpr/synch/init.c @@ -0,0 +1,96 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "../log.h" +#define TAG WINPR_TAG("sync") + +#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600)) + +BOOL winpr_InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, + LPVOID* lpContext) +{ + WLog_ERR(TAG, "not implemented"); + return FALSE; +} + +BOOL winpr_InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext) +{ + WLog_ERR(TAG, "not implemented"); + return FALSE; +} + +VOID winpr_InitOnceInitialize(PINIT_ONCE InitOnce) +{ + WLog_ERR(TAG, "not implemented"); +} + +BOOL winpr_InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, + LPVOID* Context) +{ + for (;;) + { + switch ((ULONG_PTR)InitOnce->Ptr & 3) + { + case 2: + /* already completed successfully */ + return TRUE; + + case 0: + + /* first time */ + if (InterlockedCompareExchangePointer(&InitOnce->Ptr, (PVOID)1, (PVOID)0) != + (PVOID)0) + { + /* some other thread was faster */ + break; + } + + /* it's our job to call the init function */ + if (InitFn(InitOnce, Parameter, Context)) + { + /* success */ + InitOnce->Ptr = (PVOID)2; + return TRUE; + } + + /* the init function returned an error, reset the status */ + InitOnce->Ptr = (PVOID)0; + return FALSE; + + case 1: + /* in progress */ + break; + + default: + WLog_ERR(TAG, "internal error"); + return FALSE; + } + + Sleep(5); + } +} + +#endif diff --git a/winpr/libwinpr/synch/mutex.c b/winpr/libwinpr/synch/mutex.c new file mode 100644 index 0000000..6a85db6 --- /dev/null +++ b/winpr/libwinpr/synch/mutex.c @@ -0,0 +1,247 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include "synch.h" + +#ifndef _WIN32 + +#include + +#include "../handle/handle.h" + +#include "../log.h" +#define TAG WINPR_TAG("sync.mutex") + +static BOOL MutexCloseHandle(HANDLE handle); + +static BOOL MutexIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_MUTEX, FALSE); +} + +static int MutexGetFd(HANDLE handle) +{ + WINPR_MUTEX* mux = (WINPR_MUTEX*)handle; + + if (!MutexIsHandled(handle)) + return -1; + + /* TODO: Mutex does not support file handles... */ + (void)mux; + return -1; +} + +BOOL MutexCloseHandle(HANDLE handle) +{ + WINPR_MUTEX* mutex = (WINPR_MUTEX*)handle; + int rc = 0; + + if (!MutexIsHandled(handle)) + return FALSE; + + if ((rc = pthread_mutex_destroy(&mutex->mutex))) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "pthread_mutex_destroy failed with %s [%d]", + winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc); +#if defined(WITH_DEBUG_MUTEX) + { + size_t used = 0; + void* stack = winpr_backtrace(20); + char** msg = NULL; + + if (stack) + msg = winpr_backtrace_symbols(stack, &used); + + if (msg) + { + for (size_t i = 0; i < used; i++) + WLog_ERR(TAG, "%2" PRIdz ": %s", i, msg[i]); + } + + free(msg); + winpr_backtrace_free(stack); + } +#endif + /** + * Note: unfortunately we may not return FALSE here since CloseHandle(hmutex) on + * Windows always seems to succeed independently of the mutex object locking state + */ + } + + free(mutex->name); + free(handle); + return TRUE; +} + +static HANDLE_OPS ops = { MutexIsHandled, + MutexCloseHandle, + MutexGetFd, + NULL, /* CleanupHandle */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +HANDLE CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName) +{ + HANDLE handle = NULL; + char* name = NULL; + + if (lpName) + { + name = ConvertWCharToUtf8Alloc(lpName, NULL); + if (!name) + return NULL; + } + + handle = CreateMutexA(lpMutexAttributes, bInitialOwner, name); + free(name); + return handle; +} + +HANDLE CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName) +{ + HANDLE handle = NULL; + WINPR_MUTEX* mutex = NULL; + mutex = (WINPR_MUTEX*)calloc(1, sizeof(WINPR_MUTEX)); + + if (lpMutexAttributes) + WLog_WARN(TAG, "[%s] does not support lpMutexAttributes", lpName); + + if (mutex) + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex->mutex, &attr); + WINPR_HANDLE_SET_TYPE_AND_MODE(mutex, HANDLE_TYPE_MUTEX, WINPR_FD_READ); + mutex->common.ops = &ops; + handle = (HANDLE)mutex; + + if (bInitialOwner) + pthread_mutex_lock(&mutex->mutex); + + if (lpName) + mutex->name = strdup(lpName); /* Non runtime relevant information, skip NULL check */ + } + + return handle; +} + +HANDLE CreateMutexExA(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCSTR lpName, DWORD dwFlags, + DWORD dwDesiredAccess) +{ + BOOL initial = FALSE; + /* TODO: support access modes */ + + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName, + dwDesiredAccess); + + if (dwFlags & CREATE_MUTEX_INITIAL_OWNER) + initial = TRUE; + + return CreateMutexA(lpMutexAttributes, initial, lpName); +} + +HANDLE CreateMutexExW(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCWSTR lpName, DWORD dwFlags, + DWORD dwDesiredAccess) +{ + BOOL initial = FALSE; + + /* TODO: support access modes */ + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName, + dwDesiredAccess); + + if (dwFlags & CREATE_MUTEX_INITIAL_OWNER) + initial = TRUE; + + return CreateMutexW(lpMutexAttributes, initial, lpName); +} + +HANDLE OpenMutexA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) +{ + /* TODO: Implement */ + WINPR_UNUSED(dwDesiredAccess); + WINPR_UNUSED(bInheritHandle); + WINPR_UNUSED(lpName); + WLog_ERR(TAG, "TODO: Implement"); + return NULL; +} + +HANDLE OpenMutexW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) +{ + /* TODO: Implement */ + WINPR_UNUSED(dwDesiredAccess); + WINPR_UNUSED(bInheritHandle); + WINPR_UNUSED(lpName); + WLog_ERR(TAG, "TODO: Implement"); + return NULL; +} + +BOOL ReleaseMutex(HANDLE hMutex) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + + if (!winpr_Handle_GetInfo(hMutex, &Type, &Object)) + return FALSE; + + if (Type == HANDLE_TYPE_MUTEX) + { + WINPR_MUTEX* mutex = (WINPR_MUTEX*)Object; + int rc = pthread_mutex_unlock(&mutex->mutex); + + if (rc) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "pthread_mutex_unlock failed with %s [%d]", + winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc); + return FALSE; + } + + return TRUE; + } + + return FALSE; +} + +#endif diff --git a/winpr/libwinpr/synch/pollset.c b/winpr/libwinpr/synch/pollset.c new file mode 100644 index 0000000..8711ea0 --- /dev/null +++ b/winpr/libwinpr/synch/pollset.c @@ -0,0 +1,274 @@ +#ifndef _WIN32 +#include + +#include "pollset.h" +#include +#include +#include +#include "../log.h" + +#define TAG WINPR_TAG("sync.pollset") + +#ifdef WINPR_HAVE_POLL_H +static INT16 handle_mode_to_pollevent(ULONG mode) +{ + INT16 event = 0; + + if (mode & WINPR_FD_READ) + event |= POLLIN; + + if (mode & WINPR_FD_WRITE) + event |= POLLOUT; + + return event; +} +#endif + +BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles) +{ + WINPR_ASSERT(set); +#ifdef WINPR_HAVE_POLL_H + if (nhandles > MAXIMUM_WAIT_OBJECTS) + { + set->isStatic = FALSE; + set->pollset = calloc(nhandles, sizeof(*set->pollset)); + if (!set->pollset) + return FALSE; + } + else + { + set->pollset = set->staticSet; + set->isStatic = TRUE; + } +#else + set->fdIndex = calloc(nhandles, sizeof(*set->fdIndex)); + if (!set->fdIndex) + return FALSE; + + FD_ZERO(&set->rset_base); + FD_ZERO(&set->rset); + FD_ZERO(&set->wset_base); + FD_ZERO(&set->wset); + set->maxFd = 0; + set->nread = set->nwrite = 0; +#endif + + set->size = nhandles; + set->fillIndex = 0; + return TRUE; +} + +void pollset_uninit(WINPR_POLL_SET* set) +{ + WINPR_ASSERT(set); +#ifdef WINPR_HAVE_POLL_H + if (!set->isStatic) + free(set->pollset); +#else + free(set->fdIndex); +#endif +} + +void pollset_reset(WINPR_POLL_SET* set) +{ + WINPR_ASSERT(set); +#ifndef WINPR_HAVE_POLL_H + FD_ZERO(&set->rset_base); + FD_ZERO(&set->wset_base); + set->maxFd = 0; + set->nread = set->nwrite = 0; +#endif + set->fillIndex = 0; +} + +BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode) +{ + WINPR_ASSERT(set); +#ifdef WINPR_HAVE_POLL_H + struct pollfd* item = NULL; + if (set->fillIndex == set->size) + return FALSE; + + item = &set->pollset[set->fillIndex]; + item->fd = fd; + item->revents = 0; + item->events = handle_mode_to_pollevent(mode); +#else + FdIndex* fdIndex = &set->fdIndex[set->fillIndex]; + if (mode & WINPR_FD_READ) + { + FD_SET(fd, &set->rset_base); + set->nread++; + } + + if (mode & WINPR_FD_WRITE) + { + FD_SET(fd, &set->wset_base); + set->nwrite++; + } + + if (fd > set->maxFd) + set->maxFd = fd; + + fdIndex->fd = fd; + fdIndex->mode = mode; +#endif + set->fillIndex++; + return TRUE; +} + +int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds) +{ + WINPR_ASSERT(set); + int ret = 0; + UINT64 dueTime = 0; + UINT64 now = 0; + + now = GetTickCount64(); + if (dwMilliseconds == INFINITE) + dueTime = 0xFFFFFFFFFFFFFFFF; + else + dueTime = now + dwMilliseconds; + +#ifdef WINPR_HAVE_POLL_H + int timeout = 0; + + do + { + if (dwMilliseconds == INFINITE) + timeout = -1; + else + timeout = (int)(dueTime - now); + + ret = poll(set->pollset, set->fillIndex, timeout); + if (ret >= 0) + return ret; + + if (errno != EINTR) + return -1; + + now = GetTickCount64(); + } while (now < dueTime); + +#else + do + { + struct timeval staticTimeout; + struct timeval* timeout; + + fd_set* rset = NULL; + fd_set* wset = NULL; + + if (dwMilliseconds == INFINITE) + { + timeout = NULL; + } + else + { + long waitTime = (long)(dueTime - now); + + timeout = &staticTimeout; + timeout->tv_sec = waitTime / 1000; + timeout->tv_usec = (waitTime % 1000) * 1000; + } + + if (set->nread) + { + rset = &set->rset; + memcpy(rset, &set->rset_base, sizeof(*rset)); + } + + if (set->nwrite) + { + wset = &set->wset; + memcpy(wset, &set->wset_base, sizeof(*wset)); + } + + ret = select(set->maxFd + 1, rset, wset, NULL, timeout); + if (ret >= 0) + return ret; + + if (errno != EINTR) + return -1; + + now = GetTickCount64(); + + } while (now < dueTime); + + FD_ZERO(&set->rset); + FD_ZERO(&set->wset); +#endif + + return 0; /* timeout */ +} + +BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx) +{ + WINPR_ASSERT(set); + + if (idx > set->fillIndex) + { + WLog_ERR(TAG, "index=%d out of pollset(fillIndex=%" PRIuz ")", idx, set->fillIndex); + return FALSE; + } + +#ifdef WINPR_HAVE_POLL_H + return !!(set->pollset[idx].revents & set->pollset[idx].events); +#else + FdIndex* fdIndex = &set->fdIndex[idx]; + if (fdIndex->fd < 0) + return FALSE; + + if ((fdIndex->mode & WINPR_FD_READ) && FD_ISSET(fdIndex->fd, &set->rset)) + return TRUE; + + if ((fdIndex->mode & WINPR_FD_WRITE) && FD_ISSET(fdIndex->fd, &set->wset)) + return TRUE; + + return FALSE; +#endif +} + +BOOL pollset_isReadSignaled(WINPR_POLL_SET* set, size_t idx) +{ + WINPR_ASSERT(set); + + if (idx > set->fillIndex) + { + WLog_ERR(TAG, "index=%d out of pollset(fillIndex=%" PRIuz ")", idx, set->fillIndex); + return FALSE; + } + +#ifdef WINPR_HAVE_POLL_H + return !!(set->pollset[idx].revents & POLLIN); +#else + FdIndex* fdIndex = &set->fdIndex[idx]; + if (fdIndex->fd < 0) + return FALSE; + + return FD_ISSET(fdIndex->fd, &set->rset); +#endif +} + +BOOL pollset_isWriteSignaled(WINPR_POLL_SET* set, size_t idx) +{ + WINPR_ASSERT(set); + + if (idx > set->fillIndex) + { + WLog_ERR(TAG, "index=%d out of pollset(fillIndex=%" PRIuz ")", idx, set->fillIndex); + return FALSE; + } + +#ifdef WINPR_HAVE_POLL_H + return !!(set->pollset[idx].revents & POLLOUT); +#else + FdIndex* fdIndex = &set->fdIndex[idx]; + if (fdIndex->fd < 0) + return FALSE; + + return FD_ISSET(fdIndex->fd, &set->wset); +#endif +} + +#endif diff --git a/winpr/libwinpr/synch/pollset.h b/winpr/libwinpr/synch/pollset.h new file mode 100644 index 0000000..6e478e6 --- /dev/null +++ b/winpr/libwinpr/synch/pollset.h @@ -0,0 +1,74 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * pollset + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WINPR_LIBWINPR_SYNCH_POLLSET_H_ +#define WINPR_LIBWINPR_SYNCH_POLLSET_H_ + +#include +#include + +#include + +#ifndef _WIN32 + +#ifdef WINPR_HAVE_POLL_H +#include +#else +#include + +typedef struct +{ + int fd; + ULONG mode; +} FdIndex; +#endif + +struct winpr_poll_set +{ +#ifdef WINPR_HAVE_POLL_H + struct pollfd* pollset; + struct pollfd staticSet[MAXIMUM_WAIT_OBJECTS]; + BOOL isStatic; +#else + FdIndex* fdIndex; + fd_set rset_base; + fd_set rset; + fd_set wset_base; + fd_set wset; + int nread, nwrite; + int maxFd; +#endif + size_t fillIndex; + size_t size; +}; + +typedef struct winpr_poll_set WINPR_POLL_SET; + +BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles); +void pollset_uninit(WINPR_POLL_SET* set); +void pollset_reset(WINPR_POLL_SET* set); +BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode); +int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds); + +BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx); +BOOL pollset_isReadSignaled(WINPR_POLL_SET* set, size_t idx); +BOOL pollset_isWriteSignaled(WINPR_POLL_SET* set, size_t idx); + +#endif + +#endif /* WINPR_LIBWINPR_SYNCH_POLLSET_H_ */ diff --git a/winpr/libwinpr/synch/semaphore.c b/winpr/libwinpr/synch/semaphore.c new file mode 100644 index 0000000..855675b --- /dev/null +++ b/winpr/libwinpr/synch/semaphore.c @@ -0,0 +1,257 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "synch.h" + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifndef _WIN32 + +#include +#include "../handle/handle.h" +#include "../log.h" +#define TAG WINPR_TAG("synch.semaphore") + +static BOOL SemaphoreCloseHandle(HANDLE handle); + +static BOOL SemaphoreIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_SEMAPHORE, FALSE); +} + +static int SemaphoreGetFd(HANDLE handle) +{ + WINPR_SEMAPHORE* sem = (WINPR_SEMAPHORE*)handle; + + if (!SemaphoreIsHandled(handle)) + return -1; + + return sem->pipe_fd[0]; +} + +static DWORD SemaphoreCleanupHandle(HANDLE handle) +{ + SSIZE_T length = 0; + WINPR_SEMAPHORE* sem = (WINPR_SEMAPHORE*)handle; + + if (!SemaphoreIsHandled(handle)) + return WAIT_FAILED; + + length = read(sem->pipe_fd[0], &length, 1); + + if (length != 1) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "semaphore read() failure [%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return WAIT_FAILED; + } + + return WAIT_OBJECT_0; +} + +BOOL SemaphoreCloseHandle(HANDLE handle) +{ + WINPR_SEMAPHORE* semaphore = (WINPR_SEMAPHORE*)handle; + + if (!SemaphoreIsHandled(handle)) + return FALSE; + +#ifdef WINPR_PIPE_SEMAPHORE + + if (semaphore->pipe_fd[0] != -1) + { + close(semaphore->pipe_fd[0]); + semaphore->pipe_fd[0] = -1; + + if (semaphore->pipe_fd[1] != -1) + { + close(semaphore->pipe_fd[1]); + semaphore->pipe_fd[1] = -1; + } + } + +#else +#if defined __APPLE__ + semaphore_destroy(mach_task_self(), *((winpr_sem_t*)semaphore->sem)); +#else + sem_destroy((winpr_sem_t*)semaphore->sem); +#endif +#endif + free(semaphore); + return TRUE; +} + +static HANDLE_OPS ops = { SemaphoreIsHandled, + SemaphoreCloseHandle, + SemaphoreGetFd, + SemaphoreCleanupHandle, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +HANDLE CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, + LONG lMaximumCount, LPCWSTR lpName) +{ + HANDLE handle = NULL; + WINPR_SEMAPHORE* semaphore = NULL; + semaphore = (WINPR_SEMAPHORE*)calloc(1, sizeof(WINPR_SEMAPHORE)); + + if (!semaphore) + return NULL; + + semaphore->pipe_fd[0] = -1; + semaphore->pipe_fd[1] = -1; + semaphore->sem = (winpr_sem_t*)NULL; + semaphore->common.ops = &ops; +#ifdef WINPR_PIPE_SEMAPHORE + + if (pipe(semaphore->pipe_fd) < 0) + { + WLog_ERR(TAG, "failed to create semaphore"); + free(semaphore); + return NULL; + } + + while (lInitialCount > 0) + { + if (write(semaphore->pipe_fd[1], "-", 1) != 1) + { + close(semaphore->pipe_fd[0]); + close(semaphore->pipe_fd[1]); + free(semaphore); + return NULL; + } + + lInitialCount--; + } + +#else + semaphore->sem = (winpr_sem_t*)malloc(sizeof(winpr_sem_t)); + + if (!semaphore->sem) + { + WLog_ERR(TAG, "failed to allocate semaphore memory"); + free(semaphore); + return NULL; + } + +#if defined __APPLE__ + + if (semaphore_create(mach_task_self(), semaphore->sem, SYNC_POLICY_FIFO, lMaximumCount) != + KERN_SUCCESS) +#else + if (sem_init(semaphore->sem, 0, lMaximumCount) == -1) +#endif + { + WLog_ERR(TAG, "failed to create semaphore"); + free(semaphore->sem); + free(semaphore); + return NULL; + } + +#endif + WINPR_HANDLE_SET_TYPE_AND_MODE(semaphore, HANDLE_TYPE_SEMAPHORE, WINPR_FD_READ); + handle = (HANDLE)semaphore; + return handle; +} + +HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, + LONG lMaximumCount, LPCSTR lpName) +{ + return CreateSemaphoreW(lpSemaphoreAttributes, lInitialCount, lMaximumCount, NULL); +} + +HANDLE OpenSemaphoreW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) +{ + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +HANDLE OpenSemaphoreA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) +{ + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_SEMAPHORE* semaphore = NULL; + + if (!winpr_Handle_GetInfo(hSemaphore, &Type, &Object)) + return FALSE; + + if (Type == HANDLE_TYPE_SEMAPHORE) + { + semaphore = (WINPR_SEMAPHORE*)Object; +#ifdef WINPR_PIPE_SEMAPHORE + + if (semaphore->pipe_fd[0] != -1) + { + while (lReleaseCount > 0) + { + if (write(semaphore->pipe_fd[1], "-", 1) != 1) + return FALSE; + + lReleaseCount--; + } + } + +#else + + while (lReleaseCount > 0) + { +#if defined __APPLE__ + semaphore_signal(*((winpr_sem_t*)semaphore->sem)); +#else + sem_post((winpr_sem_t*)semaphore->sem); +#endif + } + +#endif + return TRUE; + } + + WLog_ERR(TAG, "called on a handle that is not a semaphore"); + return FALSE; +} + +#endif diff --git a/winpr/libwinpr/synch/sleep.c b/winpr/libwinpr/synch/sleep.c new file mode 100644 index 0000000..be2f4c6 --- /dev/null +++ b/winpr/libwinpr/synch/sleep.c @@ -0,0 +1,148 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#include "../log.h" +#include "../thread/apc.h" +#include "../thread/thread.h" +#include "../synch/pollset.h" + +#define TAG WINPR_TAG("synch.sleep") + +#ifndef _WIN32 + +#include + +WINPR_PRAGMA_DIAG_PUSH +WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO + +#ifdef WINPR_HAVE_UNISTD_H +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 500 +#endif +#include +#endif + +WINPR_PRAGMA_DIAG_POP + +VOID Sleep(DWORD dwMilliseconds) +{ + usleep(dwMilliseconds * 1000); +} + +DWORD SleepEx(DWORD dwMilliseconds, BOOL bAlertable) +{ + WINPR_THREAD* thread = winpr_GetCurrentThread(); + WINPR_POLL_SET pollset; + int status = 0; + DWORD ret = WAIT_FAILED; + BOOL autoSignalled = 0; + + if (thread) + { + /* treat re-entrancy if a completion is calling us */ + if (thread->apc.treatingCompletions) + bAlertable = FALSE; + } + else + { + /* called from a non WinPR thread */ + bAlertable = FALSE; + } + + if (!bAlertable || !thread->apc.length) + { + usleep(dwMilliseconds * 1000); + return 0; + } + + if (!pollset_init(&pollset, thread->apc.length)) + { + WLog_ERR(TAG, "unable to initialize pollset"); + return WAIT_FAILED; + } + + if (!apc_collectFds(thread, &pollset, &autoSignalled)) + { + WLog_ERR(TAG, "unable to APC file descriptors"); + goto out; + } + + if (!autoSignalled) + { + /* we poll and wait only if no APC member is ready */ + status = pollset_poll(&pollset, dwMilliseconds); + if (status < 0) + { + WLog_ERR(TAG, "polling of apc fds failed"); + goto out; + } + } + + if (apc_executeCompletions(thread, &pollset, 0)) + { + ret = WAIT_IO_COMPLETION; + } + else + { + /* according to the spec return value is 0 see + * https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepex*/ + ret = 0; + } +out: + pollset_uninit(&pollset); + return ret; +} + +#endif + +VOID USleep(DWORD dwMicroseconds) +{ +#ifndef _WIN32 + usleep(dwMicroseconds); +#else + static LARGE_INTEGER freq = { 0 }; + LARGE_INTEGER t1 = { 0 }; + LARGE_INTEGER t2 = { 0 }; + + QueryPerformanceCounter(&t1); + + if (freq.QuadPart == 0) + { + QueryPerformanceFrequency(&freq); + } + + // in order to save cpu cyles we use Sleep() for the large share ... + if (dwMicroseconds >= 1000) + { + Sleep(dwMicroseconds / 1000); + } + // ... and busy loop until all the requested micro seconds have passed + do + { + QueryPerformanceCounter(&t2); + } while (((t2.QuadPart - t1.QuadPart) * 1000000) / freq.QuadPart < dwMicroseconds); +#endif +} diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h new file mode 100644 index 0000000..5a9f08c --- /dev/null +++ b/winpr/libwinpr/synch/synch.h @@ -0,0 +1,159 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_SYNCH_PRIVATE_H +#define WINPR_SYNCH_PRIVATE_H + +#include + +#include + +#include + +#include "../handle/handle.h" +#include "../thread/apc.h" +#include "event.h" + +#ifndef _WIN32 + +#define WINPR_PIPE_SEMAPHORE 1 + +#if defined __APPLE__ +#include +#include +#include +#include +#include +#include +#define winpr_sem_t semaphore_t +#else +#include +#include +#define winpr_sem_t sem_t +#endif + +struct winpr_mutex +{ + WINPR_HANDLE common; + char* name; + pthread_mutex_t mutex; +}; +typedef struct winpr_mutex WINPR_MUTEX; + +struct winpr_semaphore +{ + WINPR_HANDLE common; + + int pipe_fd[2]; + winpr_sem_t* sem; +}; +typedef struct winpr_semaphore WINPR_SEMAPHORE; + +#ifdef WINPR_HAVE_SYS_TIMERFD_H +#include +#include +#include +#include +#define TIMER_IMPL_TIMERFD + +#elif defined(WITH_POSIX_TIMER) +#include +#define TIMER_IMPL_POSIX + +#elif defined(__APPLE__) +#define TIMER_IMPL_DISPATCH +#include +#else +#warning missing timer implementation +#endif + +struct winpr_timer +{ + WINPR_HANDLE common; + + int fd; + BOOL bInit; + LONG lPeriod; + BOOL bManualReset; + PTIMERAPCROUTINE pfnCompletionRoutine; + LPVOID lpArgToCompletionRoutine; + +#ifdef TIMER_IMPL_TIMERFD + struct itimerspec timeout; +#endif + +#ifdef TIMER_IMPL_POSIX + WINPR_EVENT_IMPL event; + timer_t tid; + struct itimerspec timeout; +#endif + +#ifdef TIMER_IMPL_DISPATCH + WINPR_EVENT_IMPL event; + dispatch_queue_t queue; + dispatch_source_t source; + BOOL running; +#endif + char* name; + + WINPR_APC_ITEM apcItem; +}; +typedef struct winpr_timer WINPR_TIMER; + +typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER; + +struct winpr_timer_queue +{ + WINPR_HANDLE common; + + pthread_t thread; + pthread_attr_t attr; + pthread_mutex_t mutex; + pthread_cond_t cond; + pthread_mutex_t cond_mutex; + struct sched_param param; + + BOOL bCancelled; + WINPR_TIMER_QUEUE_TIMER* activeHead; + WINPR_TIMER_QUEUE_TIMER* inactiveHead; +}; +typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; + +struct winpr_timer_queue_timer +{ + WINPR_HANDLE common; + + ULONG Flags; + DWORD DueTime; + DWORD Period; + PVOID Parameter; + WAITORTIMERCALLBACK Callback; + + int FireCount; + + struct timespec StartTime; + struct timespec ExpirationTime; + + WINPR_TIMER_QUEUE* timerQueue; + WINPR_TIMER_QUEUE_TIMER* next; +}; + +#endif + +#endif /* WINPR_SYNCH_PRIVATE_H */ diff --git a/winpr/libwinpr/synch/test/CMakeLists.txt b/winpr/libwinpr/synch/test/CMakeLists.txt new file mode 100644 index 0000000..66d15c6 --- /dev/null +++ b/winpr/libwinpr/synch/test/CMakeLists.txt @@ -0,0 +1,41 @@ + +set(MODULE_NAME "TestSynch") +set(MODULE_PREFIX "TEST_SYNCH") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestSynchInit.c + TestSynchEvent.c + TestSynchMutex.c + TestSynchBarrier.c + TestSynchCritical.c + TestSynchSemaphore.c + TestSynchThread.c + # TestSynchMultipleThreads.c + TestSynchTimerQueue.c + TestSynchWaitableTimer.c + TestSynchWaitableTimerAPC.c + TestSynchAPC.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +if(FREEBSD) + include_directories(${EPOLLSHIM_INCLUDE_DIR}) +endif() + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/synch/test/TestSynchAPC.c b/winpr/libwinpr/synch/test/TestSynchAPC.c new file mode 100644 index 0000000..d6239d8 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchAPC.c @@ -0,0 +1,173 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * TestSyncAPC + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +typedef struct +{ + BOOL error; + BOOL called; +} UserApcArg; + +static void CALLBACK userApc(ULONG_PTR arg) +{ + UserApcArg* userArg = (UserApcArg*)arg; + userArg->called = TRUE; +} + +static DWORD WINAPI uncleanThread(LPVOID lpThreadParameter) +{ + /* this thread post an APC that will never get executed */ + UserApcArg* userArg = (UserApcArg*)lpThreadParameter; + if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)lpThreadParameter)) + { + userArg->error = TRUE; + return 1; + } + + return 0; +} + +static DWORD WINAPI cleanThread(LPVOID lpThreadParameter) +{ + Sleep(500); + + SleepEx(500, TRUE); + return 0; +} + +typedef struct +{ + HANDLE timer1; + DWORD timer1Calls; + HANDLE timer2; + DWORD timer2Calls; + BOOL endTest; +} UncleanCloseData; + +static VOID CALLBACK Timer1APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) +{ + UncleanCloseData* data = (UncleanCloseData*)lpArg; + data->timer1Calls++; + CloseHandle(data->timer2); + data->endTest = TRUE; +} + +static VOID CALLBACK Timer2APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) +{ + UncleanCloseData* data = (UncleanCloseData*)lpArg; + data->timer2Calls++; +} + +static DWORD /*WINAPI*/ closeHandleTest(LPVOID lpThreadParameter) +{ + LARGE_INTEGER dueTime; + UncleanCloseData* data = (UncleanCloseData*)lpThreadParameter; + data->endTest = FALSE; + + dueTime.QuadPart = -500; + if (!SetWaitableTimer(data->timer1, &dueTime, 0, Timer1APCProc, lpThreadParameter, FALSE)) + return 1; + + dueTime.QuadPart = -900; + if (!SetWaitableTimer(data->timer2, &dueTime, 0, Timer2APCProc, lpThreadParameter, FALSE)) + return 1; + + while (!data->endTest) + { + SleepEx(100, TRUE); + } + return 0; +} + +int TestSynchAPC(int argc, char* argv[]) +{ + HANDLE thread = NULL; + UserApcArg userApcArg; + + userApcArg.error = FALSE; + userApcArg.called = FALSE; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* first post an APC and check it is executed during a SleepEx */ + if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)&userApcArg)) + return 1; + + if (SleepEx(100, FALSE) != 0) + return 2; + + if (SleepEx(100, TRUE) != WAIT_IO_COMPLETION) + return 3; + + if (!userApcArg.called) + return 4; + + userApcArg.called = FALSE; + + /* test that the APC is cleaned up even when not called */ + thread = CreateThread(NULL, 0, uncleanThread, &userApcArg, 0, NULL); + if (!thread) + return 10; + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + if (userApcArg.called || userApcArg.error) + return 11; + + /* test a remote APC queuing */ + thread = CreateThread(NULL, 0, cleanThread, &userApcArg, 0, NULL); + if (!thread) + return 20; + + if (!QueueUserAPC((PAPCFUNC)userApc, thread, (ULONG_PTR)&userApcArg)) + return 21; + + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + if (!userApcArg.called) + return 22; + +#if 0 + /* test cleanup of timer completions */ + memset(&uncleanCloseData, 0, sizeof(uncleanCloseData)); + uncleanCloseData.timer1 = CreateWaitableTimerA(NULL, FALSE, NULL); + if (!uncleanCloseData.timer1) + return 31; + + uncleanCloseData.timer2 = CreateWaitableTimerA(NULL, FALSE, NULL); + if (!uncleanCloseData.timer2) + return 32; + + thread = CreateThread(NULL, 0, closeHandleTest, &uncleanCloseData, 0, NULL); + if (!thread) + return 33; + + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + if (uncleanCloseData.timer1Calls != 1 || uncleanCloseData.timer2Calls != 0) + return 34; + CloseHandle(uncleanCloseData.timer1); +#endif + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchBarrier.c b/winpr/libwinpr/synch/test/TestSynchBarrier.c new file mode 100644 index 0000000..b1c91c9 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchBarrier.c @@ -0,0 +1,257 @@ + +#include +#include +#include +#include +#include + +#include "../synch.h" + +static SYNCHRONIZATION_BARRIER gBarrier; +static HANDLE gStartEvent = NULL; +static LONG gErrorCount = 0; + +#define MAX_SLEEP_MS 22 + +struct test_params +{ + LONG threadCount; + LONG trueCount; + LONG falseCount; + DWORD loops; + DWORD flags; +}; + +static DWORD WINAPI test_synch_barrier_thread(LPVOID lpParam) +{ + BOOL status = FALSE; + struct test_params* p = (struct test_params*)lpParam; + + InterlockedIncrement(&p->threadCount); + + // printf("Thread #%03u entered.\n", tnum); + + /* wait for start event from main */ + if (WaitForSingleObject(gStartEvent, INFINITE) != WAIT_OBJECT_0) + { + InterlockedIncrement(&gErrorCount); + goto out; + } + + // printf("Thread #%03u unblocked.\n", tnum); + + for (DWORD i = 0; i < p->loops && gErrorCount == 0; i++) + { + /* simulate different execution times before the barrier */ + Sleep(1 + abs((rand() % MAX_SLEEP_MS))); + status = EnterSynchronizationBarrier(&gBarrier, p->flags); + + // printf("Thread #%03u status: %s\n", tnum, status ? "TRUE" : "FALSE"); + if (status) + InterlockedIncrement(&p->trueCount); + else + InterlockedIncrement(&p->falseCount); + } + +out: + // printf("Thread #%03u leaving.\n", tnum); + return 0; +} + +static BOOL TestSynchBarrierWithFlags(DWORD dwFlags, DWORD dwThreads, DWORD dwLoops) +{ + HANDLE* threads = NULL; + struct test_params p; + DWORD dwStatus = 0; + DWORD expectedTrueCount = 0; + DWORD expectedFalseCount = 0; + p.threadCount = 0; + p.trueCount = 0; + p.falseCount = 0; + p.loops = dwLoops; + p.flags = dwFlags; + expectedTrueCount = dwLoops; + expectedFalseCount = dwLoops * (dwThreads - 1); + printf("%s: >> Testing with flags 0x%08" PRIx32 ". Using %" PRIu32 + " threads performing %" PRIu32 " loops\n", + __func__, dwFlags, dwThreads, dwLoops); + + if (!(threads = calloc(dwThreads, sizeof(HANDLE)))) + { + printf("%s: error allocatin thread array memory\n", __func__); + return FALSE; + } + + if (!InitializeSynchronizationBarrier(&gBarrier, dwThreads, -1)) + { + printf("%s: InitializeSynchronizationBarrier failed. GetLastError() = 0x%08x", __func__, + GetLastError()); + free(threads); + DeleteSynchronizationBarrier(&gBarrier); + return FALSE; + } + + if (!(gStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("%s: CreateEvent failed with error 0x%08x", __func__, GetLastError()); + free(threads); + DeleteSynchronizationBarrier(&gBarrier); + return FALSE; + } + + DWORD i = 0; + for (; i < dwThreads; i++) + { + if (!(threads[i] = CreateThread(NULL, 0, test_synch_barrier_thread, &p, 0, NULL))) + { + printf("%s: CreateThread failed for thread #%" PRIu32 " with error 0x%08x\n", __func__, + i, GetLastError()); + InterlockedIncrement(&gErrorCount); + break; + } + } + + if (i > 0) + { + if (!SetEvent(gStartEvent)) + { + printf("%s: SetEvent(gStartEvent) failed with error = 0x%08x)\n", __func__, + GetLastError()); + InterlockedIncrement(&gErrorCount); + } + + while (i--) + { + if (WAIT_OBJECT_0 != (dwStatus = WaitForSingleObject(threads[i], INFINITE))) + { + printf("%s: WaitForSingleObject(thread[%" PRIu32 "] unexpectedly returned %" PRIu32 + " (error = 0x%08x)\n", + __func__, i, dwStatus, GetLastError()); + InterlockedIncrement(&gErrorCount); + } + + if (!CloseHandle(threads[i])) + { + printf("%s: CloseHandle(thread[%" PRIu32 "]) failed with error = 0x%08x)\n", + __func__, i, GetLastError()); + InterlockedIncrement(&gErrorCount); + } + } + } + + free(threads); + + if (!CloseHandle(gStartEvent)) + { + printf("%s: CloseHandle(gStartEvent) failed with error = 0x%08x)\n", __func__, + GetLastError()); + InterlockedIncrement(&gErrorCount); + } + + DeleteSynchronizationBarrier(&gBarrier); + + if (p.threadCount != (INT64)dwThreads) + InterlockedIncrement(&gErrorCount); + + if (p.trueCount != (INT64)expectedTrueCount) + InterlockedIncrement(&gErrorCount); + + if (p.falseCount != (INT64)expectedFalseCount) + InterlockedIncrement(&gErrorCount); + + printf("%s: error count: %" PRId32 "\n", __func__, gErrorCount); + printf("%s: thread count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.threadCount, + dwThreads); + printf("%s: true count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.trueCount, + expectedTrueCount); + printf("%s: false count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.falseCount, + expectedFalseCount); + + if (gErrorCount > 0) + { + printf("%s: Error test failed with %" PRId32 " reported errors\n", __func__, gErrorCount); + return FALSE; + } + + return TRUE; +} + +int TestSynchBarrier(int argc, char* argv[]) +{ + SYSTEM_INFO sysinfo; + DWORD dwMaxThreads = 0; + DWORD dwMinThreads = 0; + DWORD dwNumLoops = 10; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + GetNativeSystemInfo(&sysinfo); + printf("%s: Number of processors: %" PRIu32 "\n", __func__, sysinfo.dwNumberOfProcessors); + dwMinThreads = sysinfo.dwNumberOfProcessors; + dwMaxThreads = sysinfo.dwNumberOfProcessors * 4; + + if (dwMaxThreads > 32) + dwMaxThreads = 32; + + /* Test invalid parameters */ + if (InitializeSynchronizationBarrier(&gBarrier, 0, -1)) + { + fprintf( + stderr, + "%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = 0\n", + __func__); + return -1; + } + + if (InitializeSynchronizationBarrier(&gBarrier, -1, -1)) + { + fprintf( + stderr, + "%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = -1\n", + __func__); + return -1; + } + + if (InitializeSynchronizationBarrier(&gBarrier, 1, -2)) + { + fprintf( + stderr, + "%s: InitializeSynchronizationBarrier unecpectedly succeeded with lSpinCount = -2\n", + __func__); + return -1; + } + + /* Functional tests */ + + if (!TestSynchBarrierWithFlags(0, dwMaxThreads, dwNumLoops)) + { + fprintf(stderr, + "%s: TestSynchBarrierWithFlags(0) unecpectedly succeeded with lTotalThreads = -1\n", + __func__); + return -1; + } + + if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY, dwMinThreads, + dwNumLoops)) + { + fprintf(stderr, + "%s: TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY) " + "unecpectedly succeeded with lTotalThreads = -1\n", + __func__); + return -1; + } + + if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY, dwMaxThreads, + dwNumLoops)) + { + fprintf(stderr, + "%s: TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) " + "unecpectedly succeeded with lTotalThreads = -1\n", + __func__); + return -1; + } + + printf("%s: Test successfully completed\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchCritical.c b/winpr/libwinpr/synch/test/TestSynchCritical.c new file mode 100644 index 0000000..9d56356 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchCritical.c @@ -0,0 +1,363 @@ + +#include +#include +#include +#include +#include +#include +#include + +#define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 50 +#define TEST_SYNC_CRITICAL_TEST1_RUNS 4 + +static CRITICAL_SECTION critical; +static LONG gTestValueVulnerable = 0; +static LONG gTestValueSerialized = 0; + +static BOOL TestSynchCritical_TriggerAndCheckRaceCondition(HANDLE OwningThread, LONG RecursionCount) +{ + /* if called unprotected this will hopefully trigger a race condition ... */ + gTestValueVulnerable++; + + if (critical.OwningThread != OwningThread) + { + printf("CriticalSection failure: OwningThread is invalid\n"); + return FALSE; + } + if (critical.RecursionCount != RecursionCount) + { + printf("CriticalSection failure: RecursionCount is invalid\n"); + return FALSE; + } + + /* ... which we try to detect using the serialized counter */ + if (gTestValueVulnerable != InterlockedIncrement(&gTestValueSerialized)) + { + printf("CriticalSection failure: Data corruption detected\n"); + return FALSE; + } + + return TRUE; +} + +/* this thread function shall increment the global dwTestValue until the PBOOL passsed in arg is + * FALSE */ +static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg) +{ + int rc = 0; + HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId(); + + PBOOL pbContinueRunning = (PBOOL)arg; + + while (*pbContinueRunning) + { + EnterCriticalSection(&critical); + + rc = 1; + + if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc)) + return 1; + + /* add some random recursion level */ + int j = rand() % 5; + for (int i = 0; i < j; i++) + { + if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++)) + return 2; + EnterCriticalSection(&critical); + } + for (int i = 0; i < j; i++) + { + if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--)) + return 2; + LeaveCriticalSection(&critical); + } + + if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc)) + return 3; + + LeaveCriticalSection(&critical); + } + + return 0; +} + +/* this thread function tries to call TryEnterCriticalSection while the main thread holds the lock + */ +static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg) +{ + WINPR_UNUSED(arg); + if (TryEnterCriticalSection(&critical) == TRUE) + { + LeaveCriticalSection(&critical); + return 1; + } + return 0; +} + +static DWORD WINAPI TestSynchCritical_Main(LPVOID arg) +{ + SYSTEM_INFO sysinfo; + DWORD dwPreviousSpinCount = 0; + DWORD dwSpinCount = 0; + DWORD dwSpinCountExpected = 0; + HANDLE hMainThread = NULL; + HANDLE* hThreads = NULL; + HANDLE hThread = NULL; + DWORD dwThreadCount = 0; + DWORD dwThreadExitCode = 0; + BOOL bTest1Running = 0; + + PBOOL pbThreadTerminated = (PBOOL)arg; + + GetNativeSystemInfo(&sysinfo); + + hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId(); + + /** + * Test SpinCount in SetCriticalSectionSpinCount, InitializeCriticalSectionEx and + * InitializeCriticalSectionAndSpinCount SpinCount must be forced to be zero on on uniprocessor + * systems and on systems where WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT is defined + */ + + dwSpinCount = 100; + InitializeCriticalSectionEx(&critical, dwSpinCount, 0); + while (--dwSpinCount) + { + dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount); + dwSpinCountExpected = 0; +#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT) + if (sysinfo.dwNumberOfProcessors > 1) + dwSpinCountExpected = dwSpinCount + 1; +#endif + if (dwPreviousSpinCount != dwSpinCountExpected) + { + printf("CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32 + " (expected: %" PRIu32 ")\n", + dwPreviousSpinCount, dwSpinCountExpected); + goto fail; + } + + DeleteCriticalSection(&critical); + + if (dwSpinCount % 2 == 0) + InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount); + else + InitializeCriticalSectionEx(&critical, dwSpinCount, 0); + } + DeleteCriticalSection(&critical); + + /** + * Test single-threaded recursive + * TryEnterCriticalSection/EnterCriticalSection/LeaveCriticalSection + * + */ + + InitializeCriticalSection(&critical); + + int i = 0; + for (; i < 10; i++) + { + if (critical.RecursionCount != i) + { + printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n", + critical.RecursionCount, i); + goto fail; + } + if (i % 2 == 0) + { + EnterCriticalSection(&critical); + } + else + { + if (TryEnterCriticalSection(&critical) == FALSE) + { + printf("CriticalSection failure: TryEnterCriticalSection failed where it should " + "not.\n"); + goto fail; + } + } + if (critical.OwningThread != hMainThread) + { + printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n", + i); + goto fail; + } + } + while (--i >= 0) + { + LeaveCriticalSection(&critical); + if (critical.RecursionCount != i) + { + printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n", + critical.RecursionCount, i); + goto fail; + } + if (critical.OwningThread != (HANDLE)(i ? hMainThread : NULL)) + { + printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n", + i); + goto fail; + } + } + DeleteCriticalSection(&critical); + + /** + * Test using multiple threads modifying the same value + */ + + dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2; + + hThreads = (HANDLE*)calloc(dwThreadCount, sizeof(HANDLE)); + if (!hThreads) + { + printf("Problem allocating memory\n"); + goto fail; + } + + for (int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++) + { + dwSpinCount = j * 100; + InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount); + + gTestValueVulnerable = 0; + gTestValueSerialized = 0; + + /* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */ + bTest1Running = TRUE; + for (int i = 0; i < (int)dwThreadCount; i++) + { + if (!(hThreads[i] = + CreateThread(NULL, 0, TestSynchCritical_Test1, &bTest1Running, 0, NULL))) + { + printf("CriticalSection failure: Failed to create test_1 thread #%d\n", i); + goto fail; + } + } + + /* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */ + Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS); + bTest1Running = FALSE; + + for (int i = 0; i < (int)dwThreadCount; i++) + { + if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0) + { + printf("CriticalSection failure: Failed to wait for thread #%d\n", i); + goto fail; + } + GetExitCodeThread(hThreads[i], &dwThreadExitCode); + if (dwThreadExitCode != 0) + { + printf("CriticalSection failure: Thread #%d returned error code %" PRIu32 "\n", i, + dwThreadExitCode); + goto fail; + } + CloseHandle(hThreads[i]); + } + + if (gTestValueVulnerable != gTestValueSerialized) + { + printf("CriticalSection failure: unexpected test value %" PRId32 " (expected %" PRId32 + ")\n", + gTestValueVulnerable, gTestValueSerialized); + goto fail; + } + + DeleteCriticalSection(&critical); + } + + free(hThreads); + + /** + * TryEnterCriticalSection in thread must fail if we hold the lock in the main thread + */ + + InitializeCriticalSection(&critical); + + if (TryEnterCriticalSection(&critical) == FALSE) + { + printf("CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n"); + goto fail; + } + /* This thread tries to call TryEnterCriticalSection which must fail */ + if (!(hThread = CreateThread(NULL, 0, TestSynchCritical_Test2, NULL, 0, NULL))) + { + printf("CriticalSection failure: Failed to create test_2 thread\n"); + goto fail; + } + if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0) + { + printf("CriticalSection failure: Failed to wait for thread\n"); + goto fail; + } + GetExitCodeThread(hThread, &dwThreadExitCode); + if (dwThreadExitCode != 0) + { + printf("CriticalSection failure: Thread returned error code %" PRIu32 "\n", + dwThreadExitCode); + goto fail; + } + CloseHandle(hThread); + + *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */ + return 0; + +fail: + *pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */ + return 1; +} + +int TestSynchCritical(int argc, char* argv[]) +{ + BOOL bThreadTerminated = FALSE; + HANDLE hThread = NULL; + DWORD dwThreadExitCode = 0; + DWORD dwDeadLockDetectionTimeMs = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + dwDeadLockDetectionTimeMs = + 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS; + + printf("Deadlock will be assumed after %" PRIu32 " ms.\n", dwDeadLockDetectionTimeMs); + + if (!(hThread = CreateThread(NULL, 0, TestSynchCritical_Main, &bThreadTerminated, 0, NULL))) + { + printf("CriticalSection failure: Failed to create main thread\n"); + return -1; + } + + /** + * We have to be able to detect dead locks in this test. + * At the time of writing winpr's WaitForSingleObject has not implemented timeout for thread + * wait + * + * Workaround checking the value of bThreadTerminated which is passed in the thread arg + */ + + for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10) + { + if (bThreadTerminated) + break; + + Sleep(10); + } + + if (!bThreadTerminated) + { + printf("CriticalSection failure: Possible dead lock detected\n"); + return -1; + } + + GetExitCodeThread(hThread, &dwThreadExitCode); + CloseHandle(hThread); + + if (dwThreadExitCode != 0) + { + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchEvent.c b/winpr/libwinpr/synch/test/TestSynchEvent.c new file mode 100644 index 0000000..083282c --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchEvent.c @@ -0,0 +1,94 @@ + +#include +#include + +int TestSynchEvent(int argc, char* argv[]) +{ + HANDLE event = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + if (ResetEvent(NULL)) + { + printf("ResetEvent(NULL) unexpectedly succeeded\n"); + return -1; + } + + if (SetEvent(NULL)) + { + printf("SetEvent(NULL) unexpectedly succeeded\n"); + return -1; + } + + event = CreateEvent(NULL, TRUE, TRUE, NULL); + + if (!event) + { + printf("CreateEvent failure\n"); + return -1; + } + + if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForSingleObject failure 1\n"); + return -1; + } + + if (!ResetEvent(event)) + { + printf("ResetEvent failure with signaled event object\n"); + return -1; + } + + if (WaitForSingleObject(event, 0) != WAIT_TIMEOUT) + { + printf("WaitForSingleObject failure 2\n"); + return -1; + } + + if (!ResetEvent(event)) + { + /* Note: ResetEvent must also succeed if event is currently nonsignaled */ + printf("ResetEvent failure with nonsignaled event object\n"); + return -1; + } + + if (!SetEvent(event)) + { + printf("SetEvent failure with nonsignaled event object\n"); + return -1; + } + + if (WaitForSingleObject(event, 0) != WAIT_OBJECT_0) + { + printf("WaitForSingleObject failure 3\n"); + return -1; + } + + for (int i = 0; i < 10000; i++) + { + if (!SetEvent(event)) + { + printf("SetEvent failure with signaled event object (i = %d)\n", i); + return -1; + } + } + + if (!ResetEvent(event)) + { + printf("ResetEvent failure after multiple SetEvent calls\n"); + return -1; + } + + /* Independent of the amount of the previous SetEvent calls, a single + ResetEvent must be sufficient to get into nonsignaled state */ + + if (WaitForSingleObject(event, 0) != WAIT_TIMEOUT) + { + printf("WaitForSingleObject failure 4\n"); + return -1; + } + + CloseHandle(event); + + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchInit.c b/winpr/libwinpr/synch/test/TestSynchInit.c new file mode 100644 index 0000000..20da415 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchInit.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include + +#define TEST_NUM_THREADS 100 +#define TEST_NUM_FAILURES 10 + +static INIT_ONCE initOnceTest = INIT_ONCE_STATIC_INIT; + +static HANDLE hStartEvent = NULL; +static LONG* pErrors = NULL; +static LONG* pTestThreadFunctionCalls = NULL; +static LONG* pTestOnceFunctionCalls = NULL; +static LONG* pInitOnceExecuteOnceCalls = NULL; + +static BOOL CALLBACK TestOnceFunction(PINIT_ONCE once, PVOID param, PVOID* context) +{ + LONG calls = InterlockedIncrement(pTestOnceFunctionCalls) - 1; + + WINPR_UNUSED(once); + WINPR_UNUSED(param); + WINPR_UNUSED(context); + + /* simulate execution time */ + Sleep(30 + rand() % 40); + + if (calls < TEST_NUM_FAILURES) + { + /* simulated error */ + return FALSE; + } + if (calls == TEST_NUM_FAILURES) + { + return TRUE; + } + fprintf(stderr, "%s: error: called again after success\n", __func__); + InterlockedIncrement(pErrors); + return FALSE; +} + +static DWORD WINAPI TestThreadFunction(LPVOID lpParam) +{ + LONG calls = 0; + BOOL ok = 0; + + WINPR_UNUSED(lpParam); + + InterlockedIncrement(pTestThreadFunctionCalls); + if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: error: failed to wait for start event\n", __func__); + InterlockedIncrement(pErrors); + return 0; + } + + ok = InitOnceExecuteOnce(&initOnceTest, TestOnceFunction, NULL, NULL); + calls = InterlockedIncrement(pInitOnceExecuteOnceCalls); + if (!ok && calls > TEST_NUM_FAILURES) + { + fprintf(stderr, "%s: InitOnceExecuteOnce failed unexpectedly\n", __func__); + InterlockedIncrement(pErrors); + } + return 0; +} + +int TestSynchInit(int argc, char* argv[]) +{ + HANDLE hThreads[TEST_NUM_THREADS]; + DWORD dwCreatedThreads = 0; + BOOL result = FALSE; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + pErrors = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + pTestThreadFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + pTestOnceFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + pInitOnceExecuteOnceCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG)); + + if (!pErrors || !pTestThreadFunctionCalls || !pTestOnceFunctionCalls || + !pInitOnceExecuteOnceCalls) + { + fprintf(stderr, "error: _aligned_malloc failed\n"); + goto out; + } + + *pErrors = 0; + *pTestThreadFunctionCalls = 0; + *pTestOnceFunctionCalls = 0; + *pInitOnceExecuteOnceCalls = 0; + + if (!(hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + fprintf(stderr, "error creating start event\n"); + InterlockedIncrement(pErrors); + goto out; + } + + for (DWORD i = 0; i < TEST_NUM_THREADS; i++) + { + if (!(hThreads[i] = CreateThread(NULL, 0, TestThreadFunction, NULL, 0, NULL))) + { + fprintf(stderr, "error creating thread #%" PRIu32 "\n", i); + InterlockedIncrement(pErrors); + goto out; + } + dwCreatedThreads++; + } + + Sleep(100); + SetEvent(hStartEvent); + + for (DWORD i = 0; i < dwCreatedThreads; i++) + { + if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "error: error waiting for thread #%" PRIu32 "\n", i); + InterlockedIncrement(pErrors); + goto out; + } + } + + if (*pErrors == 0 && *pTestThreadFunctionCalls == TEST_NUM_THREADS && + *pInitOnceExecuteOnceCalls == TEST_NUM_THREADS && + *pTestOnceFunctionCalls == TEST_NUM_FAILURES + 1) + { + result = TRUE; + } + +out: + fprintf(stderr, "Test result: %s\n", result ? "OK" : "ERROR"); + fprintf(stderr, "Error count: %" PRId32 "\n", pErrors ? *pErrors : -1); + fprintf(stderr, "Threads created: %" PRIu32 "\n", dwCreatedThreads); + fprintf(stderr, "TestThreadFunctionCalls: %" PRId32 "\n", + pTestThreadFunctionCalls ? *pTestThreadFunctionCalls : -1); + fprintf(stderr, "InitOnceExecuteOnceCalls: %" PRId32 "\n", + pInitOnceExecuteOnceCalls ? *pInitOnceExecuteOnceCalls : -1); + fprintf(stderr, "TestOnceFunctionCalls: %" PRId32 "\n", + pTestOnceFunctionCalls ? *pTestOnceFunctionCalls : -1); + + winpr_aligned_free(pErrors); + winpr_aligned_free(pTestThreadFunctionCalls); + winpr_aligned_free(pTestOnceFunctionCalls); + winpr_aligned_free(pInitOnceExecuteOnceCalls); + + CloseHandle(hStartEvent); + + for (DWORD i = 0; i < dwCreatedThreads; i++) + { + CloseHandle(hThreads[i]); + } + + return (result ? 0 : 1); +} diff --git a/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c b/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c new file mode 100644 index 0000000..7218b64 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c @@ -0,0 +1,243 @@ + +#include + +#include +#include +#include + +#define THREADS 8 + +static DWORD WINAPI test_thread(LPVOID arg) +{ + long timeout = 50 + (rand() % 100); + WINPR_UNUSED(arg); + Sleep(timeout); + ExitThread(0); + return 0; +} + +static int start_threads(size_t count, HANDLE* threads) +{ + for (size_t i = 0; i < count; i++) + { + threads[i] = CreateThread(NULL, 0, test_thread, NULL, CREATE_SUSPENDED, NULL); + + if (!threads[i]) + { + fprintf(stderr, "%s: CreateThread [%" PRIuz "] failure\n", __func__, i); + return -1; + } + } + + for (size_t i = 0; i < count; i++) + ResumeThread(threads[i]); + return 0; +} + +static int close_threads(DWORD count, HANDLE* threads) +{ + int rc = 0; + + for (DWORD i = 0; i < count; i++) + { + if (!threads[i]) + continue; + + if (!CloseHandle(threads[i])) + { + fprintf(stderr, "%s: CloseHandle [%" PRIu32 "] failure\n", __func__, i); + rc = -1; + } + threads[i] = NULL; + } + + return rc; +} + +static BOOL TestWaitForAll(void) +{ + BOOL rc = FALSE; + HANDLE threads[THREADS] = { 0 }; + /* WaitForAll, timeout */ + if (start_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: start_threads failed\n", __func__); + goto fail; + } + + const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, 10); + if (ret != WAIT_TIMEOUT) + { + fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, timeout 10 failed, ret=%d\n", + __func__, ret); + goto fail; + } + + if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__); + goto fail; + } + + rc = TRUE; +fail: + if (close_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: close_threads failed\n", __func__); + return FALSE; + } + + return rc; +} + +static BOOL TestWaitOne(void) +{ + BOOL rc = FALSE; + HANDLE threads[THREADS] = { 0 }; + /* WaitForAll, timeout */ + if (start_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: start_threads failed\n", __func__); + goto fail; + } + + const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, INFINITE); + if (ret > (WAIT_OBJECT_0 + ARRAYSIZE(threads))) + { + fprintf(stderr, "%s: WaitForMultipleObjects INFINITE failed\n", __func__); + goto fail; + } + + if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__); + goto fail; + } + + rc = TRUE; +fail: + if (close_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: close_threads failed\n", __func__); + return FALSE; + } + + return rc; +} + +static BOOL TestWaitOneTimeout(void) +{ + BOOL rc = FALSE; + HANDLE threads[THREADS] = { 0 }; + /* WaitForAll, timeout */ + if (start_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: start_threads failed\n", __func__); + goto fail; + } + + const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, 1); + if (ret != WAIT_TIMEOUT) + { + fprintf(stderr, "%s: WaitForMultipleObjects timeout 50 failed, ret=%d\n", __func__, ret); + goto fail; + } + + if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__); + goto fail; + } + rc = TRUE; +fail: + if (close_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: close_threads failed\n", __func__); + return FALSE; + } + + return rc; +} + +static BOOL TestWaitOneTimeoutMultijoin(void) +{ + BOOL rc = FALSE; + HANDLE threads[THREADS] = { 0 }; + /* WaitForAll, timeout */ + if (start_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: start_threads failed\n", __func__); + goto fail; + } + + for (size_t i = 0; i < ARRAYSIZE(threads); i++) + { + const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, 0); + if (ret != WAIT_TIMEOUT) + { + fprintf(stderr, "%s: WaitForMultipleObjects timeout 0 failed, ret=%d\n", __func__, ret); + goto fail; + } + } + + if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__); + goto fail; + } + + rc = TRUE; +fail: + if (close_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: close_threads failed\n", __func__); + return FALSE; + } + + return rc; +} + +static BOOL TestDetach(void) +{ + BOOL rc = FALSE; + HANDLE threads[THREADS] = { 0 }; + /* WaitForAll, timeout */ + if (start_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: start_threads failed\n", __func__); + goto fail; + } + + rc = TRUE; +fail: + if (close_threads(ARRAYSIZE(threads), threads)) + { + fprintf(stderr, "%s: close_threads failed\n", __func__); + return FALSE; + } + + return rc; +} + +int TestSynchMultipleThreads(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!TestWaitForAll()) + return -1; + + if (!TestWaitOne()) + return -2; + + if (!TestWaitOneTimeout()) + return -3; + + if (!TestWaitOneTimeoutMultijoin()) + return -4; + + if (!TestDetach()) + return -5; + + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchMutex.c b/winpr/libwinpr/synch/test/TestSynchMutex.c new file mode 100644 index 0000000..296c371 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchMutex.c @@ -0,0 +1,258 @@ + +#include +#include +#include + +static BOOL test_mutex_basic(void) +{ + HANDLE mutex = NULL; + DWORD rc = 0; + + if (!(mutex = CreateMutex(NULL, FALSE, NULL))) + { + printf("%s: CreateMutex failed\n", __func__); + return FALSE; + } + + rc = WaitForSingleObject(mutex, INFINITE); + + if (rc != WAIT_OBJECT_0) + { + printf("%s: WaitForSingleObject on mutex failed with %" PRIu32 "\n", __func__, rc); + return FALSE; + } + + if (!ReleaseMutex(mutex)) + { + printf("%s: ReleaseMutex failed\n", __func__); + return FALSE; + } + + if (ReleaseMutex(mutex)) + { + printf("%s: ReleaseMutex unexpectedly succeeded on released mutex\n", __func__); + return FALSE; + } + + if (!CloseHandle(mutex)) + { + printf("%s: CloseHandle on mutex failed\n", __func__); + return FALSE; + } + + return TRUE; +} + +static BOOL test_mutex_recursive(void) +{ + HANDLE mutex = NULL; + DWORD rc = 0; + DWORD cnt = 50; + + if (!(mutex = CreateMutex(NULL, TRUE, NULL))) + { + printf("%s: CreateMutex failed\n", __func__); + return FALSE; + } + + for (UINT32 i = 0; i < cnt; i++) + { + rc = WaitForSingleObject(mutex, INFINITE); + + if (rc != WAIT_OBJECT_0) + { + printf("%s: WaitForSingleObject #%" PRIu32 " on mutex failed with %" PRIu32 "\n", + __func__, i, rc); + return FALSE; + } + } + + for (UINT32 i = 0; i < cnt; i++) + { + if (!ReleaseMutex(mutex)) + { + printf("%s: ReleaseMutex #%" PRIu32 " failed\n", __func__, i); + return FALSE; + } + } + + if (!ReleaseMutex(mutex)) + { + /* Note: The mutex was initially owned ! */ + printf("%s: Final ReleaseMutex failed\n", __func__); + return FALSE; + } + + if (ReleaseMutex(mutex)) + { + printf("%s: ReleaseMutex unexpectedly succeeded on released mutex\n", __func__); + return FALSE; + } + + if (!CloseHandle(mutex)) + { + printf("%s: CloseHandle on mutex failed\n", __func__); + return FALSE; + } + + return TRUE; +} + +static HANDLE thread1_mutex1 = NULL; +static HANDLE thread1_mutex2 = NULL; +static BOOL thread1_failed = TRUE; + +static DWORD WINAPI test_mutex_thread1(LPVOID lpParam) +{ + HANDLE hStartEvent = (HANDLE)lpParam; + DWORD rc = 0; + + if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: failed to wait for start event\n", __func__); + return 0; + } + + /** + * at this point: + * thread1_mutex1 is expected to be locked + * thread1_mutex2 is expected to be unlocked + * defined task: + * try to lock thread1_mutex1 (expected to fail) + * lock and unlock thread1_mutex2 (expected to work) + */ + rc = WaitForSingleObject(thread1_mutex1, 10); + + if (rc != WAIT_TIMEOUT) + { + fprintf(stderr, + "%s: WaitForSingleObject on thread1_mutex1 unexpectedly returned %" PRIu32 + " instead of WAIT_TIMEOUT (%u)\n", + __func__, rc, WAIT_TIMEOUT); + return 0; + } + + rc = WaitForSingleObject(thread1_mutex2, 10); + + if (rc != WAIT_OBJECT_0) + { + fprintf(stderr, + "%s: WaitForSingleObject on thread1_mutex2 unexpectedly returned %" PRIu32 + " instead of WAIT_OBJECT_0\n", + __func__, rc); + return 0; + } + + if (!ReleaseMutex(thread1_mutex2)) + { + fprintf(stderr, "%s: ReleaseMutex failed on thread1_mutex2\n", __func__); + return 0; + } + + thread1_failed = FALSE; + return 0; +} + +static BOOL test_mutex_threading(void) +{ + HANDLE hThread = NULL; + HANDLE hStartEvent = NULL; + + if (!(thread1_mutex1 = CreateMutex(NULL, TRUE, NULL))) + { + printf("%s: CreateMutex thread1_mutex1 failed\n", __func__); + goto fail; + } + + if (!(thread1_mutex2 = CreateMutex(NULL, FALSE, NULL))) + { + printf("%s: CreateMutex thread1_mutex2 failed\n", __func__); + goto fail; + } + + if (!(hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + fprintf(stderr, "%s: error creating start event\n", __func__); + goto fail; + } + + thread1_failed = TRUE; + + if (!(hThread = CreateThread(NULL, 0, test_mutex_thread1, (LPVOID)hStartEvent, 0, NULL))) + { + fprintf(stderr, "%s: error creating test_mutex_thread_1\n", __func__); + goto fail; + } + + Sleep(100); + + if (!thread1_failed) + { + fprintf(stderr, "%s: thread1 premature success\n", __func__); + goto fail; + } + + SetEvent(hStartEvent); + + if (WaitForSingleObject(hThread, 2000) != WAIT_OBJECT_0) + { + fprintf(stderr, "%s: thread1 premature success\n", __func__); + goto fail; + } + + if (thread1_failed) + { + fprintf(stderr, "%s: thread1 has not reported success\n", __func__); + goto fail; + } + + /** + * - thread1 must not have succeeded to lock thread1_mutex1 + * - thread1 must have locked and unlocked thread1_mutex2 + */ + + if (!ReleaseMutex(thread1_mutex1)) + { + printf("%s: ReleaseMutex unexpectedly failed on thread1_mutex1\n", __func__); + goto fail; + } + + if (ReleaseMutex(thread1_mutex2)) + { + printf("%s: ReleaseMutex unexpectedly succeeded on thread1_mutex2\n", __func__); + goto fail; + } + + CloseHandle(hThread); + CloseHandle(hStartEvent); + CloseHandle(thread1_mutex1); + CloseHandle(thread1_mutex2); + return TRUE; +fail: + ReleaseMutex(thread1_mutex1); + ReleaseMutex(thread1_mutex2); + CloseHandle(thread1_mutex1); + CloseHandle(thread1_mutex2); + CloseHandle(hStartEvent); + CloseHandle(hThread); + return FALSE; +} + +int TestSynchMutex(int argc, char* argv[]) +{ + int rc = 0; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_mutex_basic()) + rc += 1; + + if (!test_mutex_recursive()) + rc += 2; + + if (!test_mutex_threading()) + rc += 4; + + printf("TestSynchMutex result %d\n", rc); + return rc; +} diff --git a/winpr/libwinpr/synch/test/TestSynchSemaphore.c b/winpr/libwinpr/synch/test/TestSynchSemaphore.c new file mode 100644 index 0000000..d44446a --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchSemaphore.c @@ -0,0 +1,21 @@ + +#include +#include + +int TestSynchSemaphore(int argc, char* argv[]) +{ + HANDLE semaphore = NULL; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + semaphore = CreateSemaphore(NULL, 0, 1, NULL); + + if (!semaphore) + { + printf("CreateSemaphore failure\n"); + return -1; + } + + CloseHandle(semaphore); + + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchThread.c b/winpr/libwinpr/synch/test/TestSynchThread.c new file mode 100644 index 0000000..58f7cb0 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchThread.c @@ -0,0 +1,131 @@ + +#include +#include +#include + +static DWORD WINAPI test_thread(LPVOID arg) +{ + WINPR_UNUSED(arg); + Sleep(100); + ExitThread(0); + return 0; +} + +int TestSynchThread(int argc, char* argv[]) +{ + DWORD rc = 0; + HANDLE thread = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + thread = CreateThread(NULL, 0, test_thread, NULL, 0, NULL); + + if (!thread) + { + printf("CreateThread failure\n"); + return -1; + } + + /* TryJoin should now fail. */ + rc = WaitForSingleObject(thread, 0); + + if (WAIT_TIMEOUT != rc) + { + printf("Timed WaitForSingleObject on running thread failed with %" PRIu32 "\n", rc); + return -3; + } + + /* Join the thread */ + rc = WaitForSingleObject(thread, INFINITE); + + if (WAIT_OBJECT_0 != rc) + { + printf("WaitForSingleObject on thread failed with %" PRIu32 "\n", rc); + return -2; + } + + /* TimedJoin should now succeed. */ + rc = WaitForSingleObject(thread, 0); + + if (WAIT_OBJECT_0 != rc) + { + printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc); + return -5; + } + + /* check that WaitForSingleObject works multiple times on a terminated thread */ + for (int i = 0; i < 4; i++) + { + rc = WaitForSingleObject(thread, 0); + if (WAIT_OBJECT_0 != rc) + { + printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc); + return -6; + } + } + + if (!CloseHandle(thread)) + { + printf("CloseHandle failed!"); + return -1; + } + + thread = CreateThread(NULL, 0, test_thread, NULL, 0, NULL); + + if (!thread) + { + printf("CreateThread failure\n"); + return -1; + } + + /* TryJoin should now fail. */ + rc = WaitForSingleObject(thread, 10); + + if (WAIT_TIMEOUT != rc) + { + printf("Timed WaitForSingleObject on running thread failed with %" PRIu32 "\n", rc); + return -3; + } + + /* Join the thread */ + rc = WaitForSingleObject(thread, INFINITE); + + if (WAIT_OBJECT_0 != rc) + { + printf("WaitForSingleObject on thread failed with %" PRIu32 "\n", rc); + return -2; + } + + /* TimedJoin should now succeed. */ + rc = WaitForSingleObject(thread, 0); + + if (WAIT_OBJECT_0 != rc) + { + printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc); + return -5; + } + + if (!CloseHandle(thread)) + { + printf("CloseHandle failed!"); + return -1; + } + + /* Thread detach test */ + thread = CreateThread(NULL, 0, test_thread, NULL, 0, NULL); + + if (!thread) + { + printf("CreateThread failure\n"); + return -1; + } + + if (!CloseHandle(thread)) + { + printf("CloseHandle failed!"); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c new file mode 100644 index 0000000..08cc957 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -0,0 +1,125 @@ + +#include +#include +#include +#include + +#define FIRE_COUNT 5 +#define TIMER_COUNT 5 + +struct apc_data +{ + DWORD TimerId; + DWORD FireCount; + DWORD DueTime; + DWORD Period; + UINT32 StartTime; + DWORD MaxFireCount; + HANDLE CompletionEvent; +}; +typedef struct apc_data APC_DATA; + +static VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) +{ + UINT32 TimerTime = 0; + APC_DATA* apcData = NULL; + UINT32 expectedTime = 0; + UINT32 CurrentTime = GetTickCount(); + + WINPR_UNUSED(TimerOrWaitFired); + + if (!lpParam) + return; + + apcData = (APC_DATA*)lpParam; + + TimerTime = CurrentTime - apcData->StartTime; + expectedTime = apcData->DueTime + (apcData->Period * apcData->FireCount); + + apcData->FireCount++; + + printf("TimerRoutine: TimerId: %" PRIu32 " FireCount: %" PRIu32 " ActualTime: %" PRIu32 + " ExpectedTime: %" PRIu32 " Discrepancy: %" PRIu32 "\n", + apcData->TimerId, apcData->FireCount, TimerTime, expectedTime, TimerTime - expectedTime); + + Sleep(11); + + if (apcData->FireCount == apcData->MaxFireCount) + { + SetEvent(apcData->CompletionEvent); + } +} + +int TestSynchTimerQueue(int argc, char* argv[]) +{ + HANDLE hTimerQueue = NULL; + HANDLE hTimers[TIMER_COUNT]; + APC_DATA apcData[TIMER_COUNT]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + hTimerQueue = CreateTimerQueue(); + + if (!hTimerQueue) + { + printf("CreateTimerQueue failed (%" PRIu32 ")\n", GetLastError()); + return -1; + } + + for (DWORD index = 0; index < TIMER_COUNT; index++) + { + apcData[index].TimerId = index; + apcData[index].StartTime = GetTickCount(); + apcData[index].DueTime = (index * 10) + 50; + apcData[index].Period = 100; + apcData[index].FireCount = 0; + apcData[index].MaxFireCount = FIRE_COUNT; + + if (!(apcData[index].CompletionEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + printf("Failed to create apcData[%" PRIu32 "] event (%" PRIu32 ")\n", index, + GetLastError()); + return -1; + } + + if (!CreateTimerQueueTimer(&hTimers[index], hTimerQueue, TimerRoutine, &apcData[index], + apcData[index].DueTime, apcData[index].Period, 0)) + { + printf("CreateTimerQueueTimer failed (%" PRIu32 ")\n", GetLastError()); + return -1; + } + } + + for (DWORD index = 0; index < TIMER_COUNT; index++) + { + if (WaitForSingleObject(apcData[index].CompletionEvent, 2000) != WAIT_OBJECT_0) + { + printf("Failed to wait for timer queue timer #%" PRIu32 " (%" PRIu32 ")\n", index, + GetLastError()); + return -1; + } + } + + for (DWORD index = 0; index < TIMER_COUNT; index++) + { + /** + * Note: If the CompletionEvent parameter is INVALID_HANDLE_VALUE, the function waits + * for any running timer callback functions to complete before returning. + */ + if (!DeleteTimerQueueTimer(hTimerQueue, hTimers[index], INVALID_HANDLE_VALUE)) + { + printf("DeleteTimerQueueTimer failed (%" PRIu32 ")\n", GetLastError()); + return -1; + } + CloseHandle(apcData[index].CompletionEvent); + } + + if (!DeleteTimerQueue(hTimerQueue)) + { + printf("DeleteTimerQueue failed (%" PRIu32 ")\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c b/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c new file mode 100644 index 0000000..f61bbc1 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c @@ -0,0 +1,83 @@ + +#include +#include + +int TestSynchWaitableTimer(int argc, char* argv[]) +{ + DWORD status = 0; + HANDLE timer = NULL; + LONG period = 0; + LARGE_INTEGER due; + int result = -1; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + timer = CreateWaitableTimer(NULL, FALSE, NULL); + + if (!timer) + { + printf("CreateWaitableTimer failure\n"); + goto out; + } + + due.QuadPart = -1500000LL; /* 0.15 seconds */ + + if (!SetWaitableTimer(timer, &due, 0, NULL, NULL, 0)) + { + printf("SetWaitableTimer failure\n"); + goto out; + } + + status = WaitForSingleObject(timer, INFINITE); + + if (status != WAIT_OBJECT_0) + { + printf("WaitForSingleObject(timer, INFINITE) failure\n"); + goto out; + } + + printf("Timer Signaled\n"); + status = WaitForSingleObject(timer, 200); + + if (status != WAIT_TIMEOUT) + { + printf("WaitForSingleObject(timer, 200) failure: Actual: 0x%08" PRIX32 + ", Expected: 0x%08X\n", + status, WAIT_TIMEOUT); + goto out; + } + + due.QuadPart = 0; + period = 120; /* 0.12 seconds */ + + if (!SetWaitableTimer(timer, &due, period, NULL, NULL, 0)) + { + printf("SetWaitableTimer failure\n"); + goto out; + } + + if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForSingleObject(timer, INFINITE) failure\n"); + goto out; + } + + printf("Timer Signaled\n"); + + if (!SetWaitableTimer(timer, &due, period, NULL, NULL, 0)) + { + printf("SetWaitableTimer failure\n"); + goto out; + } + + if (WaitForMultipleObjects(1, &timer, FALSE, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForMultipleObjects(timer, INFINITE) failure\n"); + goto out; + } + + printf("Timer Signaled\n"); + result = 0; +out: + CloseHandle(timer); + return result; +} diff --git a/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c b/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c new file mode 100644 index 0000000..cf1f677 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c @@ -0,0 +1,92 @@ + +#include +#include +#include + +static int g_Count = 0; +static HANDLE g_Event = NULL; + +struct apc_data +{ + UINT32 StartTime; +}; +typedef struct apc_data APC_DATA; + +static VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) +{ + APC_DATA* apcData = NULL; + UINT32 CurrentTime = GetTickCount(); + WINPR_UNUSED(dwTimerLowValue); + WINPR_UNUSED(dwTimerHighValue); + + if (!lpArg) + return; + + apcData = (APC_DATA*)lpArg; + printf("TimerAPCProc: time: %" PRIu32 "\n", CurrentTime - apcData->StartTime); + g_Count++; + + if (g_Count >= 5) + { + SetEvent(g_Event); + } +} + +int TestSynchWaitableTimerAPC(int argc, char* argv[]) +{ + int status = -1; + DWORD rc = 0; + HANDLE hTimer = NULL; + BOOL bSuccess = 0; + LARGE_INTEGER due; + APC_DATA apcData = { 0 }; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + g_Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (!g_Event) + { + printf("Failed to create event\n"); + goto cleanup; + } + + hTimer = CreateWaitableTimer(NULL, FALSE, NULL); + if (!hTimer) + goto cleanup; + + due.QuadPart = -1000 * 100LL; /* 0.1 seconds */ + apcData.StartTime = GetTickCount(); + bSuccess = SetWaitableTimer(hTimer, &due, 10, TimerAPCProc, &apcData, FALSE); + + if (!bSuccess) + goto cleanup; + + /* nothing shall happen after 0.12 second, because thread is not in alertable state */ + rc = WaitForSingleObject(g_Event, 120); + if (rc != WAIT_TIMEOUT) + goto cleanup; + + for (;;) + { + rc = WaitForSingleObjectEx(g_Event, INFINITE, TRUE); + if (rc == WAIT_OBJECT_0) + break; + + if (rc == WAIT_IO_COMPLETION) + continue; + + printf("Failed to wait for completion event (%" PRIu32 ")\n", GetLastError()); + goto cleanup; + } + + status = 0; +cleanup: + + if (hTimer) + CloseHandle(hTimer); + + if (g_Event) + CloseHandle(g_Event); + + return status; +} diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c new file mode 100644 index 0000000..8238a88 --- /dev/null +++ b/winpr/libwinpr/synch/timer.c @@ -0,0 +1,1093 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +#include "event.h" +#include "synch.h" + +#ifndef _WIN32 + +#include "../handle/handle.h" +#include "../thread/thread.h" + +#include "../log.h" +#define TAG WINPR_TAG("synch.timer") + +static BOOL TimerCloseHandle(HANDLE handle); + +static BOOL TimerIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_TIMER, FALSE); +} + +static int TimerGetFd(HANDLE handle) +{ + WINPR_TIMER* timer = (WINPR_TIMER*)handle; + + if (!TimerIsHandled(handle)) + return -1; + + return timer->fd; +} + +static DWORD TimerCleanupHandle(HANDLE handle) +{ + SSIZE_T length = 0; + UINT64 expirations = 0; + WINPR_TIMER* timer = (WINPR_TIMER*)handle; + + if (!TimerIsHandled(handle)) + return WAIT_FAILED; + + if (timer->bManualReset) + return WAIT_OBJECT_0; + +#ifdef TIMER_IMPL_TIMERFD + do + { + length = read(timer->fd, (void*)&expirations, sizeof(UINT64)); + } while (length < 0 && errno == EINTR); + + if (length != 8) + { + if (length < 0) + { + char ebuffer[256] = { 0 }; + switch (errno) + { + case ETIMEDOUT: + case EAGAIN: + return WAIT_TIMEOUT; + + default: + break; + } + + WLog_ERR(TAG, "timer read() failure [%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + } + else + { + WLog_ERR(TAG, "timer read() failure - incorrect number of bytes read"); + } + + return WAIT_FAILED; + } +#elif defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH) + if (!winpr_event_reset(&timer->event)) + { + WLog_ERR(TAG, "timer reset() failure"); + return WAIT_FAILED; + } +#endif + + return WAIT_OBJECT_0; +} + +typedef struct +{ + WINPR_APC_ITEM apcItem; + WINPR_TIMER* timer; +} TimerDeleter; + +static void TimerPostDelete_APC(LPVOID arg) +{ + TimerDeleter* deleter = (TimerDeleter*)arg; + WINPR_ASSERT(deleter); + free(deleter->timer); + deleter->apcItem.markedForFree = TRUE; + deleter->apcItem.markedForRemove = TRUE; +} + +BOOL TimerCloseHandle(HANDLE handle) +{ + WINPR_TIMER* timer = NULL; + timer = (WINPR_TIMER*)handle; + + if (!TimerIsHandled(handle)) + return FALSE; + +#ifdef TIMER_IMPL_TIMERFD + if (timer->fd != -1) + close(timer->fd); +#endif + +#ifdef TIMER_IMPL_POSIX + timer_delete(timer->tid); +#endif + +#ifdef TIMER_IMPL_DISPATCH + dispatch_release(timer->queue); + dispatch_release(timer->source); +#endif + +#if defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH) + winpr_event_uninit(&timer->event); +#endif + + free(timer->name); + if (timer->apcItem.linked) + { + TimerDeleter* deleter = NULL; + WINPR_APC_ITEM* apcItem = NULL; + + switch (apc_remove(&timer->apcItem)) + { + case APC_REMOVE_OK: + break; + case APC_REMOVE_DELAY_FREE: + { + WINPR_THREAD* thread = winpr_GetCurrentThread(); + if (!thread) + return FALSE; + + deleter = calloc(1, sizeof(*deleter)); + if (!deleter) + { + WLog_ERR(TAG, "unable to allocate a timer deleter"); + return TRUE; + } + + deleter->timer = timer; + apcItem = &deleter->apcItem; + apcItem->type = APC_TYPE_HANDLE_FREE; + apcItem->alwaysSignaled = TRUE; + apcItem->completion = TimerPostDelete_APC; + apcItem->completionArgs = deleter; + apc_register(thread, apcItem); + return TRUE; + } + case APC_REMOVE_ERROR: + default: + WLog_ERR(TAG, "unable to remove timer from APC list"); + break; + } + } + + free(timer); + return TRUE; +} + +#ifdef TIMER_IMPL_POSIX + +static void WaitableTimerSignalHandler(int signum, siginfo_t* siginfo, void* arg) +{ + WINPR_TIMER* timer = siginfo->si_value.sival_ptr; + UINT64 data = 1; + WINPR_UNUSED(arg); + + if (!timer || (signum != SIGALRM)) + return; + + if (!winpr_event_set(&timer->event)) + WLog_ERR(TAG, "error when notifying event"); +} + +static INIT_ONCE TimerSignalHandler_InitOnce = INIT_ONCE_STATIC_INIT; + +static BOOL InstallTimerSignalHandler(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +{ + struct sigaction action; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGALRM); + action.sa_flags = SA_RESTART | SA_SIGINFO; + action.sa_sigaction = WaitableTimerSignalHandler; + sigaction(SIGALRM, &action, NULL); + return TRUE; +} +#endif + +#ifdef TIMER_IMPL_DISPATCH +static void WaitableTimerHandler(void* arg) +{ + UINT64 data = 1; + WINPR_TIMER* timer = (WINPR_TIMER*)arg; + + if (!timer) + return; + + if (!winpr_event_set(&timer->event)) + WLog_ERR(TAG, "failed to write to pipe"); + + if (timer->lPeriod == 0) + { + if (timer->running) + dispatch_suspend(timer->source); + + timer->running = FALSE; + } +} +#endif + +static int InitializeWaitableTimer(WINPR_TIMER* timer) +{ + int result = 0; + +#ifdef TIMER_IMPL_TIMERFD + timer->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (timer->fd <= 0) + return -1; +#elif defined(TIMER_IMPL_POSIX) + struct sigevent sigev = { 0 }; + InitOnceExecuteOnce(&TimerSignalHandler_InitOnce, InstallTimerSignalHandler, NULL, NULL); + sigev.sigev_notify = SIGEV_SIGNAL; + sigev.sigev_signo = SIGALRM; + sigev.sigev_value.sival_ptr = (void*)timer; + + if ((timer_create(CLOCK_MONOTONIC, &sigev, &(timer->tid))) != 0) + { + WLog_ERR(TAG, "timer_create"); + return -1; + } +#elif !defined(TIMER_IMPL_DISPATCH) + WLog_ERR(TAG, "os specific implementation is missing"); + result = -1; +#endif + + timer->bInit = TRUE; + return result; +} + +static BOOL timer_drain_fd(int fd) +{ + UINT64 expr = 0; + SSIZE_T ret = 0; + + do + { + ret = read(fd, &expr, sizeof(expr)); + } while (ret < 0 && errno == EINTR); + + return ret >= 0; +} + +static HANDLE_OPS ops = { TimerIsHandled, + TimerCloseHandle, + TimerGetFd, + TimerCleanupHandle, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +/** + * Waitable Timer + */ + +HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, + LPCSTR lpTimerName) +{ + HANDLE handle = NULL; + WINPR_TIMER* timer = NULL; + + if (lpTimerAttributes) + WLog_WARN(TAG, "[%s] does not support lpTimerAttributes", lpTimerName); + + timer = (WINPR_TIMER*)calloc(1, sizeof(WINPR_TIMER)); + + if (timer) + { + WINPR_HANDLE_SET_TYPE_AND_MODE(timer, HANDLE_TYPE_TIMER, WINPR_FD_READ); + handle = (HANDLE)timer; + timer->fd = -1; + timer->lPeriod = 0; + timer->bManualReset = bManualReset; + timer->pfnCompletionRoutine = NULL; + timer->lpArgToCompletionRoutine = NULL; + timer->bInit = FALSE; + + if (lpTimerName) + timer->name = strdup(lpTimerName); + + timer->common.ops = &ops; +#if defined(TIMER_IMPL_DISPATCH) || defined(TIMER_IMPL_POSIX) + if (!winpr_event_init(&timer->event)) + goto fail; + timer->fd = timer->event.fds[0]; +#endif + +#if defined(TIMER_IMPL_DISPATCH) + timer->queue = dispatch_queue_create(TAG, DISPATCH_QUEUE_SERIAL); + + if (!timer->queue) + goto fail; + + timer->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->queue); + + if (!timer->source) + goto fail; + + dispatch_set_context(timer->source, timer); + dispatch_source_set_event_handler_f(timer->source, WaitableTimerHandler); +#endif + } + + return handle; + +#if defined(TIMER_IMPL_DISPATCH) || defined(TIMER_IMPL_POSIX) +fail: + TimerCloseHandle(handle); + return NULL; +#endif +} + +HANDLE CreateWaitableTimerW(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, + LPCWSTR lpTimerName) +{ + HANDLE handle = NULL; + LPSTR name = NULL; + + if (lpTimerName) + { + name = ConvertWCharToUtf8Alloc(lpTimerName, NULL); + if (!name) + return NULL; + } + + handle = CreateWaitableTimerA(lpTimerAttributes, bManualReset, name); + free(name); + return handle; +} + +HANDLE CreateWaitableTimerExA(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCSTR lpTimerName, + DWORD dwFlags, DWORD dwDesiredAccess) +{ + BOOL bManualReset = (dwFlags & CREATE_WAITABLE_TIMER_MANUAL_RESET) ? TRUE : FALSE; + + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpTimerName, + dwDesiredAccess); + + return CreateWaitableTimerA(lpTimerAttributes, bManualReset, lpTimerName); +} + +HANDLE CreateWaitableTimerExW(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR lpTimerName, + DWORD dwFlags, DWORD dwDesiredAccess) +{ + HANDLE handle = NULL; + LPSTR name = NULL; + + if (lpTimerName) + { + name = ConvertWCharToUtf8Alloc(lpTimerName, NULL); + if (!name) + return NULL; + } + + handle = CreateWaitableTimerExA(lpTimerAttributes, name, dwFlags, dwDesiredAccess); + free(name); + return handle; +} + +static void timerAPC(LPVOID arg) +{ + WINPR_TIMER* timer = (WINPR_TIMER*)arg; + WINPR_ASSERT(timer); + if (!timer->lPeriod) + { + /* this is a one time shot timer with a completion, let's remove us from + the APC list */ + switch (apc_remove(&timer->apcItem)) + { + case APC_REMOVE_OK: + case APC_REMOVE_DELAY_FREE: + break; + case APC_REMOVE_ERROR: + default: + WLog_ERR(TAG, "error removing the APC routine"); + } + } + + if (timer->pfnCompletionRoutine) + timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0); + +#ifdef TIMER_IMPL_TIMERFD + while (timer_drain_fd(timer->fd)) + ; +#elif defined(TIMER_IMPL_POSIX) || defined(TIMER_IMPL_DISPATCH) + winpr_event_reset(&timer->event); +#endif +} + +BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod, + PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, + BOOL fResume) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_TIMER* timer = NULL; + LONGLONG seconds = 0; + LONGLONG nanoseconds = 0; + int status = 0; + + if (!winpr_Handle_GetInfo(hTimer, &Type, &Object)) + return FALSE; + + if (Type != HANDLE_TYPE_TIMER) + return FALSE; + + if (!lpDueTime) + return FALSE; + + if (lPeriod < 0) + return FALSE; + + if (fResume) + { + WLog_ERR(TAG, "does not support fResume"); + return FALSE; + } + + timer = (WINPR_TIMER*)Object; + timer->lPeriod = lPeriod; /* milliseconds */ + timer->pfnCompletionRoutine = pfnCompletionRoutine; + timer->lpArgToCompletionRoutine = lpArgToCompletionRoutine; + + if (!timer->bInit) + { + if (InitializeWaitableTimer(timer) < 0) + return FALSE; + } + +#if defined(TIMER_IMPL_TIMERFD) || defined(TIMER_IMPL_POSIX) + ZeroMemory(&(timer->timeout), sizeof(struct itimerspec)); + + if (lpDueTime->QuadPart < 0) + { + LONGLONG due = lpDueTime->QuadPart * (-1); + /* due time is in 100 nanosecond intervals */ + seconds = (due / 10000000); + nanoseconds = ((due % 10000000) * 100); + } + else if (lpDueTime->QuadPart == 0) + { + seconds = nanoseconds = 0; + } + else + { + WLog_ERR(TAG, "absolute time not implemented"); + return FALSE; + } + + if (lPeriod > 0) + { + timer->timeout.it_interval.tv_sec = (lPeriod / 1000); /* seconds */ + timer->timeout.it_interval.tv_nsec = ((lPeriod % 1000) * 1000000); /* nanoseconds */ + } + + if (lpDueTime->QuadPart != 0) + { + timer->timeout.it_value.tv_sec = seconds; /* seconds */ + timer->timeout.it_value.tv_nsec = nanoseconds; /* nanoseconds */ + } + else + { + timer->timeout.it_value.tv_sec = timer->timeout.it_interval.tv_sec; /* seconds */ + timer->timeout.it_value.tv_nsec = timer->timeout.it_interval.tv_nsec; /* nanoseconds */ + } + +#ifdef TIMER_IMPL_TIMERFD + status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL); + if (status) + { + WLog_ERR(TAG, "timerfd_settime failure: %d", status); + return FALSE; + } +#else + status = timer_settime(timer->tid, 0, &(timer->timeout), NULL); + if (status != 0) + { + WLog_ERR(TAG, "timer_settime failure"); + return FALSE; + } +#endif +#endif + +#ifdef TIMER_IMPL_DISPATCH + if (lpDueTime->QuadPart < 0) + { + LONGLONG due = lpDueTime->QuadPart * (-1); + /* due time is in 100 nanosecond intervals */ + seconds = (due / 10000000); + nanoseconds = due * 100; + } + else if (lpDueTime->QuadPart == 0) + { + seconds = nanoseconds = 0; + } + else + { + WLog_ERR(TAG, "absolute time not implemented"); + return FALSE; + } + + if (!winpr_event_reset(&timer->event)) + { + WLog_ERR(TAG, "error when resetting timer event"); + } + + { + if (timer->running) + dispatch_suspend(timer->source); + + dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds); + uint64_t interval = DISPATCH_TIME_FOREVER; + + if (lPeriod > 0) + interval = lPeriod * 1000000; + + dispatch_source_set_timer(timer->source, start, interval, 0); + dispatch_resume(timer->source); + timer->running = TRUE; + } +#endif + + if (pfnCompletionRoutine) + { + WINPR_APC_ITEM* apcItem = &timer->apcItem; + + /* install our APC routine that will call the completion */ + apcItem->type = APC_TYPE_TIMER; + apcItem->alwaysSignaled = FALSE; + apcItem->pollFd = timer->fd; + apcItem->pollMode = WINPR_FD_READ; + apcItem->completion = timerAPC; + apcItem->completionArgs = timer; + + if (!apcItem->linked) + { + WINPR_THREAD* thread = winpr_GetCurrentThread(); + if (!thread) + return FALSE; + + apc_register(thread, apcItem); + } + } + else + { + if (timer->apcItem.linked) + { + apc_remove(&timer->apcItem); + } + } + return TRUE; +} + +BOOL SetWaitableTimerEx(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPeriod, + PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, + PREASON_CONTEXT WakeContext, ULONG TolerableDelay) +{ + return SetWaitableTimer(hTimer, lpDueTime, lPeriod, pfnCompletionRoutine, + lpArgToCompletionRoutine, FALSE); +} + +HANDLE OpenWaitableTimerA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpTimerName) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +HANDLE OpenWaitableTimerW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpTimerName) +{ + /* TODO: Implement */ + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +BOOL CancelWaitableTimer(HANDLE hTimer) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + + if (!winpr_Handle_GetInfo(hTimer, &Type, &Object)) + return FALSE; + + if (Type != HANDLE_TYPE_TIMER) + return FALSE; + +#if defined(__APPLE__) + { + WINPR_TIMER* timer = (WINPR_TIMER*)Object; + if (timer->running) + dispatch_suspend(timer->source); + + timer->running = FALSE; + } +#endif + return TRUE; +} + +/* + * Returns inner file descriptor for usage with select() + * This file descriptor is not usable on Windows + */ + +int GetTimerFileDescriptor(HANDLE hTimer) +{ +#ifndef _WIN32 + WINPR_HANDLE* hdl = NULL; + ULONG type = 0; + + if (!winpr_Handle_GetInfo(hTimer, &type, &hdl) || type != HANDLE_TYPE_TIMER) + { + WLog_ERR(TAG, "GetTimerFileDescriptor: hTimer is not an timer"); + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + return winpr_Handle_getFd(hTimer); +#else + return -1; +#endif +} + +/** + * Timer-Queue Timer + */ + +/** + * Design, Performance, and Optimization of Timer Strategies for Real-time ORBs: + * http://www.cs.wustl.edu/~schmidt/Timer_Queue.html + */ + +static void timespec_add_ms(struct timespec* tspec, UINT32 ms) +{ + INT64 ns = 0; + WINPR_ASSERT(tspec); + ns = tspec->tv_nsec + (ms * 1000000LL); + tspec->tv_sec += (ns / 1000000000LL); + tspec->tv_nsec = (ns % 1000000000LL); +} + +static void timespec_gettimeofday(struct timespec* tspec) +{ + struct timeval tval; + WINPR_ASSERT(tspec); + gettimeofday(&tval, NULL); + tspec->tv_sec = tval.tv_sec; + tspec->tv_nsec = tval.tv_usec * 1000; +} + +static INT64 timespec_compare(const struct timespec* tspec1, const struct timespec* tspec2) +{ + WINPR_ASSERT(tspec1); + WINPR_ASSERT(tspec2); + if (tspec1->tv_sec == tspec2->tv_sec) + return (tspec1->tv_nsec - tspec2->tv_nsec); + else + return (tspec1->tv_sec - tspec2->tv_sec); +} + +static void timespec_copy(struct timespec* dst, struct timespec* src) +{ + WINPR_ASSERT(dst); + WINPR_ASSERT(src); + dst->tv_sec = src->tv_sec; + dst->tv_nsec = src->tv_nsec; +} + +static void InsertTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer) +{ + WINPR_TIMER_QUEUE_TIMER* node = NULL; + + WINPR_ASSERT(pHead); + WINPR_ASSERT(timer); + + if (!(*pHead)) + { + *pHead = timer; + timer->next = NULL; + return; + } + + node = *pHead; + + while (node->next) + { + if (timespec_compare(&(timer->ExpirationTime), &(node->ExpirationTime)) > 0) + { + if (timespec_compare(&(timer->ExpirationTime), &(node->next->ExpirationTime)) < 0) + break; + } + + node = node->next; + } + + if (node->next) + { + timer->next = node->next->next; + node->next = timer; + } + else + { + node->next = timer; + timer->next = NULL; + } +} + +static void RemoveTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer) +{ + BOOL found = FALSE; + WINPR_TIMER_QUEUE_TIMER* node = NULL; + WINPR_TIMER_QUEUE_TIMER* prevNode = NULL; + + WINPR_ASSERT(pHead); + WINPR_ASSERT(timer); + if (timer == *pHead) + { + *pHead = timer->next; + timer->next = NULL; + return; + } + + node = *pHead; + prevNode = NULL; + + while (node) + { + if (node == timer) + { + found = TRUE; + break; + } + + prevNode = node; + node = node->next; + } + + if (found) + { + if (prevNode) + { + prevNode->next = timer->next; + } + + timer->next = NULL; + } +} + +static int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) +{ + struct timespec CurrentTime; + WINPR_TIMER_QUEUE_TIMER* node = NULL; + + WINPR_ASSERT(timerQueue); + + if (!timerQueue->activeHead) + return 0; + + timespec_gettimeofday(&CurrentTime); + node = timerQueue->activeHead; + + while (node) + { + if (timespec_compare(&CurrentTime, &(node->ExpirationTime)) >= 0) + { + node->Callback(node->Parameter, TRUE); + node->FireCount++; + timerQueue->activeHead = node->next; + node->next = NULL; + + if (node->Period) + { + timespec_add_ms(&(node->ExpirationTime), node->Period); + InsertTimerQueueTimer(&(timerQueue->activeHead), node); + } + else + { + InsertTimerQueueTimer(&(timerQueue->inactiveHead), node); + } + + node = timerQueue->activeHead; + } + else + { + break; + } + } + + return 0; +} + +static void* TimerQueueThread(void* arg) +{ + int status = 0; + struct timespec timeout; + WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*)arg; + + WINPR_ASSERT(timerQueue); + while (1) + { + pthread_mutex_lock(&(timerQueue->cond_mutex)); + timespec_gettimeofday(&timeout); + + if (!timerQueue->activeHead) + { + timespec_add_ms(&timeout, 50); + } + else + { + if (timespec_compare(&timeout, &(timerQueue->activeHead->ExpirationTime)) < 0) + { + timespec_copy(&timeout, &(timerQueue->activeHead->ExpirationTime)); + } + } + + status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &timeout); + FireExpiredTimerQueueTimers(timerQueue); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + + if ((status != ETIMEDOUT) && (status != 0)) + break; + + if (timerQueue->bCancelled) + break; + } + + return NULL; +} + +static int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue) +{ + WINPR_ASSERT(timerQueue); + pthread_cond_init(&(timerQueue->cond), NULL); + pthread_mutex_init(&(timerQueue->cond_mutex), NULL); + pthread_mutex_init(&(timerQueue->mutex), NULL); + pthread_attr_init(&(timerQueue->attr)); + timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param)); + pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO); + pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue); + return 0; +} + +HANDLE CreateTimerQueue(void) +{ + HANDLE handle = NULL; + WINPR_TIMER_QUEUE* timerQueue = NULL; + timerQueue = (WINPR_TIMER_QUEUE*)calloc(1, sizeof(WINPR_TIMER_QUEUE)); + + if (timerQueue) + { + WINPR_HANDLE_SET_TYPE_AND_MODE(timerQueue, HANDLE_TYPE_TIMER_QUEUE, WINPR_FD_READ); + handle = (HANDLE)timerQueue; + timerQueue->activeHead = NULL; + timerQueue->inactiveHead = NULL; + timerQueue->bCancelled = FALSE; + StartTimerQueueThread(timerQueue); + } + + return handle; +} + +BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) +{ + void* rvalue = NULL; + WINPR_TIMER_QUEUE* timerQueue = NULL; + WINPR_TIMER_QUEUE_TIMER* node = NULL; + WINPR_TIMER_QUEUE_TIMER* nextNode = NULL; + + if (!TimerQueue) + return FALSE; + + timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue; + /* Cancel and delete timer queue timers */ + pthread_mutex_lock(&(timerQueue->cond_mutex)); + timerQueue->bCancelled = TRUE; + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + pthread_join(timerQueue->thread, &rvalue); + /** + * Quote from MSDN regarding CompletionEvent: + * If this parameter is INVALID_HANDLE_VALUE, the function waits for + * all callback functions to complete before returning. + * If this parameter is NULL, the function marks the timer for + * deletion and returns immediately. + * + * Note: The current WinPR implementation implicitly waits for any + * callback functions to complete (see pthread_join above) + */ + { + /* Move all active timers to the inactive timer list */ + node = timerQueue->activeHead; + + while (node) + { + InsertTimerQueueTimer(&(timerQueue->inactiveHead), node); + node = node->next; + } + + timerQueue->activeHead = NULL; + /* Once all timers are inactive, free them */ + node = timerQueue->inactiveHead; + + while (node) + { + nextNode = node->next; + free(node); + node = nextNode; + } + + timerQueue->inactiveHead = NULL; + } + /* Delete timer queue */ + pthread_cond_destroy(&(timerQueue->cond)); + pthread_mutex_destroy(&(timerQueue->cond_mutex)); + pthread_mutex_destroy(&(timerQueue->mutex)); + pthread_attr_destroy(&(timerQueue->attr)); + free(timerQueue); + + if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE)) + SetEvent(CompletionEvent); + + return TRUE; +} + +BOOL DeleteTimerQueue(HANDLE TimerQueue) +{ + return DeleteTimerQueueEx(TimerQueue, NULL); +} + +BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, + PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) +{ + struct timespec CurrentTime; + WINPR_TIMER_QUEUE* timerQueue = NULL; + WINPR_TIMER_QUEUE_TIMER* timer = NULL; + + if (!TimerQueue) + return FALSE; + + timespec_gettimeofday(&CurrentTime); + timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue; + timer = (WINPR_TIMER_QUEUE_TIMER*)malloc(sizeof(WINPR_TIMER_QUEUE_TIMER)); + + if (!timer) + return FALSE; + + WINPR_HANDLE_SET_TYPE_AND_MODE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER, WINPR_FD_READ); + *((UINT_PTR*)phNewTimer) = (UINT_PTR)(HANDLE)timer; + timespec_copy(&(timer->StartTime), &CurrentTime); + timespec_add_ms(&(timer->StartTime), DueTime); + timespec_copy(&(timer->ExpirationTime), &(timer->StartTime)); + timer->Flags = Flags; + timer->DueTime = DueTime; + timer->Period = Period; + timer->Callback = Callback; + timer->Parameter = Parameter; + timer->timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue; + timer->FireCount = 0; + timer->next = NULL; + pthread_mutex_lock(&(timerQueue->cond_mutex)); + InsertTimerQueueTimer(&(timerQueue->activeHead), timer); + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + return TRUE; +} + +BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period) +{ + struct timespec CurrentTime; + WINPR_TIMER_QUEUE* timerQueue = NULL; + WINPR_TIMER_QUEUE_TIMER* timer = NULL; + + if (!TimerQueue || !Timer) + return FALSE; + + timespec_gettimeofday(&CurrentTime); + timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue; + timer = (WINPR_TIMER_QUEUE_TIMER*)Timer; + pthread_mutex_lock(&(timerQueue->cond_mutex)); + RemoveTimerQueueTimer(&(timerQueue->activeHead), timer); + RemoveTimerQueueTimer(&(timerQueue->inactiveHead), timer); + timer->DueTime = DueTime; + timer->Period = Period; + timer->next = NULL; + timespec_copy(&(timer->StartTime), &CurrentTime); + timespec_add_ms(&(timer->StartTime), DueTime); + timespec_copy(&(timer->ExpirationTime), &(timer->StartTime)); + InsertTimerQueueTimer(&(timerQueue->activeHead), timer); + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + return TRUE; +} + +BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent) +{ + WINPR_TIMER_QUEUE* timerQueue = NULL; + WINPR_TIMER_QUEUE_TIMER* timer = NULL; + + if (!TimerQueue || !Timer) + return FALSE; + + timerQueue = (WINPR_TIMER_QUEUE*)TimerQueue; + timer = (WINPR_TIMER_QUEUE_TIMER*)Timer; + pthread_mutex_lock(&(timerQueue->cond_mutex)); + /** + * Quote from MSDN regarding CompletionEvent: + * If this parameter is INVALID_HANDLE_VALUE, the function waits for + * all callback functions to complete before returning. + * If this parameter is NULL, the function marks the timer for + * deletion and returns immediately. + * + * Note: The current WinPR implementation implicitly waits for any + * callback functions to complete (see cond_mutex usage) + */ + RemoveTimerQueueTimer(&(timerQueue->activeHead), timer); + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + free(timer); + + if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE)) + SetEvent(CompletionEvent); + + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c new file mode 100644 index 0000000..3bef657 --- /dev/null +++ b/winpr/libwinpr/synch/wait.c @@ -0,0 +1,583 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Hardening + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include +#include +#include +#include + +#include "synch.h" +#include "pollset.h" +#include "../thread/thread.h" +#include +#include + +#include "../log.h" +#define TAG WINPR_TAG("sync.wait") + +/** + * WaitForSingleObject + * WaitForSingleObjectEx + * WaitForMultipleObjectsEx + * SignalObjectAndWait + */ + +#ifndef _WIN32 + +#include +#include +#include +#include + +#include "../handle/handle.h" + +#include "../pipe/pipe.h" + +/* clock_gettime is not implemented on OSX prior to 10.12 */ +#if defined(__MACH__) && defined(__APPLE__) + +#include + +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 0 +#endif + +/* clock_gettime is not implemented on OSX prior to 10.12 */ +int _mach_clock_gettime(int clk_id, struct timespec* t); + +int _mach_clock_gettime(int clk_id, struct timespec* t) +{ + UINT64 time = 0; + double seconds = 0.0; + double nseconds = 0.0; + mach_timebase_info_data_t timebase = { 0 }; + mach_timebase_info(&timebase); + time = mach_absolute_time(); + nseconds = ((double)time * (double)timebase.numer) / ((double)timebase.denom); + seconds = ((double)time * (double)timebase.numer) / ((double)timebase.denom * 1e9); + t->tv_sec = seconds; + t->tv_nsec = nseconds; + return 0; +} + +/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */ +#ifdef __CLOCK_AVAILABILITY +/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be declared + * * but it may be NULL at runtime. So we need to check before using it. */ +int _mach_safe_clock_gettime(int clk_id, struct timespec* t); + +int _mach_safe_clock_gettime(int clk_id, struct timespec* t) +{ + if (clock_gettime) + { + return clock_gettime(clk_id, t); + } + + return _mach_clock_gettime(clk_id, t); +} + +#define clock_gettime _mach_safe_clock_gettime +#else +#define clock_gettime _mach_clock_gettime +#endif + +#endif + +/** + * Drop in replacement for pthread_mutex_timedlock + * http://code.google.com/p/android/issues/detail?id=7807 + * http://aleksmaus.blogspot.ca/2011/12/missing-pthreadmutextimedlock-on.html + */ +#if !defined(WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK) +#include + +static long long ts_difftime(const struct timespec* o, const struct timespec* n) +{ + long long oldValue = o->tv_sec * 1000000000LL + o->tv_nsec; + long long newValue = n->tv_sec * 1000000000LL + n->tv_nsec; + return newValue - oldValue; +} + +#ifdef ANDROID +#if (__ANDROID_API__ >= 21) +#define CONST_NEEDED const +#else +#define CONST_NEEDED +#endif +#define STATIC_NEEDED +#else /* ANDROID */ +#define CONST_NEEDED const +#define STATIC_NEEDED static +#endif + +STATIC_NEEDED int pthread_mutex_timedlock(pthread_mutex_t* mutex, + CONST_NEEDED struct timespec* timeout) +{ + struct timespec timenow = { 0 }; + struct timespec sleepytime = { 0 }; + unsigned long long diff = 0; + int retcode = -1; + /* This is just to avoid a completely busy wait */ + clock_gettime(CLOCK_MONOTONIC, &timenow); + diff = ts_difftime(&timenow, timeout); + sleepytime.tv_sec = diff / 1000000000LL; + sleepytime.tv_nsec = diff % 1000000000LL; + + while ((retcode = pthread_mutex_trylock(mutex)) == EBUSY) + { + clock_gettime(CLOCK_MONOTONIC, &timenow); + + if (ts_difftime(timeout, &timenow) >= 0) + { + return ETIMEDOUT; + } + + nanosleep(&sleepytime, NULL); + } + + return retcode; +} +#endif + +static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds) +{ + ts->tv_sec += dwMilliseconds / 1000L; + ts->tv_nsec += (dwMilliseconds % 1000L) * 1000000L; + ts->tv_sec += ts->tv_nsec / 1000000000L; + ts->tv_nsec = ts->tv_nsec % 1000000000L; +} + +DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_POLL_SET pollset = { 0 }; + + if (!winpr_Handle_GetInfo(hHandle, &Type, &Object)) + { + WLog_ERR(TAG, "invalid hHandle."); + SetLastError(ERROR_INVALID_HANDLE); + return WAIT_FAILED; + } + + if (Type == HANDLE_TYPE_PROCESS && winpr_Handle_getFd(hHandle) == -1) + { + /* note: if we have pidfd support (under linux and we have managed to associate a + * pidfd with our process), we use the regular method with pollset below. + * If not (on other platforms) we do a waitpid */ + WINPR_PROCESS* process = (WINPR_PROCESS*)Object; + + do + { + DWORD status = 0; + DWORD waitDelay = 0; + int ret = waitpid(process->pid, &(process->status), WNOHANG); + if (ret == process->pid) + { + process->dwExitCode = (DWORD)process->status; + return WAIT_OBJECT_0; + } + else if (ret < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "waitpid failure [%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + SetLastError(ERROR_INTERNAL_ERROR); + return WAIT_FAILED; + } + + /* sleep by slices of 50ms */ + waitDelay = (dwMilliseconds < 50) ? dwMilliseconds : 50; + + status = SleepEx(waitDelay, bAlertable); + if (status != 0) + return status; + + dwMilliseconds -= waitDelay; + + } while (dwMilliseconds > 50); + + return WAIT_TIMEOUT; + } + + if (Type == HANDLE_TYPE_MUTEX) + { + WINPR_MUTEX* mutex = (WINPR_MUTEX*)Object; + + if (dwMilliseconds != INFINITE) + { + int status = 0; + struct timespec timeout = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &timeout); + ts_add_ms(&timeout, dwMilliseconds); + status = pthread_mutex_timedlock(&mutex->mutex, &timeout); + + if (ETIMEDOUT == status) + return WAIT_TIMEOUT; + } + else + { + pthread_mutex_lock(&mutex->mutex); + } + + return WAIT_OBJECT_0; + } + else + { + int status = -1; + WINPR_THREAD* thread = NULL; + BOOL isSet = FALSE; + size_t extraFds = 0; + DWORD ret = 0; + BOOL autoSignaled = FALSE; + + if (bAlertable) + { + thread = (WINPR_THREAD*)_GetCurrentThread(); + if (thread) + { + /* treat reentrancy, we can't switch to alertable state when we're already + treating completions */ + if (thread->apc.treatingCompletions) + bAlertable = FALSE; + else + extraFds = thread->apc.length; + } + else + { + /* called from a non WinPR thread */ + bAlertable = FALSE; + } + } + + int fd = winpr_Handle_getFd(Object); + if (fd < 0) + { + WLog_ERR(TAG, "winpr_Handle_getFd did not return a fd!"); + SetLastError(ERROR_INVALID_HANDLE); + return WAIT_FAILED; + } + + if (!pollset_init(&pollset, 1 + extraFds)) + { + WLog_ERR(TAG, "unable to initialize pollset"); + SetLastError(ERROR_INTERNAL_ERROR); + return WAIT_FAILED; + } + + if (!pollset_add(&pollset, fd, Object->Mode)) + { + WLog_ERR(TAG, "unable to add fd in pollset"); + goto out; + } + + if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled)) + { + WLog_ERR(TAG, "unable to collect APC fds"); + goto out; + } + + if (!autoSignaled) + { + status = pollset_poll(&pollset, dwMilliseconds); + if (status < 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "pollset_poll() failure [%d] %s", errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + goto out; + } + } + + ret = WAIT_TIMEOUT; + if (bAlertable && apc_executeCompletions(thread, &pollset, 1)) + ret = WAIT_IO_COMPLETION; + + isSet = pollset_isSignaled(&pollset, 0); + pollset_uninit(&pollset); + + if (!isSet) + return ret; + + return winpr_Handle_cleanup(Object); + } + +out: + pollset_uninit(&pollset); + SetLastError(ERROR_INTERNAL_ERROR); + return WAIT_FAILED; +} + +DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) +{ + return WaitForSingleObjectEx(hHandle, dwMilliseconds, FALSE); +} + +DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, + DWORD dwMilliseconds, BOOL bAlertable) +{ + DWORD signalled = 0; + DWORD polled = 0; + DWORD poll_map[MAXIMUM_WAIT_OBJECTS] = { 0 }; + BOOL signalled_handles[MAXIMUM_WAIT_OBJECTS] = { FALSE }; + int fd = -1; + int status = -1; + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_THREAD* thread = NULL; + WINPR_POLL_SET pollset = { 0 }; + DWORD ret = WAIT_FAILED; + size_t extraFds = 0; + UINT64 now = 0; + UINT64 dueTime = 0; + + if (!nCount || (nCount > MAXIMUM_WAIT_OBJECTS)) + { + WLog_ERR(TAG, "invalid handles count(%" PRIu32 ")", nCount); + return WAIT_FAILED; + } + + if (bAlertable) + { + thread = winpr_GetCurrentThread(); + if (thread) + { + /* treat reentrancy, we can't switch to alertable state when we're already + treating completions */ + if (thread->apc.treatingCompletions) + bAlertable = FALSE; + else + extraFds = thread->apc.length; + } + else + { + /* most probably we're not called from WinPR thread, so we can't have any APC */ + bAlertable = FALSE; + } + } + + if (!pollset_init(&pollset, nCount + extraFds)) + { + WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 " extraCount=%" PRIu32 "", + nCount, extraFds); + return WAIT_FAILED; + } + + signalled = 0; + + now = GetTickCount64(); + if (dwMilliseconds != INFINITE) + dueTime = now + dwMilliseconds; + else + dueTime = 0xFFFFFFFFFFFFFFFF; + + do + { + BOOL autoSignaled = FALSE; + polled = 0; + + /* first collect file descriptors to poll */ + DWORD index = 0; + for (; index < nCount; index++) + { + if (bWaitAll) + { + if (signalled_handles[index]) + continue; + + poll_map[polled] = index; + } + + if (!winpr_Handle_GetInfo(lpHandles[index], &Type, &Object)) + { + WLog_ERR(TAG, "invalid event file descriptor at %" PRIu32, index); + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + SetLastError(ERROR_INVALID_HANDLE); + goto out; + } + + fd = winpr_Handle_getFd(Object); + if (fd == -1) + { + WLog_ERR(TAG, "invalid file descriptor at %" PRIu32, index); + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + SetLastError(ERROR_INVALID_HANDLE); + goto out; + } + + if (!pollset_add(&pollset, fd, Object->Mode)) + { + WLog_ERR(TAG, "unable to register fd in pollset at %" PRIu32, index); + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + SetLastError(ERROR_INVALID_HANDLE); + goto out; + } + + polled++; + } + + /* treat file descriptors of the APC if needed */ + if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled)) + { + WLog_ERR(TAG, "unable to register APC fds"); + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + SetLastError(ERROR_INTERNAL_ERROR); + goto out; + } + + /* poll file descriptors */ + status = 0; + if (!autoSignaled) + { + DWORD waitTime = 0; + + if (dwMilliseconds == INFINITE) + waitTime = INFINITE; + else + waitTime = (DWORD)(dueTime - now); + + status = pollset_poll(&pollset, waitTime); + if (status < 0) + { + char ebuffer[256] = { 0 }; +#ifdef WINPR_HAVE_POLL_H + WLog_ERR(TAG, "poll() handle %" PRIu32 " (%" PRIu32 ") failure [%d] %s", index, + nCount, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); +#else + WLog_ERR(TAG, "select() handle %" PRIu32 " (%" PRIu32 ") failure [%d] %s", index, + nCount, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); +#endif + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + SetLastError(ERROR_INTERNAL_ERROR); + goto out; + } + } + + /* give priority to the APC queue, to return WAIT_IO_COMPLETION */ + if (bAlertable && apc_executeCompletions(thread, &pollset, polled)) + { + ret = WAIT_IO_COMPLETION; + goto out; + } + + /* then treat pollset */ + if (status) + { + for (DWORD index = 0; index < polled; index++) + { + DWORD handlesIndex = 0; + BOOL signal_set = FALSE; + + if (bWaitAll) + handlesIndex = poll_map[index]; + else + handlesIndex = index; + + signal_set = pollset_isSignaled(&pollset, index); + if (signal_set) + { + DWORD rc = winpr_Handle_cleanup(lpHandles[handlesIndex]); + if (rc != WAIT_OBJECT_0) + { + WLog_ERR(TAG, "error in cleanup function for handle at index=%" PRIu32, + handlesIndex); + ret = rc; + goto out; + } + + if (bWaitAll) + { + signalled_handles[handlesIndex] = TRUE; + + /* Continue checks from last position. */ + for (; signalled < nCount; signalled++) + { + if (!signalled_handles[signalled]) + break; + } + } + else + { + ret = (WAIT_OBJECT_0 + handlesIndex); + goto out; + } + + if (signalled >= nCount) + { + ret = WAIT_OBJECT_0; + goto out; + } + } + } + } + + if (bAlertable && thread->apc.length > extraFds) + { + pollset_uninit(&pollset); + extraFds = thread->apc.length; + if (!pollset_init(&pollset, nCount + extraFds)) + { + WLog_ERR(TAG, "unable reallocate pollset"); + SetLastError(ERROR_INTERNAL_ERROR); + return WAIT_FAILED; + } + } + else + pollset_reset(&pollset); + + now = GetTickCount64(); + } while (now < dueTime); + + ret = WAIT_TIMEOUT; + +out: + pollset_uninit(&pollset); + return ret; +} + +DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, + DWORD dwMilliseconds) +{ + return WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, FALSE); +} + +DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, + BOOL bAlertable) +{ + if (!SetEvent(hObjectToSignal)) + return WAIT_FAILED; + + return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable); +} + +#endif diff --git a/winpr/libwinpr/sysinfo/CMakeLists.txt b/winpr/libwinpr/sysinfo/CMakeLists.txt new file mode 100644 index 0000000..799df05 --- /dev/null +++ b/winpr/libwinpr/sysinfo/CMakeLists.txt @@ -0,0 +1,31 @@ +# WinPR: Windows Portable Runtime +# libwinpr-sysinfo cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(ANDROID) + add_subdirectory(cpufeatures) +endif() + +winpr_module_add(sysinfo.c) + +if((NOT WIN32) AND (NOT APPLE) AND (NOT ANDROID) AND (NOT OPENBSD)) + winpr_library_add_private(rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + diff --git a/winpr/libwinpr/sysinfo/ModuleOptions.cmake b/winpr/libwinpr/sysinfo/ModuleOptions.cmake new file mode 100644 index 0000000..6a7ff02 --- /dev/null +++ b/winpr/libwinpr/sysinfo/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "sysinfo") +set(MINWIN_LONG_NAME "System Information Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt b/winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt new file mode 100644 index 0000000..f1b93df --- /dev/null +++ b/winpr/libwinpr/sysinfo/cpufeatures/CMakeLists.txt @@ -0,0 +1,20 @@ +# WinPR: Windows Portable Runtime +# libwinpr-sysinfo cmake build script +# +# Copyright 2017 Armin Novak +# Copyright 2017 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(cpu-features.c cpu-features.h) + diff --git a/winpr/libwinpr/sysinfo/cpufeatures/NOTICE b/winpr/libwinpr/sysinfo/cpufeatures/NOTICE new file mode 100644 index 0000000..d6c0922 --- /dev/null +++ b/winpr/libwinpr/sysinfo/cpufeatures/NOTICE @@ -0,0 +1,13 @@ +Copyright (C) 2016 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/winpr/libwinpr/sysinfo/cpufeatures/README b/winpr/libwinpr/sysinfo/cpufeatures/README new file mode 100644 index 0000000..ba85c20 --- /dev/null +++ b/winpr/libwinpr/sysinfo/cpufeatures/README @@ -0,0 +1,4 @@ +Android CPUFeatures Library + +https://developer.android.com/ndk/guides/cpu-features.html +https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures diff --git a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c new file mode 100644 index 0000000..d43b588 --- /dev/null +++ b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c @@ -0,0 +1,1426 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* ChangeLog for this library: + * + * NDK r10e?: Add MIPS MSA feature. + * + * NDK r10: Support for 64-bit CPUs (Intel, ARM & MIPS). + * + * NDK r8d: Add android_setCpu(). + * + * NDK r8c: Add new ARM CPU features: VFPv2, VFP_D32, VFP_FP16, + * VFP_FMA, NEON_FMA, IDIV_ARM, IDIV_THUMB2 and iWMMXt. + * + * Rewrite the code to parse /proc/self/auxv instead of + * the "Features" field in /proc/cpuinfo. + * + * Dynamically allocate the buffer that hold the content + * of /proc/cpuinfo to deal with newer hardware. + * + * NDK r7c: Fix CPU count computation. The old method only reported the + * number of _active_ CPUs when the library was initialized, + * which could be less than the real total. + * + * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7 + * for an ARMv6 CPU (see below). + * + * Handle kernels that only report 'neon', and not 'vfpv3' + * (VFPv3 is mandated by the ARM architecture is Neon is implemented) + * + * Handle kernels that only report 'vfpv3d16', and not 'vfpv3' + * + * Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in + * android_getCpuFamily(). + * + * NDK r4: Initial release + */ + +#include "cpu-features.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pthread_once_t g_once; +static int g_inited; +static AndroidCpuFamily g_cpuFamily; +static uint64_t g_cpuFeatures; +static int g_cpuCount; + +#ifdef __arm__ +static uint32_t g_cpuIdArm; +#endif + +static const int android_cpufeatures_debug = 0; + +#define D(...) \ + do \ + { \ + if (android_cpufeatures_debug) \ + { \ + printf(__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0) + +#ifdef __i386__ +static __inline__ void x86_cpuid(int func, int values[4]) +{ + int a, b, c, d; + /* We need to preserve ebx since we're compiling PIC code */ + /* this means we can't use "=b" for the second output register */ + __asm__ __volatile__("push %%ebx\n" + "cpuid\n" + "mov %%ebx, %1\n" + "pop %%ebx\n" + : "=a"(a), "=r"(b), "=c"(c), "=d"(d) + : "a"(func)); + values[0] = a; + values[1] = b; + values[2] = c; + values[3] = d; +} +#elif defined(__x86_64__) +static __inline__ void x86_cpuid(int func, int values[4]) +{ + int64_t a, b, c, d; + /* We need to preserve ebx since we're compiling PIC code */ + /* this means we can't use "=b" for the second output register */ + __asm__ __volatile__("push %%rbx\n" + "cpuid\n" + "mov %%rbx, %1\n" + "pop %%rbx\n" + : "=a"(a), "=r"(b), "=c"(c), "=d"(d) + : "a"(func)); + values[0] = a; + values[1] = b; + values[2] = c; + values[3] = d; +} +#endif + +/* Get the size of a file by reading it until the end. This is needed + * because files under /proc do not always return a valid size when + * using fseek(0, SEEK_END) + ftell(). Nor can they be mmap()-ed. + */ +static int get_file_size(const char* pathname) +{ + int fd, result = 0; + char buffer[256]; + fd = open(pathname, O_RDONLY); + + if (fd < 0) + { + char ebuffer[256] = { 0 }; + D("Can't open %s: %s\n", pathname, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return -1; + } + + for (;;) + { + int ret = read(fd, buffer, sizeof buffer); + + if (ret < 0) + { + char ebuffer[256] = { 0 }; + if (errno == EINTR) + continue; + + D("Error while reading %s: %s\n", pathname, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + break; + } + + if (ret == 0) + break; + + result += ret; + } + + close(fd); + return result; +} + +/* Read the content of /proc/cpuinfo into a user-provided buffer. + * Return the length of the data, or -1 on error. Does *not* + * zero-terminate the content. Will not read more + * than 'buffsize' bytes. + */ +static int read_file(const char* pathname, char* buffer, size_t buffsize) +{ + int fd, count; + fd = open(pathname, O_RDONLY); + + if (fd < 0) + { + char ebuffer[256] = { 0 }; + D("Could not open %s: %s\n", pathname, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return -1; + } + + count = 0; + + while (count < (int)buffsize) + { + int ret = read(fd, buffer + count, buffsize - count); + + if (ret < 0) + { + char ebuffer[256] = { 0 }; + if (errno == EINTR) + continue; + + D("Error while reading from %s: %s\n", pathname, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + + if (count == 0) + count = -1; + + break; + } + + if (ret == 0) + break; + + count += ret; + } + + close(fd); + return count; +} + +#ifdef __arm__ +/* Extract the content of a the first occurence of a given field in + * the content of /proc/cpuinfo and return it as a heap-allocated + * string that must be freed by the caller. + * + * Return NULL if not found + */ +static char* extract_cpuinfo_field(const char* buffer, int buflen, const char* field) +{ + int fieldlen = strlen(field); + const char* bufend = buffer + buflen; + char* result = NULL; + int len; + const char *p, *q; + /* Look for first field occurence, and ensures it starts the line. */ + p = buffer; + + for (;;) + { + p = memmem(p, bufend - p, field, fieldlen); + + if (p == NULL) + goto EXIT; + + if (p == buffer || p[-1] == '\n') + break; + + p += fieldlen; + } + + /* Skip to the first column followed by a space */ + p += fieldlen; + p = memchr(p, ':', bufend - p); + + if (p == NULL || p[1] != ' ') + goto EXIT; + + /* Find the end of the line */ + p += 2; + q = memchr(p, '\n', bufend - p); + + if (q == NULL) + q = bufend; + + /* Copy the line into a heap-allocated buffer */ + len = q - p; + result = malloc(len + 1); + + if (result == NULL) + goto EXIT; + + memcpy(result, p, len); + result[len] = '\0'; +EXIT: + return result; +} + +/* Checks that a space-separated list of items contains one given 'item'. + * Returns 1 if found, 0 otherwise. + */ +static int has_list_item(const char* list, const char* item) +{ + const char* p = list; + int itemlen = strlen(item); + + if (list == NULL) + return 0; + + while (*p) + { + const char* q; + + /* skip spaces */ + while (*p == ' ' || *p == '\t') + p++; + + /* find end of current list item */ + q = p; + + while (*q && *q != ' ' && *q != '\t') + q++; + + if (itemlen == q - p && !memcmp(p, item, itemlen)) + return 1; + + /* skip to next item */ + p = q; + } + + return 0; +} +#endif /* __arm__ */ + +/* Parse a number starting from 'input', but not going further + * than 'limit'. Return the value into '*result'. + * + * NOTE: Does not skip over leading spaces, or deal with sign characters. + * NOTE: Ignores overflows. + * + * The function returns NULL in case of error (bad format), or the new + * position after the decimal number in case of success (which will always + * be <= 'limit'). + */ +static const char* parse_number(const char* input, const char* limit, int base, int* result) +{ + const char* p = input; + int val = 0; + + while (p < limit) + { + int d = (*p - '0'); + + if ((unsigned)d >= 10U) + { + d = (*p - 'a'); + + if ((unsigned)d >= 6U) + d = (*p - 'A'); + + if ((unsigned)d >= 6U) + break; + + d += 10; + } + + if (d >= base) + break; + + val = val * base + d; + p++; + } + + if (p == input) + return NULL; + + *result = val; + return p; +} + +static const char* parse_decimal(const char* input, const char* limit, int* result) +{ + return parse_number(input, limit, 10, result); +} + +#ifdef __arm__ +static const char* parse_hexadecimal(const char* input, const char* limit, int* result) +{ + return parse_number(input, limit, 16, result); +} +#endif /* __arm__ */ + +/* This small data type is used to represent a CPU list / mask, as read + * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt + * + * For now, we don't expect more than 32 cores on mobile devices, so keep + * everything simple. + */ +typedef struct +{ + uint32_t mask; +} CpuList; + +static __inline__ void cpulist_init(CpuList* list) +{ + list->mask = 0; +} + +static __inline__ void cpulist_and(CpuList* list1, CpuList* list2) +{ + list1->mask &= list2->mask; +} + +static __inline__ void cpulist_set(CpuList* list, int index) +{ + if ((unsigned)index < 32) + { + list->mask |= (uint32_t)(1U << index); + } +} + +static __inline__ int cpulist_count(CpuList* list) +{ + return __builtin_popcount(list->mask); +} + +/* Parse a textual list of cpus and store the result inside a CpuList object. + * Input format is the following: + * - comma-separated list of items (no spaces) + * - each item is either a single decimal number (cpu index), or a range made + * of two numbers separated by a single dash (-). Ranges are inclusive. + * + * Examples: 0 + * 2,4-127,128-143 + * 0-1 + */ +static void cpulist_parse(CpuList* list, const char* line, int line_len) +{ + const char* p = line; + const char* end = p + line_len; + const char* q; + + /* NOTE: the input line coming from sysfs typically contains a + * trailing newline, so take care of it in the code below + */ + while (p < end && *p != '\n') + { + int start_value = 0; + int end_value = 0; + /* Find the end of current item, and put it into 'q' */ + q = memchr(p, ',', end - p); + + if (q == NULL) + { + q = end; + } + + /* Get first value */ + p = parse_decimal(p, q, &start_value); + + if (p == NULL) + goto BAD_FORMAT; + + end_value = start_value; + + /* If we're not at the end of the item, expect a dash and + * and integer; extract end value. + */ + if (p < q && *p == '-') + { + p = parse_decimal(p + 1, q, &end_value); + + if (p == NULL) + goto BAD_FORMAT; + } + + /* Set bits CPU list bits */ + for (int val = start_value; val <= end_value; val++) + { + cpulist_set(list, val); + } + + /* Jump to next item */ + p = q; + + if (p < end) + p++; + } + +BAD_FORMAT:; +} + +/* Read a CPU list from one sysfs file */ +static void cpulist_read_from(CpuList* list, const char* filename) +{ + char file[64]; + int filelen; + cpulist_init(list); + filelen = read_file(filename, file, sizeof file); + + if (filelen < 0) + { + char ebuffer[256] = { 0 }; + D("Could not read %s: %s\n", filename, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return; + } + + cpulist_parse(list, file, filelen); +} +#if defined(__aarch64__) +// see kernel header +#define HWCAP_FP (1 << 0) +#define HWCAP_ASIMD (1 << 1) +#define HWCAP_AES (1 << 3) +#define HWCAP_PMULL (1 << 4) +#define HWCAP_SHA1 (1 << 5) +#define HWCAP_SHA2 (1 << 6) +#define HWCAP_CRC32 (1 << 7) +#endif + +#if defined(__arm__) + +// See kernel header. +#define HWCAP_VFP (1 << 6) +#define HWCAP_IWMMXT (1 << 9) +#define HWCAP_NEON (1 << 12) +#define HWCAP_VFPv3 (1 << 13) +#define HWCAP_VFPv3D16 (1 << 14) +#define HWCAP_VFPv4 (1 << 16) +#define HWCAP_IDIVA (1 << 17) +#define HWCAP_IDIVT (1 << 18) + +// see kernel header +#define HWCAP2_AES (1 << 0) +#define HWCAP2_PMULL (1 << 1) +#define HWCAP2_SHA1 (1 << 2) +#define HWCAP2_SHA2 (1 << 3) +#define HWCAP2_CRC32 (1 << 4) + +// This is the list of 32-bit ARMv7 optional features that are _always_ +// supported by ARMv8 CPUs, as mandated by the ARM Architecture Reference +// Manual. +#define HWCAP_SET_FOR_ARMV8 \ + (HWCAP_VFP | HWCAP_NEON | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_IDIVA | HWCAP_IDIVT) +#endif + +#if defined(__mips__) +// see kernel header +#define HWCAP_MIPS_R6 (1 << 0) +#define HWCAP_MIPS_MSA (1 << 1) +#endif + +#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) + +#define AT_HWCAP 16 +#define AT_HWCAP2 26 + +// Probe the system's C library for a 'getauxval' function and call it if +// it exits, or return 0 for failure. This function is available since API +// level 20. +// +// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the +// edge case where some NDK developers use headers for a platform that is +// newer than the one really targetted by their application. +// This is typically done to use newer native APIs only when running on more +// recent Android versions, and requires careful symbol management. +// +// Note that getauxval() can't really be re-implemented here, because +// its implementation does not parse /proc/self/auxv. Instead it depends +// on values that are passed by the kernel at process-init time to the +// C runtime initialization layer. +static uint32_t get_elf_hwcap_from_getauxval(int hwcap_type) +{ + typedef unsigned long getauxval_func_t(unsigned long); + dlerror(); + void* libc_handle = dlopen("libc.so", RTLD_NOW); + + if (!libc_handle) + { + D("Could not dlopen() C library: %s\n", dlerror()); + return 0; + } + + uint32_t ret = 0; + getauxval_func_t* func = (getauxval_func_t*)dlsym(libc_handle, "getauxval"); + + if (!func) + { + D("Could not find getauxval() in C library\n"); + } + else + { + // Note: getauxval() returns 0 on failure. Doesn't touch errno. + ret = (uint32_t)(*func)(hwcap_type); + } + + dlclose(libc_handle); + return ret; +} +#endif + +#if defined(__arm__) +// Parse /proc/self/auxv to extract the ELF HW capabilities bitmap for the +// current CPU. Note that this file is not accessible from regular +// application processes on some Android platform releases. +// On success, return new ELF hwcaps, or 0 on failure. +static uint32_t get_elf_hwcap_from_proc_self_auxv(void) +{ + const char filepath[] = "/proc/self/auxv"; + int fd = TEMP_FAILURE_RETRY(open(filepath, O_RDONLY)); + + if (fd < 0) + { + char ebuffer[256] = { 0 }; + D("Could not open %s: %s\n", filepath, winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return 0; + } + + struct + { + uint32_t tag; + uint32_t value; + } entry; + + uint32_t result = 0; + + for (;;) + { + int ret = TEMP_FAILURE_RETRY(read(fd, (char*)&entry, sizeof entry)); + + if (ret < 0) + { + char ebuffer[256] = { 0 }; + D("Error while reading %s: %s\n", filepath, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + break; + } + + // Detect end of list. + if (ret == 0 || (entry.tag == 0 && entry.value == 0)) + break; + + if (entry.tag == AT_HWCAP) + { + result = entry.value; + break; + } + } + + close(fd); + return result; +} + +/* Compute the ELF HWCAP flags from the content of /proc/cpuinfo. + * This works by parsing the 'Features' line, which lists which optional + * features the device's CPU supports, on top of its reference + * architecture. + */ +static uint32_t get_elf_hwcap_from_proc_cpuinfo(const char* cpuinfo, int cpuinfo_len) +{ + uint32_t hwcaps = 0; + long architecture = 0; + char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); + + if (cpuArch) + { + architecture = strtol(cpuArch, NULL, 10); + free(cpuArch); + + if (architecture >= 8L) + { + // This is a 32-bit ARM binary running on a 64-bit ARM64 kernel. + // The 'Features' line only lists the optional features that the + // device's CPU supports, compared to its reference architecture + // which are of no use for this process. + D("Faking 32-bit ARM HWCaps on ARMv%ld CPU\n", architecture); + return HWCAP_SET_FOR_ARMV8; + } + } + + char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features"); + + if (cpuFeatures != NULL) + { + D("Found cpuFeatures = '%s'\n", cpuFeatures); + + if (has_list_item(cpuFeatures, "vfp")) + hwcaps |= HWCAP_VFP; + + if (has_list_item(cpuFeatures, "vfpv3")) + hwcaps |= HWCAP_VFPv3; + + if (has_list_item(cpuFeatures, "vfpv3d16")) + hwcaps |= HWCAP_VFPv3D16; + + if (has_list_item(cpuFeatures, "vfpv4")) + hwcaps |= HWCAP_VFPv4; + + if (has_list_item(cpuFeatures, "neon")) + hwcaps |= HWCAP_NEON; + + if (has_list_item(cpuFeatures, "idiva")) + hwcaps |= HWCAP_IDIVA; + + if (has_list_item(cpuFeatures, "idivt")) + hwcaps |= HWCAP_IDIVT; + + if (has_list_item(cpuFeatures, "idiv")) + hwcaps |= HWCAP_IDIVA | HWCAP_IDIVT; + + if (has_list_item(cpuFeatures, "iwmmxt")) + hwcaps |= HWCAP_IWMMXT; + + free(cpuFeatures); + } + + return hwcaps; +} +#endif /* __arm__ */ + +/* Return the number of cpus present on a given device. + * + * To handle all weird kernel configurations, we need to compute the + * intersection of the 'present' and 'possible' CPU lists and count + * the result. + */ +static int get_cpu_count(void) +{ + CpuList cpus_present[1]; + CpuList cpus_possible[1]; + cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present"); + cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible"); + /* Compute the intersection of both sets to get the actual number of + * CPU cores that can be used on this device by the kernel. + */ + cpulist_and(cpus_present, cpus_possible); + return cpulist_count(cpus_present); +} + +static void android_cpuInitFamily(void) +{ +#if defined(__arm__) + g_cpuFamily = ANDROID_CPU_FAMILY_ARM; +#elif defined(__i386__) + g_cpuFamily = ANDROID_CPU_FAMILY_X86; +#elif defined(__mips64) + /* Needs to be before __mips__ since the compiler defines both */ + g_cpuFamily = ANDROID_CPU_FAMILY_MIPS64; +#elif defined(__mips__) + g_cpuFamily = ANDROID_CPU_FAMILY_MIPS; +#elif defined(__aarch64__) + g_cpuFamily = ANDROID_CPU_FAMILY_ARM64; +#elif defined(__x86_64__) + g_cpuFamily = ANDROID_CPU_FAMILY_X86_64; +#else + g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN; +#endif +} + +static void android_cpuInit(void) +{ + char* cpuinfo = NULL; + int cpuinfo_len; + android_cpuInitFamily(); + g_cpuFeatures = 0; + g_cpuCount = 1; + g_inited = 1; + cpuinfo_len = get_file_size("/proc/cpuinfo"); + + if (cpuinfo_len < 0) + { + D("cpuinfo_len cannot be computed!"); + return; + } + + cpuinfo = malloc(cpuinfo_len); + + if (cpuinfo == NULL) + { + D("cpuinfo buffer could not be allocated"); + return; + } + + cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, cpuinfo_len); + D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo); + + if (cpuinfo_len < 0) /* should not happen */ + { + free(cpuinfo); + return; + } + + /* Count the CPU cores, the value may be 0 for single-core CPUs */ + g_cpuCount = get_cpu_count(); + + if (g_cpuCount == 0) + { + g_cpuCount = 1; + } + + D("found cpuCount = %d\n", g_cpuCount); +#ifdef __arm__ + { + /* Extract architecture from the "CPU Architecture" field. + * The list is well-known, unlike the the output of + * the 'Processor' field which can vary greatly. + * + * See the definition of the 'proc_arch' array in + * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in + * same file. + */ + char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); + + if (cpuArch != NULL) + { + char* end; + long archNumber; + int hasARMv7 = 0; + D("found cpuArch = '%s'\n", cpuArch); + /* read the initial decimal number, ignore the rest */ + archNumber = strtol(cpuArch, &end, 10); + + /* Note that ARMv8 is upwards compatible with ARMv7. */ + if (end > cpuArch && archNumber >= 7) + { + hasARMv7 = 1; + } + + /* Unfortunately, it seems that certain ARMv6-based CPUs + * report an incorrect architecture number of 7! + * + * See http://code.google.com/p/android/issues/detail?id=10812 + * + * We try to correct this by looking at the 'elf_format' + * field reported by the 'Processor' field, which is of the + * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for + * an ARMv6-one. + */ + if (hasARMv7) + { + char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor"); + + if (cpuProc != NULL) + { + D("found cpuProc = '%s'\n", cpuProc); + + if (has_list_item(cpuProc, "(v6l)")) + { + D("CPU processor and architecture mismatch!!\n"); + hasARMv7 = 0; + } + + free(cpuProc); + } + } + + if (hasARMv7) + { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7; + } + + /* The LDREX / STREX instructions are available from ARMv6 */ + if (archNumber >= 6) + { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX; + } + + free(cpuArch); + } + + /* Extract the list of CPU features from ELF hwcaps */ + uint32_t hwcaps = 0; + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); + + if (!hwcaps) + { + D("Parsing /proc/self/auxv to extract ELF hwcaps!\n"); + hwcaps = get_elf_hwcap_from_proc_self_auxv(); + } + + if (!hwcaps) + { + // Parsing /proc/self/auxv will fail from regular application + // processes on some Android platform versions, when this happens + // parse proc/cpuinfo instead. + D("Parsing /proc/cpuinfo to extract ELF hwcaps!\n"); + hwcaps = get_elf_hwcap_from_proc_cpuinfo(cpuinfo, cpuinfo_len); + } + + if (hwcaps != 0) + { + int has_vfp = (hwcaps & HWCAP_VFP); + int has_vfpv3 = (hwcaps & HWCAP_VFPv3); + int has_vfpv3d16 = (hwcaps & HWCAP_VFPv3D16); + int has_vfpv4 = (hwcaps & HWCAP_VFPv4); + int has_neon = (hwcaps & HWCAP_NEON); + int has_idiva = (hwcaps & HWCAP_IDIVA); + int has_idivt = (hwcaps & HWCAP_IDIVT); + int has_iwmmxt = (hwcaps & HWCAP_IWMMXT); + + // The kernel does a poor job at ensuring consistency when + // describing CPU features. So lots of guessing is needed. + + // 'vfpv4' implies VFPv3|VFP_FMA|FP16 + if (has_vfpv4) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 | ANDROID_CPU_ARM_FEATURE_VFP_FP16 | + ANDROID_CPU_ARM_FEATURE_VFP_FMA; + + // 'vfpv3' or 'vfpv3d16' imply VFPv3. Note that unlike GCC, + // a value of 'vfpv3' doesn't necessarily mean that the D32 + // feature is present, so be conservative. All CPUs in the + // field that support D32 also support NEON, so this should + // not be a problem in practice. + if (has_vfpv3 || has_vfpv3d16) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; + + // 'vfp' is super ambiguous. Depending on the kernel, it can + // either mean VFPv2 or VFPv3. Make it depend on ARMv7. + if (has_vfp) + { + if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_ARMv7) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; + else + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2; + } + + // Neon implies VFPv3|D32, and if vfpv4 is detected, NEON_FMA + if (has_neon) + { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 | ANDROID_CPU_ARM_FEATURE_NEON | + ANDROID_CPU_ARM_FEATURE_VFP_D32; + + if (has_vfpv4) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON_FMA; + } + + // VFPv3 implies VFPv2 and ARMv7 + if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_VFPv3) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 | ANDROID_CPU_ARM_FEATURE_ARMv7; + + if (has_idiva) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; + + if (has_idivt) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2; + + if (has_iwmmxt) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt; + } + + /* Extract the list of CPU features from ELF hwcaps2 */ + uint32_t hwcaps2 = 0; + hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2); + + if (hwcaps2 != 0) + { + int has_aes = (hwcaps2 & HWCAP2_AES); + int has_pmull = (hwcaps2 & HWCAP2_PMULL); + int has_sha1 = (hwcaps2 & HWCAP2_SHA1); + int has_sha2 = (hwcaps2 & HWCAP2_SHA2); + int has_crc32 = (hwcaps2 & HWCAP2_CRC32); + + if (has_aes) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES; + + if (has_pmull) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL; + + if (has_sha1) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1; + + if (has_sha2) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2; + + if (has_crc32) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32; + } + + /* Extract the cpuid value from various fields */ + // The CPUID value is broken up in several entries in /proc/cpuinfo. + // This table is used to rebuild it from the entries. + static const struct CpuIdEntry + { + const char* field; + char format; + char bit_lshift; + char bit_length; + } cpu_id_entries[] = { + { "CPU implementer", 'x', 24, 8 }, + { "CPU variant", 'x', 20, 4 }, + { "CPU part", 'x', 4, 12 }, + { "CPU revision", 'd', 0, 4 }, + }; + D("Parsing /proc/cpuinfo to recover CPUID\n"); + + for (size_t i = 0; i < sizeof(cpu_id_entries) / sizeof(cpu_id_entries[0]); ++i) + { + const struct CpuIdEntry* entry = &cpu_id_entries[i]; + char* value = extract_cpuinfo_field(cpuinfo, cpuinfo_len, entry->field); + + if (value == NULL) + continue; + + D("field=%s value='%s'\n", entry->field, value); + char* value_end = value + strlen(value); + int val = 0; + const char* start = value; + const char* p; + + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) + { + start += 2; + p = parse_hexadecimal(start, value_end, &val); + } + else if (entry->format == 'x') + p = parse_hexadecimal(value, value_end, &val); + else + p = parse_decimal(value, value_end, &val); + + if (p > (const char*)start) + { + val &= ((1 << entry->bit_length) - 1); + val <<= entry->bit_lshift; + g_cpuIdArm |= (uint32_t)val; + } + + free(value); + } + + // Handle kernel configuration bugs that prevent the correct + // reporting of CPU features. + static const struct CpuFix + { + uint32_t cpuid; + uint64_t or_flags; + } cpu_fixes[] = { + /* The Nexus 4 (Qualcomm Krait) kernel configuration + * forgets to report IDIV support. */ + { 0x510006f2, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 }, + { 0x510006f3, ANDROID_CPU_ARM_FEATURE_IDIV_ARM | ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 }, + }; + + for (size_t n = 0; n < sizeof(cpu_fixes) / sizeof(cpu_fixes[0]); ++n) + { + const struct CpuFix* entry = &cpu_fixes[n]; + + if (g_cpuIdArm == entry->cpuid) + g_cpuFeatures |= entry->or_flags; + } + + // Special case: The emulator-specific Android 4.2 kernel fails + // to report support for the 32-bit ARM IDIV instruction. + // Technically, this is a feature of the virtual CPU implemented + // by the emulator. Note that it could also support Thumb IDIV + // in the future, and this will have to be slightly updated. + char* hardware = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Hardware"); + + if (hardware) + { + if (!strcmp(hardware, "Goldfish") && g_cpuIdArm == 0x4100c080 && + (g_cpuFamily & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0) + { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; + } + + free(hardware); + } + } +#endif /* __arm__ */ +#ifdef __aarch64__ + { + /* Extract the list of CPU features from ELF hwcaps */ + uint32_t hwcaps = 0; + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); + + if (hwcaps != 0) + { + int has_fp = (hwcaps & HWCAP_FP); + int has_asimd = (hwcaps & HWCAP_ASIMD); + int has_aes = (hwcaps & HWCAP_AES); + int has_pmull = (hwcaps & HWCAP_PMULL); + int has_sha1 = (hwcaps & HWCAP_SHA1); + int has_sha2 = (hwcaps & HWCAP_SHA2); + int has_crc32 = (hwcaps & HWCAP_CRC32); + + if (has_fp == 0) + { + D("ERROR: Floating-point unit missing, but is required by Android on AArch64 " + "CPUs\n"); + } + + if (has_asimd == 0) + { + D("ERROR: ASIMD unit missing, but is required by Android on AArch64 CPUs\n"); + } + + if (has_fp) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP; + + if (has_asimd) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD; + + if (has_aes) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES; + + if (has_pmull) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL; + + if (has_sha1) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1; + + if (has_sha2) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2; + + if (has_crc32) + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32; + } + } +#endif /* __aarch64__ */ +#if defined(__i386__) || defined(__x86_64__) + int regs[4]; + /* According to http://en.wikipedia.org/wiki/CPUID */ +#define VENDOR_INTEL_b 0x756e6547 +#define VENDOR_INTEL_c 0x6c65746e +#define VENDOR_INTEL_d 0x49656e69 + x86_cpuid(0, regs); + int vendorIsIntel = + (regs[1] == VENDOR_INTEL_b && regs[2] == VENDOR_INTEL_c && regs[3] == VENDOR_INTEL_d); + x86_cpuid(1, regs); + + if ((regs[2] & (1 << 9)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3; + } + + if ((regs[2] & (1 << 23)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; + } + + if ((regs[2] & (1 << 19)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1; + } + + if ((regs[2] & (1 << 20)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2; + } + + if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; + } + + if ((regs[2] & (1 << 25)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AES_NI; + } + + if ((regs[2] & (1 << 28)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX; + } + + if ((regs[2] & (1 << 30)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_RDRAND; + } + + x86_cpuid(7, regs); + + if ((regs[1] & (1 << 5)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX2; + } + + if ((regs[1] & (1 << 29)) != 0) + { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SHA_NI; + } + +#endif +#if defined(__mips__) + { + /* MIPS and MIPS64 */ + /* Extract the list of CPU features from ELF hwcaps */ + uint32_t hwcaps = 0; + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP); + + if (hwcaps != 0) + { + int has_r6 = (hwcaps & HWCAP_MIPS_R6); + int has_msa = (hwcaps & HWCAP_MIPS_MSA); + + if (has_r6) + g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6; + + if (has_msa) + g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA; + } + } +#endif /* __mips__ */ + free(cpuinfo); +} + +AndroidCpuFamily android_getCpuFamily(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuFamily; +} + +uint64_t android_getCpuFeatures(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuFeatures; +} + +int android_getCpuCount(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuCount; +} + +static void android_cpuInitDummy(void) +{ + g_inited = 1; +} + +int android_setCpu(int cpu_count, uint64_t cpu_features) +{ + /* Fail if the library was already initialized. */ + if (g_inited) + return 0; + + android_cpuInitFamily(); + g_cpuCount = (cpu_count <= 0 ? 1 : cpu_count); + g_cpuFeatures = cpu_features; + pthread_once(&g_once, android_cpuInitDummy); + return 1; +} + +#ifdef __arm__ +uint32_t android_getCpuIdArm(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuIdArm; +} + +int android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id) +{ + if (!android_setCpu(cpu_count, cpu_features)) + return 0; + + g_cpuIdArm = cpu_id; + return 1; +} +#endif /* __arm__ */ + +/* + * Technical note: Making sense of ARM's FPU architecture versions. + * + * FPA was ARM's first attempt at an FPU architecture. There is no Android + * device that actually uses it since this technology was already obsolete + * when the project started. If you see references to FPA instructions + * somewhere, you can be sure that this doesn't apply to Android at all. + * + * FPA was followed by "VFP", soon renamed "VFPv1" due to the emergence of + * new versions / additions to it. ARM considers this obsolete right now, + * and no known Android device implements it either. + * + * VFPv2 added a few instructions to VFPv1, and is an *optional* extension + * supported by some ARMv5TE, ARMv6 and ARMv6T2 CPUs. Note that a device + * supporting the 'armeabi' ABI doesn't necessarily support these. + * + * VFPv3-D16 adds a few instructions on top of VFPv2 and is typically used + * on ARMv7-A CPUs which implement a FPU. Note that it is also mandated + * by the Android 'armeabi-v7a' ABI. The -D16 suffix in its name means + * that it provides 16 double-precision FPU registers (d0-d15) and 32 + * single-precision ones (s0-s31) which happen to be mapped to the same + * register banks. + * + * VFPv3-D32 is the name of an extension to VFPv3-D16 that provides 16 + * additional double precision registers (d16-d31). Note that there are + * still only 32 single precision registers. + * + * VFPv3xD is a *subset* of VFPv3-D16 that only provides single-precision + * registers. It is only used on ARMv7-M (i.e. on micro-controllers) which + * are not supported by Android. Note that it is not compatible with VFPv2. + * + * NOTE: The term 'VFPv3' usually designate either VFPv3-D16 or VFPv3-D32 + * depending on context. For example GCC uses it for VFPv3-D32, but + * the Linux kernel code uses it for VFPv3-D16 (especially in + * /proc/cpuinfo). Always try to use the full designation when + * possible. + * + * NEON, a.k.a. "ARM Advanced SIMD" is an extension that provides + * instructions to perform parallel computations on vectors of 8, 16, + * 32, 64 and 128 bit quantities. NEON requires VFPv32-D32 since all + * NEON registers are also mapped to the same register banks. + * + * VFPv4-D16, adds a few instructions on top of VFPv3-D16 in order to + * perform fused multiply-accumulate on VFP registers, as well as + * half-precision (16-bit) conversion operations. + * + * VFPv4-D32 is VFPv4-D16 with 32, instead of 16, FPU double precision + * registers. + * + * VPFv4-NEON is VFPv4-D32 with NEON instructions. It also adds fused + * multiply-accumulate instructions that work on the NEON registers. + * + * NOTE: Similarly, "VFPv4" might either reference VFPv4-D16 or VFPv4-D32 + * depending on context. + * + * The following information was determined by scanning the binutils-2.22 + * sources: + * + * Basic VFP instruction subsets: + * + * #define FPU_VFP_EXT_V1xD 0x08000000 // Base VFP instruction set. + * #define FPU_VFP_EXT_V1 0x04000000 // Double-precision insns. + * #define FPU_VFP_EXT_V2 0x02000000 // ARM10E VFPr1. + * #define FPU_VFP_EXT_V3xD 0x01000000 // VFPv3 single-precision. + * #define FPU_VFP_EXT_V3 0x00800000 // VFPv3 double-precision. + * #define FPU_NEON_EXT_V1 0x00400000 // Neon (SIMD) insns. + * #define FPU_VFP_EXT_D32 0x00200000 // Registers D16-D31. + * #define FPU_VFP_EXT_FP16 0x00100000 // Half-precision extensions. + * #define FPU_NEON_EXT_FMA 0x00080000 // Neon fused multiply-add + * #define FPU_VFP_EXT_FMA 0x00040000 // VFP fused multiply-add + * + * FPU types (excluding NEON) + * + * FPU_VFP_V1xD (EXT_V1xD) + * | + * +--------------------------+ + * | | + * FPU_VFP_V1 (+EXT_V1) FPU_VFP_V3xD (+EXT_V2+EXT_V3xD) + * | | + * | | + * FPU_VFP_V2 (+EXT_V2) FPU_VFP_V4_SP_D16 (+EXT_FP16+EXT_FMA) + * | + * FPU_VFP_V3D16 (+EXT_Vx3D+EXT_V3) + * | + * +--------------------------+ + * | | + * FPU_VFP_V3 (+EXT_D32) FPU_VFP_V4D16 (+EXT_FP16+EXT_FMA) + * | | + * | FPU_VFP_V4 (+EXT_D32) + * | + * FPU_VFP_HARD (+EXT_FMA+NEON_EXT_FMA) + * + * VFP architectures: + * + * ARCH_VFP_V1xD (EXT_V1xD) + * | + * +------------------+ + * | | + * | ARCH_VFP_V3xD (+EXT_V2+EXT_V3xD) + * | | + * | ARCH_VFP_V3xD_FP16 (+EXT_FP16) + * | | + * | ARCH_VFP_V4_SP_D16 (+EXT_FMA) + * | + * ARCH_VFP_V1 (+EXT_V1) + * | + * ARCH_VFP_V2 (+EXT_V2) + * | + * ARCH_VFP_V3D16 (+EXT_V3xD+EXT_V3) + * | + * +-------------------+ + * | | + * | ARCH_VFP_V3D16_FP16 (+EXT_FP16) + * | + * +-------------------+ + * | | + * | ARCH_VFP_V4_D16 (+EXT_FP16+EXT_FMA) + * | | + * | ARCH_VFP_V4 (+EXT_D32) + * | | + * | ARCH_NEON_VFP_V4 (+EXT_NEON+EXT_NEON_FMA) + * | + * ARCH_VFP_V3 (+EXT_D32) + * | + * +-------------------+ + * | | + * | ARCH_VFP_V3_FP16 (+EXT_FP16) + * | + * ARCH_VFP_V3_PLUS_NEON_V1 (+EXT_NEON) + * | + * ARCH_NEON_FP16 (+EXT_FP16) + * + * -fpu= values and their correspondance with FPU architectures above: + * + * {"vfp", FPU_ARCH_VFP_V2}, + * {"vfp9", FPU_ARCH_VFP_V2}, + * {"vfp3", FPU_ARCH_VFP_V3}, // For backwards compatbility. + * {"vfp10", FPU_ARCH_VFP_V2}, + * {"vfp10-r0", FPU_ARCH_VFP_V1}, + * {"vfpxd", FPU_ARCH_VFP_V1xD}, + * {"vfpv2", FPU_ARCH_VFP_V2}, + * {"vfpv3", FPU_ARCH_VFP_V3}, + * {"vfpv3-fp16", FPU_ARCH_VFP_V3_FP16}, + * {"vfpv3-d16", FPU_ARCH_VFP_V3D16}, + * {"vfpv3-d16-fp16", FPU_ARCH_VFP_V3D16_FP16}, + * {"vfpv3xd", FPU_ARCH_VFP_V3xD}, + * {"vfpv3xd-fp16", FPU_ARCH_VFP_V3xD_FP16}, + * {"neon", FPU_ARCH_VFP_V3_PLUS_NEON_V1}, + * {"neon-fp16", FPU_ARCH_NEON_FP16}, + * {"vfpv4", FPU_ARCH_VFP_V4}, + * {"vfpv4-d16", FPU_ARCH_VFP_V4D16}, + * {"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16}, + * {"neon-vfpv4", FPU_ARCH_NEON_VFP_V4}, + * + * + * Simplified diagram that only includes FPUs supported by Android: + * Only ARCH_VFP_V3D16 is actually mandated by the armeabi-v7a ABI, + * all others are optional and must be probed at runtime. + * + * ARCH_VFP_V3D16 (EXT_V1xD+EXT_V1+EXT_V2+EXT_V3xD+EXT_V3) + * | + * +-------------------+ + * | | + * | ARCH_VFP_V3D16_FP16 (+EXT_FP16) + * | + * +-------------------+ + * | | + * | ARCH_VFP_V4_D16 (+EXT_FP16+EXT_FMA) + * | | + * | ARCH_VFP_V4 (+EXT_D32) + * | | + * | ARCH_NEON_VFP_V4 (+EXT_NEON+EXT_NEON_FMA) + * | + * ARCH_VFP_V3 (+EXT_D32) + * | + * +-------------------+ + * | | + * | ARCH_VFP_V3_FP16 (+EXT_FP16) + * | + * ARCH_VFP_V3_PLUS_NEON_V1 (+EXT_NEON) + * | + * ARCH_NEON_FP16 (+EXT_FP16) + * + */ diff --git a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h new file mode 100644 index 0000000..9520c8a --- /dev/null +++ b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.h @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef CPU_FEATURES_H +#define CPU_FEATURES_H + +#include +#include + +__BEGIN_DECLS + +/* A list of valid values returned by android_getCpuFamily(). + * They describe the CPU Architecture of the current process. + */ +typedef enum +{ + ANDROID_CPU_FAMILY_UNKNOWN = 0, + ANDROID_CPU_FAMILY_ARM, + ANDROID_CPU_FAMILY_X86, + ANDROID_CPU_FAMILY_MIPS, + ANDROID_CPU_FAMILY_ARM64, + ANDROID_CPU_FAMILY_X86_64, + ANDROID_CPU_FAMILY_MIPS64, + + ANDROID_CPU_FAMILY_MAX /* do not remove */ + +} AndroidCpuFamily; + +/* Return the CPU family of the current process. + * + * Note that this matches the bitness of the current process. I.e. when + * running a 32-bit binary on a 64-bit capable CPU, this will return the + * 32-bit CPU family value. + */ +extern AndroidCpuFamily android_getCpuFamily(void); + +/* Return a bitmap describing a set of optional CPU features that are + * supported by the current device's CPU. The exact bit-flags returned + * depend on the value returned by android_getCpuFamily(). See the + * documentation for the ANDROID_CPU_*_FEATURE_* flags below for details. + */ +extern uint64_t android_getCpuFeatures(void); + +/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be + * recognized by the library (see note below for 64-bit ARM). Value details + * are: + * + * VFPv2: + * CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs + * support these instructions. VFPv2 is a subset of VFPv3 so this will + * be set whenever VFPv3 is set too. + * + * ARMv7: + * CPU supports the ARMv7-A basic instruction set. + * This feature is mandated by the 'armeabi-v7a' ABI. + * + * VFPv3: + * CPU supports the VFPv3-D16 instruction set, providing hardware FPU + * support for single and double precision floating point registers. + * Note that only 16 FPU registers are available by default, unless + * the D32 bit is set too. This feature is also mandated by the + * 'armeabi-v7a' ABI. + * + * VFP_D32: + * CPU VFP optional extension that provides 32 FPU registers, + * instead of 16. Note that ARM mandates this feature is the 'NEON' + * feature is implemented by the CPU. + * + * NEON: + * CPU FPU supports "ARM Advanced SIMD" instructions, also known as + * NEON. Note that this mandates the VFP_D32 feature as well, per the + * ARM Architecture specification. + * + * VFP_FP16: + * Half-width floating precision VFP extension. If set, the CPU + * supports instructions to perform floating-point operations on + * 16-bit registers. This is part of the VFPv4 specification, but + * not mandated by any Android ABI. + * + * VFP_FMA: + * Fused multiply-accumulate VFP instructions extension. Also part of + * the VFPv4 specification, but not mandated by any Android ABI. + * + * NEON_FMA: + * Fused multiply-accumulate NEON instructions extension. Optional + * extension from the VFPv4 specification, but not mandated by any + * Android ABI. + * + * IDIV_ARM: + * Integer division available in ARM mode. Only available + * on recent CPUs (e.g. Cortex-A15). + * + * IDIV_THUMB2: + * Integer division available in Thumb-2 mode. Only available + * on recent CPUs (e.g. Cortex-A15). + * + * iWMMXt: + * Optional extension that adds MMX registers and operations to an + * ARM CPU. This is only available on a few XScale-based CPU designs + * sold by Marvell. Pretty rare in practice. + * + * AES: + * CPU supports AES instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * CRC32: + * CPU supports CRC32 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * SHA2: + * CPU supports SHA2 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * SHA1: + * CPU supports SHA1 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * PMULL: + * CPU supports 64-bit PMULL and PMULL2 instructions. These + * instructions are only available for 32-bit applications + * running on ARMv8 CPU. + * + * If you want to tell the compiler to generate code that targets one of + * the feature set above, you should probably use one of the following + * flags (for more details, see technical note at the end of this file): + * + * -mfpu=vfp + * -mfpu=vfpv2 + * These are equivalent and tell GCC to use VFPv2 instructions for + * floating-point operations. Use this if you want your code to + * run on *some* ARMv6 devices, and any ARMv7-A device supported + * by Android. + * + * Generated code requires VFPv2 feature. + * + * -mfpu=vfpv3-d16 + * Tell GCC to use VFPv3 instructions (using only 16 FPU registers). + * This should be generic code that runs on any CPU that supports the + * 'armeabi-v7a' Android ABI. Note that no ARMv6 CPU supports this. + * + * Generated code requires VFPv3 feature. + * + * -mfpu=vfpv3 + * Tell GCC to use VFPv3 instructions with 32 FPU registers. + * Generated code requires VFPv3|VFP_D32 features. + * + * -mfpu=neon + * Tell GCC to use VFPv3 instructions with 32 FPU registers, and + * also support NEON intrinsics (see ). + * Generated code requires VFPv3|VFP_D32|NEON features. + * + * -mfpu=vfpv4-d16 + * Generated code requires VFPv3|VFP_FP16|VFP_FMA features. + * + * -mfpu=vfpv4 + * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32 features. + * + * -mfpu=neon-vfpv4 + * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32|NEON|NEON_FMA + * features. + * + * -mcpu=cortex-a7 + * -mcpu=cortex-a15 + * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32| + * NEON|NEON_FMA|IDIV_ARM|IDIV_THUMB2 + * This flag implies -mfpu=neon-vfpv4. + * + * -mcpu=iwmmxt + * Allows the use of iWMMXt instrinsics with GCC. + * + * IMPORTANT NOTE: These flags should only be tested when + * android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a + * 32-bit process. + * + * When running a 64-bit ARM process on an ARMv8 CPU, + * android_getCpuFeatures() will return a different set of bitflags + */ +enum +{ + ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0), + ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1), + ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2), + ANDROID_CPU_ARM_FEATURE_LDREX_STREX = (1 << 3), + ANDROID_CPU_ARM_FEATURE_VFPv2 = (1 << 4), + ANDROID_CPU_ARM_FEATURE_VFP_D32 = (1 << 5), + ANDROID_CPU_ARM_FEATURE_VFP_FP16 = (1 << 6), + ANDROID_CPU_ARM_FEATURE_VFP_FMA = (1 << 7), + ANDROID_CPU_ARM_FEATURE_NEON_FMA = (1 << 8), + ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9), + ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10), + ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11), + ANDROID_CPU_ARM_FEATURE_AES = (1 << 12), + ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13), + ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14), + ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15), + ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16), +}; + +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details + * are: + * + * FP: + * CPU has Floating-point unit. + * + * ASIMD: + * CPU has Advanced SIMD unit. + * + * AES: + * CPU supports AES instructions. + * + * CRC32: + * CPU supports CRC32 instructions. + * + * SHA2: + * CPU supports SHA2 instructions. + * + * SHA1: + * CPU supports SHA1 instructions. + * + * PMULL: + * CPU supports 64-bit PMULL and PMULL2 instructions. + */ +enum +{ + ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0), + ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1), + ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2), + ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3), + ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4), + ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5), + ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6), +}; + +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86 or + * ANDROID_CPU_FAMILY_X86_64. + */ +enum +{ + ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0), + ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1), + ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2), + ANDROID_CPU_X86_FEATURE_SSE4_1 = (1 << 3), + ANDROID_CPU_X86_FEATURE_SSE4_2 = (1 << 4), + ANDROID_CPU_X86_FEATURE_AES_NI = (1 << 5), + ANDROID_CPU_X86_FEATURE_AVX = (1 << 6), + ANDROID_CPU_X86_FEATURE_RDRAND = (1 << 7), + ANDROID_CPU_X86_FEATURE_AVX2 = (1 << 8), + ANDROID_CPU_X86_FEATURE_SHA_NI = (1 << 9), +}; + +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_MIPS + * or ANDROID_CPU_FAMILY_MIPS64. Values are: + * + * R6: + * CPU executes MIPS Release 6 instructions natively, and + * supports obsoleted R1..R5 instructions only via kernel traps. + * + * MSA: + * CPU supports Mips SIMD Architecture instructions. + */ +enum +{ + ANDROID_CPU_MIPS_FEATURE_R6 = (1 << 0), + ANDROID_CPU_MIPS_FEATURE_MSA = (1 << 1), +}; + +/* Return the number of CPU cores detected on this device. */ +extern int android_getCpuCount(void); + +/* The following is used to force the CPU count and features + * mask in sandboxed processes. Under 4.1 and higher, these processes + * cannot access /proc, which is the only way to get information from + * the kernel about the current hardware (at least on ARM). + * + * It _must_ be called only once, and before any android_getCpuXXX + * function, any other case will fail. + * + * This function return 1 on success, and 0 on failure. + */ +extern int android_setCpu(int cpu_count, uint64_t cpu_features); + +#ifdef __arm__ +/* Retrieve the ARM 32-bit CPUID value from the kernel. + * Note that this cannot work on sandboxed processes under 4.1 and + * higher, unless you called android_setCpuArm() before. + */ +extern uint32_t android_getCpuIdArm(void); + +/* An ARM-specific variant of android_setCpu() that also allows you + * to set the ARM CPUID field. + */ +extern int android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id); +#endif + +__END_DECLS + +#endif /* CPU_FEATURES_H */ diff --git a/winpr/libwinpr/sysinfo/sysinfo.c b/winpr/libwinpr/sysinfo/sysinfo.c new file mode 100644 index 0000000..f12f4eb --- /dev/null +++ b/winpr/libwinpr/sysinfo/sysinfo.c @@ -0,0 +1,1122 @@ +/** + * WinPR: Windows Portable Runtime + * System Information + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2013 Bernhard Miklautz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#if defined(ANDROID) +#include "cpufeatures/cpu-features.h" +#endif + +#if defined(__linux__) +#include +#include +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("sysinfo") + +/** + * api-ms-win-core-sysinfo-l1-1-1.dll: + * + * EnumSystemFirmwareTables + * GetSystemFirmwareTable + * GetLogicalProcessorInformation + * GetLogicalProcessorInformationEx + * GetProductInfo + * GetSystemDirectoryA + * GetSystemDirectoryW + * GetSystemTimeAdjustment + * GetSystemWindowsDirectoryA + * GetSystemWindowsDirectoryW + * GetWindowsDirectoryA + * GetWindowsDirectoryW + * GlobalMemoryStatusEx + * SetComputerNameExW + * VerSetConditionMask + */ + +#ifndef _WIN32 + +#include +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include +#include + +#if defined(__MACOSX__) || defined(__IOS__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) || defined(__DragonFly__) +#include +#endif + +static DWORD GetProcessorArchitecture(void) +{ + DWORD cpuArch = PROCESSOR_ARCHITECTURE_UNKNOWN; +#if defined(ANDROID) + AndroidCpuFamily family = android_getCpuFamily(); + + switch (family) + { + case ANDROID_CPU_FAMILY_ARM: + return PROCESSOR_ARCHITECTURE_ARM; + + case ANDROID_CPU_FAMILY_X86: + return PROCESSOR_ARCHITECTURE_INTEL; + + case ANDROID_CPU_FAMILY_MIPS: + return PROCESSOR_ARCHITECTURE_MIPS; + + case ANDROID_CPU_FAMILY_ARM64: + return PROCESSOR_ARCHITECTURE_ARM64; + + case ANDROID_CPU_FAMILY_X86_64: + return PROCESSOR_ARCHITECTURE_AMD64; + + case ANDROID_CPU_FAMILY_MIPS64: + return PROCESSOR_ARCHITECTURE_MIPS64; + + default: + return PROCESSOR_ARCHITECTURE_UNKNOWN; + } + +#elif defined(_M_ARM) + cpuArch = PROCESSOR_ARCHITECTURE_ARM; +#elif defined(_M_IX86) + cpuArch = PROCESSOR_ARCHITECTURE_INTEL; +#elif defined(_M_MIPS64) + /* Needs to be before __mips__ since the compiler defines both */ + cpuArch = PROCESSOR_ARCHITECTURE_MIPS64; +#elif defined(_M_MIPS) + cpuArch = PROCESSOR_ARCHITECTURE_MIPS; +#elif defined(_M_ARM64) + cpuArch = PROCESSOR_ARCHITECTURE_ARM64; +#elif defined(_M_AMD64) + cpuArch = PROCESSOR_ARCHITECTURE_AMD64; +#elif defined(_M_PPC) + cpuArch = PROCESSOR_ARCHITECTURE_PPC; +#elif defined(_M_ALPHA) + cpuArch = PROCESSOR_ARCHITECTURE_ALPHA; +#elif defined(_M_E2K) + cpuArch = PROCESSOR_ARCHITECTURE_E2K; +#endif + return cpuArch; +} + +static DWORD GetNumberOfProcessors(void) +{ + DWORD numCPUs = 1; +#if defined(ANDROID) + return android_getCpuCount(); + /* TODO: iOS */ +#elif defined(__linux__) || defined(__sun) || defined(_AIX) + numCPUs = (DWORD)sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(__MACOSX__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) || defined(__DragonFly__) + { + int mib[4]; + size_t length = sizeof(numCPUs); + mib[0] = CTL_HW; +#if defined(__FreeBSD__) || defined(__OpenBSD__) + mib[1] = HW_NCPU; +#else + mib[1] = HW_AVAILCPU; +#endif + sysctl(mib, 2, &numCPUs, &length, NULL, 0); + + if (numCPUs < 1) + { + mib[1] = HW_NCPU; + sysctl(mib, 2, &numCPUs, &length, NULL, 0); + + if (numCPUs < 1) + numCPUs = 1; + } + } +#elif defined(__hpux) + numCPUs = (DWORD)mpctl(MPC_GETNUMSPUS, NULL, NULL); +#elif defined(__sgi) + numCPUs = (DWORD)sysconf(_SC_NPROC_ONLN); +#endif + return numCPUs; +} + +static DWORD GetSystemPageSize(void) +{ + DWORD dwPageSize = 0; + long sc_page_size = -1; +#if defined(_SC_PAGESIZE) + + if (sc_page_size < 0) + sc_page_size = sysconf(_SC_PAGESIZE); + +#endif +#if defined(_SC_PAGE_SIZE) + + if (sc_page_size < 0) + sc_page_size = sysconf(_SC_PAGE_SIZE); + +#endif + + if (sc_page_size > 0) + dwPageSize = (DWORD)sc_page_size; + + if (dwPageSize < 4096) + dwPageSize = 4096; + + return dwPageSize; +} + +void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo) +{ + lpSystemInfo->wProcessorArchitecture = GetProcessorArchitecture(); + lpSystemInfo->wReserved = 0; + lpSystemInfo->dwPageSize = GetSystemPageSize(); + lpSystemInfo->lpMinimumApplicationAddress = NULL; + lpSystemInfo->lpMaximumApplicationAddress = NULL; + lpSystemInfo->dwActiveProcessorMask = 0; + lpSystemInfo->dwNumberOfProcessors = GetNumberOfProcessors(); + lpSystemInfo->dwProcessorType = 0; + lpSystemInfo->dwAllocationGranularity = 0; + lpSystemInfo->wProcessorLevel = 0; + lpSystemInfo->wProcessorRevision = 0; +} + +void GetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo) +{ + GetSystemInfo(lpSystemInfo); +} + +void GetSystemTime(LPSYSTEMTIME lpSystemTime) +{ + time_t ct = 0; + struct tm tres; + struct tm* stm = NULL; + WORD wMilliseconds = 0; + ct = time(NULL); + wMilliseconds = (WORD)(GetTickCount() % 1000); + stm = gmtime_r(&ct, &tres); + ZeroMemory(lpSystemTime, sizeof(SYSTEMTIME)); + + if (stm) + { + lpSystemTime->wYear = (WORD)(stm->tm_year + 1900); + lpSystemTime->wMonth = (WORD)(stm->tm_mon + 1); + lpSystemTime->wDayOfWeek = (WORD)stm->tm_wday; + lpSystemTime->wDay = (WORD)stm->tm_mday; + lpSystemTime->wHour = (WORD)stm->tm_hour; + lpSystemTime->wMinute = (WORD)stm->tm_min; + lpSystemTime->wSecond = (WORD)stm->tm_sec; + lpSystemTime->wMilliseconds = wMilliseconds; + } +} + +BOOL SetSystemTime(CONST SYSTEMTIME* lpSystemTime) +{ + /* TODO: Implement */ + return FALSE; +} + +VOID GetLocalTime(LPSYSTEMTIME lpSystemTime) +{ + time_t ct = 0; + struct tm tres; + struct tm* ltm = NULL; + WORD wMilliseconds = 0; + ct = time(NULL); + wMilliseconds = (WORD)(GetTickCount() % 1000); + ltm = localtime_r(&ct, &tres); + ZeroMemory(lpSystemTime, sizeof(SYSTEMTIME)); + + if (ltm) + { + lpSystemTime->wYear = (WORD)(ltm->tm_year + 1900); + lpSystemTime->wMonth = (WORD)(ltm->tm_mon + 1); + lpSystemTime->wDayOfWeek = (WORD)ltm->tm_wday; + lpSystemTime->wDay = (WORD)ltm->tm_mday; + lpSystemTime->wHour = (WORD)ltm->tm_hour; + lpSystemTime->wMinute = (WORD)ltm->tm_min; + lpSystemTime->wSecond = (WORD)ltm->tm_sec; + lpSystemTime->wMilliseconds = wMilliseconds; + } +} + +BOOL SetLocalTime(CONST SYSTEMTIME* lpSystemTime) +{ + /* TODO: Implement */ + return FALSE; +} + +VOID GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) +{ + ULARGE_INTEGER time64; + time64.u.HighPart = 0; + /* time represented in tenths of microseconds since midnight of January 1, 1601 */ + time64.QuadPart = time(NULL) + 11644473600LL; /* Seconds since January 1, 1601 */ + time64.QuadPart *= 10000000; /* Convert timestamp to tenths of a microsecond */ + lpSystemTimeAsFileTime->dwLowDateTime = time64.u.LowPart; + lpSystemTimeAsFileTime->dwHighDateTime = time64.u.HighPart; +} + +BOOL GetSystemTimeAdjustment(PDWORD lpTimeAdjustment, PDWORD lpTimeIncrement, + PBOOL lpTimeAdjustmentDisabled) +{ + /* TODO: Implement */ + return FALSE; +} + +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW 4 +#endif + +DWORD GetTickCount(void) +{ + DWORD ticks = 0; +#ifdef __linux__ + struct timespec ts; + + if (!clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) + ticks = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); + +#else + /** + * FIXME: this is relative to the Epoch time, and we + * need to return a value relative to the system uptime. + */ + struct timeval tv; + + if (!gettimeofday(&tv, NULL)) + ticks = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + +#endif + return ticks; +} +#endif // _WIN32 + +#if !defined(_WIN32) || defined(_UWP) + +#if defined(WITH_WINPR_DEPRECATED) +/* OSVERSIONINFOEX Structure: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724833 + */ + +BOOL GetVersionExA(LPOSVERSIONINFOA lpVersionInformation) +{ +#ifdef _UWP + + /* Windows 10 Version Info */ + if ((lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) || + (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA))) + { + lpVersionInformation->dwMajorVersion = 10; + lpVersionInformation->dwMinorVersion = 0; + lpVersionInformation->dwBuildNumber = 0; + lpVersionInformation->dwPlatformId = VER_PLATFORM_WIN32_NT; + ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion)); + + if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)) + { + LPOSVERSIONINFOEXA lpVersionInformationEx = (LPOSVERSIONINFOEXA)lpVersionInformation; + lpVersionInformationEx->wServicePackMajor = 0; + lpVersionInformationEx->wServicePackMinor = 0; + lpVersionInformationEx->wSuiteMask = 0; + lpVersionInformationEx->wProductType = VER_NT_WORKSTATION; + lpVersionInformationEx->wReserved = 0; + } + + return TRUE; + } + +#else + + /* Windows 7 SP1 Version Info */ + if ((lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) || + (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA))) + { + lpVersionInformation->dwMajorVersion = 6; + lpVersionInformation->dwMinorVersion = 1; + lpVersionInformation->dwBuildNumber = 7601; + lpVersionInformation->dwPlatformId = VER_PLATFORM_WIN32_NT; + ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion)); + + if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)) + { + LPOSVERSIONINFOEXA lpVersionInformationEx = (LPOSVERSIONINFOEXA)lpVersionInformation; + lpVersionInformationEx->wServicePackMajor = 1; + lpVersionInformationEx->wServicePackMinor = 0; + lpVersionInformationEx->wSuiteMask = 0; + lpVersionInformationEx->wProductType = VER_NT_WORKSTATION; + lpVersionInformationEx->wReserved = 0; + } + + return TRUE; + } + +#endif + return FALSE; +} + +BOOL GetVersionExW(LPOSVERSIONINFOW lpVersionInformation) +{ + ZeroMemory(lpVersionInformation->szCSDVersion, sizeof(lpVersionInformation->szCSDVersion)); + return GetVersionExA((LPOSVERSIONINFOA)lpVersionInformation); +} + +#endif + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +BOOL GetComputerNameW(LPWSTR lpBuffer, LPDWORD lpnSize) +{ + BOOL rc = 0; + LPSTR buffer = NULL; + if (!lpnSize || (*lpnSize > INT_MAX)) + return FALSE; + + if (*lpnSize > 0) + { + buffer = malloc(*lpnSize); + if (!buffer) + return FALSE; + } + rc = GetComputerNameA(buffer, lpnSize); + + if (rc && (*lpnSize > 0)) + { + const SSIZE_T res = ConvertUtf8NToWChar(buffer, *lpnSize, lpBuffer, *lpnSize); + rc = res > 0; + } + + free(buffer); + + return rc; +} + +BOOL GetComputerNameA(LPSTR lpBuffer, LPDWORD lpnSize) +{ + char* dot = NULL; + size_t length = 0; + char hostname[256] = { 0 }; + + if (!lpnSize) + { + SetLastError(ERROR_BAD_ARGUMENTS); + return FALSE; + } + + if (gethostname(hostname, sizeof(hostname)) == -1) + return FALSE; + + length = strnlen(hostname, sizeof(hostname)); + dot = strchr(hostname, '.'); + + if (dot) + length = (dot - hostname); + + if ((*lpnSize <= (DWORD)length) || !lpBuffer) + { + SetLastError(ERROR_BUFFER_OVERFLOW); + *lpnSize = (DWORD)(length + 1); + return FALSE; + } + + CopyMemory(lpBuffer, hostname, length); + lpBuffer[length] = '\0'; + *lpnSize = (DWORD)length; + return TRUE; +} + +BOOL GetComputerNameExA(COMPUTER_NAME_FORMAT NameType, LPSTR lpBuffer, LPDWORD lpnSize) +{ + size_t length = 0; + char hostname[256] = { 0 }; + + if (!lpnSize) + { + SetLastError(ERROR_BAD_ARGUMENTS); + return FALSE; + } + + if ((NameType == ComputerNameNetBIOS) || (NameType == ComputerNamePhysicalNetBIOS)) + { + BOOL rc = GetComputerNameA(lpBuffer, lpnSize); + + if (!rc) + { + if (GetLastError() == ERROR_BUFFER_OVERFLOW) + SetLastError(ERROR_MORE_DATA); + } + + return rc; + } + + if (gethostname(hostname, sizeof(hostname)) == -1) + return FALSE; + + length = strnlen(hostname, sizeof(hostname)); + + switch (NameType) + { + case ComputerNameDnsHostname: + case ComputerNameDnsDomain: + case ComputerNameDnsFullyQualified: + case ComputerNamePhysicalDnsHostname: + case ComputerNamePhysicalDnsDomain: + case ComputerNamePhysicalDnsFullyQualified: + if ((*lpnSize <= (DWORD)length) || !lpBuffer) + { + *lpnSize = (DWORD)(length + 1); + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + CopyMemory(lpBuffer, hostname, length); + lpBuffer[length] = '\0'; + *lpnSize = (DWORD)length; + break; + + default: + return FALSE; + } + + return TRUE; +} + +BOOL GetComputerNameExW(COMPUTER_NAME_FORMAT NameType, LPWSTR lpBuffer, LPDWORD lpnSize) +{ + BOOL rc = 0; + LPSTR lpABuffer = NULL; + + if (!lpnSize) + { + SetLastError(ERROR_BAD_ARGUMENTS); + return FALSE; + } + + if (*lpnSize > 0) + { + lpABuffer = calloc(*lpnSize, sizeof(CHAR)); + + if (!lpABuffer) + return FALSE; + } + + rc = GetComputerNameExA(NameType, lpABuffer, lpnSize); + + if (rc && (*lpnSize > 0)) + { + const SSIZE_T res = ConvertUtf8NToWChar(lpABuffer, *lpnSize, lpBuffer, *lpnSize); + rc = res > 0; + } + + free(lpABuffer); + return rc; +} + +#endif + +#if defined(_UWP) + +DWORD GetTickCount(void) +{ + return (DWORD)GetTickCount64(); +} + +#endif + +#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600)) + +ULONGLONG winpr_GetTickCount64(void) +{ + ULONGLONG ticks = 0; +#if defined(__linux__) + struct timespec ts; + + if (!clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) + ticks = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); + +#elif defined(_WIN32) + FILETIME ft; + ULARGE_INTEGER ul; + GetSystemTimeAsFileTime(&ft); + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + ticks = ul.QuadPart; +#else + /** + * FIXME: this is relative to the Epoch time, and we + * need to return a value relative to the system uptime. + */ + struct timeval tv; + + if (!gettimeofday(&tv, NULL)) + ticks = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + +#endif + return ticks; +} + +#endif + +/* If x86 */ +#ifdef _M_IX86_AMD64 + +#if defined(__GNUC__) +#define xgetbv(_func_, _lo_, _hi_) \ + __asm__ __volatile__("xgetbv" : "=a"(_lo_), "=d"(_hi_) : "c"(_func_)) +#elif defined(_MSC_VER) +#define xgetbv(_func_, _lo_, _hi_) \ + { \ + unsigned __int64 val = _xgetbv(_func_); \ + _lo_ = val & 0xFFFFFFFF; \ + _hi_ = (val >> 32); \ + } +#endif + +#define B_BIT_AVX2 (1 << 5) +#define B_BIT_AVX512F (1 << 16) +#define D_BIT_MMX (1 << 23) +#define D_BIT_SSE (1 << 25) +#define D_BIT_SSE2 (1 << 26) +#define D_BIT_3DN (1 << 30) +#define C_BIT_SSE3 (1 << 0) +#define C_BIT_PCLMULQDQ (1 << 1) +#define C81_BIT_LZCNT (1 << 5) +#define C_BIT_3DNP (1 << 8) +#define C_BIT_3DNP (1 << 8) +#define C_BIT_SSSE3 (1 << 9) +#define C_BIT_SSE41 (1 << 19) +#define C_BIT_SSE42 (1 << 20) +#define C_BIT_FMA (1 << 12) +#define C_BIT_AES (1 << 25) +#define C_BIT_XGETBV (1 << 27) +#define C_BIT_AVX (1 << 28) +#define E_BIT_XMM (1 << 1) +#define E_BIT_YMM (1 << 2) +#define E_BITS_AVX (E_BIT_XMM | E_BIT_YMM) + +static void cpuid(unsigned info, unsigned* eax, unsigned* ebx, unsigned* ecx, unsigned* edx) +{ +#ifdef __GNUC__ + *eax = *ebx = *ecx = *edx = 0; + __asm volatile( + /* The EBX (or RBX register on x86_64) is used for the PIC base address + * and must not be corrupted by our inline assembly. + */ +#ifdef _M_IX86 + "mov %%ebx, %%esi;" + "cpuid;" + "xchg %%ebx, %%esi;" +#else + "mov %%rbx, %%rsi;" + "cpuid;" + "xchg %%rbx, %%rsi;" +#endif + : "=a"(*eax), "=S"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(info), "c"(0)); +#elif defined(_MSC_VER) + int a[4]; + __cpuid(a, info); + *eax = a[0]; + *ebx = a[1]; + *ecx = a[2]; + *edx = a[3]; +#endif +} +#elif defined(_M_ARM) +#if defined(__linux__) +// HWCAP flags from linux kernel - uapi/asm/hwcap.h +#define HWCAP_SWP (1 << 0) +#define HWCAP_HALF (1 << 1) +#define HWCAP_THUMB (1 << 2) +#define HWCAP_26BIT (1 << 3) /* Play it safe */ +#define HWCAP_FAST_MULT (1 << 4) +#define HWCAP_FPA (1 << 5) +#define HWCAP_VFP (1 << 6) +#define HWCAP_EDSP (1 << 7) +#define HWCAP_JAVA (1 << 8) +#define HWCAP_IWMMXT (1 << 9) +#define HWCAP_CRUNCH (1 << 10) +#define HWCAP_THUMBEE (1 << 11) +#define HWCAP_NEON (1 << 12) +#define HWCAP_VFPv3 (1 << 13) +#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */ +#define HWCAP_TLS (1 << 15) +#define HWCAP_VFPv4 (1 << 16) +#define HWCAP_IDIVA (1 << 17) +#define HWCAP_IDIVT (1 << 18) +#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ +#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) + +// From linux kernel uapi/linux/auxvec.h +#define AT_HWCAP 16 + +static unsigned GetARMCPUCaps(void) +{ + unsigned caps = 0; + int fd = open("/proc/self/auxv", O_RDONLY); + + if (fd == -1) + return 0; + + static struct + { + unsigned a_type; /* Entry type */ + unsigned a_val; /* Integer value */ + } auxvec; + + while (1) + { + int num; + num = read(fd, (char*)&auxvec, sizeof(auxvec)); + + if (num < 1 || (auxvec.a_type == 0 && auxvec.a_val == 0)) + break; + + if (auxvec.a_type == AT_HWCAP) + { + caps = auxvec.a_val; + } + } + + close(fd); + return caps; +} + +#endif // defined(__linux__) +#endif // _M_IX86_AMD64 + +#ifndef _WIN32 + +BOOL IsProcessorFeaturePresent(DWORD ProcessorFeature) +{ + BOOL ret = FALSE; +#if defined(ANDROID) + const uint64_t features = android_getCpuFeatures(); + + switch (ProcessorFeature) + { + case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE: + case PF_ARM_NEON: + return features & ANDROID_CPU_ARM_FEATURE_NEON; + + default: + return FALSE; + } + +#elif defined(_M_ARM) +#ifdef __linux__ + const unsigned caps = GetARMCPUCaps(); + + switch (ProcessorFeature) + { + case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE: + case PF_ARM_NEON: + if (caps & HWCAP_NEON) + ret = TRUE; + + break; + + case PF_ARM_THUMB: + if (caps & HWCAP_THUMB) + ret = TRUE; + + case PF_ARM_VFP_32_REGISTERS_AVAILABLE: + if (caps & HWCAP_VFPD32) + ret = TRUE; + + case PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE: + if ((caps & HWCAP_IDIVA) || (caps & HWCAP_IDIVT)) + ret = TRUE; + + case PF_ARM_VFP3: + if (caps & HWCAP_VFPv3) + ret = TRUE; + + break; + + case PF_ARM_JAZELLE: + if (caps & HWCAP_JAVA) + ret = TRUE; + + break; + + case PF_ARM_DSP: + if (caps & HWCAP_EDSP) + ret = TRUE; + + break; + + case PF_ARM_MPU: + if (caps & HWCAP_EDSP) + ret = TRUE; + + break; + + case PF_ARM_THUMB2: + if ((caps & HWCAP_IDIVT) || (caps & HWCAP_VFPv4)) + ret = TRUE; + + break; + + case PF_ARM_T2EE: + if (caps & HWCAP_THUMBEE) + ret = TRUE; + + break; + + case PF_ARM_INTEL_WMMX: + if (caps & HWCAP_IWMMXT) + ret = TRUE; + + break; + + default: + break; + } + +#else // __linux__ + + switch (ProcessorFeature) + { + case PF_ARM_NEON_INSTRUCTIONS_AVAILABLE: + case PF_ARM_NEON: +#ifdef __ARM_NEON + ret = TRUE; +#endif + break; + default: + break; + } + +#endif // __linux__ +#elif defined(_M_IX86_AMD64) +#ifdef __GNUC__ + unsigned a = 0; + unsigned b = 0; + unsigned c = 0; + unsigned d = 0; + cpuid(1, &a, &b, &c, &d); + + switch (ProcessorFeature) + { + case PF_MMX_INSTRUCTIONS_AVAILABLE: + if (d & D_BIT_MMX) + ret = TRUE; + + break; + + case PF_XMMI_INSTRUCTIONS_AVAILABLE: + if (d & D_BIT_SSE) + ret = TRUE; + + break; + + case PF_XMMI64_INSTRUCTIONS_AVAILABLE: + if (d & D_BIT_SSE2) + ret = TRUE; + + break; + + case PF_3DNOW_INSTRUCTIONS_AVAILABLE: + if (d & D_BIT_3DN) + ret = TRUE; + + break; + + case PF_SSE3_INSTRUCTIONS_AVAILABLE: + if (c & C_BIT_SSE3) + ret = TRUE; + + break; + + default: + break; + } + +#endif // __GNUC__ +#elif defined(_M_E2K) + /* compiler flags on e2k arch determine CPU features */ + switch (ProcessorFeature) + { + case PF_MMX_INSTRUCTIONS_AVAILABLE: +#ifdef __MMX__ + ret = TRUE; +#endif + break; + + case PF_3DNOW_INSTRUCTIONS_AVAILABLE: +#ifdef __3dNOW__ + ret = TRUE; +#endif + break; + + case PF_SSE3_INSTRUCTIONS_AVAILABLE: +#ifdef __SSE3__ + ret = TRUE; +#endif + break; + + default: + break; + } + +#endif + return ret; +} + +#endif //_WIN32 + +DWORD GetTickCountPrecise(void) +{ +#ifdef _WIN32 + LARGE_INTEGER freq; + LARGE_INTEGER current; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(¤t); + return (DWORD)(current.QuadPart * 1000LL / freq.QuadPart); +#else + return GetTickCount(); +#endif +} + +BOOL IsProcessorFeaturePresentEx(DWORD ProcessorFeature) +{ + BOOL ret = FALSE; +#ifdef _M_ARM +#ifdef __linux__ + unsigned caps; + caps = GetARMCPUCaps(); + + switch (ProcessorFeature) + { + case PF_EX_ARM_VFP1: + if (caps & HWCAP_VFP) + ret = TRUE; + + break; + + case PF_EX_ARM_VFP3D16: + if (caps & HWCAP_VFPv3D16) + ret = TRUE; + + break; + + case PF_EX_ARM_VFP4: + if (caps & HWCAP_VFPv4) + ret = TRUE; + + break; + + case PF_EX_ARM_IDIVA: + if (caps & HWCAP_IDIVA) + ret = TRUE; + + break; + + case PF_EX_ARM_IDIVT: + if (caps & HWCAP_IDIVT) + ret = TRUE; + + break; + } + +#endif // __linux__ +#elif defined(_M_IX86_AMD64) + unsigned a = 0; + unsigned b = 0; + unsigned c = 0; + unsigned d = 0; + cpuid(1, &a, &b, &c, &d); + + switch (ProcessorFeature) + { + case PF_EX_LZCNT: + { + unsigned a81 = 0; + unsigned b81 = 0; + unsigned c81 = 0; + unsigned d81 = 0; + cpuid(0x80000001, &a81, &b81, &c81, &d81); + + if (c81 & C81_BIT_LZCNT) + ret = TRUE; + } + break; + + case PF_EX_3DNOW_PREFETCH: + if (c & C_BIT_3DNP) + ret = TRUE; + + break; + + case PF_EX_SSSE3: + if (c & C_BIT_SSSE3) + ret = TRUE; + + break; + + case PF_EX_SSE41: + if (c & C_BIT_SSE41) + ret = TRUE; + + break; + + case PF_EX_SSE42: + if (c & C_BIT_SSE42) + ret = TRUE; + + break; +#if defined(__GNUC__) || defined(_MSC_VER) + + case PF_EX_AVX: + case PF_EX_AVX2: + case PF_EX_AVX512F: + case PF_EX_FMA: + case PF_EX_AVX_AES: + case PF_EX_AVX_PCLMULQDQ: + { + /* Check for general AVX support */ + if (!(c & C_BIT_AVX)) + break; + + /* Check for xgetbv support */ + if (!(c & C_BIT_XGETBV)) + break; + + int e = 0; + int f = 0; + xgetbv(0, e, f); + + /* XGETBV enabled for applications and XMM/YMM states enabled */ + if ((e & E_BITS_AVX) == E_BITS_AVX) + { + switch (ProcessorFeature) + { + case PF_EX_AVX: + ret = TRUE; + break; + + case PF_EX_AVX2: + case PF_EX_AVX512F: + cpuid(7, &a, &b, &c, &d); + switch (ProcessorFeature) + { + case PF_EX_AVX2: + if (b & B_BIT_AVX2) + ret = TRUE; + break; + + case PF_EX_AVX512F: + if (b & B_BIT_AVX512F) + ret = TRUE; + break; + + default: + break; + } + break; + + case PF_EX_FMA: + if (c & C_BIT_FMA) + ret = TRUE; + + break; + + case PF_EX_AVX_AES: + if (c & C_BIT_AES) + ret = TRUE; + + break; + + case PF_EX_AVX_PCLMULQDQ: + if (c & C_BIT_PCLMULQDQ) + ret = TRUE; + + break; + } + } + } + break; +#endif // __GNUC__ || _MSC_VER + + default: + break; + } +#elif defined(_M_E2K) + /* compiler flags on e2k arch determine CPU features */ + switch (ProcessorFeature) + { + case PF_EX_LZCNT: +#ifdef __LZCNT__ + ret = TRUE; +#endif + break; + + case PF_EX_SSSE3: +#ifdef __SSSE3__ + ret = TRUE; +#endif + break; + + case PF_EX_SSE41: +#ifdef __SSE4_1__ + ret = TRUE; +#endif + break; + + case PF_EX_SSE42: +#ifdef __SSE4_2__ + ret = TRUE; +#endif + break; + + case PF_EX_AVX: +#ifdef __AVX__ + ret = TRUE; +#endif + break; + + case PF_EX_AVX2: +#ifdef __AVX2__ + ret = TRUE; +#endif + break; + + case PF_EX_FMA: +#ifdef __FMA__ + ret = TRUE; +#endif + break; + + default: + break; + } +#endif + return ret; +} diff --git a/winpr/libwinpr/sysinfo/test/CMakeLists.txt b/winpr/libwinpr/sysinfo/test/CMakeLists.txt new file mode 100644 index 0000000..2632e89 --- /dev/null +++ b/winpr/libwinpr/sysinfo/test/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(MODULE_NAME "TestSysInfo") +set(MODULE_PREFIX "TEST_SYSINFO") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestGetNativeSystemInfo.c + TestCPUFeatures.c + TestGetComputerName.c + TestSystemTime.c + TestLocalTime.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/sysinfo/test/TestCPUFeatures.c b/winpr/libwinpr/sysinfo/test/TestCPUFeatures.c new file mode 100644 index 0000000..8a596dd --- /dev/null +++ b/winpr/libwinpr/sysinfo/test/TestCPUFeatures.c @@ -0,0 +1,65 @@ + +#include +#include +#include + +#define TEST_FEATURE(feature) \ + printf("\t" #feature ": %s\n", IsProcessorFeaturePresent(feature) ? "yes" : "no") +#define TEST_FEATURE_EX(feature) \ + printf("\t" #feature ": %s\n", IsProcessorFeaturePresentEx(feature) ? "yes" : "no") +int TestCPUFeatures(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + printf("Base CPU Flags:\n"); +#ifdef _M_IX86_AMD64 + TEST_FEATURE(PF_MMX_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_XMMI_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_XMMI64_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_3DNOW_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_SSE3_INSTRUCTIONS_AVAILABLE); + printf("\n"); + printf("Extended CPU Flags (not found in windows API):\n"); + TEST_FEATURE_EX(PF_EX_3DNOW_PREFETCH); + TEST_FEATURE_EX(PF_EX_SSSE3); + TEST_FEATURE_EX(PF_EX_SSE41); + TEST_FEATURE_EX(PF_EX_SSE42); + TEST_FEATURE_EX(PF_EX_AVX); + TEST_FEATURE_EX(PF_EX_FMA); + TEST_FEATURE_EX(PF_EX_AVX_AES); + TEST_FEATURE_EX(PF_EX_AVX_PCLMULQDQ); +#elif defined(_M_ARM) + TEST_FEATURE(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_ARM_THUMB); + TEST_FEATURE(PF_ARM_VFP_32_REGISTERS_AVAILABLE); + TEST_FEATURE(PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE); + TEST_FEATURE(PF_ARM_VFP3); + TEST_FEATURE(PF_ARM_THUMB); + TEST_FEATURE(PF_ARM_JAZELLE); + TEST_FEATURE(PF_ARM_DSP); + TEST_FEATURE(PF_ARM_THUMB2); + TEST_FEATURE(PF_ARM_T2EE); + TEST_FEATURE(PF_ARM_INTEL_WMMX); + printf("Extended CPU Flags (not found in windows API):\n"); + TEST_FEATURE_EX(PF_EX_ARM_VFP1); + TEST_FEATURE_EX(PF_EX_ARM_VFP3D16); + TEST_FEATURE_EX(PF_EX_ARM_VFP4); + TEST_FEATURE_EX(PF_EX_ARM_IDIVA); + TEST_FEATURE_EX(PF_EX_ARM_IDIVT); +#elif defined(_M_E2K) + TEST_FEATURE(PF_MMX_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_3DNOW_INSTRUCTIONS_AVAILABLE); + TEST_FEATURE(PF_SSE3_INSTRUCTIONS_AVAILABLE); + printf("\n"); + printf("Extended CPU Flags (not found in windows API):\n"); + TEST_FEATURE_EX(PF_EX_SSSE3); + TEST_FEATURE_EX(PF_EX_SSE41); + TEST_FEATURE_EX(PF_EX_SSE42); + TEST_FEATURE_EX(PF_EX_AVX); + TEST_FEATURE_EX(PF_EX_FMA); +#endif + printf("\n"); + return 0; +} diff --git a/winpr/libwinpr/sysinfo/test/TestGetComputerName.c b/winpr/libwinpr/sysinfo/test/TestGetComputerName.c new file mode 100644 index 0000000..4444056 --- /dev/null +++ b/winpr/libwinpr/sysinfo/test/TestGetComputerName.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include + +static BOOL Test_GetComputerName(void) +{ + /** + * BOOL WINAPI GetComputerName(LPTSTR lpBuffer, LPDWORD lpnSize); + * + * GetComputerName retrieves the NetBIOS name of the local computer. + * + * lpBuffer [out] + * A pointer to a buffer that receives the computer name or the cluster virtual server name. + * The buffer size should be large enough to contain MAX_COMPUTERNAME_LENGTH + 1 characters. + * + * lpnSize [in, out] + * On input, specifies the size of the buffer, in TCHARs. + * On output, the number of TCHARs copied to the destination buffer, not including the + * terminating null character. If the buffer is too small, the function fails and GetLastError + * returns ERROR_BUFFER_OVERFLOW. The lpnSize parameter specifies the size of the buffer + * required, including the terminating null character + * + */ + + CHAR netbiosName1[MAX_COMPUTERNAME_LENGTH + 1]; + CHAR netbiosName2[MAX_COMPUTERNAME_LENGTH + 1]; + const DWORD netbiosBufferSize = sizeof(netbiosName1) / sizeof(CHAR); + DWORD dwSize = 0; + DWORD dwNameLength = 0; + DWORD dwError = 0; + + memset(netbiosName1, 0xAA, netbiosBufferSize); + memset(netbiosName2, 0xBB, netbiosBufferSize); + + /* test with null buffer and zero size (required if buffer is null) */ + dwSize = 0; + if (GetComputerNameA(NULL, &dwSize) == TRUE) + { + fprintf(stderr, "%s: (1) GetComputerNameA unexpectedly succeeded with null buffer\n", + __func__); + return FALSE; + } + if ((dwError = GetLastError()) != ERROR_BUFFER_OVERFLOW) + { + fprintf(stderr, + "%s: (2) GetLastError returned 0x%08" PRIX32 " (expected ERROR_BUFFER_OVERFLOW)\n", + __func__, dwError); + return FALSE; + } + + /* test with valid buffer and zero size */ + dwSize = 0; + if (GetComputerNameA(netbiosName1, &dwSize) == TRUE) + { + fprintf(stderr, + "%s: (3) GetComputerNameA unexpectedly succeeded with zero size parameter\n", + __func__); + return FALSE; + } + if ((dwError = GetLastError()) != ERROR_BUFFER_OVERFLOW) + { + fprintf(stderr, + "%s: (4) GetLastError returned 0x%08" PRIX32 " (expected ERROR_BUFFER_OVERFLOW)\n", + __func__, dwError); + return FALSE; + } + /* check if returned size is valid: must be the size of the buffer required, including the + * terminating null character in this case */ + if (dwSize < 2 || dwSize > netbiosBufferSize) + { + fprintf(stderr, + "%s: (5) GetComputerNameA returned wrong size %" PRIu32 + " (expected something in the range from 2 to %" PRIu32 ")\n", + __func__, dwSize, netbiosBufferSize); + return FALSE; + } + dwNameLength = dwSize - 1; + + /* test with returned size */ + if (GetComputerNameA(netbiosName1, &dwSize) == FALSE) + { + fprintf(stderr, "%s: (6) GetComputerNameA failed with error: 0x%08" PRIX32 "\n", __func__, + GetLastError()); + return FALSE; + } + /* check if returned size is valid */ + if (dwSize != dwNameLength) + { + fprintf(stderr, + "%s: (7) GetComputerNameA returned wrong size %" PRIu32 " (expected %" PRIu32 ")\n", + __func__, dwSize, dwNameLength); + return FALSE; + } + /* check if string is correctly terminated */ + if (netbiosName1[dwSize] != 0) + { + fprintf(stderr, "%s: (8) string termination error\n", __func__); + return FALSE; + } + + /* test with real buffer size */ + dwSize = netbiosBufferSize; + if (GetComputerNameA(netbiosName2, &dwSize) == FALSE) + { + fprintf(stderr, "%s: (9) GetComputerNameA failed with error: 0x%08" PRIX32 "\n", __func__, + GetLastError()); + return FALSE; + } + /* check if returned size is valid */ + if (dwSize != dwNameLength) + { + fprintf(stderr, + "%s: (10) GetComputerNameA returned wrong size %" PRIu32 " (expected %" PRIu32 + ")\n", + __func__, dwSize, dwNameLength); + return FALSE; + } + /* check if string is correctly terminated */ + if (netbiosName2[dwSize] != 0) + { + fprintf(stderr, "%s: (11) string termination error\n", __func__); + return FALSE; + } + + /* compare the results */ + if (strcmp(netbiosName1, netbiosName2)) + { + fprintf(stderr, "%s: (12) string compare mismatch\n", __func__); + return FALSE; + } + + /* test with off by one buffer size */ + dwSize = dwNameLength; + if (GetComputerNameA(netbiosName1, &dwSize) == TRUE) + { + fprintf(stderr, + "%s: (13) GetComputerNameA unexpectedly succeeded with limited buffer size\n", + __func__); + return FALSE; + } + /* check if returned size is valid */ + if (dwSize != dwNameLength + 1) + { + fprintf(stderr, + "%s: (14) GetComputerNameA returned wrong size %" PRIu32 " (expected %" PRIu32 + ")\n", + __func__, dwSize, dwNameLength + 1); + return FALSE; + } + + return TRUE; +} + +static BOOL Test_GetComputerNameEx_Format(COMPUTER_NAME_FORMAT format) +{ + /** + * BOOL WINAPI GetComputerNameEx(COMPUTER_NAME_FORMAT NameType, LPTSTR lpBuffer, LPDWORD + * lpnSize); + * + * Retrieves a NetBIOS or DNS name associated with the local computer. + * + * NameType [in] + * ComputerNameNetBIOS + * ComputerNameDnsHostname + * ComputerNameDnsDomain + * ComputerNameDnsFullyQualified + * ComputerNamePhysicalNetBIOS + * ComputerNamePhysicalDnsHostname + * ComputerNamePhysicalDnsDomain + * ComputerNamePhysicalDnsFullyQualified + * + * lpBuffer [out] + * A pointer to a buffer that receives the computer name or the cluster virtual server name. + * The length of the name may be greater than MAX_COMPUTERNAME_LENGTH characters because DNS + * allows longer names. To ensure that this buffer is large enough, set this parameter to NULL + * and use the required buffer size returned in the lpnSize parameter. + * + * lpnSize [in, out] + * On input, specifies the size of the buffer, in TCHARs. + * On output, receives the number of TCHARs copied to the destination buffer, not including the + * terminating null character. If the buffer is too small, the function fails and GetLastError + * returns ERROR_MORE_DATA. This parameter receives the size of the buffer required, including + * the terminating null character. If lpBuffer is NULL, this parameter must be zero. + * + */ + + CHAR computerName1[255 + 1]; + CHAR computerName2[255 + 1]; + + const DWORD nameBufferSize = sizeof(computerName1) / sizeof(CHAR); + DWORD dwSize = 0; + DWORD dwMinSize = 0; + DWORD dwNameLength = 0; + DWORD dwError = 0; + + memset(computerName1, 0xAA, nameBufferSize); + memset(computerName2, 0xBB, nameBufferSize); + + if (format == ComputerNameDnsDomain || format == ComputerNamePhysicalDnsDomain) + { + /* domain names may be empty, terminating null only */ + dwMinSize = 1; + } + else + { + /* computer names must be at least 1 character */ + dwMinSize = 2; + } + + /* test with null buffer and zero size (required if buffer is null) */ + dwSize = 0; + if (GetComputerNameExA(format, NULL, &dwSize) == TRUE) + { + fprintf(stderr, "%s: (1/%d) GetComputerNameExA unexpectedly succeeded with null buffer\n", + __func__, format); + return FALSE; + } + if ((dwError = GetLastError()) != ERROR_MORE_DATA) + { + fprintf(stderr, + "%s: (2/%d) GetLastError returned 0x%08" PRIX32 " (expected ERROR_MORE_DATA)\n", + __func__, format, dwError); + return FALSE; + } + + /* test with valid buffer and zero size */ + dwSize = 0; + if (GetComputerNameExA(format, computerName1, &dwSize) == TRUE) + { + fprintf(stderr, + "%s: (3/%d) GetComputerNameExA unexpectedly succeeded with zero size parameter\n", + __func__, format); + return FALSE; + } + if ((dwError = GetLastError()) != ERROR_MORE_DATA) + { + fprintf(stderr, + "%s: (4/%d) GetLastError returned 0x%08" PRIX32 " (expected ERROR_MORE_DATA)\n", + __func__, format, dwError); + return FALSE; + } + /* check if returned size is valid: must be the size of the buffer required, including the + * terminating null character in this case */ + if (dwSize < dwMinSize || dwSize > nameBufferSize) + { + fprintf(stderr, + "%s: (5/%d) GetComputerNameExA returned wrong size %" PRIu32 + " (expected something in the range from %" PRIu32 " to %" PRIu32 ")\n", + __func__, format, dwSize, dwMinSize, nameBufferSize); + return FALSE; + } + dwNameLength = dwSize - 1; + + /* test with returned size */ + if (GetComputerNameExA(format, computerName1, &dwSize) == FALSE) + { + fprintf(stderr, "%s: (6/%d) GetComputerNameExA failed with error: 0x%08" PRIX32 "\n", + __func__, format, GetLastError()); + return FALSE; + } + /* check if returned size is valid */ + if (dwSize != dwNameLength) + { + fprintf(stderr, + "%s: (7/%d) GetComputerNameExA returned wrong size %" PRIu32 " (expected %" PRIu32 + ")\n", + __func__, format, dwSize, dwNameLength); + return FALSE; + } + /* check if string is correctly terminated */ + if (computerName1[dwSize] != 0) + { + fprintf(stderr, "%s: (8/%d) string termination error\n", __func__, format); + return FALSE; + } + + /* test with real buffer size */ + dwSize = nameBufferSize; + if (GetComputerNameExA(format, computerName2, &dwSize) == FALSE) + { + fprintf(stderr, "%s: (9/%d) GetComputerNameExA failed with error: 0x%08" PRIX32 "\n", + __func__, format, GetLastError()); + return FALSE; + } + /* check if returned size is valid */ + if (dwSize != dwNameLength) + { + fprintf(stderr, + "%s: (10/%d) GetComputerNameExA returned wrong size %" PRIu32 " (expected %" PRIu32 + ")\n", + __func__, format, dwSize, dwNameLength); + return FALSE; + } + /* check if string is correctly terminated */ + if (computerName2[dwSize] != 0) + { + fprintf(stderr, "%s: (11/%d) string termination error\n", __func__, format); + return FALSE; + } + + /* compare the results */ + if (strcmp(computerName1, computerName2)) + { + fprintf(stderr, "%s: (12/%d) string compare mismatch\n", __func__, format); + return FALSE; + } + + /* test with off by one buffer size */ + dwSize = dwNameLength; + if (GetComputerNameExA(format, computerName1, &dwSize) == TRUE) + { + fprintf(stderr, + "%s: (13/%d) GetComputerNameExA unexpectedly succeeded with limited buffer size\n", + __func__, format); + return FALSE; + } + /* check if returned size is valid */ + if (dwSize != dwNameLength + 1) + { + fprintf(stderr, + "%s: (14/%d) GetComputerNameExA returned wrong size %" PRIu32 " (expected %" PRIu32 + ")\n", + __func__, format, dwSize, dwNameLength + 1); + return FALSE; + } + + return TRUE; +} + +int TestGetComputerName(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!Test_GetComputerName()) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNameNetBIOS)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNameDnsHostname)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNameDnsDomain)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNameDnsFullyQualified)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalNetBIOS)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalDnsHostname)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalDnsDomain)) + return -1; + + if (!Test_GetComputerNameEx_Format(ComputerNamePhysicalDnsFullyQualified)) + return -1; + + return 0; +} diff --git a/winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c b/winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c new file mode 100644 index 0000000..f227164 --- /dev/null +++ b/winpr/libwinpr/sysinfo/test/TestGetNativeSystemInfo.c @@ -0,0 +1,29 @@ + +#include +#include + +int TestGetNativeSystemInfo(int argc, char* argv[]) +{ + SYSTEM_INFO sysinfo; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + GetNativeSystemInfo(&sysinfo); + + printf("SystemInfo:\n"); + printf("\twProcessorArchitecture: %" PRIu16 "\n", sysinfo.wProcessorArchitecture); + printf("\twReserved: %" PRIu16 "\n", sysinfo.wReserved); + printf("\tdwPageSize: 0x%08" PRIX32 "\n", sysinfo.dwPageSize); + printf("\tlpMinimumApplicationAddress: %p\n", sysinfo.lpMinimumApplicationAddress); + printf("\tlpMaximumApplicationAddress: %p\n", sysinfo.lpMaximumApplicationAddress); + printf("\tdwActiveProcessorMask: %p\n", (void*)sysinfo.dwActiveProcessorMask); + printf("\tdwNumberOfProcessors: %" PRIu32 "\n", sysinfo.dwNumberOfProcessors); + printf("\tdwProcessorType: %" PRIu32 "\n", sysinfo.dwProcessorType); + printf("\tdwAllocationGranularity: %" PRIu32 "\n", sysinfo.dwAllocationGranularity); + printf("\twProcessorLevel: %" PRIu16 "\n", sysinfo.wProcessorLevel); + printf("\twProcessorRevision: %" PRIu16 "\n", sysinfo.wProcessorRevision); + printf("\n"); + + return 0; +} diff --git a/winpr/libwinpr/sysinfo/test/TestLocalTime.c b/winpr/libwinpr/sysinfo/test/TestLocalTime.c new file mode 100644 index 0000000..6ff5bf0 --- /dev/null +++ b/winpr/libwinpr/sysinfo/test/TestLocalTime.c @@ -0,0 +1,21 @@ + +#include +#include + +int TestLocalTime(int argc, char* argv[]) +{ + SYSTEMTIME lTime; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + GetLocalTime(&lTime); + + printf("GetLocalTime: wYear: %" PRIu16 " wMonth: %" PRIu16 " wDayOfWeek: %" PRIu16 + " wDay: %" PRIu16 " wHour: %" PRIu16 " wMinute: %" PRIu16 " wSecond: %" PRIu16 + " wMilliseconds: %" PRIu16 "\n", + lTime.wYear, lTime.wMonth, lTime.wDayOfWeek, lTime.wDay, lTime.wHour, lTime.wMinute, + lTime.wSecond, lTime.wMilliseconds); + + return 0; +} diff --git a/winpr/libwinpr/sysinfo/test/TestSystemTime.c b/winpr/libwinpr/sysinfo/test/TestSystemTime.c new file mode 100644 index 0000000..2a2b69e --- /dev/null +++ b/winpr/libwinpr/sysinfo/test/TestSystemTime.c @@ -0,0 +1,21 @@ + +#include +#include + +int TestSystemTime(int argc, char* argv[]) +{ + SYSTEMTIME sTime; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + GetSystemTime(&sTime); + + printf("GetSystemTime: wYear: %" PRIu16 " wMonth: %" PRIu16 " wDayOfWeek: %" PRIu16 + " wDay: %" PRIu16 " wHour: %" PRIu16 " wMinute: %" PRIu16 " wSecond: %" PRIu16 + " wMilliseconds: %" PRIu16 "\n", + sTime.wYear, sTime.wMonth, sTime.wDayOfWeek, sTime.wDay, sTime.wHour, sTime.wMinute, + sTime.wSecond, sTime.wMilliseconds); + + return 0; +} diff --git a/winpr/libwinpr/thread/CMakeLists.txt b/winpr/libwinpr/thread/CMakeLists.txt new file mode 100644 index 0000000..bfc04dd --- /dev/null +++ b/winpr/libwinpr/thread/CMakeLists.txt @@ -0,0 +1,34 @@ +# WinPR: Windows Portable Runtime +# libwinpr-thread cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + apc.h + apc.c + argv.c + process.c + processor.c + thread.c + thread.h + tls.c) + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + winpr_library_add_private(rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/thread/ModuleOptions.cmake b/winpr/libwinpr/thread/ModuleOptions.cmake new file mode 100644 index 0000000..ae52dd9 --- /dev/null +++ b/winpr/libwinpr/thread/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "1") +set(MINWIN_SHORT_NAME "processthreads") +set(MINWIN_LONG_NAME "Process and Thread Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/thread/apc.c b/winpr/libwinpr/thread/apc.c new file mode 100644 index 0000000..96ff8c3 --- /dev/null +++ b/winpr/libwinpr/thread/apc.c @@ -0,0 +1,271 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * APC implementation + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _WIN32 + +#include "apc.h" +#include "thread.h" +#include "../log.h" +#include "../synch/pollset.h" +#include + +#define TAG WINPR_TAG("apc") + +BOOL apc_init(APC_QUEUE* apc) +{ + pthread_mutexattr_t attr; + BOOL ret = FALSE; + + WINPR_ASSERT(apc); + + pthread_mutexattr_init(&attr); + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) + { + WLog_ERR(TAG, "failed to initialize mutex attributes to recursive"); + return FALSE; + } + + memset(apc, 0, sizeof(*apc)); + + if (pthread_mutex_init(&apc->mutex, &attr) != 0) + { + WLog_ERR(TAG, "failed to initialize main thread APC mutex"); + goto out; + } + + ret = TRUE; +out: + pthread_mutexattr_destroy(&attr); + return ret; +} + +BOOL apc_uninit(APC_QUEUE* apc) +{ + WINPR_ASSERT(apc); + return pthread_mutex_destroy(&apc->mutex) == 0; +} + +void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem) +{ + WINPR_APC_ITEM** nextp = NULL; + APC_QUEUE* apc = NULL; + + WINPR_ASSERT(thread); + WINPR_ASSERT(addItem); + + apc = &thread->apc; + WINPR_ASSERT(apc); + + pthread_mutex_lock(&apc->mutex); + if (apc->tail) + { + nextp = &apc->tail->next; + addItem->last = apc->tail; + } + else + { + nextp = &apc->head; + } + + *nextp = addItem; + apc->tail = addItem; + apc->length++; + + addItem->markedForRemove = FALSE; + addItem->boundThread = GetCurrentThreadId(); + addItem->linked = TRUE; + pthread_mutex_unlock(&apc->mutex); +} + +static INLINE void apc_item_remove(APC_QUEUE* apc, WINPR_APC_ITEM* item) +{ + WINPR_ASSERT(apc); + WINPR_ASSERT(item); + + if (!item->last) + apc->head = item->next; + else + item->last->next = item->next; + + if (!item->next) + apc->tail = item->last; + else + item->next->last = item->last; + + apc->length--; +} + +APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item) +{ + WINPR_THREAD* thread = winpr_GetCurrentThread(); + APC_QUEUE* apc = NULL; + APC_REMOVE_RESULT ret = APC_REMOVE_OK; + + WINPR_ASSERT(item); + + if (!item->linked) + return APC_REMOVE_OK; + + if (item->boundThread != GetCurrentThreadId()) + { + WLog_ERR(TAG, "removing an APC entry should be done in the creating thread"); + return APC_REMOVE_ERROR; + } + + if (!thread) + { + WLog_ERR(TAG, "unable to retrieve current thread"); + return APC_REMOVE_ERROR; + } + + apc = &thread->apc; + WINPR_ASSERT(apc); + + pthread_mutex_lock(&apc->mutex); + if (apc->treatingCompletions) + { + item->markedForRemove = TRUE; + ret = APC_REMOVE_DELAY_FREE; + goto out; + } + + apc_item_remove(apc, item); + +out: + pthread_mutex_unlock(&apc->mutex); + item->boundThread = 0xFFFFFFFF; + item->linked = FALSE; + return ret; +} + +BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled) +{ + WINPR_APC_ITEM* item = NULL; + BOOL ret = FALSE; + APC_QUEUE* apc = NULL; + + WINPR_ASSERT(thread); + WINPR_ASSERT(haveAutoSignaled); + + apc = &thread->apc; + WINPR_ASSERT(apc); + + *haveAutoSignaled = FALSE; + pthread_mutex_lock(&apc->mutex); + item = apc->head; + for (; item; item = item->next) + { + if (item->alwaysSignaled) + { + *haveAutoSignaled = TRUE; + } + else if (!pollset_add(set, item->pollFd, item->pollMode)) + goto out; + } + + ret = TRUE; +out: + pthread_mutex_unlock(&apc->mutex); + return ret; +} + +int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t idx) +{ + APC_QUEUE* apc = NULL; + WINPR_APC_ITEM* nextItem = NULL; + int ret = 0; + + WINPR_ASSERT(thread); + + apc = &thread->apc; + WINPR_ASSERT(apc); + + pthread_mutex_lock(&apc->mutex); + apc->treatingCompletions = TRUE; + + /* first pass to compute signaled items */ + for (WINPR_APC_ITEM* item = apc->head; item; item = item->next) + { + item->isSignaled = item->alwaysSignaled || pollset_isSignaled(set, idx); + if (!item->alwaysSignaled) + idx++; + } + + /* second pass: run completions */ + for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem) + { + if (item->isSignaled) + { + if (item->completion && !item->markedForRemove) + item->completion(item->completionArgs); + ret++; + } + + nextItem = item->next; + } + + /* third pass: to do final cleanup */ + for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem) + { + nextItem = item->next; + + if (item->markedForRemove) + { + apc_item_remove(apc, item); + if (item->markedForFree) + free(item); + } + } + + apc->treatingCompletions = FALSE; + pthread_mutex_unlock(&apc->mutex); + + return ret; +} + +void apc_cleanupThread(WINPR_THREAD* thread) +{ + WINPR_APC_ITEM* item = NULL; + WINPR_APC_ITEM* nextItem = NULL; + APC_QUEUE* apc = NULL; + + WINPR_ASSERT(thread); + + apc = &thread->apc; + WINPR_ASSERT(apc); + + pthread_mutex_lock(&apc->mutex); + item = apc->head; + for (; item; item = nextItem) + { + nextItem = item->next; + + if (item->type == APC_TYPE_HANDLE_FREE) + item->completion(item->completionArgs); + + item->last = item->next = NULL; + item->linked = FALSE; + if (item->markedForFree) + free(item); + } + + apc->head = apc->tail = NULL; + pthread_mutex_unlock(&apc->mutex); +} + +#endif diff --git a/winpr/libwinpr/thread/apc.h b/winpr/libwinpr/thread/apc.h new file mode 100644 index 0000000..c69920d --- /dev/null +++ b/winpr/libwinpr/thread/apc.h @@ -0,0 +1,85 @@ +/** + * WinPR: Windows Portable Runtime + * APC implementation + * + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_APC_H +#define WINPR_APC_H + +#include +#include + +#ifndef _WIN32 + +#include + +typedef struct winpr_thread WINPR_THREAD; +typedef struct winpr_APC_item WINPR_APC_ITEM; +typedef struct winpr_poll_set WINPR_POLL_SET; + +typedef void (*apc_treatment)(LPVOID arg); + +typedef enum +{ + APC_TYPE_USER, + APC_TYPE_TIMER, + APC_TYPE_HANDLE_FREE +} ApcType; + +struct winpr_APC_item +{ + ApcType type; + int pollFd; + DWORD pollMode; + apc_treatment completion; + LPVOID completionArgs; + BOOL markedForFree; + + /* private fields used by the APC */ + BOOL alwaysSignaled; + BOOL isSignaled; + DWORD boundThread; + BOOL linked; + BOOL markedForRemove; + WINPR_APC_ITEM *last, *next; +}; + +typedef enum +{ + APC_REMOVE_OK, + APC_REMOVE_ERROR, + APC_REMOVE_DELAY_FREE +} APC_REMOVE_RESULT; + +typedef struct +{ + pthread_mutex_t mutex; + DWORD length; + WINPR_APC_ITEM *head, *tail; + BOOL treatingCompletions; +} APC_QUEUE; + +BOOL apc_init(APC_QUEUE* apc); +BOOL apc_uninit(APC_QUEUE* apc); +void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem); +APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item); +BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled); +int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex); +void apc_cleanupThread(WINPR_THREAD* thread); +#endif + +#endif /* WINPR_APC_H */ diff --git a/winpr/libwinpr/thread/argv.c b/winpr/libwinpr/thread/argv.c new file mode 100644 index 0000000..3eedc41 --- /dev/null +++ b/winpr/libwinpr/thread/argv.c @@ -0,0 +1,279 @@ +/** + * WinPR: Windows Portable Runtime + * Process Argument Vector Functions + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("thread") + +/** + * CommandLineToArgvW function: + * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391/ + * + * CommandLineToArgvW has a special interpretation of backslash characters + * when they are followed by a quotation mark character ("), as follows: + * + * 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark. + * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a + * quotation mark. n backslashes not followed by a quotation mark simply produce n backslashes. + * + * The address returned by CommandLineToArgvW is the address of the first element in an array of + * LPWSTR values; the number of pointers in this array is indicated by pNumArgs. Each pointer to a + * null-terminated Unicode string represents an individual argument found on the command line. + * + * CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings, + * and for the argument strings themselves; the calling application must free the memory used by the + * argument list when it is no longer needed. To free the memory, use a single call to the LocalFree + * function. + */ + +/** + * Parsing C++ Command-Line Arguments: + * http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft + * + * Microsoft C/C++ startup code uses the following rules when + * interpreting arguments given on the operating system command line: + * + * Arguments are delimited by white space, which is either a space or a tab. + * + * The caret character (^) is not recognized as an escape character or delimiter. + * The character is handled completely by the command-line parser in the operating + * system before being passed to the argv array in the program. + * + * A string surrounded by double quotation marks ("string") is interpreted as a + * single argument, regardless of white space contained within. A quoted string + * can be embedded in an argument. + * + * A double quotation mark preceded by a backslash (\") is interpreted as a + * literal double quotation mark character ("). + * + * Backslashes are interpreted literally, unless they immediately + * precede a double quotation mark. + * + * If an even number of backslashes is followed by a double quotation mark, + * one backslash is placed in the argv array for every pair of backslashes, + * and the double quotation mark is interpreted as a string delimiter. + * + * If an odd number of backslashes is followed by a double quotation mark, + * one backslash is placed in the argv array for every pair of backslashes, + * and the double quotation mark is "escaped" by the remaining backslash, + * causing a literal double quotation mark (") to be placed in argv. + * + */ + +LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs) +{ + const char* p = NULL; + size_t length = 0; + const char* pBeg = NULL; + const char* pEnd = NULL; + char* buffer = NULL; + char* pOutput = NULL; + int numArgs = 0; + LPSTR* pArgs = NULL; + size_t maxNumArgs = 0; + size_t maxBufferSize = 0; + size_t cmdLineLength = 0; + BOOL* lpEscapedChars = NULL; + LPSTR lpEscapedCmdLine = NULL; + + if (!lpCmdLine) + return NULL; + + if (!pNumArgs) + return NULL; + + pArgs = NULL; + lpEscapedCmdLine = NULL; + cmdLineLength = strlen(lpCmdLine); + lpEscapedChars = (BOOL*)calloc(cmdLineLength + 1, sizeof(BOOL)); + + if (!lpEscapedChars) + return NULL; + + if (strstr(lpCmdLine, "\\\"")) + { + size_t n = 0; + const char* pLastEnd = NULL; + lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char)); + + if (!lpEscapedCmdLine) + { + free(lpEscapedChars); + return NULL; + } + + p = (const char*)lpCmdLine; + pLastEnd = (const char*)lpCmdLine; + pOutput = (char*)lpEscapedCmdLine; + + while (p < &lpCmdLine[cmdLineLength]) + { + pBeg = strstr(p, "\\\""); + + if (!pBeg) + { + length = strlen(p); + CopyMemory(pOutput, p, length); + pOutput += length; + break; + } + + pEnd = pBeg + 2; + + while (pBeg >= lpCmdLine) + { + if (*pBeg != '\\') + { + pBeg++; + break; + } + + pBeg--; + } + + n = ((pEnd - pBeg) - 1); + length = (pBeg - pLastEnd); + CopyMemory(pOutput, p, length); + pOutput += length; + p += length; + + for (size_t i = 0; i < (n / 2); i++) + *pOutput++ = '\\'; + + p += n + 1; + + if ((n % 2) != 0) + lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE; + + *pOutput++ = '"'; + pLastEnd = p; + } + + *pOutput++ = '\0'; + lpCmdLine = (LPCSTR)lpEscapedCmdLine; + cmdLineLength = strlen(lpCmdLine); + } + + maxNumArgs = 2; + p = (const char*)lpCmdLine; + + while (p < lpCmdLine + cmdLineLength) + { + p += strcspn(p, " \t"); + p += strspn(p, " \t"); + maxNumArgs++; + } + + maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1); + buffer = calloc(maxBufferSize, sizeof(char)); + + if (!buffer) + { + free(lpEscapedCmdLine); + free(lpEscapedChars); + return NULL; + } + + pArgs = (LPSTR*)buffer; + pOutput = (char*)&buffer[maxNumArgs * (sizeof(char*))]; + p = (const char*)lpCmdLine; + + while (p < lpCmdLine + cmdLineLength) + { + pBeg = p; + + while (1) + { + p += strcspn(p, " \t\"\0"); + + if ((*p != '"') || !lpEscapedChars[p - lpCmdLine]) + break; + + p++; + } + + if (*p != '"') + { + /* no whitespace escaped with double quotes */ + length = (p - pBeg); + CopyMemory(pOutput, pBeg, length); + pOutput[length] = '\0'; + pArgs[numArgs++] = pOutput; + pOutput += (length + 1); + } + else + { + p++; + + while (1) + { + p += strcspn(p, "\"\0"); + + if ((*p != '"') || !lpEscapedChars[p - lpCmdLine]) + break; + + p++; + } + + if (*p != '"') + WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!"); + + if (*p && *(++p)) + p += strcspn(p, " \t\0"); + + pArgs[numArgs++] = pOutput; + + while (pBeg < p) + { + if (*pBeg != '"') + *pOutput++ = *pBeg; + + pBeg++; + } + + *pOutput++ = '\0'; + } + + p += strspn(p, " \t"); + } + + free(lpEscapedCmdLine); + free(lpEscapedChars); + *pNumArgs = numArgs; + return pArgs; +} + +#ifndef _WIN32 + +LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs) +{ + return NULL; +} + +#endif diff --git a/winpr/libwinpr/thread/process.c b/winpr/libwinpr/thread/process.c new file mode 100644 index 0000000..0dbd940 --- /dev/null +++ b/winpr/libwinpr/thread/process.c @@ -0,0 +1,598 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include "../handle/nonehandle.h" + +#include + +/** + * CreateProcessA + * CreateProcessW + * CreateProcessAsUserA + * CreateProcessAsUserW + * ExitProcess + * GetCurrentProcess + * GetCurrentProcessId + * GetExitCodeProcess + * GetProcessHandleCount + * GetProcessId + * GetProcessIdOfThread + * GetProcessMitigationPolicy + * GetProcessTimes + * GetProcessVersion + * OpenProcess + * OpenProcessToken + * ProcessIdToSessionId + * SetProcessAffinityUpdateMode + * SetProcessMitigationPolicy + * SetProcessShutdownParameters + * TerminateProcess + */ + +#ifndef _WIN32 + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef __linux__ +#include +#include +#include +#endif /* __linux__ */ + +#include "thread.h" + +#include "../security/security.h" + +#ifndef NSIG +#ifdef SIGMAX +#define NSIG SIGMAX +#else +#define NSIG 64 +#endif +#endif + +/** + * If the file name does not contain a directory path, the system searches for the executable file + * in the following sequence: + * + * 1) The directory from which the application loaded. + * 2) The current directory for the parent process. + * 3) The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of + * this directory. 4) The 16-bit Windows system directory. There is no function that obtains the + * path of this directory, but it is searched. The name of this directory is System. 5) The Windows + * directory. Use the GetWindowsDirectory function to get the path of this directory. 6) The + * directories that are listed in the PATH environment variable. Note that this function does not + * search the per-application path specified by the App Paths registry key. To include this + * per-application path in the search sequence, use the ShellExecute function. + */ + +static char* FindApplicationPath(char* application) +{ + LPCSTR pathName = "PATH"; + char* path = NULL; + char* save = NULL; + DWORD nSize = 0; + LPSTR lpSystemPath = NULL; + char* filename = NULL; + + if (!application) + return NULL; + + if (application[0] == '/') + return _strdup(application); + + nSize = GetEnvironmentVariableA(pathName, NULL, 0); + + if (!nSize) + return _strdup(application); + + lpSystemPath = (LPSTR)malloc(nSize); + + if (!lpSystemPath) + return NULL; + + if (GetEnvironmentVariableA(pathName, lpSystemPath, nSize) != nSize - 1) + { + free(lpSystemPath); + return NULL; + } + + save = NULL; + path = strtok_s(lpSystemPath, ":", &save); + + while (path) + { + filename = GetCombinedPath(path, application); + + if (winpr_PathFileExists(filename)) + { + break; + } + + free(filename); + filename = NULL; + path = strtok_s(NULL, ":", &save); + } + + free(lpSystemPath); + return filename; +} + +static HANDLE CreateProcessHandle(pid_t pid); +static BOOL ProcessHandleCloseHandle(HANDLE handle); + +static BOOL _CreateProcessExA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName, + LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + pid_t pid = 0; + int numArgs = 0; + LPSTR* pArgs = NULL; + char** envp = NULL; + char* filename = NULL; + HANDLE thread = NULL; + HANDLE process = NULL; + WINPR_ACCESS_TOKEN* token = NULL; + LPTCH lpszEnvironmentBlock = NULL; + BOOL ret = FALSE; + sigset_t oldSigMask; + sigset_t newSigMask; + BOOL restoreSigMask = FALSE; + numArgs = 0; + lpszEnvironmentBlock = NULL; + /* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa + */ + if (lpCommandLine) + pArgs = CommandLineToArgvA(lpCommandLine, &numArgs); + else + pArgs = CommandLineToArgvA(lpApplicationName, &numArgs); + + if (!pArgs) + return FALSE; + + token = (WINPR_ACCESS_TOKEN*)hToken; + + if (lpEnvironment) + { + envp = EnvironmentBlockToEnvpA(lpEnvironment); + } + else + { + lpszEnvironmentBlock = GetEnvironmentStrings(); + + if (!lpszEnvironmentBlock) + goto finish; + + envp = EnvironmentBlockToEnvpA(lpszEnvironmentBlock); + } + + if (!envp) + goto finish; + + filename = FindApplicationPath(pArgs[0]); + + if (NULL == filename) + goto finish; + + /* block all signals so that the child can safely reset the caller's handlers */ + sigfillset(&newSigMask); + restoreSigMask = !pthread_sigmask(SIG_SETMASK, &newSigMask, &oldSigMask); + /* fork and exec */ + pid = fork(); + + if (pid < 0) + { + /* fork failure */ + goto finish; + } + + if (pid == 0) + { + /* child process */ +#ifndef __sun + int maxfd = 0; +#endif + sigset_t set = { 0 }; + struct sigaction act = { 0 }; + /* set default signal handlers */ + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + for (int sig = 1; sig < NSIG; sig++) + sigaction(sig, &act, NULL); + + /* unblock all signals */ + sigfillset(&set); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + if (lpStartupInfo) + { + int handle_fd = 0; + handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdOutput); + + if (handle_fd != -1) + dup2(handle_fd, STDOUT_FILENO); + + handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdError); + + if (handle_fd != -1) + dup2(handle_fd, STDERR_FILENO); + + handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdInput); + + if (handle_fd != -1) + dup2(handle_fd, STDIN_FILENO); + } + +#ifdef __sun + closefrom(3); +#else +#ifdef F_MAXFD // on some BSD derivates + maxfd = fcntl(0, F_MAXFD); +#else + maxfd = sysconf(_SC_OPEN_MAX); +#endif + + for (int fd = 3; fd < maxfd; fd++) + close(fd); + +#endif // __sun + + if (token) + { + if (token->GroupId) + { + int rc = setgid((gid_t)token->GroupId); + + if (rc < 0) + { + } + else + { + initgroups(token->Username, (gid_t)token->GroupId); + } + } + + if (token->UserId) + { + int rc = setuid((uid_t)token->UserId); + if (rc != 0) + goto finish; + } + } + + /* TODO: add better cwd handling and error checking */ + if (lpCurrentDirectory && strlen(lpCurrentDirectory) > 0) + { + int rc = chdir(lpCurrentDirectory); + if (rc != 0) + goto finish; + } + + if (execve(filename, pArgs, envp) < 0) + { + /* execve failed - end the process */ + _exit(1); + } + } + else + { + /* parent process */ + } + + process = CreateProcessHandle(pid); + + if (!process) + { + goto finish; + } + + thread = CreateNoneHandle(); + + if (!thread) + { + ProcessHandleCloseHandle(process); + goto finish; + } + + lpProcessInformation->hProcess = process; + lpProcessInformation->hThread = thread; + lpProcessInformation->dwProcessId = (DWORD)pid; + lpProcessInformation->dwThreadId = (DWORD)pid; + ret = TRUE; +finish: + + /* restore caller's original signal mask */ + if (restoreSigMask) + pthread_sigmask(SIG_SETMASK, &oldSigMask, NULL); + + free(filename); + free(pArgs); + + if (lpszEnvironmentBlock) + FreeEnvironmentStrings(lpszEnvironmentBlock); + + if (envp) + { + int i = 0; + + while (envp[i]) + { + free(envp[i]); + i++; + } + + free(envp); + } + + return ret; +} + +BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) +{ + return _CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, lpProcessAttributes, + lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, + lpCurrentDirectory, lpStartupInfo, lpProcessInformation); +} + +BOOL CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) +{ + return FALSE; +} + +BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) +{ + return _CreateProcessExA(hToken, 0, lpApplicationName, lpCommandLine, lpProcessAttributes, + lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, + lpCurrentDirectory, lpStartupInfo, lpProcessInformation); +} + +BOOL CreateProcessAsUserW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) +{ + return FALSE; +} + +BOOL CreateProcessWithLogonA(LPCSTR lpUsername, LPCSTR lpDomain, LPCSTR lpPassword, + DWORD dwLogonFlags, LPCSTR lpApplicationName, LPSTR lpCommandLine, + DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + return FALSE; +} + +BOOL CreateProcessWithLogonW(LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword, + DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + return FALSE; +} + +BOOL CreateProcessWithTokenA(HANDLE hToken, DWORD dwLogonFlags, LPCSTR lpApplicationName, + LPSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + return _CreateProcessExA(NULL, 0, lpApplicationName, lpCommandLine, NULL, NULL, FALSE, + dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, + lpProcessInformation); +} + +BOOL CreateProcessWithTokenW(HANDLE hToken, DWORD dwLogonFlags, LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + return FALSE; +} + +VOID ExitProcess(UINT uExitCode) +{ + exit((int)uExitCode); +} + +BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) +{ + WINPR_PROCESS* process = NULL; + + if (!hProcess) + return FALSE; + + if (!lpExitCode) + return FALSE; + + process = (WINPR_PROCESS*)hProcess; + *lpExitCode = process->dwExitCode; + return TRUE; +} + +HANDLE _GetCurrentProcess(VOID) +{ + return NULL; +} + +DWORD GetCurrentProcessId(VOID) +{ + return ((DWORD)getpid()); +} + +BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode) +{ + WINPR_PROCESS* process = NULL; + process = (WINPR_PROCESS*)hProcess; + + if (!process || (process->pid <= 0)) + return FALSE; + + if (kill(process->pid, SIGTERM)) + return FALSE; + + return TRUE; +} + +static BOOL ProcessHandleCloseHandle(HANDLE handle) +{ + WINPR_PROCESS* process = (WINPR_PROCESS*)handle; + WINPR_ASSERT(process); + if (process->fd >= 0) + { + close(process->fd); + process->fd = -1; + } + free(process); + return TRUE; +} + +static BOOL ProcessHandleIsHandle(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_PROCESS, FALSE); +} + +static int ProcessGetFd(HANDLE handle) +{ + WINPR_PROCESS* process = (WINPR_PROCESS*)handle; + + if (!ProcessHandleIsHandle(handle)) + return -1; + + return process->fd; +} + +static DWORD ProcessCleanupHandle(HANDLE handle) +{ + WINPR_PROCESS* process = (WINPR_PROCESS*)handle; + + WINPR_ASSERT(process); + if (process->fd > 0) + { + if (waitpid(process->pid, &process->status, WNOHANG) == process->pid) + process->dwExitCode = (DWORD)process->status; + } + return WAIT_OBJECT_0; +} + +static HANDLE_OPS ops = { ProcessHandleIsHandle, + ProcessHandleCloseHandle, + ProcessGetFd, + ProcessCleanupHandle, /* CleanupHandle */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +static int _pidfd_open(pid_t pid) +{ +#ifdef __linux__ +#if !defined(__NR_pidfd_open) +#define __NR_pidfd_open 434 +#endif /* __NR_pidfd_open */ + +#ifndef PIDFD_NONBLOCK +#define PIDFD_NONBLOCK O_NONBLOCK +#endif /* PIDFD_NONBLOCK */ + + int fd = syscall(__NR_pidfd_open, pid, PIDFD_NONBLOCK); + if (fd < 0 && errno == EINVAL) + { + /* possibly PIDFD_NONBLOCK is not supported, let's try to create a pidfd and set it + * non blocking afterward */ + int flags = 0; + fd = syscall(__NR_pidfd_open, pid, 0); + if (fd < 0) + return -1; + + flags = fcntl(fd, F_GETFL); + if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) + { + close(fd); + fd = -1; + } + } + return fd; +#else + return -1; +#endif +} + +HANDLE CreateProcessHandle(pid_t pid) +{ + WINPR_PROCESS* process = NULL; + process = (WINPR_PROCESS*)calloc(1, sizeof(WINPR_PROCESS)); + + if (!process) + return NULL; + + process->pid = pid; + process->common.Type = HANDLE_TYPE_PROCESS; + process->common.ops = &ops; + process->fd = _pidfd_open(pid); + if (process->fd >= 0) + process->common.Mode = WINPR_FD_READ; + return (HANDLE)process; +} + +#endif diff --git a/winpr/libwinpr/thread/processor.c b/winpr/libwinpr/thread/processor.c new file mode 100644 index 0000000..ed3df55 --- /dev/null +++ b/winpr/libwinpr/thread/processor.c @@ -0,0 +1,41 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +/** + * GetCurrentProcessorNumber + * GetCurrentProcessorNumberEx + * GetThreadIdealProcessorEx + * SetThreadIdealProcessorEx + * IsProcessorFeaturePresent + */ + +#ifndef _WIN32 + +DWORD GetCurrentProcessorNumber(VOID) +{ + return 0; +} + +#endif diff --git a/winpr/libwinpr/thread/test/CMakeLists.txt b/winpr/libwinpr/thread/test/CMakeLists.txt new file mode 100644 index 0000000..a78e584 --- /dev/null +++ b/winpr/libwinpr/thread/test/CMakeLists.txt @@ -0,0 +1,27 @@ + +set(MODULE_NAME "TestThread") +set(MODULE_PREFIX "TEST_THREAD") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestThreadCommandLineToArgv.c + TestThreadCreateProcess.c + TestThreadExitThread.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c b/winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c new file mode 100644 index 0000000..33d0c94 --- /dev/null +++ b/winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c @@ -0,0 +1,74 @@ + +#include +#include +#include + +static const char* test_args_line_1 = "app.exe abc d e"; + +static const char* test_args_list_1[] = { "app.exe", "abc", "d", "e", NULL }; + +static const char* test_args_line_2 = "app.exe abc \t def"; + +static const char* test_args_list_2[] = { "app.exe", "abc", "def", NULL }; + +static const char* test_args_line_3 = "app.exe \"abc\" d e"; + +static const char* test_args_list_3[] = { "app.exe", "abc", "d", "e", NULL }; + +static const char* test_args_line_4 = "app.exe a\\\\b d\"e f\"g h"; + +static const char* test_args_list_4[] = { "app.exe", "a\\\\b", "de fg", "h", NULL }; + +static const char* test_args_line_5 = "app.exe a\\\\\\\"b c d"; + +static const char* test_args_list_5[] = { "app.exe", "a\\\"b", "c", "d", NULL }; + +static const char* test_args_line_6 = "app.exe a\\\\\\\\\"b c\" d e"; + +static const char* test_args_list_6[] = { "app.exe", "a\\\\b c", "d", "e", NULL }; + +static const char* test_args_line_7 = "app.exe a\\\\\\\\\"b c\" d e f\\\\\\\\\"g h\" i j"; + +static const char* test_args_list_7[] = { "app.exe", "a\\\\b c", "d", "e", + "f\\\\g h", "i", "j", NULL }; + +static int test_command_line_parsing_case(const char* line, const char** list) +{ + LPSTR* pArgs = NULL; + int numArgs = 0; + + pArgs = NULL; + numArgs = 0; + + printf("Parsing: %s\n", line); + + pArgs = CommandLineToArgvA(line, &numArgs); + + printf("pNumArgs: %d\n", numArgs); + + for (int i = 0; i < numArgs; i++) + { + printf("argv[%d] = %s\n", i, pArgs[i]); + } + + free(pArgs); + + return 0; +} + +int TestThreadCommandLineToArgv(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + test_command_line_parsing_case(test_args_line_1, test_args_list_1); + test_command_line_parsing_case(test_args_line_2, test_args_list_2); + test_command_line_parsing_case(test_args_line_3, test_args_list_3); + test_command_line_parsing_case(test_args_line_4, test_args_list_4); + test_command_line_parsing_case(test_args_line_5, test_args_list_5); + test_command_line_parsing_case(test_args_line_6, test_args_list_6); + test_command_line_parsing_case(test_args_line_7, test_args_list_7); + + return 0; +} diff --git a/winpr/libwinpr/thread/test/TestThreadCreateProcess.c b/winpr/libwinpr/thread/test/TestThreadCreateProcess.c new file mode 100644 index 0000000..1260c1b --- /dev/null +++ b/winpr/libwinpr/thread/test/TestThreadCreateProcess.c @@ -0,0 +1,156 @@ + +#include +#include +#include +#include +#include +#include +#include + +#define TESTENV_A "HELLO=WORLD" +#define TESTENV_T _T(TESTENV_A) + +int TestThreadCreateProcess(int argc, char* argv[]) +{ + BOOL status = 0; + DWORD exitCode = 0; + LPCTSTR lpApplicationName = NULL; + +#ifdef _WIN32 + TCHAR lpCommandLine[200] = _T("cmd /C set"); +#else + TCHAR lpCommandLine[200] = _T("printenv"); +#endif + + // LPTSTR lpCommandLine; + LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL; + LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL; + BOOL bInheritHandles = 0; + DWORD dwCreationFlags = 0; + LPVOID lpEnvironment = NULL; + LPCTSTR lpCurrentDirectory = NULL; + STARTUPINFO StartupInfo = { 0 }; + PROCESS_INFORMATION ProcessInformation = { 0 }; + LPTCH lpszEnvironmentBlock = NULL; + HANDLE pipe_read = NULL; + HANDLE pipe_write = NULL; + char buf[1024] = { 0 }; + DWORD read_bytes = 0; + int ret = 0; + SECURITY_ATTRIBUTES saAttr; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + lpszEnvironmentBlock = GetEnvironmentStrings(); + + lpApplicationName = NULL; + + lpProcessAttributes = NULL; + lpThreadAttributes = NULL; + bInheritHandles = FALSE; + dwCreationFlags = 0; +#ifdef _UNICODE + dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; +#endif + lpEnvironment = lpszEnvironmentBlock; + lpCurrentDirectory = NULL; + StartupInfo.cb = sizeof(STARTUPINFO); + + status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, + lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, + lpCurrentDirectory, &StartupInfo, &ProcessInformation); + + if (!status) + { + printf("CreateProcess failed. error=%" PRIu32 "\n", GetLastError()); + return 1; + } + + if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0) + { + printf("Failed to wait for first process. error=%" PRIu32 "\n", GetLastError()); + return 1; + } + + exitCode = 0; + status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode); + + printf("GetExitCodeProcess status: %" PRId32 "\n", status); + printf("Process exited with code: 0x%08" PRIX32 "\n", exitCode); + + CloseHandle(ProcessInformation.hProcess); + CloseHandle(ProcessInformation.hThread); + FreeEnvironmentStrings(lpszEnvironmentBlock); + + /* Test stdin,stdout,stderr redirection */ + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&pipe_read, &pipe_write, &saAttr, 0)) + { + printf("Pipe creation failed. error=%" PRIu32 "\n", GetLastError()); + return 1; + } + + bInheritHandles = TRUE; + + ZeroMemory(&StartupInfo, sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof(STARTUPINFO); + StartupInfo.hStdOutput = pipe_write; + StartupInfo.hStdError = pipe_write; + StartupInfo.dwFlags = STARTF_USESTDHANDLES; + + ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION)); + + if (!(lpEnvironment = calloc(1, sizeof(TESTENV_T) + sizeof(TCHAR)))) + { + printf("Failed to allocate environment buffer. error=%" PRIu32 "\n", GetLastError()); + return 1; + } + memcpy(lpEnvironment, (void*)TESTENV_T, sizeof(TESTENV_T)); + + status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, + lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, + lpCurrentDirectory, &StartupInfo, &ProcessInformation); + + free(lpEnvironment); + + if (!status) + { + CloseHandle(pipe_read); + CloseHandle(pipe_write); + printf("CreateProcess failed. error=%" PRIu32 "\n", GetLastError()); + return 1; + } + + if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0) + { + printf("Failed to wait for second process. error=%" PRIu32 "\n", GetLastError()); + return 1; + } + + ZeroMemory(buf, sizeof(buf)); + ReadFile(pipe_read, buf, sizeof(buf) - 1, &read_bytes, NULL); + if (!strstr((const char*)buf, TESTENV_A)) + { + printf("No or unexpected data read from pipe\n"); + ret = 1; + } + + CloseHandle(pipe_read); + CloseHandle(pipe_write); + + exitCode = 0; + status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode); + + printf("GetExitCodeProcess status: %" PRId32 "\n", status); + printf("Process exited with code: 0x%08" PRIX32 "\n", exitCode); + + CloseHandle(ProcessInformation.hProcess); + CloseHandle(ProcessInformation.hThread); + + return ret; +} diff --git a/winpr/libwinpr/thread/test/TestThreadExitThread.c b/winpr/libwinpr/thread/test/TestThreadExitThread.c new file mode 100644 index 0000000..015bb85 --- /dev/null +++ b/winpr/libwinpr/thread/test/TestThreadExitThread.c @@ -0,0 +1,53 @@ +// Copyright © 2015 Hewlett-Packard Development Company, L.P. + +#include +#include +#include + +static DWORD WINAPI thread_func(LPVOID arg) +{ + WINPR_UNUSED(arg); + + /* exists of the thread the quickest as possible */ + ExitThread(0); + return 0; +} + +int TestThreadExitThread(int argc, char* argv[]) +{ + HANDLE thread = NULL; + DWORD waitResult = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* FIXME: create some noise to better guaranty the test validity and + * decrease the number of loops */ + for (int i = 0; i < 100; i++) + { + thread = CreateThread(NULL, 0, thread_func, NULL, 0, NULL); + + if (thread == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "Got an invalid thread!\n"); + return -1; + } + + waitResult = WaitForSingleObject(thread, 300); + if (waitResult != WAIT_OBJECT_0) + { + /* When the thread exits before the internal thread_list + * was updated, ExitThread() is not able to retrieve the + * related WINPR_THREAD object and is not able to signal + * the end of the thread. Therefore WaitForSingleObject + * never get the signal. + */ + fprintf(stderr, + "300ms should have been enough for the thread to be in a signaled state\n"); + return -1; + } + + CloseHandle(thread); + } + return 0; +} diff --git a/winpr/libwinpr/thread/thread.c b/winpr/libwinpr/thread/thread.c new file mode 100644 index 0000000..edf5639 --- /dev/null +++ b/winpr/libwinpr/thread/thread.c @@ -0,0 +1,1045 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2015 Hewlett-Packard Development Company, L.P. + * Copyright 2021 David Fort + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#include + +/** + * api-ms-win-core-processthreads-l1-1-1.dll + * + * CreateRemoteThread + * CreateRemoteThreadEx + * CreateThread + * DeleteProcThreadAttributeList + * ExitThread + * FlushInstructionCache + * FlushProcessWriteBuffers + * GetCurrentThread + * GetCurrentThreadId + * GetCurrentThreadStackLimits + * GetExitCodeThread + * GetPriorityClass + * GetStartupInfoW + * GetThreadContext + * GetThreadId + * GetThreadIdealProcessorEx + * GetThreadPriority + * GetThreadPriorityBoost + * GetThreadTimes + * InitializeProcThreadAttributeList + * OpenThread + * OpenThreadToken + * QueryProcessAffinityUpdateMode + * QueueUserAPC + * ResumeThread + * SetPriorityClass + * SetThreadContext + * SetThreadPriority + * SetThreadPriorityBoost + * SetThreadStackGuarantee + * SetThreadToken + * SuspendThread + * SwitchToThread + * TerminateThread + * UpdateProcThreadAttribute + */ + +#ifndef _WIN32 + +#include +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifdef WINPR_HAVE_SYS_EVENTFD_H +#include +#endif + +#include + +#include +#include + +#include + +#include "thread.h" +#include "apc.h" + +#include "../handle/handle.h" +#include "../log.h" +#define TAG WINPR_TAG("thread") + +static WINPR_THREAD mainThread; + +#if defined(WITH_THREAD_LIST) +static wListDictionary* thread_list = NULL; +#endif + +static BOOL ThreadCloseHandle(HANDLE handle); +static void cleanup_handle(void* obj); + +static BOOL ThreadIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_THREAD, FALSE); +} + +static int ThreadGetFd(HANDLE handle) +{ + WINPR_THREAD* pThread = (WINPR_THREAD*)handle; + + if (!ThreadIsHandled(handle)) + return -1; + + return pThread->event.fds[0]; +} + +#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg) +static BOOL run_mutex_init_(int (*fkt)(pthread_mutex_t*, const pthread_mutexattr_t*), + const char* name, pthread_mutex_t* mutex, + const pthread_mutexattr_t* mutexattr) +{ + int rc = 0; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(mutex); + + rc = fkt(mutex, mutexattr); + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer))); + } + return rc == 0; +} + +#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux) +static BOOL run_mutex_fkt_(int (*fkt)(pthread_mutex_t* mux), const char* name, + pthread_mutex_t* mutex) +{ + int rc = 0; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(mutex); + + rc = fkt(mutex); + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer))); + } + return rc == 0; +} + +#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg) +static BOOL run_cond_init_(int (*fkt)(pthread_cond_t*, const pthread_condattr_t*), const char* name, + pthread_cond_t* condition, const pthread_condattr_t* conditionattr) +{ + int rc = 0; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(condition); + + rc = fkt(condition, conditionattr); + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer))); + } + return rc == 0; +} + +#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond) +static BOOL run_cond_fkt_(int (*fkt)(pthread_cond_t* mux), const char* name, + pthread_cond_t* condition) +{ + int rc = 0; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(condition); + + rc = fkt(condition); + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_WARN(TAG, "[%s] failed with [%s]", name, winpr_strerror(rc, ebuffer, sizeof(ebuffer))); + } + return rc == 0; +} + +static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex) +{ + WINPR_ASSERT(mutex); + WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY); + return pthread_mutex_unlock(mutex); +} + +static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + + bundle->val = FALSE; + if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL)) + return FALSE; + + if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL)) + return FALSE; + return TRUE; +} + +static void mux_condition_bundle_uninit(mux_condition_bundle* bundle) +{ + mux_condition_bundle empty = { 0 }; + + WINPR_ASSERT(bundle); + + run_cond_fkt(pthread_cond_destroy, &bundle->cond); + run_mutex_fkt(pthread_mutex_destroy, &bundle->mux); + *bundle = empty; +} + +static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle) +{ + BOOL rc = TRUE; + WINPR_ASSERT(bundle); + + if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux)) + return FALSE; + bundle->val = TRUE; + if (!run_cond_fkt(pthread_cond_signal, &bundle->cond)) + rc = FALSE; + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux)) + rc = FALSE; + return rc; +} + +static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + return run_mutex_fkt(pthread_mutex_lock, &bundle->mux); +} + +static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux); +} + +static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name) +{ + BOOL rc = FALSE; + + WINPR_ASSERT(bundle); + WINPR_ASSERT(name); + WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY); + + while (!bundle->val) + { + int r = pthread_cond_wait(&bundle->cond, &bundle->mux); + if (r != 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "failed to wait for %s [%s]", name, + winpr_strerror(r, ebuffer, sizeof(ebuffer))); + switch (r) + { + case ENOTRECOVERABLE: + case EPERM: + case ETIMEDOUT: + case EINVAL: + goto fail; + + default: + break; + } + } + } + + rc = bundle->val; + +fail: + return rc; +} + +static BOOL signal_thread_ready(WINPR_THREAD* thread) +{ + WINPR_ASSERT(thread); + + return mux_condition_bundle_signal(&thread->isCreated); +} + +static BOOL signal_thread_is_running(WINPR_THREAD* thread) +{ + WINPR_ASSERT(thread); + + return mux_condition_bundle_signal(&thread->isRunning); +} + +static DWORD ThreadCleanupHandle(HANDLE handle) +{ + DWORD status = WAIT_FAILED; + WINPR_THREAD* thread = (WINPR_THREAD*)handle; + + if (!ThreadIsHandled(handle)) + return WAIT_FAILED; + + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) + return WAIT_FAILED; + + if (!thread->joined) + { + int rc = pthread_join(thread->thread, NULL); + + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "pthread_join failure: [%d] %s", rc, + winpr_strerror(rc, ebuffer, sizeof(ebuffer))); + goto fail; + } + else + thread->joined = TRUE; + } + + status = WAIT_OBJECT_0; + +fail: + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) + return WAIT_FAILED; + + return status; +} + +static HANDLE_OPS ops = { ThreadIsHandled, + ThreadCloseHandle, + ThreadGetFd, + ThreadCleanupHandle, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +static void dump_thread(WINPR_THREAD* thread) +{ +#if defined(WITH_DEBUG_THREADS) + void* stack = winpr_backtrace(20); + char** msg = NULL; + size_t used = 0; + WLog_DBG(TAG, "Called from:"); + msg = winpr_backtrace_symbols(stack, &used); + + for (size_t i = 0; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); + winpr_backtrace_free(stack); + WLog_DBG(TAG, "Thread handle created still not closed!"); + msg = winpr_backtrace_symbols(thread->create_stack, &used); + + for (size_t i = 0; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); + + if (thread->started) + { + WLog_DBG(TAG, "Thread still running!"); + } + else if (!thread->exit_stack) + { + WLog_DBG(TAG, "Thread suspended."); + } + else + { + WLog_DBG(TAG, "Thread exited at:"); + msg = winpr_backtrace_symbols(thread->exit_stack, &used); + + for (size_t i = 0; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); + } +#else + WINPR_UNUSED(thread); +#endif +} + +/** + * TODO: implement thread suspend/resume using pthreads + * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition + */ +static BOOL set_event(WINPR_THREAD* thread) +{ + return winpr_event_set(&thread->event); +} + +static BOOL reset_event(WINPR_THREAD* thread) +{ + return winpr_event_reset(&thread->event); +} + +#if defined(WITH_THREAD_LIST) +static BOOL thread_compare(const void* a, const void* b) +{ + const pthread_t* p1 = a; + const pthread_t* p2 = b; + BOOL rc = pthread_equal(*p1, *p2); + return rc; +} +#endif + +static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT; +static pthread_t mainThreadId; +static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES; + +static BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +{ + if (!apc_init(&mainThread.apc)) + { + WLog_ERR(TAG, "failed to initialize APC"); + goto out; + } + + mainThread.common.Type = HANDLE_TYPE_THREAD; + mainThreadId = pthread_self(); + + currentThreadTlsIndex = TlsAlloc(); + if (currentThreadTlsIndex == TLS_OUT_OF_INDEXES) + { + WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread"); + } + +#if defined(WITH_THREAD_LIST) + thread_list = ListDictionary_New(TRUE); + + if (!thread_list) + { + WLog_ERR(TAG, "Couldn't create global thread list"); + goto error_thread_list; + } + + thread_list->objectKey.fnObjectEquals = thread_compare; +#endif + +out: + return TRUE; +} + +static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread) +{ + BOOL res = FALSE; + + WINPR_ASSERT(thread); + + if (!mux_condition_bundle_lock(&thread->isRunning)) + return FALSE; + + if (!signal_thread_ready(thread)) + goto fail; + + if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning")) + goto fail; + +#if defined(WITH_THREAD_LIST) + if (!ListDictionary_Contains(thread_list, &thread->thread)) + { + WLog_ERR(TAG, "Thread not in thread_list, startup failed!"); + goto fail; + } +#endif + + res = TRUE; + +fail: + if (!mux_condition_bundle_unlock(&thread->isRunning)) + return FALSE; + + return res; +} + +/* Thread launcher function responsible for registering + * cleanup handlers and calling pthread_exit, if not done + * in thread function. */ +static void* thread_launcher(void* arg) +{ + DWORD rc = 0; + WINPR_THREAD* thread = (WINPR_THREAD*)arg; + LPTHREAD_START_ROUTINE fkt = NULL; + + if (!thread) + { + WLog_ERR(TAG, "Called with invalid argument %p", arg); + goto exit; + } + + if (!TlsSetValue(currentThreadTlsIndex, thread)) + { + WLog_ERR(TAG, "thread %d, unable to set current thread value", pthread_self()); + goto exit; + } + + if (!(fkt = thread->lpStartAddress)) + { + WLog_ERR(TAG, "Thread function argument is %p", (void*)fkt); + goto exit; + } + + if (!signal_and_wait_for_ready(thread)) + goto exit; + + rc = fkt(thread->lpParameter); +exit: + + if (thread) + { + apc_cleanupThread(thread); + + if (!thread->exited) + thread->dwExitCode = rc; + + set_event(thread); + + signal_thread_ready(thread); + + if (thread->detached || !thread->started) + cleanup_handle(thread); + } + + return NULL; +} + +static BOOL winpr_StartThread(WINPR_THREAD* thread) +{ + BOOL rc = FALSE; + BOOL locked = FALSE; + pthread_attr_t attr = { 0 }; + + if (!mux_condition_bundle_lock(&thread->isCreated)) + return FALSE; + locked = TRUE; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + if (thread->dwStackSize > 0) + pthread_attr_setstacksize(&attr, (size_t)thread->dwStackSize); + + thread->started = TRUE; + reset_event(thread); + +#if defined(WITH_THREAD_LIST) + if (!ListDictionary_Add(thread_list, &thread->thread, thread)) + { + WLog_ERR(TAG, "failed to add the thread to the thread list"); + goto error; + } +#endif + + if (pthread_create(&thread->thread, &attr, thread_launcher, thread)) + goto error; + + if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated")) + goto error; + + locked = FALSE; + if (!mux_condition_bundle_unlock(&thread->isCreated)) + goto error; + + if (!signal_thread_is_running(thread)) + { + WLog_ERR(TAG, "failed to signal the thread was ready"); + goto error; + } + + rc = TRUE; +error: + if (locked) + { + if (!mux_condition_bundle_unlock(&thread->isCreated)) + rc = FALSE; + } + + pthread_attr_destroy(&attr); + + if (rc) + dump_thread(thread); + + return rc; +} + +HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, + DWORD dwCreationFlags, LPDWORD lpThreadId) +{ + HANDLE handle = NULL; + WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD)); + + if (!thread) + return NULL; + + thread->dwStackSize = dwStackSize; + thread->lpParameter = lpParameter; + thread->lpStartAddress = lpStartAddress; + thread->lpThreadAttributes = lpThreadAttributes; + thread->common.ops = &ops; +#if defined(WITH_DEBUG_THREADS) + thread->create_stack = winpr_backtrace(20); + dump_thread(thread); +#endif + + if (!winpr_event_init(&thread->event)) + { + WLog_ERR(TAG, "failed to create event"); + goto fail; + } + + if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL)) + { + WLog_ERR(TAG, "failed to initialize thread mutex"); + goto fail; + } + + if (!apc_init(&thread->apc)) + { + WLog_ERR(TAG, "failed to initialize APC"); + goto fail; + } + + if (!mux_condition_bundle_init(&thread->isCreated)) + goto fail; + if (!mux_condition_bundle_init(&thread->isRunning)) + goto fail; + + WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ); + handle = (HANDLE)thread; + + InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL); + + if (!(dwCreationFlags & CREATE_SUSPENDED)) + { + if (!winpr_StartThread(thread)) + goto fail; + } + else + { + if (!set_event(thread)) + goto fail; + } + + return handle; +fail: + cleanup_handle(thread); + return NULL; +} + +void cleanup_handle(void* obj) +{ + WINPR_THREAD* thread = (WINPR_THREAD*)obj; + if (!thread) + return; + + if (!apc_uninit(&thread->apc)) + WLog_ERR(TAG, "failed to destroy APC"); + + mux_condition_bundle_uninit(&thread->isCreated); + mux_condition_bundle_uninit(&thread->isRunning); + run_mutex_fkt(pthread_mutex_destroy, &thread->mutex); + + winpr_event_uninit(&thread->event); + +#if defined(WITH_THREAD_LIST) + ListDictionary_Remove(thread_list, &thread->thread); +#endif +#if defined(WITH_DEBUG_THREADS) + + if (thread->create_stack) + winpr_backtrace_free(thread->create_stack); + + if (thread->exit_stack) + winpr_backtrace_free(thread->exit_stack); + +#endif + free(thread); +} + +BOOL ThreadCloseHandle(HANDLE handle) +{ + WINPR_THREAD* thread = (WINPR_THREAD*)handle; + +#if defined(WITH_THREAD_LIST) + if (!thread_list) + { + WLog_ERR(TAG, "Thread list does not exist, check call!"); + dump_thread(thread); + } + else if (!ListDictionary_Contains(thread_list, &thread->thread)) + { + WLog_ERR(TAG, "Thread list does not contain this thread! check call!"); + dump_thread(thread); + } + else + { + ListDictionary_Lock(thread_list); +#endif + dump_thread(thread); + + if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0)) + { + WLog_DBG(TAG, "Thread running, setting to detached state!"); + thread->detached = TRUE; + pthread_detach(thread->thread); + } + else + { + cleanup_handle(thread); + } + +#if defined(WITH_THREAD_LIST) + ListDictionary_Unlock(thread_list); + } +#endif + + return TRUE; +} + +HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, + SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) +{ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return NULL; +} + +VOID ExitThread(DWORD dwExitCode) +{ +#if defined(WITH_THREAD_LIST) + DWORD rc; + pthread_t tid = pthread_self(); + + if (!thread_list) + { + WLog_ERR(TAG, "function called without existing thread list!"); +#if defined(WITH_DEBUG_THREADS) + DumpThreadHandles(); +#endif + pthread_exit(0); + } + else if (!ListDictionary_Contains(thread_list, &tid)) + { + WLog_ERR(TAG, "function called, but no matching entry in thread list!"); +#if defined(WITH_DEBUG_THREADS) + DumpThreadHandles(); +#endif + pthread_exit(0); + } + else + { + WINPR_THREAD* thread; + ListDictionary_Lock(thread_list); + thread = ListDictionary_GetItemValue(thread_list, &tid); + WINPR_ASSERT(thread); + thread->exited = TRUE; + thread->dwExitCode = dwExitCode; +#if defined(WITH_DEBUG_THREADS) + thread->exit_stack = winpr_backtrace(20); +#endif + ListDictionary_Unlock(thread_list); + set_event(thread); + rc = thread->dwExitCode; + + if (thread->detached || !thread->started) + cleanup_handle(thread); + + pthread_exit((void*)(size_t)rc); + } +#else + WINPR_UNUSED(dwExitCode); +#endif +} + +BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_THREAD* thread = NULL; + + if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD) + { + WLog_ERR(TAG, "hThread is not a thread"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + thread = (WINPR_THREAD*)Object; + *lpExitCode = thread->dwExitCode; + return TRUE; +} + +WINPR_THREAD* winpr_GetCurrentThread(VOID) +{ + WINPR_THREAD* ret = NULL; + + InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL); + if (mainThreadId == pthread_self()) + return (HANDLE)&mainThread; + + ret = TlsGetValue(currentThreadTlsIndex); + return ret; +} + +HANDLE _GetCurrentThread(VOID) +{ + return (HANDLE)winpr_GetCurrentThread(); +} + +DWORD GetCurrentThreadId(VOID) +{ + pthread_t tid = 0; + tid = pthread_self(); + /* Since pthread_t can be 64-bits on some systems, take just the */ + /* lower 32-bits of it for the thread ID returned by this function. */ + return (DWORD)tid & 0xffffffffUL; +} + +typedef struct +{ + WINPR_APC_ITEM apc; + PAPCFUNC completion; + ULONG_PTR completionArg; +} UserApcItem; + +static void userAPC(LPVOID arg) +{ + UserApcItem* userApc = (UserApcItem*)arg; + + userApc->completion(userApc->completionArg); + + userApc->apc.markedForRemove = TRUE; +} + +DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_APC_ITEM* apc = NULL; + UserApcItem* apcItem = NULL; + + if (!pfnAPC) + return 1; + + if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD) + { + WLog_ERR(TAG, "hThread is not a thread"); + SetLastError(ERROR_INVALID_PARAMETER); + return (DWORD)0; + } + + apcItem = calloc(1, sizeof(*apcItem)); + if (!apcItem) + { + SetLastError(ERROR_INVALID_PARAMETER); + return (DWORD)0; + } + + apc = &apcItem->apc; + apc->type = APC_TYPE_USER; + apc->markedForFree = TRUE; + apc->alwaysSignaled = TRUE; + apc->completion = userAPC; + apc->completionArgs = apc; + apcItem->completion = pfnAPC; + apcItem->completionArg = dwData; + apc_register(hThread, apc); + return 1; +} + +DWORD ResumeThread(HANDLE hThread) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_THREAD* thread = NULL; + + if (!winpr_Handle_GetInfo(hThread, &Type, &Object) || Object->Type != HANDLE_TYPE_THREAD) + { + WLog_ERR(TAG, "hThread is not a thread"); + SetLastError(ERROR_INVALID_PARAMETER); + return (DWORD)-1; + } + + thread = (WINPR_THREAD*)Object; + + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) + return (DWORD)-1; + + if (!thread->started) + { + if (!winpr_StartThread(thread)) + { + run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex); + return (DWORD)-1; + } + } + else + WLog_WARN(TAG, "Thread already started!"); + + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) + return (DWORD)-1; + + return 0; +} + +DWORD SuspendThread(HANDLE hThread) +{ + WLog_ERR(TAG, "not implemented"); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return (DWORD)-1; +} + +BOOL SwitchToThread(VOID) +{ + /** + * Note: on some operating systems sched_yield is a stub returning -1. + * usleep should at least trigger a context switch if any thread is waiting. + */ + if (sched_yield() != 0) + usleep(1); + + return TRUE; +} + +BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_THREAD* thread = NULL; + + if (!winpr_Handle_GetInfo(hThread, &Type, &Object)) + return FALSE; + + thread = (WINPR_THREAD*)Object; + thread->exited = TRUE; + thread->dwExitCode = dwExitCode; + + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) + return FALSE; + +#ifndef ANDROID + pthread_cancel(thread->thread); +#else + WLog_ERR(TAG, "Function not supported on this platform!"); +#endif + + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) + return FALSE; + + set_event(thread); + return TRUE; +} + +VOID DumpThreadHandles(void) +{ +#if defined(WITH_DEBUG_THREADS) + char** msg = NULL; + size_t used = 0; + void* stack = winpr_backtrace(20); + WLog_DBG(TAG, "---------------- Called from ----------------------------"); + msg = winpr_backtrace_symbols(stack, &used); + + for (size_t i = 0; i < used; i++) + { + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + } + + free(msg); + winpr_backtrace_free(stack); + WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------"); + +#if defined(WITH_THREAD_LIST) + if (!thread_list) + { + WLog_DBG(TAG, "All threads properly shut down and disposed of."); + } + else + { + ULONG_PTR* keys = NULL; + ListDictionary_Lock(thread_list); + int x, count = ListDictionary_GetKeys(thread_list, &keys); + WLog_DBG(TAG, "Dumping %d elements", count); + + for (size_t x = 0; x < count; x++) + { + WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*)keys[x]); + WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x); + msg = winpr_backtrace_symbols(thread->create_stack, &used); + + for (size_t i = 0; i < used; i++) + { + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + } + + free(msg); + + if (thread->started) + { + WLog_DBG(TAG, "Thread [%d] still running!", x); + } + else + { + WLog_DBG(TAG, "Thread [%d] exited at:", x); + msg = winpr_backtrace_symbols(thread->exit_stack, &used); + + for (size_t i = 0; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); + } + } + + free(keys); + ListDictionary_Unlock(thread_list); + } +#endif + + WLog_DBG(TAG, "---------------- End Dumping thread handles -------------"); +#endif +} +#endif diff --git a/winpr/libwinpr/thread/thread.h b/winpr/libwinpr/thread/thread.h new file mode 100644 index 0000000..aef9bc9 --- /dev/null +++ b/winpr/libwinpr/thread/thread.h @@ -0,0 +1,95 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau + * Copyright 2015 Hewlett-Packard Development Company, L.P. + * Copyright 2021 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_THREAD_PRIVATE_H +#define WINPR_THREAD_PRIVATE_H + +#ifndef _WIN32 + +#include + +#include + +#include "../handle/handle.h" +#include "../synch/event.h" +#include "apc.h" + +#ifdef __GNUC__ +#define ALIGN64 __attribute__((aligned(8))) +#else +#ifdef _WIN32 +#define ALIGN64 __declspec(align(8)) +#else +#define ALIGN64 +#endif +#endif + +typedef void* (*pthread_start_routine)(void*); +typedef struct winpr_APC_item WINPR_APC_ITEM; + +typedef struct +{ + ALIGN64 pthread_mutex_t mux; + ALIGN64 pthread_cond_t cond; + ALIGN64 BOOL val; +} mux_condition_bundle; + +struct winpr_thread +{ + WINPR_HANDLE common; + + ALIGN64 BOOL started; + ALIGN64 WINPR_EVENT_IMPL event; + ALIGN64 BOOL mainProcess; + ALIGN64 BOOL detached; + ALIGN64 BOOL joined; + ALIGN64 BOOL exited; + ALIGN64 DWORD dwExitCode; + ALIGN64 pthread_t thread; + ALIGN64 SIZE_T dwStackSize; + ALIGN64 LPVOID lpParameter; + ALIGN64 pthread_mutex_t mutex; + mux_condition_bundle isRunning; + mux_condition_bundle isCreated; + ALIGN64 LPTHREAD_START_ROUTINE lpStartAddress; + ALIGN64 LPSECURITY_ATTRIBUTES lpThreadAttributes; + ALIGN64 APC_QUEUE apc; +#if defined(WITH_DEBUG_THREADS) + ALIGN64 void* create_stack; + ALIGN64 void* exit_stack; +#endif +}; + +WINPR_THREAD* winpr_GetCurrentThread(VOID); + +typedef struct +{ + WINPR_HANDLE common; + + pid_t pid; + int status; + DWORD dwExitCode; + int fd; +} WINPR_PROCESS; + +#endif + +#endif /* WINPR_THREAD_PRIVATE_H */ diff --git a/winpr/libwinpr/thread/tls.c b/winpr/libwinpr/thread/tls.c new file mode 100644 index 0000000..977d47d --- /dev/null +++ b/winpr/libwinpr/thread/tls.c @@ -0,0 +1,72 @@ +/** + * WinPR: Windows Portable Runtime + * Process Thread Functions + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +/** + * TlsAlloc + * TlsFree + * TlsGetValue + * TlsSetValue + */ + +#ifndef _WIN32 + +#include + +DWORD TlsAlloc(void) +{ + pthread_key_t key = 0; + + if (pthread_key_create(&key, NULL) != 0) + return TLS_OUT_OF_INDEXES; + + return key; +} + +LPVOID TlsGetValue(DWORD dwTlsIndex) +{ + LPVOID value = NULL; + pthread_key_t key = 0; + key = (pthread_key_t)dwTlsIndex; + value = (LPVOID)pthread_getspecific(key); + return value; +} + +BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) +{ + pthread_key_t key = 0; + key = (pthread_key_t)dwTlsIndex; + pthread_setspecific(key, lpTlsValue); + return TRUE; +} + +BOOL TlsFree(DWORD dwTlsIndex) +{ + pthread_key_t key = 0; + key = (pthread_key_t)dwTlsIndex; + pthread_key_delete(key); + return TRUE; +} + +#endif diff --git a/winpr/libwinpr/timezone/CMakeLists.txt b/winpr/libwinpr/timezone/CMakeLists.txt new file mode 100644 index 0000000..5962332 --- /dev/null +++ b/winpr/libwinpr/timezone/CMakeLists.txt @@ -0,0 +1,18 @@ +# WinPR: Windows Portable Runtime +# libwinpr-timezone cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(timezone.c TimeZones.c WindowsZones.c TimeZones.h WindowsZones.h) diff --git a/winpr/libwinpr/timezone/ModuleOptions.cmake b/winpr/libwinpr/timezone/ModuleOptions.cmake new file mode 100644 index 0000000..7f5df38 --- /dev/null +++ b/winpr/libwinpr/timezone/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "timezone") +set(MINWIN_LONG_NAME "Time Zone Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/timezone/TimeZones.c b/winpr/libwinpr/timezone/TimeZones.c new file mode 100644 index 0000000..489426f --- /dev/null +++ b/winpr/libwinpr/timezone/TimeZones.c @@ -0,0 +1,4099 @@ +/* + * Automatically generated with scripts/TimeZones.csx + */ + +#include "TimeZones.h" + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_2[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_5[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_7[] = { { + 633978108000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633978972000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_9[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_11[] = { { + 638080380000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_12[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_13[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_15[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_16[] = { { + 633346524000000000ULL, + 0ULL, + 60, + { 0, 3, 6, 2, 22, 0, 0, 0 }, + { 0, 10, 6, 2, 22, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + { 0, 10, 6, 2, 22, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 6, 2, 22, 0, 0, 0 }, + { 0, 10, 6, 2, 22, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 4, 6, 1, 22, 0, 0, 0 }, + { 0, 10, 6, 2, 22, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 5, 6, 1, 22, 0, 0, 0 }, + { 0, 8, 6, 3, 22, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 4, 6, 5, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 4, 6, 5, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 4, 6, 5, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 5, 6, 2, 22, 0, 0, 0 }, + { 0, 8, 6, 2, 22, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 5, 6, 2, 22, 0, 0, 0 }, + { 0, 8, 6, 2, 22, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 5, 6, 2, 22, 0, 0, 0 }, + { 0, 8, 6, 2, 22, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 4, 6, 1, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 4, 6, 1, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 4, 6, 1, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 4, 6, 1, 22, 0, 0, 0 }, + { 0, 9, 6, 2, 22, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 638081244000000000ULL, + 60, + { 0, 4, 6, 1, 22, 0, 0, 0 }, + { 0, 9, 6, 1, 22, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_17[] = { { + 638080380000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_20[] = { { + 635555772000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + { 0, 2, 0, 1, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_21[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_22[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 636188220000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_23[] = { { + 632084220000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 4, 0, 1, 0, 0, 0, 0 }, + }, + { + 632400444000000000ULL, + 632085084000000000ULL, + 60, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 0, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 3, 0, 2, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 3, 0, 3, 0, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 3, 0, 2, 0, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 3, 0, 2, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 11, 0, 2, 1, 0, 0, 0 }, + { 0, 3, 0, 3, 0, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 11, 0, 1, 1, 0, 0, 0 }, + { 0, 4, 0, 1, 0, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 634925916000000000ULL, + 60, + { 0, 11, 0, 1, 1, 0, 0, 0 }, + { 0, 3, 0, 2, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_24[] = { { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_25[] = { { + 635555772000000000ULL, + 0ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 636818940000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_26[] = { { + 633662748000000000ULL, + 0ULL, + 60, + { 0, 3, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 6, 1, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 4, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 4, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 3, 6, 4, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 3, 6, 4, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 3, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 5, 23, 59, 59, 999 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 3, 6, 4, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 3, 6, 4, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 3, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 6, 1, 23, 59, 59, 999 }, + }, + { + 3155378076000000000ULL, + 638081244000000000ULL, + 60, + { 0, 3, 0, 4, 0, 0, 0, 0 }, + { 0, 10, 0, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_27[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_28[] = { { + 633346524000000000ULL, + 633032028000000000ULL, + 30, + { 0, 12, 0, 2, 3, 0, 0, 0 }, + { 0, 1, 1, 1, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -30, + { 0, 5, 0, 1, 2, 30, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_29[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + { 0, 11, 2, 1, 0, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 0, 3, 0, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 11, 0, 1, 0, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 2, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 0, 2, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 0, 3, 0, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 2, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 2, 6, 4, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 11, 6, 1, 23, 59, 59, 999 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 2, 0, 3, 0, 0, 0, 0 }, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_31[] = { { + 633346524000000000ULL, + 0ULL, + 60, + { 0, 3, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 5, 6, 1, 23, 59, 59, 999 }, + { 0, 8, 6, 3, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 4, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 4, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 4, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 5, 6, 2, 23, 59, 59, 999 }, + { 0, 8, 6, 2, 23, 59, 59, 999 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 5, 6, 2, 23, 59, 59, 999 }, + { 0, 8, 6, 2, 23, 59, 59, 999 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 5, 6, 2, 23, 59, 59, 999 }, + { 0, 8, 6, 2, 23, 59, 59, 999 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 9, 6, 2, 23, 59, 59, 999 }, + }, + { + 3155378076000000000ULL, + 638081244000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_32[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 0, 1, 0, 0 }, + { 0, 4, 0, 1, 0, 1, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 0, 1, 0, 0 }, + { 0, 3, 0, 2, 0, 1, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 11, 0, 1, 0, 1, 0, 0 }, + { 0, 3, 0, 2, 0, 1, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 11, 0, 1, 0, 1, 0, 0 }, + { 0, 3, 0, 2, 0, 1, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 11, 0, 1, 0, 1, 0, 0 }, + { 0, 3, 0, 2, 0, 1, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 0, 1, 0, 0 }, + }, + { + 3155378076000000000ULL, + 634609692000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_33[] = { { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + { 0, 10, 0, 3, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_34[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 2, 6, 2, 23, 59, 59, 999 }, + { 0, 11, 2, 1, 0, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 0, 3, 0, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 11, 0, 1, 0, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 2, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 0, 2, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 0, 3, 0, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 2, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 2, 6, 4, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 2, 6, 3, 23, 59, 59, 999 }, + { 0, 11, 6, 1, 23, 59, 59, 999 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 2, 0, 3, 0, 0, 0, 0 }, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_36[] = { { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 1, 1, 1, 0, 0, 0, 0 }, + { 0, 12, 0, 5, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 3, 0, 3, 0, 0, 0, 0 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 6, 2, 23, 59, 59, 999 }, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_37[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 4, 22, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 10, 6, 4, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 4, 22, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 6, 4, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 4, 22, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 10, 6, 4, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 637450524000000000ULL, + 60, + { 0, 10, 6, 5, 23, 0, 0, 0 }, + { 0, 3, 6, 5, 22, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_38[] = { { + 635555772000000000ULL, + 0ULL, + 60, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_39[] = { { + 633346524000000000ULL, + 0ULL, + 60, + { 0, 3, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 6, 2, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + { 0, 10, 6, 2, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 5, 6, 1, 23, 59, 59, 999 }, + { 0, 8, 6, 3, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 4, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 4, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 4, 6, 5, 23, 59, 59, 999 }, + { 0, 9, 6, 1, 23, 59, 59, 999 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 5, 6, 2, 23, 59, 59, 999 }, + { 0, 8, 6, 2, 23, 59, 59, 999 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_40[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_41[] = { { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 10, 6, 3, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 2, 6, 4, 23, 59, 59, 999 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_43[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 9, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_44[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 1, 0, 0, 0 }, + { 0, 3, 0, 5, 0, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_47[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 0, 5, 1, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_49[] = { { + 636818076000000000ULL, + 636503580000000000ULL, + -60, + { 0, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 1, 1, 1, 0, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 1, 2, 1, 2, 0, 0, 0 }, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_50[] = { { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 8, 0, 5, 23, 59, 59, 999 }, + { 0, 5, 6, 5, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 8, 4, 3, 23, 59, 59, 999 }, + { 0, 5, 0, 5, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 8, 6, 1, 23, 59, 59, 999 }, + { 0, 5, 6, 1, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 7, 6, 5, 23, 59, 59, 999 }, + { 0, 4, 6, 1, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 9, 0, 5, 3, 0, 0, 0 }, + { 0, 4, 0, 5, 2, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 4, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 0, 4, 3, 0, 0, 0 }, + { 0, 3, 0, 4, 2, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + { 0, 6, 0, 2, 2, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 4, 0, 3, 3, 0, 0, 0 }, + { 0, 5, 0, 5, 2, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 4, 0, 2, 3, 0, 0, 0 }, + { 0, 5, 0, 3, 2, 0, 0, 0 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 5, 0, 2, 2, 0, 0, 0 }, + }, + { + 638395740000000000ULL, + 638081244000000000ULL, + 60, + { 0, 3, 0, 3, 3, 0, 0, 0 }, + { 0, 4, 0, 4, 2, 0, 0, 0 }, + }, + { + 638711964000000000ULL, + 638396604000000000ULL, + 60, + { 0, 3, 0, 2, 3, 0, 0, 0 }, + { 0, 4, 0, 2, 2, 0, 0, 0 }, + }, + { + 639027324000000000ULL, + 638712828000000000ULL, + 60, + { 0, 2, 0, 5, 3, 0, 0, 0 }, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + }, + { + 639342684000000000ULL, + 639028188000000000ULL, + 60, + { 0, 2, 0, 3, 3, 0, 0, 0 }, + { 0, 3, 0, 4, 2, 0, 0, 0 }, + }, + { + 639658044000000000ULL, + 639343548000000000ULL, + 60, + { 0, 2, 0, 1, 3, 0, 0, 0 }, + { 0, 3, 0, 2, 2, 0, 0, 0 }, + }, + { + 639974268000000000ULL, + 639658908000000000ULL, + 60, + { 0, 1, 0, 4, 3, 0, 0, 0 }, + { 0, 3, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 639975132000000000ULL, + 60, + { 0, 1, 0, 2, 3, 0, 0, 0 }, + { 0, 2, 0, 3, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_51[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_52[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_53[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_54[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_56[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 0, 5, 3, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_57[] = { { + 633978108000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 0, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 4, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 6, 4, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 4, 23, 59, 59, 999 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 10, 6, 4, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + }, + { + 3155378076000000000ULL, + 637450524000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 23, 59, 59, 999 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_58[] = { { + 632715804000000000ULL, + 0ULL, + 60, + { 0, 9, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 5, 5, 0, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 9, 4, 3, 23, 59, 59, 999 }, + { 0, 4, 5, 5, 0, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 9, 4, 1, 23, 59, 59, 999 }, + { 0, 4, 4, 5, 23, 59, 59, 999 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 8, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 4, 5, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 8, 4, 3, 23, 59, 59, 999 }, + { 0, 4, 4, 4, 23, 59, 59, 999 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 9, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 4, 5, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 9, 4, 5, 23, 59, 59, 999 }, + { 0, 5, 4, 3, 23, 59, 59, 999 }, + }, + { + 3155378076000000000ULL, + 638081244000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 4, 5, 23, 59, 59, 999 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_59[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_60[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 9, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 4, 1, 0, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 9, 5, 5, 23, 59, 59, 999 }, + { 0, 4, 5, 1, 0, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 9, 4, 3, 23, 59, 59, 999 }, + { 0, 4, 6, 1, 0, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 11, 4, 1, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 10, 5, 5, 23, 59, 59, 999 }, + { 0, 4, 5, 1, 0, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 5, 1, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 4, 5, 1, 0, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 10, 4, 4, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 10, 4, 4, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 638395740000000000ULL, + 638081244000000000ULL, + 60, + { 0, 10, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 638396604000000000ULL, + 60, + { 0, 10, 5, 4, 0, 0, 0, 0 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_61[] = { { + 634925052000000000ULL, + 0ULL, + 60, + { 0, 9, 5, 3, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 9, 4, 5, 23, 59, 59, 999 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 4, 4, 23, 59, 59, 999 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 5, 4, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 1, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 1, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 4, 1, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 10, 6, 4, 0, 0, 0, 0 }, + { 0, 3, 5, 5, 0, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 10, 6, 4, 1, 0, 0, 0 }, + { 0, 3, 6, 4, 0, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 638395740000000000ULL, + 638081244000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 638711964000000000ULL, + 638396604000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 639027324000000000ULL, + 638712828000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 639342684000000000ULL, + 639028188000000000ULL, + 60, + { 0, 10, 6, 4, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 639658044000000000ULL, + 639343548000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 639974268000000000ULL, + 639658908000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + }, + { + 640289628000000000ULL, + 639975132000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 4, 0, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 640290492000000000ULL, + 60, + { 0, 10, 6, 5, 1, 0, 0, 0 }, + { 0, 3, 6, 5, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_63[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 0, 5, 3, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_64[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 9, 3, 4, 1, 0, 0, 0 }, + { 0, 4, 3, 1, 1, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 10, 0, 2, 2, 0, 0, 0 }, + { 0, 4, 5, 1, 2, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 9, 0, 3, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 9, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 9, 0, 2, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + { 0, 4, 5, 1, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 9, 0, 4, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 4, 2, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 4, 2, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + }, + { + 638395740000000000ULL, + 638081244000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 3, 5, 4, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 638396604000000000ULL, + 60, + { 0, 10, 0, 4, 2, 0, 0, 0 }, + { 0, 3, 5, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_65[] = { { + 637449660000000000ULL, + 0ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 1, 0, 5, 23, 59, 59, 999 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_66[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_67[] = { { + 636187356000000000ULL, + 0ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 2, 5, 23, 59, 59, 999 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_68[] = { { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 11, 6, 2, 2, 0, 0, 0 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + { 0, 3, 5, 5, 1, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_69[] = { { + 636502716000000000ULL, + 0ULL, + 60, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + { 0, 9, 0, 1, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_70[] = { { + 634608828000000000ULL, + 0ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 12, 5, 3, 0, 0, 0, 0 }, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 10, 5, 5, 1, 0, 0, 0 }, + { 0, 3, 4, 5, 23, 59, 59, 999 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 2, 4, 5, 23, 59, 59, 999 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_71[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 10, 5, 1, 4, 0, 0, 0 }, + { 0, 4, 4, 1, 3, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 10, 6, 1, 4, 0, 0, 0 }, + { 0, 4, 5, 1, 3, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 10, 0, 1, 4, 0, 0, 0 }, + { 0, 4, 6, 1, 3, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 10, 1, 1, 4, 0, 0, 0 }, + { 0, 4, 0, 1, 3, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_72[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 1, 5, 3, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 4, 0, 0, 0 }, + { 0, 3, 1, 5, 3, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 11, 0, 2, 4, 0, 0, 0 }, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_74[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_75[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_77[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + -60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 1, 1, 0, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 12, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_78[] = { { + 632400444000000000ULL, + 0ULL, + 60, + { 0, 9, 1, 3, 23, 59, 59, 999 }, + { 0, 3, 0, 3, 0, 0, 0, 0 }, + }, + { + 632715804000000000ULL, + 632401308000000000ULL, + 60, + { 0, 9, 3, 3, 23, 59, 59, 999 }, + { 0, 3, 2, 4, 0, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 9, 6, 3, 23, 59, 59, 999 }, + { 0, 3, 5, 3, 0, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 9, 1, 3, 23, 59, 59, 999 }, + { 0, 3, 0, 4, 0, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 9, 2, 3, 23, 59, 59, 999 }, + { 0, 3, 1, 4, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 9, 3, 3, 23, 59, 59, 999 }, + { 0, 3, 2, 4, 0, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 9, 4, 3, 23, 59, 59, 999 }, + { 0, 3, 3, 3, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 9, 6, 3, 23, 59, 59, 999 }, + { 0, 3, 5, 4, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 9, 0, 3, 23, 59, 59, 999 }, + { 0, 3, 6, 4, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 9, 1, 3, 23, 59, 59, 999 }, + { 0, 3, 0, 4, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 9, 2, 3, 23, 59, 59, 999 }, + { 0, 3, 1, 3, 0, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 9, 4, 3, 23, 59, 59, 999 }, + { 0, 3, 3, 4, 0, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 9, 5, 3, 23, 59, 59, 999 }, + { 0, 3, 4, 4, 0, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 9, 6, 3, 23, 59, 59, 999 }, + { 0, 3, 5, 4, 0, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 9, 0, 3, 23, 59, 59, 999 }, + { 0, 3, 6, 3, 0, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 9, 2, 3, 23, 59, 59, 999 }, + { 0, 3, 1, 4, 0, 0, 0, 0 }, + }, + { + 638080380000000000ULL, + 637765884000000000ULL, + 60, + { 0, 9, 3, 3, 23, 59, 59, 999 }, + { 0, 3, 2, 4, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_80[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_81[] = { { + 635871132000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 5, 0, 0, 0 }, + { 0, 3, 0, 5, 4, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_82[] = { { + 633978108000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_83[] = { { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_84[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 12, 0, 1, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_86[] = { { + 634608828000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_89[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_90[] = { { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 10, 5, 5, 23, 59, 59, 999 }, + { 0, 5, 6, 5, 23, 59, 59, 999 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 10, 6, 5, 23, 59, 59, 999 }, + { 0, 4, 2, 2, 23, 59, 59, 999 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_91[] = { { + 636502716000000000ULL, + 0ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 12, 5, 3, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_96[] = { { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 12, 4, 5, 23, 59, 59, 999 }, + { 0, 6, 5, 3, 23, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_97[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_100[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_101[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 9, 6, 5, 2, 0, 0, 0 }, + { 0, 3, 6, 5, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 9, 5, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 9, 5, 4, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_102[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_103[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 7, 0, 4, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_104[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 5, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_106[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_108[] = { { + 633031164000000000ULL, + 632716668000000000ULL, + 60, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + { 0, 12, 0, 1, 2, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 633662748000000000ULL, + 633347388000000000ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_110[] = { { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 9, 5, 5, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 9, 5, 4, 23, 59, 59, 999 }, + { 0, 3, 6, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_112[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 120, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_114[] = { { + 635871132000000000ULL, + 635556636000000000ULL, + 30, + { 0, 8, 5, 2, 23, 59, 59, 999 }, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + -30, + { 0, 5, 5, 1, 23, 30, 0, 0 }, + { 0, 1, 1, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_116[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_117[] = { { + 633346524000000000ULL, + 0ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633347388000000000ULL, + 60, + { 0, 4, 0, 1, 3, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_120[] = { { + 633346524000000000ULL, + 0ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633347388000000000ULL, + 60, + { 0, 4, 0, 1, 3, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_122[] = { { + 633346524000000000ULL, + 0ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633347388000000000ULL, + 60, + { 0, 4, 0, 1, 3, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_123[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_124[] = { { + 632715804000000000ULL, + 0ULL, + 30, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 633031164000000000ULL, + 632716668000000000ULL, + 30, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 30, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633347388000000000ULL, + 30, + { 0, 4, 0, 1, 2, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_125[] = { { + 635240412000000000ULL, + 0ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + { 0, 12, 0, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_126[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_127[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 120, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 4, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_128[] = { { + 635555772000000000ULL, + 0ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 30, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 1, 2, 1, 0, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 637134300000000000ULL, + 60, + { 0, 4, 0, 1, 3, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_129[] = { { + 634293468000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 10, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 3, 1, 0, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + -60, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_131[] = { { + 633978108000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 1, 6, 1, 0, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_132[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 3, 0, 3, 3, 0, 0, 0 }, + { 0, 10, 0, 1, 2, 0, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 3, 0, 3, 3, 0, 0, 0 }, + { 0, 9, 0, 5, 2, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633347388000000000ULL, + 60, + { 0, 4, 0, 1, 3, 0, 0, 0 }, + { 0, 9, 0, 5, 2, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_134[] = { { + 633978108000000000ULL, + 633663612000000000ULL, + 60, + { 0, 1, 4, 1, 0, 0, 0, 0 }, + { 0, 11, 0, 5, 2, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 3, 0, 5, 3, 0, 0, 0 }, + { 0, 10, 0, 4, 2, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 3, 0, 1, 3, 0, 0, 0 }, + { 0, 10, 0, 4, 2, 0, 0, 0 }, + }, + { + 634925052000000000ULL, + 634609692000000000ULL, + 60, + { 0, 1, 0, 4, 3, 0, 0, 0 }, + { 0, 10, 0, 3, 2, 0, 0, 0 }, + }, + { + 635240412000000000ULL, + 634925916000000000ULL, + 60, + { 0, 1, 0, 3, 3, 0, 0, 0 }, + { 0, 10, 0, 4, 2, 0, 0, 0 }, + }, + { + 635555772000000000ULL, + 635241276000000000ULL, + 60, + { 0, 1, 0, 3, 2, 0, 0, 0 }, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + }, + { + 635871132000000000ULL, + 635556636000000000ULL, + 60, + { 0, 1, 0, 3, 3, 0, 0, 0 }, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + }, + { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 1, 0, 3, 3, 0, 0, 0 }, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 1, 0, 3, 3, 0, 0, 0 }, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + }, + { + 636818076000000000ULL, + 636503580000000000ULL, + 60, + { 0, 1, 0, 2, 3, 0, 0, 0 }, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + }, + { + 637133436000000000ULL, + 636818940000000000ULL, + 60, + { 0, 1, 0, 2, 3, 0, 0, 0 }, + { 0, 11, 0, 2, 2, 0, 0, 0 }, + }, + { + 637449660000000000ULL, + 637134300000000000ULL, + 60, + { 0, 1, 0, 2, 3, 0, 0, 0 }, + { 0, 12, 0, 3, 2, 0, 0, 0 }, + }, + { + 637765020000000000ULL, + 637450524000000000ULL, + 60, + { 0, 1, 0, 3, 3, 0, 0, 0 }, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_135[] = { { + 3155378076000000000ULL, + 0ULL, + 60, + { 0, 10, 0, 5, 3, 0, 0, 0 }, + { 0, 3, 0, 5, 2, 0, 0, 0 }, +} }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_136[] = { { + 633031164000000000ULL, + 0ULL, + 60, + { 0, 3, 0, 3, 3, 45, 0, 0 }, + { 0, 10, 0, 1, 2, 45, 0, 0 }, + }, + { + 633346524000000000ULL, + 633032028000000000ULL, + 60, + { 0, 3, 0, 3, 3, 45, 0, 0 }, + { 0, 9, 0, 5, 2, 45, 0, 0 }, + }, + { + 3155378076000000000ULL, + 633347388000000000ULL, + 60, + { 0, 4, 0, 1, 3, 45, 0, 0 }, + { 0, 9, 0, 5, 2, 45, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_138[] = { { + 636187356000000000ULL, + 635871996000000000ULL, + 60, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + { 0, 11, 0, 1, 2, 0, 0, 0 }, + }, + { + 636502716000000000ULL, + 636188220000000000ULL, + 60, + { 0, 1, 0, 3, 3, 0, 0, 0 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + } }; + +static const TIME_ZONE_RULE_ENTRY TimeZoneRuleTable_139[] = { { + 633978108000000000ULL, + 0ULL, + 0, + { 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 0, 0, 0, 0 }, + }, + { + 634293468000000000ULL, + 633978972000000000ULL, + 60, + { 0, 1, 5, 1, 0, 0, 0, 0 }, + { 0, 9, 0, 5, 0, 0, 0, 0 }, + }, + { + 634608828000000000ULL, + 634294332000000000ULL, + 60, + { 0, 4, 6, 1, 4, 0, 0, 0 }, + { 0, 9, 6, 4, 3, 0, 0, 0 }, + }, + { + 3155378076000000000ULL, + 634609692000000000ULL, + 60, + { 0, 4, 0, 1, 4, 0, 0, 0 }, + { 0, 9, 0, 5, 3, 0, 0, 0 }, + } }; + +const TIME_ZONE_ENTRY TimeZoneTable[] = { + { "Dateline Standard Time", 720, FALSE, "(UTC-12:00) International Date Line West", + "Dateline Standard Time", "Dateline Daylight Time", NULL, 0 }, + { "UTC-11", 660, FALSE, "(UTC-11:00) Coordinated Universal Time-11", "UTC-11", "UTC-11", NULL, + 0 }, + { "Aleutian Standard Time", 600, TRUE, "(UTC-10:00) Aleutian Islands", "Aleutian Standard Time", + "Aleutian Daylight Time", TimeZoneRuleTable_2, 2 }, + { "Hawaiian Standard Time", 600, FALSE, "(UTC-10:00) Hawaii", "Hawaiian Standard Time", + "Hawaiian Daylight Time", NULL, 0 }, + { "Marquesas Standard Time", 570, FALSE, "(UTC-09:30) Marquesas Islands", + "Marquesas Standard Time", "Marquesas Daylight Time", NULL, 0 }, + { "Alaskan Standard Time", 540, TRUE, "(UTC-09:00) Alaska", "Alaskan Standard Time", + "Alaskan Daylight Time", TimeZoneRuleTable_5, 2 }, + { "UTC-09", 540, FALSE, "(UTC-09:00) Coordinated Universal Time-09", "UTC-09", "UTC-09", NULL, + 0 }, + { "Pacific Standard Time (Mexico)", 480, TRUE, "(UTC-08:00) Baja California", + "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)", TimeZoneRuleTable_7, 2 }, + { "UTC-08", 480, FALSE, "(UTC-08:00) Coordinated Universal Time-08", "UTC-08", "UTC-08", NULL, + 0 }, + { "Pacific Standard Time", 480, TRUE, "(UTC-08:00) Pacific Time (US & Canada)", + "Pacific Standard Time", "Pacific Daylight Time", TimeZoneRuleTable_9, 2 }, + { "US Mountain Standard Time", 420, FALSE, "(UTC-07:00) Arizona", "US Mountain Standard Time", + "US Mountain Daylight Time", NULL, 0 }, + { "Mountain Standard Time (Mexico)", 420, TRUE, "(UTC-07:00) La Paz, Mazatlan", + "Mountain Standard Time (Mexico)", "Mountain Daylight Time (Mexico)", TimeZoneRuleTable_11, + 1 }, + { "Mountain Standard Time", 420, TRUE, "(UTC-07:00) Mountain Time (US & Canada)", + "Mountain Standard Time", "Mountain Daylight Time", TimeZoneRuleTable_12, 2 }, + { "Yukon Standard Time", 420, TRUE, "(UTC-07:00) Yukon", "Yukon Standard Time", + "Yukon Daylight Time", TimeZoneRuleTable_13, 15 }, + { "Central America Standard Time", 360, FALSE, "(UTC-06:00) Central America", + "Central America Standard Time", "Central America Daylight Time", NULL, 0 }, + { "Central Standard Time", 360, TRUE, "(UTC-06:00) Central Time (US & Canada)", + "Central Standard Time", "Central Daylight Time", TimeZoneRuleTable_15, 2 }, + { "Easter Island Standard Time", 360, TRUE, "(UTC-06:00) Easter Island", + "Easter Island Standard Time", "Easter Island Daylight Time", TimeZoneRuleTable_16, 17 }, + { "Central Standard Time (Mexico)", 360, TRUE, + "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "Central Standard Time (Mexico)", + "Central Daylight Time (Mexico)", TimeZoneRuleTable_17, 1 }, + { "Canada Central Standard Time", 360, FALSE, "(UTC-06:00) Saskatchewan", + "Canada Central Standard Time", "Canada Central Daylight Time", NULL, 0 }, + { "SA Pacific Standard Time", 300, FALSE, "(UTC-05:00) Bogota, Lima, Quito, Rio Branco", + "SA Pacific Standard Time", "SA Pacific Daylight Time", NULL, 0 }, + { "Eastern Standard Time (Mexico)", 300, TRUE, "(UTC-05:00) Chetumal", + "Eastern Standard Time (Mexico)", "Eastern Daylight Time (Mexico)", TimeZoneRuleTable_20, 2 }, + { "Eastern Standard Time", 300, TRUE, "(UTC-05:00) Eastern Time (US & Canada)", + "Eastern Standard Time", "Eastern Daylight Time", TimeZoneRuleTable_21, 2 }, + { "Haiti Standard Time", 300, TRUE, "(UTC-05:00) Haiti", "Haiti Standard Time", + "Haiti Daylight Time", TimeZoneRuleTable_22, 6 }, + { "Cuba Standard Time", 300, TRUE, "(UTC-05:00) Havana", "Cuba Standard Time", + "Cuba Daylight Time", TimeZoneRuleTable_23, 11 }, + { "US Eastern Standard Time", 300, TRUE, "(UTC-05:00) Indiana (East)", + "US Eastern Standard Time", "US Eastern Daylight Time", TimeZoneRuleTable_24, 2 }, + { "Turks And Caicos Standard Time", 300, TRUE, "(UTC-05:00) Turks and Caicos", + "Turks and Caicos Standard Time", "Turks and Caicos Daylight Time", TimeZoneRuleTable_25, 6 }, + { "Paraguay Standard Time", 240, TRUE, "(UTC-04:00) Asuncion", "Paraguay Standard Time", + "Paraguay Daylight Time", TimeZoneRuleTable_26, 16 }, + { "Atlantic Standard Time", 240, TRUE, "(UTC-04:00) Atlantic Time (Canada)", + "Atlantic Standard Time", "Atlantic Daylight Time", TimeZoneRuleTable_27, 2 }, + { "Venezuela Standard Time", 240, TRUE, "(UTC-04:00) Caracas", "Venezuela Standard Time", + "Venezuela Daylight Time", TimeZoneRuleTable_28, 10 }, + { "Central Brazilian Standard Time", 240, TRUE, "(UTC-04:00) Cuiaba", + "Central Brazilian Standard Time", "Central Brazilian Daylight Time", TimeZoneRuleTable_29, + 16 }, + { "SA Western Standard Time", 240, FALSE, "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan", + "SA Western Standard Time", "SA Western Daylight Time", NULL, 0 }, + { "Pacific SA Standard Time", 240, TRUE, "(UTC-04:00) Santiago", "Pacific SA Standard Time", + "Pacific SA Daylight Time", TimeZoneRuleTable_31, 17 }, + { "Newfoundland Standard Time", 210, TRUE, "(UTC-03:30) Newfoundland", + "Newfoundland Standard Time", "Newfoundland Daylight Time", TimeZoneRuleTable_32, 7 }, + { "Tocantins Standard Time", 180, TRUE, "(UTC-03:00) Araguaina", "Tocantins Standard Time", + "Tocantins Daylight Time", TimeZoneRuleTable_33, 2 }, + { "E. South America Standard Time", 180, TRUE, "(UTC-03:00) Brasilia", + "E. South America Standard Time", "E. South America Daylight Time", TimeZoneRuleTable_34, + 16 }, + { "SA Eastern Standard Time", 180, FALSE, "(UTC-03:00) Cayenne, Fortaleza", + "SA Eastern Standard Time", "SA Eastern Daylight Time", NULL, 0 }, + { "Argentina Standard Time", 180, TRUE, "(UTC-03:00) City of Buenos Aires", + "Argentina Standard Time", "Argentina Daylight Time", TimeZoneRuleTable_36, 3 }, + { "Greenland Standard Time", 180, TRUE, "(UTC-03:00) Greenland", "Greenland Standard Time", + "Greenland Daylight Time", TimeZoneRuleTable_37, 18 }, + { "Montevideo Standard Time", 180, TRUE, "(UTC-03:00) Montevideo", "Montevideo Standard Time", + "Montevideo Daylight Time", TimeZoneRuleTable_38, 2 }, + { "Magallanes Standard Time", 180, TRUE, "(UTC-03:00) Punta Arenas", "Magallanes Standard Time", + "Magallanes Daylight Time", TimeZoneRuleTable_39, 9 }, + { "Saint Pierre Standard Time", 180, TRUE, "(UTC-03:00) Saint Pierre and Miquelon", + "Saint Pierre Standard Time", "Saint Pierre Daylight Time", TimeZoneRuleTable_40, 2 }, + { "Bahia Standard Time", 180, TRUE, "(UTC-03:00) Salvador", "Bahia Standard Time", + "Bahia Daylight Time", TimeZoneRuleTable_41, 2 }, + { "UTC-02", 120, FALSE, "(UTC-02:00) Coordinated Universal Time-02", "UTC-02", "UTC-02", NULL, + 0 }, + { "Mid-Atlantic Standard Time", 120, TRUE, "(UTC-02:00) Mid-Atlantic - Old", + "Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time", TimeZoneRuleTable_43, 1 }, + { "Azores Standard Time", 60, TRUE, "(UTC-01:00) Azores", "Azores Standard Time", + "Azores Daylight Time", TimeZoneRuleTable_44, 1 }, + { "Cape Verde Standard Time", 60, FALSE, "(UTC-01:00) Cabo Verde Is.", + "Cabo Verde Standard Time", "Cabo Verde Daylight Time", NULL, 0 }, + { "UTC", 0, FALSE, "(UTC) Coordinated Universal Time", "Coordinated Universal Time", + "Coordinated Universal Time", NULL, 0 }, + { "GMT Standard Time", 0, TRUE, "(UTC+00:00) Dublin, Edinburgh, Lisbon, London", + "GMT Standard Time", "GMT Daylight Time", TimeZoneRuleTable_47, 1 }, + { "Greenwich Standard Time", 0, FALSE, "(UTC+00:00) Monrovia, Reykjavik", + "Greenwich Standard Time", "Greenwich Daylight Time", NULL, 0 }, + { "Sao Tome Standard Time", 0, TRUE, "(UTC+00:00) Sao Tome", "Sao Tome Standard Time", + "Sao Tome Daylight Time", TimeZoneRuleTable_49, 2 }, + { "Morocco Standard Time", 0, TRUE, "(UTC+01:00) Casablanca", "Morocco Standard Time", + "Morocco Daylight Time", TimeZoneRuleTable_50, 22 }, + { "W. Europe Standard Time", -60, TRUE, + "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "W. Europe Standard Time", + "W. Europe Daylight Time", TimeZoneRuleTable_51, 1 }, + { "Central Europe Standard Time", -60, TRUE, + "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", + "Central Europe Standard Time", "Central Europe Daylight Time", TimeZoneRuleTable_52, 1 }, + { "Romance Standard Time", -60, TRUE, "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", + "Romance Standard Time", "Romance Daylight Time", TimeZoneRuleTable_53, 1 }, + { "Central European Standard Time", -60, TRUE, "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", + "Central European Standard Time", "Central European Daylight Time", TimeZoneRuleTable_54, 1 }, + { "W. Central Africa Standard Time", -60, FALSE, "(UTC+01:00) West Central Africa", + "W. Central Africa Standard Time", "W. Central Africa Daylight Time", NULL, 0 }, + { "GTB Standard Time", -120, TRUE, "(UTC+02:00) Athens, Bucharest", "GTB Standard Time", + "GTB Daylight Time", TimeZoneRuleTable_56, 1 }, + { "Middle East Standard Time", -120, TRUE, "(UTC+02:00) Beirut", "Middle East Standard Time", + "Middle East Daylight Time", TimeZoneRuleTable_57, 13 }, + { "Egypt Standard Time", -120, TRUE, "(UTC+02:00) Cairo", "Egypt Standard Time", + "Egypt Daylight Time", TimeZoneRuleTable_58, 8 }, + { "E. Europe Standard Time", -120, TRUE, "(UTC+02:00) Chisinau", "E. Europe Standard Time", + "E. Europe Daylight Time", TimeZoneRuleTable_59, 1 }, + { "Syria Standard Time", -120, TRUE, "(UTC+02:00) Damascus", "Syria Standard Time", + "Syria Daylight Time", TimeZoneRuleTable_60, 21 }, + { "West Bank Standard Time", -120, TRUE, "(UTC+02:00) Gaza, Hebron", + "West Bank Gaza Standard Time", "West Bank Gaza Daylight Time", TimeZoneRuleTable_61, 19 }, + { "South Africa Standard Time", -120, FALSE, "(UTC+02:00) Harare, Pretoria", + "South Africa Standard Time", "South Africa Daylight Time", NULL, 0 }, + { "FLE Standard Time", -120, TRUE, "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", + "FLE Standard Time", "FLE Daylight Time", TimeZoneRuleTable_63, 1 }, + { "Israel Standard Time", -120, TRUE, "(UTC+02:00) Jerusalem", "Jerusalem Standard Time", + "Jerusalem Daylight Time", TimeZoneRuleTable_64, 21 }, + { "South Sudan Standard Time", -120, TRUE, "(UTC+02:00) Juba", "South Sudan Standard Time", + "South Sudan Daylight Time", TimeZoneRuleTable_65, 2 }, + { "Kaliningrad Standard Time", -120, TRUE, "(UTC+02:00) Kaliningrad", + "Russia TZ 1 Standard Time", "Russia TZ 1 Daylight Time", TimeZoneRuleTable_66, 5 }, + { "Sudan Standard Time", -120, TRUE, "(UTC+02:00) Khartoum", "Sudan Standard Time", + "Sudan Daylight Time", TimeZoneRuleTable_67, 2 }, + { "Libya Standard Time", -120, TRUE, "(UTC+02:00) Tripoli", "Libya Standard Time", + "Libya Daylight Time", TimeZoneRuleTable_68, 2 }, + { "Namibia Standard Time", -120, TRUE, "(UTC+02:00) Windhoek", "Namibia Standard Time", + "Namibia Daylight Time", TimeZoneRuleTable_69, 1 }, + { "Jordan Standard Time", -180, TRUE, "(UTC+03:00) Amman", "Jordan Standard Time", + "Jordan Daylight Time", TimeZoneRuleTable_70, 12 }, + { "Arabic Standard Time", -180, TRUE, "(UTC+03:00) Baghdad", "Arabic Standard Time", + "Arabic Daylight Time", TimeZoneRuleTable_71, 4 }, + { "Turkey Standard Time", -180, TRUE, "(UTC+03:00) Istanbul", "Turkey Standard Time", + "Turkey Daylight Time", TimeZoneRuleTable_72, 7 }, + { "Arab Standard Time", -180, FALSE, "(UTC+03:00) Kuwait, Riyadh", "Arab Standard Time", + "Arab Daylight Time", NULL, 0 }, + { "Belarus Standard Time", -180, TRUE, "(UTC+03:00) Minsk", "Belarus Standard Time", + "Belarus Daylight Time", TimeZoneRuleTable_74, 2 }, + { "Russian Standard Time", -180, TRUE, "(UTC+03:00) Moscow, St. Petersburg", + "Russia TZ 2 Standard Time", "Russia TZ 2 Daylight Time", TimeZoneRuleTable_75, 5 }, + { "E. Africa Standard Time", -180, FALSE, "(UTC+03:00) Nairobi", "E. Africa Standard Time", + "E. Africa Daylight Time", NULL, 0 }, + { "Volgograd Standard Time", -180, TRUE, "(UTC+03:00) Volgograd", "Volgograd Standard Time", + "Volgograd Daylight Time", TimeZoneRuleTable_77, 8 }, + { "Iran Standard Time", -210, TRUE, "(UTC+03:30) Tehran", "Iran Standard Time", + "Iran Daylight Time", TimeZoneRuleTable_78, 17 }, + { "Arabian Standard Time", -240, FALSE, "(UTC+04:00) Abu Dhabi, Muscat", + "Arabian Standard Time", "Arabian Daylight Time", NULL, 0 }, + { "Astrakhan Standard Time", -240, TRUE, "(UTC+04:00) Astrakhan, Ulyanovsk", + "Astrakhan Standard Time", "Astrakhan Daylight Time", TimeZoneRuleTable_80, 5 }, + { "Azerbaijan Standard Time", -240, TRUE, "(UTC+04:00) Baku", "Azerbaijan Standard Time", + "Azerbaijan Daylight Time", TimeZoneRuleTable_81, 1 }, + { "Russia Time Zone 3", -240, TRUE, "(UTC+04:00) Izhevsk, Samara", "Russia TZ 3 Standard Time", + "Russia TZ 3 Daylight Time", TimeZoneRuleTable_82, 3 }, + { "Mauritius Standard Time", -240, TRUE, "(UTC+04:00) Port Louis", "Mauritius Standard Time", + "Mauritius Daylight Time", TimeZoneRuleTable_83, 2 }, + { "Saratov Standard Time", -240, TRUE, "(UTC+04:00) Saratov", "Saratov Standard Time", + "Saratov Daylight Time", TimeZoneRuleTable_84, 5 }, + { "Georgian Standard Time", -240, FALSE, "(UTC+04:00) Tbilisi", "Georgian Standard Time", + "Georgian Daylight Time", NULL, 0 }, + { "Caucasus Standard Time", -240, TRUE, "(UTC+04:00) Yerevan", "Caucasus Standard Time", + "Caucasus Daylight Time", TimeZoneRuleTable_86, 1 }, + { "Afghanistan Standard Time", -270, FALSE, "(UTC+04:30) Kabul", "Afghanistan Standard Time", + "Afghanistan Daylight Time", NULL, 0 }, + { "West Asia Standard Time", -300, FALSE, "(UTC+05:00) Ashgabat, Tashkent", + "West Asia Standard Time", "West Asia Daylight Time", NULL, 0 }, + { "Ekaterinburg Standard Time", -300, TRUE, "(UTC+05:00) Ekaterinburg", + "Russia TZ 4 Standard Time", "Russia TZ 4 Daylight Time", TimeZoneRuleTable_89, 5 }, + { "Pakistan Standard Time", -300, TRUE, "(UTC+05:00) Islamabad, Karachi", + "Pakistan Standard Time", "Pakistan Daylight Time", TimeZoneRuleTable_90, 2 }, + { "Qyzylorda Standard Time", -300, TRUE, "(UTC+05:00) Qyzylorda", "Qyzylorda Standard Time", + "Qyzylorda Daylight Time", TimeZoneRuleTable_91, 2 }, + { "India Standard Time", -330, FALSE, "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", + "India Standard Time", "India Daylight Time", NULL, 0 }, + { "Sri Lanka Standard Time", -330, FALSE, "(UTC+05:30) Sri Jayawardenepura", + "Sri Lanka Standard Time", "Sri Lanka Daylight Time", NULL, 0 }, + { "Nepal Standard Time", -345, FALSE, "(UTC+05:45) Kathmandu", "Nepal Standard Time", + "Nepal Daylight Time", NULL, 0 }, + { "Central Asia Standard Time", -360, FALSE, "(UTC+06:00) Astana", "Central Asia Standard Time", + "Central Asia Daylight Time", NULL, 0 }, + { "Bangladesh Standard Time", -360, TRUE, "(UTC+06:00) Dhaka", "Bangladesh Standard Time", + "Bangladesh Daylight Time", TimeZoneRuleTable_96, 1 }, + { "Omsk Standard Time", -360, TRUE, "(UTC+06:00) Omsk", "Omsk Standard Time", + "Omsk Daylight Time", TimeZoneRuleTable_97, 5 }, + { "Myanmar Standard Time", -390, FALSE, "(UTC+06:30) Yangon (Rangoon)", "Myanmar Standard Time", + "Myanmar Daylight Time", NULL, 0 }, + { "SE Asia Standard Time", -420, FALSE, "(UTC+07:00) Bangkok, Hanoi, Jakarta", + "SE Asia Standard Time", "SE Asia Daylight Time", NULL, 0 }, + { "Altai Standard Time", -420, TRUE, "(UTC+07:00) Barnaul, Gorno-Altaysk", + "Altai Standard Time", "Altai Daylight Time", TimeZoneRuleTable_100, 5 }, + { "W. Mongolia Standard Time", -420, TRUE, "(UTC+07:00) Hovd", "W. Mongolia Standard Time", + "W. Mongolia Daylight Time", TimeZoneRuleTable_101, 3 }, + { "North Asia Standard Time", -420, TRUE, "(UTC+07:00) Krasnoyarsk", + "Russia TZ 6 Standard Time", "Russia TZ 6 Daylight Time", TimeZoneRuleTable_102, 5 }, + { "N. Central Asia Standard Time", -420, TRUE, "(UTC+07:00) Novosibirsk", + "Novosibirsk Standard Time", "Novosibirsk Daylight Time", TimeZoneRuleTable_103, 5 }, + { "Tomsk Standard Time", -420, TRUE, "(UTC+07:00) Tomsk", "Tomsk Standard Time", + "Tomsk Daylight Time", TimeZoneRuleTable_104, 5 }, + { "China Standard Time", -480, FALSE, "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", + "China Standard Time", "China Daylight Time", NULL, 0 }, + { "North Asia East Standard Time", -480, TRUE, "(UTC+08:00) Irkutsk", + "Russia TZ 7 Standard Time", "Russia TZ 7 Daylight Time", TimeZoneRuleTable_106, 5 }, + { "Singapore Standard Time", -480, FALSE, "(UTC+08:00) Kuala Lumpur, Singapore", + "Malay Peninsula Standard Time", "Malay Peninsula Daylight Time", NULL, 0 }, + { "W. Australia Standard Time", -480, TRUE, "(UTC+08:00) Perth", "W. Australia Standard Time", + "W. Australia Daylight Time", TimeZoneRuleTable_108, 4 }, + { "Taipei Standard Time", -480, FALSE, "(UTC+08:00) Taipei", "Taipei Standard Time", + "Taipei Daylight Time", NULL, 0 }, + { "Ulaanbaatar Standard Time", -480, TRUE, "(UTC+08:00) Ulaanbaatar", + "Ulaanbaatar Standard Time", "Ulaanbaatar Daylight Time", TimeZoneRuleTable_110, 2 }, + { "Aus Central W. Standard Time", -525, FALSE, "(UTC+08:45) Eucla", + "Aus Central W. Standard Time", "Aus Central W. Daylight Time", NULL, 0 }, + { "Transbaikal Standard Time", -540, TRUE, "(UTC+09:00) Chita", "Transbaikal Standard Time", + "Transbaikal Daylight Time", TimeZoneRuleTable_112, 7 }, + { "Tokyo Standard Time", -540, FALSE, "(UTC+09:00) Osaka, Sapporo, Tokyo", + "Tokyo Standard Time", "Tokyo Daylight Time", NULL, 0 }, + { "North Korea Standard Time", -540, TRUE, "(UTC+09:00) Pyongyang", "North Korea Standard Time", + "North Korea Daylight Time", TimeZoneRuleTable_114, 4 }, + { "Korea Standard Time", -540, FALSE, "(UTC+09:00) Seoul", "Korea Standard Time", + "Korea Daylight Time", NULL, 0 }, + { "Yakutsk Standard Time", -540, TRUE, "(UTC+09:00) Yakutsk", "Russia TZ 8 Standard Time", + "Russia TZ 8 Daylight Time", TimeZoneRuleTable_116, 5 }, + { "Cen. Australia Standard Time", -570, TRUE, "(UTC+09:30) Adelaide", + "Cen. Australia Standard Time", "Cen. Australia Daylight Time", TimeZoneRuleTable_117, 2 }, + { "AUS Central Standard Time", -570, FALSE, "(UTC+09:30) Darwin", "AUS Central Standard Time", + "AUS Central Daylight Time", NULL, 0 }, + { "E. Australia Standard Time", -600, FALSE, "(UTC+10:00) Brisbane", + "E. Australia Standard Time", "E. Australia Daylight Time", NULL, 0 }, + { "AUS Eastern Standard Time", -600, TRUE, "(UTC+10:00) Canberra, Melbourne, Sydney", + "AUS Eastern Standard Time", "AUS Eastern Daylight Time", TimeZoneRuleTable_120, 2 }, + { "West Pacific Standard Time", -600, FALSE, "(UTC+10:00) Guam, Port Moresby", + "West Pacific Standard Time", "West Pacific Daylight Time", NULL, 0 }, + { "Tasmania Standard Time", -600, TRUE, "(UTC+10:00) Hobart", "Tasmania Standard Time", + "Tasmania Daylight Time", TimeZoneRuleTable_122, 2 }, + { "Vladivostok Standard Time", -600, TRUE, "(UTC+10:00) Vladivostok", + "Russia TZ 9 Standard Time", "Russia TZ 9 Daylight Time", TimeZoneRuleTable_123, 5 }, + { "Lord Howe Standard Time", -630, TRUE, "(UTC+10:30) Lord Howe Island", + "Lord Howe Standard Time", "Lord Howe Daylight Time", TimeZoneRuleTable_124, 4 }, + { "Bougainville Standard Time", -660, TRUE, "(UTC+11:00) Bougainville Island", + "Bougainville Standard Time", "Bougainville Daylight Time", TimeZoneRuleTable_125, 2 }, + { "Russia Time Zone 10", -660, TRUE, "(UTC+11:00) Chokurdakh", "Russia TZ 10 Standard Time", + "Russia TZ 10 Daylight Time", TimeZoneRuleTable_126, 5 }, + { "Magadan Standard Time", -660, TRUE, "(UTC+11:00) Magadan", "Magadan Standard Time", + "Magadan Daylight Time", TimeZoneRuleTable_127, 7 }, + { "Norfolk Standard Time", -660, TRUE, "(UTC+11:00) Norfolk Island", "Norfolk Standard Time", + "Norfolk Daylight Time", TimeZoneRuleTable_128, 4 }, + { "Sakhalin Standard Time", -660, TRUE, "(UTC+11:00) Sakhalin", "Sakhalin Standard Time", + "Sakhalin Daylight Time", TimeZoneRuleTable_129, 5 }, + { "Central Pacific Standard Time", -660, FALSE, "(UTC+11:00) Solomon Is., New Caledonia", + "Central Pacific Standard Time", "Central Pacific Daylight Time", NULL, 0 }, + { "Russia Time Zone 11", -720, TRUE, "(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky", + "Russia TZ 11 Standard Time", "Russia TZ 11 Daylight Time", TimeZoneRuleTable_131, 3 }, + { "New Zealand Standard Time", -720, TRUE, "(UTC+12:00) Auckland, Wellington", + "New Zealand Standard Time", "New Zealand Daylight Time", TimeZoneRuleTable_132, 3 }, + { "UTC+12", -720, FALSE, "(UTC+12:00) Coordinated Universal Time+12", "UTC+12", "UTC+12", NULL, + 0 }, + { "Fiji Standard Time", -720, TRUE, "(UTC+12:00) Fiji", "Fiji Standard Time", + "Fiji Daylight Time", TimeZoneRuleTable_134, 13 }, + { "Kamchatka Standard Time", -720, TRUE, "(UTC+12:00) Petropavlovsk-Kamchatsky - Old", + "Kamchatka Standard Time", "Kamchatka Daylight Time", TimeZoneRuleTable_135, 1 }, + { "Chatham Islands Standard Time", -765, TRUE, "(UTC+12:45) Chatham Islands", + "Chatham Islands Standard Time", "Chatham Islands Daylight Time", TimeZoneRuleTable_136, 3 }, + { "UTC+13", -780, FALSE, "(UTC+13:00) Coordinated Universal Time+13", "UTC+13", "UTC+13", NULL, + 0 }, + { "Tonga Standard Time", -780, TRUE, "(UTC+13:00) Nuku'alofa", "Tonga Standard Time", + "Tonga Daylight Time", TimeZoneRuleTable_138, 2 }, + { "Samoa Standard Time", -780, TRUE, "(UTC+13:00) Samoa", "Samoa Standard Time", + "Samoa Daylight Time", TimeZoneRuleTable_139, 4 }, + { "Line Islands Standard Time", -840, FALSE, "(UTC+14:00) Kiritimati Island", + "Line Islands Standard Time", "Line Islands Daylight Time", NULL, 0 } +}; + +const size_t TimeZoneTableNrElements = ARRAYSIZE(TimeZoneTable); diff --git a/winpr/libwinpr/timezone/TimeZones.h b/winpr/libwinpr/timezone/TimeZones.h new file mode 100644 index 0000000..54c9f2d --- /dev/null +++ b/winpr/libwinpr/timezone/TimeZones.h @@ -0,0 +1,34 @@ +/* + * Automatically generated with scripts/TimeZones.csx + */ + +#ifndef WINPR_TIME_ZONES_H_ +#define WINPR_TIME_ZONES_H_ + +#include + +typedef struct +{ + UINT64 TicksStart; + UINT64 TicksEnd; + INT32 DaylightDelta; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; +} TIME_ZONE_RULE_ENTRY; + +typedef struct +{ + const char* Id; + INT32 Bias; + BOOL SupportsDST; + const char* DisplayName; + const char* StandardName; + const char* DaylightName; + const TIME_ZONE_RULE_ENTRY* RuleTable; + UINT32 RuleTableCount; +} TIME_ZONE_ENTRY; + +extern const TIME_ZONE_ENTRY TimeZoneTable[]; +extern const size_t TimeZoneTableNrElements; + +#endif /* WINPR_TIME_ZONES_H_ */ diff --git a/winpr/libwinpr/timezone/WindowsZones.c b/winpr/libwinpr/timezone/WindowsZones.c new file mode 100644 index 0000000..d97ec3c --- /dev/null +++ b/winpr/libwinpr/timezone/WindowsZones.c @@ -0,0 +1,530 @@ +/* + * Automatically generated with scripts/update-windows-zones.py + */ + +#include "WindowsZones.h" + +const WINDOWS_TZID_ENTRY WindowsTimeZoneIdTable[] = { + { "AUS Central Standard Time", "Australia/Darwin" }, + { "AUS Central Standard Time", "Australia/Darwin" }, + { "AUS Eastern Standard Time", "Australia/Sydney Australia/Melbourne" }, + { "AUS Eastern Standard Time", "Australia/Sydney" }, + { "Afghanistan Standard Time", "Asia/Kabul" }, + { "Afghanistan Standard Time", "Asia/Kabul" }, + { "Alaskan Standard Time", "America/Anchorage America/Juneau America/Metlakatla America/Nome " + "America/Sitka America/Yakutat" }, + { "Alaskan Standard Time", "America/Anchorage" }, + { "Aleutian Standard Time", "America/Adak" }, + { "Aleutian Standard Time", "America/Adak" }, + { "Altai Standard Time", "Asia/Barnaul" }, + { "Altai Standard Time", "Asia/Barnaul" }, + { "Arab Standard Time", "Asia/Aden" }, + { "Arab Standard Time", "Asia/Bahrain" }, + { "Arab Standard Time", "Asia/Kuwait" }, + { "Arab Standard Time", "Asia/Qatar" }, + { "Arab Standard Time", "Asia/Riyadh" }, + { "Arab Standard Time", "Asia/Riyadh" }, + { "Arabian Standard Time", "Asia/Dubai" }, + { "Arabian Standard Time", "Asia/Dubai" }, + { "Arabian Standard Time", "Asia/Muscat" }, + { "Arabian Standard Time", "Etc/GMT-4" }, + { "Arabic Standard Time", "Asia/Baghdad" }, + { "Arabic Standard Time", "Asia/Baghdad" }, + { "Argentina Standard Time", + "America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos " + "America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis " + "America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba " + "America/Jujuy America/Mendoza" }, + { "Argentina Standard Time", "America/Buenos_Aires" }, + { "Astrakhan Standard Time", "Europe/Astrakhan Europe/Ulyanovsk" }, + { "Astrakhan Standard Time", "Europe/Astrakhan" }, + { "Atlantic Standard Time", + "America/Halifax America/Glace_Bay America/Goose_Bay America/Moncton" }, + { "Atlantic Standard Time", "America/Halifax" }, + { "Atlantic Standard Time", "America/Thule" }, + { "Atlantic Standard Time", "Atlantic/Bermuda" }, + { "Aus Central W. Standard Time", "Australia/Eucla" }, + { "Aus Central W. Standard Time", "Australia/Eucla" }, + { "Azerbaijan Standard Time", "Asia/Baku" }, + { "Azerbaijan Standard Time", "Asia/Baku" }, + { "Azores Standard Time", "America/Scoresbysund" }, + { "Azores Standard Time", "Atlantic/Azores" }, + { "Azores Standard Time", "Atlantic/Azores" }, + { "Bahia Standard Time", "America/Bahia" }, + { "Bahia Standard Time", "America/Bahia" }, + { "Bangladesh Standard Time", "Asia/Dhaka" }, + { "Bangladesh Standard Time", "Asia/Dhaka" }, + { "Bangladesh Standard Time", "Asia/Thimphu" }, + { "Belarus Standard Time", "Europe/Minsk" }, + { "Belarus Standard Time", "Europe/Minsk" }, + { "Bougainville Standard Time", "Pacific/Bougainville" }, + { "Bougainville Standard Time", "Pacific/Bougainville" }, + { "Canada Central Standard Time", "America/Regina America/Swift_Current" }, + { "Canada Central Standard Time", "America/Regina" }, + { "Cape Verde Standard Time", "Atlantic/Cape_Verde" }, + { "Cape Verde Standard Time", "Atlantic/Cape_Verde" }, + { "Cape Verde Standard Time", "Etc/GMT+1" }, + { "Caucasus Standard Time", "Asia/Yerevan" }, + { "Caucasus Standard Time", "Asia/Yerevan" }, + { "Cen. Australia Standard Time", "Australia/Adelaide Australia/Broken_Hill" }, + { "Cen. Australia Standard Time", "Australia/Adelaide" }, + { "Central America Standard Time", "America/Belize" }, + { "Central America Standard Time", "America/Costa_Rica" }, + { "Central America Standard Time", "America/El_Salvador" }, + { "Central America Standard Time", "America/Guatemala" }, + { "Central America Standard Time", "America/Guatemala" }, + { "Central America Standard Time", "America/Managua" }, + { "Central America Standard Time", "America/Tegucigalpa" }, + { "Central America Standard Time", "Etc/GMT+6" }, + { "Central America Standard Time", "Pacific/Galapagos" }, + { "Central Asia Standard Time", "Antarctica/Vostok" }, + { "Central Asia Standard Time", "Asia/Almaty Asia/Qostanay" }, + { "Central Asia Standard Time", "Asia/Almaty" }, + { "Central Asia Standard Time", "Asia/Bishkek" }, + { "Central Asia Standard Time", "Asia/Urumqi" }, + { "Central Asia Standard Time", "Etc/GMT-6" }, + { "Central Asia Standard Time", "Indian/Chagos" }, + { "Central Brazilian Standard Time", "America/Cuiaba America/Campo_Grande" }, + { "Central Brazilian Standard Time", "America/Cuiaba" }, + { "Central Europe Standard Time", "Europe/Belgrade" }, + { "Central Europe Standard Time", "Europe/Bratislava" }, + { "Central Europe Standard Time", "Europe/Budapest" }, + { "Central Europe Standard Time", "Europe/Budapest" }, + { "Central Europe Standard Time", "Europe/Ljubljana" }, + { "Central Europe Standard Time", "Europe/Podgorica" }, + { "Central Europe Standard Time", "Europe/Prague" }, + { "Central Europe Standard Time", "Europe/Tirane" }, + { "Central European Standard Time", "Europe/Sarajevo" }, + { "Central European Standard Time", "Europe/Skopje" }, + { "Central European Standard Time", "Europe/Warsaw" }, + { "Central European Standard Time", "Europe/Warsaw" }, + { "Central European Standard Time", "Europe/Zagreb" }, + { "Central Pacific Standard Time", "Antarctica/Macquarie" }, + { "Central Pacific Standard Time", "Etc/GMT-11" }, + { "Central Pacific Standard Time", "Pacific/Efate" }, + { "Central Pacific Standard Time", "Pacific/Guadalcanal" }, + { "Central Pacific Standard Time", "Pacific/Guadalcanal" }, + { "Central Pacific Standard Time", "Pacific/Noumea" }, + { "Central Pacific Standard Time", "Pacific/Ponape Pacific/Kosrae" }, + { "Central Standard Time (Mexico)", + "America/Mexico_City America/Bahia_Banderas America/Merida America/Monterrey" }, + { "Central Standard Time (Mexico)", "America/Mexico_City" }, + { "Central Standard Time", + "America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee " + "America/North_Dakota/Beulah America/North_Dakota/Center America/North_Dakota/New_Salem" }, + { "Central Standard Time", "America/Chicago" }, + { "Central Standard Time", "America/Matamoros" }, + { "Central Standard Time", + "America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute" }, + { "Central Standard Time", "CST6CDT" }, + { "Chatham Islands Standard Time", "Pacific/Chatham" }, + { "Chatham Islands Standard Time", "Pacific/Chatham" }, + { "China Standard Time", "Asia/Hong_Kong" }, + { "China Standard Time", "Asia/Macau" }, + { "China Standard Time", "Asia/Shanghai" }, + { "China Standard Time", "Asia/Shanghai" }, + { "Cuba Standard Time", "America/Havana" }, + { "Cuba Standard Time", "America/Havana" }, + { "Dateline Standard Time", "Etc/GMT+12" }, + { "Dateline Standard Time", "Etc/GMT+12" }, + { "E. Africa Standard Time", "Africa/Addis_Ababa" }, + { "E. Africa Standard Time", "Africa/Asmera" }, + { "E. Africa Standard Time", "Africa/Dar_es_Salaam" }, + { "E. Africa Standard Time", "Africa/Djibouti" }, + { "E. Africa Standard Time", "Africa/Juba" }, + { "E. Africa Standard Time", "Africa/Kampala" }, + { "E. Africa Standard Time", "Africa/Mogadishu" }, + { "E. Africa Standard Time", "Africa/Nairobi" }, + { "E. Africa Standard Time", "Africa/Nairobi" }, + { "E. Africa Standard Time", "Antarctica/Syowa" }, + { "E. Africa Standard Time", "Etc/GMT-3" }, + { "E. Africa Standard Time", "Indian/Antananarivo" }, + { "E. Africa Standard Time", "Indian/Comoro" }, + { "E. Africa Standard Time", "Indian/Mayotte" }, + { "E. Australia Standard Time", "Australia/Brisbane Australia/Lindeman" }, + { "E. Australia Standard Time", "Australia/Brisbane" }, + { "E. Europe Standard Time", "Europe/Chisinau" }, + { "E. Europe Standard Time", "Europe/Chisinau" }, + { "E. South America Standard Time", "America/Sao_Paulo" }, + { "E. South America Standard Time", "America/Sao_Paulo" }, + { "Easter Island Standard Time", "Pacific/Easter" }, + { "Easter Island Standard Time", "Pacific/Easter" }, + { "Eastern Standard Time (Mexico)", "America/Cancun" }, + { "Eastern Standard Time (Mexico)", "America/Cancun" }, + { "Eastern Standard Time", "America/Nassau" }, + { "Eastern Standard Time", + "America/New_York America/Detroit America/Indiana/Petersburg America/Indiana/Vincennes " + "America/Indiana/Winamac America/Kentucky/Monticello America/Louisville" }, + { "Eastern Standard Time", "America/New_York" }, + { "Eastern Standard Time", "America/Toronto America/Iqaluit America/Montreal America/Nipigon " + "America/Pangnirtung America/Thunder_Bay" }, + { "Eastern Standard Time", "EST5EDT" }, + { "Egypt Standard Time", "Africa/Cairo" }, + { "Egypt Standard Time", "Africa/Cairo" }, + { "Ekaterinburg Standard Time", "Asia/Yekaterinburg" }, + { "Ekaterinburg Standard Time", "Asia/Yekaterinburg" }, + { "FLE Standard Time", "Europe/Helsinki" }, + { "FLE Standard Time", "Europe/Kiev Europe/Uzhgorod Europe/Zaporozhye" }, + { "FLE Standard Time", "Europe/Kiev" }, + { "FLE Standard Time", "Europe/Mariehamn" }, + { "FLE Standard Time", "Europe/Riga" }, + { "FLE Standard Time", "Europe/Sofia" }, + { "FLE Standard Time", "Europe/Tallinn" }, + { "FLE Standard Time", "Europe/Vilnius" }, + { "Fiji Standard Time", "Pacific/Fiji" }, + { "Fiji Standard Time", "Pacific/Fiji" }, + { "GMT Standard Time", "Atlantic/Canary" }, + { "GMT Standard Time", "Atlantic/Faeroe" }, + { "GMT Standard Time", "Europe/Dublin" }, + { "GMT Standard Time", "Europe/Guernsey" }, + { "GMT Standard Time", "Europe/Isle_of_Man" }, + { "GMT Standard Time", "Europe/Jersey" }, + { "GMT Standard Time", "Europe/Lisbon Atlantic/Madeira" }, + { "GMT Standard Time", "Europe/London" }, + { "GMT Standard Time", "Europe/London" }, + { "GTB Standard Time", "Asia/Nicosia Asia/Famagusta" }, + { "GTB Standard Time", "Europe/Athens" }, + { "GTB Standard Time", "Europe/Bucharest" }, + { "GTB Standard Time", "Europe/Bucharest" }, + { "Georgian Standard Time", "Asia/Tbilisi" }, + { "Georgian Standard Time", "Asia/Tbilisi" }, + { "Greenland Standard Time", "America/Godthab" }, + { "Greenland Standard Time", "America/Godthab" }, + { "Greenwich Standard Time", "Africa/Abidjan" }, + { "Greenwich Standard Time", "Africa/Accra" }, + { "Greenwich Standard Time", "Africa/Bamako" }, + { "Greenwich Standard Time", "Africa/Banjul" }, + { "Greenwich Standard Time", "Africa/Bissau" }, + { "Greenwich Standard Time", "Africa/Conakry" }, + { "Greenwich Standard Time", "Africa/Dakar" }, + { "Greenwich Standard Time", "Africa/Freetown" }, + { "Greenwich Standard Time", "Africa/Lome" }, + { "Greenwich Standard Time", "Africa/Monrovia" }, + { "Greenwich Standard Time", "Africa/Nouakchott" }, + { "Greenwich Standard Time", "Africa/Ouagadougou" }, + { "Greenwich Standard Time", "Atlantic/Reykjavik" }, + { "Greenwich Standard Time", "Atlantic/Reykjavik" }, + { "Greenwich Standard Time", "Atlantic/St_Helena" }, + { "Haiti Standard Time", "America/Port-au-Prince" }, + { "Haiti Standard Time", "America/Port-au-Prince" }, + { "Hawaiian Standard Time", "Etc/GMT+10" }, + { "Hawaiian Standard Time", "Pacific/Honolulu" }, + { "Hawaiian Standard Time", "Pacific/Honolulu" }, + { "Hawaiian Standard Time", "Pacific/Johnston" }, + { "Hawaiian Standard Time", "Pacific/Rarotonga" }, + { "Hawaiian Standard Time", "Pacific/Tahiti" }, + { "India Standard Time", "Asia/Calcutta" }, + { "India Standard Time", "Asia/Calcutta" }, + { "Iran Standard Time", "Asia/Tehran" }, + { "Iran Standard Time", "Asia/Tehran" }, + { "Israel Standard Time", "Asia/Jerusalem" }, + { "Israel Standard Time", "Asia/Jerusalem" }, + { "Jordan Standard Time", "Asia/Amman" }, + { "Jordan Standard Time", "Asia/Amman" }, + { "Kaliningrad Standard Time", "Europe/Kaliningrad" }, + { "Kaliningrad Standard Time", "Europe/Kaliningrad" }, + { "Korea Standard Time", "Asia/Seoul" }, + { "Korea Standard Time", "Asia/Seoul" }, + { "Libya Standard Time", "Africa/Tripoli" }, + { "Libya Standard Time", "Africa/Tripoli" }, + { "Line Islands Standard Time", "Etc/GMT-14" }, + { "Line Islands Standard Time", "Pacific/Kiritimati" }, + { "Line Islands Standard Time", "Pacific/Kiritimati" }, + { "Lord Howe Standard Time", "Australia/Lord_Howe" }, + { "Lord Howe Standard Time", "Australia/Lord_Howe" }, + { "Magadan Standard Time", "Asia/Magadan" }, + { "Magadan Standard Time", "Asia/Magadan" }, + { "Magallanes Standard Time", "America/Punta_Arenas" }, + { "Magallanes Standard Time", "America/Punta_Arenas" }, + { "Marquesas Standard Time", "Pacific/Marquesas" }, + { "Marquesas Standard Time", "Pacific/Marquesas" }, + { "Mauritius Standard Time", "Indian/Mahe" }, + { "Mauritius Standard Time", "Indian/Mauritius" }, + { "Mauritius Standard Time", "Indian/Mauritius" }, + { "Mauritius Standard Time", "Indian/Reunion" }, + { "Middle East Standard Time", "Asia/Beirut" }, + { "Middle East Standard Time", "Asia/Beirut" }, + { "Montevideo Standard Time", "America/Montevideo" }, + { "Montevideo Standard Time", "America/Montevideo" }, + { "Morocco Standard Time", "Africa/Casablanca" }, + { "Morocco Standard Time", "Africa/Casablanca" }, + { "Morocco Standard Time", "Africa/El_Aaiun" }, + { "Mountain Standard Time (Mexico)", "America/Chihuahua America/Mazatlan" }, + { "Mountain Standard Time (Mexico)", "America/Chihuahua" }, + { "Mountain Standard Time", "America/Denver America/Boise" }, + { "Mountain Standard Time", "America/Denver" }, + { "Mountain Standard Time", + "America/Edmonton America/Cambridge_Bay America/Inuvik America/Yellowknife" }, + { "Mountain Standard Time", "America/Ojinaga" }, + { "Mountain Standard Time", "MST7MDT" }, + { "Myanmar Standard Time", "Asia/Rangoon" }, + { "Myanmar Standard Time", "Asia/Rangoon" }, + { "Myanmar Standard Time", "Indian/Cocos" }, + { "N. Central Asia Standard Time", "Asia/Novosibirsk" }, + { "N. Central Asia Standard Time", "Asia/Novosibirsk" }, + { "Namibia Standard Time", "Africa/Windhoek" }, + { "Namibia Standard Time", "Africa/Windhoek" }, + { "Nepal Standard Time", "Asia/Katmandu" }, + { "Nepal Standard Time", "Asia/Katmandu" }, + { "New Zealand Standard Time", "Antarctica/McMurdo" }, + { "New Zealand Standard Time", "Pacific/Auckland" }, + { "New Zealand Standard Time", "Pacific/Auckland" }, + { "Newfoundland Standard Time", "America/St_Johns" }, + { "Newfoundland Standard Time", "America/St_Johns" }, + { "Norfolk Standard Time", "Pacific/Norfolk" }, + { "Norfolk Standard Time", "Pacific/Norfolk" }, + { "North Asia East Standard Time", "Asia/Irkutsk" }, + { "North Asia East Standard Time", "Asia/Irkutsk" }, + { "North Asia Standard Time", "Asia/Krasnoyarsk Asia/Novokuznetsk" }, + { "North Asia Standard Time", "Asia/Krasnoyarsk" }, + { "North Korea Standard Time", "Asia/Pyongyang" }, + { "North Korea Standard Time", "Asia/Pyongyang" }, + { "Omsk Standard Time", "Asia/Omsk" }, + { "Omsk Standard Time", "Asia/Omsk" }, + { "Pacific SA Standard Time", "America/Santiago" }, + { "Pacific SA Standard Time", "America/Santiago" }, + { "Pacific Standard Time (Mexico)", "America/Tijuana America/Santa_Isabel" }, + { "Pacific Standard Time (Mexico)", "America/Tijuana" }, + { "Pacific Standard Time", "America/Los_Angeles" }, + { "Pacific Standard Time", "America/Los_Angeles" }, + { "Pacific Standard Time", "America/Vancouver America/Dawson America/Whitehorse" }, + { "Pacific Standard Time", "PST8PDT" }, + { "Pakistan Standard Time", "Asia/Karachi" }, + { "Pakistan Standard Time", "Asia/Karachi" }, + { "Paraguay Standard Time", "America/Asuncion" }, + { "Paraguay Standard Time", "America/Asuncion" }, + { "Qyzylorda Standard Time", "Asia/Qyzylorda" }, + { "Qyzylorda Standard Time", "Asia/Qyzylorda" }, + { "Romance Standard Time", "Europe/Brussels" }, + { "Romance Standard Time", "Europe/Copenhagen" }, + { "Romance Standard Time", "Europe/Madrid Africa/Ceuta" }, + { "Romance Standard Time", "Europe/Paris" }, + { "Romance Standard Time", "Europe/Paris" }, + { "Russia Time Zone 10", "Asia/Srednekolymsk" }, + { "Russia Time Zone 10", "Asia/Srednekolymsk" }, + { "Russia Time Zone 11", "Asia/Kamchatka Asia/Anadyr" }, + { "Russia Time Zone 11", "Asia/Kamchatka" }, + { "Russia Time Zone 3", "Europe/Samara" }, + { "Russia Time Zone 3", "Europe/Samara" }, + { "Russian Standard Time", "Europe/Moscow Europe/Kirov" }, + { "Russian Standard Time", "Europe/Moscow" }, + { "Russian Standard Time", "Europe/Simferopol" }, + { "SA Eastern Standard Time", "America/Cayenne" }, + { "SA Eastern Standard Time", "America/Cayenne" }, + { "SA Eastern Standard Time", + "America/Fortaleza America/Belem America/Maceio America/Recife America/Santarem" }, + { "SA Eastern Standard Time", "America/Paramaribo" }, + { "SA Eastern Standard Time", "Antarctica/Rothera Antarctica/Palmer" }, + { "SA Eastern Standard Time", "Atlantic/Stanley" }, + { "SA Eastern Standard Time", "Etc/GMT+3" }, + { "SA Pacific Standard Time", "America/Bogota" }, + { "SA Pacific Standard Time", "America/Bogota" }, + { "SA Pacific Standard Time", "America/Cayman" }, + { "SA Pacific Standard Time", "America/Coral_Harbour" }, + { "SA Pacific Standard Time", "America/Guayaquil" }, + { "SA Pacific Standard Time", "America/Jamaica" }, + { "SA Pacific Standard Time", "America/Lima" }, + { "SA Pacific Standard Time", "America/Panama" }, + { "SA Pacific Standard Time", "America/Rio_Branco America/Eirunepe" }, + { "SA Pacific Standard Time", "Etc/GMT+5" }, + { "SA Western Standard Time", "America/Anguilla" }, + { "SA Western Standard Time", "America/Antigua" }, + { "SA Western Standard Time", "America/Aruba" }, + { "SA Western Standard Time", "America/Barbados" }, + { "SA Western Standard Time", "America/Blanc-Sablon" }, + { "SA Western Standard Time", "America/Curacao" }, + { "SA Western Standard Time", "America/Dominica" }, + { "SA Western Standard Time", "America/Grenada" }, + { "SA Western Standard Time", "America/Guadeloupe" }, + { "SA Western Standard Time", "America/Guyana" }, + { "SA Western Standard Time", "America/Kralendijk" }, + { "SA Western Standard Time", "America/La_Paz" }, + { "SA Western Standard Time", "America/La_Paz" }, + { "SA Western Standard Time", "America/Lower_Princes" }, + { "SA Western Standard Time", "America/Manaus America/Boa_Vista America/Porto_Velho" }, + { "SA Western Standard Time", "America/Marigot" }, + { "SA Western Standard Time", "America/Martinique" }, + { "SA Western Standard Time", "America/Montserrat" }, + { "SA Western Standard Time", "America/Port_of_Spain" }, + { "SA Western Standard Time", "America/Puerto_Rico" }, + { "SA Western Standard Time", "America/Santo_Domingo" }, + { "SA Western Standard Time", "America/St_Barthelemy" }, + { "SA Western Standard Time", "America/St_Kitts" }, + { "SA Western Standard Time", "America/St_Lucia" }, + { "SA Western Standard Time", "America/St_Thomas" }, + { "SA Western Standard Time", "America/St_Vincent" }, + { "SA Western Standard Time", "America/Tortola" }, + { "SA Western Standard Time", "Etc/GMT+4" }, + { "SE Asia Standard Time", "Antarctica/Davis" }, + { "SE Asia Standard Time", "Asia/Bangkok" }, + { "SE Asia Standard Time", "Asia/Bangkok" }, + { "SE Asia Standard Time", "Asia/Jakarta Asia/Pontianak" }, + { "SE Asia Standard Time", "Asia/Phnom_Penh" }, + { "SE Asia Standard Time", "Asia/Saigon" }, + { "SE Asia Standard Time", "Asia/Vientiane" }, + { "SE Asia Standard Time", "Etc/GMT-7" }, + { "SE Asia Standard Time", "Indian/Christmas" }, + { "Saint Pierre Standard Time", "America/Miquelon" }, + { "Saint Pierre Standard Time", "America/Miquelon" }, + { "Sakhalin Standard Time", "Asia/Sakhalin" }, + { "Sakhalin Standard Time", "Asia/Sakhalin" }, + { "Samoa Standard Time", "Pacific/Apia" }, + { "Samoa Standard Time", "Pacific/Apia" }, + { "Sao Tome Standard Time", "Africa/Sao_Tome" }, + { "Sao Tome Standard Time", "Africa/Sao_Tome" }, + { "Saratov Standard Time", "Europe/Saratov" }, + { "Saratov Standard Time", "Europe/Saratov" }, + { "Singapore Standard Time", "Antarctica/Casey" }, + { "Singapore Standard Time", "Asia/Brunei" }, + { "Singapore Standard Time", "Asia/Kuala_Lumpur Asia/Kuching" }, + { "Singapore Standard Time", "Asia/Makassar" }, + { "Singapore Standard Time", "Asia/Manila" }, + { "Singapore Standard Time", "Asia/Singapore" }, + { "Singapore Standard Time", "Asia/Singapore" }, + { "Singapore Standard Time", "Etc/GMT-8" }, + { "South Africa Standard Time", "Africa/Blantyre" }, + { "South Africa Standard Time", "Africa/Bujumbura" }, + { "South Africa Standard Time", "Africa/Gaborone" }, + { "South Africa Standard Time", "Africa/Harare" }, + { "South Africa Standard Time", "Africa/Johannesburg" }, + { "South Africa Standard Time", "Africa/Johannesburg" }, + { "South Africa Standard Time", "Africa/Kigali" }, + { "South Africa Standard Time", "Africa/Lubumbashi" }, + { "South Africa Standard Time", "Africa/Lusaka" }, + { "South Africa Standard Time", "Africa/Maputo" }, + { "South Africa Standard Time", "Africa/Maseru" }, + { "South Africa Standard Time", "Africa/Mbabane" }, + { "South Africa Standard Time", "Etc/GMT-2" }, + { "Sri Lanka Standard Time", "Asia/Colombo" }, + { "Sri Lanka Standard Time", "Asia/Colombo" }, + { "Sudan Standard Time", "Africa/Khartoum" }, + { "Sudan Standard Time", "Africa/Khartoum" }, + { "Syria Standard Time", "Asia/Damascus" }, + { "Syria Standard Time", "Asia/Damascus" }, + { "Taipei Standard Time", "Asia/Taipei" }, + { "Taipei Standard Time", "Asia/Taipei" }, + { "Tasmania Standard Time", "Australia/Hobart Australia/Currie" }, + { "Tasmania Standard Time", "Australia/Hobart" }, + { "Tocantins Standard Time", "America/Araguaina" }, + { "Tocantins Standard Time", "America/Araguaina" }, + { "Tokyo Standard Time", "Asia/Dili" }, + { "Tokyo Standard Time", "Asia/Jayapura" }, + { "Tokyo Standard Time", "Asia/Tokyo" }, + { "Tokyo Standard Time", "Asia/Tokyo" }, + { "Tokyo Standard Time", "Etc/GMT-9" }, + { "Tokyo Standard Time", "Pacific/Palau" }, + { "Tomsk Standard Time", "Asia/Tomsk" }, + { "Tomsk Standard Time", "Asia/Tomsk" }, + { "Tonga Standard Time", "Pacific/Tongatapu" }, + { "Tonga Standard Time", "Pacific/Tongatapu" }, + { "Transbaikal Standard Time", "Asia/Chita" }, + { "Transbaikal Standard Time", "Asia/Chita" }, + { "Turkey Standard Time", "Europe/Istanbul" }, + { "Turkey Standard Time", "Europe/Istanbul" }, + { "Turks And Caicos Standard Time", "America/Grand_Turk" }, + { "Turks And Caicos Standard Time", "America/Grand_Turk" }, + { "US Eastern Standard Time", + "America/Indianapolis America/Indiana/Marengo America/Indiana/Vevay" }, + { "US Eastern Standard Time", "America/Indianapolis" }, + { "US Mountain Standard Time", "America/Dawson_Creek America/Creston America/Fort_Nelson" }, + { "US Mountain Standard Time", "America/Hermosillo" }, + { "US Mountain Standard Time", "America/Phoenix" }, + { "US Mountain Standard Time", "America/Phoenix" }, + { "US Mountain Standard Time", "Etc/GMT+7" }, + { "UTC", "America/Danmarkshavn" }, + { "UTC", "Etc/GMT Etc/UTC" }, + { "UTC", "Etc/GMT" }, + { "UTC+12", "Etc/GMT-12" }, + { "UTC+12", "Etc/GMT-12" }, + { "UTC+12", "Pacific/Funafuti" }, + { "UTC+12", "Pacific/Majuro Pacific/Kwajalein" }, + { "UTC+12", "Pacific/Nauru" }, + { "UTC+12", "Pacific/Tarawa" }, + { "UTC+12", "Pacific/Wake" }, + { "UTC+12", "Pacific/Wallis" }, + { "UTC+13", "Etc/GMT-13" }, + { "UTC+13", "Etc/GMT-13" }, + { "UTC+13", "Pacific/Enderbury" }, + { "UTC+13", "Pacific/Fakaofo" }, + { "UTC-02", "America/Noronha" }, + { "UTC-02", "Atlantic/South_Georgia" }, + { "UTC-02", "Etc/GMT+2" }, + { "UTC-02", "Etc/GMT+2" }, + { "UTC-08", "Etc/GMT+8" }, + { "UTC-08", "Etc/GMT+8" }, + { "UTC-08", "Pacific/Pitcairn" }, + { "UTC-09", "Etc/GMT+9" }, + { "UTC-09", "Etc/GMT+9" }, + { "UTC-09", "Pacific/Gambier" }, + { "UTC-11", "Etc/GMT+11" }, + { "UTC-11", "Etc/GMT+11" }, + { "UTC-11", "Pacific/Midway" }, + { "UTC-11", "Pacific/Niue" }, + { "UTC-11", "Pacific/Pago_Pago" }, + { "Ulaanbaatar Standard Time", "Asia/Ulaanbaatar Asia/Choibalsan" }, + { "Ulaanbaatar Standard Time", "Asia/Ulaanbaatar" }, + { "Venezuela Standard Time", "America/Caracas" }, + { "Venezuela Standard Time", "America/Caracas" }, + { "Vladivostok Standard Time", "Asia/Vladivostok Asia/Ust-Nera" }, + { "Vladivostok Standard Time", "Asia/Vladivostok" }, + { "Volgograd Standard Time", "Europe/Volgograd" }, + { "Volgograd Standard Time", "Europe/Volgograd" }, + { "W. Australia Standard Time", "Australia/Perth" }, + { "W. Australia Standard Time", "Australia/Perth" }, + { "W. Central Africa Standard Time", "Africa/Algiers" }, + { "W. Central Africa Standard Time", "Africa/Bangui" }, + { "W. Central Africa Standard Time", "Africa/Brazzaville" }, + { "W. Central Africa Standard Time", "Africa/Douala" }, + { "W. Central Africa Standard Time", "Africa/Kinshasa" }, + { "W. Central Africa Standard Time", "Africa/Lagos" }, + { "W. Central Africa Standard Time", "Africa/Lagos" }, + { "W. Central Africa Standard Time", "Africa/Libreville" }, + { "W. Central Africa Standard Time", "Africa/Luanda" }, + { "W. Central Africa Standard Time", "Africa/Malabo" }, + { "W. Central Africa Standard Time", "Africa/Ndjamena" }, + { "W. Central Africa Standard Time", "Africa/Niamey" }, + { "W. Central Africa Standard Time", "Africa/Porto-Novo" }, + { "W. Central Africa Standard Time", "Africa/Tunis" }, + { "W. Central Africa Standard Time", "Etc/GMT-1" }, + { "W. Europe Standard Time", "Arctic/Longyearbyen" }, + { "W. Europe Standard Time", "Europe/Amsterdam" }, + { "W. Europe Standard Time", "Europe/Andorra" }, + { "W. Europe Standard Time", "Europe/Berlin Europe/Busingen" }, + { "W. Europe Standard Time", "Europe/Berlin" }, + { "W. Europe Standard Time", "Europe/Gibraltar" }, + { "W. Europe Standard Time", "Europe/Luxembourg" }, + { "W. Europe Standard Time", "Europe/Malta" }, + { "W. Europe Standard Time", "Europe/Monaco" }, + { "W. Europe Standard Time", "Europe/Oslo" }, + { "W. Europe Standard Time", "Europe/Rome" }, + { "W. Europe Standard Time", "Europe/San_Marino" }, + { "W. Europe Standard Time", "Europe/Stockholm" }, + { "W. Europe Standard Time", "Europe/Vaduz" }, + { "W. Europe Standard Time", "Europe/Vatican" }, + { "W. Europe Standard Time", "Europe/Vienna" }, + { "W. Europe Standard Time", "Europe/Zurich" }, + { "W. Mongolia Standard Time", "Asia/Hovd" }, + { "W. Mongolia Standard Time", "Asia/Hovd" }, + { "West Asia Standard Time", "Antarctica/Mawson" }, + { "West Asia Standard Time", "Asia/Ashgabat" }, + { "West Asia Standard Time", "Asia/Dushanbe" }, + { "West Asia Standard Time", "Asia/Oral Asia/Aqtau Asia/Aqtobe Asia/Atyrau" }, + { "West Asia Standard Time", "Asia/Tashkent Asia/Samarkand" }, + { "West Asia Standard Time", "Asia/Tashkent" }, + { "West Asia Standard Time", "Etc/GMT-5" }, + { "West Asia Standard Time", "Indian/Kerguelen" }, + { "West Asia Standard Time", "Indian/Maldives" }, + { "West Bank Standard Time", "Asia/Hebron Asia/Gaza" }, + { "West Bank Standard Time", "Asia/Hebron" }, + { "West Pacific Standard Time", "Antarctica/DumontDUrville" }, + { "West Pacific Standard Time", "Etc/GMT-10" }, + { "West Pacific Standard Time", "Pacific/Guam" }, + { "West Pacific Standard Time", "Pacific/Port_Moresby" }, + { "West Pacific Standard Time", "Pacific/Port_Moresby" }, + { "West Pacific Standard Time", "Pacific/Saipan" }, + { "West Pacific Standard Time", "Pacific/Truk" }, + { "Yakutsk Standard Time", "Asia/Yakutsk Asia/Khandyga" }, + { "Yakutsk Standard Time", "Asia/Yakutsk" }, +}; + +const size_t WindowsTimeZoneIdTableNrElements = ARRAYSIZE(WindowsTimeZoneIdTable); diff --git a/winpr/libwinpr/timezone/WindowsZones.h b/winpr/libwinpr/timezone/WindowsZones.h new file mode 100644 index 0000000..231c0c6 --- /dev/null +++ b/winpr/libwinpr/timezone/WindowsZones.h @@ -0,0 +1,18 @@ +/* + * Automatically generated with scripts/update-windows-zones.py + */ +#ifndef WINPR_WINDOWS_ZONES_H_ +#define WINPR_WINDOWS_ZONES_H_ + +#include + +typedef struct +{ + const char* windows; + const char* tzid; +} WINDOWS_TZID_ENTRY; + +extern const WINDOWS_TZID_ENTRY WindowsTimeZoneIdTable[]; +extern const size_t WindowsTimeZoneIdTableNrElements; + +#endif /* WINPR_WINDOWS_ZONES_H_ */ diff --git a/winpr/libwinpr/timezone/timezone.c b/winpr/libwinpr/timezone/timezone.c new file mode 100644 index 0000000..f6d9874 --- /dev/null +++ b/winpr/libwinpr/timezone/timezone.c @@ -0,0 +1,581 @@ +/** + * WinPR: Windows Portable Runtime + * Time Zone + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include "../log.h" + +#define TAG WINPR_TAG("timezone") + +#ifndef _WIN32 + +#include +#include + +#include "TimeZones.h" +#include "WindowsZones.h" + +static UINT64 winpr_windows_gmtime(void) +{ + time_t unix_time = 0; + UINT64 windows_time = 0; + time(&unix_time); + + if (unix_time < 0) + return 0; + + windows_time = (UINT64)unix_time; + windows_time *= 10000000; + windows_time += 621355968000000000ULL; + return windows_time; +} + +static char* winpr_read_unix_timezone_identifier_from_file(FILE* fp) +{ + const INT CHUNK_SIZE = 32; + INT64 rc = 0; + INT64 read = 0; + INT64 length = CHUNK_SIZE; + char* tzid = NULL; + + tzid = (char*)malloc(length); + if (!tzid) + return NULL; + + do + { + rc = fread(tzid + read, 1, length - read - 1, fp); + if (rc > 0) + read += rc; + + if (read < (length - 1)) + break; + + length += CHUNK_SIZE; + char* tmp = (char*)realloc(tzid, length); + if (!tmp) + { + free(tzid); + return NULL; + } + + tzid = tmp; + } while (rc > 0); + + if (ferror(fp)) + { + free(tzid); + return NULL; + } + + tzid[read] = '\0'; + if (read > 0) + { + if (tzid[read - 1] == '\n') + tzid[read - 1] = '\0'; + } + + return tzid; +} + +static char* winpr_get_timezone_from_link(const char* links[], size_t count) +{ + const char* _links[] = { "/etc/localtime", "/etc/TZ" }; + + if (links == NULL) + { + links = _links; + count = ARRAYSIZE(_links); + } + + /* + * On linux distros such as Redhat or Archlinux, a symlink at /etc/localtime + * will point to /usr/share/zoneinfo/region/place where region/place could be + * America/Montreal for example. + * Some distributions do have to symlink at /etc/TZ. + */ + + for (size_t x = 0; x < count; x++) + { + char* tzid = NULL; + const char* link = links[x]; + char* buf = realpath(link, NULL); + + if (buf) + { + size_t sep = 0; + size_t alloc = 0; + size_t pos = 0; + size_t len = pos = strlen(buf); + + /* find the position of the 2nd to last "/" */ + for (size_t i = 1; i <= len; i++) + { + const size_t curpos = len - i; + const char cur = buf[curpos]; + + if (cur == '/') + sep++; + if (sep >= 2) + { + alloc = i; + pos = len - i + 1; + break; + } + } + + if ((len == 0) || (sep != 2)) + goto end; + + tzid = (char*)calloc(alloc + 1, sizeof(char)); + + if (!tzid) + goto end; + + strncpy(tzid, &buf[pos], alloc); + WLog_DBG(TAG, "tzid: %s", tzid); + goto end; + } + + end: + free(buf); + if (tzid) + return tzid; + } + + return NULL; +} + +#if defined(ANDROID) +#include "../utils/android.h" + +static char* winpr_get_android_timezone_identifier(void) +{ + char* tzid = NULL; + JNIEnv* jniEnv; + + /* Preferred: Try to get identifier from java TimeZone class */ + if (jniVm && ((*jniVm)->GetEnv(jniVm, (void**)&jniEnv, JNI_VERSION_1_6) == JNI_OK)) + { + const char* raw; + jclass jObjClass; + jobject jObj; + jmethodID jDefaultTimezone; + jmethodID jTimezoneIdentifier; + jstring tzJId; + jboolean attached = (*jniVm)->AttachCurrentThread(jniVm, &jniEnv, NULL); + jObjClass = (*jniEnv)->FindClass(jniEnv, "java/util/TimeZone"); + + if (!jObjClass) + goto fail; + + jDefaultTimezone = + (*jniEnv)->GetStaticMethodID(jniEnv, jObjClass, "getDefault", "()Ljava/util/TimeZone;"); + + if (!jDefaultTimezone) + goto fail; + + jObj = (*jniEnv)->CallStaticObjectMethod(jniEnv, jObjClass, jDefaultTimezone); + + if (!jObj) + goto fail; + + jTimezoneIdentifier = + (*jniEnv)->GetMethodID(jniEnv, jObjClass, "getID", "()Ljava/lang/String;"); + + if (!jTimezoneIdentifier) + goto fail; + + tzJId = (*jniEnv)->CallObjectMethod(jniEnv, jObj, jTimezoneIdentifier); + + if (!tzJId) + goto fail; + + raw = (*jniEnv)->GetStringUTFChars(jniEnv, tzJId, 0); + + if (raw) + tzid = _strdup(raw); + + (*jniEnv)->ReleaseStringUTFChars(jniEnv, tzJId, raw); + fail: + + if (attached) + (*jniVm)->DetachCurrentThread(jniVm); + } + + /* Fall back to property, might not be available. */ + if (!tzid) + { + FILE* fp = popen("getprop persist.sys.timezone", "r"); + + if (fp) + { + tzid = winpr_read_unix_timezone_identifier_from_file(fp); + pclose(fp); + } + } + + return tzid; +} +#endif + +static char* winpr_get_unix_timezone_identifier_from_file(void) +{ +#if defined(ANDROID) + return winpr_get_android_timezone_identifier(); +#else + FILE* fp = NULL; + char* tzid = NULL; +#if defined(__FreeBSD__) || defined(__OpenBSD__) + fp = winpr_fopen("/var/db/zoneinfo", "r"); +#else + fp = winpr_fopen("/etc/timezone", "r"); +#endif + + if (NULL == fp) + return NULL; + + tzid = winpr_read_unix_timezone_identifier_from_file(fp); + fclose(fp); + if (tzid != NULL) + WLog_DBG(TAG, "tzid: %s", tzid); + return tzid; +#endif +} + +static BOOL winpr_match_unix_timezone_identifier_with_list(const char* tzid, const char* list) +{ + char* p = NULL; + char* list_copy = NULL; + char* context = NULL; + + list_copy = _strdup(list); + + if (!list_copy) + return FALSE; + + p = strtok_s(list_copy, " ", &context); + + while (p != NULL) + { + if (strcmp(p, tzid) == 0) + { + free(list_copy); + return TRUE; + } + + p = strtok_s(NULL, " ", &context); + } + + free(list_copy); + return FALSE; +} + +static TIME_ZONE_ENTRY* winpr_detect_windows_time_zone(void) +{ + char* tzid = NULL; + char* ntzid = NULL; + LPCSTR tz = "TZ"; + + DWORD nSize = GetEnvironmentVariableA(tz, NULL, 0); + if (nSize) + { + tzid = (char*)malloc(nSize); + if (!GetEnvironmentVariableA(tz, tzid, nSize)) + { + free(tzid); + tzid = NULL; + } + } + + if (tzid == NULL) + tzid = winpr_get_unix_timezone_identifier_from_file(); + + if (tzid == NULL) + { + tzid = winpr_get_timezone_from_link(NULL, 0); + } + else + { + const char* zipath = "/usr/share/zoneinfo/"; + char buf[1024] = { 0 }; + const char* links[] = { buf }; + + snprintf(buf, ARRAYSIZE(buf), "%s%s", zipath, tzid); + ntzid = winpr_get_timezone_from_link(links, 1); + if (ntzid != NULL) + { + free(tzid); + tzid = ntzid; + } + } + + if (tzid == NULL) + return NULL; + + WLog_INFO(TAG, "tzid: %s", tzid); + + for (size_t i = 0; i < TimeZoneTableNrElements; i++) + { + const TIME_ZONE_ENTRY* tze = &TimeZoneTable[i]; + + for (size_t j = 0; j < WindowsTimeZoneIdTableNrElements; j++) + { + const WINDOWS_TZID_ENTRY* wzid = &WindowsTimeZoneIdTable[j]; + + if (strcmp(tze->Id, wzid->windows) != 0) + continue; + + if (winpr_match_unix_timezone_identifier_with_list(tzid, wzid->tzid)) + { + TIME_ZONE_ENTRY* ctimezone = (TIME_ZONE_ENTRY*)malloc(sizeof(TIME_ZONE_ENTRY)); + free(tzid); + + if (!ctimezone) + return NULL; + + *ctimezone = TimeZoneTable[i]; + return ctimezone; + } + } + } + + WLog_ERR(TAG, "Unable to find a match for unix timezone: %s", tzid); + free(tzid); + return NULL; +} + +static const TIME_ZONE_RULE_ENTRY* +winpr_get_current_time_zone_rule(const TIME_ZONE_RULE_ENTRY* rules, UINT32 count) +{ + UINT64 windows_time = 0; + windows_time = winpr_windows_gmtime(); + + for (UINT32 i = 0; i < count; i++) + { + if ((rules[i].TicksStart >= windows_time) && (windows_time >= rules[i].TicksEnd)) + { + /*WLog_ERR(TAG, "Got rule %" PRIu32 " from table at %p with count %"PRIu32"", i, + * (void*) rules, count);*/ + return &rules[i]; + } + } + + WLog_ERR(TAG, "Unable to get current timezone rule"); + return NULL; +} + +DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation) +{ + time_t t = 0; + struct tm tres; + struct tm* local_time = NULL; + TIME_ZONE_ENTRY* dtz = NULL; + LPTIME_ZONE_INFORMATION tz = lpTimeZoneInformation; + lpTimeZoneInformation->StandardBias = 0; + time(&t); + local_time = localtime_r(&t, &tres); + if (!local_time) + goto out_error; + + memset(tz, 0, sizeof(TIME_ZONE_INFORMATION)); +#ifdef WINPR_HAVE_TM_GMTOFF + { + long bias = -(local_time->tm_gmtoff / 60L); + + if (bias > INT32_MAX) + bias = INT32_MAX; + + tz->Bias = (LONG)bias; + } +#else + tz->Bias = 0; +#endif + dtz = winpr_detect_windows_time_zone(); + + if (dtz != NULL) + { + const TIME_ZONE_INFORMATION empty = { 0 }; + + WLog_DBG(TAG, "tz: Bias=%" PRId32 " sn='%s' dln='%s'", dtz->Bias, dtz->StandardName, + dtz->DaylightName); + + *tz = empty; + tz->Bias = dtz->Bias; + + if (ConvertUtf8ToWChar(dtz->StandardName, tz->StandardName, ARRAYSIZE(tz->StandardName)) < + 0) + { + WLog_ERR(TAG, "StandardName conversion failed - using default"); + goto out_error; + } + + if (ConvertUtf8ToWChar(dtz->DaylightName, tz->DaylightName, ARRAYSIZE(tz->DaylightName)) < + 0) + { + WLog_ERR(TAG, "DaylightName conversion failed - using default"); + goto out_error; + } + + if ((dtz->SupportsDST) && (dtz->RuleTableCount > 0)) + { + const TIME_ZONE_RULE_ENTRY* rule = + winpr_get_current_time_zone_rule(dtz->RuleTable, dtz->RuleTableCount); + + if (rule != NULL) + { + tz->DaylightBias = -rule->DaylightDelta; + tz->StandardDate = rule->StandardDate; + tz->DaylightDate = rule->DaylightDate; + } + } + + free(dtz); + /* 1 ... TIME_ZONE_ID_STANDARD + * 2 ... TIME_ZONE_ID_DAYLIGHT */ + return local_time->tm_isdst ? 2 : 1; + } + + /* could not detect timezone, use computed bias from tm_gmtoff */ + WLog_DBG(TAG, "tz not found, using computed bias %" PRId32 ".", tz->Bias); +out_error: + free(dtz); + memcpy(tz->StandardName, L"Client Local Time", sizeof(tz->StandardName)); + memcpy(tz->DaylightName, L"Client Local Time", sizeof(tz->DaylightName)); + return 0; /* TIME_ZONE_ID_UNKNOWN */ +} + +BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation) +{ + WINPR_UNUSED(lpTimeZoneInformation); + return FALSE; +} + +BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime) +{ + WINPR_UNUSED(lpSystemTime); + WINPR_UNUSED(lpFileTime); + return FALSE; +} + +BOOL FileTimeToSystemTime(const FILETIME* lpFileTime, LPSYSTEMTIME lpSystemTime) +{ + WINPR_UNUSED(lpFileTime); + WINPR_UNUSED(lpSystemTime); + return FALSE; +} + +BOOL SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTimeZone, + LPSYSTEMTIME lpUniversalTime, LPSYSTEMTIME lpLocalTime) +{ + WINPR_UNUSED(lpTimeZone); + WINPR_UNUSED(lpUniversalTime); + WINPR_UNUSED(lpLocalTime); + return FALSE; +} + +BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation, + LPSYSTEMTIME lpLocalTime, LPSYSTEMTIME lpUniversalTime) +{ + WINPR_UNUSED(lpTimeZoneInformation); + WINPR_UNUSED(lpLocalTime); + WINPR_UNUSED(lpUniversalTime); + return FALSE; +} + +#endif + +/* + * GetDynamicTimeZoneInformation is provided by the SDK if _WIN32_WINNT >= 0x0600 in SDKs above 7.1A + * and incorrectly if _WIN32_WINNT >= 0x0501 in older SDKs + */ +#if !defined(_WIN32) || \ + (defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \ + !defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */ + +DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation) +{ + WINPR_UNUSED(pTimeZoneInformation); + return 0; +} + +BOOL SetDynamicTimeZoneInformation(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation) +{ + WINPR_UNUSED(lpTimeZoneInformation); + return FALSE; +} + +BOOL GetTimeZoneInformationForYear(USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, + LPTIME_ZONE_INFORMATION ptzi) +{ + WINPR_UNUSED(wYear); + WINPR_UNUSED(pdtzi); + WINPR_UNUSED(ptzi); + return FALSE; +} + +#endif + +#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0601)) /* Windows 7 */ + +BOOL SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, + const SYSTEMTIME* lpUniversalTime, LPSYSTEMTIME lpLocalTime) +{ + WINPR_UNUSED(lpTimeZoneInformation); + WINPR_UNUSED(lpUniversalTime); + WINPR_UNUSED(lpLocalTime); + return FALSE; +} + +BOOL TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, + const SYSTEMTIME* lpLocalTime, LPSYSTEMTIME lpUniversalTime) +{ + WINPR_UNUSED(lpTimeZoneInformation); + WINPR_UNUSED(lpLocalTime); + WINPR_UNUSED(lpUniversalTime); + return FALSE; +} + +#endif + +#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0602)) /* Windows 8 */ + +DWORD EnumDynamicTimeZoneInformation(const DWORD dwIndex, + PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation) +{ + WINPR_UNUSED(dwIndex); + WINPR_UNUSED(lpTimeZoneInformation); + return 0; +} + +DWORD GetDynamicTimeZoneInformationEffectiveYears( + const PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear) +{ + WINPR_UNUSED(lpTimeZoneInformation); + WINPR_UNUSED(FirstYear); + WINPR_UNUSED(LastYear); + return 0; +} + +#endif diff --git a/winpr/libwinpr/utils/CMakeLists.txt b/winpr/libwinpr/utils/CMakeLists.txt new file mode 100644 index 0000000..91c7353 --- /dev/null +++ b/winpr/libwinpr/utils/CMakeLists.txt @@ -0,0 +1,228 @@ +# WinPR: Windows Portable Runtime +# libwinpr-utils cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(CheckFunctionExists) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +option(WITH_LODEPNG "build WinPR with PNG support" OFF) +if (WITH_LODEPNG) + find_package(lodepng REQUIRED) + + winpr_definition_add(-DWITH_LODEPNG) + set(WINPR_WITH_PNG ON CACHE BOOL "build cache") + + winpr_include_directory_add(${lodepng_INCLUDE_DIRS}) + winpr_library_add_private(${lodepng_LIBRARIES}) +endif() + +option(WINPR_UTILS_IMAGE_PNG "Add PNG <--> BMP conversion support to clipboard" OFF) +if (WINPR_UTILS_IMAGE_PNG) + find_package(PNG REQUIRED) + + set(WINPR_WITH_PNG ON CACHE BOOL "build cache") + winpr_include_directory_add(${PNG_INCLUDE_DIRS}) + winpr_library_add_private(${PNG_LIBRARIES}) +endif() + +option(WINPR_UTILS_IMAGE_WEBP "Add WebP <--> BMP conversion support to clipboard" OFF) +if (WINPR_UTILS_IMAGE_WEBP) + find_package(PkgConfig REQUIRED) + pkg_check_modules(WEBP libwebp REQUIRED) + + winpr_include_directory_add(${WEBP_INCLUDE_DIRS}) + winpr_library_add_private(${WEBP_LIBRARIES}) +endif() + +option(WINPR_UTILS_IMAGE_JPEG "Add Jpeg <--> BMP conversion support to clipboard" OFF) +if (WINPR_UTILS_IMAGE_JPEG) + find_package(PkgConfig REQUIRED) + pkg_check_modules(JPEG libjpeg REQUIRED) + + winpr_include_directory_add(${JPEG_INCLUDE_DIRS}) + winpr_library_add_private(${JPEG_LIBRARIES}) +endif() + + +set(COLLECTIONS_SRCS + collections/Object.c + collections/Queue.c + collections/Stack.c + collections/PubSub.c + collections/BitStream.c + collections/ArrayList.c + collections/LinkedList.c + collections/HashTable.c + collections/ListDictionary.c + collections/CountdownEvent.c + collections/BufferPool.c + collections/ObjectPool.c + collections/StreamPool.c + collections/MessageQueue.c + collections/MessagePipe.c) + +if (WINPR_HAVE_SYSLOG_H) + set(SYSLOG_SRCS + wlog/SyslogAppender.c + wlog/SyslogAppender.h + ) +endif() + +find_package(libsystemd) +option(WITH_SYSTEMD "allows to export wLog to systemd journal" ${libsystemd_FOUND}) +if(WITH_LIBSYSTEMD) + find_package(libsystemd REQUIRED) + set(WINPR_HAVE_JOURNALD_H TRUE) + set(JOURNALD_SRCS + wlog/JournaldAppender.c + wlog/JournaldAppender.h + ) + winpr_include_directory_add(${LIBSYSTEMD_INCLUDE_DIR}) + winpr_library_add_private(${LIBSYSTEMD_LIBRARY}) +else() + unset(WINPR_HAVE_JOURNALD_H) +endif() + +set(WLOG_SRCS + wlog/wlog.c + wlog/wlog.h + wlog/Layout.c + wlog/Layout.h + wlog/Message.c + wlog/Message.h + wlog/DataMessage.c + wlog/DataMessage.h + wlog/ImageMessage.c + wlog/ImageMessage.h + wlog/PacketMessage.c + wlog/PacketMessage.h + wlog/Appender.c + wlog/Appender.h + wlog/FileAppender.c + wlog/FileAppender.h + wlog/BinaryAppender.c + wlog/BinaryAppender.h + wlog/CallbackAppender.c + wlog/CallbackAppender.h + wlog/ConsoleAppender.c + wlog/ConsoleAppender.h + wlog/UdpAppender.c + wlog/UdpAppender.h + ${SYSLOG_SRCS} + ${JOURNALD_SRCS} + ) + +set(ASN1_SRCS + asn1/asn1.c +) + +set(SRCS + ini.c + sam.c + ntlm.c + image.c + print.c + stream.h + stream.c + strlst.c + debug.c + winpr.c + cmdline.c + ssl.c) + +if (ANDROID) + list(APPEND SRCS android.h android.c) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + if (NOT WINPR_HAVE_UNWIND_H) + message("[backtrace] android NDK without unwind.h, falling back to corkscrew") + set(WINPR_HAVE_CORKSCREW 1) + endif() +endif() + +if (WINPR_HAVE_CORKSCREW) + list(APPEND SRCS + corkscrew/debug.c + corkscrew/debug.h) +endif() + +if (WIN32) + list(APPEND SRCS + windows/debug.c + windows/debug.h) +endif() + +if (WINPR_HAVE_EXECINFO_H) + option(USE_EXECINFO "Use execinfo.h to generate backtraces" ON) + if (USE_EXECINFO) + winpr_definition_add(-DUSE_EXECINFO) + list(APPEND SRCS + execinfo/debug.c + execinfo/debug.h) + endif() +endif() + +if (WINPR_HAVE_UNWIND_H) + option(USE_UNWIND "Use unwind.h to generate backtraces" ON) + if (USE_UNWIND) + winpr_definition_add(-DUSE_UNWIND) + list(APPEND SRCS + unwind/debug.c + unwind/debug.h) + endif() +endif() + +winpr_module_add(${SRCS} + ${COLLECTIONS_SRCS} + ${WLOG_SRCS} + ${ASN1_SRCS} +) + +winpr_include_directory_add( + "." +) + +if(OPENSSL_FOUND) + winpr_include_directory_add(${OPENSSL_INCLUDE_DIR}) + winpr_library_add_private(${OPENSSL_LIBRARIES}) +endif() + +if(MBEDTLS_FOUND) + winpr_include_directory_add(${MBEDTLS_INCLUDE_DIR}) + winpr_library_add_private(${MBEDTLS_LIBRARIES}) +endif() + +if(UNIX) + winpr_library_add_private(m) + + set(CMAKE_REQUIRED_INCLUDES backtrace.h) + check_function_exists(backtrace BACKTRACE) + if (NOT BACKTRACE) + set(CMAKE_REQUIRED_LIBRARIES execinfo) + check_function_exists(backtrace EXECINFO) + if (EXECINFO) + winpr_library_add_private(execinfo) + endif() + endif() +endif() + +if(WIN32) + winpr_library_add_public(dbghelp) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/utils/ModuleOptions.cmake b/winpr/libwinpr/utils/ModuleOptions.cmake new file mode 100644 index 0000000..9e37ca6 --- /dev/null +++ b/winpr/libwinpr/utils/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "utils") +set(MINWIN_LONG_NAME "WinPR Utils") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") + diff --git a/winpr/libwinpr/utils/android.c b/winpr/libwinpr/utils/android.c new file mode 100644 index 0000000..4c7113f --- /dev/null +++ b/winpr/libwinpr/utils/android.c @@ -0,0 +1,77 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Winpr android helpers + * + * Copyright 2022 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android.h" +#include + +#include +#include + +#include "../log.h" + +#define TAG WINPR_TAG("android") + +JavaVM* jniVm = NULL; + +WINPR_API jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + WLog_INFO(TAG, "Setting up JNI environement..."); + + jniVm = vm; + return JNI_VERSION_1_6; +} + +WINPR_API void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + WLog_INFO(TAG, "Tearing down JNI environement..."); + + if ((*jniVm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) + { + WLog_FATAL(TAG, "Failed to get the environment"); + return; + } +} + +jboolean winpr_jni_attach_thread(JNIEnv** env) +{ + WINPR_ASSERT(jniVm); + + if ((*jniVm)->GetEnv(jniVm, (void**)env, JNI_VERSION_1_4) != JNI_OK) + { + WLog_INFO(TAG, "android_java_callback: attaching current thread"); + (*jniVm)->AttachCurrentThread(jniVm, env, NULL); + + if ((*jniVm)->GetEnv(jniVm, (void**)env, JNI_VERSION_1_4) != JNI_OK) + { + WLog_ERR(TAG, "android_java_callback: failed to obtain current JNI environment"); + } + + return JNI_TRUE; + } + + return JNI_FALSE; +} + +/* attach current thread to JVM */ +void winpr_jni_detach_thread(void) +{ + WINPR_ASSERT(jniVm); + (*jniVm)->DetachCurrentThread(jniVm); +} diff --git a/winpr/libwinpr/utils/android.h b/winpr/libwinpr/utils/android.h new file mode 100644 index 0000000..30b264b --- /dev/null +++ b/winpr/libwinpr/utils/android.h @@ -0,0 +1,30 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Winpr android helpers + * + * Copyright 2022 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_UTILS_ANDROID_PRIV_H +#define WINPR_UTILS_ANDROID_PRIV_H + +#include + +extern JavaVM* jniVm; + +jboolean winpr_jni_attach_thread(JNIEnv** env); +void winpr_jni_detach_thread(void); + +#endif /* WINPR_UTILS_ANDROID_PRIV_H */ diff --git a/winpr/libwinpr/utils/asn1/asn1.c b/winpr/libwinpr/utils/asn1/asn1.c new file mode 100644 index 0000000..201f23c --- /dev/null +++ b/winpr/libwinpr/utils/asn1/asn1.c @@ -0,0 +1,1490 @@ +/** + * WinPR: Windows Portable Runtime + * ASN1 routines + * + * Copyright 2022 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "../../log.h" +#define TAG WINPR_TAG("asn1") + +typedef struct +{ + size_t poolOffset; + size_t capacity; + size_t used; +} Asn1Chunk; + +#define MAX_STATIC_ITEMS 50 + +/** @brief type of encoder container */ +typedef enum +{ + ASN1_CONTAINER_SEQ, + ASN1_CONTAINER_SET, + ASN1_CONTAINER_APP, + ASN1_CONTAINER_CONTEXT_ONLY, + ASN1_CONTAINER_OCTETSTRING, +} ContainerType; + +typedef struct WinPrAsn1EncContainer WinPrAsn1EncContainer; +/** @brief a container in the ASN1 stream (sequence, set, app or contextual) */ +struct WinPrAsn1EncContainer +{ + size_t headerChunkId; + BOOL contextual; + WinPrAsn1_tag tag; + ContainerType containerType; +}; + +/** @brief the encoder internal state */ +struct WinPrAsn1Encoder +{ + WinPrAsn1EncodingRule encoding; + wStream* pool; + + Asn1Chunk* chunks; + Asn1Chunk staticChunks[MAX_STATIC_ITEMS]; + size_t freeChunkId; + size_t chunksCapacity; + + WinPrAsn1EncContainer* containers; + WinPrAsn1EncContainer staticContainers[MAX_STATIC_ITEMS]; + size_t freeContainerIndex; + size_t containerCapacity; +}; + +#define WINPR_ASSERT_VALID_TAG(t) WINPR_ASSERT(t < 64) + +void WinPrAsn1FreeOID(WinPrAsn1_OID* poid) +{ + WINPR_ASSERT(poid); + free(poid->data); + poid->data = NULL; + poid->len = 0; +} + +void WinPrAsn1FreeOctetString(WinPrAsn1_OctetString* octets) +{ + WinPrAsn1FreeOID(octets); +} + +/** + * The encoder is implemented with the goals to: + * * have an API which is convenient to use (avoid computing inner elements size) + * * hide the BER/DER encoding details + * * avoid multiple copies and memory moves when building the content + * + * To achieve this, the encoder contains a big memory block (encoder->pool), and various chunks + * (encoder->chunks) pointing to that memory block. The idea is to reserve some space in the pool + * for the container headers when we start a new container element. For example when a sequence is + * started we reserve 6 bytes which is the maximum size: byte0 + length. Then fill the content of + * the sequence in further chunks. When a container is closed, we compute the inner size (by adding + * the size of inner chunks), we write the headers bytes, and we adjust the chunk size accordingly. + * + * For example to encode: + * SEQ + * IASTRING(test1) + * INTEGER(200) + * + * with this code: + * + * WinPrAsn1EncSeqContainer(enc); + * WinPrAsn1EncIA5String(enc, "test1"); + * WinPrAsn1EncInteger(enc, 200); + * + * Memory pool and chunks would look like: + * + * [ reserved for seq][string|5|"test1"][integer|0x81|200] + * (6 bytes) + * |-----------------||----------------------------------| + * ^ ^ + * | | + * chunk0 chunk1 + * + * As we try to compact chunks as much as we can, we managed to encode the ia5string and the + * integer using the same chunk. + * + * When the sequence is closed with: + * + * WinPrAsn1EncEndContainer(enc); + * + * The final pool and chunks will look like: + * + * XXXXXX[seq headers][string|5|"test1"][integer|0x81|200] + * + * |-----------||----------------------------------| + * ^ ^ + * | | + * chunk0 chunk1 + * + * The generated content can be retrieved using: + * + * WinPrAsn1EncToStream(enc, targetStream); + * + * It will sequentially write all the chunks in the given target stream. + */ + +WinPrAsn1Encoder* WinPrAsn1Encoder_New(WinPrAsn1EncodingRule encoding) +{ + WinPrAsn1Encoder* enc = calloc(1, sizeof(*enc)); + if (!enc) + return NULL; + + enc->encoding = encoding; + enc->pool = Stream_New(NULL, 1024); + if (!enc->pool) + { + free(enc); + return NULL; + } + + enc->containers = &enc->staticContainers[0]; + enc->chunks = &enc->staticChunks[0]; + enc->chunksCapacity = MAX_STATIC_ITEMS; + enc->freeContainerIndex = 0; + return enc; +} + +void WinPrAsn1Encoder_Reset(WinPrAsn1Encoder* enc) +{ + WINPR_ASSERT(enc); + + enc->freeContainerIndex = 0; + enc->freeChunkId = 0; + + ZeroMemory(enc->chunks, sizeof(*enc->chunks) * enc->chunksCapacity); +} + +void WinPrAsn1Encoder_Free(WinPrAsn1Encoder** penc) +{ + WinPrAsn1Encoder* enc = NULL; + + WINPR_ASSERT(penc); + enc = *penc; + if (enc) + { + if (enc->containers != &enc->staticContainers[0]) + free(enc->containers); + + if (enc->chunks != &enc->staticChunks[0]) + free(enc->chunks); + + Stream_Free(enc->pool, TRUE); + free(enc); + } + *penc = NULL; +} + +static Asn1Chunk* asn1enc_get_free_chunk(WinPrAsn1Encoder* enc, size_t chunkSz, BOOL commit, + size_t* id) +{ + Asn1Chunk* ret = NULL; + WINPR_ASSERT(enc); + WINPR_ASSERT(chunkSz); + + if (commit) + { + /* if it's not a reservation let's see if the last chunk is not a reservation and can be + * expanded */ + size_t lastChunk = enc->freeChunkId ? enc->freeChunkId - 1 : 0; + ret = &enc->chunks[lastChunk]; + if (ret->capacity && ret->capacity == ret->used) + { + if (!Stream_EnsureRemainingCapacity(enc->pool, chunkSz)) + return NULL; + + Stream_Seek(enc->pool, chunkSz); + ret->capacity += chunkSz; + ret->used += chunkSz; + if (id) + *id = lastChunk; + return ret; + } + } + + if (enc->freeChunkId == enc->chunksCapacity) + { + /* chunks need a resize */ + Asn1Chunk* src = (enc->chunks != &enc->staticChunks[0]) ? enc->chunks : NULL; + Asn1Chunk* tmp = realloc(src, (enc->chunksCapacity + 10) * sizeof(*src)); + if (!tmp) + return NULL; + + if (enc->chunks == &enc->staticChunks[0]) + memcpy(tmp, &enc->staticChunks[0], enc->chunksCapacity * sizeof(*src)); + else + memset(tmp + enc->freeChunkId, 0, sizeof(*tmp) * 10); + + enc->chunks = tmp; + enc->chunksCapacity += 10; + } + if (enc->freeChunkId == enc->chunksCapacity) + return NULL; + + if (!Stream_EnsureRemainingCapacity(enc->pool, chunkSz)) + return NULL; + + ret = &enc->chunks[enc->freeChunkId]; + ret->poolOffset = Stream_GetPosition(enc->pool); + ret->capacity = chunkSz; + ret->used = commit ? chunkSz : 0; + if (id) + *id = enc->freeChunkId; + + enc->freeChunkId++; + Stream_Seek(enc->pool, chunkSz); + return ret; +} + +static WinPrAsn1EncContainer* asn1enc_get_free_container(WinPrAsn1Encoder* enc, size_t* id) +{ + WinPrAsn1EncContainer* ret = NULL; + WINPR_ASSERT(enc); + + if (enc->freeContainerIndex == enc->containerCapacity) + { + /* containers need a resize (or switch from static to dynamic) */ + WinPrAsn1EncContainer* src = + (enc->containers != &enc->staticContainers[0]) ? enc->containers : NULL; + WinPrAsn1EncContainer* tmp = realloc(src, (enc->containerCapacity + 10) * sizeof(*src)); + if (!tmp) + return NULL; + + if (enc->containers == &enc->staticContainers[0]) + memcpy(tmp, &enc->staticContainers[0], enc->containerCapacity * sizeof(*src)); + + enc->containers = tmp; + enc->containerCapacity += 10; + } + if (enc->freeContainerIndex == enc->containerCapacity) + return NULL; + + ret = &enc->containers[enc->freeContainerIndex]; + *id = enc->freeContainerIndex; + + enc->freeContainerIndex++; + return ret; +} + +static size_t lenBytes(size_t len) +{ + if (len < 128) + return 1; + if (len < (1 << 8)) + return 2; + if (len < (1 << 16)) + return 3; + if (len < (1 << 24)) + return 4; + + return 5; +} + +static void asn1WriteLen(wStream* s, size_t len) +{ + if (len < 128) + { + Stream_Write_UINT8(s, (UINT8)len); + } + else if (len < (1 << 8)) + { + Stream_Write_UINT8(s, 0x81); + Stream_Write_UINT8(s, (UINT8)len); + } + else if (len < (1 << 16)) + { + Stream_Write_UINT8(s, 0x82); + Stream_Write_UINT16_BE(s, (UINT16)len); + } + else if (len < (1 << 24)) + { + Stream_Write_UINT8(s, 0x83); + Stream_Write_UINT24_BE(s, (UINT32)len); + } + else + { + WINPR_ASSERT(len <= UINT32_MAX); + Stream_Write_UINT8(s, 0x84); + Stream_Write_UINT32_BE(s, (UINT32)len); + } +} + +static WinPrAsn1EncContainer* getAsn1Container(WinPrAsn1Encoder* enc, ContainerType ctype, + WinPrAsn1_tag tag, BOOL contextual, size_t maxLen) +{ + size_t ret = 0; + size_t chunkId = 0; + WinPrAsn1EncContainer* container = NULL; + + Asn1Chunk* chunk = asn1enc_get_free_chunk(enc, maxLen, FALSE, &chunkId); + if (!chunk) + return NULL; + + container = asn1enc_get_free_container(enc, &ret); + container->containerType = ctype; + container->tag = tag; + container->contextual = contextual; + container->headerChunkId = chunkId; + return container; +} + +BOOL WinPrAsn1EncAppContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId) +{ + WINPR_ASSERT_VALID_TAG(tagId); + return getAsn1Container(enc, ASN1_CONTAINER_APP, tagId, FALSE, 6) != NULL; +} + +BOOL WinPrAsn1EncSeqContainer(WinPrAsn1Encoder* enc) +{ + return getAsn1Container(enc, ASN1_CONTAINER_SEQ, 0, FALSE, 6) != NULL; +} + +BOOL WinPrAsn1EncSetContainer(WinPrAsn1Encoder* enc) +{ + return getAsn1Container(enc, ASN1_CONTAINER_SET, 0, FALSE, 6) != NULL; +} + +BOOL WinPrAsn1EncContextualSeqContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId) +{ + return getAsn1Container(enc, ASN1_CONTAINER_SEQ, tagId, TRUE, 6 + 6) != NULL; +} + +BOOL WinPrAsn1EncContextualSetContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId) +{ + return getAsn1Container(enc, ASN1_CONTAINER_SET, tagId, TRUE, 6 + 6) != NULL; +} + +BOOL WinPrAsn1EncContextualContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId) +{ + return getAsn1Container(enc, ASN1_CONTAINER_CONTEXT_ONLY, tagId, TRUE, 6) != NULL; +} + +BOOL WinPrAsn1EncOctetStringContainer(WinPrAsn1Encoder* enc) +{ + return getAsn1Container(enc, ASN1_CONTAINER_OCTETSTRING, 0, FALSE, 6) != NULL; +} + +BOOL WinPrAsn1EncContextualOctetStringContainer(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId) +{ + return getAsn1Container(enc, ASN1_CONTAINER_OCTETSTRING, tagId, TRUE, 6 + 6) != NULL; +} + +size_t WinPrAsn1EncEndContainer(WinPrAsn1Encoder* enc) +{ + size_t innerLen = 0; + size_t unused = 0; + size_t innerHeaderBytes = 0; + size_t outerHeaderBytes = 0; + BYTE containerByte = 0; + WinPrAsn1EncContainer* container = NULL; + Asn1Chunk* chunk = NULL; + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(enc); + WINPR_ASSERT(enc->freeContainerIndex); + + /* compute inner length */ + container = &enc->containers[enc->freeContainerIndex - 1]; + innerLen = 0; + for (size_t i = container->headerChunkId + 1; i < enc->freeChunkId; i++) + innerLen += enc->chunks[i].used; + + /* compute effective headerLength */ + switch (container->containerType) + { + case ASN1_CONTAINER_SEQ: + containerByte = ER_TAG_SEQUENCE; + innerHeaderBytes = 1 + lenBytes(innerLen); + break; + case ASN1_CONTAINER_SET: + containerByte = ER_TAG_SET; + innerHeaderBytes = 1 + lenBytes(innerLen); + break; + case ASN1_CONTAINER_OCTETSTRING: + containerByte = ER_TAG_OCTET_STRING; + innerHeaderBytes = 1 + lenBytes(innerLen); + break; + case ASN1_CONTAINER_APP: + containerByte = ER_TAG_APP | container->tag; + innerHeaderBytes = 1 + lenBytes(innerLen); + break; + case ASN1_CONTAINER_CONTEXT_ONLY: + innerHeaderBytes = 0; + break; + default: + WLog_ERR(TAG, "invalid containerType"); + return 0; + } + + outerHeaderBytes = innerHeaderBytes; + if (container->contextual) + { + outerHeaderBytes = 1 + lenBytes(innerHeaderBytes + innerLen) + innerHeaderBytes; + } + + /* we write the headers at the end of the reserved space and we adjust + * the chunk to be a non reserved chunk */ + chunk = &enc->chunks[container->headerChunkId]; + unused = chunk->capacity - outerHeaderBytes; + chunk->poolOffset += unused; + chunk->capacity = chunk->used = outerHeaderBytes; + + Stream_StaticInit(s, Stream_Buffer(enc->pool) + chunk->poolOffset, outerHeaderBytes); + if (container->contextual) + { + Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | container->tag); + asn1WriteLen(s, innerHeaderBytes + innerLen); + } + + switch (container->containerType) + { + case ASN1_CONTAINER_SEQ: + case ASN1_CONTAINER_SET: + case ASN1_CONTAINER_OCTETSTRING: + case ASN1_CONTAINER_APP: + Stream_Write_UINT8(s, containerByte); + asn1WriteLen(s, innerLen); + break; + case ASN1_CONTAINER_CONTEXT_ONLY: + break; + default: + WLog_ERR(TAG, "invalid containerType"); + return 0; + } + + /* TODO: here there is place for packing chunks */ + enc->freeContainerIndex--; + return outerHeaderBytes + innerLen; +} + +static BOOL asn1_getWriteStream(WinPrAsn1Encoder* enc, size_t len, wStream* s) +{ + BYTE* dest = NULL; + Asn1Chunk* chunk = asn1enc_get_free_chunk(enc, len, TRUE, NULL); + if (!chunk) + return FALSE; + + dest = Stream_Buffer(enc->pool) + chunk->poolOffset + chunk->capacity - len; + Stream_StaticInit(s, dest, len); + return TRUE; +} + +size_t WinPrAsn1EncRawContent(WinPrAsn1Encoder* enc, const WinPrAsn1_MemoryChunk* c) +{ + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(enc); + WINPR_ASSERT(c); + + if (!asn1_getWriteStream(enc, c->len, s)) + return 0; + + Stream_Write(s, c->data, c->len); + return c->len; +} + +size_t WinPrAsn1EncContextualRawContent(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_MemoryChunk* c) +{ + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(enc); + WINPR_ASSERT(c); + WINPR_ASSERT_VALID_TAG(tagId); + + size_t len = 1 + lenBytes(c->len) + c->len; + if (!asn1_getWriteStream(enc, len, s)) + return 0; + + Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId); + asn1WriteLen(s, c->len); + + Stream_Write(s, c->data, c->len); + return len; +} + +static size_t asn1IntegerLen(WinPrAsn1_INTEGER value) +{ + if (value <= 127 && value >= -128) + return 2; + else if (value <= 32767 && value >= -32768) + return 3; + else + return 5; +} + +static size_t WinPrAsn1EncIntegerLike(WinPrAsn1Encoder* enc, WinPrAsn1_tag b, + WinPrAsn1_INTEGER value) +{ + wStream staticS; + wStream* s = &staticS; + size_t len = 0; + + len = asn1IntegerLen(value); + if (!asn1_getWriteStream(enc, 1 + len, s)) + return 0; + + Stream_Write_UINT8(s, b); + switch (len) + { + case 2: + Stream_Write_UINT8(s, 1); + Stream_Write_UINT8(s, value); + break; + case 3: + Stream_Write_UINT8(s, 2); + Stream_Write_UINT16_BE(s, value); + break; + case 5: + Stream_Write_UINT8(s, 4); + Stream_Write_UINT32_BE(s, value); + break; + } + return 1 + len; +} + +size_t WinPrAsn1EncInteger(WinPrAsn1Encoder* enc, WinPrAsn1_INTEGER value) +{ + return WinPrAsn1EncIntegerLike(enc, ER_TAG_INTEGER, value); +} + +size_t WinPrAsn1EncEnumerated(WinPrAsn1Encoder* enc, WinPrAsn1_ENUMERATED value) +{ + return WinPrAsn1EncIntegerLike(enc, ER_TAG_ENUMERATED, value); +} + +static size_t WinPrAsn1EncContextualIntegerLike(WinPrAsn1Encoder* enc, WinPrAsn1_tag tag, + WinPrAsn1_tagId tagId, WinPrAsn1_INTEGER value) +{ + wStream staticS; + wStream* s = &staticS; + size_t len = 0; + size_t outLen = 0; + + WINPR_ASSERT(enc); + WINPR_ASSERT_VALID_TAG(tagId); + + len = asn1IntegerLen(value); + + outLen = 1 + lenBytes(1 + len) + (1 + len); + if (!asn1_getWriteStream(enc, outLen, s)) + return 0; + + Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId); + asn1WriteLen(s, 1 + len); + + Stream_Write_UINT8(s, tag); + switch (len) + { + case 2: + Stream_Write_UINT8(s, 1); + Stream_Write_UINT8(s, value); + break; + case 3: + Stream_Write_UINT8(s, 2); + Stream_Write_UINT16_BE(s, value); + break; + case 5: + Stream_Write_UINT8(s, 4); + Stream_Write_UINT32_BE(s, value); + break; + } + return outLen; +} + +size_t WinPrAsn1EncContextualInteger(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_INTEGER value) +{ + return WinPrAsn1EncContextualIntegerLike(enc, ER_TAG_INTEGER, tagId, value); +} + +size_t WinPrAsn1EncContextualEnumerated(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_ENUMERATED value) +{ + return WinPrAsn1EncContextualIntegerLike(enc, ER_TAG_ENUMERATED, tagId, value); +} + +size_t WinPrAsn1EncBoolean(WinPrAsn1Encoder* enc, WinPrAsn1_BOOL b) +{ + wStream staticS; + wStream* s = &staticS; + + if (!asn1_getWriteStream(enc, 3, s)) + return 0; + + Stream_Write_UINT8(s, ER_TAG_BOOLEAN); + Stream_Write_UINT8(s, 1); + Stream_Write_UINT8(s, b ? 0xff : 0); + + return 3; +} + +size_t WinPrAsn1EncContextualBoolean(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, WinPrAsn1_BOOL b) +{ + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(enc); + WINPR_ASSERT_VALID_TAG(tagId); + + if (!asn1_getWriteStream(enc, 5, s)) + return 0; + + Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId); + Stream_Write_UINT8(s, 3); + + Stream_Write_UINT8(s, ER_TAG_BOOLEAN); + Stream_Write_UINT8(s, 1); + Stream_Write_UINT8(s, b ? 0xff : 0); + + return 5; +} + +static size_t WinPrAsn1EncMemoryChunk(WinPrAsn1Encoder* enc, BYTE wireType, + const WinPrAsn1_MemoryChunk* mchunk) +{ + wStream s; + size_t len = 0; + + WINPR_ASSERT(enc); + WINPR_ASSERT(mchunk); + len = 1 + lenBytes(mchunk->len) + mchunk->len; + + if (!asn1_getWriteStream(enc, len, &s)) + return 0; + + Stream_Write_UINT8(&s, wireType); + asn1WriteLen(&s, mchunk->len); + Stream_Write(&s, mchunk->data, mchunk->len); + return len; +} + +size_t WinPrAsn1EncOID(WinPrAsn1Encoder* enc, const WinPrAsn1_OID* oid) +{ + return WinPrAsn1EncMemoryChunk(enc, ER_TAG_OBJECT_IDENTIFIER, oid); +} + +size_t WinPrAsn1EncOctetString(WinPrAsn1Encoder* enc, const WinPrAsn1_OctetString* octets) +{ + return WinPrAsn1EncMemoryChunk(enc, ER_TAG_OCTET_STRING, octets); +} + +size_t WinPrAsn1EncIA5String(WinPrAsn1Encoder* enc, WinPrAsn1_IA5STRING ia5) +{ + WinPrAsn1_MemoryChunk chunk; + WINPR_ASSERT(ia5); + chunk.data = (BYTE*)ia5; + chunk.len = strlen(ia5); + return WinPrAsn1EncMemoryChunk(enc, ER_TAG_IA5STRING, &chunk); +} + +size_t WinPrAsn1EncGeneralString(WinPrAsn1Encoder* enc, WinPrAsn1_STRING str) +{ + WinPrAsn1_MemoryChunk chunk; + WINPR_ASSERT(str); + chunk.data = (BYTE*)str; + chunk.len = strlen(str); + return WinPrAsn1EncMemoryChunk(enc, ER_TAG_GENERAL_STRING, &chunk); +} + +static size_t WinPrAsn1EncContextualMemoryChunk(WinPrAsn1Encoder* enc, BYTE wireType, + WinPrAsn1_tagId tagId, + const WinPrAsn1_MemoryChunk* mchunk) +{ + wStream s; + size_t len = 0; + size_t outLen = 0; + + WINPR_ASSERT(enc); + WINPR_ASSERT_VALID_TAG(tagId); + WINPR_ASSERT(mchunk); + len = 1 + lenBytes(mchunk->len) + mchunk->len; + + outLen = 1 + lenBytes(len) + len; + if (!asn1_getWriteStream(enc, outLen, &s)) + return 0; + + Stream_Write_UINT8(&s, ER_TAG_CONTEXTUAL | tagId); + asn1WriteLen(&s, len); + + Stream_Write_UINT8(&s, wireType); + asn1WriteLen(&s, mchunk->len); + Stream_Write(&s, mchunk->data, mchunk->len); + return outLen; +} + +size_t WinPrAsn1EncContextualOID(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_OID* oid) +{ + return WinPrAsn1EncContextualMemoryChunk(enc, ER_TAG_OBJECT_IDENTIFIER, tagId, oid); +} + +size_t WinPrAsn1EncContextualOctetString(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_OctetString* octets) +{ + return WinPrAsn1EncContextualMemoryChunk(enc, ER_TAG_OCTET_STRING, tagId, octets); +} + +size_t WinPrAsn1EncContextualIA5String(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + WinPrAsn1_IA5STRING ia5) +{ + WinPrAsn1_MemoryChunk chunk; + WINPR_ASSERT(ia5); + chunk.data = (BYTE*)ia5; + chunk.len = strlen(ia5); + + return WinPrAsn1EncContextualMemoryChunk(enc, ER_TAG_IA5STRING, tagId, &chunk); +} + +static void write2digit(wStream* s, UINT8 v) +{ + Stream_Write_UINT8(s, '0' + (v / 10)); + Stream_Write_UINT8(s, '0' + (v % 10)); +} + +size_t WinPrAsn1EncUtcTime(WinPrAsn1Encoder* enc, const WinPrAsn1_UTCTIME* utc) +{ + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(enc); + WINPR_ASSERT(utc); + WINPR_ASSERT(utc->year >= 2000); + + if (!asn1_getWriteStream(enc, 15, s)) + return 0; + + Stream_Write_UINT8(s, ER_TAG_UTCTIME); + Stream_Write_UINT8(s, 13); + + write2digit(s, utc->year - 2000); + write2digit(s, utc->month); + write2digit(s, utc->day); + write2digit(s, utc->hour); + write2digit(s, utc->minute); + write2digit(s, utc->second); + Stream_Write_UINT8(s, utc->tz); + return 15; +} + +size_t WinPrAsn1EncContextualUtcTime(WinPrAsn1Encoder* enc, WinPrAsn1_tagId tagId, + const WinPrAsn1_UTCTIME* utc) +{ + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(enc); + WINPR_ASSERT_VALID_TAG(tagId); + WINPR_ASSERT(utc); + WINPR_ASSERT(utc->year >= 2000); + + if (!asn1_getWriteStream(enc, 17, s)) + return 0; + + Stream_Write_UINT8(s, ER_TAG_CONTEXTUAL | tagId); + Stream_Write_UINT8(s, 15); + + Stream_Write_UINT8(s, ER_TAG_UTCTIME); + Stream_Write_UINT8(s, 13); + + write2digit(s, utc->year - 2000); + write2digit(s, utc->month); + write2digit(s, utc->day); + write2digit(s, utc->hour); + write2digit(s, utc->minute); + write2digit(s, utc->second); + Stream_Write_UINT8(s, utc->tz); + + return 17; +} + +BOOL WinPrAsn1EncStreamSize(WinPrAsn1Encoder* enc, size_t* s) +{ + size_t finalSize = 0; + + WINPR_ASSERT(enc); + WINPR_ASSERT(s); + + if (enc->freeContainerIndex != 0) + { + WLog_ERR(TAG, "some container have not been closed"); + return FALSE; + } + + for (size_t i = 0; i < enc->freeChunkId; i++) + finalSize += enc->chunks[i].used; + *s = finalSize; + return TRUE; +} + +BOOL WinPrAsn1EncToStream(WinPrAsn1Encoder* enc, wStream* s) +{ + size_t finalSize = 0; + + WINPR_ASSERT(enc); + WINPR_ASSERT(s); + + if (!WinPrAsn1EncStreamSize(enc, &finalSize)) + return FALSE; + + if (!Stream_EnsureRemainingCapacity(s, finalSize)) + return FALSE; + + for (size_t i = 0; i < enc->freeChunkId; i++) + { + BYTE* src = Stream_Buffer(enc->pool) + enc->chunks[i].poolOffset; + Stream_Write(s, src, enc->chunks[i].used); + } + + return TRUE; +} + +void WinPrAsn1Decoder_Init(WinPrAsn1Decoder* decoder, WinPrAsn1EncodingRule encoding, + wStream* source) +{ + WINPR_ASSERT(decoder); + WINPR_ASSERT(source); + + decoder->encoding = encoding; + memcpy(&decoder->source, source, sizeof(*source)); +} + +void WinPrAsn1Decoder_InitMem(WinPrAsn1Decoder* decoder, WinPrAsn1EncodingRule encoding, + const BYTE* source, size_t len) +{ + WINPR_ASSERT(decoder); + WINPR_ASSERT(source); + + decoder->encoding = encoding; + Stream_StaticConstInit(&decoder->source, source, len); +} + +BOOL WinPrAsn1DecPeekTag(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag) +{ + WINPR_ASSERT(dec); + WINPR_ASSERT(tag); + + if (Stream_GetRemainingLength(&dec->source) < 1) + return FALSE; + Stream_Peek(&dec->source, tag, 1); + return TRUE; +} + +static size_t readLen(wStream* s, size_t* len, BOOL derCheck) +{ + size_t retLen = 0; + size_t ret = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) + return 0; + + Stream_Read_UINT8(s, retLen); + ret++; + if (retLen & 0x80) + { + BYTE tmp = 0; + size_t nBytes = (retLen & 0x7f); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, nBytes)) + return 0; + + ret += nBytes; + for (retLen = 0; nBytes; nBytes--) + { + Stream_Read_UINT8(s, tmp); + retLen = (retLen << 8) + tmp; + } + + if (derCheck) + { + /* check that the DER rule is respected, and that length encoding is optimal */ + if (ret > 1 && retLen < 128) + return 0; + } + } + + *len = retLen; + return ret; +} + +static size_t readTagAndLen(WinPrAsn1Decoder* dec, wStream* s, WinPrAsn1_tag* tag, size_t* len) +{ + size_t lenBytes = 0; + + if (Stream_GetRemainingLength(s) < 1) + return 0; + + Stream_Read(s, tag, 1); + lenBytes = readLen(s, len, (dec->encoding == WINPR_ASN1_DER)); + if (lenBytes == 0) + return 0; + + return 1 + lenBytes; +} + +size_t WinPrAsn1DecReadTagAndLen(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, size_t* len) +{ + WINPR_ASSERT(dec); + WINPR_ASSERT(tag); + WINPR_ASSERT(len); + + return readTagAndLen(dec, &dec->source, tag, len); +} + +size_t WinPrAsn1DecPeekTagAndLen(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, size_t* len) +{ + wStream staticS; + wStream* s = &staticS; + + WINPR_ASSERT(dec); + + Stream_StaticConstInit(s, Stream_ConstPointer(&dec->source), + Stream_GetRemainingLength(&dec->source)); + return readTagAndLen(dec, s, tag, len); +} + +size_t WinPrAsn1DecReadTagLenValue(WinPrAsn1Decoder* dec, WinPrAsn1_tag* tag, size_t* len, + WinPrAsn1Decoder* value) +{ + size_t ret = 0; + WINPR_ASSERT(dec); + WINPR_ASSERT(tag); + WINPR_ASSERT(len); + WINPR_ASSERT(value); + + ret = readTagAndLen(dec, &dec->source, tag, len); + if (!ret) + return 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, *len)) + return 0; + + value->encoding = dec->encoding; + Stream_StaticInit(&value->source, Stream_Pointer(&dec->source), *len); + Stream_Seek(&dec->source, *len); + return ret + *len; +} + +size_t WinPrAsn1DecReadBoolean(WinPrAsn1Decoder* dec, WinPrAsn1_BOOL* target) +{ + BYTE v = 0; + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != ER_TAG_BOOLEAN) + return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len) || len != 1) + return 0; + + Stream_Read_UINT8(&dec->source, v); + *target = !!v; + return ret; +} + +static size_t WinPrAsn1DecReadIntegerLike(WinPrAsn1Decoder* dec, WinPrAsn1_tag expectedTag, + WinPrAsn1_INTEGER* target) +{ + signed char v = 0; + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != expectedTag) + return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len) || len > 4) + return 0; + + ret += len; + for (*target = 0; len; len--) + { + Stream_Read_INT8(&dec->source, v); + *target = (*target << 8) + v; + } + + /* TODO: check ber/der rules */ + return ret; +} + +size_t WinPrAsn1DecReadInteger(WinPrAsn1Decoder* dec, WinPrAsn1_INTEGER* target) +{ + return WinPrAsn1DecReadIntegerLike(dec, ER_TAG_INTEGER, target); +} + +size_t WinPrAsn1DecReadEnumerated(WinPrAsn1Decoder* dec, WinPrAsn1_ENUMERATED* target) +{ + return WinPrAsn1DecReadIntegerLike(dec, ER_TAG_ENUMERATED, target); +} + +static size_t WinPrAsn1DecReadMemoryChunkLike(WinPrAsn1Decoder* dec, WinPrAsn1_tag expectedTag, + WinPrAsn1_MemoryChunk* target, BOOL allocate) +{ + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != expectedTag) + return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len)) + return 0; + + ret += len; + + target->len = len; + if (allocate) + { + target->data = malloc(len); + if (!target->data) + return 0; + Stream_Read(&dec->source, target->data, len); + } + else + { + target->data = Stream_Pointer(&dec->source); + Stream_Seek(&dec->source, len); + } + + return ret; +} + +size_t WinPrAsn1DecReadOID(WinPrAsn1Decoder* dec, WinPrAsn1_OID* target, BOOL allocate) +{ + return WinPrAsn1DecReadMemoryChunkLike(dec, ER_TAG_OBJECT_IDENTIFIER, + (WinPrAsn1_MemoryChunk*)target, allocate); +} + +size_t WinPrAsn1DecReadOctetString(WinPrAsn1Decoder* dec, WinPrAsn1_OctetString* target, + BOOL allocate) +{ + return WinPrAsn1DecReadMemoryChunkLike(dec, ER_TAG_OCTET_STRING, (WinPrAsn1_OctetString*)target, + allocate); +} + +size_t WinPrAsn1DecReadIA5String(WinPrAsn1Decoder* dec, WinPrAsn1_IA5STRING* target) +{ + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + WinPrAsn1_IA5STRING s = NULL; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != ER_TAG_IA5STRING) + return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len)) + return 0; + + ret += len; + + s = malloc(len + 1); + if (!s) + return 0; + Stream_Read(&dec->source, s, len); + s[len] = 0; + *target = s; + return ret; +} + +size_t WinPrAsn1DecReadGeneralString(WinPrAsn1Decoder* dec, WinPrAsn1_STRING* target) +{ + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + WinPrAsn1_IA5STRING s = NULL; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != ER_TAG_GENERAL_STRING) + return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len)) + return 0; + + ret += len; + + s = malloc(len + 1); + if (!s) + return 0; + Stream_Read(&dec->source, s, len); + s[len] = 0; + *target = s; + return ret; +} + +static int read2digits(wStream* s) +{ + int ret = 0; + char c = 0; + + Stream_Read_UINT8(s, c); + if (c < '0' || c > '9') + return -1; + + ret = (c - '0') * 10; + + Stream_Read_UINT8(s, c); + if (c < '0' || c > '9') + return -1; + + ret += (c - '0'); + return ret; +} + +size_t WinPrAsn1DecReadUtcTime(WinPrAsn1Decoder* dec, WinPrAsn1_UTCTIME* target) +{ + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + int v = 0; + wStream sub; + wStream* s = ⊂ + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != ER_TAG_UTCTIME) + return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, &dec->source, len) || len < 12) + return 0; + + Stream_StaticConstInit(s, Stream_ConstPointer(&dec->source), len); + + v = read2digits(s); + if (v <= 0) + return 0; + target->year = 2000 + v; + + v = read2digits(s); + if (v <= 0) + return 0; + target->month = v; + + v = read2digits(s); + if (v <= 0) + return 0; + target->day = v; + + v = read2digits(s); + if (v <= 0) + return 0; + target->hour = v; + + v = read2digits(s); + if (v <= 0) + return 0; + target->minute = v; + + v = read2digits(s); + if (v <= 0) + return 0; + target->second = v; + + if (Stream_GetRemainingLength(s) >= 1) + { + Stream_Read_UINT8(s, target->tz); + } + + Stream_Seek(&dec->source, len); + ret += len; + + return ret; +} + +size_t WinPrAsn1DecReadNull(WinPrAsn1Decoder* dec) +{ + WinPrAsn1_tag tag = 0; + size_t len = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + + ret = readTagAndLen(dec, &dec->source, &tag, &len); + if (!ret || tag != ER_TAG_NULL || len) + return 0; + + return ret; +} + +static size_t readConstructed(WinPrAsn1Decoder* dec, wStream* s, WinPrAsn1_tag* tag, + WinPrAsn1Decoder* target) +{ + size_t len = 0; + size_t ret = 0; + + ret = readTagAndLen(dec, s, tag, &len); + if (!ret || !Stream_CheckAndLogRequiredLength(TAG, s, len)) + return 0; + + target->encoding = dec->encoding; + Stream_StaticConstInit(&target->source, Stream_ConstPointer(s), len); + Stream_Seek(s, len); + return ret + len; +} + +size_t WinPrAsn1DecReadApp(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, WinPrAsn1Decoder* target) +{ + WinPrAsn1_tag tag = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readConstructed(dec, &dec->source, &tag, target); + if ((tag & ER_TAG_APP) != ER_TAG_APP) + return 0; + + *tagId = (tag & ER_TAG_MASK); + return ret; +} + +size_t WinPrAsn1DecReadSequence(WinPrAsn1Decoder* dec, WinPrAsn1Decoder* target) +{ + WinPrAsn1_tag tag = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readConstructed(dec, &dec->source, &tag, target); + if (tag != ER_TAG_SEQUENCE) + return 0; + + return ret; +} + +size_t WinPrAsn1DecReadSet(WinPrAsn1Decoder* dec, WinPrAsn1Decoder* target) +{ + WinPrAsn1_tag tag = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(target); + + ret = readConstructed(dec, &dec->source, &tag, target); + if (tag != ER_TAG_SET) + return 0; + + return ret; +} + +static size_t readContextualTag(WinPrAsn1Decoder* dec, wStream* s, WinPrAsn1_tagId* tagId, + WinPrAsn1Decoder* ctxtDec) +{ + size_t ret = 0; + WinPrAsn1_tag ftag = 0; + + ret = readConstructed(dec, s, &ftag, ctxtDec); + if (!ret) + return 0; + + if ((ftag & ER_TAG_CONTEXTUAL) != ER_TAG_CONTEXTUAL) + return 0; + + *tagId = (ftag & ER_TAG_MASK); + return ret; +} + +size_t WinPrAsn1DecReadContextualTag(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, + WinPrAsn1Decoder* ctxtDec) +{ + WINPR_ASSERT(dec); + WINPR_ASSERT(tagId); + WINPR_ASSERT(ctxtDec); + + return readContextualTag(dec, &dec->source, tagId, ctxtDec); +} + +size_t WinPrAsn1DecPeekContextualTag(WinPrAsn1Decoder* dec, WinPrAsn1_tagId* tagId, + WinPrAsn1Decoder* ctxtDec) +{ + wStream staticS; + WINPR_ASSERT(dec); + + Stream_StaticConstInit(&staticS, Stream_ConstPointer(&dec->source), + Stream_GetRemainingLength(&dec->source)); + return readContextualTag(dec, &staticS, tagId, ctxtDec); +} + +static size_t readContextualHeader(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1Decoder* content) +{ + WinPrAsn1_tag ftag = 0; + size_t ret = 0; + + WINPR_ASSERT(dec); + WINPR_ASSERT(error); + WINPR_ASSERT(content); + + *error = TRUE; + ret = WinPrAsn1DecPeekContextualTag(dec, &ftag, content); + if (!ret) + return 0; + + if (ftag != tagId) + { + *error = FALSE; + return 0; + } + + *error = FALSE; + return ret; +} + +size_t WinPrAsn1DecReadContextualBool(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1_BOOL* target) +{ + size_t ret = 0; + size_t ret2 = 0; + WinPrAsn1Decoder content; + + ret = readContextualHeader(dec, tagId, error, &content); + if (!ret) + return 0; + + ret2 = WinPrAsn1DecReadBoolean(&content, target); + if (!ret2) + { + *error = TRUE; + return 0; + } + + Stream_Seek(&dec->source, ret); + return ret; +} + +size_t WinPrAsn1DecReadContextualInteger(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1_INTEGER* target) +{ + size_t ret = 0; + size_t ret2 = 0; + WinPrAsn1Decoder content; + + ret = readContextualHeader(dec, tagId, error, &content); + if (!ret) + return 0; + + ret2 = WinPrAsn1DecReadInteger(&content, target); + if (!ret2) + { + *error = TRUE; + return 0; + } + + Stream_Seek(&dec->source, ret); + return ret; +} + +size_t WinPrAsn1DecReadContextualOID(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1_OID* target, BOOL allocate) +{ + size_t ret = 0; + size_t ret2 = 0; + WinPrAsn1Decoder content; + + ret = readContextualHeader(dec, tagId, error, &content); + if (!ret) + return 0; + + ret2 = WinPrAsn1DecReadOID(&content, target, allocate); + if (!ret2) + { + *error = TRUE; + return 0; + } + + Stream_Seek(&dec->source, ret); + return ret; +} + +size_t WinPrAsn1DecReadContextualOctetString(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, + BOOL* error, WinPrAsn1_OctetString* target, + BOOL allocate) +{ + size_t ret = 0; + size_t ret2 = 0; + WinPrAsn1Decoder content; + + ret = readContextualHeader(dec, tagId, error, &content); + if (!ret) + return 0; + + ret2 = WinPrAsn1DecReadOctetString(&content, target, allocate); + if (!ret2) + { + *error = TRUE; + return 0; + } + + Stream_Seek(&dec->source, ret); + return ret; +} + +size_t WinPrAsn1DecReadContextualSequence(WinPrAsn1Decoder* dec, WinPrAsn1_tagId tagId, BOOL* error, + WinPrAsn1Decoder* target) +{ + size_t ret = 0; + size_t ret2 = 0; + WinPrAsn1Decoder content; + + ret = readContextualHeader(dec, tagId, error, &content); + if (!ret) + return 0; + + ret2 = WinPrAsn1DecReadSequence(&content, target); + if (!ret2) + { + *error = TRUE; + return 0; + } + + Stream_Seek(&dec->source, ret); + return ret; +} + +wStream WinPrAsn1DecGetStream(WinPrAsn1Decoder* dec) +{ + wStream s = { 0 }; + WINPR_ASSERT(dec); + + Stream_StaticConstInit(&s, Stream_ConstPointer(&dec->source), + Stream_GetRemainingLength(&dec->source)); + return s; +} diff --git a/winpr/libwinpr/utils/cmdline.c b/winpr/libwinpr/utils/cmdline.c new file mode 100644 index 0000000..3d93c0a --- /dev/null +++ b/winpr/libwinpr/utils/cmdline.c @@ -0,0 +1,850 @@ +/** + * WinPR: Windows Portable Runtime + * Command-Line Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "../log.h" + +#define TAG WINPR_TAG("commandline") + +/** + * Command-line syntax: some basic concepts: + * https://pythonconquerstheuniverse.wordpress.com/2010/07/25/command-line-syntax-some-basic-concepts/ + */ + +/** + * Command-Line Syntax: + * + * + * + * : '/' or '-' or ('+' | '-') + * + * : option, named argument, flag + * + * : ':' or '=' + * + * : argument value + * + */ + +static void log_error(DWORD flags, LPCSTR message, int index, LPCSTR argv) +{ + if ((flags & COMMAND_LINE_SILENCE_PARSER) == 0) + WLog_ERR(TAG, message, index, argv); +} + +int CommandLineParseArgumentsA(int argc, LPSTR* argv, COMMAND_LINE_ARGUMENT_A* options, DWORD flags, + void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter, + COMMAND_LINE_POST_FILTER_FN_A postFilter) +{ + int status = 0; + int count = 0; + size_t length = 0; + BOOL notescaped = FALSE; + const char* sigil = NULL; + size_t sigil_length = 0; + char* keyword = NULL; + size_t keyword_length = 0; + SSIZE_T keyword_index = 0; + char* separator = NULL; + char* value = NULL; + int toggle = 0; + + if (!argv) + return status; + + if (argc == 1) + { + if (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD) + status = 0; + else + status = COMMAND_LINE_STATUS_PRINT_HELP; + + return status; + } + + for (int i = 1; i < argc; i++) + { + BOOL found = FALSE; + BOOL escaped = TRUE; + + if (preFilter) + { + count = preFilter(context, i, argc, argv); + + if (count < 0) + { + log_error(flags, "Failed for index %d [%s]: PreFilter rule could not be applied", i, + argv[i]); + status = COMMAND_LINE_ERROR; + return status; + } + + if (count > 0) + { + i += (count - 1); + continue; + } + } + + sigil = argv[i]; + length = strlen(argv[i]); + + if ((sigil[0] == '/') && (flags & COMMAND_LINE_SIGIL_SLASH)) + { + sigil_length = 1; + } + else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_DASH)) + { + sigil_length = 1; + + if (length > 2) + { + if ((sigil[1] == '-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH)) + sigil_length = 2; + } + } + else if ((sigil[0] == '+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS)) + { + sigil_length = 1; + } + else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS)) + { + sigil_length = 1; + } + else if (flags & COMMAND_LINE_SIGIL_NONE) + { + sigil_length = 0; + } + else if (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED) + { + if (notescaped) + { + log_error(flags, "Failed at index %d [%s]: Unescaped sigil", i, argv[i]); + return COMMAND_LINE_ERROR; + } + + sigil_length = 0; + escaped = FALSE; + notescaped = TRUE; + } + else + { + log_error(flags, "Failed at index %d [%s]: Invalid sigil", i, argv[i]); + return COMMAND_LINE_ERROR; + } + + if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE) || + (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED)) + { + if (length < (sigil_length + 1)) + { + if ((flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD)) + continue; + + return COMMAND_LINE_ERROR_NO_KEYWORD; + } + + keyword_index = sigil_length; + keyword = &argv[i][keyword_index]; + toggle = -1; + + if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE) + { + if (strncmp(keyword, "enable-", 7) == 0) + { + toggle = TRUE; + keyword_index += 7; + keyword = &argv[i][keyword_index]; + } + else if (strncmp(keyword, "disable-", 8) == 0) + { + toggle = FALSE; + keyword_index += 8; + keyword = &argv[i][keyword_index]; + } + } + + separator = NULL; + + if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator)) + separator = strchr(keyword, ':'); + + if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator)) + separator = strchr(keyword, '='); + + if (separator) + { + SSIZE_T separator_index = (separator - argv[i]); + SSIZE_T value_index = separator_index + 1; + keyword_length = (separator - keyword); + value = &argv[i][value_index]; + } + else + { + keyword_length = (length - keyword_index); + value = NULL; + } + + if (!escaped) + continue; + + for (int j = 0; options[j].Name != NULL; j++) + { + COMMAND_LINE_ARGUMENT_A* cur = &options[j]; + BOOL match = FALSE; + + if (strncmp(cur->Name, keyword, keyword_length) == 0) + { + if (strlen(cur->Name) == keyword_length) + match = TRUE; + } + + if ((!match) && (cur->Alias != NULL)) + { + if (strncmp(cur->Alias, keyword, keyword_length) == 0) + { + if (strlen(cur->Alias) == keyword_length) + match = TRUE; + } + } + + if (!match) + continue; + + found = match; + cur->Index = i; + + if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc)) + { + BOOL argument = 0; + int value_present = 1; + + if (flags & COMMAND_LINE_SIGIL_DASH) + { + if (strncmp(argv[i + 1], "-", 1) == 0) + value_present = 0; + } + + if (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH) + { + if (strncmp(argv[i + 1], "--", 2) == 0) + value_present = 0; + } + + if (flags & COMMAND_LINE_SIGIL_SLASH) + { + if (strncmp(argv[i + 1], "/", 1) == 0) + value_present = 0; + } + + if ((cur->Flags & COMMAND_LINE_VALUE_REQUIRED) || + (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL)) + argument = TRUE; + else + argument = FALSE; + + if (value_present && argument) + { + i++; + value = argv[i]; + } + else if (!value_present && (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL)) + { + value = NULL; + } + else if (!value_present && argument) + { + log_error(flags, "Failed at index %d [%s]: Argument required", i, argv[i]); + return COMMAND_LINE_ERROR; + } + } + + if (!(flags & COMMAND_LINE_SEPARATOR_SPACE)) + { + if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG)) + { + log_error(flags, "Failed at index %d [%s]: Unexpected value", i, argv[i]); + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; + } + } + else + { + if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG)) + { + i--; + value = NULL; + } + } + + if (!value && (cur->Flags & COMMAND_LINE_VALUE_REQUIRED)) + { + log_error(flags, "Failed at index %d [%s]: Missing value", i, argv[i]); + status = COMMAND_LINE_ERROR_MISSING_VALUE; + return status; + } + + cur->Flags |= COMMAND_LINE_ARGUMENT_PRESENT; + + if (value) + { + if (!(cur->Flags & (COMMAND_LINE_VALUE_OPTIONAL | COMMAND_LINE_VALUE_REQUIRED))) + { + log_error(flags, "Failed at index %d [%s]: Unexpected value", i, argv[i]); + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; + } + + cur->Value = value; + cur->Flags |= COMMAND_LINE_VALUE_PRESENT; + } + else + { + if (cur->Flags & COMMAND_LINE_VALUE_FLAG) + { + cur->Value = (LPSTR)1; + cur->Flags |= COMMAND_LINE_VALUE_PRESENT; + } + else if (cur->Flags & COMMAND_LINE_VALUE_BOOL) + { + if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE) + { + if (toggle == -1) + cur->Value = BoolValueTrue; + else if (!toggle) + cur->Value = BoolValueFalse; + else + cur->Value = BoolValueTrue; + } + else + { + if (sigil[0] == '+') + cur->Value = BoolValueTrue; + else if (sigil[0] == '-') + cur->Value = BoolValueFalse; + else + cur->Value = BoolValueTrue; + } + + cur->Flags |= COMMAND_LINE_VALUE_PRESENT; + } + } + + if (postFilter) + { + count = postFilter(context, &options[j]); + + if (count < 0) + { + log_error(flags, + "Failed at index %d [%s]: PostFilter rule could not be applied", + i, argv[i]); + status = COMMAND_LINE_ERROR; + return status; + } + } + + if (cur->Flags & COMMAND_LINE_PRINT) + return COMMAND_LINE_STATUS_PRINT; + else if (cur->Flags & COMMAND_LINE_PRINT_HELP) + return COMMAND_LINE_STATUS_PRINT_HELP; + else if (cur->Flags & COMMAND_LINE_PRINT_VERSION) + return COMMAND_LINE_STATUS_PRINT_VERSION; + else if (cur->Flags & COMMAND_LINE_PRINT_BUILDCONFIG) + return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG; + } + + if (!found && (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD) == 0) + { + log_error(flags, "Failed at index %d [%s]: Unexpected keyword", i, argv[i]); + return COMMAND_LINE_ERROR_NO_KEYWORD; + } + } + } + + return status; +} + +int CommandLineParseArgumentsW(int argc, LPWSTR* argv, COMMAND_LINE_ARGUMENT_W* options, + DWORD flags, void* context, COMMAND_LINE_PRE_FILTER_FN_W preFilter, + COMMAND_LINE_POST_FILTER_FN_W postFilter) +{ + return 0; +} + +int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options) +{ + for (size_t i = 0; options[i].Name != NULL; i++) + { + options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK; + options[i].Value = NULL; + } + + return 0; +} + +int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options) +{ + for (int i = 0; options[i].Name != NULL; i++) + { + options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK; + options[i].Value = NULL; + } + + return 0; +} + +const COMMAND_LINE_ARGUMENT_A* CommandLineFindArgumentA(const COMMAND_LINE_ARGUMENT_A* options, + LPCSTR Name) +{ + WINPR_ASSERT(options); + WINPR_ASSERT(Name); + + for (size_t i = 0; options[i].Name != NULL; i++) + { + if (strcmp(options[i].Name, Name) == 0) + return &options[i]; + + if (options[i].Alias != NULL) + { + if (strcmp(options[i].Alias, Name) == 0) + return &options[i]; + } + } + + return NULL; +} + +const COMMAND_LINE_ARGUMENT_W* CommandLineFindArgumentW(const COMMAND_LINE_ARGUMENT_W* options, + LPCWSTR Name) +{ + WINPR_ASSERT(options); + WINPR_ASSERT(Name); + + for (size_t i = 0; options[i].Name != NULL; i++) + { + if (_wcscmp(options[i].Name, Name) == 0) + return &options[i]; + + if (options[i].Alias != NULL) + { + if (_wcscmp(options[i].Alias, Name) == 0) + return &options[i]; + } + } + + return NULL; +} + +const COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(const COMMAND_LINE_ARGUMENT_A* argument) +{ + const COMMAND_LINE_ARGUMENT_A* nextArgument = NULL; + + if (!argument || !argument->Name) + return NULL; + + nextArgument = &argument[1]; + + if (nextArgument->Name == NULL) + return NULL; + + return nextArgument; +} + +static int is_quoted(char c) +{ + switch (c) + { + case '"': + return 1; + case '\'': + return -1; + default: + return 0; + } +} + +static size_t get_element_count(const char* list, BOOL* failed, BOOL fullquoted) +{ + size_t count = 0; + int quoted = 0; + BOOL finished = FALSE; + BOOL first = TRUE; + const char* it = list; + + if (!list) + return 0; + if (strlen(list) == 0) + return 0; + + while (!finished) + { + BOOL nextFirst = FALSE; + switch (*it) + { + case '\0': + if (quoted != 0) + { + WLog_ERR(TAG, "Invalid argument (missing closing quote) '%s'", list); + *failed = TRUE; + return 0; + } + finished = TRUE; + break; + case '\'': + case '"': + if (!fullquoted) + { + int now = is_quoted(*it); + if ((quoted == 0) && !first) + { + WLog_ERR(TAG, "Invalid argument (misplaced quote) '%s'", list); + *failed = TRUE; + return 0; + } + + if (now == quoted) + quoted = 0; + else if (quoted == 0) + quoted = now; + } + break; + case ',': + if (first) + { + WLog_ERR(TAG, "Invalid argument (empty list elements) '%s'", list); + *failed = TRUE; + return 0; + } + if (quoted == 0) + { + nextFirst = TRUE; + count++; + } + break; + default: + break; + } + + first = nextFirst; + it++; + } + return count + 1; +} + +static char* get_next_comma(char* string, BOOL fullquoted) +{ + const char* log = string; + int quoted = 0; + BOOL first = TRUE; + + WINPR_ASSERT(string); + + while (TRUE) + { + switch (*string) + { + case '\0': + if (quoted != 0) + WLog_ERR(TAG, "Invalid quoted argument '%s'", log); + return NULL; + + case '\'': + case '"': + if (!fullquoted) + { + int now = is_quoted(*string); + if ((quoted == 0) && !first) + { + WLog_ERR(TAG, "Invalid quoted argument '%s'", log); + return NULL; + } + if (now == quoted) + quoted = 0; + else if (quoted == 0) + quoted = now; + } + break; + + case ',': + if (first) + { + WLog_ERR(TAG, "Invalid argument (empty list elements) '%s'", log); + return NULL; + } + if (quoted == 0) + return string; + break; + + default: + break; + } + first = FALSE; + string++; + } + + return NULL; +} + +static BOOL is_valid_fullquoted(const char* string) +{ + char cur = '\0'; + char last = '\0'; + const char quote = *string++; + + /* We did not start with a quote. */ + if (is_quoted(quote) == 0) + return FALSE; + + while ((cur = *string++) != '\0') + { + /* A quote is found. */ + if (cur == quote) + { + /* If the quote was escaped, it is valid. */ + if (last != '\\') + { + /* Only allow unescaped quote as last character in string. */ + if (*string != '\0') + return FALSE; + } + /* If the last quote in the string is escaped, it is wrong. */ + else if (*string != '\0') + return FALSE; + } + last = cur; + } + + /* The string did not terminate with the same quote as it started. */ + if (last != quote) + return FALSE; + return TRUE; +} + +char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list, size_t* count) +{ + char** p = NULL; + char* str = NULL; + size_t nArgs = 0; + size_t prefix = 0; + size_t len = 0; + size_t namelen = 0; + BOOL failed = FALSE; + char* copy = NULL; + char* unquoted = NULL; + BOOL fullquoted = FALSE; + + BOOL success = FALSE; + if (count == NULL) + goto fail; + + *count = 0; + if (list) + { + int start = 0; + int end = 0; + unquoted = copy = _strdup(list); + if (!copy) + goto fail; + + len = strlen(unquoted); + if (len > 0) + { + start = is_quoted(unquoted[0]); + end = is_quoted(unquoted[len - 1]); + + if ((start != 0) && (end != 0)) + { + if (start != end) + { + WLog_ERR(TAG, "invalid argument (quote mismatch) '%s'", list); + goto fail; + } + if (!is_valid_fullquoted(unquoted)) + goto fail; + unquoted[len - 1] = '\0'; + unquoted++; + len -= 2; + fullquoted = TRUE; + } + } + } + + *count = get_element_count(unquoted, &failed, fullquoted); + if (failed) + goto fail; + + if (*count == 0) + { + if (!name) + goto fail; + else + { + size_t clen = strlen(name); + p = (char**)calloc(2UL + clen, sizeof(char*)); + + if (p) + { + char* dst = (char*)&p[1]; + p[0] = dst; + sprintf_s(dst, clen + 1, "%s", name); + *count = 1; + success = TRUE; + goto fail; + } + } + } + + nArgs = *count; + + if (name) + nArgs++; + + prefix = (nArgs + 1UL) * sizeof(char*); + if (name) + namelen = strlen(name); + p = (char**)calloc(len + prefix + 1 + namelen + 1, sizeof(char*)); + + if (!p) + goto fail; + + str = &((char*)p)[prefix]; + memcpy(str, unquoted, len); + + if (name) + { + char* namestr = &((char*)p)[prefix + len + 1]; + memcpy(namestr, name, namelen); + + p[0] = namestr; + } + + for (size_t index = name ? 1 : 0; index < nArgs; index++) + { + char* ptr = str; + const int quote = is_quoted(*ptr); + char* comma = get_next_comma(str, fullquoted); + + if ((quote != 0) && !fullquoted) + ptr++; + + p[index] = ptr; + + if (comma) + { + char* last = comma - 1; + const int lastQuote = is_quoted(*last); + + if (!fullquoted) + { + if (lastQuote != quote) + { + WLog_ERR(TAG, "invalid argument (quote mismatch) '%s'", list); + goto fail; + } + else if (lastQuote != 0) + *last = '\0'; + } + *comma = '\0'; + + str = comma + 1; + } + else if (quote) + { + char* end = strrchr(ptr, '"'); + if (!end) + goto fail; + *end = '\0'; + } + } + + *count = nArgs; + success = TRUE; +fail: + free(copy); + if (!success) + { + if (count) + *count = 0; + free(p); + return NULL; + } + return p; +} + +char** CommandLineParseCommaSeparatedValues(const char* list, size_t* count) +{ + return CommandLineParseCommaSeparatedValuesEx(NULL, list, count); +} + +char* CommandLineToCommaSeparatedValues(int argc, char* argv[]) +{ + return CommandLineToCommaSeparatedValuesEx(argc, argv, NULL, 0); +} + +static const char* filtered(const char* arg, const char* filters[], size_t number) +{ + if (number == 0) + return arg; + for (size_t x = 0; x < number; x++) + { + const char* filter = filters[x]; + size_t len = strlen(filter); + if (_strnicmp(arg, filter, len) == 0) + return &arg[len]; + } + return NULL; +} + +char* CommandLineToCommaSeparatedValuesEx(int argc, char* argv[], const char* filters[], + size_t number) +{ + char* str = NULL; + size_t offset = 0; + size_t size = argc + 1; + if ((argc <= 0) || !argv) + return NULL; + + for (int x = 0; x < argc; x++) + size += strlen(argv[x]); + + str = calloc(size, sizeof(char)); + if (!str) + return NULL; + for (int x = 0; x < argc; x++) + { + int rc = 0; + const char* arg = filtered(argv[x], filters, number); + if (!arg) + continue; + rc = _snprintf(&str[offset], size - offset, "%s,", arg); + if (rc <= 0) + { + free(str); + return NULL; + } + offset += (size_t)rc; + } + if (offset > 0) + str[offset - 1] = '\0'; + return str; +} diff --git a/winpr/libwinpr/utils/collections/ArrayList.c b/winpr/libwinpr/utils/collections/ArrayList.c new file mode 100644 index 0000000..5595edb --- /dev/null +++ b/winpr/libwinpr/utils/collections/ArrayList.c @@ -0,0 +1,604 @@ +/** + * WinPR: Windows Portable Runtime + * System.Collections.ArrayList + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include + +#if defined(_WIN32) && (_MSC_VER < 1800) && !defined(__MINGW32__) +#define va_copy(dest, src) (dest = src) +#endif + +struct s_wArrayList +{ + size_t capacity; + size_t growthFactor; + BOOL synchronized; + + size_t size; + void** array; + CRITICAL_SECTION lock; + + wObject object; +}; + +/** + * C equivalent of the C# ArrayList Class: + * http://msdn.microsoft.com/en-us/library/system.collections.arraylist.aspx + */ + +/** + * Properties + */ + +/** + * Gets or sets the number of elements that the ArrayList can contain. + */ + +size_t ArrayList_Capacity(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + return arrayList->capacity; +} + +/** + * Gets the number of elements actually contained in the ArrayList. + */ + +size_t ArrayList_Count(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + return arrayList->size; +} + +/** + * Gets the internal list of items contained in the ArrayList. + */ + +size_t ArrayList_Items(wArrayList* arrayList, ULONG_PTR** ppItems) +{ + WINPR_ASSERT(arrayList); + *ppItems = (ULONG_PTR*)arrayList->array; + return arrayList->size; +} + +/** + * Gets a value indicating whether the ArrayList has a fixed size. + */ + +BOOL ArrayList_IsFixedSized(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + return FALSE; +} + +/** + * Gets a value indicating whether the ArrayList is read-only. + */ + +BOOL ArrayList_IsReadOnly(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + return FALSE; +} + +/** + * Gets a value indicating whether access to the ArrayList is synchronized (thread safe). + */ + +BOOL ArrayList_IsSynchronized(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + return arrayList->synchronized; +} + +/** + * Lock access to the ArrayList + */ + +static void ArrayList_Lock_Conditional(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + if (arrayList->synchronized) + EnterCriticalSection(&arrayList->lock); +} + +void ArrayList_Lock(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + EnterCriticalSection(&arrayList->lock); +} + +/** + * Unlock access to the ArrayList + */ + +static void ArrayList_Unlock_Conditional(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + if (arrayList->synchronized) + LeaveCriticalSection(&arrayList->lock); +} + +void ArrayList_Unlock(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + LeaveCriticalSection(&arrayList->lock); +} + +/** + * Gets the element at the specified index. + */ + +void* ArrayList_GetItem(wArrayList* arrayList, size_t index) +{ + void* obj = NULL; + + WINPR_ASSERT(arrayList); + if (index < arrayList->size) + { + obj = arrayList->array[index]; + } + + return obj; +} + +/** + * Sets the element at the specified index. + */ + +BOOL ArrayList_SetItem(wArrayList* arrayList, size_t index, const void* obj) +{ + WINPR_ASSERT(arrayList); + if (index >= arrayList->size) + return FALSE; + + if (arrayList->object.fnObjectNew) + { + arrayList->array[index] = arrayList->object.fnObjectNew(obj); + if (obj && !arrayList->array[index]) + return FALSE; + } + else + { + union + { + const void* cpv; + void* pv; + } cnv; + cnv.cpv = obj; + arrayList->array[index] = cnv.pv; + } + return TRUE; +} + +/** + * Methods + */ +static BOOL ArrayList_EnsureCapacity(wArrayList* arrayList, size_t count) +{ + WINPR_ASSERT(arrayList); + WINPR_ASSERT(count > 0); + + if (arrayList->size + count > arrayList->capacity) + { + void** newArray = NULL; + size_t newCapacity = arrayList->capacity * arrayList->growthFactor; + if (newCapacity < arrayList->size + count) + newCapacity = arrayList->size + count; + + newArray = (void**)realloc(arrayList->array, sizeof(void*) * newCapacity); + + if (!newArray) + return FALSE; + + arrayList->array = newArray; + arrayList->capacity = newCapacity; + } + + return TRUE; +} +/** + * Shift a section of the list. + */ + +static BOOL ArrayList_Shift(wArrayList* arrayList, size_t index, SSIZE_T count) +{ + WINPR_ASSERT(arrayList); + if (count > 0) + { + if (!ArrayList_EnsureCapacity(arrayList, count)) + return FALSE; + + MoveMemory(&arrayList->array[index + count], &arrayList->array[index], + (arrayList->size - index) * sizeof(void*)); + arrayList->size += count; + } + else if (count < 0) + { + INT64 chunk = arrayList->size - index + count; + + if (chunk > 0) + MoveMemory(&arrayList->array[index], &arrayList->array[index - count], + (size_t)chunk * sizeof(void*)); + + arrayList->size += count; + } + + return TRUE; +} + +/** + * Removes all elements from the ArrayList. + */ + +void ArrayList_Clear(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + for (size_t index = 0; index < arrayList->size; index++) + { + if (arrayList->object.fnObjectFree) + arrayList->object.fnObjectFree(arrayList->array[index]); + + arrayList->array[index] = NULL; + } + + arrayList->size = 0; + + ArrayList_Unlock_Conditional(arrayList); +} + +/** + * Determines whether an element is in the ArrayList. + */ + +BOOL ArrayList_Contains(wArrayList* arrayList, const void* obj) +{ + BOOL rc = FALSE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + for (size_t index = 0; index < arrayList->size; index++) + { + rc = arrayList->object.fnObjectEquals(arrayList->array[index], obj); + + if (rc) + break; + } + + ArrayList_Unlock_Conditional(arrayList); + + return rc; +} + +#if defined(WITH_WINPR_DEPRECATED) +int ArrayList_Add(wArrayList* arrayList, const void* obj) +{ + WINPR_ASSERT(arrayList); + if (!ArrayList_Append(arrayList, obj)) + return -1; + return (int)ArrayList_Count(arrayList) - 1; +} +#endif + +/** + * Adds an object to the end of the ArrayList. + */ + +BOOL ArrayList_Append(wArrayList* arrayList, const void* obj) +{ + size_t index = 0; + BOOL rc = FALSE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + if (!ArrayList_EnsureCapacity(arrayList, 1)) + goto out; + + index = arrayList->size++; + rc = ArrayList_SetItem(arrayList, index, obj); +out: + + ArrayList_Unlock_Conditional(arrayList); + + return rc; +} + +/* + * Inserts an element into the ArrayList at the specified index. + */ + +BOOL ArrayList_Insert(wArrayList* arrayList, size_t index, const void* obj) +{ + BOOL ret = TRUE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + if (index < arrayList->size) + { + if (!ArrayList_Shift(arrayList, index, 1)) + { + ret = FALSE; + } + else + { + ArrayList_SetItem(arrayList, index, obj); + } + } + + ArrayList_Unlock_Conditional(arrayList); + + return ret; +} + +/** + * Removes the first occurrence of a specific object from the ArrayList. + */ + +BOOL ArrayList_Remove(wArrayList* arrayList, const void* obj) +{ + BOOL found = FALSE; + BOOL ret = TRUE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + size_t index = 0; + for (; index < arrayList->size; index++) + { + if (arrayList->object.fnObjectEquals(arrayList->array[index], obj)) + { + found = TRUE; + break; + } + } + + if (found) + { + if (arrayList->object.fnObjectFree) + arrayList->object.fnObjectFree(arrayList->array[index]); + + ret = ArrayList_Shift(arrayList, index, -1); + } + + ArrayList_Unlock_Conditional(arrayList); + + return ret; +} + +/** + * Removes the element at the specified index of the ArrayList. + */ + +BOOL ArrayList_RemoveAt(wArrayList* arrayList, size_t index) +{ + BOOL ret = TRUE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + if (index < arrayList->size) + { + if (arrayList->object.fnObjectFree) + arrayList->object.fnObjectFree(arrayList->array[index]); + + ret = ArrayList_Shift(arrayList, index, -1); + } + + ArrayList_Unlock_Conditional(arrayList); + + return ret; +} + +/** + * Searches for the specified Object and returns the zero-based index of the first occurrence within + * the entire ArrayList. + * + * Searches for the specified Object and returns the zero-based index of the last occurrence within + * the range of elements in the ArrayList that extends from the first element to the specified + * index. + * + * Searches for the specified Object and returns the zero-based index of the last occurrence within + * the range of elements in the ArrayList that contains the specified number of elements and ends at + * the specified index. + */ + +SSIZE_T ArrayList_IndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex, SSIZE_T count) +{ + SSIZE_T sindex = 0; + SSIZE_T cindex = 0; + BOOL found = FALSE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + sindex = (size_t)startIndex; + if (startIndex < 0) + sindex = 0; + + cindex = (size_t)count; + if (count < 0) + cindex = arrayList->size; + + SSIZE_T index = sindex; + for (; index < sindex + cindex; index++) + { + if (arrayList->object.fnObjectEquals(arrayList->array[index], obj)) + { + found = TRUE; + break; + } + } + + if (!found) + index = -1; + + ArrayList_Unlock_Conditional(arrayList); + + return index; +} + +/** + * Searches for the specified Object and returns the zero-based index of the last occurrence within + * the entire ArrayList. + * + * Searches for the specified Object and returns the zero-based index of the last occurrence within + * the range of elements in the ArrayList that extends from the first element to the specified + * index. + * + * Searches for the specified Object and returns the zero-based index of the last occurrence within + * the range of elements in the ArrayList that contains the specified number of elements and ends at + * the specified index. + */ + +SSIZE_T ArrayList_LastIndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex, + SSIZE_T count) +{ + SSIZE_T sindex = 0; + SSIZE_T cindex = 0; + BOOL found = FALSE; + + WINPR_ASSERT(arrayList); + ArrayList_Lock_Conditional(arrayList); + + sindex = (size_t)startIndex; + if (startIndex < 0) + sindex = 0; + + cindex = (size_t)count; + if (count < 0) + cindex = arrayList->size; + + SSIZE_T index = sindex + cindex; + for (; index > sindex; index--) + { + if (arrayList->object.fnObjectEquals(arrayList->array[index - 1], obj)) + { + found = TRUE; + break; + } + } + + if (!found) + index = -1; + + ArrayList_Unlock_Conditional(arrayList); + + return index; +} + +static BOOL ArrayList_DefaultCompare(const void* objA, const void* objB) +{ + return objA == objB ? TRUE : FALSE; +} + +wObject* ArrayList_Object(wArrayList* arrayList) +{ + WINPR_ASSERT(arrayList); + return &arrayList->object; +} + +BOOL ArrayList_ForEach(wArrayList* arrayList, ArrayList_ForEachFkt fkt, ...) +{ + BOOL rc = 0; + va_list ap; + va_start(ap, fkt); + rc = ArrayList_ForEachAP(arrayList, fkt, ap); + va_end(ap); + + return rc; +} + +BOOL ArrayList_ForEachAP(wArrayList* arrayList, ArrayList_ForEachFkt fkt, va_list ap) +{ + BOOL rc = FALSE; + va_list cap; + + WINPR_ASSERT(arrayList); + WINPR_ASSERT(fkt); + + ArrayList_Lock_Conditional(arrayList); + size_t count = ArrayList_Count(arrayList); + for (size_t index = 0; index < count; index++) + { + BOOL rs = 0; + void* obj = ArrayList_GetItem(arrayList, index); + va_copy(cap, ap); + rs = fkt(obj, index, cap); + va_end(cap); + if (!rs) + goto fail; + } + rc = TRUE; +fail: + ArrayList_Unlock_Conditional(arrayList); + return rc; +} + +/** + * Construction, Destruction + */ + +wArrayList* ArrayList_New(BOOL synchronized) +{ + wObject* obj = NULL; + wArrayList* arrayList = NULL; + arrayList = (wArrayList*)calloc(1, sizeof(wArrayList)); + + if (!arrayList) + return NULL; + + arrayList->synchronized = synchronized; + arrayList->growthFactor = 2; + obj = ArrayList_Object(arrayList); + if (!obj) + goto fail; + obj->fnObjectEquals = ArrayList_DefaultCompare; + if (!ArrayList_EnsureCapacity(arrayList, 32)) + goto fail; + + InitializeCriticalSectionAndSpinCount(&arrayList->lock, 4000); + return arrayList; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + ArrayList_Free(arrayList); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void ArrayList_Free(wArrayList* arrayList) +{ + if (!arrayList) + return; + + ArrayList_Clear(arrayList); + DeleteCriticalSection(&arrayList->lock); + free(arrayList->array); + free(arrayList); +} diff --git a/winpr/libwinpr/utils/collections/BitStream.c b/winpr/libwinpr/utils/collections/BitStream.c new file mode 100644 index 0000000..e57af94 --- /dev/null +++ b/winpr/libwinpr/utils/collections/BitStream.c @@ -0,0 +1,176 @@ +/** + * WinPR: Windows Portable Runtime + * BitStream + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +static const char* BYTE_BIT_STRINGS_LSB[256] = { + "00000000", "00000001", "00000010", "00000011", "00000100", "00000101", "00000110", "00000111", + "00001000", "00001001", "00001010", "00001011", "00001100", "00001101", "00001110", "00001111", + "00010000", "00010001", "00010010", "00010011", "00010100", "00010101", "00010110", "00010111", + "00011000", "00011001", "00011010", "00011011", "00011100", "00011101", "00011110", "00011111", + "00100000", "00100001", "00100010", "00100011", "00100100", "00100101", "00100110", "00100111", + "00101000", "00101001", "00101010", "00101011", "00101100", "00101101", "00101110", "00101111", + "00110000", "00110001", "00110010", "00110011", "00110100", "00110101", "00110110", "00110111", + "00111000", "00111001", "00111010", "00111011", "00111100", "00111101", "00111110", "00111111", + "01000000", "01000001", "01000010", "01000011", "01000100", "01000101", "01000110", "01000111", + "01001000", "01001001", "01001010", "01001011", "01001100", "01001101", "01001110", "01001111", + "01010000", "01010001", "01010010", "01010011", "01010100", "01010101", "01010110", "01010111", + "01011000", "01011001", "01011010", "01011011", "01011100", "01011101", "01011110", "01011111", + "01100000", "01100001", "01100010", "01100011", "01100100", "01100101", "01100110", "01100111", + "01101000", "01101001", "01101010", "01101011", "01101100", "01101101", "01101110", "01101111", + "01110000", "01110001", "01110010", "01110011", "01110100", "01110101", "01110110", "01110111", + "01111000", "01111001", "01111010", "01111011", "01111100", "01111101", "01111110", "01111111", + "10000000", "10000001", "10000010", "10000011", "10000100", "10000101", "10000110", "10000111", + "10001000", "10001001", "10001010", "10001011", "10001100", "10001101", "10001110", "10001111", + "10010000", "10010001", "10010010", "10010011", "10010100", "10010101", "10010110", "10010111", + "10011000", "10011001", "10011010", "10011011", "10011100", "10011101", "10011110", "10011111", + "10100000", "10100001", "10100010", "10100011", "10100100", "10100101", "10100110", "10100111", + "10101000", "10101001", "10101010", "10101011", "10101100", "10101101", "10101110", "10101111", + "10110000", "10110001", "10110010", "10110011", "10110100", "10110101", "10110110", "10110111", + "10111000", "10111001", "10111010", "10111011", "10111100", "10111101", "10111110", "10111111", + "11000000", "11000001", "11000010", "11000011", "11000100", "11000101", "11000110", "11000111", + "11001000", "11001001", "11001010", "11001011", "11001100", "11001101", "11001110", "11001111", + "11010000", "11010001", "11010010", "11010011", "11010100", "11010101", "11010110", "11010111", + "11011000", "11011001", "11011010", "11011011", "11011100", "11011101", "11011110", "11011111", + "11100000", "11100001", "11100010", "11100011", "11100100", "11100101", "11100110", "11100111", + "11101000", "11101001", "11101010", "11101011", "11101100", "11101101", "11101110", "11101111", + "11110000", "11110001", "11110010", "11110011", "11110100", "11110101", "11110110", "11110111", + "11111000", "11111001", "11111010", "11111011", "11111100", "11111101", "11111110", "11111111" +}; + +static const char* BYTE_BIT_STRINGS_MSB[256] = { + "00000000", "10000000", "01000000", "11000000", "00100000", "10100000", "01100000", "11100000", + "00010000", "10010000", "01010000", "11010000", "00110000", "10110000", "01110000", "11110000", + "00001000", "10001000", "01001000", "11001000", "00101000", "10101000", "01101000", "11101000", + "00011000", "10011000", "01011000", "11011000", "00111000", "10111000", "01111000", "11111000", + "00000100", "10000100", "01000100", "11000100", "00100100", "10100100", "01100100", "11100100", + "00010100", "10010100", "01010100", "11010100", "00110100", "10110100", "01110100", "11110100", + "00001100", "10001100", "01001100", "11001100", "00101100", "10101100", "01101100", "11101100", + "00011100", "10011100", "01011100", "11011100", "00111100", "10111100", "01111100", "11111100", + "00000010", "10000010", "01000010", "11000010", "00100010", "10100010", "01100010", "11100010", + "00010010", "10010010", "01010010", "11010010", "00110010", "10110010", "01110010", "11110010", + "00001010", "10001010", "01001010", "11001010", "00101010", "10101010", "01101010", "11101010", + "00011010", "10011010", "01011010", "11011010", "00111010", "10111010", "01111010", "11111010", + "00000110", "10000110", "01000110", "11000110", "00100110", "10100110", "01100110", "11100110", + "00010110", "10010110", "01010110", "11010110", "00110110", "10110110", "01110110", "11110110", + "00001110", "10001110", "01001110", "11001110", "00101110", "10101110", "01101110", "11101110", + "00011110", "10011110", "01011110", "11011110", "00111110", "10111110", "01111110", "11111110", + "00000001", "10000001", "01000001", "11000001", "00100001", "10100001", "01100001", "11100001", + "00010001", "10010001", "01010001", "11010001", "00110001", "10110001", "01110001", "11110001", + "00001001", "10001001", "01001001", "11001001", "00101001", "10101001", "01101001", "11101001", + "00011001", "10011001", "01011001", "11011001", "00111001", "10111001", "01111001", "11111001", + "00000101", "10000101", "01000101", "11000101", "00100101", "10100101", "01100101", "11100101", + "00010101", "10010101", "01010101", "11010101", "00110101", "10110101", "01110101", "11110101", + "00001101", "10001101", "01001101", "11001101", "00101101", "10101101", "01101101", "11101101", + "00011101", "10011101", "01011101", "11011101", "00111101", "10111101", "01111101", "11111101", + "00000011", "10000011", "01000011", "11000011", "00100011", "10100011", "01100011", "11100011", + "00010011", "10010011", "01010011", "11010011", "00110011", "10110011", "01110011", "11110011", + "00001011", "10001011", "01001011", "11001011", "00101011", "10101011", "01101011", "11101011", + "00011011", "10011011", "01011011", "11011011", "00111011", "10111011", "01111011", "11111011", + "00000111", "10000111", "01000111", "11000111", "00100111", "10100111", "01100111", "11100111", + "00010111", "10010111", "01010111", "11010111", "00110111", "10110111", "01110111", "11110111", + "00001111", "10001111", "01001111", "11001111", "00101111", "10101111", "01101111", "11101111", + "00011111", "10011111", "01011111", "11011111", "00111111", "10111111", "01111111", "11111111" +}; + +void BitDump(const char* tag, UINT32 level, const BYTE* buffer, UINT32 length, UINT32 flags) +{ + const char** strs = (flags & BITDUMP_MSB_FIRST) ? BYTE_BIT_STRINGS_MSB : BYTE_BIT_STRINGS_LSB; + char pbuffer[64 * 8 + 1] = { 0 }; + size_t pos = 0; + + WINPR_ASSERT(tag); + WINPR_ASSERT(buffer || (length == 0)); + + DWORD i = 0; + for (; i < length; i += 8) + { + const char* str = strs[buffer[i / 8]]; + const int nbits = (length - i) > 8 ? 8 : (length - i); + const int rc = _snprintf(&pbuffer[pos], length - pos, "%.*s ", nbits, str); + if (rc < 0) + return; + + pos += (size_t)rc; + if ((i % 64) == 0) + { + pos = 0; + WLog_LVL(tag, level, "%s", pbuffer); + } + } + + if (i) + WLog_LVL(tag, level, "%s ", pbuffer); +} + +UINT32 ReverseBits32(UINT32 bits, UINT32 nbits) +{ + UINT32 rbits = 0; + + do + { + rbits = (rbits | (bits & 1)) << 1; + bits >>= 1; + nbits--; + } while (nbits > 0); + + rbits >>= 1; + return rbits; +} + +void BitStream_Attach(wBitStream* bs, const BYTE* buffer, UINT32 capacity) +{ + union + { + const BYTE* cpv; + BYTE* pv; + } cnv; + + WINPR_ASSERT(bs); + WINPR_ASSERT(buffer); + + cnv.cpv = buffer; + + bs->position = 0; + bs->buffer = cnv.pv; + bs->offset = 0; + bs->accumulator = 0; + bs->pointer = cnv.pv; + bs->capacity = capacity; + bs->length = bs->capacity * 8; +} + +wBitStream* BitStream_New(void) +{ + wBitStream* bs = (wBitStream*)calloc(1, sizeof(wBitStream)); + + return bs; +} + +void BitStream_Free(wBitStream* bs) +{ + if (!bs) + return; + + free(bs); +} diff --git a/winpr/libwinpr/utils/collections/BufferPool.c b/winpr/libwinpr/utils/collections/BufferPool.c new file mode 100644 index 0000000..ebdf4f1 --- /dev/null +++ b/winpr/libwinpr/utils/collections/BufferPool.c @@ -0,0 +1,558 @@ +/** + * WinPR: Windows Portable Runtime + * Buffer Pool + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +typedef struct +{ + SSIZE_T size; + void* buffer; +} wBufferPoolItem; + +struct s_wBufferPool +{ + SSIZE_T fixedSize; + DWORD alignment; + BOOL synchronized; + CRITICAL_SECTION lock; + + SSIZE_T size; + SSIZE_T capacity; + void** array; + + SSIZE_T aSize; + SSIZE_T aCapacity; + wBufferPoolItem* aArray; + + SSIZE_T uSize; + SSIZE_T uCapacity; + wBufferPoolItem* uArray; +}; + +static BOOL BufferPool_Lock(wBufferPool* pool) +{ + if (!pool) + return FALSE; + + if (pool->synchronized) + EnterCriticalSection(&pool->lock); + return TRUE; +} + +static BOOL BufferPool_Unlock(wBufferPool* pool) +{ + if (!pool) + return FALSE; + + if (pool->synchronized) + LeaveCriticalSection(&pool->lock); + return TRUE; +} + +/** + * C equivalent of the C# BufferManager Class: + * http://msdn.microsoft.com/en-us/library/ms405814.aspx + */ + +/** + * Methods + */ + +static BOOL BufferPool_ShiftAvailable(wBufferPool* pool, size_t index, int count) +{ + if (count > 0) + { + if (pool->aSize + count > pool->aCapacity) + { + wBufferPoolItem* newArray = NULL; + SSIZE_T newCapacity = pool->aCapacity * 2; + + if (pool->alignment > 0) + newArray = (wBufferPoolItem*)winpr_aligned_realloc( + pool->aArray, sizeof(wBufferPoolItem) * newCapacity, pool->alignment); + else + newArray = + (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity); + if (!newArray) + return FALSE; + pool->aArray = newArray; + pool->aCapacity = newCapacity; + } + + MoveMemory(&pool->aArray[index + count], &pool->aArray[index], + (pool->aSize - index) * sizeof(wBufferPoolItem)); + pool->aSize += count; + } + else if (count < 0) + { + MoveMemory(&pool->aArray[index], &pool->aArray[index - count], + (pool->aSize - index) * sizeof(wBufferPoolItem)); + pool->aSize += count; + } + return TRUE; +} + +static BOOL BufferPool_ShiftUsed(wBufferPool* pool, SSIZE_T index, SSIZE_T count) +{ + if (count > 0) + { + if (pool->uSize + count > pool->uCapacity) + { + SSIZE_T newUCapacity = pool->uCapacity * 2; + wBufferPoolItem* newUArray = NULL; + if (pool->alignment > 0) + newUArray = (wBufferPoolItem*)winpr_aligned_realloc( + pool->uArray, sizeof(wBufferPoolItem) * newUCapacity, pool->alignment); + else + newUArray = + (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity); + if (!newUArray) + return FALSE; + pool->uCapacity = newUCapacity; + pool->uArray = newUArray; + } + + MoveMemory(&pool->uArray[index + count], &pool->uArray[index], + (pool->uSize - index) * sizeof(wBufferPoolItem)); + pool->uSize += count; + } + else if (count < 0) + { + MoveMemory(&pool->uArray[index], &pool->uArray[index - count], + (pool->uSize - index) * sizeof(wBufferPoolItem)); + pool->uSize += count; + } + return TRUE; +} + +/** + * Get the buffer pool size + */ + +SSIZE_T BufferPool_GetPoolSize(wBufferPool* pool) +{ + SSIZE_T size = 0; + + BufferPool_Lock(pool); + + if (pool->fixedSize) + { + /* fixed size buffers */ + size = pool->size; + } + else + { + /* variable size buffers */ + size = pool->uSize; + } + + BufferPool_Unlock(pool); + + return size; +} + +/** + * Get the size of a pooled buffer + */ + +SSIZE_T BufferPool_GetBufferSize(wBufferPool* pool, const void* buffer) +{ + SSIZE_T size = 0; + BOOL found = FALSE; + + BufferPool_Lock(pool); + + if (pool->fixedSize) + { + /* fixed size buffers */ + size = pool->fixedSize; + found = TRUE; + } + else + { + /* variable size buffers */ + + for (SSIZE_T index = 0; index < pool->uSize; index++) + { + if (pool->uArray[index].buffer == buffer) + { + size = pool->uArray[index].size; + found = TRUE; + break; + } + } + } + + BufferPool_Unlock(pool); + + return (found) ? size : -1; +} + +/** + * Gets a buffer of at least the specified size from the pool. + */ + +void* BufferPool_Take(wBufferPool* pool, SSIZE_T size) +{ + SSIZE_T maxSize = 0; + SSIZE_T maxIndex = 0; + SSIZE_T foundIndex = -1; + BOOL found = FALSE; + void* buffer = NULL; + + BufferPool_Lock(pool); + + if (pool->fixedSize) + { + /* fixed size buffers */ + + if (pool->size > 0) + buffer = pool->array[--(pool->size)]; + + if (!buffer) + { + if (pool->alignment) + buffer = winpr_aligned_malloc(pool->fixedSize, pool->alignment); + else + buffer = malloc(pool->fixedSize); + } + + if (!buffer) + goto out_error; + } + else + { + /* variable size buffers */ + + maxSize = 0; + maxIndex = 0; + + if (size < 1) + size = pool->fixedSize; + + for (SSIZE_T index = 0; index < pool->aSize; index++) + { + if (pool->aArray[index].size > maxSize) + { + maxIndex = index; + maxSize = pool->aArray[index].size; + } + + if (pool->aArray[index].size >= size) + { + foundIndex = index; + found = TRUE; + break; + } + } + + if (!found && maxSize) + { + foundIndex = maxIndex; + found = TRUE; + } + + if (!found) + { + if (!size) + buffer = NULL; + else + { + if (pool->alignment) + buffer = winpr_aligned_malloc(size, pool->alignment); + else + buffer = malloc(size); + + if (!buffer) + goto out_error; + } + } + else + { + buffer = pool->aArray[foundIndex].buffer; + + if (maxSize < size) + { + void* newBuffer = NULL; + if (pool->alignment) + newBuffer = winpr_aligned_realloc(buffer, size, pool->alignment); + else + newBuffer = realloc(buffer, size); + + if (!newBuffer) + goto out_error_no_free; + + buffer = newBuffer; + } + + if (!BufferPool_ShiftAvailable(pool, foundIndex, -1)) + goto out_error; + } + + if (!buffer) + goto out_error; + + if (pool->uSize + 1 > pool->uCapacity) + { + size_t newUCapacity = pool->uCapacity * 2; + wBufferPoolItem* newUArray = + (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity); + if (!newUArray) + goto out_error; + + pool->uCapacity = newUCapacity; + pool->uArray = newUArray; + } + + pool->uArray[pool->uSize].buffer = buffer; + pool->uArray[pool->uSize].size = size; + (pool->uSize)++; + } + + BufferPool_Unlock(pool); + + return buffer; + +out_error: + if (pool->alignment) + winpr_aligned_free(buffer); + else + free(buffer); +out_error_no_free: + BufferPool_Unlock(pool); + return NULL; +} + +/** + * Returns a buffer to the pool. + */ + +BOOL BufferPool_Return(wBufferPool* pool, void* buffer) +{ + BOOL rc = FALSE; + SSIZE_T size = 0; + BOOL found = FALSE; + + BufferPool_Lock(pool); + + if (pool->fixedSize) + { + /* fixed size buffers */ + + if ((pool->size + 1) >= pool->capacity) + { + SSIZE_T newCapacity = pool->capacity * 2; + void** newArray = (void**)realloc(pool->array, sizeof(void*) * newCapacity); + if (!newArray) + goto out_error; + + pool->capacity = newCapacity; + pool->array = newArray; + } + + pool->array[(pool->size)++] = buffer; + } + else + { + /* variable size buffers */ + + SSIZE_T index = 0; + for (; index < pool->uSize; index++) + { + if (pool->uArray[index].buffer == buffer) + { + found = TRUE; + break; + } + } + + if (found) + { + size = pool->uArray[index].size; + if (!BufferPool_ShiftUsed(pool, index, -1)) + goto out_error; + } + + if (size) + { + if ((pool->aSize + 1) >= pool->aCapacity) + { + SSIZE_T newCapacity = pool->aCapacity * 2; + wBufferPoolItem* newArray = + (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity); + if (!newArray) + goto out_error; + + pool->aCapacity = newCapacity; + pool->aArray = newArray; + } + + pool->aArray[pool->aSize].buffer = buffer; + pool->aArray[pool->aSize].size = size; + (pool->aSize)++; + } + } + + rc = TRUE; +out_error: + BufferPool_Unlock(pool); + return rc; +} + +/** + * Releases the buffers currently cached in the pool. + */ + +void BufferPool_Clear(wBufferPool* pool) +{ + BufferPool_Lock(pool); + + if (pool->fixedSize) + { + /* fixed size buffers */ + + while (pool->size > 0) + { + (pool->size)--; + + if (pool->alignment) + winpr_aligned_free(pool->array[pool->size]); + else + free(pool->array[pool->size]); + } + } + else + { + /* variable size buffers */ + + while (pool->aSize > 0) + { + (pool->aSize)--; + + if (pool->alignment) + winpr_aligned_free(pool->aArray[pool->aSize].buffer); + else + free(pool->aArray[pool->aSize].buffer); + } + + while (pool->uSize > 0) + { + (pool->uSize)--; + + if (pool->alignment) + winpr_aligned_free(pool->uArray[pool->uSize].buffer); + else + free(pool->uArray[pool->uSize].buffer); + } + } + + BufferPool_Unlock(pool); +} + +/** + * Construction, Destruction + */ + +wBufferPool* BufferPool_New(BOOL synchronized, SSIZE_T fixedSize, DWORD alignment) +{ + wBufferPool* pool = NULL; + + pool = (wBufferPool*)calloc(1, sizeof(wBufferPool)); + + if (pool) + { + pool->fixedSize = fixedSize; + + if (pool->fixedSize < 0) + pool->fixedSize = 0; + + pool->alignment = alignment; + pool->synchronized = synchronized; + + if (pool->synchronized) + InitializeCriticalSectionAndSpinCount(&pool->lock, 4000); + + if (pool->fixedSize) + { + /* fixed size buffers */ + + pool->size = 0; + pool->capacity = 32; + pool->array = (void**)calloc(pool->capacity, sizeof(void*)); + if (!pool->array) + goto out_error; + } + else + { + /* variable size buffers */ + + pool->aSize = 0; + pool->aCapacity = 32; + pool->aArray = (wBufferPoolItem*)calloc(pool->aCapacity, sizeof(wBufferPoolItem)); + if (!pool->aArray) + goto out_error; + + pool->uSize = 0; + pool->uCapacity = 32; + pool->uArray = (wBufferPoolItem*)calloc(pool->uCapacity, sizeof(wBufferPoolItem)); + if (!pool->uArray) + goto out_error; + } + } + + return pool; + +out_error: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + BufferPool_Free(pool); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void BufferPool_Free(wBufferPool* pool) +{ + if (pool) + { + BufferPool_Clear(pool); + + if (pool->synchronized) + DeleteCriticalSection(&pool->lock); + + if (pool->fixedSize) + { + /* fixed size buffers */ + + free(pool->array); + } + else + { + /* variable size buffers */ + + free(pool->aArray); + free(pool->uArray); + } + + free(pool); + } +} diff --git a/winpr/libwinpr/utils/collections/CountdownEvent.c b/winpr/libwinpr/utils/collections/CountdownEvent.c new file mode 100644 index 0000000..fd23e0c --- /dev/null +++ b/winpr/libwinpr/utils/collections/CountdownEvent.c @@ -0,0 +1,203 @@ +/** + * WinPR: Windows Portable Runtime + * Countdown Event + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +struct CountdownEvent +{ + size_t count; + CRITICAL_SECTION lock; + HANDLE event; + size_t initialCount; +}; + +/** + * C equivalent of the C# CountdownEvent Class + * http://msdn.microsoft.com/en-us/library/dd235708/ + */ + +/** + * Properties + */ + +/** + * Gets the number of remaining signals required to set the event. + */ + +size_t CountdownEvent_CurrentCount(wCountdownEvent* countdown) +{ + WINPR_ASSERT(countdown); + return countdown->count; +} + +/** + * Gets the numbers of signals initially required to set the event. + */ + +size_t CountdownEvent_InitialCount(wCountdownEvent* countdown) +{ + WINPR_ASSERT(countdown); + return countdown->initialCount; +} + +/** + * Determines whether the event is set. + */ + +BOOL CountdownEvent_IsSet(wCountdownEvent* countdown) +{ + BOOL status = FALSE; + + WINPR_ASSERT(countdown); + if (WaitForSingleObject(countdown->event, 0) == WAIT_OBJECT_0) + status = TRUE; + + return status; +} + +/** + * Gets a WaitHandle that is used to wait for the event to be set. + */ + +HANDLE CountdownEvent_WaitHandle(wCountdownEvent* countdown) +{ + WINPR_ASSERT(countdown); + return countdown->event; +} + +/** + * Methods + */ + +/** + * Increments the CountdownEvent's current count by a specified value. + */ + +void CountdownEvent_AddCount(wCountdownEvent* countdown, size_t signalCount) +{ + WINPR_ASSERT(countdown); + EnterCriticalSection(&countdown->lock); + + countdown->count += signalCount; + + if (countdown->count > 0) + ResetEvent(countdown->event); + + LeaveCriticalSection(&countdown->lock); +} + +/** + * Registers multiple signals with the CountdownEvent, decrementing the value of CurrentCount by the + * specified amount. + */ + +BOOL CountdownEvent_Signal(wCountdownEvent* countdown, size_t signalCount) +{ + BOOL status = FALSE; + BOOL newStatus = FALSE; + BOOL oldStatus = FALSE; + + WINPR_ASSERT(countdown); + + EnterCriticalSection(&countdown->lock); + + if (WaitForSingleObject(countdown->event, 0) == WAIT_OBJECT_0) + oldStatus = TRUE; + + if (signalCount <= countdown->count) + countdown->count -= signalCount; + else + countdown->count = 0; + + if (countdown->count == 0) + newStatus = TRUE; + + if (newStatus && (!oldStatus)) + { + SetEvent(countdown->event); + status = TRUE; + } + + LeaveCriticalSection(&countdown->lock); + + return status; +} + +/** + * Resets the InitialCount property to a specified value. + */ + +void CountdownEvent_Reset(wCountdownEvent* countdown, size_t count) +{ + WINPR_ASSERT(countdown); + countdown->initialCount = count; +} + +/** + * Construction, Destruction + */ + +wCountdownEvent* CountdownEvent_New(size_t initialCount) +{ + wCountdownEvent* countdown = (wCountdownEvent*)calloc(1, sizeof(wCountdownEvent)); + + if (!countdown) + return NULL; + + countdown->count = initialCount; + countdown->initialCount = initialCount; + + if (!InitializeCriticalSectionAndSpinCount(&countdown->lock, 4000)) + goto fail; + + countdown->event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!countdown->event) + goto fail; + + if (countdown->count == 0) + { + if (!SetEvent(countdown->event)) + goto fail; + } + + return countdown; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + CountdownEvent_Free(countdown); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void CountdownEvent_Free(wCountdownEvent* countdown) +{ + if (!countdown) + return; + + DeleteCriticalSection(&countdown->lock); + CloseHandle(countdown->event); + + free(countdown); +} diff --git a/winpr/libwinpr/utils/collections/HashTable.c b/winpr/libwinpr/utils/collections/HashTable.c new file mode 100644 index 0000000..7782b2b --- /dev/null +++ b/winpr/libwinpr/utils/collections/HashTable.c @@ -0,0 +1,870 @@ +/** + * WinPR: Windows Portable Runtime + * System.Collections.Hashtable + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +/** + * This implementation is based on the public domain + * hash table implementation made by Keith Pomakis: + * + * http://www.pomakis.com/hashtable/hashtable.c + * http://www.pomakis.com/hashtable/hashtable.h + */ + +typedef struct s_wKeyValuePair wKeyValuePair; + +struct s_wKeyValuePair +{ + void* key; + void* value; + + wKeyValuePair* next; + BOOL markedForRemove; +}; + +struct s_wHashTable +{ + BOOL synchronized; + CRITICAL_SECTION lock; + + size_t numOfBuckets; + size_t numOfElements; + float idealRatio; + float lowerRehashThreshold; + float upperRehashThreshold; + wKeyValuePair** bucketArray; + + HASH_TABLE_HASH_FN hash; + wObject key; + wObject value; + + DWORD foreachRecursionLevel; + DWORD pendingRemoves; +}; + +BOOL HashTable_PointerCompare(const void* pointer1, const void* pointer2) +{ + return (pointer1 == pointer2); +} + +UINT32 HashTable_PointerHash(const void* pointer) +{ + return ((UINT32)(UINT_PTR)pointer) >> 4; +} + +BOOL HashTable_StringCompare(const void* string1, const void* string2) +{ + if (!string1 || !string2) + return (string1 == string2); + + return (strcmp((const char*)string1, (const char*)string2) == 0); +} + +UINT32 HashTable_StringHash(const void* key) +{ + UINT32 c = 0; + UINT32 hash = 5381; + const BYTE* str = (const BYTE*)key; + + /* djb2 algorithm */ + while ((c = *str++) != '\0') + hash = (hash * 33) + c; + + return hash; +} + +void* HashTable_StringClone(const void* str) +{ + return winpr_ObjectStringClone(str); +} + +void HashTable_StringFree(void* str) +{ + winpr_ObjectStringFree(str); +} + +static INLINE BOOL HashTable_IsProbablePrime(size_t oddNumber) +{ + for (size_t i = 3; i < 51; i += 2) + { + if (oddNumber == i) + return TRUE; + else if (oddNumber % i == 0) + return FALSE; + } + + return TRUE; /* maybe */ +} + +static INLINE size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table) +{ + WINPR_ASSERT(table); + + const float tmp = (table->numOfElements / table->idealRatio); + size_t idealNumOfBuckets = (size_t)tmp; + + if (idealNumOfBuckets < 5) + idealNumOfBuckets = 5; + else + idealNumOfBuckets |= 0x01; + + while (!HashTable_IsProbablePrime(idealNumOfBuckets)) + idealNumOfBuckets += 2; + + return idealNumOfBuckets; +} + +static INLINE void HashTable_Rehash(wHashTable* table, size_t numOfBuckets) +{ + UINT32 hashValue = 0; + wKeyValuePair* nextPair = NULL; + wKeyValuePair** newBucketArray = NULL; + + WINPR_ASSERT(table); + if (numOfBuckets == 0) + numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table); + + if (numOfBuckets == table->numOfBuckets) + return; /* already the right size! */ + + newBucketArray = (wKeyValuePair**)calloc(numOfBuckets, sizeof(wKeyValuePair*)); + + if (!newBucketArray) + { + /* + * Couldn't allocate memory for the new array. + * This isn't a fatal error; we just can't perform the rehash. + */ + return; + } + + for (size_t index = 0; index < table->numOfBuckets; index++) + { + wKeyValuePair* pair = table->bucketArray[index]; + + while (pair) + { + nextPair = pair->next; + hashValue = table->hash(pair->key) % numOfBuckets; + pair->next = newBucketArray[hashValue]; + newBucketArray[hashValue] = pair; + pair = nextPair; + } + } + + free(table->bucketArray); + table->bucketArray = newBucketArray; + table->numOfBuckets = numOfBuckets; +} + +static INLINE BOOL HashTable_Equals(wHashTable* table, const wKeyValuePair* pair, const void* key) +{ + WINPR_ASSERT(table); + WINPR_ASSERT(pair); + WINPR_ASSERT(key); + return table->key.fnObjectEquals(key, pair->key); +} + +static INLINE wKeyValuePair* HashTable_Get(wHashTable* table, const void* key) +{ + UINT32 hashValue = 0; + wKeyValuePair* pair = NULL; + + WINPR_ASSERT(table); + if (!key) + return NULL; + + hashValue = table->hash(key) % table->numOfBuckets; + pair = table->bucketArray[hashValue]; + + while (pair && !HashTable_Equals(table, pair, key)) + pair = pair->next; + + return pair; +} + +static INLINE void disposeKey(wHashTable* table, void* key) +{ + WINPR_ASSERT(table); + if (table->key.fnObjectFree) + table->key.fnObjectFree(key); +} + +static INLINE void disposeValue(wHashTable* table, void* value) +{ + WINPR_ASSERT(table); + if (table->value.fnObjectFree) + table->value.fnObjectFree(value); +} + +static INLINE void disposePair(wHashTable* table, wKeyValuePair* pair) +{ + WINPR_ASSERT(table); + if (!pair) + return; + disposeKey(table, pair->key); + disposeValue(table, pair->value); + free(pair); +} + +static INLINE void setKey(wHashTable* table, wKeyValuePair* pair, const void* key) +{ + WINPR_ASSERT(table); + if (!pair) + return; + disposeKey(table, pair->key); + if (table->key.fnObjectNew) + pair->key = table->key.fnObjectNew(key); + else + { + union + { + const void* cpv; + void* pv; + } cnv; + cnv.cpv = key; + pair->key = cnv.pv; + } +} + +static INLINE void setValue(wHashTable* table, wKeyValuePair* pair, const void* value) +{ + WINPR_ASSERT(table); + if (!pair) + return; + disposeValue(table, pair->value); + if (table->value.fnObjectNew) + pair->value = table->value.fnObjectNew(value); + else + { + union + { + const void* cpv; + void* pv; + } cnv; + cnv.cpv = value; + pair->value = cnv.pv; + } +} + +/** + * C equivalent of the C# Hashtable Class: + * http://msdn.microsoft.com/en-us/library/system.collections.hashtable.aspx + */ + +/** + * Properties + */ + +/** + * Gets the number of key/value pairs contained in the HashTable. + */ + +size_t HashTable_Count(wHashTable* table) +{ + WINPR_ASSERT(table); + return table->numOfElements; +} + +/** + * Methods + */ + +/** + * Adds an element with the specified key and value into the HashTable. + */ +#if defined(WITH_WINPR_DEPRECATED) +int HashTable_Add(wHashTable* table, const void* key, const void* value) +{ + if (!HashTable_Insert(table, key, value)) + return -1; + return 0; +} +#endif + +BOOL HashTable_Insert(wHashTable* table, const void* key, const void* value) +{ + BOOL rc = FALSE; + UINT32 hashValue = 0; + wKeyValuePair* pair = NULL; + wKeyValuePair* newPair = NULL; + + WINPR_ASSERT(table); + if (!key || !value) + return FALSE; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + hashValue = table->hash(key) % table->numOfBuckets; + pair = table->bucketArray[hashValue]; + + while (pair && !HashTable_Equals(table, pair, key)) + pair = pair->next; + + if (pair) + { + if (pair->markedForRemove) + { + /* this entry was set to be removed but will be recycled instead */ + table->pendingRemoves--; + pair->markedForRemove = FALSE; + table->numOfElements++; + } + + if (pair->key != key) + { + setKey(table, pair, key); + } + + if (pair->value != value) + { + setValue(table, pair, value); + } + rc = TRUE; + } + else + { + newPair = (wKeyValuePair*)calloc(1, sizeof(wKeyValuePair)); + + if (newPair) + { + setKey(table, newPair, key); + setValue(table, newPair, value); + newPair->next = table->bucketArray[hashValue]; + newPair->markedForRemove = FALSE; + table->bucketArray[hashValue] = newPair; + table->numOfElements++; + + if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio) + { + float elementToBucketRatio = + (float)table->numOfElements / (float)table->numOfBuckets; + + if (elementToBucketRatio > table->upperRehashThreshold) + HashTable_Rehash(table, 0); + } + rc = TRUE; + } + } + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return rc; +} + +/** + * Removes the element with the specified key from the HashTable. + */ + +BOOL HashTable_Remove(wHashTable* table, const void* key) +{ + UINT32 hashValue = 0; + BOOL status = TRUE; + wKeyValuePair* pair = NULL; + wKeyValuePair* previousPair = NULL; + + WINPR_ASSERT(table); + if (!key) + return FALSE; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + hashValue = table->hash(key) % table->numOfBuckets; + pair = table->bucketArray[hashValue]; + + while (pair && !HashTable_Equals(table, pair, key)) + { + previousPair = pair; + pair = pair->next; + } + + if (!pair) + { + status = FALSE; + goto out; + } + + if (table->foreachRecursionLevel) + { + /* if we are running a HashTable_Foreach, just mark the entry for removal */ + pair->markedForRemove = TRUE; + table->pendingRemoves++; + table->numOfElements--; + goto out; + } + + if (previousPair) + previousPair->next = pair->next; + else + table->bucketArray[hashValue] = pair->next; + + disposePair(table, pair); + table->numOfElements--; + + if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f) + { + float elementToBucketRatio = (float)table->numOfElements / (float)table->numOfBuckets; + + if (elementToBucketRatio < table->lowerRehashThreshold) + HashTable_Rehash(table, 0); + } + +out: + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return status; +} + +/** + * Get an item value using key + */ + +void* HashTable_GetItemValue(wHashTable* table, const void* key) +{ + void* value = NULL; + wKeyValuePair* pair = NULL; + + WINPR_ASSERT(table); + if (!key) + return NULL; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + pair = HashTable_Get(table, key); + + if (pair && !pair->markedForRemove) + value = pair->value; + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return value; +} + +/** + * Set an item value using key + */ + +BOOL HashTable_SetItemValue(wHashTable* table, const void* key, const void* value) +{ + BOOL status = TRUE; + wKeyValuePair* pair = NULL; + + WINPR_ASSERT(table); + if (!key) + return FALSE; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + pair = HashTable_Get(table, key); + + if (!pair || pair->markedForRemove) + status = FALSE; + else + { + setValue(table, pair, value); + } + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return status; +} + +/** + * Removes all elements from the HashTable. + */ + +void HashTable_Clear(wHashTable* table) +{ + wKeyValuePair* nextPair = NULL; + + WINPR_ASSERT(table); + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + for (size_t index = 0; index < table->numOfBuckets; index++) + { + wKeyValuePair* pair = table->bucketArray[index]; + + while (pair) + { + nextPair = pair->next; + + if (table->foreachRecursionLevel) + { + /* if we're in a foreach we just mark the entry for removal */ + pair->markedForRemove = TRUE; + table->pendingRemoves++; + } + else + { + disposePair(table, pair); + pair = nextPair; + } + } + + table->bucketArray[index] = NULL; + } + + table->numOfElements = 0; + if (table->foreachRecursionLevel == 0) + HashTable_Rehash(table, 5); + + if (table->synchronized) + LeaveCriticalSection(&table->lock); +} + +/** + * Gets the list of keys as an array + */ + +size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys) +{ + size_t iKey = 0; + size_t count = 0; + ULONG_PTR* pKeys = NULL; + wKeyValuePair* nextPair = NULL; + + WINPR_ASSERT(table); + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + iKey = 0; + count = table->numOfElements; + *ppKeys = NULL; + + if (count < 1) + { + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return 0; + } + + pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR)); + + if (!pKeys) + { + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return 0; + } + + for (size_t index = 0; index < table->numOfBuckets; index++) + { + wKeyValuePair* pair = table->bucketArray[index]; + + while (pair) + { + nextPair = pair->next; + if (!pair->markedForRemove) + pKeys[iKey++] = (ULONG_PTR)pair->key; + pair = nextPair; + } + } + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + if (ppKeys) + *ppKeys = pKeys; + else + free(pKeys); + return count; +} + +BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg) +{ + BOOL ret = TRUE; + + WINPR_ASSERT(table); + WINPR_ASSERT(fn); + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + table->foreachRecursionLevel++; + for (size_t index = 0; index < table->numOfBuckets; index++) + { + for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next) + { + if (!pair->markedForRemove && !fn(pair->key, pair->value, arg)) + { + ret = FALSE; + goto out; + } + } + } + table->foreachRecursionLevel--; + + if (!table->foreachRecursionLevel && table->pendingRemoves) + { + /* if we're the last recursive foreach call, let's do the cleanup if needed */ + wKeyValuePair** prevPtr = NULL; + for (size_t index = 0; index < table->numOfBuckets; index++) + { + wKeyValuePair* nextPair = NULL; + prevPtr = &table->bucketArray[index]; + for (wKeyValuePair* pair = table->bucketArray[index]; pair;) + { + nextPair = pair->next; + + if (pair->markedForRemove) + { + disposePair(table, pair); + *prevPtr = nextPair; + } + else + { + prevPtr = &pair->next; + } + pair = nextPair; + } + } + table->pendingRemoves = 0; + } + +out: + if (table->synchronized) + LeaveCriticalSection(&table->lock); + return ret; +} + +/** + * Determines whether the HashTable contains a specific key. + */ + +BOOL HashTable_Contains(wHashTable* table, const void* key) +{ + BOOL status = 0; + wKeyValuePair* pair = NULL; + + WINPR_ASSERT(table); + if (!key) + return FALSE; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + pair = HashTable_Get(table, key); + status = (pair && !pair->markedForRemove); + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return status; +} + +/** + * Determines whether the HashTable contains a specific key. + */ + +BOOL HashTable_ContainsKey(wHashTable* table, const void* key) +{ + BOOL status = 0; + wKeyValuePair* pair = NULL; + + WINPR_ASSERT(table); + if (!key) + return FALSE; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + pair = HashTable_Get(table, key); + status = (pair && !pair->markedForRemove); + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return status; +} + +/** + * Determines whether the HashTable contains a specific value. + */ + +BOOL HashTable_ContainsValue(wHashTable* table, const void* value) +{ + BOOL status = FALSE; + + WINPR_ASSERT(table); + if (!value) + return FALSE; + + if (table->synchronized) + EnterCriticalSection(&table->lock); + + for (size_t index = 0; index < table->numOfBuckets; index++) + { + wKeyValuePair* pair = table->bucketArray[index]; + + while (pair) + { + if (!pair->markedForRemove && HashTable_Equals(table, pair, value)) + { + status = TRUE; + break; + } + + pair = pair->next; + } + + if (status) + break; + } + + if (table->synchronized) + LeaveCriticalSection(&table->lock); + + return status; +} + +/** + * Construction, Destruction + */ + +wHashTable* HashTable_New(BOOL synchronized) +{ + wHashTable* table = (wHashTable*)calloc(1, sizeof(wHashTable)); + + if (!table) + goto fail; + + table->synchronized = synchronized; + InitializeCriticalSectionAndSpinCount(&(table->lock), 4000); + table->numOfBuckets = 64; + table->numOfElements = 0; + table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets, sizeof(wKeyValuePair*)); + + if (!table->bucketArray) + goto fail; + + table->idealRatio = 3.0; + table->lowerRehashThreshold = 0.0; + table->upperRehashThreshold = 15.0; + table->hash = HashTable_PointerHash; + table->key.fnObjectEquals = HashTable_PointerCompare; + table->value.fnObjectEquals = HashTable_PointerCompare; + + return table; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + HashTable_Free(table); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void HashTable_Free(wHashTable* table) +{ + wKeyValuePair* pair = NULL; + wKeyValuePair* nextPair = NULL; + + if (!table) + return; + + if (table->bucketArray) + { + for (size_t index = 0; index < table->numOfBuckets; index++) + { + pair = table->bucketArray[index]; + + while (pair) + { + nextPair = pair->next; + + disposePair(table, pair); + pair = nextPair; + } + } + free(table->bucketArray); + } + DeleteCriticalSection(&(table->lock)); + + free(table); +} + +void HashTable_Lock(wHashTable* table) +{ + WINPR_ASSERT(table); + EnterCriticalSection(&table->lock); +} + +void HashTable_Unlock(wHashTable* table) +{ + WINPR_ASSERT(table); + LeaveCriticalSection(&table->lock); +} + +wObject* HashTable_KeyObject(wHashTable* table) +{ + WINPR_ASSERT(table); + return &table->key; +} + +wObject* HashTable_ValueObject(wHashTable* table) +{ + WINPR_ASSERT(table); + return &table->value; +} + +BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn) +{ + WINPR_ASSERT(table); + table->hash = fn; + return fn != NULL; +} + +BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues) +{ + wObject* obj = NULL; + + if (!HashTable_SetHashFunction(table, HashTable_StringHash)) + return FALSE; + + obj = HashTable_KeyObject(table); + obj->fnObjectEquals = HashTable_StringCompare; + obj->fnObjectNew = HashTable_StringClone; + obj->fnObjectFree = HashTable_StringFree; + + if (stringValues) + { + obj = HashTable_ValueObject(table); + obj->fnObjectEquals = HashTable_StringCompare; + obj->fnObjectNew = HashTable_StringClone; + obj->fnObjectFree = HashTable_StringFree; + } + return TRUE; +} diff --git a/winpr/libwinpr/utils/collections/LinkedList.c b/winpr/libwinpr/utils/collections/LinkedList.c new file mode 100644 index 0000000..48d64d9 --- /dev/null +++ b/winpr/libwinpr/utils/collections/LinkedList.c @@ -0,0 +1,385 @@ +/** + * WinPR: Windows Portable Runtime + * System.Collections.Generic.LinkedList + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +typedef struct s_wLinkedListItem wLinkedListNode; + +struct s_wLinkedListItem +{ + void* value; + wLinkedListNode* prev; + wLinkedListNode* next; +}; + +struct s_wLinkedList +{ + size_t count; + int initial; + wLinkedListNode* head; + wLinkedListNode* tail; + wLinkedListNode* current; + wObject object; +}; + +/** + * C equivalent of the C# LinkedList Class: + * http://msdn.microsoft.com/en-us/library/he2s3bh7.aspx + * + * Internal implementation uses a doubly-linked list + */ + +/** + * Properties + */ + +/** + * Gets the number of nodes actually contained in the LinkedList. + */ + +size_t LinkedList_Count(wLinkedList* list) +{ + WINPR_ASSERT(list); + return list->count; +} + +/** + * Gets the first node of the LinkedList. + */ + +void* LinkedList_First(wLinkedList* list) +{ + WINPR_ASSERT(list); + if (list->head) + return list->head->value; + else + return NULL; +} + +/** + * Gets the last node of the LinkedList. + */ + +void* LinkedList_Last(wLinkedList* list) +{ + WINPR_ASSERT(list); + if (list->tail) + return list->tail->value; + else + return NULL; +} + +/** + * Methods + */ + +/** + * Determines whether the LinkedList contains a specific value. + */ + +BOOL LinkedList_Contains(wLinkedList* list, const void* value) +{ + wLinkedListNode* item = NULL; + OBJECT_EQUALS_FN keyEquals = NULL; + + WINPR_ASSERT(list); + if (!list->head) + return FALSE; + + item = list->head; + keyEquals = list->object.fnObjectEquals; + + while (item) + { + if (keyEquals(item->value, value)) + break; + + item = item->next; + } + + return (item) ? TRUE : FALSE; +} + +static wLinkedListNode* LinkedList_FreeNode(wLinkedList* list, wLinkedListNode* node) +{ + wLinkedListNode* next = NULL; + wLinkedListNode* prev = NULL; + + WINPR_ASSERT(list); + WINPR_ASSERT(node); + + next = node->next; + prev = node->prev; + if (prev) + prev->next = next; + + if (next) + next->prev = prev; + + if (node == list->head) + list->head = node->next; + + if (node == list->tail) + list->tail = node->prev; + + if (list->object.fnObjectUninit) + list->object.fnObjectUninit(node); + + if (list->object.fnObjectFree) + list->object.fnObjectFree(node); + + free(node); + list->count--; + return next; +} + +/** + * Removes all entries from the LinkedList. + */ + +void LinkedList_Clear(wLinkedList* list) +{ + wLinkedListNode* node = NULL; + WINPR_ASSERT(list); + if (!list->head) + return; + + node = list->head; + + while (node) + node = LinkedList_FreeNode(list, node); + + list->head = list->tail = NULL; + list->count = 0; +} + +static wLinkedListNode* LinkedList_Create(wLinkedList* list, const void* value) +{ + wLinkedListNode* node = NULL; + + WINPR_ASSERT(list); + node = (wLinkedListNode*)calloc(1, sizeof(wLinkedListNode)); + + if (!node) + return NULL; + + if (list->object.fnObjectNew) + node->value = list->object.fnObjectNew(value); + else + { + union + { + const void* cpv; + void* pv; + } cnv; + cnv.cpv = value; + node->value = cnv.pv; + } + + if (list->object.fnObjectInit) + list->object.fnObjectInit(node); + + return node; +} +/** + * Adds a new node containing the specified value at the start of the LinkedList. + */ + +BOOL LinkedList_AddFirst(wLinkedList* list, const void* value) +{ + wLinkedListNode* node = LinkedList_Create(list, value); + + if (!node) + return FALSE; + + if (!list->head) + { + list->tail = list->head = node; + } + else + { + list->head->prev = node; + node->next = list->head; + list->head = node; + } + + list->count++; + return TRUE; +} + +/** + * Adds a new node containing the specified value at the end of the LinkedList. + */ + +BOOL LinkedList_AddLast(wLinkedList* list, const void* value) +{ + wLinkedListNode* node = LinkedList_Create(list, value); + + if (!node) + return FALSE; + + if (!list->tail) + { + list->head = list->tail = node; + } + else + { + list->tail->next = node; + node->prev = list->tail; + list->tail = node; + } + + list->count++; + return TRUE; +} + +/** + * Removes the first occurrence of the specified value from the LinkedList. + */ + +BOOL LinkedList_Remove(wLinkedList* list, const void* value) +{ + wLinkedListNode* node = NULL; + OBJECT_EQUALS_FN keyEquals = NULL; + WINPR_ASSERT(list); + + keyEquals = list->object.fnObjectEquals; + node = list->head; + + while (node) + { + if (keyEquals(node->value, value)) + { + LinkedList_FreeNode(list, node); + return TRUE; + } + + node = node->next; + } + + return FALSE; +} + +/** + * Removes the node at the start of the LinkedList. + */ + +void LinkedList_RemoveFirst(wLinkedList* list) +{ + WINPR_ASSERT(list); + if (list->head) + LinkedList_FreeNode(list, list->head); +} + +/** + * Removes the node at the end of the LinkedList. + */ + +void LinkedList_RemoveLast(wLinkedList* list) +{ + WINPR_ASSERT(list); + if (list->tail) + LinkedList_FreeNode(list, list->tail); +} + +/** + * Sets the enumerator to its initial position, which is before the first element in the collection. + */ + +void LinkedList_Enumerator_Reset(wLinkedList* list) +{ + WINPR_ASSERT(list); + list->initial = 1; + list->current = list->head; +} + +/* + * Gets the element at the current position of the enumerator. + */ + +void* LinkedList_Enumerator_Current(wLinkedList* list) +{ + WINPR_ASSERT(list); + if (list->initial) + return NULL; + + if (list->current) + return list->current->value; + else + return NULL; +} + +/* + * Advances the enumerator to the next element of the LinkedList. + */ + +BOOL LinkedList_Enumerator_MoveNext(wLinkedList* list) +{ + WINPR_ASSERT(list); + if (list->initial) + list->initial = 0; + else if (list->current) + list->current = list->current->next; + + if (!list->current) + return FALSE; + + return TRUE; +} + +static BOOL default_equal_function(const void* objA, const void* objB) +{ + return objA == objB; +} + +/** + * Construction, Destruction + */ + +wLinkedList* LinkedList_New(void) +{ + wLinkedList* list = NULL; + list = (wLinkedList*)calloc(1, sizeof(wLinkedList)); + + if (list) + { + list->object.fnObjectEquals = default_equal_function; + } + + return list; +} + +void LinkedList_Free(wLinkedList* list) +{ + if (list) + { + LinkedList_Clear(list); + free(list); + } +} + +wObject* LinkedList_Object(wLinkedList* list) +{ + WINPR_ASSERT(list); + + return &list->object; +} diff --git a/winpr/libwinpr/utils/collections/ListDictionary.c b/winpr/libwinpr/utils/collections/ListDictionary.c new file mode 100644 index 0000000..cf238a9 --- /dev/null +++ b/winpr/libwinpr/utils/collections/ListDictionary.c @@ -0,0 +1,562 @@ +/** + * WinPR: Windows Portable Runtime + * System.Collections.Specialized.ListDictionary + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +typedef struct s_wListDictionaryItem wListDictionaryItem; + +struct s_wListDictionaryItem +{ + void* key; + void* value; + + wListDictionaryItem* next; +}; + +struct s_wListDictionary +{ + BOOL synchronized; + CRITICAL_SECTION lock; + + wListDictionaryItem* head; + wObject objectKey; + wObject objectValue; +}; + +/** + * C equivalent of the C# ListDictionary Class: + * http://msdn.microsoft.com/en-us/library/system.collections.specialized.listdictionary.aspx + * + * Internal implementation uses a singly-linked list + */ + +WINPR_API wObject* ListDictionary_KeyObject(wListDictionary* _dictionary) +{ + WINPR_ASSERT(_dictionary); + return &_dictionary->objectKey; +} + +WINPR_API wObject* ListDictionary_ValueObject(wListDictionary* _dictionary) +{ + WINPR_ASSERT(_dictionary); + return &_dictionary->objectValue; +} + +/** + * Properties + */ + +/** + * Gets the number of key/value pairs contained in the ListDictionary. + */ + +size_t ListDictionary_Count(wListDictionary* listDictionary) +{ + size_t count = 0; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + if (listDictionary->head) + { + wListDictionaryItem* item = listDictionary->head; + + while (item) + { + count++; + item = item->next; + } + } + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return count; +} + +/** + * Lock access to the ListDictionary + */ + +void ListDictionary_Lock(wListDictionary* listDictionary) +{ + WINPR_ASSERT(listDictionary); + + EnterCriticalSection(&listDictionary->lock); +} + +/** + * Unlock access to the ListDictionary + */ + +void ListDictionary_Unlock(wListDictionary* listDictionary) +{ + WINPR_ASSERT(listDictionary); + + LeaveCriticalSection(&listDictionary->lock); +} + +/** + * Methods + */ + +/** + * Gets the list of keys as an array + */ + +size_t ListDictionary_GetKeys(wListDictionary* listDictionary, ULONG_PTR** ppKeys) +{ + ULONG_PTR* pKeys = NULL; + + WINPR_ASSERT(listDictionary); + if (!ppKeys) + return 0; + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + size_t count = 0; + + if (listDictionary->head) + { + wListDictionaryItem* item = listDictionary->head; + + while (item) + { + count++; + item = item->next; + } + } + + if (count > 0) + { + pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR)); + + if (!pKeys) + { + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return -1; + } + } + + size_t index = 0; + + if (listDictionary->head) + { + wListDictionaryItem* item = listDictionary->head; + + while (item) + { + pKeys[index++] = (ULONG_PTR)item->key; + item = item->next; + } + } + + *ppKeys = pKeys; + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return count; +} + +static void item_free(wListDictionary* listDictionary, wListDictionaryItem* item) +{ + WINPR_ASSERT(listDictionary); + + if (item) + { + if (listDictionary->objectKey.fnObjectFree) + listDictionary->objectKey.fnObjectFree(item->key); + if (listDictionary->objectValue.fnObjectFree) + listDictionary->objectValue.fnObjectFree(item->value); + } + free(item); +} + +static void item_set(wListDictionary* listDictionary, wListDictionaryItem* item, const void* value) +{ + WINPR_ASSERT(listDictionary); + WINPR_ASSERT(item); + + if (listDictionary->objectValue.fnObjectFree) + listDictionary->objectValue.fnObjectFree(item->value); + + if (listDictionary->objectValue.fnObjectNew) + item->value = listDictionary->objectValue.fnObjectNew(value); + else + item->value = (void*)(uintptr_t)value; +} + +static wListDictionaryItem* new_item(wListDictionary* listDictionary, const void* key, + const void* value) +{ + wListDictionaryItem* item = (wListDictionaryItem*)calloc(1, sizeof(wListDictionaryItem)); + if (!item) + return NULL; + + if (listDictionary->objectKey.fnObjectNew) + item->key = listDictionary->objectKey.fnObjectNew(key); + else + item->key = (void*)(uintptr_t)key; + if (!item->key) + goto fail; + + item_set(listDictionary, item, value); + if (value && !item->value) + goto fail; + + return item; + +fail: + item_free(listDictionary, item); + return NULL; +} + +/** + * Adds an entry with the specified key and value into the ListDictionary. + */ + +BOOL ListDictionary_Add(wListDictionary* listDictionary, const void* key, const void* value) +{ + BOOL ret = FALSE; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + wListDictionaryItem* item = new_item(listDictionary, key, value); + + if (!item) + goto out_error; + + if (!listDictionary->head) + { + listDictionary->head = item; + } + else + { + wListDictionaryItem* lastItem = listDictionary->head; + + while (lastItem->next) + lastItem = lastItem->next; + + lastItem->next = item; + } + + ret = TRUE; +out_error: + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return ret; +} + +/** + * Removes all entries from the ListDictionary. + */ + +void ListDictionary_Clear(wListDictionary* listDictionary) +{ + wListDictionaryItem* item = NULL; + wListDictionaryItem* nextItem = NULL; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + if (listDictionary->head) + { + item = listDictionary->head; + + while (item) + { + nextItem = item->next; + + item_free(listDictionary, item); + item = nextItem; + } + + listDictionary->head = NULL; + } + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); +} + +/** + * Determines whether the ListDictionary contains a specific key. + */ + +BOOL ListDictionary_Contains(wListDictionary* listDictionary, const void* key) +{ + wListDictionaryItem* item = NULL; + OBJECT_EQUALS_FN keyEquals = NULL; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&(listDictionary->lock)); + + keyEquals = listDictionary->objectKey.fnObjectEquals; + item = listDictionary->head; + + while (item) + { + if (keyEquals(item->key, key)) + break; + + item = item->next; + } + + if (listDictionary->synchronized) + LeaveCriticalSection(&(listDictionary->lock)); + + return (item) ? TRUE : FALSE; +} + +/** + * Removes the entry with the specified key from the ListDictionary. + */ + +static void* ListDictionary_RemoveOrTake(wListDictionary* listDictionary, const void* key, + BOOL take) +{ + void* value = NULL; + wListDictionaryItem* item = NULL; + wListDictionaryItem* prevItem = NULL; + OBJECT_EQUALS_FN keyEquals = NULL; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + keyEquals = listDictionary->objectKey.fnObjectEquals; + item = listDictionary->head; + prevItem = NULL; + + while (item) + { + if (keyEquals(item->key, key)) + { + if (!prevItem) + listDictionary->head = item->next; + else + prevItem->next = item->next; + + if (take) + { + value = item->value; + item->value = NULL; + } + item_free(listDictionary, item); + break; + } + + prevItem = item; + item = item->next; + } + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return value; +} + +void ListDictionary_Remove(wListDictionary* listDictionary, const void* key) +{ + ListDictionary_RemoveOrTake(listDictionary, key, FALSE); +} + +void* ListDictionary_Take(wListDictionary* listDictionary, const void* key) +{ + return ListDictionary_RemoveOrTake(listDictionary, key, TRUE); +} + +/** + * Removes the first (head) entry from the list + */ + +static void* ListDictionary_Remove_Or_Take_Head(wListDictionary* listDictionary, BOOL take) +{ + wListDictionaryItem* item = NULL; + void* value = NULL; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + if (listDictionary->head) + { + item = listDictionary->head; + listDictionary->head = listDictionary->head->next; + if (take) + { + value = item->value; + item->value = NULL; + } + item_free(listDictionary, item); + } + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return value; +} + +void ListDictionary_Remove_Head(wListDictionary* listDictionary) +{ + ListDictionary_Remove_Or_Take_Head(listDictionary, FALSE); +} + +void* ListDictionary_Take_Head(wListDictionary* listDictionary) +{ + return ListDictionary_Remove_Or_Take_Head(listDictionary, TRUE); +} + +/** + * Get an item value using key + */ + +void* ListDictionary_GetItemValue(wListDictionary* listDictionary, const void* key) +{ + void* value = NULL; + wListDictionaryItem* item = NULL; + OBJECT_EQUALS_FN keyEquals = NULL; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + keyEquals = listDictionary->objectKey.fnObjectEquals; + + if (listDictionary->head) + { + item = listDictionary->head; + + while (item) + { + if (keyEquals(item->key, key)) + break; + + item = item->next; + } + } + + value = (item) ? item->value : NULL; + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return value; +} + +/** + * Set an item value using key + */ + +BOOL ListDictionary_SetItemValue(wListDictionary* listDictionary, const void* key, + const void* value) +{ + BOOL status = FALSE; + wListDictionaryItem* item = NULL; + OBJECT_EQUALS_FN keyEquals = NULL; + + WINPR_ASSERT(listDictionary); + + if (listDictionary->synchronized) + EnterCriticalSection(&listDictionary->lock); + + keyEquals = listDictionary->objectKey.fnObjectEquals; + + if (listDictionary->head) + { + item = listDictionary->head; + + while (item) + { + if (keyEquals(item->key, key)) + break; + + item = item->next; + } + + if (item) + item_set(listDictionary, item, value); + + status = (item) ? TRUE : FALSE; + } + + if (listDictionary->synchronized) + LeaveCriticalSection(&listDictionary->lock); + + return status; +} + +static BOOL default_equal_function(const void* obj1, const void* obj2) +{ + return (obj1 == obj2); +} +/** + * Construction, Destruction + */ + +wListDictionary* ListDictionary_New(BOOL synchronized) +{ + wListDictionary* listDictionary = (wListDictionary*)calloc(1, sizeof(wListDictionary)); + + if (!listDictionary) + return NULL; + + listDictionary->synchronized = synchronized; + + if (!InitializeCriticalSectionAndSpinCount(&(listDictionary->lock), 4000)) + { + free(listDictionary); + return NULL; + } + + listDictionary->objectKey.fnObjectEquals = default_equal_function; + listDictionary->objectValue.fnObjectEquals = default_equal_function; + return listDictionary; +} + +void ListDictionary_Free(wListDictionary* listDictionary) +{ + if (listDictionary) + { + ListDictionary_Clear(listDictionary); + DeleteCriticalSection(&listDictionary->lock); + free(listDictionary); + } +} diff --git a/winpr/libwinpr/utils/collections/MessagePipe.c b/winpr/libwinpr/utils/collections/MessagePipe.c new file mode 100644 index 0000000..98adfe7 --- /dev/null +++ b/winpr/libwinpr/utils/collections/MessagePipe.c @@ -0,0 +1,80 @@ +/** + * WinPR: Windows Portable Runtime + * Message Pipe + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +/** + * Properties + */ + +/** + * Methods + */ + +void MessagePipe_PostQuit(wMessagePipe* pipe, int nExitCode) +{ + MessageQueue_PostQuit(pipe->In, nExitCode); + MessageQueue_PostQuit(pipe->Out, nExitCode); +} + +/** + * Construction, Destruction + */ + +wMessagePipe* MessagePipe_New(void) +{ + wMessagePipe* pipe = NULL; + + pipe = (wMessagePipe*)malloc(sizeof(wMessagePipe)); + + if (!pipe) + return NULL; + + pipe->In = MessageQueue_New(NULL); + if (!pipe->In) + goto error_in; + + pipe->Out = MessageQueue_New(NULL); + if (!pipe->Out) + goto error_out; + + return pipe; + +error_out: + MessageQueue_Free(pipe->In); +error_in: + free(pipe); + return NULL; +} + +void MessagePipe_Free(wMessagePipe* pipe) +{ + if (pipe) + { + MessageQueue_Free(pipe->In); + MessageQueue_Free(pipe->Out); + + free(pipe); + } +} diff --git a/winpr/libwinpr/utils/collections/MessageQueue.c b/winpr/libwinpr/utils/collections/MessageQueue.c new file mode 100644 index 0000000..5481bd4 --- /dev/null +++ b/winpr/libwinpr/utils/collections/MessageQueue.c @@ -0,0 +1,313 @@ +/** + * WinPR: Windows Portable Runtime + * Message Queue + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include + +struct s_wMessageQueue +{ + size_t head; + size_t tail; + size_t size; + size_t capacity; + BOOL closed; + wMessage* array; + CRITICAL_SECTION lock; + HANDLE event; + + wObject object; +}; + +/** + * Message Queue inspired from Windows: + * http://msdn.microsoft.com/en-us/library/ms632590/ + */ + +/** + * Properties + */ + +wObject* MessageQueue_Object(wMessageQueue* queue) +{ + WINPR_ASSERT(queue); + return &queue->object; +} + +/** + * Gets an event which is set when the queue is non-empty + */ + +HANDLE MessageQueue_Event(wMessageQueue* queue) +{ + WINPR_ASSERT(queue); + return queue->event; +} + +/** + * Gets the queue size + */ + +size_t MessageQueue_Size(wMessageQueue* queue) +{ + WINPR_ASSERT(queue); + return queue->size; +} + +/** + * Methods + */ + +BOOL MessageQueue_Wait(wMessageQueue* queue) +{ + BOOL status = FALSE; + + WINPR_ASSERT(queue); + if (WaitForSingleObject(queue->event, INFINITE) == WAIT_OBJECT_0) + status = TRUE; + + return status; +} + +static BOOL MessageQueue_EnsureCapacity(wMessageQueue* queue, size_t count) +{ + WINPR_ASSERT(queue); + + if (queue->size + count >= queue->capacity) + { + wMessage* new_arr = NULL; + size_t old_capacity = queue->capacity; + size_t new_capacity = queue->capacity * 2; + + if (new_capacity < queue->size + count) + new_capacity = queue->size + count; + + new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity); + if (!new_arr) + return FALSE; + queue->array = new_arr; + queue->capacity = new_capacity; + ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(wMessage)); + + /* rearrange wrapped entries */ + if (queue->tail <= queue->head) + { + CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(wMessage)); + queue->tail += old_capacity; + } + } + + return TRUE; +} + +BOOL MessageQueue_Dispatch(wMessageQueue* queue, const wMessage* message) +{ + wMessage* dst = NULL; + BOOL ret = FALSE; + WINPR_ASSERT(queue); + + if (!message) + return FALSE; + + WINPR_ASSERT(queue); + EnterCriticalSection(&queue->lock); + + if (queue->closed) + goto out; + + if (!MessageQueue_EnsureCapacity(queue, 1)) + goto out; + + dst = &(queue->array[queue->tail]); + *dst = *message; + dst->time = GetTickCount64(); + + queue->tail = (queue->tail + 1) % queue->capacity; + queue->size++; + + if (queue->size > 0) + SetEvent(queue->event); + + if (message->id == WMQ_QUIT) + queue->closed = TRUE; + + ret = TRUE; +out: + LeaveCriticalSection(&queue->lock); + return ret; +} + +BOOL MessageQueue_Post(wMessageQueue* queue, void* context, UINT32 type, void* wParam, void* lParam) +{ + wMessage message = { 0 }; + + message.context = context; + message.id = type; + message.wParam = wParam; + message.lParam = lParam; + message.Free = NULL; + + return MessageQueue_Dispatch(queue, &message); +} + +BOOL MessageQueue_PostQuit(wMessageQueue* queue, int nExitCode) +{ + return MessageQueue_Post(queue, NULL, WMQ_QUIT, (void*)(size_t)nExitCode, NULL); +} + +int MessageQueue_Get(wMessageQueue* queue, wMessage* message) +{ + int status = -1; + + if (!MessageQueue_Wait(queue)) + return status; + + EnterCriticalSection(&queue->lock); + + if (queue->size > 0) + { + CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage)); + ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage)); + queue->head = (queue->head + 1) % queue->capacity; + queue->size--; + + if (queue->size < 1) + ResetEvent(queue->event); + + status = (message->id != WMQ_QUIT) ? 1 : 0; + } + + LeaveCriticalSection(&queue->lock); + + return status; +} + +int MessageQueue_Peek(wMessageQueue* queue, wMessage* message, BOOL remove) +{ + int status = 0; + + WINPR_ASSERT(queue); + EnterCriticalSection(&queue->lock); + + if (queue->size > 0) + { + CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage)); + status = 1; + + if (remove) + { + ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage)); + queue->head = (queue->head + 1) % queue->capacity; + queue->size--; + + if (queue->size < 1) + ResetEvent(queue->event); + } + } + + LeaveCriticalSection(&queue->lock); + + return status; +} + +/** + * Construction, Destruction + */ + +wMessageQueue* MessageQueue_New(const wObject* callback) +{ + wMessageQueue* queue = NULL; + + queue = (wMessageQueue*)calloc(1, sizeof(wMessageQueue)); + if (!queue) + return NULL; + + if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000)) + goto fail; + + if (!MessageQueue_EnsureCapacity(queue, 32)) + goto fail; + + queue->event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!queue->event) + goto fail; + + if (callback) + queue->object = *callback; + + return queue; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + MessageQueue_Free(queue); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void MessageQueue_Free(wMessageQueue* queue) +{ + if (!queue) + return; + + if (queue->event) + MessageQueue_Clear(queue); + + CloseHandle(queue->event); + DeleteCriticalSection(&queue->lock); + + free(queue->array); + free(queue); +} + +int MessageQueue_Clear(wMessageQueue* queue) +{ + int status = 0; + + WINPR_ASSERT(queue); + WINPR_ASSERT(queue->event); + + EnterCriticalSection(&queue->lock); + + while (queue->size > 0) + { + wMessage* msg = &(queue->array[queue->head]); + + /* Free resources of message. */ + if (queue->object.fnObjectUninit) + queue->object.fnObjectUninit(msg); + if (queue->object.fnObjectFree) + queue->object.fnObjectFree(msg); + + ZeroMemory(msg, sizeof(wMessage)); + + queue->head = (queue->head + 1) % queue->capacity; + queue->size--; + } + ResetEvent(queue->event); + queue->closed = FALSE; + + LeaveCriticalSection(&queue->lock); + + return status; +} diff --git a/winpr/libwinpr/utils/collections/Object.c b/winpr/libwinpr/utils/collections/Object.c new file mode 100644 index 0000000..8bee86c --- /dev/null +++ b/winpr/libwinpr/utils/collections/Object.c @@ -0,0 +1,42 @@ +/** + * WinPR: Windows Portable Runtime + * Collections + * + * Copyright 2024 Armin Novak + * Copyright 2024 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +void* winpr_ObjectStringClone(const void* pvstr) +{ + const char* str = pvstr; + if (!str) + return NULL; + return _strdup(str); +} + +void* winpr_ObjectWStringClone(const void* pvstr) +{ + const WCHAR* str = pvstr; + if (!str) + return NULL; + return _wcsdup(str); +} + +void winpr_ObjectStringFree(void* pvstr) +{ + free(pvstr); +} diff --git a/winpr/libwinpr/utils/collections/ObjectPool.c b/winpr/libwinpr/utils/collections/ObjectPool.c new file mode 100644 index 0000000..d7bb623 --- /dev/null +++ b/winpr/libwinpr/utils/collections/ObjectPool.c @@ -0,0 +1,185 @@ +/** + * WinPR: Windows Portable Runtime + * Object Pool + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +struct s_wObjectPool +{ + size_t size; + size_t capacity; + void** array; + CRITICAL_SECTION lock; + wObject object; + BOOL synchronized; +}; + +/** + * C Object Pool similar to C# BufferManager Class: + * http://msdn.microsoft.com/en-us/library/ms405814.aspx + */ + +/** + * Methods + */ + +static void ObjectPool_Lock(wObjectPool* pool) +{ + WINPR_ASSERT(pool); + if (pool->synchronized) + EnterCriticalSection(&pool->lock); +} + +static void ObjectPool_Unlock(wObjectPool* pool) +{ + WINPR_ASSERT(pool); + if (pool->synchronized) + LeaveCriticalSection(&pool->lock); +} + +/** + * Gets an object from the pool. + */ + +void* ObjectPool_Take(wObjectPool* pool) +{ + void* obj = NULL; + + ObjectPool_Lock(pool); + + if (pool->size > 0) + obj = pool->array[--(pool->size)]; + + if (!obj) + { + if (pool->object.fnObjectNew) + obj = pool->object.fnObjectNew(NULL); + } + + if (pool->object.fnObjectInit) + pool->object.fnObjectInit(obj); + + ObjectPool_Unlock(pool); + + return obj; +} + +/** + * Returns an object to the pool. + */ + +void ObjectPool_Return(wObjectPool* pool, void* obj) +{ + ObjectPool_Lock(pool); + + if ((pool->size + 1) >= pool->capacity) + { + size_t new_cap = 0; + void** new_arr = NULL; + + new_cap = pool->capacity * 2; + new_arr = (void**)realloc(pool->array, sizeof(void*) * new_cap); + if (!new_arr) + goto out; + + pool->array = new_arr; + pool->capacity = new_cap; + } + + pool->array[(pool->size)++] = obj; + + if (pool->object.fnObjectUninit) + pool->object.fnObjectUninit(obj); + +out: + ObjectPool_Unlock(pool); +} + +wObject* ObjectPool_Object(wObjectPool* pool) +{ + WINPR_ASSERT(pool); + return &pool->object; +} + +/** + * Releases the buffers currently cached in the pool. + */ + +void ObjectPool_Clear(wObjectPool* pool) +{ + ObjectPool_Lock(pool); + + while (pool->size > 0) + { + (pool->size)--; + + if (pool->object.fnObjectFree) + pool->object.fnObjectFree(pool->array[pool->size]); + } + + ObjectPool_Unlock(pool); +} + +/** + * Construction, Destruction + */ + +wObjectPool* ObjectPool_New(BOOL synchronized) +{ + wObjectPool* pool = NULL; + + pool = (wObjectPool*)calloc(1, sizeof(wObjectPool)); + + if (pool) + { + pool->capacity = 32; + pool->size = 0; + pool->array = (void**)calloc(pool->capacity, sizeof(void*)); + if (!pool->array) + { + free(pool); + return NULL; + } + pool->synchronized = synchronized; + + if (pool->synchronized) + InitializeCriticalSectionAndSpinCount(&pool->lock, 4000); + } + + return pool; +} + +void ObjectPool_Free(wObjectPool* pool) +{ + if (pool) + { + ObjectPool_Clear(pool); + + if (pool->synchronized) + DeleteCriticalSection(&pool->lock); + + free(pool->array); + + free(pool); + } +} diff --git a/winpr/libwinpr/utils/collections/PubSub.c b/winpr/libwinpr/utils/collections/PubSub.c new file mode 100644 index 0000000..0efffb7 --- /dev/null +++ b/winpr/libwinpr/utils/collections/PubSub.c @@ -0,0 +1,265 @@ +/** + * WinPR: Windows Portable Runtime + * Publisher/Subscriber Pattern + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +/** + * Events (C# Programming Guide) + * http://msdn.microsoft.com/en-us/library/awbftdfh.aspx + */ + +struct s_wPubSub +{ + CRITICAL_SECTION lock; + BOOL synchronized; + + size_t size; + size_t count; + wEventType* events; +}; + +/** + * Properties + */ + +wEventType* PubSub_GetEventTypes(wPubSub* pubSub, size_t* count) +{ + WINPR_ASSERT(pubSub); + if (count) + *count = pubSub->count; + + return pubSub->events; +} + +/** + * Methods + */ + +void PubSub_Lock(wPubSub* pubSub) +{ + WINPR_ASSERT(pubSub); + if (pubSub->synchronized) + EnterCriticalSection(&pubSub->lock); +} + +void PubSub_Unlock(wPubSub* pubSub) +{ + WINPR_ASSERT(pubSub); + if (pubSub->synchronized) + LeaveCriticalSection(&pubSub->lock); +} + +wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName) +{ + wEventType* event = NULL; + + WINPR_ASSERT(pubSub); + WINPR_ASSERT(EventName); + for (size_t index = 0; index < pubSub->count; index++) + { + if (strcmp(pubSub->events[index].EventName, EventName) == 0) + { + event = &(pubSub->events[index]); + break; + } + } + + return event; +} + +void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, size_t count) +{ + WINPR_ASSERT(pubSub); + WINPR_ASSERT(events || (count == 0)); + if (pubSub->synchronized) + PubSub_Lock(pubSub); + + while (pubSub->count + count >= pubSub->size) + { + size_t new_size = 0; + wEventType* new_event = NULL; + + new_size = pubSub->size * 2; + new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType)); + if (!new_event) + return; + pubSub->size = new_size; + pubSub->events = new_event; + } + + CopyMemory(&pubSub->events[pubSub->count], events, count * sizeof(wEventType)); + pubSub->count += count; + + if (pubSub->synchronized) + PubSub_Unlock(pubSub); +} + +int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, ...) +{ + wEventType* event = NULL; + int status = -1; + WINPR_ASSERT(pubSub); + + va_list ap; + va_start(ap, EventName); + pEventHandler EventHandler = va_arg(ap, pEventHandler); + + if (pubSub->synchronized) + PubSub_Lock(pubSub); + + event = PubSub_FindEventType(pubSub, EventName); + + if (event) + { + status = 0; + + if (event->EventHandlerCount < MAX_EVENT_HANDLERS) + event->EventHandlers[event->EventHandlerCount++] = EventHandler; + else + status = -1; + } + + if (pubSub->synchronized) + PubSub_Unlock(pubSub); + + va_end(ap); + return status; +} + +int PubSub_Unsubscribe(wPubSub* pubSub, const char* EventName, ...) +{ + wEventType* event = NULL; + int status = -1; + WINPR_ASSERT(pubSub); + WINPR_ASSERT(EventName); + + va_list ap; + va_start(ap, EventName); + pEventHandler EventHandler = va_arg(ap, pEventHandler); + + if (pubSub->synchronized) + PubSub_Lock(pubSub); + + event = PubSub_FindEventType(pubSub, EventName); + + if (event) + { + status = 0; + + for (size_t index = 0; index < event->EventHandlerCount; index++) + { + if (event->EventHandlers[index] == EventHandler) + { + event->EventHandlers[index] = NULL; + event->EventHandlerCount--; + MoveMemory(&event->EventHandlers[index], &event->EventHandlers[index + 1], + (MAX_EVENT_HANDLERS - index - 1) * sizeof(pEventHandler)); + status = 1; + } + } + } + + if (pubSub->synchronized) + PubSub_Unlock(pubSub); + + va_end(ap); + return status; +} + +int PubSub_OnEvent(wPubSub* pubSub, const char* EventName, void* context, const wEventArgs* e) +{ + wEventType* event = NULL; + int status = -1; + + if (!pubSub) + return -1; + WINPR_ASSERT(e); + + if (pubSub->synchronized) + PubSub_Lock(pubSub); + + event = PubSub_FindEventType(pubSub, EventName); + + if (pubSub->synchronized) + PubSub_Unlock(pubSub); + + if (event) + { + status = 0; + + for (size_t index = 0; index < event->EventHandlerCount; index++) + { + if (event->EventHandlers[index]) + { + event->EventHandlers[index](context, e); + status++; + } + } + } + + return status; +} + +/** + * Construction, Destruction + */ + +wPubSub* PubSub_New(BOOL synchronized) +{ + wPubSub* pubSub = (wPubSub*)calloc(1, sizeof(wPubSub)); + + if (!pubSub) + return NULL; + + pubSub->synchronized = synchronized; + + if (pubSub->synchronized && !InitializeCriticalSectionAndSpinCount(&pubSub->lock, 4000)) + goto fail; + + pubSub->count = 0; + pubSub->size = 64; + + pubSub->events = (wEventType*)calloc(pubSub->size, sizeof(wEventType)); + if (!pubSub->events) + goto fail; + + return pubSub; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + PubSub_Free(pubSub); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void PubSub_Free(wPubSub* pubSub) +{ + if (pubSub) + { + if (pubSub->synchronized) + DeleteCriticalSection(&pubSub->lock); + + free(pubSub->events); + free(pubSub); + } +} diff --git a/winpr/libwinpr/utils/collections/Queue.c b/winpr/libwinpr/utils/collections/Queue.c new file mode 100644 index 0000000..d24e1dd --- /dev/null +++ b/winpr/libwinpr/utils/collections/Queue.c @@ -0,0 +1,345 @@ +/** + * WinPR: Windows Portable Runtime + * System.Collections.Queue + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +struct s_wQueue +{ + size_t capacity; + size_t growthFactor; + BOOL synchronized; + + BYTE padding[4]; + + size_t head; + size_t tail; + size_t size; + void** array; + CRITICAL_SECTION lock; + HANDLE event; + + wObject object; + BOOL haveLock; + + BYTE padding2[4]; +}; + +/** + * C equivalent of the C# Queue Class: + * http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx + */ + +/** + * Properties + */ + +/** + * Gets the number of elements contained in the Queue. + */ + +size_t Queue_Count(wQueue* queue) +{ + size_t ret = 0; + + Queue_Lock(queue); + + ret = queue->size; + + Queue_Unlock(queue); + + return ret; +} + +/** + * Lock access to the ArrayList + */ + +void Queue_Lock(wQueue* queue) +{ + WINPR_ASSERT(queue); + if (queue->synchronized) + EnterCriticalSection(&queue->lock); +} + +/** + * Unlock access to the ArrayList + */ + +void Queue_Unlock(wQueue* queue) +{ + WINPR_ASSERT(queue); + if (queue->synchronized) + LeaveCriticalSection(&queue->lock); +} + +/** + * Gets an event which is set when the queue is non-empty + */ + +HANDLE Queue_Event(wQueue* queue) +{ + WINPR_ASSERT(queue); + return queue->event; +} + +wObject* Queue_Object(wQueue* queue) +{ + WINPR_ASSERT(queue); + return &queue->object; +} + +/** + * Methods + */ + +/** + * Removes all objects from the Queue. + */ + +void Queue_Clear(wQueue* queue) +{ + Queue_Lock(queue); + + for (size_t index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity) + { + if (queue->object.fnObjectFree) + queue->object.fnObjectFree(queue->array[index]); + + queue->array[index] = NULL; + } + + queue->size = 0; + queue->head = queue->tail = 0; + ResetEvent(queue->event); + Queue_Unlock(queue); +} + +/** + * Determines whether an element is in the Queue. + */ + +BOOL Queue_Contains(wQueue* queue, const void* obj) +{ + BOOL found = FALSE; + + Queue_Lock(queue); + + for (size_t index = 0; index < queue->tail; index++) + { + if (queue->object.fnObjectEquals(queue->array[index], obj)) + { + found = TRUE; + break; + } + } + + Queue_Unlock(queue); + + return found; +} + +static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count) +{ + WINPR_ASSERT(queue); + + if (queue->size + count >= queue->capacity) + { + const size_t old_capacity = queue->capacity; + size_t new_capacity = queue->capacity * queue->growthFactor; + void** newArray = NULL; + if (new_capacity < queue->size + count) + new_capacity = queue->size + count; + newArray = (void**)realloc(queue->array, sizeof(void*) * new_capacity); + + if (!newArray) + return FALSE; + + queue->capacity = new_capacity; + queue->array = newArray; + ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(void*)); + + /* rearrange wrapped entries */ + if (queue->tail <= queue->head) + { + CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(void*)); + queue->tail += old_capacity; + } + } + return TRUE; +} + +/** + * Adds an object to the end of the Queue. + */ + +BOOL Queue_Enqueue(wQueue* queue, const void* obj) +{ + BOOL ret = TRUE; + + Queue_Lock(queue); + + if (!Queue_EnsureCapacity(queue, 1)) + goto out; + + if (queue->object.fnObjectNew) + queue->array[queue->tail] = queue->object.fnObjectNew(obj); + else + { + union + { + const void* cv; + void* v; + } cnv; + cnv.cv = obj; + queue->array[queue->tail] = cnv.v; + } + queue->tail = (queue->tail + 1) % queue->capacity; + queue->size++; + SetEvent(queue->event); +out: + + Queue_Unlock(queue); + + return ret; +} + +/** + * Removes and returns the object at the beginning of the Queue. + */ + +void* Queue_Dequeue(wQueue* queue) +{ + void* obj = NULL; + + Queue_Lock(queue); + + if (queue->size > 0) + { + obj = queue->array[queue->head]; + queue->array[queue->head] = NULL; + queue->head = (queue->head + 1) % queue->capacity; + queue->size--; + } + + if (queue->size < 1) + ResetEvent(queue->event); + + Queue_Unlock(queue); + + return obj; +} + +/** + * Returns the object at the beginning of the Queue without removing it. + */ + +void* Queue_Peek(wQueue* queue) +{ + void* obj = NULL; + + Queue_Lock(queue); + + if (queue->size > 0) + obj = queue->array[queue->head]; + + Queue_Unlock(queue); + + return obj; +} + +void Queue_Discard(wQueue* queue) +{ + void* obj = NULL; + + Queue_Lock(queue); + obj = Queue_Dequeue(queue); + + if (queue->object.fnObjectFree) + queue->object.fnObjectFree(obj); + Queue_Unlock(queue); +} + +static BOOL default_queue_equals(const void* obj1, const void* obj2) +{ + return (obj1 == obj2); +} + +/** + * Construction, Destruction + */ + +wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor) +{ + wObject* obj = NULL; + wQueue* queue = NULL; + queue = (wQueue*)calloc(1, sizeof(wQueue)); + + if (!queue) + return NULL; + + queue->synchronized = synchronized; + + queue->growthFactor = 2; + if (growthFactor > 0) + queue->growthFactor = (size_t)growthFactor; + + if (capacity <= 0) + capacity = 32; + if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000)) + goto fail; + queue->haveLock = TRUE; + if (!Queue_EnsureCapacity(queue, (size_t)capacity)) + goto fail; + + queue->event = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (!queue->event) + goto fail; + + obj = Queue_Object(queue); + obj->fnObjectEquals = default_queue_equals; + + return queue; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + Queue_Free(queue); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void Queue_Free(wQueue* queue) +{ + if (!queue) + return; + + if (queue->haveLock) + { + Queue_Clear(queue); + DeleteCriticalSection(&queue->lock); + } + CloseHandle(queue->event); + free(queue->array); + free(queue); +} diff --git a/winpr/libwinpr/utils/collections/Stack.c b/winpr/libwinpr/utils/collections/Stack.c new file mode 100644 index 0000000..f8b5c5e --- /dev/null +++ b/winpr/libwinpr/utils/collections/Stack.c @@ -0,0 +1,252 @@ +/** + * WinPR: Windows Portable Runtime + * System.Collections.Stack + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +struct s_wStack +{ + size_t size; + size_t capacity; + void** array; + CRITICAL_SECTION lock; + BOOL synchronized; + wObject object; +}; + +/** + * C equivalent of the C# Stack Class: + * http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx + */ + +/** + * Properties + */ + +/** + * Gets the number of elements contained in the Stack. + */ + +size_t Stack_Count(wStack* stack) +{ + size_t ret = 0; + WINPR_ASSERT(stack); + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + + ret = stack->size; + + if (stack->synchronized) + LeaveCriticalSection(&stack->lock); + + return ret; +} + +/** + * Gets a value indicating whether access to the Stack is synchronized (thread safe). + */ + +BOOL Stack_IsSynchronized(wStack* stack) +{ + WINPR_ASSERT(stack); + return stack->synchronized; +} + +wObject* Stack_Object(wStack* stack) +{ + WINPR_ASSERT(stack); + return &stack->object; +} + +/** + * Methods + */ + +/** + * Removes all objects from the Stack. + */ + +void Stack_Clear(wStack* stack) +{ + WINPR_ASSERT(stack); + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + + for (size_t index = 0; index < stack->size; index++) + { + if (stack->object.fnObjectFree) + stack->object.fnObjectFree(stack->array[index]); + + stack->array[index] = NULL; + } + + stack->size = 0; + + if (stack->synchronized) + LeaveCriticalSection(&stack->lock); +} + +/** + * Determines whether an element is in the Stack. + */ + +BOOL Stack_Contains(wStack* stack, const void* obj) +{ + BOOL found = FALSE; + + WINPR_ASSERT(stack); + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + + for (size_t i = 0; i < stack->size; i++) + { + if (stack->object.fnObjectEquals(stack->array[i], obj)) + { + found = TRUE; + break; + } + } + + if (stack->synchronized) + LeaveCriticalSection(&stack->lock); + + return found; +} + +/** + * Inserts an object at the top of the Stack. + */ + +void Stack_Push(wStack* stack, void* obj) +{ + WINPR_ASSERT(stack); + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + + if ((stack->size + 1) >= stack->capacity) + { + const size_t new_cap = stack->capacity * 2; + void** new_arr = (void**)realloc(stack->array, sizeof(void*) * new_cap); + + if (!new_arr) + goto end; + + stack->array = new_arr; + stack->capacity = new_cap; + } + + stack->array[(stack->size)++] = obj; + +end: + if (stack->synchronized) + LeaveCriticalSection(&stack->lock); +} + +/** + * Removes and returns the object at the top of the Stack. + */ + +void* Stack_Pop(wStack* stack) +{ + void* obj = NULL; + + WINPR_ASSERT(stack); + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + + if (stack->size > 0) + obj = stack->array[--(stack->size)]; + + if (stack->synchronized) + LeaveCriticalSection(&stack->lock); + + return obj; +} + +/** + * Returns the object at the top of the Stack without removing it. + */ + +void* Stack_Peek(wStack* stack) +{ + void* obj = NULL; + + WINPR_ASSERT(stack); + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + + if (stack->size > 0) + obj = stack->array[stack->size - 1]; + + if (stack->synchronized) + LeaveCriticalSection(&stack->lock); + + return obj; +} + +static BOOL default_stack_equals(const void* obj1, const void* obj2) +{ + return (obj1 == obj2); +} + +/** + * Construction, Destruction + */ + +wStack* Stack_New(BOOL synchronized) +{ + wStack* stack = NULL; + stack = (wStack*)calloc(1, sizeof(wStack)); + + if (!stack) + return NULL; + + stack->object.fnObjectEquals = default_stack_equals; + stack->synchronized = synchronized; + stack->capacity = 32; + stack->array = (void**)calloc(stack->capacity, sizeof(void*)); + + if (!stack->array) + goto out_free; + + if (stack->synchronized && !InitializeCriticalSectionAndSpinCount(&stack->lock, 4000)) + goto out_free; + + return stack; +out_free: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + Stack_Free(stack); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void Stack_Free(wStack* stack) +{ + if (stack) + { + if (stack->synchronized) + DeleteCriticalSection(&stack->lock); + + free(stack->array); + free(stack); + } +} diff --git a/winpr/libwinpr/utils/collections/StreamPool.c b/winpr/libwinpr/utils/collections/StreamPool.c new file mode 100644 index 0000000..910309f --- /dev/null +++ b/winpr/libwinpr/utils/collections/StreamPool.c @@ -0,0 +1,407 @@ +/** + * WinPR: Windows Portable Runtime + * Object Pool + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#include "../stream.h" + +struct s_wStreamPool +{ + size_t aSize; + size_t aCapacity; + wStream** aArray; + + size_t uSize; + size_t uCapacity; + wStream** uArray; + + CRITICAL_SECTION lock; + BOOL synchronized; + size_t defaultSize; +}; + +/** + * Lock the stream pool + */ + +static INLINE void StreamPool_Lock(wStreamPool* pool) +{ + WINPR_ASSERT(pool); + if (pool->synchronized) + EnterCriticalSection(&pool->lock); +} + +/** + * Unlock the stream pool + */ + +static INLINE void StreamPool_Unlock(wStreamPool* pool) +{ + WINPR_ASSERT(pool); + if (pool->synchronized) + LeaveCriticalSection(&pool->lock); +} + +static BOOL StreamPool_EnsureCapacity(wStreamPool* pool, size_t count, BOOL usedOrAvailable) +{ + size_t new_cap = 0; + size_t* cap = NULL; + size_t* size = NULL; + wStream*** array = NULL; + + WINPR_ASSERT(pool); + + cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity; + size = (usedOrAvailable) ? &pool->uSize : &pool->aSize; + array = (usedOrAvailable) ? &pool->uArray : &pool->aArray; + if (*cap == 0) + new_cap = *size + count; + else if (*size + count > *cap) + new_cap = *cap * 2; + else if ((*size + count) < *cap / 3) + new_cap = *cap / 2; + + if (new_cap > 0) + { + wStream** new_arr = NULL; + + if (*cap < *size + count) + *cap += count; + + new_arr = (wStream**)realloc(*array, sizeof(wStream*) * new_cap); + if (!new_arr) + return FALSE; + *cap = new_cap; + *array = new_arr; + } + return TRUE; +} + +/** + * Methods + */ + +static void StreamPool_ShiftUsed(wStreamPool* pool, size_t index, INT64 count) +{ + WINPR_ASSERT(pool); + if (count > 0) + { + const size_t pcount = (size_t)count; + StreamPool_EnsureCapacity(pool, pcount, TRUE); + + MoveMemory(&pool->uArray[index + pcount], &pool->uArray[index], + (pool->uSize - index) * sizeof(wStream*)); + pool->uSize += pcount; + } + else if (count < 0) + { + const size_t pcount = (size_t)-count; + if ((pool->uSize - index - pcount) > 0) + { + MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount], + (pool->uSize - index - pcount) * sizeof(wStream*)); + } + + pool->uSize -= pcount; + } +} + +/** + * Adds a used stream to the pool. + */ + +static void StreamPool_AddUsed(wStreamPool* pool, wStream* s) +{ + StreamPool_EnsureCapacity(pool, 1, TRUE); + pool->uArray[(pool->uSize)++] = s; +} + +/** + * Removes a used stream from the pool. + */ + +static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s) +{ + WINPR_ASSERT(pool); + for (size_t index = 0; index < pool->uSize; index++) + { + if (pool->uArray[index] == s) + { + StreamPool_ShiftUsed(pool, index, -1); + break; + } + } +} + +static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index, INT64 count) +{ + WINPR_ASSERT(pool); + if (count > 0) + { + const size_t pcount = (size_t)count; + + StreamPool_EnsureCapacity(pool, pcount, FALSE); + MoveMemory(&pool->aArray[index + pcount], &pool->aArray[index], + (pool->aSize - index) * sizeof(wStream*)); + pool->aSize += pcount; + } + else if (count < 0) + { + const size_t pcount = (size_t)-count; + + if ((pool->aSize - index - pcount) > 0) + { + MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount], + (pool->aSize - index - pcount) * sizeof(wStream*)); + } + + pool->aSize -= pcount; + } +} + +/** + * Gets a stream from the pool. + */ + +wStream* StreamPool_Take(wStreamPool* pool, size_t size) +{ + SSIZE_T foundIndex = -1; + wStream* s = NULL; + + StreamPool_Lock(pool); + + if (size == 0) + size = pool->defaultSize; + + for (size_t index = 0; index < pool->aSize; index++) + { + s = pool->aArray[index]; + + if (Stream_Capacity(s) >= size) + { + foundIndex = index; + break; + } + } + + if (foundIndex < 0) + { + s = Stream_New(NULL, size); + if (!s) + goto out_fail; + } + else + { + Stream_SetPosition(s, 0); + Stream_SetLength(s, Stream_Capacity(s)); + StreamPool_ShiftAvailable(pool, foundIndex, -1); + } + + if (s) + { + s->pool = pool; + s->count = 1; + StreamPool_AddUsed(pool, s); + } + +out_fail: + StreamPool_Unlock(pool); + + return s; +} + +/** + * Returns an object to the pool. + */ + +static void StreamPool_Remove(wStreamPool* pool, wStream* s) +{ + StreamPool_EnsureCapacity(pool, 1, FALSE); + Stream_EnsureValidity(s); + for (size_t x = 0; x < pool->aSize; x++) + { + wStream* cs = pool->aArray[x]; + + WINPR_ASSERT(cs != s); + } + pool->aArray[(pool->aSize)++] = s; + StreamPool_RemoveUsed(pool, s); +} + +static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s) +{ + StreamPool_Lock(pool); + if (s->count > 0) + s->count--; + if (s->count == 0) + StreamPool_Remove(pool, s); + StreamPool_Unlock(pool); +} + +void StreamPool_Return(wStreamPool* pool, wStream* s) +{ + WINPR_ASSERT(pool); + if (!s) + return; + + StreamPool_Lock(pool); + StreamPool_Remove(pool, s); + StreamPool_Unlock(pool); +} + +/** + * Increment stream reference count + */ + +void Stream_AddRef(wStream* s) +{ + WINPR_ASSERT(s); + if (s->pool) + { + StreamPool_Lock(s->pool); + s->count++; + StreamPool_Unlock(s->pool); + } +} + +/** + * Decrement stream reference count + */ + +void Stream_Release(wStream* s) +{ + WINPR_ASSERT(s); + if (s->pool) + StreamPool_ReleaseOrReturn(s->pool, s); +} + +/** + * Find stream in pool using pointer inside buffer + */ + +wStream* StreamPool_Find(wStreamPool* pool, BYTE* ptr) +{ + wStream* s = NULL; + BOOL found = FALSE; + + StreamPool_Lock(pool); + + for (size_t index = 0; index < pool->uSize; index++) + { + s = pool->uArray[index]; + + if ((ptr >= Stream_Buffer(s)) && (ptr < (Stream_Buffer(s) + Stream_Capacity(s)))) + { + found = TRUE; + break; + } + } + + StreamPool_Unlock(pool); + + return (found) ? s : NULL; +} + +/** + * Releases the streams currently cached in the pool. + */ + +void StreamPool_Clear(wStreamPool* pool) +{ + StreamPool_Lock(pool); + + while (pool->aSize > 0) + { + wStream* s = pool->aArray[--pool->aSize]; + Stream_Free(s, s->isAllocatedStream); + } + + while (pool->uSize > 0) + { + wStream* s = pool->uArray[--pool->uSize]; + Stream_Free(s, s->isAllocatedStream); + } + + StreamPool_Unlock(pool); +} + +/** + * Construction, Destruction + */ + +wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize) +{ + wStreamPool* pool = NULL; + + pool = (wStreamPool*)calloc(1, sizeof(wStreamPool)); + + if (pool) + { + pool->synchronized = synchronized; + pool->defaultSize = defaultSize; + + if (!StreamPool_EnsureCapacity(pool, 32, FALSE)) + goto fail; + if (!StreamPool_EnsureCapacity(pool, 32, TRUE)) + goto fail; + + InitializeCriticalSectionAndSpinCount(&pool->lock, 4000); + } + + return pool; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + StreamPool_Free(pool); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void StreamPool_Free(wStreamPool* pool) +{ + if (pool) + { + StreamPool_Clear(pool); + + DeleteCriticalSection(&pool->lock); + + free(pool->aArray); + free(pool->uArray); + + free(pool); + } +} + +char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size) +{ + WINPR_ASSERT(pool); + + if (!buffer || (size < 1)) + return NULL; + _snprintf(buffer, size - 1, + "aSize =%" PRIuz ", uSize =%" PRIuz "aCapacity=%" PRIuz ", uCapacity=%" PRIuz, + pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity); + buffer[size - 1] = '\0'; + return buffer; +} diff --git a/winpr/libwinpr/utils/corkscrew/backtrace.h b/winpr/libwinpr/utils/corkscrew/backtrace.h new file mode 100644 index 0000000..408f988 --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/backtrace.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* A stack unwinder. */ + +#ifndef _CORKSCREW_BACKTRACE_H +#define _CORKSCREW_BACKTRACE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include + + /* + * Describes a single frame of a backtrace. + */ + typedef struct + { + uintptr_t absolute_pc; /* absolute PC offset */ + uintptr_t stack_top; /* top of stack for this frame */ + size_t stack_size; /* size of this stack frame */ + } backtrace_frame_t; + + /* + * Describes the symbols associated with a backtrace frame. + */ + typedef struct + { + uintptr_t relative_pc; /* relative frame PC offset from the start of the library, + or the absolute PC if the library is unknown */ + uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the + library or 0 if the library is unknown */ + char* map_name; /* executable or library name, or NULL if unknown */ + char* symbol_name; /* symbol name, or NULL if unknown */ + char* demangled_name; /* demangled symbol name, or NULL if unknown */ + } backtrace_symbol_t; + + /* + * Unwinds the call stack for the current thread of execution. + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + */ + ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + + /* + * Unwinds the call stack for a thread within this process. + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + * + * The task is briefly suspended while the backtrace is being collected. + */ + ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + + /* + * Unwinds the call stack of a task within a remote process using ptrace(). + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + */ + ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + + /* + * Gets the symbols for each frame of a backtrace. + * The symbols array must be big enough to hold one symbol record per frame. + * The symbols must later be freed using free_backtrace_symbols. + */ + void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + + /* + * Gets the symbols for each frame of a backtrace from a remote process. + * The symbols array must be big enough to hold one symbol record per frame. + * The symbols must later be freed using free_backtrace_symbols. + */ + void get_backtrace_symbols_ptrace(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + + /* + * Frees the storage associated with backtrace symbols. + */ + void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames); + + enum + { + // A hint for how big to make the line buffer for format_backtrace_line + MAX_BACKTRACE_LINE_LENGTH = 800, + }; + + /** + * Formats a line from a backtrace as a zero-terminated string into the specified buffer. + */ + void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_BACKTRACE_H diff --git a/winpr/libwinpr/utils/corkscrew/debug.c b/winpr/libwinpr/utils/corkscrew/debug.c new file mode 100644 index 0000000..0fc2fd6 --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/debug.c @@ -0,0 +1,260 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "debug.h" + +#define TAG "com.winpr.utils.debug" +#define LOGT(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_TRACE, __VA_ARGS__); \ + } while (0) +#define LOGD(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_DEBUG, __VA_ARGS__); \ + } while (0) +#define LOGI(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_INFO, __VA_ARGS__); \ + } while (0) +#define LOGW(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_WARN, __VA_ARGS__); \ + } while (0) +#define LOGE(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_ERROR, __VA_ARGS__); \ + } while (0) +#define LOGF(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_FATAL, __VA_ARGS__); \ + } while (0) + +static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!"; + +typedef struct +{ + backtrace_frame_t* buffer; + size_t max; + size_t used; +} t_corkscrew_data; + +typedef struct +{ + void* hdl; + ssize_t (*unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + ssize_t (*unwind_backtrace_thread)(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + ssize_t (*unwind_backtrace_ptrace)(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + void (*get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + void (*get_backtrace_symbols_ptrace)(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + void (*free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames); + void (*format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, + size_t bufferSize); +} t_corkscrew; + +static pthread_once_t initialized = PTHREAD_ONCE_INIT; +static t_corkscrew* fkt = NULL; + +void load_library(void) +{ + static t_corkscrew lib; + { + lib.hdl = dlopen("libcorkscrew.so", RTLD_LAZY); + + if (!lib.hdl) + { + LOGF("dlopen error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace = dlsym(lib.hdl, "unwind_backtrace"); + + if (!lib.unwind_backtrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace_thread = dlsym(lib.hdl, "unwind_backtrace_thread"); + + if (!lib.unwind_backtrace_thread) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace_ptrace = dlsym(lib.hdl, "unwind_backtrace_ptrace"); + + if (!lib.unwind_backtrace_ptrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.get_backtrace_symbols = dlsym(lib.hdl, "get_backtrace_symbols"); + + if (!lib.get_backtrace_symbols) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.get_backtrace_symbols_ptrace = dlsym(lib.hdl, "get_backtrace_symbols_ptrace"); + + if (!lib.get_backtrace_symbols_ptrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.free_backtrace_symbols = dlsym(lib.hdl, "free_backtrace_symbols"); + + if (!lib.free_backtrace_symbols) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.format_backtrace_line = dlsym(lib.hdl, "format_backtrace_line"); + + if (!lib.format_backtrace_line) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + fkt = &lib; + return; + } +fail: +{ + if (lib.hdl) + dlclose(lib.hdl); + + fkt = NULL; +} +} + +void winpr_corkscrew_backtrace_free(void* buffer) +{ + t_corkscrew_data* data = (t_corkscrew_data*)buffer; + if (!data) + return; + + free(data->buffer); + free(data); +} + +void* winpr_corkscrew_backtrace(DWORD size) +{ + t_corkscrew_data* data = calloc(1, sizeof(t_corkscrew_data)); + + if (!data) + return NULL; + + data->buffer = calloc(size, sizeof(backtrace_frame_t)); + + if (!data->buffer) + { + free(data); + return NULL; + } + + pthread_once(&initialized, load_library); + data->max = size; + data->used = fkt->unwind_backtrace(data->buffer, 0, size); + return data; +} + +char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used) +{ + t_corkscrew_data* data = (t_corkscrew_data*)buffer; + if (used) + *used = 0; + + if (!data) + return NULL; + + pthread_once(&initialized, load_library); + + if (!fkt) + { + LOGF(support_msg); + return NULL; + } + else + { + size_t line_len = (data->max > 1024) ? data->max : 1024; + size_t array_size = data->used * sizeof(char*); + size_t lines_size = data->used * line_len; + char** vlines = calloc(1, array_size + lines_size); + backtrace_symbol_t* symbols = calloc(data->used, sizeof(backtrace_symbol_t)); + + if (!vlines || !symbols) + { + free(vlines); + free(symbols); + return NULL; + } + + /* Set the pointers in the allocated buffer's initial array section */ + for (size_t i = 0; i < data->used; i++) + vlines[i] = (char*)vlines + array_size + i * line_len; + + fkt->get_backtrace_symbols(data->buffer, data->used, symbols); + + for (size_t i = 0; i < data->used; i++) + fkt->format_backtrace_line(i, &data->buffer[i], &symbols[i], vlines[i], line_len); + + fkt->free_backtrace_symbols(symbols, data->used); + free(symbols); + + if (used) + *used = data->used; + + return vlines; + } +} diff --git a/winpr/libwinpr/utils/corkscrew/debug.h b/winpr/libwinpr/utils/corkscrew/debug.h new file mode 100644 index 0000000..e98b9bd --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_CORKSCREW_H +#define WINPR_DEBUG_CORKSCREW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_corkscrew_backtrace(DWORD size); + void winpr_corkscrew_backtrace_free(void* buffer); + char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used); + void winpr_corkscrew_backtrace_symbols_fd(void* buffer, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_CORKSCREW_H */ diff --git a/winpr/libwinpr/utils/corkscrew/demangle.h b/winpr/libwinpr/utils/corkscrew/demangle.h new file mode 100644 index 0000000..75c01d7 --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/demangle.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* C++ symbol name demangling. */ + +#ifndef _CORKSCREW_DEMANGLE_H +#define _CORKSCREW_DEMANGLE_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* + * Demangles a C++ symbol name. + * If name is NULL or if the name cannot be demangled, returns NULL. + * Otherwise, returns a newly allocated string that contains the demangled name. + * + * The caller must free the returned string using free(). + */ + char* demangle_symbol_name(const char* name); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_DEMANGLE_H diff --git a/winpr/libwinpr/utils/corkscrew/map_info.h b/winpr/libwinpr/utils/corkscrew/map_info.h new file mode 100644 index 0000000..dc2275e --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/map_info.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Process memory map. */ + +#ifndef _CORKSCREW_MAP_INFO_H +#define _CORKSCREW_MAP_INFO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + struct map_info* next; + uintptr_t start; + uintptr_t end; + bool is_readable; + bool is_writable; + bool is_executable; + void* data; // arbitrary data associated with the map by the user, initially NULL + char name[]; + } map_info_t; + + /* Loads memory map from /proc//maps. */ + map_info_t* load_map_info_list(pid_t tid); + + /* Frees memory map. */ + void free_map_info_list(map_info_t* milist); + + /* Finds the memory map that contains the specified address. */ + const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr); + + /* Returns true if the addr is in a readable map. */ + bool is_readable_map(const map_info_t* milist, uintptr_t addr); + /* Returns true if the addr is in a writable map. */ + bool is_writable_map(const map_info_t* milist, uintptr_t addr); + /* Returns true if the addr is in an executable map. */ + bool is_executable_map(const map_info_t* milist, uintptr_t addr); + + /* Acquires a reference to the memory map for this process. + * The result is cached and refreshed automatically. + * Make sure to release the map info when done. */ + map_info_t* acquire_my_map_info_list(); + + /* Releases a reference to the map info for this process that was + * previous acquired using acquire_my_map_info_list(). */ + void release_my_map_info_list(map_info_t* milist); + + /* Flushes the cached memory map so the next call to + * acquire_my_map_info_list() gets fresh data. */ + void flush_my_map_info_list(); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_MAP_INFO_H diff --git a/winpr/libwinpr/utils/corkscrew/ptrace.h b/winpr/libwinpr/utils/corkscrew/ptrace.h new file mode 100644 index 0000000..8ff7575 --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/ptrace.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Useful ptrace() utility functions. */ + +#ifndef _CORKSCREW_PTRACE_H +#define _CORKSCREW_PTRACE_H + +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Stores information about a process that is used for several different + * ptrace() based operations. */ + typedef struct + { + map_info_t* map_info_list; + } ptrace_context_t; + + /* Describes how to access memory from a process. */ + typedef struct + { + pid_t tid; + const map_info_t* map_info_list; + } memory_t; + +#ifdef __i386__ + /* ptrace() register context. */ + typedef struct pt_regs_x86 + { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t xds; + uint32_t xes; + uint32_t xfs; + uint32_t xgs; + uint32_t orig_eax; + uint32_t eip; + uint32_t xcs; + uint32_t eflags; + uint32_t esp; + uint32_t xss; + } pt_regs_x86_t; +#endif + +#ifdef __mips__ + /* ptrace() GET_REGS context. */ + typedef struct pt_regs_mips + { + uint64_t regs[32]; + uint64_t lo; + uint64_t hi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; + } pt_regs_mips_t; +#endif + + /* + * Initializes a memory structure for accessing memory from this process. + */ + void init_memory(memory_t* memory, const map_info_t* map_info_list); + + /* + * Initializes a memory structure for accessing memory from another process + * using ptrace(). + */ + void init_memory_ptrace(memory_t* memory, pid_t tid); + + /* + * Reads a word of memory safely. + * If the memory is local, ensures that the address is readable before dereferencing it. + * Returns false and a value of 0xffffffff if the word could not be read. + */ + bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value); + + /* + * Reads a word of memory safely using ptrace(). + * Returns false and a value of 0xffffffff if the word could not be read. + */ + bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value); + + /* + * Loads information needed for examining a remote process using ptrace(). + * The caller must already have successfully attached to the process + * using ptrace(). + * + * The context can be used for any threads belonging to that process + * assuming ptrace() is attached to them before performing the actual + * unwinding. The context can continue to be used to decode backtraces + * even after ptrace() has been detached from the process. + */ + ptrace_context_t* load_ptrace_context(pid_t pid); + + /* + * Frees a ptrace context. + */ + void free_ptrace_context(ptrace_context_t* context); + + /* + * Finds a symbol using ptrace. + * Returns the containing map and information about the symbol, or + * NULL if one or the other is not available. + */ + void find_symbol_ptrace(const ptrace_context_t* context, uintptr_t addr, + const map_info_t** out_map_info, const symbol_t** out_symbol); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_PTRACE_H diff --git a/winpr/libwinpr/utils/corkscrew/symbol_table.h b/winpr/libwinpr/utils/corkscrew/symbol_table.h new file mode 100644 index 0000000..380e17d --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/symbol_table.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CORKSCREW_SYMBOL_TABLE_H +#define _CORKSCREW_SYMBOL_TABLE_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + uintptr_t start; + uintptr_t end; + char* name; + } symbol_t; + + typedef struct + { + symbol_t* symbols; + size_t num_symbols; + } symbol_table_t; + + /* + * Loads a symbol table from a given file. + * Returns NULL on error. + */ + symbol_table_t* load_symbol_table(const char* filename); + + /* + * Frees a symbol table. + */ + void free_symbol_table(symbol_table_t* table); + + /* + * Finds a symbol associated with an address in the symbol table. + * Returns NULL if not found. + */ + const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_SYMBOL_TABLE_H diff --git a/winpr/libwinpr/utils/debug.c b/winpr/libwinpr/utils/debug.c new file mode 100644 index 0000000..a7c9a13 --- /dev/null +++ b/winpr/libwinpr/utils/debug.c @@ -0,0 +1,235 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include + +#include +#include + +#if defined(USE_EXECINFO) +#include +#endif + +#if defined(USE_UNWIND) +#include +#endif + +#if defined(WINPR_HAVE_CORKSCREW) +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#endif + +#include +#include + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +#define TAG "com.winpr.utils.debug" +#define LOGT(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_TRACE, __VA_ARGS__); \ + } while (0) +#define LOGD(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_DEBUG, __VA_ARGS__); \ + } while (0) +#define LOGI(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_INFO, __VA_ARGS__); \ + } while (0) +#define LOGW(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_WARN, __VA_ARGS__); \ + } while (0) +#define LOGE(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_ERROR, __VA_ARGS__); \ + } while (0) +#define LOGF(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_FATAL, __VA_ARGS__); \ + } while (0) + +static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!"; + +void winpr_backtrace_free(void* buffer) +{ + if (!buffer) + return; + +#if defined(USE_UNWIND) + winpr_unwind_backtrace_free(buffer); +#elif defined(USE_EXECINFO) + winpr_execinfo_backtrace_free(buffer); +#elif defined(WINPR_HAVE_CORKSCREW) + winpr_corkscrew_backtrace_free(buffer); +#elif defined(_WIN32) || defined(_WIN64) + winpr_win_backtrace_free(buffer); +#else + free(buffer); + LOGF(support_msg); +#endif +} + +void* winpr_backtrace(DWORD size) +{ +#if defined(USE_UNWIND) + return winpr_unwind_backtrace(size); +#elif defined(USE_EXECINFO) + return winpr_execinfo_backtrace(size); +#elif defined(WINPR_HAVE_CORKSCREW) + return winpr_corkscrew_backtrace(size); +#elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) + return winpr_win_backtrace(size); +#else + LOGF(support_msg); + /* return a non NULL buffer to allow the backtrace function familiy to succeed without failing + */ + return _strdup(support_msg); +#endif +} + +char** winpr_backtrace_symbols(void* buffer, size_t* used) +{ + if (used) + *used = 0; + + if (!buffer) + { + LOGF(support_msg); + return NULL; + } + +#if defined(USE_UNWIND) + return winpr_unwind_backtrace_symbols(buffer, used); +#elif defined(USE_EXECINFO) + return winpr_execinfo_backtrace_symbols(buffer, used); +#elif defined(WINPR_HAVE_CORKSCREW) + return winpr_corkscrew_backtrace_symbols(buffer, used); +#elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) + return winpr_win_backtrace_symbols(buffer, used); +#else + LOGF(support_msg); + + /* We return a char** on heap that is compatible with free: + * + * 1. We allocate sizeof(char*) + strlen + 1 bytes. + * 2. The first sizeof(char*) bytes contain the pointer to the string following the pointer. + * 3. The at data + sizeof(char*) contains the actual string + */ + size_t len = strlen(support_msg); + char* ppmsg = calloc(sizeof(char*) + len + 1, sizeof(char)); + if (!ppmsg) + return NULL; + char** msgptr = (char**)ppmsg; + char* msg = &ppmsg[sizeof(char*)]; + + *msgptr = msg; + strncpy(msg, support_msg, len); + *used = 1; + return ppmsg; +#endif +} + +void winpr_backtrace_symbols_fd(void* buffer, int fd) +{ + if (!buffer) + { + LOGF(support_msg); + return; + } + +#if defined(USE_EXECINFO) && !defined(USE_UNWIND) + winpr_execinfo_backtrace_symbols_fd(buffer, fd); +#elif !defined(ANDROID) + { + size_t used = 0; + char** lines = winpr_backtrace_symbols(buffer, &used); + + if (!lines) + return; + + for (size_t i = 0; i < used; i++) + _write(fd, lines[i], (unsigned)strnlen(lines[i], UINT32_MAX)); + free(lines); + } +#else + LOGF(support_msg); +#endif +} + +void winpr_log_backtrace(const char* tag, DWORD level, DWORD size) +{ + winpr_log_backtrace_ex(WLog_Get(tag), level, size); +} + +void winpr_log_backtrace_ex(wLog* log, DWORD level, DWORD size) +{ + size_t used = 0; + char** msg = NULL; + void* stack = winpr_backtrace(20); + + if (!stack) + { + WLog_Print(log, WLOG_ERROR, "winpr_backtrace failed!\n"); + goto fail; + } + + msg = winpr_backtrace_symbols(stack, &used); + + if (msg) + { + for (size_t x = 0; x < used; x++) + WLog_Print(log, level, "%" PRIuz ": %s", x, msg[x]); + } + free(msg); + +fail: + winpr_backtrace_free(stack); +} + +char* winpr_strerror(DWORD dw, char* dmsg, size_t size) +{ +#ifdef __STDC_LIB_EXT1__ + strerror_s(dw, dmsg, size); +#elif defined(WINPR_HAVE_STRERROR_R) + strerror_r(dw, dmsg, size); +#else + _snprintf(dmsg, size, "%s", strerror(dw)); +#endif + return dmsg; +} diff --git a/winpr/libwinpr/utils/execinfo/debug.c b/winpr/libwinpr/utils/execinfo/debug.c new file mode 100644 index 0000000..9867b9d --- /dev/null +++ b/winpr/libwinpr/utils/execinfo/debug.c @@ -0,0 +1,94 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "debug.h" + +typedef struct +{ + void** buffer; + size_t max; + size_t used; +} t_execinfo; + +void winpr_execinfo_backtrace_free(void* buffer) +{ + t_execinfo* data = (t_execinfo*)buffer; + if (!data) + return; + + free(data->buffer); + free(data); +} + +void* winpr_execinfo_backtrace(DWORD size) +{ + t_execinfo* data = calloc(1, sizeof(t_execinfo)); + + if (!data) + return NULL; + + data->buffer = calloc(size, sizeof(void*)); + + if (!data->buffer) + { + free(data); + return NULL; + } + + const int rc = backtrace(data->buffer, size); + if (rc < 0) + { + free(data); + return NULL; + } + data->max = size; + data->used = (size_t)rc; + return data; +} + +char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used) +{ + t_execinfo* data = (t_execinfo*)buffer; + if (used) + *used = 0; + + if (!data) + return NULL; + + if (used) + *used = data->used; + + return backtrace_symbols(data->buffer, data->used); +} + +void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd) +{ + t_execinfo* data = (t_execinfo*)buffer; + + if (!data) + return; + + backtrace_symbols_fd(data->buffer, data->used, fd); +} diff --git a/winpr/libwinpr/utils/execinfo/debug.h b/winpr/libwinpr/utils/execinfo/debug.h new file mode 100644 index 0000000..aaf7748 --- /dev/null +++ b/winpr/libwinpr/utils/execinfo/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_EXECINFO_H +#define WINPR_DEBUG_EXECINFO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_execinfo_backtrace(DWORD size); + void winpr_execinfo_backtrace_free(void* buffer); + char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used); + void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_EXECINFO_H */ diff --git a/winpr/libwinpr/utils/image.c b/winpr/libwinpr/utils/image.c new file mode 100644 index 0000000..cc2e2ad --- /dev/null +++ b/winpr/libwinpr/utils/image.c @@ -0,0 +1,1258 @@ +/** + * WinPR: Windows Portable Runtime + * Image Utils + * + * Copyright 2014 Marc-Andre Moreau + * Copyright 2016 Inuvika Inc. + * Copyright 2016 David PHAM-VAN + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include + +#include + +#if defined(WINPR_UTILS_IMAGE_PNG) +#include +#endif + +#if defined(WINPR_UTILS_IMAGE_JPEG) +#define INT32 INT32_WINPR +#include +#undef INT32 +#endif + +#if defined(WINPR_UTILS_IMAGE_WEBP) +#include +#include +#endif + +#if defined(WITH_LODEPNG) +#include +#endif +#include + +#include "../log.h" +#define TAG WINPR_TAG("utils.image") + +static SSIZE_T winpr_convert_from_jpeg(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data); +static SSIZE_T winpr_convert_from_png(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data); +static SSIZE_T winpr_convert_from_webp(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data); + +static BOOL writeBitmapFileHeader(wStream* s, const WINPR_BITMAP_FILE_HEADER* bf) +{ + if (!Stream_EnsureRemainingCapacity(s, sizeof(WINPR_BITMAP_FILE_HEADER))) + return FALSE; + + Stream_Write_UINT8(s, bf->bfType[0]); + Stream_Write_UINT8(s, bf->bfType[1]); + Stream_Write_UINT32(s, bf->bfSize); + Stream_Write_UINT16(s, bf->bfReserved1); + Stream_Write_UINT16(s, bf->bfReserved2); + Stream_Write_UINT32(s, bf->bfOffBits); + return TRUE; +} + +static BOOL readBitmapFileHeader(wStream* s, WINPR_BITMAP_FILE_HEADER* bf) +{ + if (!s || !bf || (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(WINPR_BITMAP_FILE_HEADER)))) + return FALSE; + + Stream_Read_UINT8(s, bf->bfType[0]); + Stream_Read_UINT8(s, bf->bfType[1]); + Stream_Read_UINT32(s, bf->bfSize); + Stream_Read_UINT16(s, bf->bfReserved1); + Stream_Read_UINT16(s, bf->bfReserved2); + Stream_Read_UINT32(s, bf->bfOffBits); + + if (bf->bfSize < sizeof(WINPR_BITMAP_FILE_HEADER)) + { + WLog_ERR(TAG, ""); + return FALSE; + } + + return Stream_CheckAndLogRequiredCapacity(TAG, s, + bf->bfSize - sizeof(WINPR_BITMAP_FILE_HEADER)); +} + +static BOOL writeBitmapInfoHeader(wStream* s, const WINPR_BITMAP_INFO_HEADER* bi) +{ + if (!Stream_EnsureRemainingCapacity(s, sizeof(WINPR_BITMAP_INFO_HEADER))) + return FALSE; + + Stream_Write_UINT32(s, bi->biSize); + Stream_Write_INT32(s, bi->biWidth); + Stream_Write_INT32(s, bi->biHeight); + Stream_Write_UINT16(s, bi->biPlanes); + Stream_Write_UINT16(s, bi->biBitCount); + Stream_Write_UINT32(s, bi->biCompression); + Stream_Write_UINT32(s, bi->biSizeImage); + Stream_Write_INT32(s, bi->biXPelsPerMeter); + Stream_Write_INT32(s, bi->biYPelsPerMeter); + Stream_Write_UINT32(s, bi->biClrUsed); + Stream_Write_UINT32(s, bi->biClrImportant); + return TRUE; +} + +static BOOL readBitmapInfoHeader(wStream* s, WINPR_BITMAP_INFO_HEADER* bi, size_t* poffset) +{ + if (!s || !bi || (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(WINPR_BITMAP_INFO_HEADER)))) + return FALSE; + + const size_t start = Stream_GetPosition(s); + Stream_Read_UINT32(s, bi->biSize); + Stream_Read_INT32(s, bi->biWidth); + Stream_Read_INT32(s, bi->biHeight); + Stream_Read_UINT16(s, bi->biPlanes); + Stream_Read_UINT16(s, bi->biBitCount); + Stream_Read_UINT32(s, bi->biCompression); + Stream_Read_UINT32(s, bi->biSizeImage); + Stream_Read_INT32(s, bi->biXPelsPerMeter); + Stream_Read_INT32(s, bi->biYPelsPerMeter); + Stream_Read_UINT32(s, bi->biClrUsed); + Stream_Read_UINT32(s, bi->biClrImportant); + + if ((bi->biBitCount < 1) || (bi->biBitCount > 32)) + { + WLog_WARN(TAG, "invalid biBitCount=%" PRIu32, bi->biBitCount); + return FALSE; + } + + /* https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader */ + size_t offset = 0; + switch (bi->biCompression) + { + case BI_RGB: + if (bi->biBitCount <= 8) + { + DWORD used = bi->biClrUsed; + if (used == 0) + used = (1 << bi->biBitCount) / 8; + offset += sizeof(RGBQUAD) * used; + } + if (bi->biSizeImage == 0) + { + UINT32 stride = ((((bi->biWidth * bi->biBitCount) + 31) & ~31) >> 3); + bi->biSizeImage = abs(bi->biHeight) * stride; + } + break; + case BI_BITFIELDS: + offset += sizeof(DWORD) * 3; // 3 DWORD color masks + break; + default: + WLog_ERR(TAG, "unsupported biCompression %" PRIu32, bi->biCompression); + return FALSE; + } + + if (bi->biSizeImage == 0) + { + WLog_ERR(TAG, "invalid biSizeImage %" PRIuz, bi->biSizeImage); + return FALSE; + } + + const size_t pos = Stream_GetPosition(s) - start; + if (bi->biSize < pos) + { + WLog_ERR(TAG, "invalid biSize %" PRIuz " < (actual) offset %" PRIuz, bi->biSize, pos); + return FALSE; + } + + *poffset = offset; + return Stream_SafeSeek(s, bi->biSize - pos); +} + +BYTE* winpr_bitmap_construct_header(size_t width, size_t height, size_t bpp) +{ + BYTE* result = NULL; + WINPR_BITMAP_FILE_HEADER bf = { 0 }; + WINPR_BITMAP_INFO_HEADER bi = { 0 }; + wStream* s = NULL; + size_t imgSize = 0; + + imgSize = width * height * (bpp / 8); + if ((width > INT32_MAX) || (height > INT32_MAX) || (bpp > UINT16_MAX) || (imgSize > UINT32_MAX)) + return NULL; + + s = Stream_New(NULL, WINPR_IMAGE_BMP_HEADER_LEN); + if (!s) + return NULL; + + bf.bfType[0] = 'B'; + bf.bfType[1] = 'M'; + bf.bfReserved1 = 0; + bf.bfReserved2 = 0; + bi.biSize = (UINT32)sizeof(WINPR_BITMAP_INFO_HEADER); + bf.bfOffBits = (UINT32)sizeof(WINPR_BITMAP_FILE_HEADER) + bi.biSize; + bi.biSizeImage = (UINT32)imgSize; + bf.bfSize = bf.bfOffBits + bi.biSizeImage; + bi.biWidth = (INT32)width; + bi.biHeight = -1 * (INT32)height; + bi.biPlanes = 1; + bi.biBitCount = (UINT16)bpp; + bi.biCompression = BI_RGB; + bi.biXPelsPerMeter = (INT32)width; + bi.biYPelsPerMeter = (INT32)height; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + size_t offset = 0; + switch (bi.biCompression) + { + case BI_RGB: + if (bi.biBitCount <= 8) + { + DWORD used = bi.biClrUsed; + if (used == 0) + used = (1 << bi.biBitCount) / 8; + offset += sizeof(RGBQUAD) * used; + } + break; + case BI_BITFIELDS: + offset += sizeof(DWORD) * 3; // 3 DWORD color masks + break; + default: + return FALSE; + } + + if (!writeBitmapFileHeader(s, &bf)) + goto fail; + + if (!writeBitmapInfoHeader(s, &bi)) + goto fail; + + if (!Stream_EnsureRemainingCapacity(s, offset)) + goto fail; + + Stream_Zero(s, offset); + result = Stream_Buffer(s); +fail: + Stream_Free(s, result == 0); + return result; +} + +/** + * Refer to "Compressed Image File Formats: JPEG, PNG, GIF, XBM, BMP" book + */ + +static void* winpr_bitmap_write_buffer(const BYTE* data, size_t size, UINT32 width, UINT32 height, + UINT32 stride, UINT32 bpp, UINT32* pSize) +{ + WINPR_ASSERT(data || (size == 0)); + + void* result = NULL; + const size_t bpp_stride = 1ull * width * (bpp / 8); + wStream* s = Stream_New(NULL, 1024); + + if (stride == 0) + stride = bpp_stride; + + BYTE* bmp_header = winpr_bitmap_construct_header(width, height, bpp); + if (!bmp_header) + goto fail; + if (!Stream_EnsureRemainingCapacity(s, WINPR_IMAGE_BMP_HEADER_LEN)) + goto fail; + Stream_Write(s, bmp_header, WINPR_IMAGE_BMP_HEADER_LEN); + + if (!Stream_EnsureRemainingCapacity(s, stride * height * 1ull)) + goto fail; + + for (size_t y = 0; y < height; y++) + { + const BYTE* line = &data[stride * y]; + + Stream_Write(s, line, stride); + } + + result = Stream_Buffer(s); + *pSize = Stream_GetPosition(s); +fail: + Stream_Free(s, result == NULL); + free(bmp_header); + return result; +} + +int winpr_bitmap_write(const char* filename, const BYTE* data, size_t width, size_t height, + size_t bpp) +{ + return winpr_bitmap_write_ex(filename, data, 0, width, height, bpp); +} + +int winpr_bitmap_write_ex(const char* filename, const BYTE* data, size_t stride, size_t width, + size_t height, size_t bpp) +{ + FILE* fp = NULL; + int ret = -1; + const size_t bpp_stride = ((((width * bpp) + 31) & ~31) >> 3); + + if (stride == 0) + stride = bpp_stride; + + UINT32 bmpsize = 0; + const size_t size = stride * 1ull * height; + void* bmpdata = winpr_bitmap_write_buffer(data, size, width, height, stride, bpp, &bmpsize); + if (!bmpdata) + goto fail; + + fp = winpr_fopen(filename, "w+b"); + if (!fp) + { + WLog_ERR(TAG, "failed to open file %s", filename); + goto fail; + } + + if (fwrite(bmpdata, bmpsize, 1, fp) != 1) + goto fail; + +fail: + if (fp) + fclose(fp); + free(bmpdata); + return ret; +} + +static int write_and_free(const char* filename, void* data, size_t size) +{ + int status = -1; + if (!data) + goto fail; + + FILE* fp = winpr_fopen(filename, "w+b"); + if (!fp) + goto fail; + + size_t w = fwrite(data, 1, size, fp); + fclose(fp); + + status = (w == size) ? 1 : -1; +fail: + free(data); + return status; +} + +int winpr_image_write(wImage* image, const char* filename) +{ + WINPR_ASSERT(image); + return winpr_image_write_ex(image, image->type, filename); +} + +int winpr_image_write_ex(wImage* image, UINT32 format, const char* filename) +{ + WINPR_ASSERT(image); + + size_t size = 0; + void* data = winpr_image_write_buffer(image, format, &size); + if (!data) + return -1; + return write_and_free(filename, data, size); +} + +static int winpr_image_bitmap_read_buffer(wImage* image, const BYTE* buffer, size_t size) +{ + int rc = -1; + BOOL vFlip = 0; + WINPR_BITMAP_FILE_HEADER bf = { 0 }; + WINPR_BITMAP_INFO_HEADER bi = { 0 }; + wStream sbuffer = { 0 }; + wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size); + + if (!s) + return -1; + + size_t bmpoffset = 0; + if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset)) + goto fail; + + if ((bf.bfType[0] != 'B') || (bf.bfType[1] != 'M')) + { + WLog_WARN(TAG, "Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]); + goto fail; + } + + image->type = WINPR_IMAGE_BITMAP; + + const size_t pos = Stream_GetPosition(s); + const size_t expect = bf.bfOffBits; + + if (pos != expect) + { + WLog_WARN(TAG, "pos=%" PRIuz ", expected %" PRIuz ", offset=" PRIuz, pos, expect, + bmpoffset); + goto fail; + } + + if (!Stream_CheckAndLogRequiredCapacity(TAG, s, bi.biSizeImage)) + goto fail; + + if (bi.biWidth <= 0) + { + WLog_WARN(TAG, "bi.biWidth=%" PRId32, bi.biWidth); + goto fail; + } + + image->width = (UINT32)bi.biWidth; + + if (bi.biHeight < 0) + { + vFlip = FALSE; + image->height = (UINT32)(-1 * bi.biHeight); + } + else + { + vFlip = TRUE; + image->height = (UINT32)bi.biHeight; + } + + if (image->height <= 0) + { + WLog_WARN(TAG, "image->height=%" PRIu32, image->height); + goto fail; + } + + image->bitsPerPixel = bi.biBitCount; + image->bytesPerPixel = (image->bitsPerPixel / 8); + image->scanline = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) >> 3); + const size_t bmpsize = 1ull * image->scanline * image->height; + if (bmpsize != bi.biSizeImage) + WLog_WARN(TAG, "bmpsize=%" PRIuz " != bi.biSizeImage=%" PRIu32, bmpsize, bi.biSizeImage); + if (bi.biSizeImage < bmpsize) + goto fail; + + image->data = (BYTE*)malloc(bi.biSizeImage); + + if (!image->data) + goto fail; + + if (!vFlip) + Stream_Read(s, image->data, bi.biSizeImage); + else + { + BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]); + + for (size_t index = 0; index < image->height; index++) + { + Stream_Read(s, pDstData, image->scanline); + pDstData -= image->scanline; + } + } + + rc = 1; +fail: + + if (rc < 0) + { + free(image->data); + image->data = NULL; + } + + return rc; +} + +int winpr_image_read(wImage* image, const char* filename) +{ + int status = -1; + + FILE* fp = winpr_fopen(filename, "rb"); + if (!fp) + { + WLog_ERR(TAG, "failed to open file %s", filename); + return -1; + } + + fseek(fp, 0, SEEK_END); + INT64 pos = _ftelli64(fp); + fseek(fp, 0, SEEK_SET); + + if (pos > 0) + { + char* buffer = malloc((size_t)pos); + if (buffer) + { + size_t r = fread(buffer, 1, (size_t)pos, fp); + if (r == (size_t)pos) + { + status = winpr_image_read_buffer(image, buffer, (size_t)pos); + } + } + free(buffer); + } + fclose(fp); + return status; +} + +int winpr_image_read_buffer(wImage* image, const BYTE* buffer, size_t size) +{ + BYTE sig[12] = { 0 }; + int status = -1; + + if (size < sizeof(sig)) + return -1; + + CopyMemory(sig, buffer, sizeof(sig)); + + if ((sig[0] == 'B') && (sig[1] == 'M')) + { + image->type = WINPR_IMAGE_BITMAP; + status = winpr_image_bitmap_read_buffer(image, buffer, size); + } + else if ((sig[0] == 'R') && (sig[1] == 'I') && (sig[2] == 'F') && (sig[3] == 'F') && + (sig[8] == 'W') && (sig[9] == 'E') && (sig[10] == 'B') && (sig[11] == 'P')) + { + image->type = WINPR_IMAGE_WEBP; + const SSIZE_T rc = + winpr_convert_from_webp((const char*)buffer, size, &image->width, &image->height, + &image->bitsPerPixel, ((char**)&image->data)); + if (rc >= 0) + { + image->bytesPerPixel = (image->bitsPerPixel + 7) / 8; + image->scanline = image->width * image->bytesPerPixel; + status = 1; + } + } + else if ((sig[0] == 0xFF) && (sig[1] == 0xD8) && (sig[2] == 0xFF) && (sig[3] == 0xE0) && + (sig[6] == 0x4A) && (sig[7] == 0x46) && (sig[8] == 0x49) && (sig[9] == 0x46) && + (sig[10] == 0x00)) + { + image->type = WINPR_IMAGE_JPEG; + const SSIZE_T rc = + winpr_convert_from_jpeg((const char*)buffer, size, &image->width, &image->height, + &image->bitsPerPixel, ((char**)&image->data)); + if (rc >= 0) + { + image->bytesPerPixel = (image->bitsPerPixel + 7) / 8; + image->scanline = image->width * image->bytesPerPixel; + status = 1; + } + } + else if ((sig[0] == 0x89) && (sig[1] == 'P') && (sig[2] == 'N') && (sig[3] == 'G') && + (sig[4] == '\r') && (sig[5] == '\n') && (sig[6] == 0x1A) && (sig[7] == '\n')) + { + image->type = WINPR_IMAGE_PNG; + const SSIZE_T rc = + winpr_convert_from_png((const char*)buffer, size, &image->width, &image->height, + &image->bitsPerPixel, ((char**)&image->data)); + if (rc >= 0) + { + image->bytesPerPixel = (image->bitsPerPixel + 7) / 8; + image->scanline = image->width * image->bytesPerPixel; + status = 1; + } + } + + return status; +} + +wImage* winpr_image_new(void) +{ + wImage* image = (wImage*)calloc(1, sizeof(wImage)); + + if (!image) + return NULL; + + return image; +} + +void winpr_image_free(wImage* image, BOOL bFreeBuffer) +{ + if (!image) + return; + + if (bFreeBuffer) + free(image->data); + + free(image); +} + +void* winpr_convert_to_jpeg(const void* data, size_t size, UINT32 width, UINT32 height, + UINT32 stride, UINT32 bpp, UINT32* pSize) +{ + WINPR_ASSERT(data || (size == 0)); + WINPR_ASSERT(pSize); + + *pSize = 0; + +#if !defined(WINPR_UTILS_IMAGE_JPEG) + return NULL; +#else + BYTE* outbuffer = NULL; + unsigned long outsize = 0; + struct jpeg_compress_struct cinfo = { 0 }; + + const size_t expect1 = 1ull * stride * height; + const size_t bytes = (bpp + 7) / 8; + const size_t expect2 = 1ull * width * height * bytes; + if (expect1 != expect2) + return NULL; + if (expect1 > size) + return NULL; + + /* Set up the error handler. */ + struct jpeg_error_mgr jerr = { 0 }; + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_compress(&cinfo); + jpeg_mem_dest(&cinfo, &outbuffer, &outsize); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = (bpp + 7) / 8; + cinfo.in_color_space = (bpp > 24) ? JCS_EXT_BGRA : JCS_EXT_BGR; + cinfo.data_precision = 8; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, 100, TRUE); + /* Use 4:4:4 subsampling (default is 4:2:0) */ + cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1; + + jpeg_start_compress(&cinfo, TRUE); + + const unsigned char* cdata = data; + for (size_t x = 0; x < height; x++) + { + const JDIMENSION offset = x * stride; + const JSAMPROW coffset = &cdata[offset]; + if (jpeg_write_scanlines(&cinfo, &coffset, 1) != 1) + goto fail; + } + +fail: + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + *pSize = outsize; + return outbuffer; +#endif +} + +SSIZE_T winpr_convert_from_jpeg(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data) +{ + WINPR_ASSERT(comp_data || (comp_data_bytes == 0)); + WINPR_ASSERT(width); + WINPR_ASSERT(height); + WINPR_ASSERT(bpp); + WINPR_ASSERT(ppdecomp_data); + +#if !defined(WINPR_UTILS_IMAGE_JPEG) + return -1; +#else + struct jpeg_decompress_struct cinfo = { 0 }; + struct jpeg_error_mgr jerr; + SSIZE_T size = -1; + char* decomp_data = NULL; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_mem_src(&cinfo, comp_data, comp_data_bytes); + + if (jpeg_read_header(&cinfo, 1) != JPEG_HEADER_OK) + goto fail; + + cinfo.out_color_space = cinfo.num_components > 3 ? JCS_EXT_RGBA : JCS_EXT_BGR; + + *width = cinfo.image_width; + *height = cinfo.image_height; + *bpp = cinfo.num_components * 8; + + if (!jpeg_start_decompress(&cinfo)) + goto fail; + + size_t stride = cinfo.image_width * cinfo.num_components; + + decomp_data = calloc(stride, cinfo.image_height); + if (decomp_data) + { + while (cinfo.output_scanline < cinfo.image_height) + { + JSAMPROW row = &decomp_data[cinfo.output_scanline * stride]; + if (jpeg_read_scanlines(&cinfo, &row, 1) != 1) + goto fail; + } + size = stride * cinfo.image_height; + } + jpeg_finish_decompress(&cinfo); + +fail: + jpeg_destroy_decompress(&cinfo); + *ppdecomp_data = decomp_data; + return size; +#endif +} + +void* winpr_convert_to_webp(const void* data, size_t size, UINT32 width, UINT32 height, + UINT32 stride, UINT32 bpp, UINT32* pSize) +{ + WINPR_ASSERT(data || (size == 0)); + WINPR_ASSERT(pSize); + + *pSize = 0; + +#if !defined(WINPR_UTILS_IMAGE_WEBP) + return NULL; +#else + size_t dstSize = 0; + uint8_t* pDstData = NULL; + switch (bpp) + { + case 32: + dstSize = WebPEncodeLosslessBGRA(data, width, height, stride, &pDstData); + break; + case 24: + dstSize = WebPEncodeLosslessBGR(data, width, height, stride, &pDstData); + break; + default: + return NULL; + } + + void* rc = malloc(dstSize); + if (rc) + { + memcpy(rc, pDstData, dstSize); + *pSize = dstSize; + } + WebPFree(pDstData); + return rc; +#endif +} + +SSIZE_T winpr_convert_from_webp(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data) +{ + WINPR_ASSERT(comp_data || (comp_data_bytes == 0)); + WINPR_ASSERT(width); + WINPR_ASSERT(height); + WINPR_ASSERT(bpp); + WINPR_ASSERT(ppdecomp_data); + + *width = 0; + *height = 0; + *bpp = 0; + *ppdecomp_data = NULL; +#if !defined(WINPR_UTILS_IMAGE_WEBP) + return -1; +#else + + uint8_t* dst = WebPDecodeBGRA(comp_data, comp_data_bytes, width, height); + if (!dst) + return -1; + + *bpp = 32; + *ppdecomp_data = dst; + return (*width) * (*height) * 4; +#endif +} + +#if defined(WINPR_UTILS_IMAGE_PNG) +struct png_mem_encode +{ + char* buffer; + size_t size; +}; + +static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* with libpng15 next line causes pointer deference error; use libpng12 */ + struct png_mem_encode* p = + (struct png_mem_encode*)png_get_io_ptr(png_ptr); /* was png_ptr->io_ptr */ + size_t nsize = p->size + length; + + /* allocate or grow buffer */ + if (p->buffer) + p->buffer = realloc(p->buffer, nsize); + else + p->buffer = malloc(nsize); + + if (!p->buffer) + png_error(png_ptr, "Write Error"); + + /* copy new bytes to end of buffer */ + memcpy(p->buffer + p->size, data, length); + p->size += length; +} + +/* This is optional but included to show how png_set_write_fn() is called */ +static void png_flush(png_structp png_ptr) +{ +} + +static SSIZE_T save_png_to_buffer(UINT32 bpp, UINT32 width, UINT32 height, const uint8_t* data, + size_t size, void** pDstData) +{ + int rc = -1; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_byte** row_pointers = NULL; + struct png_mem_encode state = { 0 }; + + *pDstData = NULL; + + if (!data || (size == 0)) + return 0; + + WINPR_ASSERT(pDstData); + + const size_t bytes_per_pixel = (bpp + 7) / 8; + const size_t bytes_per_row = width * bytes_per_pixel; + if (size < bytes_per_row * height) + goto fail; + + /* Initialize the write struct. */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) + goto fail; + + /* Initialize the info struct. */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + goto fail; + + /* Set up error handling. */ + if (setjmp(png_jmpbuf(png_ptr))) + goto fail; + + /* Set image attributes. */ + int colorType = PNG_COLOR_TYPE_PALETTE; + if (bpp > 8) + colorType = PNG_COLOR_TYPE_RGB; + if (bpp > 16) + colorType = PNG_COLOR_TYPE_RGB; + if (bpp > 24) + colorType = PNG_COLOR_TYPE_RGBA; + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, colorType, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + /* Initialize rows of PNG. */ + row_pointers = png_malloc(png_ptr, height * sizeof(png_byte*)); + for (size_t y = 0; y < height; ++y) + { + uint8_t* row = png_malloc(png_ptr, sizeof(uint8_t) * bytes_per_row); + row_pointers[y] = (png_byte*)row; + for (size_t x = 0; x < width; ++x) + { + + *row++ = *data++; + if (bpp > 8) + *row++ = *data++; + if (bpp > 16) + *row++ = *data++; + if (bpp > 24) + *row++ = *data++; + } + } + + /* Actually write the image data. */ + png_set_write_fn(png_ptr, &state, png_write_data, png_flush); + png_set_rows(png_ptr, info_ptr, row_pointers); + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL); + + /* Cleanup. */ + for (size_t y = 0; y < height; y++) + png_free(png_ptr, row_pointers[y]); + png_free(png_ptr, row_pointers); + + /* Finish writing. */ + rc = state.size; + *pDstData = state.buffer; +fail: + png_destroy_write_struct(&png_ptr, &info_ptr); + if (rc < 0) + free(state.buffer); + return rc; +} + +typedef struct +{ + png_bytep buffer; + png_uint_32 bufsize; + png_uint_32 current_pos; +} MEMORY_READER_STATE; + +static void read_data_memory(png_structp png_ptr, png_bytep data, size_t length) +{ + MEMORY_READER_STATE* f = png_get_io_ptr(png_ptr); + if (length > (f->bufsize - f->current_pos)) + png_error(png_ptr, "read error in read_data_memory (loadpng)"); + else + { + memcpy(data, f->buffer + f->current_pos, length); + f->current_pos += length; + } +} + +static void* winpr_read_png_from_buffer(const void* data, size_t SrcSize, size_t* pSize, + UINT32* pWidth, UINT32* pHeight, UINT32* pBpp) +{ + void* rc = NULL; + png_uint_32 width = 0; + png_uint_32 height = 0; + int bit_depth = 0; + int color_type = 0; + int interlace_type = 0; + int transforms = PNG_TRANSFORM_IDENTITY; + MEMORY_READER_STATE memory_reader_state = { 0 }; + png_bytepp row_pointers = NULL; + png_infop info_ptr = NULL; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto fail; + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + goto fail; + + memory_reader_state.buffer = (png_bytep)data; + memory_reader_state.bufsize = SrcSize; + memory_reader_state.current_pos = 0; + + png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory); + + transforms |= PNG_TRANSFORM_BGR; + png_read_png(png_ptr, info_ptr, transforms, NULL); + + if (png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, + NULL, NULL) != 1) + goto fail; + + size_t bpp = PNG_IMAGE_PIXEL_SIZE(color_type); + + row_pointers = png_get_rows(png_ptr, info_ptr); + if (row_pointers) + { + const size_t stride = width * bpp; + const size_t png_stride = png_get_rowbytes(png_ptr, info_ptr); + const size_t size = width * height * bpp; + const size_t copybytes = stride > png_stride ? png_stride : stride; + + rc = malloc(size); + if (rc) + { + char* cur = rc; + for (int i = 0; i < height; i++) + { + memcpy(cur, row_pointers[i], copybytes); + cur += stride; + } + *pSize = size; + *pWidth = width; + *pHeight = height; + *pBpp = bpp * 8; + } + } +fail: + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return rc; +} +#endif + +void* winpr_convert_to_png(const void* data, size_t size, UINT32 width, UINT32 height, + UINT32 stride, UINT32 bpp, UINT32* pSize) +{ + WINPR_ASSERT(data || (size == 0)); + WINPR_ASSERT(pSize); + + *pSize = 0; + +#if defined(WINPR_UTILS_IMAGE_PNG) + void* dst = NULL; + SSIZE_T rc = save_png_to_buffer(bpp, width, height, data, size, &dst); + if (rc <= 0) + return NULL; + *pSize = (UINT32)rc; + return dst; +#elif defined(WITH_LODEPNG) + { + BYTE* dst = NULL; + size_t dstsize = 0; + unsigned rc = 1; + + switch (bpp) + { + case 32: + rc = lodepng_encode32(&dst, &dstsize, data, width, height); + break; + case 24: + rc = lodepng_encode24(&dst, &dstsize, data, width, height); + break; + default: + break; + } + if (rc) + return NULL; + *pSize = (UINT32)dstsize; + return dst; + } +#else + return NULL; +#endif +} + +SSIZE_T winpr_convert_from_png(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data) +{ +#if defined(WINPR_UTILS_IMAGE_PNG) + size_t len = 0; + *ppdecomp_data = + winpr_read_png_from_buffer(comp_data, comp_data_bytes, &len, width, height, bpp); + if (!*ppdecomp_data) + return -1; + return (SSIZE_T)len; +#elif defined(WITH_LODEPNG) + *bpp = 32; + return lodepng_decode32((unsigned char**)ppdecomp_data, width, height, comp_data, + comp_data_bytes); +#else + return -1; +#endif +} + +BOOL winpr_image_format_is_supported(UINT32 format) +{ + switch (format) + { + case WINPR_IMAGE_BITMAP: +#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WITH_LODEPNG) + case WINPR_IMAGE_PNG: +#endif +#if defined(WINPR_UTILS_IMAGE_JPEG) + case WINPR_IMAGE_JPEG: +#endif +#if defined(WINPR_UTILS_IMAGE_WEBP) + case WINPR_IMAGE_WEBP: +#endif + return TRUE; + default: + return FALSE; + } +} + +static BYTE* convert(const wImage* image, size_t* pstride, UINT32 flags) +{ + WINPR_ASSERT(image); + WINPR_ASSERT(pstride); + + *pstride = 0; + if (image->bitsPerPixel < 24) + return NULL; + + const size_t stride = image->width * 4ull; + BYTE* data = calloc(stride, image->height); + if (data) + { + for (size_t y = 0; y < image->height; y++) + { + const BYTE* srcLine = &image->data[image->scanline * y]; + BYTE* dstLine = &data[stride * y]; + if (image->bitsPerPixel == 32) + memcpy(dstLine, srcLine, stride); + else + { + for (size_t x = 0; x < image->width; x++) + { + const BYTE* src = &srcLine[image->bytesPerPixel * x]; + BYTE* dst = &dstLine[4ull * x]; + BYTE b = *src++; + BYTE g = *src++; + BYTE r = *src++; + + *dst++ = b; + *dst++ = g; + *dst++ = r; + *dst++ = 0xff; + } + } + } + *pstride = stride; + } + return data; +} + +static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags) +{ + if (a != b) + { + if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0) + { + const int diff = abs((int)a) - abs((int)b); + /* filter out quantization errors */ + if (diff > 6) + return FALSE; + } + else + { + return FALSE; + } + } + return TRUE; +} + +static BOOL compare_pixel(const BYTE* pa, const BYTE* pb, UINT32 flags) +{ + WINPR_ASSERT(pa); + WINPR_ASSERT(pb); + + if (!compare_byte_relaxed(*pa++, *pb++, flags)) + return FALSE; + if (!compare_byte_relaxed(*pa++, *pb++, flags)) + return FALSE; + if (!compare_byte_relaxed(*pa++, *pb++, flags)) + return FALSE; + if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0) + { + if (!compare_byte_relaxed(*pa++, *pb++, flags)) + return FALSE; + } + return TRUE; +} + +BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB, UINT32 flags) +{ + if (imageA == imageB) + return TRUE; + if (!imageA || !imageB) + return FALSE; + + if (imageA->height != imageB->height) + return FALSE; + if (imageA->width != imageB->width) + return FALSE; + + if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0) + { + if (imageA->bitsPerPixel != imageB->bitsPerPixel) + return FALSE; + if (imageA->bytesPerPixel != imageB->bytesPerPixel) + return FALSE; + } + + BOOL rc = FALSE; + size_t astride = 0; + size_t bstride = 0; + BYTE* dataA = convert(imageA, &astride, flags); + BYTE* dataB = convert(imageA, &bstride, flags); + if (dataA && dataB && (astride == bstride)) + { + rc = TRUE; + for (size_t y = 0; y < imageA->height; y++) + { + const BYTE* lineA = &dataA[astride * y]; + const BYTE* lineB = &dataB[bstride * y]; + + for (size_t x = 0; x < imageA->width; x++) + { + const BYTE* pa = &lineA[x * 4ull]; + const BYTE* pb = &lineB[x * 4ull]; + + if (!compare_pixel(pa, pb, flags)) + rc = FALSE; + } + } + } + free(dataA); + free(dataB); + return rc; +} + +const char* winpr_image_format_mime(UINT32 format) +{ + switch (format) + { + case WINPR_IMAGE_BITMAP: + return "image/bmp"; + case WINPR_IMAGE_PNG: + return "image/png"; + case WINPR_IMAGE_WEBP: + return "image/webp"; + case WINPR_IMAGE_JPEG: + return "image/jpeg"; + default: + return NULL; + } +} + +const char* winpr_image_format_extension(UINT32 format) +{ + switch (format) + { + case WINPR_IMAGE_BITMAP: + return "bmp"; + case WINPR_IMAGE_PNG: + return "png"; + case WINPR_IMAGE_WEBP: + return "webp"; + case WINPR_IMAGE_JPEG: + return "jpg"; + default: + return NULL; + } +} + +void* winpr_image_write_buffer(wImage* image, UINT32 format, size_t* psize) +{ + WINPR_ASSERT(image); + switch (format) + { + case WINPR_IMAGE_BITMAP: + { + UINT32 outsize = 0; + size_t size = 1ull * image->height * image->scanline; + void* data = winpr_bitmap_write_buffer(image->data, size, image->width, image->height, + image->scanline, image->bitsPerPixel, &outsize); + *psize = outsize; + return data; + } + break; + case WINPR_IMAGE_WEBP: + { + UINT32 outsize = 0; + size_t size = 1ull * image->height * image->scanline; + void* data = winpr_convert_to_webp(image->data, size, image->width, image->height, + image->scanline, image->bitsPerPixel, &outsize); + *psize = outsize; + return data; + } + break; + case WINPR_IMAGE_JPEG: + { + UINT32 outsize = 0; + size_t size = 1ull * image->height * image->scanline; + void* data = winpr_convert_to_jpeg(image->data, size, image->width, image->height, + image->scanline, image->bitsPerPixel, &outsize); + *psize = outsize; + return data; + } + break; + case WINPR_IMAGE_PNG: + { + UINT32 outsize = 0; + size_t size = 1ull * image->height * image->scanline; + void* data = winpr_convert_to_png(image->data, size, image->width, image->height, + image->scanline, image->bitsPerPixel, &outsize); + *psize = outsize; + return data; + } + break; + default: + *psize = 0; + return NULL; + } +} diff --git a/winpr/libwinpr/utils/ini.c b/winpr/libwinpr/utils/ini.c new file mode 100644 index 0000000..5ebac34 --- /dev/null +++ b/winpr/libwinpr/utils/ini.c @@ -0,0 +1,889 @@ +/** + * WinPR: Windows Portable Runtime + * .ini config file + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +typedef struct +{ + char* name; + char* value; +} wIniFileKey; + +typedef struct +{ + char* name; + size_t nKeys; + size_t cKeys; + wIniFileKey** keys; +} wIniFileSection; + +struct s_wIniFile +{ + char* line; + char* nextLine; + size_t lineLength; + char* tokctx; + char* buffer; + size_t buffersize; + char* filename; + BOOL readOnly; + size_t nSections; + size_t cSections; + wIniFileSection** sections; +}; + +static BOOL IniFile_Load_NextLine(wIniFile* ini, char* str) +{ + size_t length = 0; + + WINPR_ASSERT(ini); + + ini->nextLine = strtok_s(str, "\n", &ini->tokctx); + + if (ini->nextLine) + length = strlen(ini->nextLine); + + if (length > 0) + { + if (ini->nextLine[length - 1] == '\r') + { + ini->nextLine[length - 1] = '\0'; + length--; + } + + if (length < 1) + ini->nextLine = NULL; + } + + return (ini->nextLine) ? TRUE : FALSE; +} + +static BOOL IniFile_BufferResize(wIniFile* ini, size_t size) +{ + WINPR_ASSERT(ini); + if (size > ini->buffersize) + { + const size_t diff = size - ini->buffersize; + char* tmp = realloc(ini->buffer, size); + if (!tmp) + return FALSE; + + memset(&tmp[ini->buffersize], 0, diff * sizeof(char)); + ini->buffer = tmp; + ini->buffersize = size; + } + return TRUE; +} + +static BOOL IniFile_Load_String(wIniFile* ini, const char* iniString) +{ + size_t fileSize = 0; + + WINPR_ASSERT(ini); + + if (!iniString) + return FALSE; + + ini->line = NULL; + ini->nextLine = NULL; + fileSize = strlen(iniString); + + if (fileSize < 1) + return FALSE; + + if (!IniFile_BufferResize(ini, fileSize + 2)) + return FALSE; + + CopyMemory(ini->buffer, iniString, fileSize); + ini->buffer[fileSize] = '\n'; + IniFile_Load_NextLine(ini, ini->buffer); + return TRUE; +} + +static void IniFile_Close_File(FILE* fp) +{ + if (fp) + fclose(fp); +} + +static FILE* IniFile_Open_File(wIniFile* ini, const char* filename) +{ + WINPR_ASSERT(ini); + + if (!filename) + return FALSE; + + if (ini->readOnly) + return winpr_fopen(filename, "rb"); + else + return winpr_fopen(filename, "w+b"); +} + +static BOOL IniFile_Load_File(wIniFile* ini, const char* filename) +{ + BOOL rc = FALSE; + INT64 fileSize = 0; + + WINPR_ASSERT(ini); + + FILE* fp = IniFile_Open_File(ini, filename); + if (!fp) + return FALSE; + + if (_fseeki64(fp, 0, SEEK_END) < 0) + goto out_file; + + fileSize = _ftelli64(fp); + + if (fileSize < 0) + goto out_file; + + if (_fseeki64(fp, 0, SEEK_SET) < 0) + goto out_file; + + ini->line = NULL; + ini->nextLine = NULL; + + if (fileSize < 1) + goto out_file; + + if (fileSize > SIZE_MAX) + goto out_file; + + if (!IniFile_BufferResize(ini, (size_t)fileSize + 2)) + goto out_file; + + if (fread(ini->buffer, (size_t)fileSize, 1ul, fp) != 1) + goto out_file; + + ini->buffer[fileSize] = '\n'; + IniFile_Load_NextLine(ini, ini->buffer); + rc = TRUE; + +out_file: + IniFile_Close_File(fp); + return rc; +} + +static BOOL IniFile_Load_HasNextLine(wIniFile* ini) +{ + WINPR_ASSERT(ini); + + return (ini->nextLine) ? TRUE : FALSE; +} + +static char* IniFile_Load_GetNextLine(wIniFile* ini) +{ + WINPR_ASSERT(ini); + + ini->line = ini->nextLine; + ini->lineLength = strlen(ini->line); + IniFile_Load_NextLine(ini, NULL); + return ini->line; +} + +static void IniFile_Key_Free(wIniFileKey* key) +{ + if (!key) + return; + + free(key->name); + free(key->value); + free(key); +} + +static wIniFileKey* IniFile_Key_New(const char* name, const char* value) +{ + if (!name || !value) + return NULL; + + wIniFileKey* key = calloc(1, sizeof(wIniFileKey)); + + if (key) + { + key->name = _strdup(name); + key->value = _strdup(value); + + if (!key->name || !key->value) + { + IniFile_Key_Free(key); + return NULL; + } + } + + return key; +} + +static void IniFile_Section_Free(wIniFileSection* section) +{ + if (!section) + return; + + free(section->name); + + for (size_t index = 0; index < section->nKeys; index++) + { + IniFile_Key_Free(section->keys[index]); + } + + free(section->keys); + free(section); +} + +static BOOL IniFile_SectionKeysResize(wIniFileSection* section, size_t count) +{ + WINPR_ASSERT(section); + + if (section->nKeys + count >= section->cKeys) + { + const size_t new_size = section->cKeys + count + 1024; + const size_t diff = new_size - section->cKeys; + wIniFileKey** new_keys = + (wIniFileKey**)realloc(section->keys, sizeof(wIniFileKey*) * new_size); + + if (!new_keys) + return FALSE; + + memset(&new_keys[section->cKeys], 0, diff * sizeof(wIniFileKey*)); + section->cKeys = new_size; + section->keys = new_keys; + } + return TRUE; +} + +static wIniFileSection* IniFile_Section_New(const char* name) +{ + if (!name) + return NULL; + + wIniFileSection* section = calloc(1, sizeof(wIniFileSection)); + + if (!section) + goto fail; + + section->name = _strdup(name); + + if (!section->name) + goto fail; + + if (!IniFile_SectionKeysResize(section, 64)) + goto fail; + + return section; + +fail: + IniFile_Section_Free(section); + return NULL; +} + +static wIniFileSection* IniFile_GetSection(wIniFile* ini, const char* name) +{ + wIniFileSection* section = NULL; + + WINPR_ASSERT(ini); + + if (!name) + return NULL; + + for (size_t index = 0; index < ini->nSections; index++) + { + if (_stricmp(name, ini->sections[index]->name) == 0) + { + section = ini->sections[index]; + break; + } + } + + return section; +} + +static BOOL IniFile_SectionResize(wIniFile* ini, size_t count) +{ + WINPR_ASSERT(ini); + + if (ini->nSections + count >= ini->cSections) + { + const size_t new_size = ini->cSections + count + 1024; + const size_t diff = new_size - ini->cSections; + wIniFileSection** new_sect = + (wIniFileSection**)realloc(ini->sections, sizeof(wIniFileSection*) * new_size); + + if (!new_sect) + return FALSE; + + memset(&new_sect[ini->cSections], 0, diff * sizeof(wIniFileSection*)); + ini->cSections = new_size; + ini->sections = new_sect; + } + return TRUE; +} + +static wIniFileSection* IniFile_AddToSection(wIniFile* ini, const char* name) +{ + WINPR_ASSERT(ini); + + if (!name) + return NULL; + + wIniFileSection* section = IniFile_GetSection(ini, name); + + if (!section) + { + if (!IniFile_SectionResize(ini, 1)) + return NULL; + + section = IniFile_Section_New(name); + if (!section) + return NULL; + ini->sections[ini->nSections++] = section; + } + + return section; +} + +static wIniFileKey* IniFile_GetKey(wIniFileSection* section, const char* name) +{ + wIniFileKey* key = NULL; + + WINPR_ASSERT(section); + + if (!name) + return NULL; + + for (size_t index = 0; index < section->nKeys; index++) + { + if (_stricmp(name, section->keys[index]->name) == 0) + { + key = section->keys[index]; + break; + } + } + + return key; +} + +static wIniFileKey* IniFile_AddKey(wIniFileSection* section, const char* name, const char* value) +{ + WINPR_ASSERT(section); + + if (!name || !value) + return NULL; + + wIniFileKey* key = IniFile_GetKey(section, name); + + if (!key) + { + if (!IniFile_SectionKeysResize(section, 1)) + return NULL; + + key = IniFile_Key_New(name, value); + + if (!key) + return NULL; + + section->keys[section->nKeys++] = key; + } + else + { + free(key->value); + key->value = _strdup(value); + + if (!key->value) + return NULL; + } + + return key; +} + +static int IniFile_Load(wIniFile* ini) +{ + char* name = NULL; + char* value = NULL; + char* separator = NULL; + char* beg = NULL; + char* end = NULL; + wIniFileSection* section = NULL; + + WINPR_ASSERT(ini); + + while (IniFile_Load_HasNextLine(ini)) + { + char* line = IniFile_Load_GetNextLine(ini); + + if (line[0] == ';') + continue; + + if (line[0] == '[') + { + beg = &line[1]; + end = strchr(line, ']'); + + if (!end) + return -1; + + *end = '\0'; + IniFile_AddToSection(ini, beg); + section = ini->sections[ini->nSections - 1]; + } + else + { + separator = strchr(line, '='); + + if (separator == NULL) + return -1; + + end = separator; + + while ((&end[-1] > line) && ((end[-1] == ' ') || (end[-1] == '\t'))) + end--; + + *end = '\0'; + name = line; + beg = separator + 1; + + while (*beg && ((*beg == ' ') || (*beg == '\t'))) + beg++; + + if (*beg == '"') + beg++; + + end = &line[ini->lineLength]; + + while ((end > beg) && ((end[-1] == ' ') || (end[-1] == '\t'))) + end--; + + if (end[-1] == '"') + end[-1] = '\0'; + + value = beg; + + if (!IniFile_AddKey(section, name, value)) + return -1; + } + } + + return 1; +} + +static BOOL IniFile_SetFilename(wIniFile* ini, const char* name) +{ + WINPR_ASSERT(ini); + free(ini->filename); + ini->filename = NULL; + + if (!name) + return TRUE; + ini->filename = _strdup(name); + return ini->filename != NULL; +} + +int IniFile_ReadBuffer(wIniFile* ini, const char* buffer) +{ + BOOL status = 0; + + WINPR_ASSERT(ini); + + if (!buffer) + return -1; + + ini->readOnly = TRUE; + status = IniFile_Load_String(ini, buffer); + + if (!status) + return -1; + + return IniFile_Load(ini); +} + +int IniFile_ReadFile(wIniFile* ini, const char* filename) +{ + WINPR_ASSERT(ini); + + ini->readOnly = TRUE; + if (!IniFile_SetFilename(ini, filename)) + return -1; + if (!ini->filename) + return -1; + + if (!IniFile_Load_File(ini, filename)) + return -1; + + return IniFile_Load(ini); +} + +char** IniFile_GetSectionNames(wIniFile* ini, size_t* count) +{ + WINPR_ASSERT(ini); + + if (!count) + return NULL; + + if (ini->nSections > INT_MAX) + return NULL; + + size_t length = (sizeof(char*) * ini->nSections) + sizeof(char); + + for (size_t index = 0; index < ini->nSections; index++) + { + wIniFileSection* section = ini->sections[index]; + const size_t nameLength = strlen(section->name); + length += (nameLength + 1); + } + + char** sectionNames = (char**)calloc(length, sizeof(char*)); + + if (!sectionNames) + return NULL; + + char* p = (char*)&((BYTE*)sectionNames)[sizeof(char*) * ini->nSections]; + + for (size_t index = 0; index < ini->nSections; index++) + { + sectionNames[index] = p; + wIniFileSection* section = ini->sections[index]; + const size_t nameLength = strlen(section->name); + CopyMemory(p, section->name, nameLength + 1); + p += (nameLength + 1); + } + + *p = '\0'; + *count = ini->nSections; + return sectionNames; +} + +char** IniFile_GetSectionKeyNames(wIniFile* ini, const char* section, size_t* count) +{ + WINPR_ASSERT(ini); + + if (!section || !count) + return NULL; + + wIniFileSection* pSection = IniFile_GetSection(ini, section); + + if (!pSection) + return NULL; + + if (pSection->nKeys > INT_MAX) + return NULL; + + size_t length = (sizeof(char*) * pSection->nKeys) + sizeof(char); + + for (size_t index = 0; index < pSection->nKeys; index++) + { + wIniFileKey* pKey = pSection->keys[index]; + const size_t nameLength = strlen(pKey->name); + length += (nameLength + 1); + } + + char** keyNames = (char**)calloc(length, sizeof(char*)); + + if (!keyNames) + return NULL; + + char* p = (char*)&((BYTE*)keyNames)[sizeof(char*) * pSection->nKeys]; + + for (size_t index = 0; index < pSection->nKeys; index++) + { + keyNames[index] = p; + wIniFileKey* pKey = pSection->keys[index]; + const size_t nameLength = strlen(pKey->name); + CopyMemory(p, pKey->name, nameLength + 1); + p += (nameLength + 1); + } + + *p = '\0'; + *count = pSection->nKeys; + return keyNames; +} + +const char* IniFile_GetKeyValueString(wIniFile* ini, const char* section, const char* key) +{ + const char* value = NULL; + wIniFileKey* pKey = NULL; + wIniFileSection* pSection = NULL; + + WINPR_ASSERT(ini); + + pSection = IniFile_GetSection(ini, section); + + if (!pSection) + return NULL; + + pKey = IniFile_GetKey(pSection, key); + + if (!pKey) + return NULL; + + value = (const char*)pKey->value; + return value; +} + +int IniFile_GetKeyValueInt(wIniFile* ini, const char* section, const char* key) +{ + int err = 0; + long value = 0; + wIniFileKey* pKey = NULL; + wIniFileSection* pSection = NULL; + + WINPR_ASSERT(ini); + + pSection = IniFile_GetSection(ini, section); + + if (!pSection) + return 0; + + pKey = IniFile_GetKey(pSection, key); + + if (!pKey) + return 0; + + err = errno; + errno = 0; + value = strtol(pKey->value, NULL, 0); + if ((value < INT_MIN) || (value > INT_MAX) || (errno != 0)) + { + errno = err; + return 0; + } + return (int)value; +} + +int IniFile_SetKeyValueString(wIniFile* ini, const char* section, const char* key, + const char* value) +{ + wIniFileKey* pKey = NULL; + + WINPR_ASSERT(ini); + wIniFileSection* pSection = IniFile_GetSection(ini, section); + + if (!pSection) + pSection = IniFile_AddToSection(ini, section); + + if (!pSection) + return -1; + + pKey = IniFile_AddKey(pSection, key, value); + + if (!pKey) + return -1; + + return 1; +} + +int IniFile_SetKeyValueInt(wIniFile* ini, const char* section, const char* key, int value) +{ + char strVal[128] = { 0 }; + wIniFileKey* pKey = NULL; + wIniFileSection* pSection = NULL; + + WINPR_ASSERT(ini); + + sprintf_s(strVal, sizeof(strVal), "%d", value); + pSection = IniFile_GetSection(ini, section); + + if (!pSection) + pSection = IniFile_AddToSection(ini, section); + + if (!pSection) + return -1; + + pKey = IniFile_AddKey(pSection, key, strVal); + + if (!pKey) + return -1; + + return 1; +} + +char* IniFile_WriteBuffer(wIniFile* ini) +{ + size_t offset = 0; + size_t size = 0; + char* buffer = NULL; + + WINPR_ASSERT(ini); + + for (size_t i = 0; i < ini->nSections; i++) + { + wIniFileSection* section = ini->sections[i]; + size += (strlen(section->name) + 3); + + for (size_t j = 0; j < section->nKeys; j++) + { + wIniFileKey* key = section->keys[j]; + size += (strlen(key->name) + strlen(key->value) + 2); + } + + size += 1; + } + + size += 1; + buffer = calloc(size + 1, sizeof(char)); + + if (!buffer) + return NULL; + + offset = 0; + + for (size_t i = 0; i < ini->nSections; i++) + { + wIniFileSection* section = ini->sections[i]; + sprintf_s(&buffer[offset], size - offset, "[%s]\n", section->name); + offset += (strlen(section->name) + 3); + + for (size_t j = 0; j < section->nKeys; j++) + { + wIniFileKey* key = section->keys[j]; + sprintf_s(&buffer[offset], size - offset, "%s=%s\n", key->name, key->value); + offset += (strlen(key->name) + strlen(key->value) + 2); + } + + sprintf_s(&buffer[offset], size - offset, "\n"); + offset += 1; + } + + return buffer; +} + +int IniFile_WriteFile(wIniFile* ini, const char* filename) +{ + int ret = -1; + + WINPR_ASSERT(ini); + + char* buffer = IniFile_WriteBuffer(ini); + + if (!buffer) + return -1; + + const size_t length = strlen(buffer); + ini->readOnly = FALSE; + + if (!filename) + filename = ini->filename; + + FILE* fp = IniFile_Open_File(ini, filename); + if (!fp) + goto fail; + + if (fwrite((void*)buffer, length, 1, fp) != 1) + goto fail; + + ret = 1; + +fail: + IniFile_Close_File(fp); + free(buffer); + return ret; +} + +void IniFile_Free(wIniFile* ini) +{ + if (!ini) + return; + + IniFile_SetFilename(ini, NULL); + + for (size_t index = 0; index < ini->nSections; index++) + IniFile_Section_Free(ini->sections[index]); + + free(ini->sections); + free(ini->buffer); + free(ini); +} + +wIniFile* IniFile_New(void) +{ + wIniFile* ini = (wIniFile*)calloc(1, sizeof(wIniFile)); + + if (!ini) + goto fail; + + if (!IniFile_SectionResize(ini, 64)) + goto fail; + + return ini; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + IniFile_Free(ini); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +wIniFile* IniFile_Clone(const wIniFile* ini) +{ + if (!ini) + return NULL; + + wIniFile* copy = IniFile_New(); + if (!copy) + goto fail; + + copy->lineLength = ini->lineLength; + if (!IniFile_SetFilename(copy, ini->filename)) + goto fail; + + if (ini->buffersize > 0) + { + if (!IniFile_BufferResize(copy, ini->buffersize)) + goto fail; + memcpy(copy->buffer, ini->buffer, copy->buffersize); + } + + copy->readOnly = ini->readOnly; + + for (size_t x = 0; x < ini->nSections; x++) + { + const wIniFileSection* cur = ini->sections[x]; + if (!cur) + goto fail; + + wIniFileSection* scopy = IniFile_AddToSection(copy, cur->name); + if (!scopy) + goto fail; + + for (size_t y = 0; y < cur->nKeys; y++) + { + const wIniFileKey* key = cur->keys[y]; + if (!key) + goto fail; + + IniFile_AddKey(scopy, key->name, key->value); + } + } + return copy; + +fail: + IniFile_Free(copy); + return NULL; +} diff --git a/winpr/libwinpr/utils/ntlm.c b/winpr/libwinpr/utils/ntlm.c new file mode 100644 index 0000000..25e0e5a --- /dev/null +++ b/winpr/libwinpr/utils/ntlm.c @@ -0,0 +1,182 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include + +/** + * Define NTOWFv1(Password, User, Domain) as + * MD4(UNICODE(Password)) + * EndDefine + */ + +BOOL NTOWFv1W(LPWSTR Password, UINT32 PasswordLength, BYTE* NtHash) +{ + if (!Password || !NtHash) + return FALSE; + + if (!winpr_Digest(WINPR_MD_MD4, (BYTE*)Password, (size_t)PasswordLength, NtHash, + WINPR_MD4_DIGEST_LENGTH)) + return FALSE; + + return TRUE; +} + +BOOL NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash) +{ + LPWSTR PasswordW = NULL; + BOOL result = FALSE; + size_t pwdCharLength = 0; + + if (!NtHash) + return FALSE; + + PasswordW = ConvertUtf8NToWCharAlloc(Password, PasswordLength, &pwdCharLength); + if (!PasswordW) + return FALSE; + + if (!NTOWFv1W(PasswordW, (UINT32)pwdCharLength * sizeof(WCHAR), NtHash)) + goto out_fail; + + result = TRUE; +out_fail: + free(PasswordW); + return result; +} + +/** + * Define NTOWFv2(Password, User, Domain) as + * HMAC_MD5(MD4(UNICODE(Password)), + * UNICODE(ConcatenationOf(UpperCase(User), Domain))) + * EndDefine + */ + +BOOL NTOWFv2W(LPWSTR Password, UINT32 PasswordLength, LPWSTR User, UINT32 UserLength, LPWSTR Domain, + UINT32 DomainLength, BYTE* NtHash) +{ + BYTE NtHashV1[WINPR_MD5_DIGEST_LENGTH]; + + if ((!User) || (!Password) || (!NtHash)) + return FALSE; + + if (!NTOWFv1W(Password, PasswordLength, NtHashV1)) + return FALSE; + + return NTOWFv2FromHashW(NtHashV1, User, UserLength, Domain, DomainLength, NtHash); +} + +BOOL NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, UINT32 UserLength, LPSTR Domain, + UINT32 DomainLength, BYTE* NtHash) +{ + LPWSTR UserW = NULL; + LPWSTR DomainW = NULL; + LPWSTR PasswordW = NULL; + BOOL result = FALSE; + size_t userCharLength = 0; + size_t domainCharLength = 0; + size_t pwdCharLength = 0; + + if (!NtHash) + return FALSE; + + UserW = ConvertUtf8NToWCharAlloc(User, UserLength, &userCharLength); + DomainW = ConvertUtf8NToWCharAlloc(Domain, DomainLength, &domainCharLength); + PasswordW = ConvertUtf8NToWCharAlloc(Password, PasswordLength, &pwdCharLength); + + if (!UserW || !DomainW || !PasswordW) + goto out_fail; + + if (!NTOWFv2W(PasswordW, (UINT32)pwdCharLength * sizeof(WCHAR), UserW, + (UINT32)userCharLength * sizeof(WCHAR), DomainW, + (UINT32)domainCharLength * sizeof(WCHAR), NtHash)) + goto out_fail; + + result = TRUE; +out_fail: + free(UserW); + free(DomainW); + free(PasswordW); + return result; +} + +BOOL NTOWFv2FromHashW(BYTE* NtHashV1, LPWSTR User, UINT32 UserLength, LPWSTR Domain, + UINT32 DomainLength, BYTE* NtHash) +{ + BYTE* buffer = NULL; + BYTE result = FALSE; + + if (!User || !NtHash) + return FALSE; + + if (!(buffer = (BYTE*)malloc(UserLength + DomainLength))) + return FALSE; + + /* Concatenate(UpperCase(User), Domain) */ + CopyMemory(buffer, User, UserLength); + CharUpperBuffW((LPWSTR)buffer, UserLength / 2); + + if (DomainLength > 0) + { + CopyMemory(&buffer[UserLength], Domain, DomainLength); + } + + /* Compute the HMAC-MD5 hash of the above value using the NTLMv1 hash as the key, the result is + * the NTLMv2 hash */ + if (!winpr_HMAC(WINPR_MD_MD5, NtHashV1, 16, buffer, UserLength + DomainLength, NtHash, + WINPR_MD5_DIGEST_LENGTH)) + goto out_fail; + + result = TRUE; +out_fail: + free(buffer); + return result; +} + +BOOL NTOWFv2FromHashA(BYTE* NtHashV1, LPSTR User, UINT32 UserLength, LPSTR Domain, + UINT32 DomainLength, BYTE* NtHash) +{ + LPWSTR UserW = NULL; + LPWSTR DomainW = NULL; + BOOL result = FALSE; + size_t userCharLength = 0; + size_t domainCharLength = 0; + if (!NtHash) + return FALSE; + + UserW = ConvertUtf8NToWCharAlloc(User, UserLength, &userCharLength); + DomainW = ConvertUtf8NToWCharAlloc(Domain, DomainLength, &domainCharLength); + + if (!UserW || !DomainW) + goto out_fail; + + if (!NTOWFv2FromHashW(NtHashV1, UserW, (UINT32)userCharLength * sizeof(WCHAR), DomainW, + (UINT32)domainCharLength * sizeof(WCHAR), NtHash)) + goto out_fail; + + result = TRUE; +out_fail: + free(UserW); + free(DomainW); + return result; +} diff --git a/winpr/libwinpr/utils/print.c b/winpr/libwinpr/utils/print.c new file mode 100644 index 0000000..5f0c4a6 --- /dev/null +++ b/winpr/libwinpr/utils/print.c @@ -0,0 +1,262 @@ +/** + * WinPR: Windows Portable Runtime + * Print Utils + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "../log.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +void winpr_HexDump(const char* tag, UINT32 level, const void* data, size_t length) +{ + wLog* log = WLog_Get(tag); + winpr_HexLogDump(log, level, data, length); +} + +void winpr_HexLogDump(wLog* log, UINT32 lvl, const void* data, size_t length) +{ + const BYTE* p = data; + size_t line = 0; + size_t offset = 0; + const size_t maxlen = 20; /* 64bit SIZE_MAX as decimal */ + /* String line length: + * prefix '[1234] ' + * hexdump '01 02 03 04' + * separator ' ' + * ASIC line 'ab..cd' + * zero terminator '\0' + */ + const size_t blen = + (maxlen + 3) + (WINPR_HEXDUMP_LINE_LENGTH * 3) + 3 + WINPR_HEXDUMP_LINE_LENGTH + 1; + size_t pos = 0; + + char* buffer = NULL; + + if (!WLog_IsLevelActive(log, lvl)) + return; + + if (!log) + return; + + buffer = malloc(blen); + + if (!buffer) + { + char ebuffer[256] = { 0 }; + WLog_Print(log, WLOG_ERROR, "malloc(%" PRIuz ") failed with [%" PRIuz "] %s", blen, errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return; + } + + while (offset < length) + { + int rc = _snprintf(&buffer[pos], blen - pos, "%04" PRIuz " ", offset); + + if (rc < 0) + goto fail; + + pos += (size_t)rc; + line = length - offset; + + if (line > WINPR_HEXDUMP_LINE_LENGTH) + line = WINPR_HEXDUMP_LINE_LENGTH; + + size_t i = 0; + for (; i < line; i++) + { + rc = _snprintf(&buffer[pos], blen - pos, "%02" PRIx8 " ", p[i]); + + if (rc < 0) + goto fail; + + pos += (size_t)rc; + } + + for (; i < WINPR_HEXDUMP_LINE_LENGTH; i++) + { + rc = _snprintf(&buffer[pos], blen - pos, " "); + + if (rc < 0) + goto fail; + + pos += (size_t)rc; + } + + for (size_t i = 0; i < line; i++) + { + rc = _snprintf(&buffer[pos], blen - pos, "%c", + (p[i] >= 0x20 && p[i] < 0x7F) ? (char)p[i] : '.'); + + if (rc < 0) + goto fail; + + pos += (size_t)rc; + } + + WLog_Print(log, lvl, "%s", buffer); + offset += line; + p += line; + pos = 0; + } + + WLog_Print(log, lvl, "[length=%" PRIuz "] ", length); +fail: + free(buffer); +} + +void winpr_CArrayDump(const char* tag, UINT32 level, const void* data, size_t length, size_t width) +{ + const BYTE* p = data; + size_t offset = 0; + const size_t llen = ((length > width) ? width : length) * 4ull + 1ull; + size_t pos = 0; + char* buffer = malloc(llen); + + if (!buffer) + { + char ebuffer[256] = { 0 }; + WLog_ERR(tag, "malloc(%" PRIuz ") failed with [%d] %s", llen, errno, + winpr_strerror(errno, ebuffer, sizeof(ebuffer))); + return; + } + + while (offset < length) + { + size_t line = length - offset; + + if (line > width) + line = width; + + pos = 0; + + for (size_t i = 0; i < line; i++) + { + const int rc = _snprintf(&buffer[pos], llen - pos, "\\x%02" PRIX8 "", p[i]); + if (rc < 0) + goto fail; + pos += (size_t)rc; + } + + WLog_LVL(tag, level, "%s", buffer); + offset += line; + p += line; + } + +fail: + free(buffer); +} + +static BYTE value(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return 10 + c - 'A'; + if ((c >= 'a') && (c <= 'f')) + return 10 + c - 'a'; + return 0; +} + +size_t winpr_HexStringToBinBuffer(const char* str, size_t strLength, BYTE* data, size_t dataLength) +{ + size_t y = 0; + size_t maxStrLen = 0; + if (!str || !data || (strLength == 0) || (dataLength == 0)) + return 0; + + maxStrLen = strnlen(str, strLength); + for (size_t x = 0; x < maxStrLen;) + { + BYTE val = value(str[x++]); + if (x < maxStrLen) + val = (BYTE)(val << 4) | (value(str[x++])); + if (x < maxStrLen) + { + if (str[x] == ' ') + x++; + } + data[y++] = val; + if (y >= dataLength) + return y; + } + return y; +} + +size_t winpr_BinToHexStringBuffer(const BYTE* data, size_t length, char* dstStr, size_t dstSize, + BOOL space) +{ + const size_t n = space ? 3 : 2; + const char bin2hex[] = "0123456789ABCDEF"; + const size_t maxLength = MIN(length, dstSize / n); + + if (!data || !dstStr || (length == 0) || (dstSize == 0)) + return 0; + + for (size_t i = 0; i < maxLength; i++) + { + const int ln = data[i] & 0xF; + const int hn = (data[i] >> 4) & 0xF; + char* dst = &dstStr[i * n]; + + dst[0] = bin2hex[hn]; + dst[1] = bin2hex[ln]; + + if (space) + dst[2] = ' '; + } + + if (space && (maxLength > 0)) + { + dstStr[maxLength * n - 1] = '\0'; + return maxLength * n - 1; + } + dstStr[maxLength * n] = '\0'; + return maxLength * n; +} + +char* winpr_BinToHexString(const BYTE* data, size_t length, BOOL space) +{ + size_t rc = 0; + const size_t n = space ? 3 : 2; + const size_t size = (length + 1ULL) * n; + char* p = (char*)malloc(size); + + if (!p) + return NULL; + + rc = winpr_BinToHexStringBuffer(data, length, p, size, space); + if (rc == 0) + { + free(p); + return NULL; + } + + return p; +} diff --git a/winpr/libwinpr/utils/sam.c b/winpr/libwinpr/utils/sam.c new file mode 100644 index 0000000..31163d9 --- /dev/null +++ b/winpr/libwinpr/utils/sam.c @@ -0,0 +1,366 @@ +/** + * WinPR: Windows Portable Runtime + * Security Accounts Manager (SAM) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../log.h" + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#define WINPR_SAM_FILE "C:\\SAM" +#else +#define WINPR_SAM_FILE "/etc/winpr/SAM" +#endif +#define TAG WINPR_TAG("utils") + +struct winpr_sam +{ + FILE* fp; + char* line; + char* buffer; + char* context; + BOOL readOnly; +}; + +static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR Domain, + DWORD DomainLength) +{ + WINPR_SAM_ENTRY* entry = calloc(1, sizeof(WINPR_SAM_ENTRY)); + if (!entry) + return NULL; + if (User && (UserLength > 0)) + entry->User = _strdup(User); + entry->UserLength = UserLength; + if (Domain && (DomainLength > 0)) + entry->Domain = _strdup(Domain); + entry->DomainLength = DomainLength; + return entry; +} + +static BOOL SamAreEntriesEqual(const WINPR_SAM_ENTRY* a, const WINPR_SAM_ENTRY* b) +{ + if (!a || !b) + return FALSE; + if (a->UserLength != b->UserLength) + return FALSE; + if (a->DomainLength != b->DomainLength) + return FALSE; + if (strncmp(a->User, b->User, a->UserLength) != 0) + return FALSE; + if (strncmp(a->Domain, b->Domain, a->DomainLength) != 0) + return FALSE; + return TRUE; +} + +WINPR_SAM* SamOpen(const char* filename, BOOL readOnly) +{ + FILE* fp = NULL; + WINPR_SAM* sam = NULL; + + if (!filename) + filename = WINPR_SAM_FILE; + + if (readOnly) + fp = winpr_fopen(filename, "r"); + else + { + fp = winpr_fopen(filename, "r+"); + + if (!fp) + fp = winpr_fopen(filename, "w+"); + } + + if (fp) + { + sam = (WINPR_SAM*)calloc(1, sizeof(WINPR_SAM)); + + if (!sam) + { + fclose(fp); + return NULL; + } + + sam->readOnly = readOnly; + sam->fp = fp; + } + else + { + WLog_DBG(TAG, "Could not open SAM file!"); + return NULL; + } + + return sam; +} + +static BOOL SamLookupStart(WINPR_SAM* sam) +{ + size_t readSize = 0; + INT64 fileSize = 0; + + if (!sam || !sam->fp) + return FALSE; + + _fseeki64(sam->fp, 0, SEEK_END); + fileSize = _ftelli64(sam->fp); + _fseeki64(sam->fp, 0, SEEK_SET); + + if (fileSize < 1) + return FALSE; + + sam->context = NULL; + sam->buffer = (char*)calloc((size_t)fileSize + 2, 1); + + if (!sam->buffer) + return FALSE; + + readSize = fread(sam->buffer, (size_t)fileSize, 1, sam->fp); + + if (!readSize) + { + if (!ferror(sam->fp)) + readSize = (size_t)fileSize; + } + + if (readSize < 1) + { + free(sam->buffer); + sam->buffer = NULL; + return FALSE; + } + + sam->buffer[fileSize] = '\n'; + sam->buffer[fileSize + 1] = '\0'; + sam->line = strtok_s(sam->buffer, "\n", &sam->context); + return TRUE; +} + +static void SamLookupFinish(WINPR_SAM* sam) +{ + free(sam->buffer); + sam->buffer = NULL; + sam->line = NULL; +} + +static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry) +{ + char* p[5]; + size_t LmHashLength = 0; + size_t NtHashLength = 0; + size_t count = 0; + char* cur = NULL; + + if (!sam || !entry || !sam->line) + return FALSE; + + cur = sam->line; + + while ((cur = strchr(cur, ':')) != NULL) + { + count++; + cur++; + } + + if (count < 4) + return FALSE; + + p[0] = sam->line; + p[1] = strchr(p[0], ':') + 1; + p[2] = strchr(p[1], ':') + 1; + p[3] = strchr(p[2], ':') + 1; + p[4] = strchr(p[3], ':') + 1; + LmHashLength = (p[3] - p[2] - 1); + NtHashLength = (p[4] - p[3] - 1); + + if ((LmHashLength != 0) && (LmHashLength != 32)) + return FALSE; + + if ((NtHashLength != 0) && (NtHashLength != 32)) + return FALSE; + + entry->UserLength = (UINT32)(p[1] - p[0] - 1); + entry->User = (LPSTR)malloc(entry->UserLength + 1); + + if (!entry->User) + return FALSE; + + entry->User[entry->UserLength] = '\0'; + entry->DomainLength = (UINT32)(p[2] - p[1] - 1); + memcpy(entry->User, p[0], entry->UserLength); + + if (entry->DomainLength > 0) + { + entry->Domain = (LPSTR)malloc(entry->DomainLength + 1); + + if (!entry->Domain) + { + free(entry->User); + entry->User = NULL; + return FALSE; + } + + memcpy(entry->Domain, p[1], entry->DomainLength); + entry->Domain[entry->DomainLength] = '\0'; + } + else + entry->Domain = NULL; + + if (LmHashLength == 32) + winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash)); + + if (NtHashLength == 32) + winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash, sizeof(entry->NtHash)); + + return TRUE; +} + +void SamFreeEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry) +{ + if (entry) + { + if (entry->UserLength > 0) + free(entry->User); + + if (entry->DomainLength > 0) + free(entry->Domain); + + free(entry); + } +} + +void SamResetEntry(WINPR_SAM_ENTRY* entry) +{ + if (!entry) + return; + + if (entry->UserLength) + { + free(entry->User); + entry->User = NULL; + } + + if (entry->DomainLength) + { + free(entry->Domain); + entry->Domain = NULL; + } + + ZeroMemory(entry->LmHash, sizeof(entry->LmHash)); + ZeroMemory(entry->NtHash, sizeof(entry->NtHash)); +} + +WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain, + UINT32 DomainLength) +{ + size_t length = 0; + BOOL found = FALSE; + WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength); + WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY)); + + if (!entry || !search) + goto fail; + + if (!SamLookupStart(sam)) + goto fail; + + while (sam->line != NULL) + { + length = strlen(sam->line); + + if (length > 1) + { + if (sam->line[0] != '#') + { + if (!SamReadEntry(sam, entry)) + { + goto out_fail; + } + + if (SamAreEntriesEqual(entry, search)) + { + found = 1; + break; + } + } + } + + SamResetEntry(entry); + sam->line = strtok_s(NULL, "\n", &sam->context); + } + +out_fail: + SamLookupFinish(sam); +fail: + SamFreeEntry(sam, search); + + if (!found) + { + SamFreeEntry(sam, entry); + return NULL; + } + + return entry; +} + +WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain, + UINT32 DomainLength) +{ + WINPR_SAM_ENTRY* entry = NULL; + char* utfUser = NULL; + char* utfDomain = NULL; + size_t userCharLen = 0; + size_t domainCharLen = 0; + + utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen); + if (!utfUser) + goto fail; + if (DomainLength > 0) + { + utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen); + if (!utfDomain) + goto fail; + } + entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen); +fail: + free(utfUser); + free(utfDomain); + return entry; +} + +void SamClose(WINPR_SAM* sam) +{ + if (sam != NULL) + { + if (sam->fp) + fclose(sam->fp); + free(sam); + } +} diff --git a/winpr/libwinpr/utils/ssl.c b/winpr/libwinpr/utils/ssl.c new file mode 100644 index 0000000..8764969 --- /dev/null +++ b/winpr/libwinpr/utils/ssl.c @@ -0,0 +1,447 @@ +/** + * WinPR: Windows Portable Runtime + * OpenSSL Library Initialization + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL + +#include +#include + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +#include +#endif + +#include "../log.h" +#define TAG WINPR_TAG("utils.ssl") + +static BOOL g_winpr_openssl_initialized_by_winpr = FALSE; + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +static OSSL_PROVIDER* s_winpr_openssl_provider_fips = NULL; +static OSSL_PROVIDER* s_winpr_openssl_provider_legacy = NULL; +static OSSL_PROVIDER* s_winpr_openssl_provider_default = NULL; +#endif + +/** + * Note from OpenSSL 1.1.0 "CHANGES": + * OpenSSL now uses a new threading API. It is no longer necessary to + * set locking callbacks to use OpenSSL in a multi-threaded environment. + */ + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + +#define WINPR_OPENSSL_LOCKING_REQUIRED 1 + +static int g_winpr_openssl_num_locks = 0; +static HANDLE* g_winpr_openssl_locks = NULL; + +struct CRYPTO_dynlock_value +{ + HANDLE mutex; +}; + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) +static unsigned long _winpr_openssl_id(void) +{ + return (unsigned long)GetCurrentThreadId(); +} +#endif + +static void _winpr_openssl_locking(int mode, int type, const char* file, int line) +{ + if (mode & CRYPTO_LOCK) + { + WaitForSingleObject(g_winpr_openssl_locks[type], INFINITE); + } + else + { + ReleaseMutex(g_winpr_openssl_locks[type]); + } +} + +static struct CRYPTO_dynlock_value* _winpr_openssl_dynlock_create(const char* file, int line) +{ + struct CRYPTO_dynlock_value* dynlock; + + if (!(dynlock = (struct CRYPTO_dynlock_value*)malloc(sizeof(struct CRYPTO_dynlock_value)))) + return NULL; + + if (!(dynlock->mutex = CreateMutex(NULL, FALSE, NULL))) + { + free(dynlock); + return NULL; + } + + return dynlock; +} + +static void _winpr_openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value* dynlock, + const char* file, int line) +{ + if (mode & CRYPTO_LOCK) + { + WaitForSingleObject(dynlock->mutex, INFINITE); + } + else + { + ReleaseMutex(dynlock->mutex); + } +} + +static void _winpr_openssl_dynlock_destroy(struct CRYPTO_dynlock_value* dynlock, const char* file, + int line) +{ + CloseHandle(dynlock->mutex); + free(dynlock); +} + +static BOOL _winpr_openssl_initialize_locking(void) +{ + int count; + + /* OpenSSL static locking */ + + if (CRYPTO_get_locking_callback()) + { + WLog_WARN(TAG, "OpenSSL static locking callback is already set"); + } + else + { + if ((count = CRYPTO_num_locks()) > 0) + { + HANDLE* locks; + + if (!(locks = calloc(count, sizeof(HANDLE)))) + { + WLog_ERR(TAG, "error allocating lock table"); + return FALSE; + } + + for (int i = 0; i < count; i++) + { + if (!(locks[i] = CreateMutex(NULL, FALSE, NULL))) + { + WLog_ERR(TAG, "error creating lock #%d", i); + + while (i--) + { + if (locks[i]) + CloseHandle(locks[i]); + } + + free(locks); + return FALSE; + } + } + + g_winpr_openssl_locks = locks; + g_winpr_openssl_num_locks = count; + CRYPTO_set_locking_callback(_winpr_openssl_locking); + } + } + + /* OpenSSL dynamic locking */ + + if (CRYPTO_get_dynlock_create_callback() || CRYPTO_get_dynlock_lock_callback() || + CRYPTO_get_dynlock_destroy_callback()) + { + WLog_WARN(TAG, "dynamic locking callbacks are already set"); + } + else + { + CRYPTO_set_dynlock_create_callback(_winpr_openssl_dynlock_create); + CRYPTO_set_dynlock_lock_callback(_winpr_openssl_dynlock_lock); + CRYPTO_set_dynlock_destroy_callback(_winpr_openssl_dynlock_destroy); + } + + /* Use the deprecated CRYPTO_get_id_callback() if building against OpenSSL < 1.0.0 */ +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) + + if (CRYPTO_get_id_callback()) + { + WLog_WARN(TAG, "OpenSSL id_callback is already set"); + } + else + { + CRYPTO_set_id_callback(_winpr_openssl_id); + } + +#endif + return TRUE; +} + +static BOOL _winpr_openssl_cleanup_locking(void) +{ + /* undo our static locking modifications */ + if (CRYPTO_get_locking_callback() == _winpr_openssl_locking) + { + CRYPTO_set_locking_callback(NULL); + + for (int i = 0; i < g_winpr_openssl_num_locks; i++) + { + CloseHandle(g_winpr_openssl_locks[i]); + } + + g_winpr_openssl_num_locks = 0; + free(g_winpr_openssl_locks); + g_winpr_openssl_locks = NULL; + } + + /* unset our dynamic locking callbacks */ + + if (CRYPTO_get_dynlock_create_callback() == _winpr_openssl_dynlock_create) + { + CRYPTO_set_dynlock_create_callback(NULL); + } + + if (CRYPTO_get_dynlock_lock_callback() == _winpr_openssl_dynlock_lock) + { + CRYPTO_set_dynlock_lock_callback(NULL); + } + + if (CRYPTO_get_dynlock_destroy_callback() == _winpr_openssl_dynlock_destroy) + { + CRYPTO_set_dynlock_destroy_callback(NULL); + } + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) + + if (CRYPTO_get_id_callback() == _winpr_openssl_id) + { + CRYPTO_set_id_callback(NULL); + } + +#endif + return TRUE; +} + +#endif /* OpenSSL < 1.1.0 */ + +static BOOL winpr_enable_fips(DWORD flags) +{ + if (flags & WINPR_SSL_INIT_ENABLE_FIPS) + { +#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER) + WLog_ERR(TAG, "Openssl fips mode not available on openssl versions less than 1.0.1!"); + return FALSE; +#else + WLog_DBG(TAG, "Ensuring openssl fips mode is enabled"); + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + s_winpr_openssl_provider_fips = OSSL_PROVIDER_load(NULL, "fips"); + if (s_winpr_openssl_provider_fips == NULL) + { + WLog_WARN(TAG, "OpenSSL FIPS provider failled to load"); + } + if (!EVP_default_properties_is_fips_enabled(NULL)) +#else + if (FIPS_mode() != 1) +#endif + { +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + if (EVP_set_default_properties(NULL, "fips=yes")) +#else + if (FIPS_mode_set(1)) +#endif + WLog_INFO(TAG, "Openssl fips mode enabled!"); + else + { + WLog_ERR(TAG, "Openssl fips mode enable failed!"); + return FALSE; + } + } + +#endif + } + + return TRUE; +} + +static void winpr_openssl_cleanup(void) +{ + winpr_CleanupSSL(WINPR_SSL_INIT_DEFAULT); +} + +static BOOL CALLBACK winpr_openssl_initialize(PINIT_ONCE once, PVOID param, PVOID* context) +{ + DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT; + + if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED) + { + return TRUE; + } + +#ifdef WINPR_OPENSSL_LOCKING_REQUIRED + + if (flags & WINPR_SSL_INIT_ENABLE_LOCKING) + { + if (!_winpr_openssl_initialize_locking()) + { + return FALSE; + } + } + +#endif + /* SSL_load_error_strings() is void */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + SSL_load_error_strings(); + /* SSL_library_init() always returns "1" */ + SSL_library_init(); + OpenSSL_add_all_digests(); + OpenSSL_add_all_ciphers(); +#else + + if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS | + OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | + OPENSSL_INIT_ENGINE_ALL_BUILTIN, + NULL) != 1) + return FALSE; + +#endif + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + /* The legacy provider is needed for MD4. */ + s_winpr_openssl_provider_legacy = OSSL_PROVIDER_load(NULL, "legacy"); + if (s_winpr_openssl_provider_legacy == NULL) + { + WLog_WARN(TAG, "OpenSSL LEGACY provider failed to load, no md4 support available!"); + } + s_winpr_openssl_provider_default = OSSL_PROVIDER_load(NULL, "default"); + if (s_winpr_openssl_provider_default == NULL) + { + WLog_WARN(TAG, "OpenSSL DEFAULT provider failed to load"); + } +#endif + + atexit(winpr_openssl_cleanup); + g_winpr_openssl_initialized_by_winpr = TRUE; + return TRUE; +} + +/* exported functions */ + +BOOL winpr_InitializeSSL(DWORD flags) +{ + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; + + if (!InitOnceExecuteOnce(&once, winpr_openssl_initialize, &flags, NULL)) + return FALSE; + + return winpr_enable_fips(flags); +} + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +static int unload(OSSL_PROVIDER* provider, void* data) +{ + if (!provider) + return 1; + const char* name = OSSL_PROVIDER_get0_name(provider); + if (!name) + return 1; + + OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default(); + const int rc = OSSL_PROVIDER_available(ctx, name); + if (rc < 1) + return 1; + OSSL_PROVIDER_unload(provider); + return 1; +} +#endif + +BOOL winpr_CleanupSSL(DWORD flags) +{ + if (flags & WINPR_SSL_CLEANUP_GLOBAL) + { + if (!g_winpr_openssl_initialized_by_winpr) + { + WLog_WARN(TAG, "ssl was not initialized by winpr"); + return FALSE; + } + + g_winpr_openssl_initialized_by_winpr = FALSE; +#ifdef WINPR_OPENSSL_LOCKING_REQUIRED + _winpr_openssl_cleanup_locking(); +#endif +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + EVP_cleanup(); +#endif +#ifdef WINPR_OPENSSL_LOCKING_REQUIRED + flags |= WINPR_SSL_CLEANUP_THREAD; +#endif + } + +#ifdef WINPR_OPENSSL_LOCKING_REQUIRED + + if (flags & WINPR_SSL_CLEANUP_THREAD) + { +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) + ERR_remove_state(0); +#else + ERR_remove_thread_state(NULL); +#endif + } + +#endif +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default(); + OSSL_PROVIDER_do_all(ctx, unload, NULL); +#endif + + return TRUE; +} + +BOOL winpr_FIPSMode(void) +{ +#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER) + return FALSE; +#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + return (EVP_default_properties_is_fips_enabled(NULL) == 1); +#else + return (FIPS_mode() == 1); +#endif +} + +#else + +BOOL winpr_InitializeSSL(DWORD flags) +{ + return TRUE; +} + +BOOL winpr_CleanupSSL(DWORD flags) +{ + return TRUE; +} + +BOOL winpr_FIPSMode(void) +{ + return FALSE; +} + +#endif diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c new file mode 100644 index 0000000..4c7696f --- /dev/null +++ b/winpr/libwinpr/utils/stream.c @@ -0,0 +1,523 @@ +/* + * WinPR: Windows Portable Runtime + * Stream Utils + * + * Copyright 2011 Vic Lee + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "stream.h" +#include "../log.h" + +#define STREAM_TAG WINPR_TAG("wStream") + +#define STREAM_ASSERT(cond) \ + do \ + { \ + if (!(cond)) \ + { \ + WLog_FATAL(STREAM_TAG, "%s [%s:%s:%" PRIuz "]", #cond, __FILE__, __func__, \ + (size_t)__LINE__); \ + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); \ + abort(); \ + } \ + } while (0) + +BOOL Stream_EnsureCapacity(wStream* s, size_t size) +{ + WINPR_ASSERT(s); + if (s->capacity < size) + { + size_t position = 0; + size_t old_capacity = 0; + size_t new_capacity = 0; + BYTE* new_buf = NULL; + + old_capacity = s->capacity; + new_capacity = old_capacity; + + do + { + new_capacity *= 2; + } while (new_capacity < size); + + position = Stream_GetPosition(s); + + if (!s->isOwner) + { + new_buf = (BYTE*)malloc(new_capacity); + CopyMemory(new_buf, s->buffer, s->capacity); + s->isOwner = TRUE; + } + else + { + new_buf = (BYTE*)realloc(s->buffer, new_capacity); + } + + if (!new_buf) + return FALSE; + s->buffer = new_buf; + s->capacity = new_capacity; + s->length = new_capacity; + ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity); + + Stream_SetPosition(s, position); + } + return TRUE; +} + +BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size) +{ + if (Stream_GetPosition(s) + size > Stream_Capacity(s)) + return Stream_EnsureCapacity(s, Stream_Capacity(s) + size); + return TRUE; +} + +wStream* Stream_New(BYTE* buffer, size_t size) +{ + wStream* s = NULL; + + if (!buffer && !size) + return NULL; + + s = malloc(sizeof(wStream)); + if (!s) + return NULL; + + if (buffer) + s->buffer = buffer; + else + s->buffer = (BYTE*)malloc(size); + + if (!s->buffer) + { + free(s); + return NULL; + } + + s->pointer = s->buffer; + s->capacity = size; + s->length = size; + + s->pool = NULL; + s->count = 0; + s->isAllocatedStream = TRUE; + s->isOwner = TRUE; + return s; +} + +wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size) +{ + union + { + BYTE* b; + const BYTE* cb; + } cnv; + + cnv.cb = buffer; + return Stream_StaticInit(s, cnv.b, size); +} + +wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size) +{ + const wStream empty = { 0 }; + + WINPR_ASSERT(s); + WINPR_ASSERT(buffer); + + *s = empty; + s->buffer = s->pointer = buffer; + s->capacity = s->length = size; + s->pool = NULL; + s->count = 0; + s->isAllocatedStream = FALSE; + s->isOwner = FALSE; + return s; +} + +void Stream_EnsureValidity(wStream* s) +{ + size_t cur = 0; + + STREAM_ASSERT(s); + STREAM_ASSERT(s->pointer >= s->buffer); + + cur = (size_t)(s->pointer - s->buffer); + STREAM_ASSERT(cur <= s->capacity); + STREAM_ASSERT(s->length <= s->capacity); +} + +void Stream_Free(wStream* s, BOOL bFreeBuffer) +{ + if (s) + { + Stream_EnsureValidity(s); + if (bFreeBuffer && s->isOwner) + free(s->buffer); + + if (s->isAllocatedStream) + free(s); + } +} + +BOOL Stream_SetLength(wStream* _s, size_t _l) +{ + if ((_l) > Stream_Capacity(_s)) + { + _s->length = 0; + return FALSE; + } + _s->length = _l; + return TRUE; +} + +BOOL Stream_SetPosition(wStream* _s, size_t _p) +{ + if ((_p) > Stream_Capacity(_s)) + { + _s->pointer = _s->buffer; + return FALSE; + } + _s->pointer = _s->buffer + (_p); + return TRUE; +} + +void Stream_SealLength(wStream* _s) +{ + size_t cur = 0; + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + cur = (size_t)(_s->pointer - _s->buffer); + WINPR_ASSERT(cur <= _s->capacity); + if (cur <= _s->capacity) + _s->length = cur; + else + { + WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds"); + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); + _s->length = 0; + } +} + +#if defined(WITH_WINPR_DEPRECATED) +BOOL Stream_SetPointer(wStream* _s, BYTE* _p) +{ + WINPR_ASSERT(_s); + if (!_p || (_s->buffer > _p) || (_s->buffer + _s->capacity < _p)) + { + _s->pointer = _s->buffer; + return FALSE; + } + _s->pointer = _p; + return TRUE; +} + +BOOL Stream_SetBuffer(wStream* _s, BYTE* _b) +{ + WINPR_ASSERT(_s); + WINPR_ASSERT(_b); + + _s->buffer = _b; + _s->pointer = _b; + return _s->buffer != NULL; +} + +void Stream_SetCapacity(wStream* _s, size_t _c) +{ + WINPR_ASSERT(_s); + _s->capacity = _c; +} + +#endif + +size_t Stream_GetRemainingCapacity(const wStream* _s) +{ + size_t cur = 0; + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + cur = (size_t)(_s->pointer - _s->buffer); + WINPR_ASSERT(cur <= _s->capacity); + if (cur > _s->capacity) + { + WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds"); + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); + return 0; + } + return (_s->capacity - cur); +} + +size_t Stream_GetRemainingLength(const wStream* _s) +{ + size_t cur = 0; + WINPR_ASSERT(_s); + WINPR_ASSERT(_s->buffer <= _s->pointer); + WINPR_ASSERT(_s->length <= _s->capacity); + cur = (size_t)(_s->pointer - _s->buffer); + WINPR_ASSERT(cur <= _s->length); + if (cur > _s->length) + { + WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was read out of bounds"); + winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); + return 0; + } + return (_s->length - cur); +} + +BOOL Stream_Write_UTF16_String(wStream* s, const WCHAR* src, size_t length) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(src || (length == 0)); + if (!s || !src) + return FALSE; + + if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, (s), length, sizeof(WCHAR))) + return FALSE; + + for (size_t x = 0; x < length; x++) + Stream_Write_UINT16(s, src[x]); + + return TRUE; +} + +BOOL Stream_Read_UTF16_String(wStream* s, WCHAR* dst, size_t length) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(dst); + + if (!Stream_CheckAndLogRequiredLengthOfSize(STREAM_TAG, s, length, sizeof(WCHAR))) + return FALSE; + + for (size_t x = 0; x < length; x++) + Stream_Read_UINT16(s, dst[x]); + + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, ...) +{ + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredCapacityExVa(tag, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredCapacityExVa(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, va_list args) +{ + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + return Stream_CheckAndLogRequiredCapacityWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt, + args); + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 0) +BOOL Stream_CheckAndLogRequiredCapacityWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, + va_list args) +{ + + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + { + char prefix[1024] = { 0 }; + + vsnprintf(prefix, sizeof(prefix), fmt, args); + + WLog_Print(log, level, + "[%s] invalid remaining capacity, got %" PRIuz ", require at least %" PRIu64 + " [element size=%" PRIuz "]", + prefix, actual, nmemb, size); + winpr_log_backtrace_ex(log, level, 20); + return FALSE; + } + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 7) +BOOL Stream_CheckAndLogRequiredCapacityWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, ...) +{ + + WINPR_ASSERT(size != 0); + const size_t actual = Stream_GetRemainingCapacity(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredCapacityWLogExVa(log, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 7) +BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, ...) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthExVa(tag, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, va_list args) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt, + args); + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, const char* fmt, ...) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, nmemb, size, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(6, 0) +BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb, + size_t size, WINPR_FORMAT_ARG const char* fmt, + va_list args) +{ + WINPR_ASSERT(size > 0); + const size_t actual = Stream_GetRemainingLength(s) / size; + + if (actual < nmemb) + { + char prefix[1024] = { 0 }; + + vsnprintf(prefix, sizeof(prefix), fmt, args); + + WLog_Print(log, level, + "[%s] invalid length, got %" PRIuz ", require at least %" PRIuz + " [element size=%" PRIuz "]", + prefix, actual, nmemb, size); + winpr_log_backtrace_ex(log, level, 20); + return FALSE; + } + return TRUE; +} + +SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t dlen, const char* src, size_t length, + BOOL fill) +{ + WCHAR* str = Stream_PointerAs(s, WCHAR); + + if (length == 0) + return 0; + + if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, dlen, sizeof(WCHAR))) + return -1; + + SSIZE_T rc = ConvertUtf8NToWChar(src, length, str, dlen); + if (rc < 0) + return -1; + + Stream_Seek(s, (size_t)rc * sizeof(WCHAR)); + + if (fill) + Stream_Zero(s, (dlen - (size_t)rc) * sizeof(WCHAR)); + return rc; +} + +char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t dlen, size_t* psize) +{ + const WCHAR* str = Stream_ConstPointer(s); + if (dlen > SIZE_MAX / sizeof(WCHAR)) + return NULL; + + if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, dlen * sizeof(WCHAR))) + return NULL; + + Stream_Seek(s, dlen * sizeof(WCHAR)); + return ConvertWCharNToUtf8Alloc(str, dlen, psize); +} + +SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer, + size_t utfBufferCharLength) +{ + const WCHAR* ptr = Stream_ConstPointer(s); + if (wcharLength > SIZE_MAX / sizeof(WCHAR)) + return -1; + + if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR))) + return -1; + + Stream_Seek(s, wcharLength * sizeof(WCHAR)); + return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength); +} + +BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt) +{ + if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")", + fkt, file, line)) + return FALSE; + + Stream_Seek(s, size); + return TRUE; +} diff --git a/winpr/libwinpr/utils/stream.h b/winpr/libwinpr/utils/stream.h new file mode 100644 index 0000000..cc4eb7c --- /dev/null +++ b/winpr/libwinpr/utils/stream.h @@ -0,0 +1,28 @@ +/* + * WinPR: Windows Portable Runtime + * Stream Utils + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBWINPR_UTILS_STREAM_H +#define LIBWINPR_UTILS_STREAM_H + +#include + +void Stream_EnsureValidity(wStream* s); + +#endif /* LIBWINPR_UTILS_STREAM_H */ diff --git a/winpr/libwinpr/utils/strlst.c b/winpr/libwinpr/utils/strlst.c new file mode 100644 index 0000000..c83e29c --- /dev/null +++ b/winpr/libwinpr/utils/strlst.c @@ -0,0 +1,74 @@ +/* + * String List Utils + * + * Copyright 2018 Pascal Bourguignon + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include + +void string_list_free(char** string_list) +{ + for (size_t i = 0; string_list[i]; i++) + { + free(string_list[i]); + } + + free(string_list); +} + +int string_list_length(const char* const* string_list) +{ + int i = 0; + for (; string_list[i]; i++) + ; + + return i; +} + +char** string_list_copy(const char* const* string_list) +{ + int length = string_list_length(string_list); + char** copy = calloc(length + 1, sizeof(char*)); + + if (!copy) + { + return 0; + } + + for (int i = 0; i < length; i++) + { + copy[i] = _strdup(string_list[i]); + } + + copy[length] = 0; + return copy; +} + +void string_list_print(FILE* out, const char* const* string_list) +{ + for (int j = 0; string_list[j]; j++) + { + fprintf(out, "[%2d]: %s\n", j, string_list[j]); + } + + fflush(out); +} diff --git a/winpr/libwinpr/utils/test/CMakeLists.txt b/winpr/libwinpr/utils/test/CMakeLists.txt new file mode 100644 index 0000000..ad22f20 --- /dev/null +++ b/winpr/libwinpr/utils/test/CMakeLists.txt @@ -0,0 +1,55 @@ + +set(MODULE_NAME "TestWinPRUtils") +set(MODULE_PREFIX "TEST_WINPR_UTILS") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestIni.c + TestVersion.c + TestImage.c + TestBacktrace.c + TestQueue.c + TestPrint.c + TestPubSub.c + TestStream.c + TestBitStream.c + TestArrayList.c + TestLinkedList.c + TestListDictionary.c + TestCmdLine.c + TestASN1.c + TestWLog.c + TestWLogCallback.c + TestHashTable.c + TestBufferPool.c + TestStreamPool.c + TestMessageQueue.c + TestMessagePipe.c) + +if (WITH_LODEPNG) + list(APPEND ${MODULES_PREFIX}_TESTS + TestImage.c + ) +endif() + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_definitions(-DTEST_SOURCE_PATH="${CMAKE_CURRENT_SOURCE_DIR}") +add_definitions(-DTEST_BINARY_PATH="${CMAKE_CURRENT_BINARY_DIR}") + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/utils/test/TestASN1.c b/winpr/libwinpr/utils/test/TestASN1.c new file mode 100644 index 0000000..d54bdbc --- /dev/null +++ b/winpr/libwinpr/utils/test/TestASN1.c @@ -0,0 +1,335 @@ +#include +#include + +static const BYTE boolContent[] = { 0x01, 0x01, 0xFF }; +static const BYTE badBoolContent[] = { 0x01, 0x04, 0xFF }; + +static const BYTE integerContent[] = { 0x02, 0x01, 0x02 }; +static const BYTE badIntegerContent[] = { 0x02, 0x04, 0x02 }; + +static const BYTE seqContent[] = { 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1B, 0x44, + 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x53, 0x69, 0x67, + 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x20, 0x43, 0x6F, 0x2E, 0x31 }; + +static const BYTE contextualInteger[] = { 0xA0, 0x03, 0x02, 0x01, 0x02 }; + +static const BYTE oidContent[] = { 0x06, 0x03, 0x55, 0x04, 0x0A }; +static const BYTE badOidContent[] = { 0x06, 0x89, 0x55, 0x04, 0x0A }; +static const BYTE oidValue[] = { 0x55, 0x04, 0x0A }; + +static const BYTE ia5stringContent[] = { 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, + 0x63, 0x70, 0x73, 0x2E, 0x72, 0x6F, 0x6F, 0x74, 0x2D, + 0x78, 0x31, 0x2E, 0x6C, 0x65, 0x74, 0x73, 0x65, 0x6E, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x2E, 0x6F, 0x72, 0x67 }; + +static const BYTE utctimeContent[] = { 0x17, 0x0D, 0x32, 0x31, 0x30, 0x33, 0x31, 0x37, + 0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5A }; + +int TestASN1Read(int argc, char* argv[]) +{ + WinPrAsn1Decoder decoder; + WinPrAsn1Decoder seqDecoder; + wStream staticS; + WinPrAsn1_BOOL boolV = 0; + WinPrAsn1_INTEGER integerV = 0; + WinPrAsn1_OID oidV; + WinPrAsn1_IA5STRING ia5stringV = NULL; + WinPrAsn1_UTCTIME utctimeV; + WinPrAsn1_tag tag = 0; + size_t len = 0; + BOOL error = 0; + + /* ============== Test INTEGERs ================ */ + Stream_StaticConstInit(&staticS, integerContent, sizeof(integerContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadInteger(&decoder, &integerV)) + return -1; + + Stream_StaticConstInit(&staticS, badIntegerContent, sizeof(badIntegerContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (WinPrAsn1DecReadInteger(&decoder, &integerV)) + return -1; + + /* ================ Test BOOL ================*/ + Stream_StaticConstInit(&staticS, boolContent, sizeof(boolContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadBoolean(&decoder, &boolV)) + return -10; + + Stream_StaticConstInit(&staticS, badBoolContent, sizeof(badBoolContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (WinPrAsn1DecReadBoolean(&decoder, &boolV)) + return -11; + + /* ================ Test OID ================*/ + Stream_StaticConstInit(&staticS, oidContent, sizeof(oidContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadOID(&decoder, &oidV, TRUE) || oidV.len != 3 || + memcmp(oidV.data, oidValue, oidV.len)) + return -15; + WinPrAsn1FreeOID(&oidV); + + Stream_StaticConstInit(&staticS, badOidContent, sizeof(badOidContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (WinPrAsn1DecReadOID(&decoder, &oidV, TRUE)) + return -15; + WinPrAsn1FreeOID(&oidV); + + Stream_StaticConstInit(&staticS, ia5stringContent, sizeof(ia5stringContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadIA5String(&decoder, &ia5stringV) || + strcmp(ia5stringV, "http://cps.root-x1.letsencrypt.org")) + return -16; + free(ia5stringV); + + /* ================ Test utc time ================*/ + Stream_StaticConstInit(&staticS, utctimeContent, sizeof(utctimeContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadUtcTime(&decoder, &utctimeV) || utctimeV.year != 2021 || + utctimeV.month != 3 || utctimeV.day != 17 || utctimeV.minute != 40 || utctimeV.tz != 'Z') + return -17; + + /* ================ Test sequence ================*/ + Stream_StaticConstInit(&staticS, seqContent, sizeof(seqContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadSequence(&decoder, &seqDecoder)) + return -20; + + Stream_StaticConstInit(&staticS, seqContent, sizeof(seqContent)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + if (!WinPrAsn1DecReadTagLenValue(&decoder, &tag, &len, &seqDecoder)) + return -21; + + if (tag != ER_TAG_SEQUENCE) + return -22; + + if (!WinPrAsn1DecPeekTag(&seqDecoder, &tag) || tag != ER_TAG_OBJECT_IDENTIFIER) + return -23; + + /* ================ Test contextual ================*/ + Stream_StaticConstInit(&staticS, contextualInteger, sizeof(contextualInteger)); + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + error = TRUE; + if (!WinPrAsn1DecReadContextualInteger(&decoder, 0, &error, &integerV) || error) + return -25; + + /* test reading a contextual integer that is not there (index 1). + * that should not touch the decoder read head and we shall be able to extract contextual tag 0 + * after that + */ + WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS); + error = FALSE; + if (WinPrAsn1DecReadContextualInteger(&decoder, 1, &error, &integerV) || error) + return -26; + + error = FALSE; + if (!WinPrAsn1DecReadContextualInteger(&decoder, 0, &error, &integerV) || error) + return -27; + + return 0; +} + +static BYTE oid1_val[] = { 1 }; +static const WinPrAsn1_OID oid1 = { sizeof(oid1_val), oid1_val }; +static BYTE oid2_val[] = { 2, 2 }; +static WinPrAsn1_OID oid2 = { sizeof(oid2_val), oid2_val }; +static BYTE oid3_val[] = { 3, 3, 3 }; +static WinPrAsn1_OID oid3 = { sizeof(oid3_val), oid3_val }; +static BYTE oid4_val[] = { 4, 4, 4, 4 }; +static WinPrAsn1_OID oid4 = { sizeof(oid4_val), oid4_val }; + +int TestASN1Write(int argc, char* argv[]) +{ + wStream* s = NULL; + size_t expectedOuputSz = 0; + int retCode = 100; + WinPrAsn1_UTCTIME utcTime; + WinPrAsn1_IA5STRING ia5string = NULL; + WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); + if (!enc) + goto out; + + /* Let's encode something like: + * APP(3) + * SEQ2 + * OID1 + * OID2 + * SEQ3 + * OID3 + * OID4 + * + * [5] integer(200) + * [6] SEQ (empty) + * [7] UTC time (2016-03-17 16:40:41 UTC) + * [8] IA5String(test) + * [9] OctetString + * SEQ(empty) + * + */ + + /* APP(3) */ + retCode = 101; + if (!WinPrAsn1EncAppContainer(enc, 3)) + goto out; + + /* SEQ2 */ + retCode = 102; + if (!WinPrAsn1EncSeqContainer(enc)) + goto out; + + retCode = 103; + if (WinPrAsn1EncOID(enc, &oid1) != 3) + goto out; + + retCode = 104; + if (WinPrAsn1EncOID(enc, &oid2) != 4) + goto out; + + retCode = 105; + if (WinPrAsn1EncEndContainer(enc) != 9) + goto out; + + /* SEQ3 */ + retCode = 110; + if (!WinPrAsn1EncSeqContainer(enc)) + goto out; + + retCode = 111; + if (WinPrAsn1EncOID(enc, &oid3) != 5) + goto out; + + retCode = 112; + if (WinPrAsn1EncOID(enc, &oid4) != 6) + goto out; + + retCode = 113; + if (WinPrAsn1EncEndContainer(enc) != 13) + goto out; + + /* [5] integer(200) */ + retCode = 114; + if (WinPrAsn1EncContextualInteger(enc, 5, 200) != 6) + goto out; + + /* [6] SEQ (empty) */ + retCode = 115; + if (!WinPrAsn1EncContextualSeqContainer(enc, 6)) + goto out; + + retCode = 116; + if (WinPrAsn1EncEndContainer(enc) != 4) + goto out; + + /* [7] UTC time (2016-03-17 16:40:41 UTC) */ + retCode = 117; + utcTime.year = 2016; + utcTime.month = 3; + utcTime.day = 17; + utcTime.hour = 16; + utcTime.minute = 40; + utcTime.second = 41; + utcTime.tz = 'Z'; + if (WinPrAsn1EncContextualUtcTime(enc, 7, &utcTime) != 17) + goto out; + + /* [8] IA5String(test) */ + retCode = 118; + ia5string = "test"; + if (!WinPrAsn1EncContextualContainer(enc, 8)) + goto out; + + retCode = 119; + if (WinPrAsn1EncIA5String(enc, ia5string) != 6) + goto out; + + retCode = 120; + if (WinPrAsn1EncEndContainer(enc) != 8) + goto out; + + /* [9] OctetString + * SEQ(empty) + */ + retCode = 121; + if (!WinPrAsn1EncContextualOctetStringContainer(enc, 9)) + goto out; + + retCode = 122; + if (!WinPrAsn1EncSeqContainer(enc)) + goto out; + + retCode = 123; + if (WinPrAsn1EncEndContainer(enc) != 2) + goto out; + + retCode = 124; + if (WinPrAsn1EncEndContainer(enc) != 6) + goto out; + + /* close APP */ + expectedOuputSz = 24 + 6 + 4 + 17 + 8 + 6; + retCode = 200; + if (WinPrAsn1EncEndContainer(enc) != expectedOuputSz) + goto out; + + /* let's output the result */ + retCode = 201; + s = Stream_New(NULL, 1024); + if (!s) + goto out; + + retCode = 202; + if (!WinPrAsn1EncToStream(enc, s) || Stream_GetPosition(s) != expectedOuputSz) + goto out; + /* winpr_HexDump("", WLOG_ERROR, Stream_Buffer(s), Stream_GetPosition(s));*/ + + /* + * let's perform a mini-performance test, where we encode an ASN1 message with a big depth, + * so that we trigger reallocation routines in the encoder. We're gonna encode something like + * SEQ1 + * SEQ2 + * SEQ3 + * ... + * SEQ1000 + * INTEGER(2) + * + * As static chunks and containers are 50, a depth of 1000 should be enough + * + */ + WinPrAsn1Encoder_Reset(enc); + + retCode = 203; + for (size_t i = 0; i < 1000; i++) + { + if (!WinPrAsn1EncSeqContainer(enc)) + goto out; + } + + retCode = 204; + if (WinPrAsn1EncInteger(enc, 2) != 3) + goto out; + + retCode = 205; + for (size_t i = 0; i < 1000; i++) + { + if (!WinPrAsn1EncEndContainer(enc)) + goto out; + } + + retCode = 0; + +out: + if (s) + Stream_Free(s, TRUE); + WinPrAsn1Encoder_Free(&enc); + return retCode; +} + +int TestASN1(int argc, char* argv[]) +{ + int ret = TestASN1Read(argc, argv); + if (ret) + return ret; + + return TestASN1Write(argc, argv); +} diff --git a/winpr/libwinpr/utils/test/TestArrayList.c b/winpr/libwinpr/utils/test/TestArrayList.c new file mode 100644 index 0000000..069394c --- /dev/null +++ b/winpr/libwinpr/utils/test/TestArrayList.c @@ -0,0 +1,82 @@ + +#include +#include +#include + +int TestArrayList(int argc, char* argv[]) +{ + int count = 0; + int rc = 0; + size_t val = 0; + wArrayList* arrayList = NULL; + const size_t elemsToInsert = 10; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + arrayList = ArrayList_New(TRUE); + if (!arrayList) + return -1; + + for (size_t index = 0; index < elemsToInsert; index++) + { + if (!ArrayList_Append(arrayList, (void*)index)) + return -1; + } + + count = ArrayList_Count(arrayList); + + printf("ArrayList count: %d\n", count); + + SSIZE_T index = ArrayList_IndexOf(arrayList, (void*)(size_t)6, -1, -1); + + printf("ArrayList index: %" PRIdz "\n", index); + + if (index != 6) + return -1; + + ArrayList_Insert(arrayList, 5, (void*)(size_t)100); + + index = ArrayList_IndexOf(arrayList, (void*)(size_t)6, -1, -1); + printf("ArrayList index: %" PRIdz "\n", index); + + if (index != 7) + return -1; + + ArrayList_Remove(arrayList, (void*)(size_t)100); + + rc = ArrayList_IndexOf(arrayList, (void*)(size_t)6, -1, -1); + printf("ArrayList index: %d\n", rc); + + if (rc != 6) + return -1; + + for (size_t index = 0; index < elemsToInsert; index++) + { + val = (size_t)ArrayList_GetItem(arrayList, 0); + if (!ArrayList_RemoveAt(arrayList, 0)) + return -1; + if (val != index) + { + printf("ArrayList: shifted %" PRIdz " entries, expected value %" PRIdz ", got %" PRIdz + "\n", + index, index, val); + return -1; + } + } + + rc = ArrayList_IndexOf(arrayList, (void*)(size_t)elemsToInsert, -1, -1); + printf("ArrayList index: %d\n", rc); + if (rc != -1) + return -1; + + count = ArrayList_Count(arrayList); + printf("ArrayList count: %d\n", count); + if (count != 0) + return -1; + + ArrayList_Clear(arrayList); + ArrayList_Free(arrayList); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestBacktrace.c b/winpr/libwinpr/utils/test/TestBacktrace.c new file mode 100644 index 0000000..5fea333 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestBacktrace.c @@ -0,0 +1,34 @@ +#include +#include + +int TestBacktrace(int argc, char* argv[]) +{ + int rc = -1; + size_t used = 0; + char** msg = NULL; + void* stack = winpr_backtrace(20); + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!stack) + { + fprintf(stderr, "winpr_backtrace failed!\n"); + return -1; + } + + msg = winpr_backtrace_symbols(stack, &used); + + if (msg) + { + for (size_t x = 0; x < used; x++) + printf("%" PRIuz ": %s\n", x, msg[x]); + + rc = 0; + } + + winpr_backtrace_symbols_fd(stack, fileno(stdout)); + winpr_backtrace_free(stack); + free(msg); + return rc; +} diff --git a/winpr/libwinpr/utils/test/TestBitStream.c b/winpr/libwinpr/utils/test/TestBitStream.c new file mode 100644 index 0000000..83c911c --- /dev/null +++ b/winpr/libwinpr/utils/test/TestBitStream.c @@ -0,0 +1,86 @@ + +#include +#include +#include +#include + +static void BitStrGen(void) +{ + char str[64] = { 0 }; + + for (DWORD i = 0; i < 256;) + { + printf("\t"); + + for (DWORD j = 0; j < 4; j++) + { + if (0) + { + /* Least Significant Bit First */ + str[0] = (i & (1 << 7)) ? '1' : '0'; + str[1] = (i & (1 << 6)) ? '1' : '0'; + str[2] = (i & (1 << 5)) ? '1' : '0'; + str[3] = (i & (1 << 4)) ? '1' : '0'; + str[4] = (i & (1 << 3)) ? '1' : '0'; + str[5] = (i & (1 << 2)) ? '1' : '0'; + str[6] = (i & (1 << 1)) ? '1' : '0'; + str[7] = (i & (1 << 0)) ? '1' : '0'; + str[8] = '\0'; + } + else + { + /* Most Significant Bit First */ + str[7] = (i & (1 << 7)) ? '1' : '0'; + str[6] = (i & (1 << 6)) ? '1' : '0'; + str[5] = (i & (1 << 5)) ? '1' : '0'; + str[4] = (i & (1 << 4)) ? '1' : '0'; + str[3] = (i & (1 << 3)) ? '1' : '0'; + str[2] = (i & (1 << 2)) ? '1' : '0'; + str[1] = (i & (1 << 1)) ? '1' : '0'; + str[0] = (i & (1 << 0)) ? '1' : '0'; + str[8] = '\0'; + } + + printf("\"%s\",%s", str, j == 3 ? "" : " "); + i++; + } + + printf("\n"); + } +} + +int TestBitStream(int argc, char* argv[]) +{ + wBitStream* bs = NULL; + BYTE buffer[1024] = { 0 }; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + bs = BitStream_New(); + if (!bs) + return 1; + BitStream_Attach(bs, buffer, sizeof(buffer)); + BitStream_Write_Bits(bs, 0xAF, 8); /* 11110101 */ + BitStream_Write_Bits(bs, 0xF, 4); /* 1111 */ + BitStream_Write_Bits(bs, 0xA, 4); /* 0101 */ + BitStream_Flush(bs); + BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST); + BitStream_Write_Bits(bs, 3, 2); /* 11 */ + BitStream_Write_Bits(bs, 0, 3); /* 000 */ + BitStream_Write_Bits(bs, 0x2D, 6); /* 101101 */ + BitStream_Write_Bits(bs, 0x19, 5); /* 11001 */ + // BitStream_Flush(bs); /* flush should be done automatically here (32 bits written) */ + BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST); + BitStream_Write_Bits(bs, 3, 2); /* 11 */ + BitStream_Flush(bs); + BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST); + BitStream_Write_Bits(bs, 00, 2); /* 00 */ + BitStream_Write_Bits(bs, 0xF, 4); /* 1111 */ + BitStream_Write_Bits(bs, 0, 20); + BitStream_Write_Bits(bs, 0xAFF, 12); /* 111111110101 */ + BitStream_Flush(bs); + BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST); + BitStream_Free(bs); + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestBufferPool.c b/winpr/libwinpr/utils/test/TestBufferPool.c new file mode 100644 index 0000000..d954caa --- /dev/null +++ b/winpr/libwinpr/utils/test/TestBufferPool.c @@ -0,0 +1,68 @@ + +#include +#include +#include + +int TestBufferPool(int argc, char* argv[]) +{ + DWORD PoolSize = 0; + int BufferSize = 0; + wBufferPool* pool = NULL; + BYTE* Buffers[10]; + int DefaultSize = 1234; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + pool = BufferPool_New(TRUE, -1, 16); + if (!pool) + return -1; + + Buffers[0] = BufferPool_Take(pool, DefaultSize); + Buffers[1] = BufferPool_Take(pool, DefaultSize); + Buffers[2] = BufferPool_Take(pool, 2048); + if (!Buffers[0] || !Buffers[1] || !Buffers[2]) + return -1; + + BufferSize = BufferPool_GetBufferSize(pool, Buffers[0]); + + if (BufferSize != DefaultSize) + { + printf("BufferPool_GetBufferSize failure: Actual: %d Expected: %" PRIu32 "\n", BufferSize, + DefaultSize); + return -1; + } + + BufferSize = BufferPool_GetBufferSize(pool, Buffers[1]); + + if (BufferSize != DefaultSize) + { + printf("BufferPool_GetBufferSize failure: Actual: %d Expected: %" PRIu32 "\n", BufferSize, + DefaultSize); + return -1; + } + + BufferSize = BufferPool_GetBufferSize(pool, Buffers[2]); + + if (BufferSize != 2048) + { + printf("BufferPool_GetBufferSize failure: Actual: %d Expected: 2048\n", BufferSize); + return -1; + } + + BufferPool_Return(pool, Buffers[1]); + + PoolSize = BufferPool_GetPoolSize(pool); + + if (PoolSize != 2) + { + printf("BufferPool_GetPoolSize failure: Actual: %" PRIu32 " Expected: 2\n", PoolSize); + return -1; + } + + BufferPool_Clear(pool); + + BufferPool_Free(pool); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestCmdLine.c b/winpr/libwinpr/utils/test/TestCmdLine.c new file mode 100644 index 0000000..b6657f5 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestCmdLine.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include + +static const char* testArgv[] = { "mstsc.exe", + "+z", + "/w:1024", + "/h:768", + "/bpp:32", + "/admin", + "/multimon", + "+fonts", + "-wallpaper", + "/v:localhost:3389", + "/valuelist:value1,value2", + "/valuelist-empty:", + 0 }; + +static const char testListAppName[] = "test app name"; +static const char* testListArgs[] = { + "a,b,c,d", "a:,\"b:xxx, yyy\",c", "a:,,,b", "a:,\",b", "\"a,b,c,d d d,fff\"", "", + NULL, "'a,b,\",c'", "\"a,b,',c\"", "', a, ', b,c'", "\"a,b,\",c\"" +}; + +static const char* testListArgs1[] = { testListAppName, "a", "b", "c", "d" }; +static const char* testListArgs2[] = { testListAppName, "a:", "b:xxx, yyy", "c" }; +// static const char* testListArgs3[] = {}; +// static const char* testListArgs4[] = {}; +static const char* testListArgs5[] = { testListAppName, "a", "b", "c", "d d d", "fff" }; +static const char* testListArgs6[] = { testListAppName }; +static const char* testListArgs7[] = { testListAppName }; +static const char* testListArgs8[] = { testListAppName, "a", "b", "\"", "c" }; +static const char* testListArgs9[] = { testListAppName, "a", "b", "'", "c" }; +// static const char* testListArgs10[] = {}; +// static const char* testListArgs11[] = {}; + +static const char** testListArgsResult[] = { testListArgs1, + testListArgs2, + NULL /* testListArgs3 */, + NULL /* testListArgs4 */, + testListArgs5, + testListArgs6, + testListArgs7, + testListArgs8, + testListArgs9, + NULL /* testListArgs10 */, + NULL /* testListArgs11 */ }; +static const size_t testListArgsCount[] = { + ARRAYSIZE(testListArgs1), + ARRAYSIZE(testListArgs2), + 0 /* ARRAYSIZE(testListArgs3) */, + 0 /* ARRAYSIZE(testListArgs4) */, + ARRAYSIZE(testListArgs5), + ARRAYSIZE(testListArgs6), + ARRAYSIZE(testListArgs7), + ARRAYSIZE(testListArgs8), + ARRAYSIZE(testListArgs9), + 0 /* ARRAYSIZE(testListArgs10) */, + 0 /* ARRAYSIZE(testListArgs11) */ +}; + +static BOOL checkResult(size_t index, char** actual, size_t actualCount) +{ + const char** result = testListArgsResult[index]; + const size_t resultCount = testListArgsCount[index]; + + if (resultCount != actualCount) + return FALSE; + + if (actualCount == 0) + { + return (actual == NULL); + } + else + { + if (!actual) + return FALSE; + + for (size_t x = 0; x < actualCount; x++) + { + const char* a = result[x]; + const char* b = actual[x]; + + if (strcmp(a, b) != 0) + return FALSE; + } + } + + return TRUE; +} + +static BOOL TestCommandLineParseCommaSeparatedValuesEx(void) +{ + WINPR_ASSERT(ARRAYSIZE(testListArgs) == ARRAYSIZE(testListArgsResult)); + WINPR_ASSERT(ARRAYSIZE(testListArgs) == ARRAYSIZE(testListArgsCount)); + + for (size_t x = 0; x < ARRAYSIZE(testListArgs); x++) + { + const char* list = testListArgs[x]; + size_t count = 42; + char** ptr = CommandLineParseCommaSeparatedValuesEx(testListAppName, list, &count); + BOOL valid = checkResult(x, ptr, count); + free(ptr); + if (!valid) + return FALSE; + } + + return TRUE; +} + +int TestCmdLine(int argc, char* argv[]) +{ + int status = 0; + int ret = -1; + DWORD flags = 0; + long width = 0; + long height = 0; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + int testArgc = 0; + char** command_line = NULL; + COMMAND_LINE_ARGUMENT_A args[] = { + { "v", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "destination server" }, + { "port", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "server port" }, + { "w", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "width" }, + { "h", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "height" }, + { "f", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "fullscreen" }, + { "bpp", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, + "session bpp (color depth)" }, + { "admin", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "console", + "admin (or console) session" }, + { "multimon", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "multi-monitor" }, + { "a", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, "addin", "addin" }, + { "u", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "username" }, + { "p", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "password" }, + { "d", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "domain" }, + { "z", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "compression" }, + { "audio", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "audio output mode" }, + { "mic", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "audio input (microphone)" }, + { "fonts", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, + "smooth fonts (cleartype)" }, + { "aero", COMMAND_LINE_VALUE_BOOL, NULL, NULL, BoolValueFalse, -1, NULL, + "desktop composition" }, + { "window-drag", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, + "full window drag" }, + { "menu-anims", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, + "menu animations" }, + { "themes", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "themes" }, + { "wallpaper", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "wallpaper" }, + { "codec", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, "codec" }, + { "nego", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, + "protocol security negotiation" }, + { "sec", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, + "force specific protocol security" }, +#if defined(WITH_FREERDP_DEPRECATED) + { "sec-rdp", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, + "rdp protocol security" }, + { "sec-tls", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, + "tls protocol security" }, + { "sec-nla", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, + "nla protocol security" }, + { "sec-ext", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, + "nla extended protocol security" }, + { "cert-name", COMMAND_LINE_VALUE_REQUIRED, NULL, NULL, NULL, -1, NULL, + "certificate name" }, + { "cert-ignore", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "ignore certificate" }, +#endif + { "valuelist", COMMAND_LINE_VALUE_REQUIRED, ",", NULL, NULL, -1, NULL, + "List of comma separated values." }, + { "valuelist-empty", COMMAND_LINE_VALUE_REQUIRED, ",", NULL, NULL, -1, NULL, + "List of comma separated values. Used to test correct behavior if an empty list was " + "passed." }, + { "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, + NULL, "print version" }, + { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?", + "print help" }, + { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } + }; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + flags = COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_SIGIL_PLUS_MINUS; + testArgc = string_list_length(testArgv); + command_line = string_list_copy(testArgv); + + if (!command_line) + { + printf("Argument duplication failed (not enough memory?)\n"); + return ret; + } + + status = CommandLineParseArgumentsA(testArgc, command_line, args, flags, NULL, NULL, NULL); + + if (status != 0) + { + printf("CommandLineParseArgumentsA failure: %d\n", status); + goto out; + } + + arg = CommandLineFindArgumentA(args, "w"); + + if (strcmp("1024", arg->Value) != 0) + { + printf("CommandLineFindArgumentA: unexpected %s value %s\n", arg->Name, arg->Value); + goto out; + } + + arg = CommandLineFindArgumentA(args, "h"); + + if (strcmp("768", arg->Value) != 0) + { + printf("CommandLineFindArgumentA: unexpected %s value %s\n", arg->Name, arg->Value); + goto out; + } + + arg = CommandLineFindArgumentA(args, "f"); + + if (arg->Value) + { + printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name); + goto out; + } + + arg = CommandLineFindArgumentA(args, "admin"); + + if (!arg->Value) + { + printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name); + goto out; + } + + arg = CommandLineFindArgumentA(args, "multimon"); + + if (!arg->Value) + { + printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name); + goto out; + } + + arg = CommandLineFindArgumentA(args, "v"); + + if (strcmp("localhost:3389", arg->Value) != 0) + { + printf("CommandLineFindArgumentA: unexpected %s value %s\n", arg->Name, arg->Value); + goto out; + } + + arg = CommandLineFindArgumentA(args, "fonts"); + + if (!arg->Value) + { + printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name); + goto out; + } + + arg = CommandLineFindArgumentA(args, "wallpaper"); + + if (arg->Value) + { + printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name); + goto out; + } + + arg = CommandLineFindArgumentA(args, "help"); + + if (arg->Value) + { + printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name); + goto out; + } + + arg = args; + errno = 0; + + do + { + if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT)) + continue; + + printf("Argument: %s\n", arg->Name); + CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "v") + { + } + CommandLineSwitchCase(arg, "w") + { + width = strtol(arg->Value, NULL, 0); + + if (errno != 0) + goto out; + } + CommandLineSwitchCase(arg, "h") + { + height = strtol(arg->Value, NULL, 0); + + if (errno != 0) + goto out; + } + CommandLineSwitchCase(arg, "valuelist") + { + char** p = NULL; + size_t count = 0; + p = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count); + free(p); + + if (!p || count != 3) + { + printf("CommandLineParseCommaSeparatedValuesEx: invalid p or count (%" PRIuz + "!=3)\n", + count); + goto out; + } + } + CommandLineSwitchCase(arg, "valuelist-empty") + { + char** p = NULL; + size_t count = 0; + p = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count); + free(p); + + if (!p || count != 1) + { + printf("CommandLineParseCommaSeparatedValuesEx: invalid p or count (%" PRIuz + "!=1)\n", + count); + goto out; + } + } + CommandLineSwitchDefault(arg) + { + } + CommandLineSwitchEnd(arg) + } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + if ((width != 1024) || (height != 768)) + { + printf("Unexpected width and height: Actual: (%ldx%ld), Expected: (1024x768)\n", width, + height); + goto out; + } + ret = 0; + +out: + string_list_free(command_line); + + if (!TestCommandLineParseCommaSeparatedValuesEx()) + return -1; + return ret; +} diff --git a/winpr/libwinpr/utils/test/TestHashTable.c b/winpr/libwinpr/utils/test/TestHashTable.c new file mode 100644 index 0000000..764853f --- /dev/null +++ b/winpr/libwinpr/utils/test/TestHashTable.c @@ -0,0 +1,448 @@ + +#include +#include +#include + +static char* key1 = "key1"; +static char* key2 = "key2"; +static char* key3 = "key3"; + +static char* val1 = "val1"; +static char* val2 = "val2"; +static char* val3 = "val3"; + +static int test_hash_table_pointer(void) +{ + int rc = -1; + size_t count = 0; + char* value = NULL; + wHashTable* table = NULL; + table = HashTable_New(TRUE); + + if (!table) + return -1; + + if (!HashTable_Insert(table, key1, val1)) + goto fail; + if (!HashTable_Insert(table, key2, val2)) + goto fail; + if (!HashTable_Insert(table, key3, val3)) + goto fail; + count = HashTable_Count(table); + + if (count != 3) + { + printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Remove(table, key2)) + goto fail; + count = HashTable_Count(table); + + if (count != 2) + { + printf("HashTable_Count: Expected : 2, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Remove(table, key3)) + goto fail; + count = HashTable_Count(table); + + if (count != 1) + { + printf("HashTable_Count: Expected : 1, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Remove(table, key1)) + goto fail; + count = HashTable_Count(table); + + if (count != 0) + { + printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Insert(table, key1, val1)) + goto fail; + if (!HashTable_Insert(table, key2, val2)) + goto fail; + if (!HashTable_Insert(table, key3, val3)) + goto fail; + count = HashTable_Count(table); + + if (count != 3) + { + printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count); + goto fail; + } + + value = (char*)HashTable_GetItemValue(table, key1); + + if (strcmp(value, val1) != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val1, value); + goto fail; + } + + value = (char*)HashTable_GetItemValue(table, key2); + + if (strcmp(value, val2) != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val2, value); + goto fail; + } + + value = (char*)HashTable_GetItemValue(table, key3); + + if (strcmp(value, val3) != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val3, value); + goto fail; + } + + if (!HashTable_SetItemValue(table, key2, "apple")) + goto fail; + value = (char*)HashTable_GetItemValue(table, key2); + + if (strcmp(value, "apple") != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", "apple", value); + goto fail; + } + + if (!HashTable_Contains(table, key2)) + { + printf("HashTable_Contains: Expected : TRUE, Actual: FALSE\n"); + goto fail; + } + + if (!HashTable_Remove(table, key2)) + { + printf("HashTable_Remove: Expected : TRUE, Actual: FALSE\n"); + goto fail; + } + + if (HashTable_Remove(table, key2)) + { + printf("HashTable_Remove: Expected : FALSE, Actual: TRUE\n"); + goto fail; + } + + HashTable_Clear(table); + count = HashTable_Count(table); + + if (count != 0) + { + printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count); + goto fail; + } + + rc = 1; +fail: + HashTable_Free(table); + return rc; +} + +static int test_hash_table_string(void) +{ + int rc = -1; + size_t count = 0; + char* value = NULL; + wHashTable* table = HashTable_New(TRUE); + + if (!table) + return -1; + + if (!HashTable_SetupForStringData(table, TRUE)) + goto fail; + + if (!HashTable_Insert(table, key1, val1)) + goto fail; + if (!HashTable_Insert(table, key2, val2)) + goto fail; + if (!HashTable_Insert(table, key3, val3)) + goto fail; + count = HashTable_Count(table); + + if (count != 3) + { + printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Remove(table, key2)) + goto fail; + count = HashTable_Count(table); + + if (count != 2) + { + printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Remove(table, key3)) + goto fail; + count = HashTable_Count(table); + + if (count != 1) + { + printf("HashTable_Count: Expected : 1, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Remove(table, key1)) + goto fail; + count = HashTable_Count(table); + + if (count != 0) + { + printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count); + goto fail; + } + + if (!HashTable_Insert(table, key1, val1)) + goto fail; + if (!HashTable_Insert(table, key2, val2)) + goto fail; + if (!HashTable_Insert(table, key3, val3)) + goto fail; + count = HashTable_Count(table); + + if (count != 3) + { + printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count); + goto fail; + } + + value = (char*)HashTable_GetItemValue(table, key1); + + if (strcmp(value, val1) != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val1, value); + goto fail; + } + + value = (char*)HashTable_GetItemValue(table, key2); + + if (strcmp(value, val2) != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val2, value); + goto fail; + } + + value = (char*)HashTable_GetItemValue(table, key3); + + if (strcmp(value, val3) != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val3, value); + goto fail; + } + + if (!HashTable_SetItemValue(table, key2, "apple")) + goto fail; + value = (char*)HashTable_GetItemValue(table, key2); + + if (strcmp(value, "apple") != 0) + { + printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", "apple", value); + goto fail; + } + + if (!HashTable_Contains(table, key2)) + { + printf("HashTable_Contains: Expected : TRUE, Actual: FALSE\n"); + goto fail; + } + + if (!HashTable_Remove(table, key2)) + { + printf("HashTable_Remove: Expected : TRUE, Actual: FALSE\n"); + goto fail; + } + + if (HashTable_Remove(table, key2)) + { + printf("HashTable_Remove: Expected : FALSE, Actual: TRUE\n"); + goto fail; + } + + HashTable_Clear(table); + count = HashTable_Count(table); + + if (count != 0) + { + printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count); + goto fail; + } + + rc = 1; +fail: + HashTable_Free(table); + return rc; +} + +typedef struct +{ + wHashTable* table; + int strlenCounter; + int foreachCalls; + + BOOL test3error; +} ForeachData; + +static BOOL foreachFn1(const void* key, void* value, void* arg) +{ + ForeachData* d = (ForeachData*)arg; + WINPR_UNUSED(key); + d->strlenCounter += strlen((const char*)value); + return TRUE; +} + +static BOOL foreachFn2(const void* key, void* value, void* arg) +{ + ForeachData* d = (ForeachData*)arg; + WINPR_UNUSED(key); + WINPR_UNUSED(value); + d->foreachCalls++; + + if (d->foreachCalls == 2) + return FALSE; + return TRUE; +} + +static BOOL foreachFn3(const void* key, void* value, void* arg) +{ + const char* keyStr = (const char*)key; + + ForeachData* d = (ForeachData*)arg; + ForeachData d2; + + WINPR_UNUSED(value); + WINPR_ASSERT(keyStr); + + if (strcmp(keyStr, "key1") == 0) + { + /* when we pass on key1, let's remove key2 and check that the value is not + * visible anymore (even if has just been marked for removal)*/ + HashTable_Remove(d->table, "key2"); + + if (HashTable_Contains(d->table, "key2")) + { + d->test3error = TRUE; + return FALSE; + } + + if (HashTable_ContainsValue(d->table, "value2")) + { + d->test3error = TRUE; + return FALSE; + } + + /* number of elements of the table shall be correct too */ + if (HashTable_Count(d->table) != 2) + { + d->test3error = TRUE; + return FALSE; + } + + /* we try recursive HashTable_Foreach */ + d2.table = d->table; + d2.strlenCounter = 0; + + if (!HashTable_Foreach(d->table, foreachFn1, &d2)) + { + d->test3error = TRUE; + return FALSE; + } + if (d2.strlenCounter != 8) + { + d->test3error = TRUE; + return FALSE; + } + } + return TRUE; +} + +static int test_hash_foreach(void) +{ + ForeachData foreachData; + wHashTable* table = NULL; + int retCode = 0; + + foreachData.table = table = HashTable_New(TRUE); + if (!table) + return -1; + + if (!HashTable_SetupForStringData(table, TRUE)) + goto out; + + if (HashTable_Insert(table, key1, val1) < 0 || HashTable_Insert(table, key2, val2) < 0 || + HashTable_Insert(table, key3, val3) < 0) + { + retCode = -2; + goto out; + } + + /* let's try a first trivial foreach */ + foreachData.strlenCounter = 0; + if (!HashTable_Foreach(table, foreachFn1, &foreachData)) + { + retCode = -10; + goto out; + } + if (foreachData.strlenCounter != 12) + { + retCode = -11; + goto out; + } + + /* interrupted foreach */ + foreachData.foreachCalls = 0; + if (HashTable_Foreach(table, foreachFn2, &foreachData)) + { + retCode = -20; + goto out; + } + if (foreachData.foreachCalls != 2) + { + retCode = -21; + goto out; + } + + /* let's try a foreach() call that will remove a value from the table in the callback */ + foreachData.test3error = FALSE; + if (!HashTable_Foreach(table, foreachFn3, &foreachData)) + { + retCode = -30; + goto out; + } + if (foreachData.test3error) + { + retCode = -31; + goto out; + } + +out: + HashTable_Free(table); + return retCode; +} + +int TestHashTable(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (test_hash_table_pointer() < 0) + return 1; + + if (test_hash_table_string() < 0) + return 2; + + if (test_hash_foreach() < 0) + return 3; + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestImage.c b/winpr/libwinpr/utils/test/TestImage.c new file mode 100644 index 0000000..8635fa1 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestImage.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include + +static const char test_src_filename[] = TEST_SOURCE_PATH "/rgb"; +static const char test_bin_filename[] = TEST_BINARY_PATH "/rgb"; + +static BOOL test_image_equal(const wImage* imageA, const wImage* imageB) +{ + return winpr_image_equal(imageA, imageB, + WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA | + WINPR_IMAGE_CMP_FUZZY); +} + +static BOOL test_equal_to(const wImage* bmp, const char* name, UINT32 format) +{ + BOOL rc = FALSE; + wImage* cmp = winpr_image_new(); + if (!cmp) + goto fail; + + char path[MAX_PATH] = { 0 }; + _snprintf(path, sizeof(path), "%s.%s", name, winpr_image_format_extension(format)); + const int cmpSize = winpr_image_read(cmp, path); + if (cmpSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, path); + goto fail; + } + + rc = test_image_equal(bmp, cmp); + if (!rc) + fprintf(stderr, "[%s] winpr_image_eqal failed", __func__); + +fail: + winpr_image_free(cmp, TRUE); + return rc; +} + +static BOOL test_equal(void) +{ + BOOL rc = FALSE; + wImage* bmp = winpr_image_new(); + + if (!bmp) + goto fail; + + char path[MAX_PATH] = { 0 }; + _snprintf(path, sizeof(path), "%s.bmp", test_src_filename); + PathCchConvertStyleA(path, sizeof(path), PATH_STYLE_NATIVE); + + const int bmpSize = winpr_image_read(bmp, path); + if (bmpSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, path); + goto fail; + } + + for (UINT32 x = 0; x < UINT8_MAX; x++) + { + if (!winpr_image_format_is_supported(x)) + continue; + if (!test_equal_to(bmp, test_src_filename, x)) + goto fail; + } + + rc = TRUE; +fail: + winpr_image_free(bmp, TRUE); + + return rc; +} + +static BOOL test_read_write_compare(const char* tname, const char* tdst, UINT32 format) +{ + BOOL rc = FALSE; + wImage* bmp1 = winpr_image_new(); + wImage* bmp2 = winpr_image_new(); + wImage* bmp3 = winpr_image_new(); + if (!bmp1 || !bmp2 || !bmp3) + goto fail; + + char spath[MAX_PATH] = { 0 }; + char dpath[MAX_PATH] = { 0 }; + char bpath1[MAX_PATH] = { 0 }; + char bpath2[MAX_PATH] = { 0 }; + _snprintf(spath, sizeof(spath), "%s.%s", tname, winpr_image_format_extension(format)); + _snprintf(dpath, sizeof(dpath), "%s.%s", tdst, winpr_image_format_extension(format)); + _snprintf(bpath1, sizeof(bpath1), "%s.src.%s", dpath, + winpr_image_format_extension(WINPR_IMAGE_BITMAP)); + _snprintf(bpath2, sizeof(bpath2), "%s.bin.%s", dpath, + winpr_image_format_extension(WINPR_IMAGE_BITMAP)); + PathCchConvertStyleA(spath, sizeof(spath), PATH_STYLE_NATIVE); + PathCchConvertStyleA(dpath, sizeof(dpath), PATH_STYLE_NATIVE); + PathCchConvertStyleA(bpath1, sizeof(bpath1), PATH_STYLE_NATIVE); + PathCchConvertStyleA(bpath2, sizeof(bpath2), PATH_STYLE_NATIVE); + + const int bmpRSize = winpr_image_read(bmp1, spath); + if (bmpRSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, spath); + goto fail; + } + + const int bmpWSize = winpr_image_write(bmp1, dpath); + if (bmpWSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_write failed for %s", __func__, dpath); + goto fail; + } + + const int bmp2RSize = winpr_image_read(bmp2, dpath); + if (bmp2RSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, dpath); + goto fail; + } + + const int bmpSrcWSize = winpr_image_write_ex(bmp1, WINPR_IMAGE_BITMAP, bpath1); + if (bmpSrcWSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_write_ex failed for %s", __func__, bpath1); + goto fail; + } + + /* write a bitmap and read it back. + * this tests if we have the proper internal format */ + const int bmpBinWSize = winpr_image_write_ex(bmp2, WINPR_IMAGE_BITMAP, bpath2); + if (bmpBinWSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_write_ex failed for %s", __func__, bpath2); + goto fail; + } + + const int bmp3RSize = winpr_image_read(bmp3, bpath2); + if (bmp3RSize <= 0) + { + fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, bpath2); + goto fail; + } + + if (!winpr_image_equal(bmp1, bmp2, + WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA | + WINPR_IMAGE_CMP_FUZZY)) + { + fprintf(stderr, "[%s] winpr_image_eqal failed bmp1 bmp2", __func__); + goto fail; + } + + rc = winpr_image_equal(bmp3, bmp2, + WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA | + WINPR_IMAGE_CMP_FUZZY); + if (!rc) + fprintf(stderr, "[%s] winpr_image_eqal failed bmp3 bmp2", __func__); +fail: + winpr_image_free(bmp1, TRUE); + winpr_image_free(bmp2, TRUE); + winpr_image_free(bmp3, TRUE); + return rc; +} + +static BOOL test_read_write(void) +{ + BOOL rc = TRUE; + for (UINT32 x = 0; x < UINT8_MAX; x++) + { + if (!winpr_image_format_is_supported(x)) + continue; + if (!test_read_write_compare(test_src_filename, test_bin_filename, x)) + rc = FALSE; + } + return rc; +} + +static BOOL test_load_file(const char* name) +{ + BOOL rc = FALSE; + wImage* image = winpr_image_new(); + if (!image || !name) + goto fail; + + const int res = winpr_image_read(image, name); + rc = (res > 0) ? TRUE : FALSE; + +fail: + winpr_image_free(image, TRUE); + return rc; +} + +static BOOL test_load(void) +{ + const char* names[] = { + "rgb.16a.bmp", "rgb.16a.nocolor.bmp", "rgb.16.bmp", "rgb.16.nocolor.bmp", + "rgb.16x.bmp", "rgb.16x.nocolor.bmp", "rgb.24.bmp", "rgb.24.nocolor.bmp", + "rgb.32.bmp", "rgb.32.nocolor.bmp", "rgb.32x.bmp", "rgb.32x.nocolor.bmp", + "rgb.bmp" + }; + + for (size_t x = 0; x < ARRAYSIZE(names); x++) + { + const char* name = names[x]; + char* fname = GetCombinedPath(TEST_SOURCE_PATH, name); + const BOOL res = test_load_file(fname); + free(fname); + if (!res) + return FALSE; + } + + return TRUE; +} + +int TestImage(int argc, char* argv[]) +{ + int rc = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_equal()) + rc -= 1; + + if (!test_read_write()) + rc -= 2; + + if (!test_load()) + rc -= 4; + + return rc; +} diff --git a/winpr/libwinpr/utils/test/TestIni.c b/winpr/libwinpr/utils/test/TestIni.c new file mode 100644 index 0000000..2dd24f0 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestIni.c @@ -0,0 +1,160 @@ + +#include +#include + +static const char TEST_INI_01[] = "; This is a sample .ini config file\n" + "\n" + "[first_section]\n" + "one = 1\n" + "five = 5\n" + "animal = BIRD\n" + "\n" + "[second_section]\n" + "path = \"/usr/local/bin\"\n" + "URL = \"http://www.example.com/~username\"\n" + "\n"; + +static const char TEST_INI_02[] = "[FreeRDS]\n" + "prefix=\"/usr/local\"\n" + "bindir=\"bin\"\n" + "sbindir=\"sbin\"\n" + "libdir=\"lib\"\n" + "datarootdir=\"share\"\n" + "localstatedir=\"var\"\n" + "sysconfdir=\"etc\"\n" + "\n"; + +static const char TEST_INI_03[] = "[FreeRDS]\n" + "prefix=\"/usr/local\"\n" + "bindir=\"bin\"\n" + "# some illegal string\n" + "sbindir=\"sbin\"\n" + "libdir=\"lib\"\n" + "invalid key-value pair\n" + "datarootdir=\"share\"\n" + "localstatedir=\"var\"\n" + "sysconfdir=\"etc\"\n" + "\n"; + +int TestIni(int argc, char* argv[]) +{ + int rc = -1; + size_t nKeys = 0; + size_t nSections = 0; + UINT32 iValue = 0; + wIniFile* ini = NULL; + const char* sValue = NULL; + char** keyNames = NULL; + char** sectionNames = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* First Sample */ + ini = IniFile_New(); + if (!ini) + goto fail; + + if (IniFile_ReadBuffer(ini, TEST_INI_01) < 0) + goto fail; + + free(sectionNames); + sectionNames = IniFile_GetSectionNames(ini, &nSections); + if (!sectionNames && (nSections > 0)) + goto fail; + + for (size_t i = 0; i < nSections; i++) + { + free(keyNames); + keyNames = IniFile_GetSectionKeyNames(ini, sectionNames[i], &nKeys); + printf("[%s]\n", sectionNames[i]); + if (!keyNames && (nKeys > 0)) + goto fail; + for (size_t j = 0; j < nKeys; j++) + { + sValue = IniFile_GetKeyValueString(ini, sectionNames[i], keyNames[j]); + printf("%s = %s\n", keyNames[j], sValue); + } + } + + iValue = IniFile_GetKeyValueInt(ini, "first_section", "one"); + + if (iValue != 1) + { + printf("IniFile_GetKeyValueInt failure\n"); + goto fail; + } + + iValue = IniFile_GetKeyValueInt(ini, "first_section", "five"); + + if (iValue != 5) + { + printf("IniFile_GetKeyValueInt failure\n"); + goto fail; + } + + sValue = IniFile_GetKeyValueString(ini, "first_section", "animal"); + + if (strcmp(sValue, "BIRD") != 0) + { + printf("IniFile_GetKeyValueString failure\n"); + goto fail; + } + + sValue = IniFile_GetKeyValueString(ini, "second_section", "path"); + + if (strcmp(sValue, "/usr/local/bin") != 0) + { + printf("IniFile_GetKeyValueString failure\n"); + goto fail; + } + + sValue = IniFile_GetKeyValueString(ini, "second_section", "URL"); + + if (strcmp(sValue, "http://www.example.com/~username") != 0) + { + printf("IniFile_GetKeyValueString failure\n"); + goto fail; + } + + IniFile_Free(ini); + /* Second Sample */ + ini = IniFile_New(); + if (!ini) + goto fail; + if (IniFile_ReadBuffer(ini, TEST_INI_02) < 0) + goto fail; + free(sectionNames); + sectionNames = IniFile_GetSectionNames(ini, &nSections); + if (!sectionNames && (nSections > 0)) + goto fail; + + for (size_t i = 0; i < nSections; i++) + { + free(keyNames); + keyNames = IniFile_GetSectionKeyNames(ini, sectionNames[i], &nKeys); + printf("[%s]\n", sectionNames[i]); + + if (!keyNames && (nKeys > 0)) + goto fail; + for (size_t j = 0; j < nKeys; j++) + { + sValue = IniFile_GetKeyValueString(ini, sectionNames[i], keyNames[j]); + printf("%s = %s\n", keyNames[j], sValue); + } + } + + IniFile_Free(ini); + /* Third sample - invalid input */ + ini = IniFile_New(); + + if (IniFile_ReadBuffer(ini, TEST_INI_03) != -1) + goto fail; + + rc = 0; +fail: + free(keyNames); + free(sectionNames); + IniFile_Free(ini); + return rc; +} diff --git a/winpr/libwinpr/utils/test/TestLinkedList.c b/winpr/libwinpr/utils/test/TestLinkedList.c new file mode 100644 index 0000000..6c6a2c3 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestLinkedList.c @@ -0,0 +1,135 @@ + +#include +#include +#include + +int TestLinkedList(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + wLinkedList* list = LinkedList_New(); + if (!list) + return -1; + + if (!LinkedList_AddFirst(list, (void*)(size_t)1)) + return -1; + if (!LinkedList_AddLast(list, (void*)(size_t)2)) + return -1; + if (!LinkedList_AddLast(list, (void*)(size_t)3)) + return -1; + size_t count = LinkedList_Count(list); + + if (count != 3) + { + printf("LinkedList_Count: expected 3, actual: %" PRIuz "\n", count); + return -1; + } + + LinkedList_Enumerator_Reset(list); + + while (LinkedList_Enumerator_MoveNext(list)) + { + printf("\t%p\n", LinkedList_Enumerator_Current(list)); + } + + printf("\n"); + printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list)); + LinkedList_RemoveFirst(list); + LinkedList_RemoveLast(list); + count = LinkedList_Count(list); + + if (count != 1) + { + printf("LinkedList_Count: expected 1, actual: %" PRIuz "\n", count); + return -1; + } + + LinkedList_Enumerator_Reset(list); + + while (LinkedList_Enumerator_MoveNext(list)) + { + printf("\t%p\n", LinkedList_Enumerator_Current(list)); + } + + printf("\n"); + printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list)); + LinkedList_RemoveFirst(list); + LinkedList_RemoveLast(list); + count = LinkedList_Count(list); + + if (count != 0) + { + printf("LinkedList_Count: expected 0, actual: %" PRIuz "\n", count); + return -1; + } + + if (!LinkedList_AddFirst(list, (void*)(size_t)4)) + return -1; + if (!LinkedList_AddLast(list, (void*)(size_t)5)) + return -1; + if (!LinkedList_AddLast(list, (void*)(size_t)6)) + return -1; + count = LinkedList_Count(list); + + if (count != 3) + { + printf("LinkedList_Count: expected 3, actual: %" PRIuz "\n", count); + return -1; + } + + LinkedList_Enumerator_Reset(list); + + while (LinkedList_Enumerator_MoveNext(list)) + { + printf("\t%p\n", LinkedList_Enumerator_Current(list)); + } + + printf("\n"); + printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list)); + if (!LinkedList_Remove(list, (void*)(size_t)5)) + return -1; + LinkedList_Enumerator_Reset(list); + + while (LinkedList_Enumerator_MoveNext(list)) + { + printf("\t%p\n", LinkedList_Enumerator_Current(list)); + } + + printf("\n"); + printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list)); + LinkedList_Free(list); + /* Test enumerator robustness */ + /* enumerator on an empty list */ + list = LinkedList_New(); + if (!list) + return -1; + LinkedList_Enumerator_Reset(list); + + while (LinkedList_Enumerator_MoveNext(list)) + { + printf("\terror: %p\n", LinkedList_Enumerator_Current(list)); + } + + printf("\n"); + LinkedList_Free(list); + /* Use an enumerator without reset */ + list = LinkedList_New(); + if (!list) + return -1; + if (!LinkedList_AddFirst(list, (void*)(size_t)4)) + return -1; + if (!LinkedList_AddLast(list, (void*)(size_t)5)) + return -1; + if (!LinkedList_AddLast(list, (void*)(size_t)6)) + return -1; + + while (LinkedList_Enumerator_MoveNext(list)) + { + printf("\t%p\n", LinkedList_Enumerator_Current(list)); + } + + printf("\n"); + LinkedList_Free(list); + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestListDictionary.c b/winpr/libwinpr/utils/test/TestListDictionary.c new file mode 100644 index 0000000..7be8013 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestListDictionary.c @@ -0,0 +1,178 @@ + +#include +#include +#include + +static char* key1 = "key1"; +static char* key2 = "key2"; +static char* key3 = "key3"; + +static char* val1 = "val1"; +static char* val2 = "val2"; +static char* val3 = "val3"; + +int TestListDictionary(int argc, char* argv[]) +{ + size_t count = 0; + char* value = NULL; + wListDictionary* list = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + list = ListDictionary_New(TRUE); + if (!list) + return -1; + + if (!ListDictionary_Add(list, key1, val1) || !ListDictionary_Add(list, key2, val2) || + !ListDictionary_Add(list, key3, val3)) + return -1; + + count = ListDictionary_Count(list); + + if (count != 3) + { + printf("ListDictionary_Count: Expected : 3, Actual: %" PRIuz "\n", count); + return -1; + } + + ListDictionary_Remove(list, key2); + + count = ListDictionary_Count(list); + + if (count != 2) + { + printf("ListDictionary_Count: Expected : 2, Actual: %" PRIuz "\n", count); + return -1; + } + + ListDictionary_Remove(list, key3); + + count = ListDictionary_Count(list); + + if (count != 1) + { + printf("ListDictionary_Count: Expected : 1, Actual: %" PRIuz "\n", count); + return -1; + } + + ListDictionary_Remove(list, key1); + + count = ListDictionary_Count(list); + + if (count != 0) + { + printf("ListDictionary_Count: Expected : 0, Actual: %" PRIuz "\n", count); + return -1; + } + + if (!ListDictionary_Add(list, key1, val1) || !ListDictionary_Add(list, key2, val2) || + !ListDictionary_Add(list, key3, val3)) + return -1; + + count = ListDictionary_Count(list); + + if (count != 3) + { + printf("ListDictionary_Count: Expected : 3, Actual: %" PRIuz "\n", count); + return -1; + } + + value = (char*)ListDictionary_GetItemValue(list, key1); + + if (strcmp(value, val1) != 0) + { + printf("ListDictionary_GetItemValue: Expected : %" PRIuz ", Actual: %" PRIuz "\n", + (size_t)val1, (size_t)value); + return -1; + } + + value = (char*)ListDictionary_GetItemValue(list, key2); + + if (strcmp(value, val2) != 0) + { + printf("ListDictionary_GetItemValue: Expected : %" PRIuz ", Actual: %" PRIuz "\n", + (size_t)val2, (size_t)value); + return -1; + } + + value = (char*)ListDictionary_GetItemValue(list, key3); + + if (strcmp(value, val3) != 0) + { + printf("ListDictionary_GetItemValue: Expected : %" PRIuz ", Actual: %" PRIuz "\n", + (size_t)val3, (size_t)value); + return -1; + } + + ListDictionary_SetItemValue(list, key2, "apple"); + + value = (char*)ListDictionary_GetItemValue(list, key2); + + if (strcmp(value, "apple") != 0) + { + printf("ListDictionary_GetItemValue: Expected : %s, Actual: %s\n", "apple", value); + return -1; + } + + if (!ListDictionary_Contains(list, key2)) + { + printf("ListDictionary_Contains: Expected : TRUE, Actual: FALSE\n"); + return -1; + } + + if (!ListDictionary_Take(list, key2)) + { + printf("ListDictionary_Remove: Expected : TRUE, Actual: FALSE\n"); + return -1; + } + + if (ListDictionary_Take(list, key2)) + { + printf("ListDictionary_Remove: Expected : FALSE, Actual: TRUE\n"); + return -1; + } + + value = ListDictionary_Take_Head(list); + count = ListDictionary_Count(list); + if (strncmp(value, val1, 4) || count != 1) + { + printf("ListDictionary_Remove_Head: Expected : %s, Actual: %s Count: %" PRIuz "\n", val1, + value, count); + return -1; + } + + value = ListDictionary_Take_Head(list); + count = ListDictionary_Count(list); + if (strncmp(value, val3, 4) || count != 0) + { + printf("ListDictionary_Remove_Head: Expected : %s, Actual: %s Count: %" PRIuz "\n", val3, + value, count); + return -1; + } + + value = ListDictionary_Take_Head(list); + if (value) + { + printf("ListDictionary_Remove_Head: Expected : (null), Actual: %s\n", value); + return -1; + } + + if (!ListDictionary_Add(list, key1, val1) || !ListDictionary_Add(list, key2, val2) || + !ListDictionary_Add(list, key3, val3)) + return -1; + + ListDictionary_Clear(list); + + count = ListDictionary_Count(list); + + if (count != 0) + { + printf("ListDictionary_Count: Expected : 0, Actual: %" PRIuz "\n", count); + return -1; + } + + ListDictionary_Free(list); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestMessagePipe.c b/winpr/libwinpr/utils/test/TestMessagePipe.c new file mode 100644 index 0000000..c5a3ee2 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestMessagePipe.c @@ -0,0 +1,105 @@ + +#include +#include +#include + +static DWORD WINAPI message_echo_pipe_client_thread(LPVOID arg) +{ + int index = 0; + wMessagePipe* pipe = (wMessagePipe*)arg; + + while (index < 100) + { + wMessage message = { 0 }; + int count = -1; + + if (!MessageQueue_Post(pipe->In, NULL, 0, (void*)(size_t)index, NULL)) + break; + + if (!MessageQueue_Wait(pipe->Out)) + break; + + if (!MessageQueue_Peek(pipe->Out, &message, TRUE)) + break; + + if (message.id == WMQ_QUIT) + break; + + count = (int)(size_t)message.wParam; + + if (count != index) + printf("Echo count mismatch: Actual: %d, Expected: %d\n", count, index); + + index++; + } + + MessageQueue_PostQuit(pipe->In, 0); + + return 0; +} + +static DWORD WINAPI message_echo_pipe_server_thread(LPVOID arg) +{ + wMessage message = { 0 }; + wMessagePipe* pipe = (wMessagePipe*)arg; + + while (MessageQueue_Wait(pipe->In)) + { + if (MessageQueue_Peek(pipe->In, &message, TRUE)) + { + if (message.id == WMQ_QUIT) + break; + + if (!MessageQueue_Dispatch(pipe->Out, &message)) + break; + } + } + + return 0; +} + +int TestMessagePipe(int argc, char* argv[]) +{ + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; + wMessagePipe* EchoPipe = NULL; + int ret = 1; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!(EchoPipe = MessagePipe_New())) + { + printf("failed to create message pipe\n"); + goto out; + } + + if (!(ClientThread = + CreateThread(NULL, 0, message_echo_pipe_client_thread, (void*)EchoPipe, 0, NULL))) + { + printf("failed to create client thread\n"); + goto out; + } + + if (!(ServerThread = + CreateThread(NULL, 0, message_echo_pipe_server_thread, (void*)EchoPipe, 0, NULL))) + { + printf("failed to create server thread\n"); + goto out; + } + + WaitForSingleObject(ClientThread, INFINITE); + WaitForSingleObject(ServerThread, INFINITE); + + ret = 0; + +out: + if (EchoPipe) + MessagePipe_Free(EchoPipe); + if (ClientThread) + CloseHandle(ClientThread); + if (ServerThread) + CloseHandle(ServerThread); + + return ret; +} diff --git a/winpr/libwinpr/utils/test/TestMessageQueue.c b/winpr/libwinpr/utils/test/TestMessageQueue.c new file mode 100644 index 0000000..b3245b1 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestMessageQueue.c @@ -0,0 +1,56 @@ + +#include +#include +#include + +static DWORD WINAPI message_queue_consumer_thread(LPVOID arg) +{ + wMessage message = { 0 }; + wMessageQueue* queue = (wMessageQueue*)arg; + + while (MessageQueue_Wait(queue)) + { + if (MessageQueue_Peek(queue, &message, TRUE)) + { + if (message.id == WMQ_QUIT) + break; + + printf("Message.Type: %" PRIu32 "\n", message.id); + } + } + + return 0; +} + +int TestMessageQueue(int argc, char* argv[]) +{ + HANDLE thread = NULL; + wMessageQueue* queue = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!(queue = MessageQueue_New(NULL))) + { + printf("failed to create message queue\n"); + return 1; + } + + if (!(thread = CreateThread(NULL, 0, message_queue_consumer_thread, (void*)queue, 0, NULL))) + { + printf("failed to create thread\n"); + MessageQueue_Free(queue); + return 1; + } + + if (!MessageQueue_Post(queue, NULL, 123, NULL, NULL) || + !MessageQueue_Post(queue, NULL, 456, NULL, NULL) || + !MessageQueue_Post(queue, NULL, 789, NULL, NULL) || !MessageQueue_PostQuit(queue, 0) || + WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) + return -1; + + MessageQueue_Free(queue); + CloseHandle(thread); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestPrint.c b/winpr/libwinpr/utils/test/TestPrint.c new file mode 100644 index 0000000..f60b903 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestPrint.c @@ -0,0 +1,401 @@ + +#include +#include +#include +#include + +/** + * C Programming/C Reference/stdio.h/printf: + * http://en.wikibooks.org/wiki/C_Programming/C_Reference/stdio.h/printf + * + * C Programming/Procedures and functions/printf: + * http://en.wikibooks.org/wiki/C_Programming/Procedures_and_functions/printf + * + * C Tutorial – printf, Format Specifiers, Format Conversions and Formatted Output: + * http://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output + */ + +#define _printf printf + +static BOOL test_bin_tohex_string(void) +{ + BOOL rc = FALSE; + { + const BYTE binbuffer[33] = { 0 }; + const char empty[33] = { 0 }; + char strbuffer[33] = { 0 }; + + size_t len = + winpr_BinToHexStringBuffer(NULL, sizeof(binbuffer), strbuffer, sizeof(strbuffer), TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer, 0, strbuffer, sizeof(strbuffer), TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = + winpr_BinToHexStringBuffer(binbuffer, sizeof(binbuffer), NULL, sizeof(strbuffer), TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer, sizeof(binbuffer), strbuffer, 0, TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer, 0, strbuffer, 0, TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer, sizeof(binbuffer), NULL, 0, TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(NULL, sizeof(binbuffer), strbuffer, 0, TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + + len = winpr_BinToHexStringBuffer(binbuffer, 0, NULL, 0, TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(NULL, 0, NULL, 0, TRUE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer, 0, NULL, 0, FALSE); + if (len != 0) + goto fail; + if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0) + goto fail; + } + { + const BYTE binbuffer1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + const char strbuffer1[] = "0102030405060708090A0B0C0D0E0F1011"; + const char strbuffer1_space[] = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11"; + + char buffer[1024] = { 0 }; + size_t len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer, + sizeof(buffer), FALSE); + if (len != strnlen(strbuffer1, sizeof(strbuffer1))) + goto fail; + if (memcmp(strbuffer1, buffer, sizeof(strbuffer1)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer, sizeof(buffer), + TRUE); + if (len != strnlen(strbuffer1_space, sizeof(strbuffer1_space))) + goto fail; + if (memcmp(strbuffer1_space, buffer, sizeof(strbuffer1_space)) != 0) + goto fail; + } + { + const BYTE binbuffer1[] = { 0xF1, 0xe2, 0xD3, 0xc4, 0xB5, 0xA6, 0x97, 0x88, 0x79, + 0x6A, 0x5b, 0x4C, 0x3d, 0x2E, 0x1f, 0x00, 0xfF }; + const char strbuffer1[] = "F1E2D3C4B5A69788796A5B4C3D2E1F00FF"; + const char strbuffer1_space[] = "F1 E2 D3 C4 B5 A6 97 88 79 6A 5B 4C 3D 2E 1F 00 FF"; + char buffer[1024] = { 0 }; + size_t len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer, + sizeof(buffer), FALSE); + if (len != strnlen(strbuffer1, sizeof(strbuffer1))) + goto fail; + if (memcmp(strbuffer1, buffer, sizeof(strbuffer1)) != 0) + goto fail; + len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer, sizeof(buffer), + TRUE); + if (len != strnlen(strbuffer1_space, sizeof(strbuffer1_space))) + goto fail; + if (memcmp(strbuffer1_space, buffer, sizeof(strbuffer1_space)) != 0) + goto fail; + } + { + } + rc = TRUE; +fail: + return rc; +} + +static BOOL test_bin_tohex_string_alloc(void) +{ + BOOL rc = FALSE; + char* str = NULL; + { + const BYTE binbuffer[33] = { 0 }; + + str = winpr_BinToHexString(NULL, sizeof(binbuffer), TRUE); + if (str) + goto fail; + str = winpr_BinToHexString(binbuffer, 0, TRUE); + if (str) + goto fail; + str = winpr_BinToHexString(binbuffer, 0, FALSE); + if (str) + goto fail; + str = winpr_BinToHexString(NULL, sizeof(binbuffer), FALSE); + if (str) + goto fail; + } + { + const BYTE binbuffer1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + const char strbuffer1[] = "0102030405060708090A0B0C0D0E0F1011"; + const char strbuffer1_space[] = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11"; + + str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), FALSE); + if (!str) + goto fail; + if (memcmp(strbuffer1, str, sizeof(strbuffer1)) != 0) + goto fail; + free(str); + str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), TRUE); + if (!str) + goto fail; + if (memcmp(strbuffer1_space, str, sizeof(strbuffer1_space)) != 0) + goto fail; + free(str); + str = NULL; + } + { + const BYTE binbuffer1[] = { 0xF1, 0xe2, 0xD3, 0xc4, 0xB5, 0xA6, 0x97, 0x88, 0x79, + 0x6A, 0x5b, 0x4C, 0x3d, 0x2E, 0x1f, 0x00, 0xfF }; + const char strbuffer1[] = "F1E2D3C4B5A69788796A5B4C3D2E1F00FF"; + const char strbuffer1_space[] = "F1 E2 D3 C4 B5 A6 97 88 79 6A 5B 4C 3D 2E 1F 00 FF"; + str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), FALSE); + if (!str) + goto fail; + if (memcmp(strbuffer1, str, sizeof(strbuffer1)) != 0) + goto fail; + + free(str); + str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), TRUE); + if (!str) + goto fail; + if (memcmp(strbuffer1_space, str, sizeof(strbuffer1_space)) != 0) + goto fail; + free(str); + str = NULL; + } + rc = TRUE; +fail: + free(str); + return rc; +} + +static BOOL test_hex_string_to_bin(void) +{ + BOOL rc = FALSE; + { + const char stringbuffer[] = "123456789ABCDEFabcdef"; + const BYTE empty[1024] = { 0 }; + BYTE buffer[1024] = { 0 }; + size_t len = winpr_HexStringToBinBuffer(NULL, 0, NULL, 0); + if (len != 0) + goto fail; + if (memcmp(buffer, empty, sizeof(buffer)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(NULL, sizeof(stringbuffer), buffer, sizeof(buffer)); + if (len != 0) + goto fail; + if (memcmp(buffer, empty, sizeof(buffer)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, 0, buffer, sizeof(buffer)); + if (len != 0) + goto fail; + if (memcmp(buffer, empty, sizeof(buffer)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), NULL, sizeof(buffer)); + if (len != 0) + goto fail; + if (memcmp(buffer, empty, sizeof(buffer)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, 0); + if (len != 0) + goto fail; + if (memcmp(buffer, empty, sizeof(buffer)) != 0) + goto fail; + } + { + const char stringbuffer[] = "123456789ABCDEF1abcdef"; + const BYTE expected[] = { + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1, 0xab, 0xcd, 0xef + }; + BYTE buffer[32] = { 0 }; + size_t len = + winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer)); + if (len != sizeof(expected)) + goto fail; + if (memcmp(buffer, expected, sizeof(expected)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, + sizeof(expected) / 2); + if (len != sizeof(expected) / 2) + goto fail; + if (memcmp(buffer, expected, sizeof(expected) / 2) != 0) + goto fail; + } + { + const char stringbuffer[] = "12 34 56 78 9A BC DE F1 ab cd ef"; + const BYTE expected[] = { + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1, 0xab, 0xcd, 0xef + }; + BYTE buffer[1024] = { 0 }; + size_t len = + winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer)); + if (len != sizeof(expected)) + goto fail; + if (memcmp(buffer, expected, sizeof(expected)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, + sizeof(expected) / 2); + if (len != sizeof(expected) / 2) + goto fail; + if (memcmp(buffer, expected, sizeof(expected) / 2) != 0) + goto fail; + } + { + const char stringbuffer[] = "123456789ABCDEF1abcdef9"; + const BYTE expected[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, + 0xde, 0xf1, 0xab, 0xcd, 0xef, 0x09 }; + BYTE buffer[1024] = { 0 }; + size_t len = + winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer)); + if (len != sizeof(expected)) + goto fail; + if (memcmp(buffer, expected, sizeof(expected)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, + sizeof(expected) / 2); + if (len != sizeof(expected) / 2) + goto fail; + if (memcmp(buffer, expected, sizeof(expected) / 2) != 0) + goto fail; + } + { + const char stringbuffer[] = "12 34 56 78 9A BC DE F1 ab cd ef 9"; + const BYTE expected[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, + 0xde, 0xf1, 0xab, 0xcd, 0xef, 0x09 }; + BYTE buffer[1024] = { 0 }; + size_t len = + winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer)); + if (len != sizeof(expected)) + goto fail; + if (memcmp(buffer, expected, sizeof(expected)) != 0) + goto fail; + len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, + sizeof(expected) / 2); + if (len != sizeof(expected) / 2) + goto fail; + if (memcmp(buffer, expected, sizeof(expected) / 2) != 0) + goto fail; + } + rc = TRUE; +fail: + return rc; +} + +int TestPrint(int argc, char* argv[]) +{ + int a = 0; + int b = 0; + float c = NAN; + float d = NAN; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * 7 + * 7 + * 007 + * 5.10 + */ + + a = 15; + b = a / 2; + _printf("%d\n", b); + _printf("%3d\n", b); + _printf("%03d\n", b); + c = 15.3f; + d = c / 3.0f; + _printf("%3.2f\n", d); + + /** + * 0 -17.778 + * 20 -6.667 + * 40 04.444 + * 60 15.556 + * 80 26.667 + * 100 37.778 + * 120 48.889 + * 140 60.000 + * 160 71.111 + * 180 82.222 + * 200 93.333 + * 220 104.444 + * 240 115.556 + * 260 126.667 + * 280 137.778 + * 300 148.889 + */ + + for (int a = 0; a <= 300; a = a + 20) + _printf("%3d %06.3f\n", a, (5.0 / 9.0) * (a - 32)); + + /** + * The color: blue + * First number: 12345 + * Second number: 0025 + * Third number: 1234 + * Float number: 3.14 + * Hexadecimal: ff + * Octal: 377 + * Unsigned value: 150 + * Just print the percentage sign % + */ + + _printf("The color: %s\n", "blue"); + _printf("First number: %d\n", 12345); + _printf("Second number: %04d\n", 25); + _printf("Third number: %i\n", 1234); + _printf("Float number: %3.2f\n", 3.14159); + _printf("Hexadecimal: %x/%X\n", 255, 255); + _printf("Octal: %o\n", 255); + _printf("Unsigned value: %u\n", 150); + _printf("Just print the percentage sign %%\n"); + + /** + * :Hello, world!: + * : Hello, world!: + * :Hello, wor: + * :Hello, world!: + * :Hello, world! : + * :Hello, world!: + * : Hello, wor: + * :Hello, wor : + */ + + _printf(":%s:\n", "Hello, world!"); + _printf(":%15s:\n", "Hello, world!"); + _printf(":%.10s:\n", "Hello, world!"); + _printf(":%-10s:\n", "Hello, world!"); + _printf(":%-15s:\n", "Hello, world!"); + _printf(":%.15s:\n", "Hello, world!"); + _printf(":%15.10s:\n", "Hello, world!"); + _printf(":%-15.10s:\n", "Hello, world!"); + + if (!test_bin_tohex_string()) + return -1; + if (!test_bin_tohex_string_alloc()) + return -1; + if (!test_hex_string_to_bin()) + return -1; + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestPubSub.c b/winpr/libwinpr/utils/test/TestPubSub.c new file mode 100644 index 0000000..0b05b15 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestPubSub.c @@ -0,0 +1,73 @@ + +#include +#include +#include + +DEFINE_EVENT_BEGIN(MouseMotion) +int x; +int y; +DEFINE_EVENT_END(MouseMotion) + +DEFINE_EVENT_BEGIN(MouseButton) +int x; +int y; +int flags; +int button; +DEFINE_EVENT_END(MouseButton) + +static void MouseMotionEventHandler(void* context, const MouseMotionEventArgs* e) +{ + printf("MouseMotionEvent: x: %d y: %d\n", e->x, e->y); +} + +static void MouseButtonEventHandler(void* context, const MouseButtonEventArgs* e) +{ + printf("MouseButtonEvent: x: %d y: %d flags: %d button: %d\n", e->x, e->y, e->flags, e->button); +} + +static wEventType Node_Events[] = { DEFINE_EVENT_ENTRY(MouseMotion), + DEFINE_EVENT_ENTRY(MouseButton) }; + +#define NODE_EVENT_COUNT (sizeof(Node_Events) / sizeof(wEventType)) + +int TestPubSub(int argc, char* argv[]) +{ + wPubSub* node = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + node = PubSub_New(TRUE); + if (!node) + return -1; + + PubSub_AddEventTypes(node, Node_Events, NODE_EVENT_COUNT); + + PubSub_SubscribeMouseMotion(node, MouseMotionEventHandler); + PubSub_SubscribeMouseButton(node, MouseButtonEventHandler); + + /* Call Event Handler */ + { + MouseMotionEventArgs e; + + e.x = 64; + e.y = 128; + + PubSub_OnMouseMotion(node, NULL, &e); + } + + { + MouseButtonEventArgs e; + + e.x = 23; + e.y = 56; + e.flags = 7; + e.button = 1; + + PubSub_OnMouseButton(node, NULL, &e); + } + + PubSub_Free(node); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestQueue.c b/winpr/libwinpr/utils/test/TestQueue.c new file mode 100644 index 0000000..9c65af5 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestQueue.c @@ -0,0 +1,58 @@ + +#include +#include +#include + +int TestQueue(int argc, char* argv[]) +{ + size_t item = 0; + size_t count = 0; + wQueue* queue = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + queue = Queue_New(TRUE, -1, -1); + if (!queue) + return -1; + + for (size_t index = 1; index <= 10; index++) + { + Queue_Enqueue(queue, (void*)(size_t)index); + } + + count = Queue_Count(queue); + printf("queue count: %" PRIuz "\n", count); + + for (size_t index = 1; index <= 10; index++) + { + item = (size_t)Queue_Dequeue(queue); + + if (item != index) + return -1; + } + + count = Queue_Count(queue); + printf("queue count: %" PRIuz "\n", count); + + Queue_Enqueue(queue, (void*)(size_t)1); + Queue_Enqueue(queue, (void*)(size_t)2); + Queue_Enqueue(queue, (void*)(size_t)3); + + Queue_Dequeue(queue); + Queue_Dequeue(queue); + + Queue_Enqueue(queue, (void*)(size_t)4); + Queue_Enqueue(queue, (void*)(size_t)5); + Queue_Enqueue(queue, (void*)(size_t)6); + + Queue_Dequeue(queue); + Queue_Dequeue(queue); + Queue_Dequeue(queue); + Queue_Dequeue(queue); + + Queue_Clear(queue); + Queue_Free(queue); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestStream.c b/winpr/libwinpr/utils/test/TestStream.c new file mode 100644 index 0000000..00130ae --- /dev/null +++ b/winpr/libwinpr/utils/test/TestStream.c @@ -0,0 +1,682 @@ +#include +#include +#include + +static BOOL TestStream_Verify(wStream* s, size_t mincap, size_t len, size_t pos) +{ + if (Stream_Buffer(s) == NULL) + { + printf("stream buffer is null\n"); + return FALSE; + } + + if (Stream_ConstPointer(s) == NULL) + { + printf("stream pointer is null\n"); + return FALSE; + } + + if (Stream_PointerAs(s, BYTE) < Stream_Buffer(s)) + { + printf("stream pointer (%p) or buffer (%p) is invalid\n", Stream_ConstPointer(s), + (void*)Stream_Buffer(s)); + return FALSE; + } + + if (Stream_Capacity(s) < mincap) + { + printf("stream capacity is %" PRIuz " but minimum expected value is %" PRIuz "\n", + Stream_Capacity(s), mincap); + return FALSE; + } + + if (Stream_Length(s) != len) + { + printf("stream has unexpected length (%" PRIuz " instead of %" PRIuz ")\n", + Stream_Length(s), len); + return FALSE; + } + + if (Stream_GetPosition(s) != pos) + { + printf("stream has unexpected position (%" PRIuz " instead of %" PRIuz ")\n", + Stream_GetPosition(s), pos); + return FALSE; + } + + if (Stream_GetPosition(s) > Stream_Length(s)) + { + printf("stream position (%" PRIuz ") exceeds length (%" PRIuz ")\n", Stream_GetPosition(s), + Stream_Length(s)); + return FALSE; + } + + if (Stream_GetPosition(s) > Stream_Capacity(s)) + { + printf("stream position (%" PRIuz ") exceeds capacity (%" PRIuz ")\n", + Stream_GetPosition(s), Stream_Capacity(s)); + return FALSE; + } + + if (Stream_Length(s) > Stream_Capacity(s)) + { + printf("stream length (%" PRIuz ") exceeds capacity (%" PRIuz ")\n", Stream_Length(s), + Stream_Capacity(s)); + return FALSE; + } + + if (Stream_GetRemainingLength(s) != len - pos) + { + printf("stream remaining length (%" PRIuz " instead of %" PRIuz ")\n", + Stream_GetRemainingLength(s), len - pos); + return FALSE; + } + + return TRUE; +} + +static BOOL TestStream_New(void) +{ + wStream* s = NULL; + /* Test creation of a 0-size stream with no buffer */ + s = Stream_New(NULL, 0); + + if (s) + return FALSE; + + return TRUE; +} + +static BOOL TestStream_Static(void) +{ + BYTE buffer[20]; + wStream staticStream; + wStream* s = &staticStream; + UINT16 v = 0; + /* Test creation of a static stream */ + Stream_StaticInit(s, buffer, sizeof(buffer)); + Stream_Write_UINT16(s, 0xcab1); + Stream_SetPosition(s, 0); + Stream_Read_UINT16(s, v); + + if (v != 0xcab1) + return FALSE; + + Stream_SetPosition(s, 0); + Stream_Write_UINT16(s, 1); + + if (!Stream_EnsureRemainingCapacity(s, 10)) /* we can ask for 10 bytes */ + return FALSE; + + /* 30 is bigger than the buffer, it will be reallocated on the heap */ + if (!Stream_EnsureRemainingCapacity(s, 30) || !s->isOwner) + return FALSE; + + Stream_Write_UINT16(s, 2); + Stream_SetPosition(s, 0); + Stream_Read_UINT16(s, v); + + if (v != 1) + return FALSE; + + Stream_Read_UINT16(s, v); + + if (v != 2) + return FALSE; + + Stream_Free(s, TRUE); + return TRUE; +} + +static BOOL TestStream_Create(size_t count, BOOL selfAlloc) +{ + size_t len = 0; + size_t cap = 0; + wStream* s = NULL; + void* buffer = NULL; + + for (size_t i = 0; i < count; i++) + { + len = cap = i + 1; + + if (selfAlloc) + { + if (!(buffer = malloc(cap))) + { + printf("%s: failed to allocate buffer of size %" PRIuz "\n", __func__, cap); + goto fail; + } + } + + if (!(s = Stream_New(selfAlloc ? buffer : NULL, len))) + { + printf("%s: Stream_New failed for stream #%" PRIuz "\n", __func__, i); + goto fail; + } + + if (!TestStream_Verify(s, cap, len, 0)) + { + goto fail; + } + + for (size_t pos = 0; pos < len; pos++) + { + Stream_SetPosition(s, pos); + Stream_SealLength(s); + + if (!TestStream_Verify(s, cap, pos, pos)) + { + goto fail; + } + } + + if (selfAlloc) + { + memset(buffer, i % 256, cap); + + if (memcmp(buffer, Stream_Buffer(s), cap)) + { + printf("%s: buffer memory corruption\n", __func__); + goto fail; + } + } + + Stream_Free(s, buffer ? FALSE : TRUE); + free(buffer); + } + + return TRUE; +fail: + free(buffer); + + if (s) + { + Stream_Free(s, buffer ? FALSE : TRUE); + } + + return FALSE; +} + +static BOOL TestStream_Extent(UINT32 maxSize) +{ + wStream* s = NULL; + BOOL result = FALSE; + + if (!(s = Stream_New(NULL, 1))) + { + printf("%s: Stream_New failed\n", __func__); + return FALSE; + } + + for (UINT32 i = 1; i < maxSize; i++) + { + if (i % 2) + { + if (!Stream_EnsureRemainingCapacity(s, i)) + goto fail; + } + else + { + if (!Stream_EnsureCapacity(s, i)) + goto fail; + } + + Stream_SetPosition(s, i); + Stream_SealLength(s); + + if (!TestStream_Verify(s, i, i, i)) + { + printf("%s: failed to verify stream in iteration %" PRIu32 "\n", __func__, i); + goto fail; + } + } + + result = TRUE; +fail: + + if (s) + { + Stream_Free(s, TRUE); + } + + return result; +} + +#define Stream_Peek_UINT8_BE Stream_Peek_UINT8 +#define Stream_Read_UINT8_BE Stream_Read_UINT8 +#define Stream_Peek_INT8_BE Stream_Peek_INT8 +#define Stream_Read_INT8_BE Stream_Read_INT8 + +#define TestStream_PeekAndRead(_s, _r, _t) \ + do \ + { \ + _t _a; \ + _t _b; \ + BYTE* _p = Stream_Buffer(_s); \ + Stream_SetPosition(_s, 0); \ + Stream_Peek_##_t(_s, _a); \ + Stream_Read_##_t(_s, _b); \ + if (_a != _b) \ + { \ + printf("%s: test1 " #_t "_LE failed\n", __func__); \ + _r = FALSE; \ + } \ + for (size_t _i = 0; _i < sizeof(_t); _i++) \ + { \ + if (((_a >> (_i * 8)) & 0xFF) != _p[_i]) \ + { \ + printf("%s: test2 " #_t "_LE failed\n", __func__); \ + _r = FALSE; \ + break; \ + } \ + } \ + /* printf("a: 0x%016llX\n", a); */ \ + Stream_SetPosition(_s, 0); \ + Stream_Peek_##_t##_BE(_s, _a); \ + Stream_Read_##_t##_BE(_s, _b); \ + if (_a != _b) \ + { \ + printf("%s: test1 " #_t "_BE failed\n", __func__); \ + _r = FALSE; \ + } \ + for (size_t _i = 0; _i < sizeof(_t); _i++) \ + { \ + if (((_a >> (_i * 8)) & 0xFF) != _p[sizeof(_t) - _i - 1]) \ + { \ + printf("%s: test2 " #_t "_BE failed\n", __func__); \ + _r = FALSE; \ + break; \ + } \ + } \ + /* printf("a: 0x%016llX\n", a); */ \ + } while (0) + +static BOOL TestStream_Reading(void) +{ + BYTE src[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + wStream* s = NULL; + BOOL result = TRUE; + + if (!(s = Stream_New(src, sizeof(src)))) + { + printf("%s: Stream_New failed\n", __func__); + return FALSE; + } + + TestStream_PeekAndRead(s, result, UINT8); + TestStream_PeekAndRead(s, result, INT8); + TestStream_PeekAndRead(s, result, UINT16); + TestStream_PeekAndRead(s, result, INT16); + TestStream_PeekAndRead(s, result, UINT32); + TestStream_PeekAndRead(s, result, INT32); + TestStream_PeekAndRead(s, result, UINT64); + TestStream_PeekAndRead(s, result, INT64); + Stream_Free(s, FALSE); + return result; +} + +static BOOL TestStream_Write(void) +{ + BOOL rc = FALSE; + UINT8 u8 = 0; + UINT16 u16 = 0; + UINT32 u32 = 0; + UINT64 u64 = 0; + const BYTE data[] = "someteststreamdata"; + wStream* s = Stream_New(NULL, 100); + + if (!s) + goto out; + + if (s->pointer != s->buffer) + goto out; + + Stream_Write(s, data, sizeof(data)); + + if (memcmp(Stream_Buffer(s), data, sizeof(data)) == 0) + rc = TRUE; + + if (s->pointer != s->buffer + sizeof(data)) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Write_UINT8(s, 42); + + if (s->pointer != s->buffer + 1) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Peek_UINT8(s, u8); + + if (u8 != 42) + goto out; + + Stream_Write_UINT16(s, 0x1234); + + if (s->pointer != s->buffer + 2) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Peek_UINT16(s, u16); + + if (u16 != 0x1234) + goto out; + + Stream_Write_UINT32(s, 0x12345678UL); + + if (s->pointer != s->buffer + 4) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Peek_UINT32(s, u32); + + if (u32 != 0x12345678UL) + goto out; + + Stream_Write_UINT64(s, 0x1234567890ABCDEFULL); + + if (s->pointer != s->buffer + 8) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Peek_UINT64(s, u64); + + if (u64 != 0x1234567890ABCDEFULL) + goto out; + +out: + Stream_Free(s, TRUE); + return rc; +} + +static BOOL TestStream_Seek(void) +{ + BOOL rc = FALSE; + wStream* s = Stream_New(NULL, 100); + + if (!s) + goto out; + + if (s->pointer != s->buffer) + goto out; + + Stream_Seek(s, 5); + + if (s->pointer != s->buffer + 5) + goto out; + + Stream_Seek_UINT8(s); + + if (s->pointer != s->buffer + 6) + goto out; + + Stream_Seek_UINT16(s); + + if (s->pointer != s->buffer + 8) + goto out; + + Stream_Seek_UINT32(s); + + if (s->pointer != s->buffer + 12) + goto out; + + Stream_Seek_UINT64(s); + + if (s->pointer != s->buffer + 20) + goto out; + + rc = TRUE; +out: + Stream_Free(s, TRUE); + return rc; +} + +static BOOL TestStream_Rewind(void) +{ + BOOL rc = FALSE; + wStream* s = Stream_New(NULL, 100); + + if (!s) + goto out; + + if (s->pointer != s->buffer) + goto out; + + Stream_Seek(s, 100); + + if (s->pointer != s->buffer + 100) + goto out; + + Stream_Rewind(s, 10); + + if (s->pointer != s->buffer + 90) + goto out; + + Stream_Rewind_UINT8(s); + + if (s->pointer != s->buffer + 89) + goto out; + + Stream_Rewind_UINT16(s); + + if (s->pointer != s->buffer + 87) + goto out; + + Stream_Rewind_UINT32(s); + + if (s->pointer != s->buffer + 83) + goto out; + + Stream_Rewind_UINT64(s); + + if (s->pointer != s->buffer + 75) + goto out; + + rc = TRUE; +out: + Stream_Free(s, TRUE); + return rc; +} + +static BOOL TestStream_Zero(void) +{ + BOOL rc = FALSE; + const BYTE data[] = "someteststreamdata"; + wStream* s = Stream_New(NULL, sizeof(data)); + + if (!s) + goto out; + + Stream_Write(s, data, sizeof(data)); + + if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Zero(s, 5); + + if (s->pointer != s->buffer + 5) + goto out; + + if (memcmp(Stream_ConstPointer(s), data + 5, sizeof(data) - 5) != 0) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + for (UINT32 x = 0; x < 5; x++) + { + UINT8 val = 0; + Stream_Read_UINT8(s, val); + + if (val != 0) + goto out; + } + + rc = TRUE; +out: + Stream_Free(s, TRUE); + return rc; +} + +static BOOL TestStream_Fill(void) +{ + BOOL rc = FALSE; + const BYTE fill[7] = "XXXXXXX"; + const BYTE data[] = "someteststreamdata"; + wStream* s = Stream_New(NULL, sizeof(data)); + + if (!s) + goto out; + + Stream_Write(s, data, sizeof(data)); + + if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Fill(s, fill[0], sizeof(fill)); + + if (s->pointer != s->buffer + sizeof(fill)) + goto out; + + if (memcmp(Stream_ConstPointer(s), data + sizeof(fill), sizeof(data) - sizeof(fill)) != 0) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + if (memcmp(Stream_ConstPointer(s), fill, sizeof(fill)) != 0) + goto out; + + rc = TRUE; +out: + Stream_Free(s, TRUE); + return rc; +} + +static BOOL TestStream_Copy(void) +{ + BOOL rc = FALSE; + const BYTE data[] = "someteststreamdata"; + wStream* s = Stream_New(NULL, sizeof(data)); + wStream* d = Stream_New(NULL, sizeof(data)); + + if (!s || !d) + goto out; + + if (s->pointer != s->buffer) + goto out; + + Stream_Write(s, data, sizeof(data)); + + if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0) + goto out; + + if (s->pointer != s->buffer + sizeof(data)) + goto out; + + Stream_SetPosition(s, 0); + + if (s->pointer != s->buffer) + goto out; + + Stream_Copy(s, d, sizeof(data)); + + if (s->pointer != s->buffer + sizeof(data)) + goto out; + + if (d->pointer != d->buffer + sizeof(data)) + goto out; + + if (Stream_GetPosition(s) != Stream_GetPosition(d)) + goto out; + + if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0) + goto out; + + if (memcmp(Stream_Buffer(d), data, sizeof(data)) != 0) + goto out; + + rc = TRUE; +out: + Stream_Free(s, TRUE); + Stream_Free(d, TRUE); + return rc; +} + +int TestStream(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!TestStream_Create(200, FALSE)) + return 1; + + if (!TestStream_Create(200, TRUE)) + return 2; + + if (!TestStream_Extent(4096)) + return 3; + + if (!TestStream_Reading()) + return 4; + + if (!TestStream_New()) + return 5; + + if (!TestStream_Write()) + return 6; + + if (!TestStream_Seek()) + return 7; + + if (!TestStream_Rewind()) + return 8; + + if (!TestStream_Zero()) + return 9; + + if (!TestStream_Fill()) + return 10; + + if (!TestStream_Copy()) + return 11; + + if (!TestStream_Static()) + return 12; + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestStreamPool.c b/winpr/libwinpr/utils/test/TestStreamPool.c new file mode 100644 index 0000000..1844a53 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestStreamPool.c @@ -0,0 +1,78 @@ + +#include +#include +#include + +#define BUFFER_SIZE 16384 + +int TestStreamPool(int argc, char* argv[]) +{ + wStream* s[5] = { 0 }; + char buffer[8192] = { 0 }; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + wStreamPool* pool = StreamPool_New(TRUE, BUFFER_SIZE); + + s[0] = StreamPool_Take(pool, 0); + s[1] = StreamPool_Take(pool, 0); + s[2] = StreamPool_Take(pool, 0); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + Stream_Release(s[0]); + Stream_Release(s[1]); + Stream_Release(s[2]); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + s[3] = StreamPool_Take(pool, 0); + s[4] = StreamPool_Take(pool, 0); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + Stream_Release(s[3]); + Stream_Release(s[4]); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + s[2] = StreamPool_Take(pool, 0); + s[3] = StreamPool_Take(pool, 0); + s[4] = StreamPool_Take(pool, 0); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + Stream_AddRef(s[2]); + + Stream_AddRef(s[3]); + Stream_AddRef(s[3]); + + Stream_AddRef(s[4]); + Stream_AddRef(s[4]); + Stream_AddRef(s[4]); + + Stream_Release(s[2]); + Stream_Release(s[2]); + + Stream_Release(s[3]); + Stream_Release(s[3]); + Stream_Release(s[3]); + + Stream_Release(s[4]); + Stream_Release(s[4]); + Stream_Release(s[4]); + Stream_Release(s[4]); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + s[2] = StreamPool_Take(pool, 0); + s[3] = StreamPool_Take(pool, 0); + s[4] = StreamPool_Take(pool, 0); + + printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer))); + + StreamPool_Free(pool); + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestVersion.c b/winpr/libwinpr/utils/test/TestVersion.c new file mode 100644 index 0000000..71a1d74 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestVersion.c @@ -0,0 +1,47 @@ + +#include + +#include +#include + +int TestVersion(int argc, char* argv[]) +{ + const char* version = NULL; + const char* git = NULL; + const char* build = NULL; + int major = 0; + int minor = 0; + int revision = 0; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + winpr_get_version(&major, &minor, &revision); + + if (major != WINPR_VERSION_MAJOR) + return -1; + + if (minor != WINPR_VERSION_MINOR) + return -1; + + if (revision != WINPR_VERSION_REVISION) + return -1; + + version = winpr_get_version_string(); + + if (!version) + return -1; + + git = winpr_get_build_revision(); + + if (!git) + return -1; + + if (strncmp(git, WINPR_GIT_REVISION, sizeof(WINPR_GIT_REVISION))) + return -1; + + build = winpr_get_build_config(); + + if (!build) + return -1; + + return 0; +} diff --git a/winpr/libwinpr/utils/test/TestWLog.c b/winpr/libwinpr/utils/test/TestWLog.c new file mode 100644 index 0000000..92f1114 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestWLog.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +int TestWLog(int argc, char* argv[]) +{ + wLog* root = NULL; + wLog* logA = NULL; + wLog* logB = NULL; + wLogLayout* layout = NULL; + wLogAppender* appender = NULL; + char* tmp_path = NULL; + char* wlog_file = NULL; + int result = 1; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!(tmp_path = GetKnownPath(KNOWN_PATH_TEMP))) + { + fprintf(stderr, "Failed to get temporary directory!\n"); + goto out; + } + + root = WLog_GetRoot(); + + WLog_SetLogAppenderType(root, WLOG_APPENDER_BINARY); + + appender = WLog_GetLogAppender(root); + if (!WLog_ConfigureAppender(appender, "outputfilename", "test_w.log")) + goto out; + if (!WLog_ConfigureAppender(appender, "outputfilepath", tmp_path)) + goto out; + + layout = WLog_GetLogLayout(root); + WLog_Layout_SetPrefixFormat(root, layout, "[%lv:%mn] [%fl|%fn|%ln] - "); + + WLog_OpenAppender(root); + + logA = WLog_Get("com.test.ChannelA"); + logB = WLog_Get("com.test.ChannelB"); + + WLog_SetLogLevel(logA, WLOG_INFO); + WLog_SetLogLevel(logB, WLOG_ERROR); + + WLog_Print(logA, WLOG_INFO, "this is a test"); + WLog_Print(logA, WLOG_WARN, "this is a %dnd %s", 2, "test"); + WLog_Print(logA, WLOG_ERROR, "this is an error"); + WLog_Print(logA, WLOG_TRACE, "this is a trace output"); + + WLog_Print(logB, WLOG_INFO, "just some info"); + WLog_Print(logB, WLOG_WARN, "we're warning a %dnd %s", 2, "time"); + WLog_Print(logB, WLOG_ERROR, "we've got an error"); + WLog_Print(logB, WLOG_TRACE, "leaving a trace behind"); + + WLog_CloseAppender(root); + + if ((wlog_file = GetCombinedPath(tmp_path, "test_w.log"))) + winpr_DeleteFile(wlog_file); + + result = 0; +out: + free(wlog_file); + free(tmp_path); + + return result; +} diff --git a/winpr/libwinpr/utils/test/TestWLogCallback.c b/winpr/libwinpr/utils/test/TestWLogCallback.c new file mode 100644 index 0000000..1acbf65 --- /dev/null +++ b/winpr/libwinpr/utils/test/TestWLogCallback.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include + +typedef struct +{ + UINT32 level; + char* msg; + char* channel; +} test_t; + +static const char* function = NULL; +static const char* channels[] = { "com.test.channelA", "com.test.channelB" }; + +static const test_t messages[] = { { WLOG_INFO, "this is a test", "com.test.channelA" }, + { WLOG_INFO, "Just some info", "com.test.channelB" }, + { WLOG_WARN, "this is a %dnd %s", "com.test.channelA" }, + { WLOG_WARN, "we're warning a %dnd %s", "com.test.channelB" }, + { WLOG_ERROR, "this is an error", "com.test.channelA" }, + { WLOG_ERROR, "we've got an error", "com.test.channelB" }, + { WLOG_TRACE, "this is a trace output", "com.test.channelA" }, + { WLOG_TRACE, "leaving a trace behind", "com.test.channelB" } }; + +static BOOL success = TRUE; +static int pos = 0; + +static BOOL check(const wLogMessage* msg) +{ + BOOL rc = TRUE; + if (!msg) + rc = FALSE; + else if (strcmp(msg->FileName, __FILE__)) + rc = FALSE; + else if (strcmp(msg->FunctionName, function)) + rc = FALSE; + else if (strcmp(msg->PrefixString, messages[pos].channel)) + rc = FALSE; + else if (msg->Level != messages[pos].level) + rc = FALSE; + else if (strcmp(msg->FormatString, messages[pos].msg)) + rc = FALSE; + pos++; + + if (!rc) + { + fprintf(stderr, "Test failed!\n"); + success = FALSE; + } + return rc; +} + +static BOOL CallbackAppenderMessage(const wLogMessage* msg) +{ + check(msg); + return TRUE; +} + +static BOOL CallbackAppenderData(const wLogMessage* msg) +{ + fprintf(stdout, "%s\n", __func__); + return TRUE; +} + +static BOOL CallbackAppenderImage(const wLogMessage* msg) +{ + fprintf(stdout, "%s\n", __func__); + return TRUE; +} + +static BOOL CallbackAppenderPackage(const wLogMessage* msg) +{ + fprintf(stdout, "%s\n", __func__); + return TRUE; +} + +int TestWLogCallback(int argc, char* argv[]) +{ + wLog* root = NULL; + wLog* logA = NULL; + wLog* logB = NULL; + wLogLayout* layout = NULL; + wLogAppender* appender = NULL; + wLogCallbacks callbacks; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + function = __func__; + + root = WLog_GetRoot(); + + WLog_SetLogAppenderType(root, WLOG_APPENDER_CALLBACK); + + appender = WLog_GetLogAppender(root); + + callbacks.data = CallbackAppenderData; + callbacks.image = CallbackAppenderImage; + callbacks.message = CallbackAppenderMessage; + callbacks.package = CallbackAppenderPackage; + + if (!WLog_ConfigureAppender(appender, "callbacks", (void*)&callbacks)) + return -1; + + layout = WLog_GetLogLayout(root); + WLog_Layout_SetPrefixFormat(root, layout, "%mn"); + + WLog_OpenAppender(root); + + logA = WLog_Get(channels[0]); + logB = WLog_Get(channels[1]); + + WLog_SetLogLevel(logA, WLOG_TRACE); + WLog_SetLogLevel(logB, WLOG_TRACE); + + WLog_Print(logA, messages[0].level, messages[0].msg); + WLog_Print(logB, messages[1].level, messages[1].msg); + WLog_Print(logA, messages[2].level, messages[2].msg, 2, "test"); + WLog_Print(logB, messages[3].level, messages[3].msg, 2, "time"); + WLog_Print(logA, messages[4].level, messages[4].msg); + WLog_Print(logB, messages[5].level, messages[5].msg); + WLog_Print(logA, messages[6].level, messages[6].msg); + WLog_Print(logB, messages[7].level, messages[7].msg); + + WLog_CloseAppender(root); + + return success ? 0 : -1; +} diff --git a/winpr/libwinpr/utils/test/lodepng_32bit.bmp b/winpr/libwinpr/utils/test/lodepng_32bit.bmp new file mode 100644 index 0000000..d52d34c Binary files /dev/null and b/winpr/libwinpr/utils/test/lodepng_32bit.bmp differ diff --git a/winpr/libwinpr/utils/test/lodepng_32bit.png b/winpr/libwinpr/utils/test/lodepng_32bit.png new file mode 100644 index 0000000..9c55f28 Binary files /dev/null and b/winpr/libwinpr/utils/test/lodepng_32bit.png differ diff --git a/winpr/libwinpr/utils/test/rgb.16.bmp b/winpr/libwinpr/utils/test/rgb.16.bmp new file mode 100644 index 0000000..3e6e1ad Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.16.nocolor.bmp new file mode 100644 index 0000000..70875dd Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16a.bmp b/winpr/libwinpr/utils/test/rgb.16a.bmp new file mode 100644 index 0000000..4a7b506 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16a.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp new file mode 100644 index 0000000..ae739dd Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16x.bmp b/winpr/libwinpr/utils/test/rgb.16x.bmp new file mode 100644 index 0000000..5fb5cd8 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16x.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp new file mode 100644 index 0000000..7e04ec3 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.24.bmp b/winpr/libwinpr/utils/test/rgb.24.bmp new file mode 100644 index 0000000..7719f9d Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.24.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.24.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.24.nocolor.bmp new file mode 100644 index 0000000..ffc30ed Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.24.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32.bmp b/winpr/libwinpr/utils/test/rgb.32.bmp new file mode 100644 index 0000000..5d9c1df Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.32.nocolor.bmp new file mode 100644 index 0000000..5217757 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32x.bmp b/winpr/libwinpr/utils/test/rgb.32x.bmp new file mode 100644 index 0000000..41ae40c Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32x.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp new file mode 100644 index 0000000..10abb96 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.bmp b/winpr/libwinpr/utils/test/rgb.bmp new file mode 100644 index 0000000..3e7e5bb Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.jpg b/winpr/libwinpr/utils/test/rgb.jpg new file mode 100644 index 0000000..89c5a31 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.jpg differ diff --git a/winpr/libwinpr/utils/test/rgb.png b/winpr/libwinpr/utils/test/rgb.png new file mode 100644 index 0000000..70877d4 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.png differ diff --git a/winpr/libwinpr/utils/test/rgb.webp b/winpr/libwinpr/utils/test/rgb.webp new file mode 100644 index 0000000..26e0c05 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.webp differ diff --git a/winpr/libwinpr/utils/unwind/debug.c b/winpr/libwinpr/utils/unwind/debug.c new file mode 100644 index 0000000..1bc8b94 --- /dev/null +++ b/winpr/libwinpr/utils/unwind/debug.c @@ -0,0 +1,132 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include +#include "debug.h" + +#include + +typedef struct +{ + uintptr_t pc; + void* langSpecificData; +} unwind_info_t; + +typedef struct +{ + size_t pos; + size_t size; + unwind_info_t* info; +} unwind_context_t; + +static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) +{ + unwind_context_t* ctx = arg; + + assert(ctx); + + if (ctx->pos < ctx->size) + { + unwind_info_t* info = &ctx->info[ctx->pos++]; + info->pc = _Unwind_GetIP(context); + info->langSpecificData = (void*)_Unwind_GetLanguageSpecificData(context); + } + + return _URC_NO_REASON; +} + +void* winpr_unwind_backtrace(DWORD size) +{ + _Unwind_Reason_Code rc = _URC_FOREIGN_EXCEPTION_CAUGHT; + unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t)); + if (!ctx) + goto fail; + ctx->size = size; + ctx->info = calloc(size, sizeof(unwind_info_t)); + if (!ctx->info) + goto fail; + + rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx); + if (rc != _URC_END_OF_STACK) + goto fail; + + return ctx; +fail: + winpr_unwind_backtrace_free(ctx); + return NULL; +} + +void winpr_unwind_backtrace_free(void* buffer) +{ + unwind_context_t* ctx = buffer; + if (!ctx) + return; + free(ctx->info); + free(ctx); +} + +#define UNWIND_MAX_LINE_SIZE 1024 + +char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used) +{ + union + { + char* cp; + char** cpp; + } cnv; + unwind_context_t* ctx = buffer; + cnv.cpp = NULL; + + if (!ctx) + return NULL; + + cnv.cpp = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*)); + if (!cnv.cpp) + return NULL; + + if (used) + *used = ctx->pos; + + for (size_t x = 0; x < ctx->pos; x++) + { + char* msg = cnv.cp + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE; + const unwind_info_t* info = &ctx->info[x]; + Dl_info dlinfo = { 0 }; + int rc = dladdr((void*)info->pc, &dlinfo); + + cnv.cpp[x] = msg; + + if (rc == 0) + _snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", (void*)info->pc); + else + _snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]", + dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr); + } + + return cnv.cpp; +} diff --git a/winpr/libwinpr/utils/unwind/debug.h b/winpr/libwinpr/utils/unwind/debug.h new file mode 100644 index 0000000..5cd1290 --- /dev/null +++ b/winpr/libwinpr/utils/unwind/debug.h @@ -0,0 +1,41 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_UNWIND_H +#define WINPR_DEBUG_UNWIND_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + + void* winpr_unwind_backtrace(DWORD size); + void winpr_unwind_backtrace_free(void* buffer); + char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_UNWIND_H */ diff --git a/winpr/libwinpr/utils/windows/debug.c b/winpr/libwinpr/utils/windows/debug.c new file mode 100644 index 0000000..84e04c2 --- /dev/null +++ b/winpr/libwinpr/utils/windows/debug.c @@ -0,0 +1,168 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include "debug.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +typedef struct +{ + PVOID* stack; + ULONG used; + ULONG max; +} t_win_stack; + +void winpr_win_backtrace_free(void* buffer) +{ + t_win_stack* data = (t_win_stack*)buffer; + if (!data) + return; + + free(data->stack); + free(data); +} + +void* winpr_win_backtrace(DWORD size) +{ + HANDLE process = GetCurrentProcess(); + t_win_stack* data = calloc(1, sizeof(t_win_stack)); + + if (!data) + return NULL; + + data->max = size; + data->stack = calloc(data->max, sizeof(PVOID)); + + if (!data->stack) + { + free(data); + return NULL; + } + + SymInitialize(process, NULL, TRUE); + data->used = RtlCaptureStackBackTrace(2, size, data->stack, NULL); + return data; +} + +char** winpr_win_backtrace_symbols(void* buffer, size_t* used) +{ + if (used) + *used = 0; + + if (!buffer) + return NULL; + + { + size_t line_len = 1024; + HANDLE process = GetCurrentProcess(); + t_win_stack* data = (t_win_stack*)buffer; + size_t array_size = data->used * sizeof(char*); + size_t lines_size = data->used * line_len; + char** vlines = calloc(1, array_size + lines_size); + SYMBOL_INFO* symbol = calloc(1, sizeof(SYMBOL_INFO) + line_len * sizeof(char)); + IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(1, sizeof(IMAGEHLP_LINE64)); + + if (!vlines || !symbol || !line) + { + free(vlines); + free(symbol); + free(line); + return NULL; + } + + line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + symbol->MaxNameLen = (ULONG)line_len; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + /* Set the pointers in the allocated buffer's initial array section */ + for (size_t i = 0; i < data->used; i++) + vlines[i] = (char*)vlines + array_size + i * line_len; + + for (size_t i = 0; i < data->used; i++) + { + DWORD64 address = (DWORD64)(data->stack[i]); + DWORD displacement; + SymFromAddr(process, address, 0, symbol); + + if (SymGetLineFromAddr64(process, address, &displacement, line)) + { + sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s in %s:%" PRIu32, symbol->Address, + symbol->Name, line->FileName, line->LineNumber); + } + else + sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s", symbol->Address, symbol->Name); + } + + if (used) + *used = data->used; + + free(symbol); + free(line); + return vlines; + } +} + +char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size) +{ + DWORD rc; + DWORD nSize = 0; + DWORD dwFlags = 0; + LPTSTR msg = NULL; + BOOL alloc = FALSE; + dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; +#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER + alloc = TRUE; + dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; +#else + nSize = (DWORD)(size * sizeof(WCHAR)); + msg = (LPTSTR)calloc(nSize, sizeof(WCHAR)); +#endif + rc = FormatMessage(dwFlags, NULL, dw, 0, alloc ? (LPTSTR)&msg : msg, nSize, NULL); + + if (rc) + { +#if defined(UNICODE) + WideCharToMultiByte(CP_ACP, 0, msg, rc, dmsg, (int)MIN(size - 1, INT_MAX), NULL, NULL); +#else /* defined(UNICODE) */ + memcpy(dmsg, msg, MIN(rc, size - 1)); +#endif /* defined(UNICODE) */ + dmsg[MIN(rc, size - 1)] = 0; +#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER + LocalFree(msg); +#else + free(msg); +#endif + } + else + { + _snprintf(dmsg, size, "FAILURE: 0x%08" PRIX32 "", GetLastError()); + } + + return dmsg; +} diff --git a/winpr/libwinpr/utils/windows/debug.h b/winpr/libwinpr/utils/windows/debug.h new file mode 100644 index 0000000..c36b87a --- /dev/null +++ b/winpr/libwinpr/utils/windows/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_WIN_H +#define WINPR_DEBUG_WIN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_win_backtrace(DWORD size); + void winpr_win_backtrace_free(void* buffer); + char** winpr_win_backtrace_symbols(void* buffer, size_t* used); + char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_WIN_H */ diff --git a/winpr/libwinpr/utils/winpr.c b/winpr/libwinpr/utils/winpr.c new file mode 100644 index 0000000..1271464 --- /dev/null +++ b/winpr/libwinpr/utils/winpr.c @@ -0,0 +1,67 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2015 Armin Novak + * Copyright 2015 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(WIN32) +#include +#endif + +void winpr_get_version(int* major, int* minor, int* revision) +{ + if (major) + *major = WINPR_VERSION_MAJOR; + if (minor) + *minor = WINPR_VERSION_MINOR; + if (revision) + *revision = WINPR_VERSION_REVISION; +} + +const char* winpr_get_version_string(void) +{ + return WINPR_VERSION_FULL; +} + +const char* winpr_get_build_revision(void) +{ + return WINPR_GIT_REVISION; +} + +const char* winpr_get_build_config(void) +{ + static const char build_config[] = + "Build configuration: " WINPR_BUILD_CONFIG "\n" + "Build type: " WINPR_BUILD_TYPE "\n" + "CFLAGS: " WINPR_CFLAGS "\n" + "Compiler: " WINPR_COMPILER_ID ", " WINPR_COMPILER_VERSION "\n" + "Target architecture: " WINPR_TARGET_ARCH "\n"; + + return build_config; +} diff --git a/winpr/libwinpr/utils/wlog/Appender.c b/winpr/libwinpr/utils/wlog/Appender.c new file mode 100644 index 0000000..a1cbbd6 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Appender.c @@ -0,0 +1,176 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "Appender.h" + +void WLog_Appender_Free(wLog* log, wLogAppender* appender) +{ + if (!appender) + return; + + if (appender->Layout) + { + WLog_Layout_Free(log, appender->Layout); + appender->Layout = NULL; + } + + DeleteCriticalSection(&appender->lock); + appender->Free(appender); +} + +wLogAppender* WLog_GetLogAppender(wLog* log) +{ + if (!log) + return NULL; + + if (!log->Appender) + return WLog_GetLogAppender(log->Parent); + + return log->Appender; +} + +BOOL WLog_OpenAppender(wLog* log) +{ + int status = 0; + wLogAppender* appender = NULL; + + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->Open) + return TRUE; + + if (!appender->active) + { + status = appender->Open(log, appender); + appender->active = TRUE; + } + + return status; +} + +BOOL WLog_CloseAppender(wLog* log) +{ + int status = 0; + wLogAppender* appender = NULL; + + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->Close) + return TRUE; + + if (appender->active) + { + status = appender->Close(log, appender); + appender->active = FALSE; + } + + return status; +} + +static wLogAppender* WLog_Appender_New(wLog* log, DWORD logAppenderType) +{ + wLogAppender* appender = NULL; + + if (!log) + return NULL; + + switch (logAppenderType) + { + case WLOG_APPENDER_CONSOLE: + appender = WLog_ConsoleAppender_New(log); + break; + case WLOG_APPENDER_FILE: + appender = WLog_FileAppender_New(log); + break; + case WLOG_APPENDER_BINARY: + appender = WLog_BinaryAppender_New(log); + break; + case WLOG_APPENDER_CALLBACK: + appender = WLog_CallbackAppender_New(log); + break; +#ifdef WINPR_HAVE_SYSLOG_H + case WLOG_APPENDER_SYSLOG: + appender = WLog_SyslogAppender_New(log); + break; +#endif +#ifdef WINPR_HAVE_JOURNALD_H + case WLOG_APPENDER_JOURNALD: + appender = WLog_JournaldAppender_New(log); + break; +#endif + case WLOG_APPENDER_UDP: + appender = (wLogAppender*)WLog_UdpAppender_New(log); + break; + default: + fprintf(stderr, "%s: unknown handler type %" PRIu32 "\n", __func__, logAppenderType); + appender = NULL; + break; + } + + if (!appender) + appender = (wLogAppender*)WLog_ConsoleAppender_New(log); + + if (!appender) + return NULL; + + if (!(appender->Layout = WLog_Layout_New(log))) + { + WLog_Appender_Free(log, appender); + return NULL; + } + + InitializeCriticalSectionAndSpinCount(&appender->lock, 4000); + + return appender; +} + +BOOL WLog_SetLogAppenderType(wLog* log, DWORD logAppenderType) +{ + if (!log) + return FALSE; + + if (log->Appender) + { + WLog_Appender_Free(log, log->Appender); + log->Appender = NULL; + } + + log->Appender = WLog_Appender_New(log, logAppenderType); + return log->Appender != NULL; +} + +BOOL WLog_ConfigureAppender(wLogAppender* appender, const char* setting, void* value) +{ + /* Just check the settings string is not empty */ + if (!appender || !setting || (strnlen(setting, 2) == 0)) + return FALSE; + + if (appender->Set) + return appender->Set(appender, setting, value); + else + return FALSE; +} diff --git a/winpr/libwinpr/utils/wlog/Appender.h b/winpr/libwinpr/utils/wlog/Appender.h new file mode 100644 index 0000000..00f8119 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Appender.h @@ -0,0 +1,39 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_APPENDER_PRIVATE_H +#define WINPR_WLOG_APPENDER_PRIVATE_H + +#include "wlog.h" + +void WLog_Appender_Free(wLog* log, wLogAppender* appender); + +#include "FileAppender.h" +#include "ConsoleAppender.h" +#include "BinaryAppender.h" +#include "CallbackAppender.h" +#ifdef WINPR_HAVE_JOURNALD_H +#include "JournaldAppender.h" +#endif +#ifdef WINPR_HAVE_SYSLOG_H +#include "SyslogAppender.h" +#endif +#include "UdpAppender.h" + +#endif /* WINPR_WLOG_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/BinaryAppender.c b/winpr/libwinpr/utils/wlog/BinaryAppender.c new file mode 100644 index 0000000..e9a440a --- /dev/null +++ b/winpr/libwinpr/utils/wlog/BinaryAppender.c @@ -0,0 +1,238 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "BinaryAppender.h" +#include +#include +#include +#include +#include + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + char* FileName; + char* FilePath; + char* FullFileName; + FILE* FileDescriptor; +} wLogBinaryAppender; + +static BOOL WLog_BinaryAppender_Open(wLog* log, wLogAppender* appender) +{ + wLogBinaryAppender* binaryAppender = NULL; + if (!log || !appender) + return FALSE; + + binaryAppender = (wLogBinaryAppender*)appender; + if (!binaryAppender->FileName) + { + binaryAppender->FileName = (char*)malloc(MAX_PATH); + if (!binaryAppender->FileName) + return FALSE; + sprintf_s(binaryAppender->FileName, MAX_PATH, "%" PRIu32 ".wlog", GetCurrentProcessId()); + } + + if (!binaryAppender->FilePath) + { + binaryAppender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + if (!binaryAppender->FilePath) + return FALSE; + } + + if (!binaryAppender->FullFileName) + { + binaryAppender->FullFileName = + GetCombinedPath(binaryAppender->FilePath, binaryAppender->FileName); + if (!binaryAppender->FullFileName) + return FALSE; + } + + if (!winpr_PathFileExists(binaryAppender->FilePath)) + { + if (!winpr_PathMakePath(binaryAppender->FilePath, 0)) + return FALSE; + UnixChangeFileMode(binaryAppender->FilePath, 0xFFFF); + } + + binaryAppender->FileDescriptor = winpr_fopen(binaryAppender->FullFileName, "a+"); + + if (!binaryAppender->FileDescriptor) + return FALSE; + + return TRUE; +} + +static BOOL WLog_BinaryAppender_Close(wLog* log, wLogAppender* appender) +{ + wLogBinaryAppender* binaryAppender = NULL; + + if (!appender) + return FALSE; + + binaryAppender = (wLogBinaryAppender*)appender; + if (!binaryAppender->FileDescriptor) + return TRUE; + + if (binaryAppender->FileDescriptor) + fclose(binaryAppender->FileDescriptor); + + binaryAppender->FileDescriptor = NULL; + + return TRUE; +} + +static BOOL WLog_BinaryAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + FILE* fp = NULL; + wStream* s = NULL; + size_t MessageLength = 0; + size_t FileNameLength = 0; + size_t FunctionNameLength = 0; + size_t TextStringLength = 0; + BOOL ret = TRUE; + wLogBinaryAppender* binaryAppender = NULL; + + if (!log || !appender || !message) + return FALSE; + + binaryAppender = (wLogBinaryAppender*)appender; + + fp = binaryAppender->FileDescriptor; + + if (!fp) + return FALSE; + + FileNameLength = strnlen(message->FileName, INT_MAX); + FunctionNameLength = strnlen(message->FunctionName, INT_MAX); + TextStringLength = strnlen(message->TextString, INT_MAX); + + MessageLength = + 16 + (4 + FileNameLength + 1) + (4 + FunctionNameLength + 1) + (4 + TextStringLength + 1); + + if ((MessageLength > UINT32_MAX) || (FileNameLength > UINT32_MAX) || + (FunctionNameLength > UINT32_MAX) || (TextStringLength > UINT32_MAX)) + return FALSE; + + s = Stream_New(NULL, MessageLength); + if (!s) + return FALSE; + + Stream_Write_UINT32(s, (UINT32)MessageLength); + + Stream_Write_UINT32(s, message->Type); + Stream_Write_UINT32(s, message->Level); + + WINPR_ASSERT(message->LineNumber <= UINT32_MAX); + Stream_Write_UINT32(s, (UINT32)message->LineNumber); + + Stream_Write_UINT32(s, (UINT32)FileNameLength); + Stream_Write(s, message->FileName, FileNameLength + 1); + + Stream_Write_UINT32(s, (UINT32)FunctionNameLength); + Stream_Write(s, message->FunctionName, FunctionNameLength + 1); + + Stream_Write_UINT32(s, (UINT32)TextStringLength); + Stream_Write(s, message->TextString, TextStringLength + 1); + + Stream_SealLength(s); + + if (fwrite(Stream_Buffer(s), MessageLength, 1, fp) != 1) + ret = FALSE; + + Stream_Free(s, TRUE); + + return ret; +} + +static BOOL WLog_BinaryAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + return TRUE; +} + +static BOOL WLog_BinaryAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + return TRUE; +} + +static BOOL WLog_BinaryAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogBinaryAppender* binaryAppender = (wLogBinaryAppender*)appender; + + /* Just check if the value string is longer than 0 */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (!strcmp("outputfilename", setting)) + { + binaryAppender->FileName = _strdup((const char*)value); + if (!binaryAppender->FileName) + return FALSE; + } + else if (!strcmp("outputfilepath", setting)) + { + binaryAppender->FilePath = _strdup((const char*)value); + if (!binaryAppender->FilePath) + return FALSE; + } + else + return FALSE; + + return TRUE; +} + +static void WLog_BinaryAppender_Free(wLogAppender* appender) +{ + wLogBinaryAppender* binaryAppender = NULL; + if (appender) + { + binaryAppender = (wLogBinaryAppender*)appender; + free(binaryAppender->FileName); + free(binaryAppender->FilePath); + free(binaryAppender->FullFileName); + free(binaryAppender); + } +} + +wLogAppender* WLog_BinaryAppender_New(wLog* log) +{ + wLogBinaryAppender* BinaryAppender = NULL; + + BinaryAppender = (wLogBinaryAppender*)calloc(1, sizeof(wLogBinaryAppender)); + if (!BinaryAppender) + return NULL; + + BinaryAppender->Type = WLOG_APPENDER_BINARY; + BinaryAppender->Open = WLog_BinaryAppender_Open; + BinaryAppender->Close = WLog_BinaryAppender_Close; + BinaryAppender->WriteMessage = WLog_BinaryAppender_WriteMessage; + BinaryAppender->WriteDataMessage = WLog_BinaryAppender_WriteDataMessage; + BinaryAppender->WriteImageMessage = WLog_BinaryAppender_WriteImageMessage; + BinaryAppender->Free = WLog_BinaryAppender_Free; + BinaryAppender->Set = WLog_BinaryAppender_Set; + + return (wLogAppender*)BinaryAppender; +} diff --git a/winpr/libwinpr/utils/wlog/BinaryAppender.h b/winpr/libwinpr/utils/wlog/BinaryAppender.h new file mode 100644 index 0000000..fb65d9f --- /dev/null +++ b/winpr/libwinpr/utils/wlog/BinaryAppender.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_BINARY_APPENDER_PRIVATE_H +#define WINPR_WLOG_BINARY_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_BinaryAppender_New(wLog* log); + +#endif /* WINPR_WLOG_BINARY_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/CallbackAppender.c b/winpr/libwinpr/utils/wlog/CallbackAppender.c new file mode 100644 index 0000000..f324e7c --- /dev/null +++ b/winpr/libwinpr/utils/wlog/CallbackAppender.c @@ -0,0 +1,168 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2014 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "CallbackAppender.h" + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + wLogCallbacks* callbacks; +} wLogCallbackAppender; + +static BOOL WLog_CallbackAppender_Open(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_CallbackAppender_Close(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_CallbackAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + + if (callbackAppender->callbacks && callbackAppender->callbacks->message) + return callbackAppender->callbacks->message(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + if (callbackAppender->callbacks && callbackAppender->callbacks->data) + return callbackAppender->callbacks->data(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + if (callbackAppender->callbacks && callbackAppender->callbacks->image) + return callbackAppender->callbacks->image(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_WritePacketMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogCallbackAppender* callbackAppender = NULL; + + if (!appender) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + callbackAppender = (wLogCallbackAppender*)appender; + if (callbackAppender->callbacks && callbackAppender->callbacks->package) + return callbackAppender->callbacks->package(message); + else + return FALSE; +} + +static BOOL WLog_CallbackAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender; + + if (!value || strcmp(setting, "callbacks")) + return FALSE; + + if (!(callbackAppender->callbacks = calloc(1, sizeof(wLogCallbacks)))) + { + return FALSE; + } + + callbackAppender->callbacks = memcpy(callbackAppender->callbacks, value, sizeof(wLogCallbacks)); + return TRUE; +} + +static void WLog_CallbackAppender_Free(wLogAppender* appender) +{ + wLogCallbackAppender* callbackAppender = NULL; + if (!appender) + { + return; + } + + callbackAppender = (wLogCallbackAppender*)appender; + + free(callbackAppender->callbacks); + free(appender); +} + +wLogAppender* WLog_CallbackAppender_New(wLog* log) +{ + wLogCallbackAppender* CallbackAppender = NULL; + + CallbackAppender = (wLogCallbackAppender*)calloc(1, sizeof(wLogCallbackAppender)); + if (!CallbackAppender) + return NULL; + + CallbackAppender->Type = WLOG_APPENDER_CALLBACK; + + CallbackAppender->Open = WLog_CallbackAppender_Open; + CallbackAppender->Close = WLog_CallbackAppender_Close; + CallbackAppender->WriteMessage = WLog_CallbackAppender_WriteMessage; + CallbackAppender->WriteDataMessage = WLog_CallbackAppender_WriteDataMessage; + CallbackAppender->WriteImageMessage = WLog_CallbackAppender_WriteImageMessage; + CallbackAppender->WritePacketMessage = WLog_CallbackAppender_WritePacketMessage; + CallbackAppender->Free = WLog_CallbackAppender_Free; + CallbackAppender->Set = WLog_CallbackAppender_Set; + + return (wLogAppender*)CallbackAppender; +} diff --git a/winpr/libwinpr/utils/wlog/CallbackAppender.h b/winpr/libwinpr/utils/wlog/CallbackAppender.h new file mode 100644 index 0000000..cd10f7d --- /dev/null +++ b/winpr/libwinpr/utils/wlog/CallbackAppender.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2014 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H +#define WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_CallbackAppender_New(wLog* log); + +#endif /* WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.c b/winpr/libwinpr/utils/wlog/ConsoleAppender.c new file mode 100644 index 0000000..0a50ef6 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.c @@ -0,0 +1,276 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ConsoleAppender.h" +#include "Message.h" + +#ifdef ANDROID +#include +#endif + +#define WLOG_CONSOLE_DEFAULT 0 +#define WLOG_CONSOLE_STDOUT 1 +#define WLOG_CONSOLE_STDERR 2 +#define WLOG_CONSOLE_DEBUG 4 + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + int outputStream; +} wLogConsoleAppender; + +static BOOL WLog_ConsoleAppender_Open(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_ConsoleAppender_Close(wLog* log, wLogAppender* appender) +{ + return TRUE; +} + +static BOOL WLog_ConsoleAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + FILE* fp = NULL; + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogConsoleAppender* consoleAppender = NULL; + if (!appender) + return FALSE; + + consoleAppender = (wLogConsoleAppender*)appender; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + +#ifdef _WIN32 + if (consoleAppender->outputStream == WLOG_CONSOLE_DEBUG) + { + OutputDebugStringA(message->PrefixString); + OutputDebugStringA(message->TextString); + OutputDebugStringA("\n"); + + return TRUE; + } +#endif +#ifdef ANDROID + (void)fp; + android_LogPriority level; + switch (message->Level) + { + case WLOG_TRACE: + level = ANDROID_LOG_VERBOSE; + break; + case WLOG_DEBUG: + level = ANDROID_LOG_DEBUG; + break; + case WLOG_INFO: + level = ANDROID_LOG_INFO; + break; + case WLOG_WARN: + level = ANDROID_LOG_WARN; + break; + case WLOG_ERROR: + level = ANDROID_LOG_ERROR; + break; + case WLOG_FATAL: + level = ANDROID_LOG_FATAL; + break; + case WLOG_OFF: + level = ANDROID_LOG_SILENT; + break; + default: + level = ANDROID_LOG_FATAL; + break; + } + + if (level != ANDROID_LOG_SILENT) + __android_log_print(level, log->Name, "%s%s", message->PrefixString, message->TextString); + +#else + switch (consoleAppender->outputStream) + { + case WLOG_CONSOLE_STDOUT: + fp = stdout; + break; + case WLOG_CONSOLE_STDERR: + fp = stderr; + break; + default: + switch (message->Level) + { + case WLOG_TRACE: + case WLOG_DEBUG: + case WLOG_INFO: + fp = stdout; + break; + default: + fp = stderr; + break; + } + break; + } + + if (message->Level != WLOG_OFF) + fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); +#endif + return TRUE; +} + +static int g_DataId = 0; + +static BOOL WLog_ConsoleAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ +#if defined(ANDROID) + return FALSE; +#else + int DataId = 0; + char* FullFileName = NULL; + + DataId = g_DataId++; + FullFileName = WLog_Message_GetOutputFileName(DataId, "dat"); + + WLog_DataMessage_Write(FullFileName, message->Data, message->Length); + + free(FullFileName); + + return TRUE; +#endif +} + +static int g_ImageId = 0; + +static BOOL WLog_ConsoleAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ +#if defined(ANDROID) + return FALSE; +#else + int ImageId = 0; + char* FullFileName = NULL; + + ImageId = g_ImageId++; + FullFileName = WLog_Message_GetOutputFileName(ImageId, "bmp"); + + WLog_ImageMessage_Write(FullFileName, message->ImageData, message->ImageWidth, + message->ImageHeight, message->ImageBpp); + + free(FullFileName); + + return TRUE; +#endif +} + +static int g_PacketId = 0; + +static BOOL WLog_ConsoleAppender_WritePacketMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ +#if defined(ANDROID) + return FALSE; +#else + char* FullFileName = NULL; + + g_PacketId++; + + if (!appender->PacketMessageContext) + { + FullFileName = WLog_Message_GetOutputFileName(-1, "pcap"); + appender->PacketMessageContext = (void*)Pcap_Open(FullFileName, TRUE); + free(FullFileName); + } + + if (appender->PacketMessageContext) + return WLog_PacketMessage_Write((wPcap*)appender->PacketMessageContext, message->PacketData, + message->PacketLength, message->PacketFlags); + + return TRUE; +#endif +} +static BOOL WLog_ConsoleAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogConsoleAppender* consoleAppender = (wLogConsoleAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (strcmp("outputstream", setting)) + return FALSE; + + if (!strcmp("stdout", value)) + consoleAppender->outputStream = WLOG_CONSOLE_STDOUT; + else if (!strcmp("stderr", value)) + consoleAppender->outputStream = WLOG_CONSOLE_STDERR; + else if (!strcmp("default", value)) + consoleAppender->outputStream = WLOG_CONSOLE_DEFAULT; + else if (!strcmp("debug", value)) + consoleAppender->outputStream = WLOG_CONSOLE_DEBUG; + else + return FALSE; + + return TRUE; +} + +static void WLog_ConsoleAppender_Free(wLogAppender* appender) +{ + if (appender) + { + if (appender->PacketMessageContext) + { + Pcap_Close((wPcap*)appender->PacketMessageContext); + } + + free(appender); + } +} + +wLogAppender* WLog_ConsoleAppender_New(wLog* log) +{ + wLogConsoleAppender* ConsoleAppender = NULL; + + ConsoleAppender = (wLogConsoleAppender*)calloc(1, sizeof(wLogConsoleAppender)); + + if (!ConsoleAppender) + return NULL; + + ConsoleAppender->Type = WLOG_APPENDER_CONSOLE; + + ConsoleAppender->Open = WLog_ConsoleAppender_Open; + ConsoleAppender->Close = WLog_ConsoleAppender_Close; + ConsoleAppender->WriteMessage = WLog_ConsoleAppender_WriteMessage; + ConsoleAppender->WriteDataMessage = WLog_ConsoleAppender_WriteDataMessage; + ConsoleAppender->WriteImageMessage = WLog_ConsoleAppender_WriteImageMessage; + ConsoleAppender->WritePacketMessage = WLog_ConsoleAppender_WritePacketMessage; + ConsoleAppender->Set = WLog_ConsoleAppender_Set; + ConsoleAppender->Free = WLog_ConsoleAppender_Free; + + ConsoleAppender->outputStream = WLOG_CONSOLE_DEFAULT; + +#ifdef _WIN32 + if (IsDebuggerPresent()) + ConsoleAppender->outputStream = WLOG_CONSOLE_DEBUG; +#endif + + return (wLogAppender*)ConsoleAppender; +} diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.h b/winpr/libwinpr/utils/wlog/ConsoleAppender.h new file mode 100644 index 0000000..f6a1405 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H +#define WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_ConsoleAppender_New(wLog* log); + +#endif /* WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/DataMessage.c b/winpr/libwinpr/utils/wlog/DataMessage.c new file mode 100644 index 0000000..512fddd --- /dev/null +++ b/winpr/libwinpr/utils/wlog/DataMessage.c @@ -0,0 +1,48 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "wlog.h" + +#include "DataMessage.h" + +#include + +#include "../../log.h" +#define TAG WINPR_TAG("utils.wlog") + +BOOL WLog_DataMessage_Write(const char* filename, const void* data, size_t length) +{ + FILE* fp = NULL; + BOOL ret = TRUE; + + fp = winpr_fopen(filename, "w+b"); + + if (!fp) + { + // WLog_ERR(TAG, "failed to open file %s", filename); + return FALSE; + } + + if (fwrite(data, length, 1, fp) != 1) + ret = FALSE; + fclose(fp); + return ret; +} diff --git a/winpr/libwinpr/utils/wlog/DataMessage.h b/winpr/libwinpr/utils/wlog/DataMessage.h new file mode 100644 index 0000000..db2c09e --- /dev/null +++ b/winpr/libwinpr/utils/wlog/DataMessage.h @@ -0,0 +1,25 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_DATA_MESSAGE_PRIVATE_H +#define WINPR_WLOG_DATA_MESSAGE_PRIVATE_H + +BOOL WLog_DataMessage_Write(const char* filename, const void* data, size_t length); + +#endif /* WINPR_WLOG_DATA_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/FileAppender.c b/winpr/libwinpr/utils/wlog/FileAppender.c new file mode 100644 index 0000000..7afc658 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/FileAppender.c @@ -0,0 +1,287 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "FileAppender.h" +#include "Message.h" + +#include +#include +#include +#include + +typedef struct +{ + WLOG_APPENDER_COMMON(); + + char* FileName; + char* FilePath; + char* FullFileName; + FILE* FileDescriptor; +} wLogFileAppender; + +static BOOL WLog_FileAppender_SetOutputFileName(wLogFileAppender* appender, const char* filename) +{ + appender->FileName = _strdup(filename); + + if (!appender->FileName) + return FALSE; + + return TRUE; +} + +static BOOL WLog_FileAppender_SetOutputFilePath(wLogFileAppender* appender, const char* filepath) +{ + appender->FilePath = _strdup(filepath); + + if (!appender->FilePath) + return FALSE; + + return TRUE; +} + +static BOOL WLog_FileAppender_Open(wLog* log, wLogAppender* appender) +{ + wLogFileAppender* fileAppender = NULL; + + if (!log || !appender) + return FALSE; + + fileAppender = (wLogFileAppender*)appender; + + if (!fileAppender->FilePath) + { + fileAppender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + + if (!fileAppender->FilePath) + return FALSE; + } + + if (!fileAppender->FileName) + { + fileAppender->FileName = (char*)malloc(MAX_PATH); + + if (!fileAppender->FileName) + return FALSE; + + sprintf_s(fileAppender->FileName, MAX_PATH, "%" PRIu32 ".log", GetCurrentProcessId()); + } + + if (!fileAppender->FullFileName) + { + fileAppender->FullFileName = + GetCombinedPath(fileAppender->FilePath, fileAppender->FileName); + + if (!fileAppender->FullFileName) + return FALSE; + } + + if (!winpr_PathFileExists(fileAppender->FilePath)) + { + if (!winpr_PathMakePath(fileAppender->FilePath, 0)) + return FALSE; + + UnixChangeFileMode(fileAppender->FilePath, 0xFFFF); + } + + fileAppender->FileDescriptor = winpr_fopen(fileAppender->FullFileName, "a+"); + + if (!fileAppender->FileDescriptor) + return FALSE; + + return TRUE; +} + +static BOOL WLog_FileAppender_Close(wLog* log, wLogAppender* appender) +{ + wLogFileAppender* fileAppender = NULL; + + if (!log || !appender) + return FALSE; + + fileAppender = (wLogFileAppender*)appender; + + if (!fileAppender->FileDescriptor) + return TRUE; + + fclose(fileAppender->FileDescriptor); + fileAppender->FileDescriptor = NULL; + return TRUE; +} + +static BOOL WLog_FileAppender_WriteMessage(wLog* log, wLogAppender* appender, wLogMessage* message) +{ + FILE* fp = NULL; + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogFileAppender* fileAppender = NULL; + + if (!log || !appender || !message) + return FALSE; + + fileAppender = (wLogFileAppender*)appender; + fp = fileAppender->FileDescriptor; + + if (!fp) + return FALSE; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); + fflush(fp); /* slow! */ + return TRUE; +} + +static int g_DataId = 0; + +static BOOL WLog_FileAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int DataId = 0; + char* FullFileName = NULL; + + if (!log || !appender || !message) + return FALSE; + + DataId = g_DataId++; + FullFileName = WLog_Message_GetOutputFileName(DataId, "dat"); + WLog_DataMessage_Write(FullFileName, message->Data, message->Length); + free(FullFileName); + return TRUE; +} + +static int g_ImageId = 0; + +static BOOL WLog_FileAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int ImageId = 0; + char* FullFileName = NULL; + + if (!log || !appender || !message) + return FALSE; + + ImageId = g_ImageId++; + FullFileName = WLog_Message_GetOutputFileName(ImageId, "bmp"); + WLog_ImageMessage_Write(FullFileName, message->ImageData, message->ImageWidth, + message->ImageHeight, message->ImageBpp); + free(FullFileName); + return TRUE; +} + +static BOOL WLog_FileAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogFileAppender* fileAppender = (wLogFileAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (!strcmp("outputfilename", setting)) + return WLog_FileAppender_SetOutputFileName(fileAppender, (const char*)value); + + if (!strcmp("outputfilepath", setting)) + return WLog_FileAppender_SetOutputFilePath(fileAppender, (const char*)value); + + return FALSE; +} + +static void WLog_FileAppender_Free(wLogAppender* appender) +{ + wLogFileAppender* fileAppender = NULL; + + if (appender) + { + fileAppender = (wLogFileAppender*)appender; + free(fileAppender->FileName); + free(fileAppender->FilePath); + free(fileAppender->FullFileName); + free(fileAppender); + } +} + +wLogAppender* WLog_FileAppender_New(wLog* log) +{ + LPSTR env = NULL; + LPCSTR name = NULL; + DWORD nSize = 0; + wLogFileAppender* FileAppender = NULL; + FileAppender = (wLogFileAppender*)calloc(1, sizeof(wLogFileAppender)); + + if (!FileAppender) + return NULL; + + FileAppender->Type = WLOG_APPENDER_FILE; + FileAppender->Open = WLog_FileAppender_Open; + FileAppender->Close = WLog_FileAppender_Close; + FileAppender->WriteMessage = WLog_FileAppender_WriteMessage; + FileAppender->WriteDataMessage = WLog_FileAppender_WriteDataMessage; + FileAppender->WriteImageMessage = WLog_FileAppender_WriteImageMessage; + FileAppender->Free = WLog_FileAppender_Free; + FileAppender->Set = WLog_FileAppender_Set; + name = "WLOG_FILEAPPENDER_OUTPUT_FILE_PATH"; + nSize = GetEnvironmentVariableA(name, NULL, 0); + + if (nSize) + { + BOOL status = 0; + env = (LPSTR)malloc(nSize); + + if (!env) + goto error_free; + + if (GetEnvironmentVariableA(name, env, nSize) != nSize - 1) + { + free(env); + goto error_free; + } + + status = WLog_FileAppender_SetOutputFilePath(FileAppender, env); + free(env); + + if (!status) + goto error_free; + } + + name = "WLOG_FILEAPPENDER_OUTPUT_FILE_NAME"; + nSize = GetEnvironmentVariableA(name, NULL, 0); + + if (nSize) + { + BOOL status = FALSE; + env = (LPSTR)malloc(nSize); + + if (!env) + goto error_output_file_name; + + if (GetEnvironmentVariableA(name, env, nSize) == nSize - 1) + status = WLog_FileAppender_SetOutputFileName(FileAppender, env); + free(env); + + if (!status) + goto error_output_file_name; + } + + return (wLogAppender*)FileAppender; +error_output_file_name: + free(FileAppender->FilePath); +error_free: + free(FileAppender); + return NULL; +} diff --git a/winpr/libwinpr/utils/wlog/FileAppender.h b/winpr/libwinpr/utils/wlog/FileAppender.h new file mode 100644 index 0000000..8938488 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/FileAppender.h @@ -0,0 +1,28 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_FILE_APPENDER_PRIVATE_H +#define WINPR_WLOG_FILE_APPENDER_PRIVATE_H + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_FileAppender_New(wLog* log); + +#endif /* WINPR_WLOG_FILE_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/ImageMessage.c b/winpr/libwinpr/utils/wlog/ImageMessage.c new file mode 100644 index 0000000..ce60032 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ImageMessage.c @@ -0,0 +1,37 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "wlog.h" +#include + +#include "ImageMessage.h" + +BOOL WLog_ImageMessage_Write(char* filename, void* data, size_t width, size_t height, size_t bpp) +{ + int status = 0; + + status = winpr_bitmap_write(filename, data, width, height, bpp); + + if (status < 0) + return FALSE; + + return TRUE; +} diff --git a/winpr/libwinpr/utils/wlog/ImageMessage.h b/winpr/libwinpr/utils/wlog/ImageMessage.h new file mode 100644 index 0000000..15ed81b --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ImageMessage.h @@ -0,0 +1,25 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H +#define WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H + +BOOL WLog_ImageMessage_Write(char* filename, void* data, size_t width, size_t height, size_t bpp); + +#endif /* WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/JournaldAppender.c b/winpr/libwinpr/utils/wlog/JournaldAppender.c new file mode 100644 index 0000000..504108b --- /dev/null +++ b/winpr/libwinpr/utils/wlog/JournaldAppender.c @@ -0,0 +1,210 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "JournaldAppender.h" + +#include +#include +#include + +#include +#include + +typedef struct +{ + WLOG_APPENDER_COMMON(); + char* identifier; + FILE* stream; +} wLogJournaldAppender; + +static BOOL WLog_JournaldAppender_Open(wLog* log, wLogAppender* appender) +{ + int fd = 0; + wLogJournaldAppender* journaldAppender = NULL; + + if (!log || !appender) + return FALSE; + + journaldAppender = (wLogJournaldAppender*)appender; + if (journaldAppender->stream) + return TRUE; + + fd = sd_journal_stream_fd(journaldAppender->identifier, LOG_INFO, 1); + if (fd < 0) + return FALSE; + + journaldAppender->stream = fdopen(fd, "w"); + if (!journaldAppender->stream) + { + close(fd); + return FALSE; + } + + setbuffer(journaldAppender->stream, NULL, 0); + return TRUE; +} + +static BOOL WLog_JournaldAppender_Close(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_JournaldAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + char* formatStr = NULL; + wLogJournaldAppender* journaldAppender = NULL; + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + + if (!log || !appender || !message) + return FALSE; + + journaldAppender = (wLogJournaldAppender*)appender; + + switch (message->Level) + { + case WLOG_TRACE: + case WLOG_DEBUG: + formatStr = "<7>%s%s\n"; + break; + case WLOG_INFO: + formatStr = "<6>%s%s\n"; + break; + case WLOG_WARN: + formatStr = "<4>%s%s\n"; + break; + case WLOG_ERROR: + formatStr = "<3>%s%s\n"; + break; + case WLOG_FATAL: + formatStr = "<2>%s%s\n"; + break; + case WLOG_OFF: + return TRUE; + default: + fprintf(stderr, "%s: unknown level %" PRIu32 "\n", __func__, message->Level); + return FALSE; + } + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + if (message->Level != WLOG_OFF) + fprintf(journaldAppender->stream, formatStr, message->PrefixString, message->TextString); + return TRUE; +} + +static BOOL WLog_JournaldAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_JournaldAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_JournaldAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + wLogJournaldAppender* journaldAppender = (wLogJournaldAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (strcmp("identifier", setting)) + return FALSE; + + /* If the stream is already open the identifier can't be changed */ + if (journaldAppender->stream) + return FALSE; + + if (journaldAppender->identifier) + free(journaldAppender->identifier); + + return ((journaldAppender->identifier = _strdup((const char*)value)) != NULL); +} + +static void WLog_JournaldAppender_Free(wLogAppender* appender) +{ + wLogJournaldAppender* journaldAppender = NULL; + if (appender) + { + journaldAppender = (wLogJournaldAppender*)appender; + if (journaldAppender->stream) + fclose(journaldAppender->stream); + free(journaldAppender->identifier); + free(journaldAppender); + } +} + +wLogAppender* WLog_JournaldAppender_New(wLog* log) +{ + wLogJournaldAppender* appender = NULL; + DWORD nSize = 0; + LPCSTR name = "WLOG_JOURNALD_ID"; + + appender = (wLogJournaldAppender*)calloc(1, sizeof(wLogJournaldAppender)); + if (!appender) + return NULL; + + appender->Type = WLOG_APPENDER_JOURNALD; + appender->Open = WLog_JournaldAppender_Open; + appender->Close = WLog_JournaldAppender_Close; + appender->WriteMessage = WLog_JournaldAppender_WriteMessage; + appender->WriteDataMessage = WLog_JournaldAppender_WriteDataMessage; + appender->WriteImageMessage = WLog_JournaldAppender_WriteImageMessage; + appender->Set = WLog_JournaldAppender_Set; + appender->Free = WLog_JournaldAppender_Free; + + nSize = GetEnvironmentVariableA(name, NULL, 0); + if (nSize) + { + appender->identifier = (LPSTR)malloc(nSize); + if (!appender->identifier) + goto error_open; + + if (GetEnvironmentVariableA(name, appender->identifier, nSize) != nSize - 1) + goto error_open; + + if (!WLog_JournaldAppender_Open(log, (wLogAppender*)appender)) + goto error_open; + } + + return (wLogAppender*)appender; + +error_open: + free(appender->identifier); + free(appender); + return NULL; +} diff --git a/winpr/libwinpr/utils/wlog/JournaldAppender.h b/winpr/libwinpr/utils/wlog/JournaldAppender.h new file mode 100644 index 0000000..49223c5 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/JournaldAppender.h @@ -0,0 +1,31 @@ +/** + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ +#define WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ + +#include "wlog.h" + +wLogAppender* WLog_JournaldAppender_New(wLog* log); + +#endif /* WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ */ diff --git a/winpr/libwinpr/utils/wlog/Layout.c b/winpr/libwinpr/utils/wlog/Layout.c new file mode 100644 index 0000000..188c15b --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Layout.c @@ -0,0 +1,375 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "wlog.h" + +#include "Layout.h" + +#if defined __linux__ && !defined ANDROID +#include +#include +#endif + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +struct format_option_recurse; + +struct format_option +{ + const char* fmt; + size_t fmtlen; + const char* replace; + size_t replacelen; + const char* (*fkt)(void*); + void* arg; + const char* (*ext)(const struct format_option* opt, const char* str, size_t* preplacelen, + size_t* pskiplen); + struct format_option_recurse* recurse; +}; + +struct format_option_recurse +{ + struct format_option* options; + size_t nroptions; + wLog* log; + wLogLayout* layout; + wLogMessage* message; + char buffer[WLOG_MAX_PREFIX_SIZE]; +}; + +/** + * Log Layout + */ +WINPR_ATTR_FORMAT_ARG(3, 0) +static void WLog_PrintMessagePrefixVA(wLog* log, wLogMessage* message, + WINPR_FORMAT_ARG const char* format, va_list args) +{ + WINPR_ASSERT(message); + vsnprintf(message->PrefixString, WLOG_MAX_PREFIX_SIZE - 1, format, args); +} + +WINPR_ATTR_FORMAT_ARG(3, 4) +static void WLog_PrintMessagePrefix(wLog* log, wLogMessage* message, + WINPR_FORMAT_ARG const char* format, ...) +{ + va_list args; + va_start(args, format); + WLog_PrintMessagePrefixVA(log, message, format, args); + va_end(args); +} + +static const char* get_tid(void* arg) +{ + char* str = arg; + size_t tid = 0; +#if defined __linux__ && !defined ANDROID + /* On Linux we prefer to see the LWP id */ + tid = (size_t)syscall(SYS_gettid); +#else + tid = (size_t)GetCurrentThreadId(); +#endif + sprintf(str, "%08" PRIxz, tid); + return str; +} + +static BOOL log_invalid_fmt(const char* what) +{ + fprintf(stderr, "Invalid format string '%s'\n", what); + return FALSE; +} + +static BOOL check_and_log_format_size(char* format, size_t size, size_t index, size_t add) +{ + /* format string must be '\0' terminated, so abort at size - 1 */ + if (index + add + 1 >= size) + { + fprintf(stderr, + "Format string too long ['%s', max %" PRIuz ", used %" PRIuz ", adding %" PRIuz + "]\n", + format, size, index, add); + return FALSE; + } + return TRUE; +} + +static int opt_compare_fn(const void* a, const void* b) +{ + const char* what = a; + const struct format_option* opt = b; + if (!opt) + return -1; + return strncmp(what, opt->fmt, opt->fmtlen); +} + +static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse, + char* format, size_t formatlen); + +static const char* skip_if_null(const struct format_option* opt, const char* fmt, + size_t* preplacelen, size_t* pskiplen) +{ + WINPR_ASSERT(opt); + WINPR_ASSERT(fmt); + WINPR_ASSERT(preplacelen); + WINPR_ASSERT(pskiplen); + + *preplacelen = 0; + *pskiplen = 0; + + const char* str = &fmt[opt->fmtlen]; /* Skip first %{ from string */ + const char* end = strstr(str, opt->replace); + if (!end) + return NULL; + *pskiplen = end - fmt + opt->replacelen; + + if (!opt->arg) + return NULL; + + const size_t replacelen = end - str; + + char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 }; + memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1)); + + if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer, + ARRAYSIZE(opt->recurse->buffer))) + return NULL; + + *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer)); + return opt->recurse->buffer; +} + +static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse, + char* format, size_t formatlen) +{ + WINPR_ASSERT(FormatString); + WINPR_ASSERT(recurse); + + size_t index = 0; + + while (*FormatString) + { + const struct format_option* opt = + bsearch(FormatString, recurse->options, recurse->nroptions, + sizeof(struct format_option), opt_compare_fn); + if (opt) + { + size_t replacelen = opt->replacelen; + size_t fmtlen = opt->fmtlen; + const char* replace = opt->replace; + const void* arg = opt->arg; + + if (opt->ext) + replace = opt->ext(opt, FormatString, &replacelen, &fmtlen); + if (opt->fkt) + arg = opt->fkt(opt->arg); + + if (replace && (replacelen > 0)) + { + const int rc = _snprintf(&format[index], formatlen - index, replace, arg); + if (rc < 0) + return FALSE; + if (!check_and_log_format_size(format, formatlen, index, rc)) + return FALSE; + index += rc; + } + FormatString += fmtlen; + } + else + { + /* Unknown format string */ + if (*FormatString == '%') + return log_invalid_fmt(FormatString); + + if (!check_and_log_format_size(format, formatlen, index, 1)) + return FALSE; + format[index++] = *FormatString++; + } + } + + if (!check_and_log_format_size(format, formatlen, index, 0)) + return FALSE; + return TRUE; +} + +BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message) +{ + char format[WLOG_MAX_PREFIX_SIZE] = { 0 }; + + WINPR_ASSERT(layout); + WINPR_ASSERT(message); + + char tid[32] = { 0 }; + SYSTEMTIME localTime = { 0 }; + GetLocalTime(&localTime); + + struct format_option_recurse recurse = { + .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message + }; + +#define ENTRY(x) x, sizeof(x) - 1 + struct format_option options[] = { + { ENTRY("%ctx"), ENTRY("%s"), log->custom, log->context, NULL, &recurse }, /* log context */ + { ENTRY("%dw"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDayOfWeek, NULL, + &recurse }, /* day of week */ + { ENTRY("%dy"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDay, NULL, + &recurse }, /* day of year */ + { ENTRY("%fl"), ENTRY("%s"), NULL, (void*)message->FileName, NULL, &recurse }, /* file */ + { ENTRY("%fn"), ENTRY("%s"), NULL, (void*)message->FunctionName, NULL, + &recurse }, /* function */ + { ENTRY("%hr"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wHour, NULL, + &recurse }, /* hours */ + { ENTRY("%ln"), ENTRY("%" PRIuz), NULL, (void*)(size_t)message->LineNumber, NULL, + &recurse }, /* line number */ + { ENTRY("%lv"), ENTRY("%s"), NULL, (void*)WLOG_LEVELS[message->Level], NULL, + &recurse }, /* log level */ + { ENTRY("%mi"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMinute, NULL, + &recurse }, /* minutes */ + { ENTRY("%ml"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMilliseconds, NULL, + &recurse }, /* milliseconds */ + { ENTRY("%mn"), ENTRY("%s"), NULL, log->Name, NULL, &recurse }, /* module name */ + { ENTRY("%mo"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wMonth, NULL, + &recurse }, /* month */ + { ENTRY("%pid"), ENTRY("%u"), NULL, (void*)(size_t)GetCurrentProcessId(), NULL, + &recurse }, /* process id */ + { ENTRY("%se"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wSecond, NULL, + &recurse }, /* seconds */ + { ENTRY("%tid"), ENTRY("%s"), get_tid, tid, NULL, &recurse }, /* thread id */ + { ENTRY("%yr"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wYear, NULL, + &recurse }, /* year */ + { ENTRY("%{"), ENTRY("%}"), NULL, log->context, skip_if_null, + &recurse }, /* skip if no context */ + }; + + recurse.options = options; + recurse.nroptions = ARRAYSIZE(options); + + if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format))) + return FALSE; + + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY + + WLog_PrintMessagePrefix(log, message, format); + + WINPR_PRAGMA_DIAG_POP + + return TRUE; +} + +wLogLayout* WLog_GetLogLayout(wLog* log) +{ + wLogAppender* appender = NULL; + appender = WLog_GetLogAppender(log); + return appender->Layout; +} + +BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format) +{ + free(layout->FormatString); + layout->FormatString = NULL; + + if (format) + { + layout->FormatString = _strdup(format); + + if (!layout->FormatString) + return FALSE; + } + + return TRUE; +} + +wLogLayout* WLog_Layout_New(wLog* log) +{ + LPCSTR prefix = "WLOG_PREFIX"; + DWORD nSize = 0; + char* env = NULL; + wLogLayout* layout = NULL; + layout = (wLogLayout*)calloc(1, sizeof(wLogLayout)); + + if (!layout) + return NULL; + + nSize = GetEnvironmentVariableA(prefix, NULL, 0); + + if (nSize) + { + env = (LPSTR)malloc(nSize); + + if (!env) + { + free(layout); + return NULL; + } + + if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1) + { + free(env); + free(layout); + return NULL; + } + } + + if (env) + layout->FormatString = env; + else + { +#ifdef ANDROID + layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: "); +#else + layout->FormatString = + _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: "); +#endif + + if (!layout->FormatString) + { + free(layout); + return NULL; + } + } + + return layout; +} + +void WLog_Layout_Free(wLog* log, wLogLayout* layout) +{ + if (layout) + { + if (layout->FormatString) + { + free(layout->FormatString); + layout->FormatString = NULL; + } + + free(layout); + } +} diff --git a/winpr/libwinpr/utils/wlog/Layout.h b/winpr/libwinpr/utils/wlog/Layout.h new file mode 100644 index 0000000..8698077 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Layout.h @@ -0,0 +1,41 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_LAYOUT_PRIVATE_H +#define WINPR_WLOG_LAYOUT_PRIVATE_H + +#include "wlog.h" + +/** + * Log Layout + */ + +struct s_wLogLayout +{ + DWORD Type; + + LPSTR FormatString; +}; + +void WLog_Layout_Free(wLog* log, wLogLayout* layout); + +WINPR_ATTR_MALLOC(WLog_Layout_Free, 2) +wLogLayout* WLog_Layout_New(wLog* log); + +#endif /* WINPR_WLOG_LAYOUT_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/Message.c b/winpr/libwinpr/utils/wlog/Message.c new file mode 100644 index 0000000..bedb5ed --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Message.c @@ -0,0 +1,64 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "wlog.h" + +#include "Message.h" + +char* WLog_Message_GetOutputFileName(int id, const char* ext) +{ + DWORD ProcessId = 0; + char* FilePath = NULL; + char* FileName = NULL; + char* FullFileName = NULL; + + if (!(FileName = (char*)malloc(256))) + return NULL; + + FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + + if (!winpr_PathFileExists(FilePath)) + { + if (!winpr_PathMakePath(FilePath, NULL)) + { + free(FileName); + free(FilePath); + return NULL; + } + } + + ProcessId = GetCurrentProcessId(); + if (id >= 0) + sprintf_s(FileName, 256, "%" PRIu32 "-%d.%s", ProcessId, id, ext); + else + sprintf_s(FileName, 256, "%" PRIu32 ".%s", ProcessId, ext); + + FullFileName = GetCombinedPath(FilePath, FileName); + + free(FileName); + free(FilePath); + + return FullFileName; +} diff --git a/winpr/libwinpr/utils/wlog/Message.h b/winpr/libwinpr/utils/wlog/Message.h new file mode 100644 index 0000000..c65b33c --- /dev/null +++ b/winpr/libwinpr/utils/wlog/Message.h @@ -0,0 +1,29 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_MESSAGE_PRIVATE_H +#define WINPR_WLOG_MESSAGE_PRIVATE_H + +#include "DataMessage.h" +#include "ImageMessage.h" +#include "PacketMessage.h" + +char* WLog_Message_GetOutputFileName(int id, const char* ext); + +#endif /* WINPR_WLOG_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/PacketMessage.c b/winpr/libwinpr/utils/wlog/PacketMessage.c new file mode 100644 index 0000000..cc1c812 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/PacketMessage.c @@ -0,0 +1,487 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "wlog.h" + +#include "PacketMessage.h" + +#include +#include +#include +#include + +#include "../../log.h" +#define TAG WINPR_TAG("utils.wlog") + +#ifndef _WIN32 +#include +#else +#include +#include +#include + +static int gettimeofday(struct timeval* tp, void* tz) +{ + struct _timeb timebuffer; + _ftime(&timebuffer); + tp->tv_sec = (long)timebuffer.time; + tp->tv_usec = timebuffer.millitm * 1000; + return 0; +} +#endif + +static BOOL Pcap_Read_Header(wPcap* pcap, wPcapHeader* header) +{ + if (pcap && pcap->fp && fread((void*)header, sizeof(wPcapHeader), 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +/* currently unused code */ +#if 0 +static BOOL Pcap_Read_RecordHeader(wPcap* pcap, wPcapRecordHeader* record) +{ + if (pcap && pcap->fp && (fread((void*) record, sizeof(wPcapRecordHeader), 1, pcap->fp) == 1)) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Read_Record(wPcap* pcap, wPcapRecord* record) +{ + if (pcap && pcap->fp) + { + if (!Pcap_Read_RecordHeader(pcap, &record->header)) + return FALSE; + record->length = record->header.incl_len; + record->data = malloc(record->length); + if (!record->data) + return FALSE; + if (fread(record->data, record->length, 1, pcap->fp) != 1) + { + free(record->data); + record->length = 0; + record->data = NULL; + return FALSE; + } + } + return TRUE; +} + +static BOOL Pcap_Add_Record(wPcap* pcap, void* data, UINT32 length) +{ + wPcapRecord* record; + struct timeval tp; + + if (!pcap->tail) + { + pcap->tail = (wPcapRecord*) calloc(1, sizeof(wPcapRecord)); + if (!pcap->tail) + return FALSE; + pcap->head = pcap->tail; + pcap->record = pcap->head; + record = pcap->tail; + } + else + { + record = (wPcapRecord*) calloc(1, sizeof(wPcapRecord)); + if (!record) + return FALSE; + pcap->tail->next = record; + pcap->tail = record; + } + + if (!pcap->record) + pcap->record = record; + + record->data = data; + record->length = length; + record->header.incl_len = length; + record->header.orig_len = length; + gettimeofday(&tp, 0); + record->header.ts_sec = tp.tv_sec; + record->header.ts_usec = tp.tv_usec; + return TRUE; +} + +static BOOL Pcap_HasNext_Record(wPcap* pcap) +{ + if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16) + return FALSE; + + return TRUE; +} + +static BOOL Pcap_GetNext_RecordHeader(wPcap* pcap, wPcapRecord* record) +{ + if (!Pcap_HasNext_Record(pcap) || !Pcap_Read_RecordHeader(pcap, &record->header)) + return FALSE; + + record->length = record->header.incl_len; + return TRUE; +} + +static BOOL Pcap_GetNext_RecordContent(wPcap* pcap, wPcapRecord* record) +{ + if (pcap && pcap->fp && fread(record->data, record->length, 1, pcap->fp) == 1) + return TRUE; + + return FALSE; +} + +static BOOL Pcap_GetNext_Record(wPcap* pcap, wPcapRecord* record) +{ + if (!Pcap_HasNext_Record(pcap)) + return FALSE; + + return Pcap_Read_Record(pcap, record); +} +#endif + +static BOOL Pcap_Write_Header(wPcap* pcap, wPcapHeader* header) +{ + if (pcap && pcap->fp && fwrite((void*)header, sizeof(wPcapHeader), 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Write_RecordHeader(wPcap* pcap, wPcapRecordHeader* record) +{ + if (pcap && pcap->fp && fwrite((void*)record, sizeof(wPcapRecordHeader), 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Write_RecordContent(wPcap* pcap, wPcapRecord* record) +{ + if (pcap && pcap->fp && fwrite(record->data, record->length, 1, pcap->fp) == 1) + return TRUE; + return FALSE; +} + +static BOOL Pcap_Write_Record(wPcap* pcap, wPcapRecord* record) +{ + return Pcap_Write_RecordHeader(pcap, &record->header) && Pcap_Write_RecordContent(pcap, record); +} + +wPcap* Pcap_Open(char* name, BOOL write) +{ + wPcap* pcap = NULL; + FILE* pcap_fp = winpr_fopen(name, write ? "w+b" : "rb"); + + if (!pcap_fp) + { + WLog_ERR(TAG, "opening pcap file"); + return NULL; + } + + pcap = (wPcap*)calloc(1, sizeof(wPcap)); + + if (!pcap) + goto out_fail; + + pcap->name = name; + pcap->write = write; + pcap->record_count = 0; + pcap->fp = pcap_fp; + + if (write) + { + pcap->header.magic_number = PCAP_MAGIC_NUMBER; + pcap->header.version_major = 2; + pcap->header.version_minor = 4; + pcap->header.thiszone = 0; + pcap->header.sigfigs = 0; + pcap->header.snaplen = 0xFFFFFFFF; + pcap->header.network = 1; /* ethernet */ + if (!Pcap_Write_Header(pcap, &pcap->header)) + goto out_fail; + } + else + { + if (_fseeki64(pcap->fp, 0, SEEK_END) < 0) + goto out_fail; + pcap->file_size = (SSIZE_T)_ftelli64(pcap->fp); + if (pcap->file_size < 0) + goto out_fail; + if (_fseeki64(pcap->fp, 0, SEEK_SET) < 0) + goto out_fail; + if (!Pcap_Read_Header(pcap, &pcap->header)) + goto out_fail; + } + + return pcap; + +out_fail: + if (pcap_fp) + fclose(pcap_fp); + free(pcap); + return NULL; +} + +void Pcap_Flush(wPcap* pcap) +{ + if (!pcap || !pcap->fp) + return; + + while (pcap->record) + { + if (!Pcap_Write_Record(pcap, pcap->record)) + return; + pcap->record = pcap->record->next; + } + + fflush(pcap->fp); + return; +} + +void Pcap_Close(wPcap* pcap) +{ + if (!pcap || !pcap->fp) + return; + + Pcap_Flush(pcap); + fclose(pcap->fp); + free(pcap); +} + +static BOOL WLog_PacketMessage_Write_EthernetHeader(wPcap* pcap, wEthernetHeader* ethernet) +{ + wStream* s = NULL; + wStream sbuffer = { 0 }; + BYTE buffer[14] = { 0 }; + BOOL ret = TRUE; + + if (!pcap || !pcap->fp || !ethernet) + return FALSE; + + s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + if (!s) + return FALSE; + Stream_Write(s, ethernet->Destination, 6); + Stream_Write(s, ethernet->Source, 6); + Stream_Write_UINT16_BE(s, ethernet->Type); + if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1) + ret = FALSE; + + return ret; +} + +static UINT16 IPv4Checksum(BYTE* ipv4, int length) +{ + UINT16 tmp16 = 0; + long checksum = 0; + + while (length > 1) + { + tmp16 = *((UINT16*)ipv4); + checksum += tmp16; + length -= 2; + ipv4 += 2; + } + + if (length > 0) + checksum += *ipv4; + + while (checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + return (UINT16)(~checksum); +} + +static BOOL WLog_PacketMessage_Write_IPv4Header(wPcap* pcap, wIPv4Header* ipv4) +{ + wStream* s = NULL; + wStream sbuffer = { 0 }; + BYTE buffer[20] = { 0 }; + int ret = TRUE; + + if (!pcap || !pcap->fp || !ipv4) + return FALSE; + + s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + if (!s) + return FALSE; + Stream_Write_UINT8(s, (ipv4->Version << 4) | ipv4->InternetHeaderLength); + Stream_Write_UINT8(s, ipv4->TypeOfService); + Stream_Write_UINT16_BE(s, ipv4->TotalLength); + Stream_Write_UINT16_BE(s, ipv4->Identification); + Stream_Write_UINT16_BE(s, (ipv4->InternetProtocolFlags << 13) | ipv4->FragmentOffset); + Stream_Write_UINT8(s, ipv4->TimeToLive); + Stream_Write_UINT8(s, ipv4->Protocol); + Stream_Write_UINT16(s, ipv4->HeaderChecksum); + Stream_Write_UINT32_BE(s, ipv4->SourceAddress); + Stream_Write_UINT32_BE(s, ipv4->DestinationAddress); + ipv4->HeaderChecksum = IPv4Checksum((BYTE*)buffer, 20); + Stream_Rewind(s, 10); + Stream_Write_UINT16(s, ipv4->HeaderChecksum); + + if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1) + ret = FALSE; + + return ret; +} + +static BOOL WLog_PacketMessage_Write_TcpHeader(wPcap* pcap, wTcpHeader* tcp) +{ + wStream* s = NULL; + wStream sbuffer = { 0 }; + BYTE buffer[20] = { 0 }; + BOOL ret = TRUE; + + if (!pcap || !pcap->fp || !tcp) + return FALSE; + + s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + if (!s) + return FALSE; + Stream_Write_UINT16_BE(s, tcp->SourcePort); + Stream_Write_UINT16_BE(s, tcp->DestinationPort); + Stream_Write_UINT32_BE(s, tcp->SequenceNumber); + Stream_Write_UINT32_BE(s, tcp->AcknowledgementNumber); + Stream_Write_UINT8(s, (tcp->Offset << 4) | tcp->Reserved); + Stream_Write_UINT8(s, tcp->TcpFlags); + Stream_Write_UINT16_BE(s, tcp->Window); + Stream_Write_UINT16_BE(s, tcp->Checksum); + Stream_Write_UINT16_BE(s, tcp->UrgentPointer); + + if (pcap->fp) + { + if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1) + ret = FALSE; + } + + return ret; +} + +static UINT32 g_InboundSequenceNumber = 0; +static UINT32 g_OutboundSequenceNumber = 0; + +BOOL WLog_PacketMessage_Write(wPcap* pcap, void* data, size_t length, DWORD flags) +{ + wTcpHeader tcp; + wIPv4Header ipv4; + struct timeval tp; + wPcapRecord record; + wEthernetHeader ethernet; + ethernet.Type = 0x0800; + + if (!pcap || !pcap->fp) + return FALSE; + + if (flags & WLOG_PACKET_OUTBOUND) + { + /* 00:15:5D:01:64:04 */ + ethernet.Source[0] = 0x00; + ethernet.Source[1] = 0x15; + ethernet.Source[2] = 0x5D; + ethernet.Source[3] = 0x01; + ethernet.Source[4] = 0x64; + ethernet.Source[5] = 0x04; + /* 00:15:5D:01:64:01 */ + ethernet.Destination[0] = 0x00; + ethernet.Destination[1] = 0x15; + ethernet.Destination[2] = 0x5D; + ethernet.Destination[3] = 0x01; + ethernet.Destination[4] = 0x64; + ethernet.Destination[5] = 0x01; + } + else + { + /* 00:15:5D:01:64:01 */ + ethernet.Source[0] = 0x00; + ethernet.Source[1] = 0x15; + ethernet.Source[2] = 0x5D; + ethernet.Source[3] = 0x01; + ethernet.Source[4] = 0x64; + ethernet.Source[5] = 0x01; + /* 00:15:5D:01:64:04 */ + ethernet.Destination[0] = 0x00; + ethernet.Destination[1] = 0x15; + ethernet.Destination[2] = 0x5D; + ethernet.Destination[3] = 0x01; + ethernet.Destination[4] = 0x64; + ethernet.Destination[5] = 0x04; + } + + ipv4.Version = 4; + ipv4.InternetHeaderLength = 5; + ipv4.TypeOfService = 0; + ipv4.TotalLength = (UINT16)(length + 20 + 20); + ipv4.Identification = 0; + ipv4.InternetProtocolFlags = 0x02; + ipv4.FragmentOffset = 0; + ipv4.TimeToLive = 128; + ipv4.Protocol = 6; /* TCP */ + ipv4.HeaderChecksum = 0; + + if (flags & WLOG_PACKET_OUTBOUND) + { + ipv4.SourceAddress = 0xC0A80196; /* 192.168.1.150 */ + ipv4.DestinationAddress = 0x4A7D64C8; /* 74.125.100.200 */ + } + else + { + ipv4.SourceAddress = 0x4A7D64C8; /* 74.125.100.200 */ + ipv4.DestinationAddress = 0xC0A80196; /* 192.168.1.150 */ + } + + tcp.SourcePort = 3389; + tcp.DestinationPort = 3389; + + if (flags & WLOG_PACKET_OUTBOUND) + { + tcp.SequenceNumber = g_OutboundSequenceNumber; + tcp.AcknowledgementNumber = g_InboundSequenceNumber; + g_OutboundSequenceNumber += length; + } + else + { + tcp.SequenceNumber = g_InboundSequenceNumber; + tcp.AcknowledgementNumber = g_OutboundSequenceNumber; + g_InboundSequenceNumber += length; + } + + tcp.Offset = 5; + tcp.Reserved = 0; + tcp.TcpFlags = 0x0018; + tcp.Window = 0x7FFF; + tcp.Checksum = 0; + tcp.UrgentPointer = 0; + record.data = data; + record.length = length; + const size_t offset = 14 + 20 + 20; + WINPR_ASSERT(record.length <= UINT32_MAX - offset); + record.header.incl_len = (UINT32)record.length + offset; + record.header.orig_len = (UINT32)record.length + offset; + record.next = NULL; + gettimeofday(&tp, 0); + record.header.ts_sec = tp.tv_sec; + record.header.ts_usec = tp.tv_usec; + if (!Pcap_Write_RecordHeader(pcap, &record.header) || + !WLog_PacketMessage_Write_EthernetHeader(pcap, ðernet) || + !WLog_PacketMessage_Write_IPv4Header(pcap, &ipv4) || + !WLog_PacketMessage_Write_TcpHeader(pcap, &tcp) || !Pcap_Write_RecordContent(pcap, &record)) + return FALSE; + fflush(pcap->fp); + return TRUE; +} diff --git a/winpr/libwinpr/utils/wlog/PacketMessage.h b/winpr/libwinpr/utils/wlog/PacketMessage.h new file mode 100644 index 0000000..088ee8f --- /dev/null +++ b/winpr/libwinpr/utils/wlog/PacketMessage.h @@ -0,0 +1,111 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H +#define WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H + +#include "wlog.h" + +#define PCAP_MAGIC_NUMBER 0xA1B2C3D4 + +typedef struct +{ + UINT32 magic_number; /* magic number */ + UINT16 version_major; /* major version number */ + UINT16 version_minor; /* minor version number */ + INT32 thiszone; /* GMT to local correction */ + UINT32 sigfigs; /* accuracy of timestamps */ + UINT32 snaplen; /* max length of captured packets, in octets */ + UINT32 network; /* data link type */ +} wPcapHeader; + +typedef struct +{ + UINT32 ts_sec; /* timestamp seconds */ + UINT32 ts_usec; /* timestamp microseconds */ + UINT32 incl_len; /* number of octets of packet saved in file */ + UINT32 orig_len; /* actual length of packet */ +} wPcapRecordHeader; + +typedef struct s_wPcapRecort +{ + wPcapRecordHeader header; + void* data; + size_t length; + struct s_wPcapRecort* next; +} wPcapRecord; + +typedef struct +{ + FILE* fp; + char* name; + BOOL write; + SSIZE_T file_size; + size_t record_count; + wPcapHeader header; + wPcapRecord* head; + wPcapRecord* tail; + wPcapRecord* record; +} wPcap; + +wPcap* Pcap_Open(char* name, BOOL write); +void Pcap_Close(wPcap* pcap); + +void Pcap_Flush(wPcap* pcap); + +typedef struct +{ + BYTE Destination[6]; + BYTE Source[6]; + UINT16 Type; +} wEthernetHeader; + +typedef struct +{ + BYTE Version; + BYTE InternetHeaderLength; + BYTE TypeOfService; + UINT16 TotalLength; + UINT16 Identification; + BYTE InternetProtocolFlags; + UINT16 FragmentOffset; + BYTE TimeToLive; + BYTE Protocol; + UINT16 HeaderChecksum; + UINT32 SourceAddress; + UINT32 DestinationAddress; +} wIPv4Header; + +typedef struct +{ + UINT16 SourcePort; + UINT16 DestinationPort; + UINT32 SequenceNumber; + UINT32 AcknowledgementNumber; + BYTE Offset; + BYTE Reserved; + BYTE TcpFlags; + UINT16 Window; + UINT16 Checksum; + UINT16 UrgentPointer; +} wTcpHeader; + +BOOL WLog_PacketMessage_Write(wPcap* pcap, void* data, size_t length, DWORD flags); + +#endif /* WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/SyslogAppender.c b/winpr/libwinpr/utils/wlog/SyslogAppender.c new file mode 100644 index 0000000..73baade --- /dev/null +++ b/winpr/libwinpr/utils/wlog/SyslogAppender.c @@ -0,0 +1,137 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "SyslogAppender.h" +#include + +typedef struct +{ + WLOG_APPENDER_COMMON(); +} wLogSyslogAppender; + +static int getSyslogLevel(DWORD level) +{ + switch (level) + { + case WLOG_TRACE: + case WLOG_DEBUG: + return LOG_DEBUG; + case WLOG_INFO: + return LOG_INFO; + case WLOG_WARN: + return LOG_WARNING; + case WLOG_ERROR: + return LOG_ERR; + case WLOG_FATAL: + return LOG_CRIT; + case WLOG_OFF: + default: + return -1; + } +} + +static BOOL WLog_SyslogAppender_Open(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_SyslogAppender_Close(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_SyslogAppender_WriteMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int syslogLevel = 0; + + if (!log || !appender || !message) + return FALSE; + + syslogLevel = getSyslogLevel(message->Level); + if (syslogLevel >= 0) + syslog(syslogLevel, "%s", message->TextString); + + return TRUE; +} + +static BOOL WLog_SyslogAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int syslogLevel = 0; + + if (!log || !appender || !message) + return FALSE; + + syslogLevel = getSyslogLevel(message->Level); + if (syslogLevel >= 0) + syslog(syslogLevel, "skipped data message of %" PRIuz " bytes", message->Length); + + return TRUE; +} + +static BOOL WLog_SyslogAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + int syslogLevel = 0; + + if (!log || !appender || !message) + return FALSE; + + syslogLevel = getSyslogLevel(message->Level); + if (syslogLevel >= 0) + syslog(syslogLevel, "skipped image (%" PRIuz "x%" PRIuz "x%" PRIuz ")", message->ImageWidth, + message->ImageHeight, message->ImageBpp); + + return TRUE; +} + +static void WLog_SyslogAppender_Free(wLogAppender* appender) +{ + free(appender); +} + +wLogAppender* WLog_SyslogAppender_New(wLog* log) +{ + wLogSyslogAppender* appender = NULL; + + appender = (wLogSyslogAppender*)calloc(1, sizeof(wLogSyslogAppender)); + if (!appender) + return NULL; + + appender->Type = WLOG_APPENDER_SYSLOG; + + appender->Open = WLog_SyslogAppender_Open; + appender->Close = WLog_SyslogAppender_Close; + appender->WriteMessage = WLog_SyslogAppender_WriteMessage; + appender->WriteDataMessage = WLog_SyslogAppender_WriteDataMessage; + appender->WriteImageMessage = WLog_SyslogAppender_WriteImageMessage; + appender->Free = WLog_SyslogAppender_Free; + + return (wLogAppender*)appender; +} diff --git a/winpr/libwinpr/utils/wlog/SyslogAppender.h b/winpr/libwinpr/utils/wlog/SyslogAppender.h new file mode 100644 index 0000000..bbb30d5 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/SyslogAppender.h @@ -0,0 +1,32 @@ +/** + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ +#define WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_SyslogAppender_New(wLog* log); + +#endif /* WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ */ diff --git a/winpr/libwinpr/utils/wlog/UdpAppender.c b/winpr/libwinpr/utils/wlog/UdpAppender.c new file mode 100644 index 0000000..19501dc --- /dev/null +++ b/winpr/libwinpr/utils/wlog/UdpAppender.c @@ -0,0 +1,222 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "wlog.h" + +typedef struct +{ + WLOG_APPENDER_COMMON(); + char* host; + struct sockaddr targetAddr; + int targetAddrLen; + SOCKET sock; +} wLogUdpAppender; + +static BOOL WLog_UdpAppender_Open(wLog* log, wLogAppender* appender) +{ + wLogUdpAppender* udpAppender = NULL; + char addressString[256] = { 0 }; + struct addrinfo hints = { 0 }; + struct addrinfo* result = { 0 }; + int status = 0; + size_t addrLen = 0; + char* colonPos = NULL; + + if (!appender) + return FALSE; + + udpAppender = (wLogUdpAppender*)appender; + + if (udpAppender->targetAddrLen) /* already opened */ + return TRUE; + + colonPos = strchr(udpAppender->host, ':'); + + if (!colonPos) + return FALSE; + + addrLen = (colonPos - udpAppender->host); + memcpy(addressString, udpAppender->host, addrLen); + addressString[addrLen] = '\0'; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + status = getaddrinfo(addressString, colonPos + 1, &hints, &result); + + if (status != 0) + return FALSE; + + if (result->ai_addrlen > sizeof(udpAppender->targetAddr)) + { + freeaddrinfo(result); + return FALSE; + } + + memcpy(&udpAppender->targetAddr, result->ai_addr, result->ai_addrlen); + udpAppender->targetAddrLen = (int)result->ai_addrlen; + freeaddrinfo(result); + return TRUE; +} + +static BOOL WLog_UdpAppender_Close(wLog* log, wLogAppender* appender) +{ + if (!log || !appender) + return FALSE; + + return TRUE; +} + +static BOOL WLog_UdpAppender_WriteMessage(wLog* log, wLogAppender* appender, wLogMessage* message) +{ + char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 }; + wLogUdpAppender* udpAppender = NULL; + + if (!log || !appender || !message) + return FALSE; + + udpAppender = (wLogUdpAppender*)appender; + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + _sendto(udpAppender->sock, message->PrefixString, (int)strnlen(message->PrefixString, INT_MAX), + 0, &udpAppender->targetAddr, udpAppender->targetAddrLen); + _sendto(udpAppender->sock, message->TextString, (int)strnlen(message->TextString, INT_MAX), 0, + &udpAppender->targetAddr, udpAppender->targetAddrLen); + _sendto(udpAppender->sock, "\n", 1, 0, &udpAppender->targetAddr, udpAppender->targetAddrLen); + return TRUE; +} + +static BOOL WLog_UdpAppender_WriteDataMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_UdpAppender_WriteImageMessage(wLog* log, wLogAppender* appender, + wLogMessage* message) +{ + if (!log || !appender || !message) + return FALSE; + + return TRUE; +} + +static BOOL WLog_UdpAppender_Set(wLogAppender* appender, const char* setting, void* value) +{ + const char target[] = "target"; + wLogUdpAppender* udpAppender = (wLogUdpAppender*)appender; + + /* Just check the value string is not empty */ + if (!value || (strnlen(value, 2) == 0)) + return FALSE; + + if (strncmp(target, setting, sizeof(target))) + return FALSE; + + udpAppender->targetAddrLen = 0; + + if (udpAppender->host) + free(udpAppender->host); + + udpAppender->host = _strdup((const char*)value); + return (udpAppender->host != NULL) && WLog_UdpAppender_Open(NULL, appender); +} + +static void WLog_UdpAppender_Free(wLogAppender* appender) +{ + wLogUdpAppender* udpAppender = NULL; + + if (appender) + { + udpAppender = (wLogUdpAppender*)appender; + + if (udpAppender->sock != INVALID_SOCKET) + { + closesocket(udpAppender->sock); + udpAppender->sock = INVALID_SOCKET; + } + + free(udpAppender->host); + free(udpAppender); + } +} + +wLogAppender* WLog_UdpAppender_New(wLog* log) +{ + wLogUdpAppender* appender = NULL; + DWORD nSize = 0; + LPCSTR name = NULL; + appender = (wLogUdpAppender*)calloc(1, sizeof(wLogUdpAppender)); + + if (!appender) + return NULL; + + appender->Type = WLOG_APPENDER_UDP; + appender->Open = WLog_UdpAppender_Open; + appender->Close = WLog_UdpAppender_Close; + appender->WriteMessage = WLog_UdpAppender_WriteMessage; + appender->WriteDataMessage = WLog_UdpAppender_WriteDataMessage; + appender->WriteImageMessage = WLog_UdpAppender_WriteImageMessage; + appender->Free = WLog_UdpAppender_Free; + appender->Set = WLog_UdpAppender_Set; + appender->sock = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (appender->sock == INVALID_SOCKET) + goto error_sock; + + name = "WLOG_UDP_TARGET"; + nSize = GetEnvironmentVariableA(name, NULL, 0); + + if (nSize) + { + appender->host = (LPSTR)malloc(nSize); + + if (!appender->host) + goto error_open; + + if (GetEnvironmentVariableA(name, appender->host, nSize) != nSize - 1) + goto error_open; + + if (!WLog_UdpAppender_Open(log, (wLogAppender*)appender)) + goto error_open; + } + else + { + appender->host = _strdup("127.0.0.1:20000"); + + if (!appender->host) + goto error_open; + } + + return (wLogAppender*)appender; +error_open: + free(appender->host); + closesocket(appender->sock); +error_sock: + free(appender); + return NULL; +} diff --git a/winpr/libwinpr/utils/wlog/UdpAppender.h b/winpr/libwinpr/utils/wlog/UdpAppender.h new file mode 100644 index 0000000..eda98b9 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/UdpAppender.h @@ -0,0 +1,34 @@ +/** + * Copyright © 2015 Thincast Technologies GmbH + * Copyright © 2015 David FORT + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ +#define WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ + +#include + +#include "wlog.h" + +WINPR_ATTR_MALLOC(WLog_Appender_Free, 2) +wLogAppender* WLog_UdpAppender_New(wLog* log); + +#endif /* WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ */ diff --git a/winpr/libwinpr/utils/wlog/wlog.c b/winpr/libwinpr/utils/wlog/wlog.c new file mode 100644 index 0000000..4f064ff --- /dev/null +++ b/winpr/libwinpr/utils/wlog/wlog.c @@ -0,0 +1,1071 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(ANDROID) +#include +#include "../log.h" +#endif + +#include "wlog.h" + +typedef struct +{ + DWORD Level; + LPSTR* Names; + size_t NameCount; +} wLogFilter; + +#define WLOG_FILTER_NOT_FILTERED -1 +#define WLOG_FILTER_NOT_INITIALIZED -2 +/** + * References for general logging concepts: + * + * Short introduction to log4j: + * http://logging.apache.org/log4j/1.2/manual.html + * + * logging - Logging facility for Python: + * http://docs.python.org/2/library/logging.html + */ + +LPCSTR WLOG_LEVELS[7] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" }; + +static INIT_ONCE _WLogInitialized = INIT_ONCE_STATIC_INIT; +static DWORD g_FilterCount = 0; +static wLogFilter* g_Filters = NULL; +static wLog* g_RootLog = NULL; + +static wLog* WLog_New(LPCSTR name, wLog* rootLogger); +static void WLog_Free(wLog* log); +static LONG WLog_GetFilterLogLevel(wLog* log); +static int WLog_ParseLogLevel(LPCSTR level); +static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name); +static BOOL WLog_ParseFilters(wLog* root); +static wLog* WLog_Get_int(wLog* root, LPCSTR name); + +#if !defined(_WIN32) +static void WLog_Uninit_(void) __attribute__((destructor)); +#endif + +static void WLog_Uninit_(void) +{ + wLog* child = NULL; + wLog* root = g_RootLog; + + if (!root) + return; + + for (DWORD index = 0; index < root->ChildrenCount; index++) + { + child = root->Children[index]; + WLog_Free(child); + } + + WLog_Free(root); + g_RootLog = NULL; +} + +static void WLog_Lock(wLog* log) +{ + WINPR_ASSERT(log); + EnterCriticalSection(&log->lock); +} + +static void WLog_Unlock(wLog* log) +{ + WINPR_ASSERT(log); + LeaveCriticalSection(&log->lock); +} + +static BOOL CALLBACK WLog_InitializeRoot(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +{ + char* env = NULL; + DWORD nSize = 0; + DWORD logAppenderType = 0; + LPCSTR appender = "WLOG_APPENDER"; + + WINPR_UNUSED(InitOnce); + WINPR_UNUSED(Parameter); + WINPR_UNUSED(Context); + + if (!(g_RootLog = WLog_New("", NULL))) + return FALSE; + + g_RootLog->IsRoot = TRUE; + logAppenderType = WLOG_APPENDER_CONSOLE; + nSize = GetEnvironmentVariableA(appender, NULL, 0); + + if (nSize) + { + env = (LPSTR)malloc(nSize); + + if (!env) + goto fail; + + if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1) + { + fprintf(stderr, "%s environment variable modified in my back", appender); + free(env); + goto fail; + } + + if (_stricmp(env, "CONSOLE") == 0) + logAppenderType = WLOG_APPENDER_CONSOLE; + else if (_stricmp(env, "FILE") == 0) + logAppenderType = WLOG_APPENDER_FILE; + else if (_stricmp(env, "BINARY") == 0) + logAppenderType = WLOG_APPENDER_BINARY; + +#ifdef WINPR_HAVE_SYSLOG_H + else if (_stricmp(env, "SYSLOG") == 0) + logAppenderType = WLOG_APPENDER_SYSLOG; + +#endif /* WINPR_HAVE_SYSLOG_H */ +#ifdef WINPR_HAVE_JOURNALD_H + else if (_stricmp(env, "JOURNALD") == 0) + logAppenderType = WLOG_APPENDER_JOURNALD; + +#endif + else if (_stricmp(env, "UDP") == 0) + logAppenderType = WLOG_APPENDER_UDP; + + free(env); + } + + if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType)) + goto fail; + + if (!WLog_ParseFilters(g_RootLog)) + goto fail; + + atexit(WLog_Uninit_); + + return TRUE; +fail: + WLog_Uninit_(); + return FALSE; +} + +static BOOL log_recursion(LPCSTR file, LPCSTR fkt, size_t line) +{ + BOOL status = FALSE; + char** msg = NULL; + size_t used = 0; + void* bt = winpr_backtrace(20); +#if defined(ANDROID) + LPCSTR tag = WINPR_TAG("utils.wlog"); +#endif + + if (!bt) + return FALSE; + + msg = winpr_backtrace_symbols(bt, &used); + + if (!msg) + goto out; + +#if defined(ANDROID) + + if (__android_log_print(ANDROID_LOG_FATAL, tag, "Recursion detected!!!") < 0) + goto out; + + if (__android_log_print(ANDROID_LOG_FATAL, tag, "Check %s [%s:%zu]", fkt, file, line) < 0) + goto out; + + for (size_t i = 0; i < used; i++) + if (__android_log_print(ANDROID_LOG_FATAL, tag, "%zu: %s", i, msg[i]) < 0) + goto out; + +#else + + if (fprintf(stderr, "[%s]: Recursion detected!\n", fkt) < 0) + goto out; + + if (fprintf(stderr, "[%s]: Check %s:%" PRIuz "\n", fkt, file, line) < 0) + goto out; + + for (size_t i = 0; i < used; i++) + if (fprintf(stderr, "%s: %" PRIuz ": %s\n", fkt, i, msg[i]) < 0) + goto out; + +#endif + status = TRUE; +out: + free(msg); + winpr_backtrace_free(bt); + return status; +} + +static BOOL WLog_Write(wLog* log, wLogMessage* message) +{ + BOOL status = FALSE; + wLogAppender* appender = NULL; + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->active) + if (!WLog_OpenAppender(log)) + return FALSE; + + EnterCriticalSection(&appender->lock); + + if (appender->WriteMessage) + { + if (appender->recursive) + status = log_recursion(message->FileName, message->FunctionName, message->LineNumber); + else + { + appender->recursive = TRUE; + status = appender->WriteMessage(log, appender, message); + appender->recursive = FALSE; + } + } + + LeaveCriticalSection(&appender->lock); + return status; +} + +static BOOL WLog_WriteData(wLog* log, wLogMessage* message) +{ + BOOL status = 0; + wLogAppender* appender = NULL; + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->active) + if (!WLog_OpenAppender(log)) + return FALSE; + + if (!appender->WriteDataMessage) + return FALSE; + + EnterCriticalSection(&appender->lock); + + if (appender->recursive) + status = log_recursion(message->FileName, message->FunctionName, message->LineNumber); + else + { + appender->recursive = TRUE; + status = appender->WriteDataMessage(log, appender, message); + appender->recursive = FALSE; + } + + LeaveCriticalSection(&appender->lock); + return status; +} + +static BOOL WLog_WriteImage(wLog* log, wLogMessage* message) +{ + BOOL status = 0; + wLogAppender* appender = NULL; + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->active) + if (!WLog_OpenAppender(log)) + return FALSE; + + if (!appender->WriteImageMessage) + return FALSE; + + EnterCriticalSection(&appender->lock); + + if (appender->recursive) + status = log_recursion(message->FileName, message->FunctionName, message->LineNumber); + else + { + appender->recursive = TRUE; + status = appender->WriteImageMessage(log, appender, message); + appender->recursive = FALSE; + } + + LeaveCriticalSection(&appender->lock); + return status; +} + +static BOOL WLog_WritePacket(wLog* log, wLogMessage* message) +{ + BOOL status = 0; + wLogAppender* appender = NULL; + appender = WLog_GetLogAppender(log); + + if (!appender) + return FALSE; + + if (!appender->active) + if (!WLog_OpenAppender(log)) + return FALSE; + + if (!appender->WritePacketMessage) + return FALSE; + + EnterCriticalSection(&appender->lock); + + if (appender->recursive) + status = log_recursion(message->FileName, message->FunctionName, message->LineNumber); + else + { + appender->recursive = TRUE; + status = appender->WritePacketMessage(log, appender, message); + appender->recursive = FALSE; + } + + LeaveCriticalSection(&appender->lock); + return status; +} + +BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level, size_t line, const char* file, + const char* function, va_list args) +{ + BOOL status = FALSE; + wLogMessage message = { 0 }; + message.Type = type; + message.Level = level; + message.LineNumber = line; + message.FileName = file; + message.FunctionName = function; + + switch (type) + { + case WLOG_MESSAGE_TEXT: + message.FormatString = va_arg(args, const char*); + + if (!strchr(message.FormatString, '%')) + { + message.TextString = message.FormatString; + status = WLog_Write(log, &message); + } + else + { + char formattedLogMessage[WLOG_MAX_STRING_SIZE] = { 0 }; + + if (vsnprintf(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message.FormatString, + args) < 0) + return FALSE; + + message.TextString = formattedLogMessage; + status = WLog_Write(log, &message); + } + + break; + + case WLOG_MESSAGE_DATA: + message.Data = va_arg(args, void*); + message.Length = va_arg(args, size_t); + status = WLog_WriteData(log, &message); + break; + + case WLOG_MESSAGE_IMAGE: + message.ImageData = va_arg(args, void*); + message.ImageWidth = va_arg(args, size_t); + message.ImageHeight = va_arg(args, size_t); + message.ImageBpp = va_arg(args, size_t); + status = WLog_WriteImage(log, &message); + break; + + case WLOG_MESSAGE_PACKET: + message.PacketData = va_arg(args, void*); + message.PacketLength = va_arg(args, size_t); + message.PacketFlags = va_arg(args, unsigned); + status = WLog_WritePacket(log, &message); + break; + + default: + break; + } + + return status; +} + +BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level, size_t line, const char* file, + const char* function, ...) +{ + BOOL status = 0; + va_list args; + va_start(args, function); + status = WLog_PrintMessageVA(log, type, level, line, file, function, args); + va_end(args); + return status; +} + +DWORD WLog_GetLogLevel(wLog* log) +{ + if (!log) + return WLOG_OFF; + + if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED) + log->FilterLevel = WLog_GetFilterLogLevel(log); + + if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED) + return (DWORD)log->FilterLevel; + else if (log->Level == WLOG_LEVEL_INHERIT) + log->Level = WLog_GetLogLevel(log->Parent); + + return log->Level; +} + +BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level) +{ + DWORD level = 0; + + if (!_log) + return FALSE; + + level = WLog_GetLogLevel(_log); + + if (level == WLOG_OFF) + return FALSE; + + return _log_level >= level; +} + +BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level) +{ + int lvl = 0; + + if (!log || !level) + return FALSE; + + lvl = WLog_ParseLogLevel(level); + + if (lvl < 0) + return FALSE; + + return WLog_SetLogLevel(log, (DWORD)lvl); +} + +static BOOL WLog_reset_log_filters(wLog* log) +{ + if (!log) + return FALSE; + + log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED; + + for (DWORD x = 0; x < log->ChildrenCount; x++) + { + wLog* child = log->Children[x]; + + if (!WLog_reset_log_filters(child)) + return FALSE; + } + + return TRUE; +} + +static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter) +{ + LPSTR p = NULL; + LPCSTR filterStr = NULL; + + if (!filter) + return FALSE; + + DWORD count = 1; + LPCSTR cpp = filter; + + while ((cpp = strchr(cpp, ',')) != NULL) + { + count++; + cpp++; + } + + DWORD pos = g_FilterCount; + DWORD size = g_FilterCount + count; + wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size * sizeof(wLogFilter)); + + if (!tmp) + return FALSE; + + g_Filters = tmp; + LPSTR cp = (LPSTR)_strdup(filter); + + if (!cp) + return FALSE; + + p = cp; + filterStr = cp; + + do + { + p = strchr(p, ','); + + if (p) + *p = '\0'; + + if (pos < size) + { + if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr)) + { + free(cp); + return FALSE; + } + } + else + break; + + if (p) + { + filterStr = p + 1; + p++; + } + } while (p != NULL); + + g_FilterCount = size; + free(cp); + return WLog_reset_log_filters(root); +} + +BOOL WLog_AddStringLogFilters(LPCSTR filter) +{ + /* Ensure logger is initialized */ + wLog* root = WLog_GetRoot(); + return WLog_AddStringLogFilters_int(root, filter); +} + +static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel) +{ + if (!log) + return FALSE; + + if (log->inherit) + { + log->Level = logLevel; + + for (DWORD x = 0; x < log->ChildrenCount; x++) + { + wLog* child = log->Children[x]; + + if (!WLog_UpdateInheritLevel(child, logLevel)) + return FALSE; + } + } + + return TRUE; +} + +BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel) +{ + if (!log) + return FALSE; + + if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT)) + logLevel = WLOG_OFF; + + log->Level = logLevel; + log->inherit = (logLevel == WLOG_LEVEL_INHERIT) ? TRUE : FALSE; + + for (DWORD x = 0; x < log->ChildrenCount; x++) + { + wLog* child = log->Children[x]; + + if (!WLog_UpdateInheritLevel(child, logLevel)) + return FALSE; + } + + return WLog_reset_log_filters(log); +} + +int WLog_ParseLogLevel(LPCSTR level) +{ + int iLevel = -1; + + if (!level) + return -1; + + if (_stricmp(level, "TRACE") == 0) + iLevel = WLOG_TRACE; + else if (_stricmp(level, "DEBUG") == 0) + iLevel = WLOG_DEBUG; + else if (_stricmp(level, "INFO") == 0) + iLevel = WLOG_INFO; + else if (_stricmp(level, "WARN") == 0) + iLevel = WLOG_WARN; + else if (_stricmp(level, "ERROR") == 0) + iLevel = WLOG_ERROR; + else if (_stricmp(level, "FATAL") == 0) + iLevel = WLOG_FATAL; + else if (_stricmp(level, "OFF") == 0) + iLevel = WLOG_OFF; + + return iLevel; +} + +BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name) +{ + const char* pc = NULL; + char* p = NULL; + char* q = NULL; + size_t count = 0; + LPSTR names = NULL; + int iLevel = 0; + count = 1; + + WINPR_UNUSED(root); + + if (!name) + return FALSE; + + pc = name; + + if (pc) + { + while ((pc = strchr(pc, '.')) != NULL) + { + count++; + pc++; + } + } + + names = _strdup(name); + + if (!names) + return FALSE; + + filter->NameCount = count; + filter->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR)); + + if (!filter->Names) + { + free(names); + filter->NameCount = 0; + return FALSE; + } + + filter->Names[count] = NULL; + count = 0; + p = (char*)names; + filter->Names[count++] = p; + q = strrchr(p, ':'); + + if (!q) + { + free(names); + free(filter->Names); + filter->Names = NULL; + filter->NameCount = 0; + return FALSE; + } + + *q = '\0'; + q++; + iLevel = WLog_ParseLogLevel(q); + + if (iLevel < 0) + { + free(names); + free(filter->Names); + filter->Names = NULL; + filter->NameCount = 0; + return FALSE; + } + + filter->Level = (DWORD)iLevel; + + while ((p = strchr(p, '.')) != NULL) + { + if (count < filter->NameCount) + filter->Names[count++] = p + 1; + + *p = '\0'; + p++; + } + + return TRUE; +} + +BOOL WLog_ParseFilters(wLog* root) +{ + LPCSTR filter = "WLOG_FILTER"; + BOOL res = FALSE; + char* env = NULL; + DWORD nSize = 0; + free(g_Filters); + g_Filters = NULL; + g_FilterCount = 0; + nSize = GetEnvironmentVariableA(filter, NULL, 0); + + if (nSize < 1) + return TRUE; + + env = (LPSTR)malloc(nSize); + + if (!env) + return FALSE; + + if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1) + res = WLog_AddStringLogFilters_int(root, env); + + free(env); + return res; +} + +LONG WLog_GetFilterLogLevel(wLog* log) +{ + BOOL match = FALSE; + + if (log->FilterLevel >= 0) + return log->FilterLevel; + + log->FilterLevel = WLOG_FILTER_NOT_FILTERED; + for (DWORD i = 0; i < g_FilterCount; i++) + { + const wLogFilter* filter = &g_Filters[i]; + for (DWORD j = 0; j < filter->NameCount; j++) + { + if (j >= log->NameCount) + break; + + if (_stricmp(filter->Names[j], "*") == 0) + { + match = TRUE; + log->FilterLevel = filter->Level; + break; + } + + if (_stricmp(filter->Names[j], log->Names[j]) != 0) + break; + + if (j == (log->NameCount - 1)) + { + match = log->NameCount == filter->NameCount; + if (match) + log->FilterLevel = filter->Level; + break; + } + } + + if (match) + break; + } + + return log->FilterLevel; +} + +static BOOL WLog_ParseName(wLog* log, LPCSTR name) +{ + const char* cp = name; + char* p = NULL; + size_t count = 1; + LPSTR names = NULL; + + while ((cp = strchr(cp, '.')) != NULL) + { + count++; + cp++; + } + + names = _strdup(name); + + if (!names) + return FALSE; + + log->NameCount = count; + log->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR)); + + if (!log->Names) + { + free(names); + return FALSE; + } + + log->Names[count] = NULL; + count = 0; + p = (char*)names; + log->Names[count++] = p; + + while ((p = strchr(p, '.')) != NULL) + { + if (count < log->NameCount) + log->Names[count++] = p + 1; + + *p = '\0'; + p++; + } + + return TRUE; +} + +wLog* WLog_New(LPCSTR name, wLog* rootLogger) +{ + wLog* log = NULL; + char* env = NULL; + DWORD nSize = 0; + int iLevel = 0; + log = (wLog*)calloc(1, sizeof(wLog)); + + if (!log) + return NULL; + + log->Name = _strdup(name); + + if (!log->Name) + goto out_fail; + + if (!WLog_ParseName(log, name)) + goto out_fail; + + log->Parent = rootLogger; + log->ChildrenCount = 0; + log->ChildrenSize = 16; + log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED; + + if (!(log->Children = (wLog**)calloc(log->ChildrenSize, sizeof(wLog*)))) + goto out_fail; + + log->Appender = NULL; + + if (rootLogger) + { + log->Level = WLOG_LEVEL_INHERIT; + log->inherit = TRUE; + } + else + { + LPCSTR level = "WLOG_LEVEL"; + log->Level = WLOG_INFO; + nSize = GetEnvironmentVariableA(level, NULL, 0); + + if (nSize) + { + env = (LPSTR)malloc(nSize); + + if (!env) + goto out_fail; + + if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1) + { + fprintf(stderr, "%s environment variable changed in my back !\n", level); + free(env); + goto out_fail; + } + + iLevel = WLog_ParseLogLevel(env); + free(env); + + if (iLevel >= 0) + { + if (!WLog_SetLogLevel(log, (DWORD)iLevel)) + goto out_fail; + } + } + } + + iLevel = WLog_GetFilterLogLevel(log); + + if (iLevel >= 0) + { + if (!WLog_SetLogLevel(log, (DWORD)iLevel)) + goto out_fail; + } + + InitializeCriticalSectionAndSpinCount(&log->lock, 4000); + + return log; +out_fail: + free(log->Children); + free(log->Name); + free(log); + return NULL; +} + +void WLog_Free(wLog* log) +{ + if (log) + { + if (log->Appender) + { + WLog_Appender_Free(log, log->Appender); + log->Appender = NULL; + } + + free(log->Name); + free(log->Names[0]); + free(log->Names); + free(log->Children); + DeleteCriticalSection(&log->lock); + free(log); + } +} + +wLog* WLog_GetRoot(void) +{ + if (!InitOnceExecuteOnce(&_WLogInitialized, WLog_InitializeRoot, NULL, NULL)) + return NULL; + + return g_RootLog; +} + +static BOOL WLog_AddChild(wLog* parent, wLog* child) +{ + BOOL status = FALSE; + + WLog_Lock(parent); + + if (parent->ChildrenCount >= parent->ChildrenSize) + { + wLog** tmp = NULL; + parent->ChildrenSize *= 2; + + if (!parent->ChildrenSize) + { + if (parent->Children) + free(parent->Children); + + parent->Children = NULL; + } + else + { + tmp = (wLog**)realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize); + + if (!tmp) + { + if (parent->Children) + free(parent->Children); + + parent->Children = NULL; + goto exit; + } + + parent->Children = tmp; + } + } + + if (!parent->Children) + goto exit; + + parent->Children[parent->ChildrenCount++] = child; + child->Parent = parent; + + WLog_Unlock(parent); + + status = TRUE; +exit: + return status; +} + +static wLog* WLog_FindChild(wLog* root, LPCSTR name) +{ + wLog* child = NULL; + BOOL found = FALSE; + + if (!root) + return NULL; + + WLog_Lock(root); + + for (DWORD index = 0; index < root->ChildrenCount; index++) + { + child = root->Children[index]; + + if (strcmp(child->Name, name) == 0) + { + found = TRUE; + break; + } + } + + WLog_Unlock(root); + + return (found) ? child : NULL; +} + +static wLog* WLog_Get_int(wLog* root, LPCSTR name) +{ + wLog* log = NULL; + + if (!(log = WLog_FindChild(root, name))) + { + if (!root) + return NULL; + + if (!(log = WLog_New(name, root))) + return NULL; + + if (!WLog_AddChild(root, log)) + { + WLog_Free(log); + return NULL; + } + } + + return log; +} + +wLog* WLog_Get(LPCSTR name) +{ + wLog* root = WLog_GetRoot(); + return WLog_Get_int(root, name); +} + +#if defined(WITH_WINPR_DEPRECATED) +BOOL WLog_Init(void) +{ + return WLog_GetRoot() != NULL; +} + +BOOL WLog_Uninit(void) +{ + wLog* root = g_RootLog; + + if (!root) + return FALSE; + + WLog_Lock(root); + + for (DWORD index = 0; index < root->ChildrenCount; index++) + { + wLog* child = root->Children[index]; + WLog_Free(child); + } + + WLog_Unlock(root); + + WLog_Free(root); + g_RootLog = NULL; + + return TRUE; +} +#endif + +BOOL WLog_SetContext(wLog* log, const char* (*fkt)(void*), void* context) +{ + WINPR_ASSERT(log); + + log->custom = fkt; + log->context = context; + return TRUE; +} diff --git a/winpr/libwinpr/utils/wlog/wlog.h b/winpr/libwinpr/utils/wlog/wlog.h new file mode 100644 index 0000000..2d4a41e --- /dev/null +++ b/winpr/libwinpr/utils/wlog/wlog.h @@ -0,0 +1,92 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WLOG_PRIVATE_H +#define WINPR_WLOG_PRIVATE_H + +#include + +#define WLOG_MAX_PREFIX_SIZE 512 +#define WLOG_MAX_STRING_SIZE 8192 + +typedef BOOL (*WLOG_APPENDER_OPEN_FN)(wLog* log, wLogAppender* appender); +typedef BOOL (*WLOG_APPENDER_CLOSE_FN)(wLog* log, wLogAppender* appender); +typedef BOOL (*WLOG_APPENDER_WRITE_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_WRITE_DATA_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_WRITE_IMAGE_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_WRITE_PACKET_MESSAGE_FN)(wLog* log, wLogAppender* appender, + wLogMessage* message); +typedef BOOL (*WLOG_APPENDER_SET)(wLogAppender* appender, const char* setting, void* value); +typedef void (*WLOG_APPENDER_FREE)(wLogAppender* appender); + +#define WLOG_APPENDER_COMMON() \ + DWORD Type; \ + BOOL active; \ + wLogLayout* Layout; \ + CRITICAL_SECTION lock; \ + BOOL recursive; \ + void* TextMessageContext; \ + void* DataMessageContext; \ + void* ImageMessageContext; \ + void* PacketMessageContext; \ + WLOG_APPENDER_OPEN_FN Open; \ + WLOG_APPENDER_CLOSE_FN Close; \ + WLOG_APPENDER_WRITE_MESSAGE_FN WriteMessage; \ + WLOG_APPENDER_WRITE_DATA_MESSAGE_FN WriteDataMessage; \ + WLOG_APPENDER_WRITE_IMAGE_MESSAGE_FN WriteImageMessage; \ + WLOG_APPENDER_WRITE_PACKET_MESSAGE_FN WritePacketMessage; \ + WLOG_APPENDER_FREE Free; \ + WLOG_APPENDER_SET Set + +struct s_wLogAppender +{ + WLOG_APPENDER_COMMON(); +}; + +struct s_wLog +{ + LPSTR Name; + LONG FilterLevel; + DWORD Level; + + BOOL IsRoot; + BOOL inherit; + LPSTR* Names; + size_t NameCount; + wLogAppender* Appender; + + wLog* Parent; + wLog** Children; + DWORD ChildrenCount; + DWORD ChildrenSize; + CRITICAL_SECTION lock; + const char* (*custom)(void*); + void* context; +}; + +extern const char* WLOG_LEVELS[7]; +BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message); + +#include "Layout.h" +#include "Appender.h" + +#endif /* WINPR_WLOG_PRIVATE_H */ diff --git a/winpr/libwinpr/winsock/CMakeLists.txt b/winpr/libwinpr/winsock/CMakeLists.txt new file mode 100644 index 0000000..ef13cd1 --- /dev/null +++ b/winpr/libwinpr/winsock/CMakeLists.txt @@ -0,0 +1,22 @@ +# WinPR: Windows Portable Runtime +# libwinpr-winsock cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(winsock.c) + +if(WIN32) + winpr_library_add_public(ws2_32) +endif() diff --git a/winpr/libwinpr/winsock/ModuleOptions.cmake b/winpr/libwinpr/winsock/ModuleOptions.cmake new file mode 100644 index 0000000..af49b6e --- /dev/null +++ b/winpr/libwinpr/winsock/ModuleOptions.cmake @@ -0,0 +1,8 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "winsock") +set(MINWIN_LONG_NAME "Windows Sockets (Winsock)") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") diff --git a/winpr/libwinpr/winsock/winsock.c b/winpr/libwinpr/winsock/winsock.c new file mode 100644 index 0000000..3ac8e50 --- /dev/null +++ b/winpr/libwinpr/winsock/winsock.c @@ -0,0 +1,1290 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Sockets (Winsock) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +#ifdef WINPR_HAVE_UNISTD_H +#include +#endif +#ifdef WINPR_HAVE_SYS_FILIO_H +#include +#endif +#ifdef WINPR_HAVE_SYS_SOCKIO_H +#include +#endif + +#ifndef _WIN32 +#include +#endif + +#ifdef __APPLE__ +#define WSAIOCTL_IFADDRS +#include +#endif + +/** + * ws2_32.dll: + * + * __WSAFDIsSet + * accept + * bind + * closesocket + * connect + * freeaddrinfo + * FreeAddrInfoEx + * FreeAddrInfoExW + * FreeAddrInfoW + * getaddrinfo + * GetAddrInfoExA + * GetAddrInfoExCancel + * GetAddrInfoExOverlappedResult + * GetAddrInfoExW + * GetAddrInfoW + * gethostbyaddr + * gethostbyname + * gethostname + * GetHostNameW + * getnameinfo + * GetNameInfoW + * getpeername + * getprotobyname + * getprotobynumber + * getservbyname + * getservbyport + * getsockname + * getsockopt + * htonl + * htons + * inet_addr + * inet_ntoa + * inet_ntop + * inet_pton + * InetNtopW + * InetPtonW + * ioctlsocket + * listen + * ntohl + * ntohs + * recv + * recvfrom + * select + * send + * sendto + * SetAddrInfoExA + * SetAddrInfoExW + * setsockopt + * shutdown + * socket + * WahCloseApcHelper + * WahCloseHandleHelper + * WahCloseNotificationHandleHelper + * WahCloseSocketHandle + * WahCloseThread + * WahCompleteRequest + * WahCreateHandleContextTable + * WahCreateNotificationHandle + * WahCreateSocketHandle + * WahDestroyHandleContextTable + * WahDisableNonIFSHandleSupport + * WahEnableNonIFSHandleSupport + * WahEnumerateHandleContexts + * WahInsertHandleContext + * WahNotifyAllProcesses + * WahOpenApcHelper + * WahOpenCurrentThread + * WahOpenHandleHelper + * WahOpenNotificationHandleHelper + * WahQueueUserApc + * WahReferenceContextByHandle + * WahRemoveHandleContext + * WahWaitForNotification + * WahWriteLSPEvent + * WEP + * WPUCompleteOverlappedRequest + * WPUGetProviderPathEx + * WSAAccept + * WSAAddressToStringA + * WSAAddressToStringW + * WSAAdvertiseProvider + * WSAAsyncGetHostByAddr + * WSAAsyncGetHostByName + * WSAAsyncGetProtoByName + * WSAAsyncGetProtoByNumber + * WSAAsyncGetServByName + * WSAAsyncGetServByPort + * WSAAsyncSelect + * WSACancelAsyncRequest + * WSACancelBlockingCall + * WSACleanup + * WSACloseEvent + * WSAConnect + * WSAConnectByList + * WSAConnectByNameA + * WSAConnectByNameW + * WSACreateEvent + * WSADuplicateSocketA + * WSADuplicateSocketW + * WSAEnumNameSpaceProvidersA + * WSAEnumNameSpaceProvidersExA + * WSAEnumNameSpaceProvidersExW + * WSAEnumNameSpaceProvidersW + * WSAEnumNetworkEvents + * WSAEnumProtocolsA + * WSAEnumProtocolsW + * WSAEventSelect + * WSAGetLastError + * WSAGetOverlappedResult + * WSAGetQOSByName + * WSAGetServiceClassInfoA + * WSAGetServiceClassInfoW + * WSAGetServiceClassNameByClassIdA + * WSAGetServiceClassNameByClassIdW + * WSAHtonl + * WSAHtons + * WSAInstallServiceClassA + * WSAInstallServiceClassW + * WSAIoctl + * WSAIsBlocking + * WSAJoinLeaf + * WSALookupServiceBeginA + * WSALookupServiceBeginW + * WSALookupServiceEnd + * WSALookupServiceNextA + * WSALookupServiceNextW + * WSANSPIoctl + * WSANtohl + * WSANtohs + * WSAPoll + * WSAProviderCompleteAsyncCall + * WSAProviderConfigChange + * WSApSetPostRoutine + * WSARecv + * WSARecvDisconnect + * WSARecvFrom + * WSARemoveServiceClass + * WSAResetEvent + * WSASend + * WSASendDisconnect + * WSASendMsg + * WSASendTo + * WSASetBlockingHook + * WSASetEvent + * WSASetLastError + * WSASetServiceA + * WSASetServiceW + * WSASocketA + * WSASocketW + * WSAStartup + * WSAStringToAddressA + * WSAStringToAddressW + * WSAUnadvertiseProvider + * WSAUnhookBlockingHook + * WSAWaitForMultipleEvents + * WSCDeinstallProvider + * WSCDeinstallProviderEx + * WSCEnableNSProvider + * WSCEnumProtocols + * WSCEnumProtocolsEx + * WSCGetApplicationCategory + * WSCGetApplicationCategoryEx + * WSCGetProviderInfo + * WSCGetProviderPath + * WSCInstallNameSpace + * WSCInstallNameSpaceEx + * WSCInstallNameSpaceEx2 + * WSCInstallProvider + * WSCInstallProviderAndChains + * WSCInstallProviderEx + * WSCSetApplicationCategory + * WSCSetApplicationCategoryEx + * WSCSetProviderInfo + * WSCUnInstallNameSpace + * WSCUnInstallNameSpaceEx2 + * WSCUpdateProvider + * WSCUpdateProviderEx + * WSCWriteNameSpaceOrder + * WSCWriteProviderOrder + * WSCWriteProviderOrderEx + */ + +#ifdef _WIN32 + +#if (_WIN32_WINNT < 0x0600) + +PCSTR winpr_inet_ntop(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize) +{ + if (Family == AF_INET) + { + struct sockaddr_in in = { 0 }; + + in.sin_family = AF_INET; + memcpy(&in.sin_addr, pAddr, sizeof(struct in_addr)); + getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in), pStringBuf, StringBufSize, + NULL, 0, NI_NUMERICHOST); + return pStringBuf; + } + else if (Family == AF_INET6) + { + struct sockaddr_in6 in = { 0 }; + + in.sin6_family = AF_INET6; + memcpy(&in.sin6_addr, pAddr, sizeof(struct in_addr6)); + getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6), pStringBuf, StringBufSize, + NULL, 0, NI_NUMERICHOST); + return pStringBuf; + } + + return NULL; +} + +INT winpr_inet_pton(INT Family, PCSTR pszAddrString, PVOID pAddrBuf) +{ + SOCKADDR_STORAGE addr; + int addr_len = sizeof(addr); + + if ((Family != AF_INET) && (Family != AF_INET6)) + return -1; + + if (WSAStringToAddressA((char*)pszAddrString, Family, NULL, (struct sockaddr*)&addr, + &addr_len) != 0) + return 0; + + if (Family == AF_INET) + { + memcpy(pAddrBuf, &((struct sockaddr_in*)&addr)->sin_addr, sizeof(struct in_addr)); + } + else if (Family == AF_INET6) + { + memcpy(pAddrBuf, &((struct sockaddr_in6*)&addr)->sin6_addr, sizeof(struct in6_addr)); + } + + return 1; +} + +#endif /* (_WIN32_WINNT < 0x0600) */ + +#else /* _WIN32 */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData) +{ + WINPR_ASSERT(lpWSAData); + + ZeroMemory(lpWSAData, sizeof(WSADATA)); + lpWSAData->wVersion = wVersionRequired; + lpWSAData->wHighVersion = MAKEWORD(2, 2); + return 0; /* success */ +} + +int WSACleanup(void) +{ + return 0; /* success */ +} + +void WSASetLastError(int iError) +{ + switch (iError) + { + /* Base error codes */ + case WSAEINTR: + errno = EINTR; + break; + + case WSAEBADF: + errno = EBADF; + break; + + case WSAEACCES: + errno = EACCES; + break; + + case WSAEFAULT: + errno = EFAULT; + break; + + case WSAEINVAL: + errno = EINVAL; + break; + + case WSAEMFILE: + errno = EMFILE; + break; + + /* BSD sockets error codes */ + + case WSAEWOULDBLOCK: + errno = EWOULDBLOCK; + break; + + case WSAEINPROGRESS: + errno = EINPROGRESS; + break; + + case WSAEALREADY: + errno = EALREADY; + break; + + case WSAENOTSOCK: + errno = ENOTSOCK; + break; + + case WSAEDESTADDRREQ: + errno = EDESTADDRREQ; + break; + + case WSAEMSGSIZE: + errno = EMSGSIZE; + break; + + case WSAEPROTOTYPE: + errno = EPROTOTYPE; + break; + + case WSAENOPROTOOPT: + errno = ENOPROTOOPT; + break; + + case WSAEPROTONOSUPPORT: + errno = EPROTONOSUPPORT; + break; + + case WSAESOCKTNOSUPPORT: + errno = ESOCKTNOSUPPORT; + break; + + case WSAEOPNOTSUPP: + errno = EOPNOTSUPP; + break; + + case WSAEPFNOSUPPORT: + errno = EPFNOSUPPORT; + break; + + case WSAEAFNOSUPPORT: + errno = EAFNOSUPPORT; + break; + + case WSAEADDRINUSE: + errno = EADDRINUSE; + break; + + case WSAEADDRNOTAVAIL: + errno = EADDRNOTAVAIL; + break; + + case WSAENETDOWN: + errno = ENETDOWN; + break; + + case WSAENETUNREACH: + errno = ENETUNREACH; + break; + + case WSAENETRESET: + errno = ENETRESET; + break; + + case WSAECONNABORTED: + errno = ECONNABORTED; + break; + + case WSAECONNRESET: + errno = ECONNRESET; + break; + + case WSAENOBUFS: + errno = ENOBUFS; + break; + + case WSAEISCONN: + errno = EISCONN; + break; + + case WSAENOTCONN: + errno = ENOTCONN; + break; + + case WSAESHUTDOWN: + errno = ESHUTDOWN; + break; + + case WSAETOOMANYREFS: + errno = ETOOMANYREFS; + break; + + case WSAETIMEDOUT: + errno = ETIMEDOUT; + break; + + case WSAECONNREFUSED: + errno = ECONNREFUSED; + break; + + case WSAELOOP: + errno = ELOOP; + break; + + case WSAENAMETOOLONG: + errno = ENAMETOOLONG; + break; + + case WSAEHOSTDOWN: + errno = EHOSTDOWN; + break; + + case WSAEHOSTUNREACH: + errno = EHOSTUNREACH; + break; + + case WSAENOTEMPTY: + errno = ENOTEMPTY; + break; +#ifdef EPROCLIM + + case WSAEPROCLIM: + errno = EPROCLIM; + break; +#endif + + case WSAEUSERS: + errno = EUSERS; + break; + + case WSAEDQUOT: + errno = EDQUOT; + break; + + case WSAESTALE: + errno = ESTALE; + break; + + case WSAEREMOTE: + errno = EREMOTE; + break; + } +} + +int WSAGetLastError(void) +{ + int iError = 0; + + switch (errno) + { + /* Base error codes */ + case EINTR: + iError = WSAEINTR; + break; + + case EBADF: + iError = WSAEBADF; + break; + + case EACCES: + iError = WSAEACCES; + break; + + case EFAULT: + iError = WSAEFAULT; + break; + + case EINVAL: + iError = WSAEINVAL; + break; + + case EMFILE: + iError = WSAEMFILE; + break; + + /* BSD sockets error codes */ + + case EWOULDBLOCK: + iError = WSAEWOULDBLOCK; + break; + + case EINPROGRESS: + iError = WSAEINPROGRESS; + break; + + case EALREADY: + iError = WSAEALREADY; + break; + + case ENOTSOCK: + iError = WSAENOTSOCK; + break; + + case EDESTADDRREQ: + iError = WSAEDESTADDRREQ; + break; + + case EMSGSIZE: + iError = WSAEMSGSIZE; + break; + + case EPROTOTYPE: + iError = WSAEPROTOTYPE; + break; + + case ENOPROTOOPT: + iError = WSAENOPROTOOPT; + break; + + case EPROTONOSUPPORT: + iError = WSAEPROTONOSUPPORT; + break; + + case ESOCKTNOSUPPORT: + iError = WSAESOCKTNOSUPPORT; + break; + + case EOPNOTSUPP: + iError = WSAEOPNOTSUPP; + break; + + case EPFNOSUPPORT: + iError = WSAEPFNOSUPPORT; + break; + + case EAFNOSUPPORT: + iError = WSAEAFNOSUPPORT; + break; + + case EADDRINUSE: + iError = WSAEADDRINUSE; + break; + + case EADDRNOTAVAIL: + iError = WSAEADDRNOTAVAIL; + break; + + case ENETDOWN: + iError = WSAENETDOWN; + break; + + case ENETUNREACH: + iError = WSAENETUNREACH; + break; + + case ENETRESET: + iError = WSAENETRESET; + break; + + case ECONNABORTED: + iError = WSAECONNABORTED; + break; + + case ECONNRESET: + iError = WSAECONNRESET; + break; + + case ENOBUFS: + iError = WSAENOBUFS; + break; + + case EISCONN: + iError = WSAEISCONN; + break; + + case ENOTCONN: + iError = WSAENOTCONN; + break; + + case ESHUTDOWN: + iError = WSAESHUTDOWN; + break; + + case ETOOMANYREFS: + iError = WSAETOOMANYREFS; + break; + + case ETIMEDOUT: + iError = WSAETIMEDOUT; + break; + + case ECONNREFUSED: + iError = WSAECONNREFUSED; + break; + + case ELOOP: + iError = WSAELOOP; + break; + + case ENAMETOOLONG: + iError = WSAENAMETOOLONG; + break; + + case EHOSTDOWN: + iError = WSAEHOSTDOWN; + break; + + case EHOSTUNREACH: + iError = WSAEHOSTUNREACH; + break; + + case ENOTEMPTY: + iError = WSAENOTEMPTY; + break; +#ifdef EPROCLIM + + case EPROCLIM: + iError = WSAEPROCLIM; + break; +#endif + + case EUSERS: + iError = WSAEUSERS; + break; + + case EDQUOT: + iError = WSAEDQUOT; + break; + + case ESTALE: + iError = WSAESTALE; + break; + + case EREMOTE: + iError = WSAEREMOTE; + break; + /* Special cases */ +#if (EAGAIN != EWOULDBLOCK) + + case EAGAIN: + iError = WSAEWOULDBLOCK; + break; +#endif +#if defined(EPROTO) + + case EPROTO: + iError = WSAECONNRESET; + break; +#endif + } + + /** + * Windows Sockets Extended Error Codes: + * + * WSASYSNOTREADY + * WSAVERNOTSUPPORTED + * WSANOTINITIALISED + * WSAEDISCON + * WSAENOMORE + * WSAECANCELLED + * WSAEINVALIDPROCTABLE + * WSAEINVALIDPROVIDER + * WSAEPROVIDERFAILEDINIT + * WSASYSCALLFAILURE + * WSASERVICE_NOT_FOUND + * WSATYPE_NOT_FOUND + * WSA_E_NO_MORE + * WSA_E_CANCELLED + * WSAEREFUSED + */ + return iError; +} + +HANDLE WSACreateEvent(void) +{ + return CreateEvent(NULL, TRUE, FALSE, NULL); +} + +BOOL WSASetEvent(HANDLE hEvent) +{ + return SetEvent(hEvent); +} + +BOOL WSAResetEvent(HANDLE hEvent) +{ + /* POSIX systems auto reset the socket, + * if no more data is available. */ + return TRUE; +} + +BOOL WSACloseEvent(HANDLE hEvent) +{ + BOOL status = 0; + status = CloseHandle(hEvent); + + if (!status) + SetLastError(6); + + return status; +} + +int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, LONG lNetworkEvents) +{ + u_long arg = 1; + ULONG mode = 0; + + if (_ioctlsocket(s, FIONBIO, &arg) != 0) + return SOCKET_ERROR; + + if (arg == 0) + return 0; + + if (lNetworkEvents & FD_READ) + mode |= WINPR_FD_READ; + + if (lNetworkEvents & FD_WRITE) + mode |= WINPR_FD_WRITE; + + if (SetEventFileDescriptor(hEventObject, s, mode) < 0) + return SOCKET_ERROR; + + return 0; +} + +DWORD WSAWaitForMultipleEvents(DWORD cEvents, const HANDLE* lphEvents, BOOL fWaitAll, + DWORD dwTimeout, BOOL fAlertable) +{ + return WaitForMultipleObjectsEx(cEvents, lphEvents, fWaitAll, dwTimeout, fAlertable); +} + +SOCKET WSASocketA(int af, int type, int protocol, LPWSAPROTOCOL_INFOA lpProtocolInfo, GROUP g, + DWORD dwFlags) +{ + SOCKET s = 0; + s = _socket(af, type, protocol); + return s; +} + +SOCKET WSASocketW(int af, int type, int protocol, LPWSAPROTOCOL_INFOW lpProtocolInfo, GROUP g, + DWORD dwFlags) +{ + return WSASocketA(af, type, protocol, (LPWSAPROTOCOL_INFOA)lpProtocolInfo, g, dwFlags); +} + +int WSAIoctl(SOCKET s, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, + LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, + LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +{ + int fd = 0; + int index = 0; + ULONG nFlags = 0; + size_t offset = 0; + size_t ifreq_len = 0; + struct ifreq* ifreq = NULL; + struct ifconf ifconf; + char address[128]; + char broadcast[128]; + char netmask[128]; + char buffer[4096]; + int numInterfaces = 0; + int maxNumInterfaces = 0; + INTERFACE_INFO* pInterface = NULL; + INTERFACE_INFO* pInterfaces = NULL; + struct sockaddr_in* pAddress = NULL; + struct sockaddr_in* pBroadcast = NULL; + struct sockaddr_in* pNetmask = NULL; + + if ((dwIoControlCode != SIO_GET_INTERFACE_LIST) || + (!lpvOutBuffer || !cbOutBuffer || !lpcbBytesReturned)) + { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + + fd = (int)s; + pInterfaces = (INTERFACE_INFO*)lpvOutBuffer; + maxNumInterfaces = cbOutBuffer / sizeof(INTERFACE_INFO); +#ifdef WSAIOCTL_IFADDRS + { + struct ifaddrs* ifap = NULL; + + if (getifaddrs(&ifap) != 0) + { + WSASetLastError(WSAENETDOWN); + return SOCKET_ERROR; + } + + index = 0; + numInterfaces = 0; + + for (struct ifaddrs* ifa = ifap; ifa; ifa = ifa->ifa_next) + { + pInterface = &pInterfaces[index]; + pAddress = (struct sockaddr_in*)&pInterface->iiAddress; + pBroadcast = (struct sockaddr_in*)&pInterface->iiBroadcastAddress; + pNetmask = (struct sockaddr_in*)&pInterface->iiNetmask; + nFlags = 0; + + if (ifa->ifa_flags & IFF_UP) + nFlags |= _IFF_UP; + + if (ifa->ifa_flags & IFF_BROADCAST) + nFlags |= _IFF_BROADCAST; + + if (ifa->ifa_flags & IFF_LOOPBACK) + nFlags |= _IFF_LOOPBACK; + + if (ifa->ifa_flags & IFF_POINTOPOINT) + nFlags |= _IFF_POINTTOPOINT; + + if (ifa->ifa_flags & IFF_MULTICAST) + nFlags |= _IFF_MULTICAST; + + pInterface->iiFlags = nFlags; + + if (ifa->ifa_addr) + { + if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6)) + continue; + + getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr), address, sizeof(address), 0, 0, + NI_NUMERICHOST); + inet_pton(ifa->ifa_addr->sa_family, address, (void*)&pAddress->sin_addr); + } + else + { + ZeroMemory(pAddress, sizeof(struct sockaddr_in)); + } + + if (ifa->ifa_dstaddr) + { + if ((ifa->ifa_dstaddr->sa_family != AF_INET) && + (ifa->ifa_dstaddr->sa_family != AF_INET6)) + continue; + + getnameinfo(ifa->ifa_dstaddr, sizeof(struct sockaddr), broadcast, sizeof(broadcast), + 0, 0, NI_NUMERICHOST); + inet_pton(ifa->ifa_dstaddr->sa_family, broadcast, (void*)&pBroadcast->sin_addr); + } + else + { + ZeroMemory(pBroadcast, sizeof(struct sockaddr_in)); + } + + if (ifa->ifa_netmask) + { + if ((ifa->ifa_netmask->sa_family != AF_INET) && + (ifa->ifa_netmask->sa_family != AF_INET6)) + continue; + + getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr), netmask, sizeof(netmask), 0, + 0, NI_NUMERICHOST); + inet_pton(ifa->ifa_netmask->sa_family, netmask, (void*)&pNetmask->sin_addr); + } + else + { + ZeroMemory(pNetmask, sizeof(struct sockaddr_in)); + } + + numInterfaces++; + index++; + } + + *lpcbBytesReturned = (DWORD)(numInterfaces * sizeof(INTERFACE_INFO)); + freeifaddrs(ifap); + return 0; + } +#endif + ifconf.ifc_len = sizeof(buffer); + ifconf.ifc_buf = buffer; + + if (ioctl(fd, SIOCGIFCONF, &ifconf) != 0) + { + WSASetLastError(WSAENETDOWN); + return SOCKET_ERROR; + } + + index = 0; + offset = 0; + numInterfaces = 0; + ifreq = ifconf.ifc_req; + + while ((ifconf.ifc_len >= 0) && (offset < (size_t)ifconf.ifc_len) && + (numInterfaces < maxNumInterfaces)) + { + pInterface = &pInterfaces[index]; + pAddress = (struct sockaddr_in*)&pInterface->iiAddress; + pBroadcast = (struct sockaddr_in*)&pInterface->iiBroadcastAddress; + pNetmask = (struct sockaddr_in*)&pInterface->iiNetmask; + + if (ioctl(fd, SIOCGIFFLAGS, ifreq) != 0) + goto next_ifreq; + + nFlags = 0; + + if (ifreq->ifr_flags & IFF_UP) + nFlags |= _IFF_UP; + + if (ifreq->ifr_flags & IFF_BROADCAST) + nFlags |= _IFF_BROADCAST; + + if (ifreq->ifr_flags & IFF_LOOPBACK) + nFlags |= _IFF_LOOPBACK; + + if (ifreq->ifr_flags & IFF_POINTOPOINT) + nFlags |= _IFF_POINTTOPOINT; + + if (ifreq->ifr_flags & IFF_MULTICAST) + nFlags |= _IFF_MULTICAST; + + pInterface->iiFlags = nFlags; + + if (ioctl(fd, SIOCGIFADDR, ifreq) != 0) + goto next_ifreq; + + if ((ifreq->ifr_addr.sa_family != AF_INET) && (ifreq->ifr_addr.sa_family != AF_INET6)) + goto next_ifreq; + + getnameinfo(&ifreq->ifr_addr, sizeof(ifreq->ifr_addr), address, sizeof(address), 0, 0, + NI_NUMERICHOST); + inet_pton(ifreq->ifr_addr.sa_family, address, (void*)&pAddress->sin_addr); + + if (ioctl(fd, SIOCGIFBRDADDR, ifreq) != 0) + goto next_ifreq; + + if ((ifreq->ifr_addr.sa_family != AF_INET) && (ifreq->ifr_addr.sa_family != AF_INET6)) + goto next_ifreq; + + getnameinfo(&ifreq->ifr_addr, sizeof(ifreq->ifr_addr), broadcast, sizeof(broadcast), 0, 0, + NI_NUMERICHOST); + inet_pton(ifreq->ifr_addr.sa_family, broadcast, (void*)&pBroadcast->sin_addr); + + if (ioctl(fd, SIOCGIFNETMASK, ifreq) != 0) + goto next_ifreq; + + if ((ifreq->ifr_addr.sa_family != AF_INET) && (ifreq->ifr_addr.sa_family != AF_INET6)) + goto next_ifreq; + + getnameinfo(&ifreq->ifr_addr, sizeof(ifreq->ifr_addr), netmask, sizeof(netmask), 0, 0, + NI_NUMERICHOST); + inet_pton(ifreq->ifr_addr.sa_family, netmask, (void*)&pNetmask->sin_addr); + numInterfaces++; + next_ifreq: +#if !defined(__linux__) && !defined(__sun__) && !defined(__CYGWIN__) && !defined(EMSCRIPTEN) + ifreq_len = IFNAMSIZ + ifreq->ifr_addr.sa_len; +#else + ifreq_len = sizeof(*ifreq); +#endif + ifreq = (struct ifreq*)&((BYTE*)ifreq)[ifreq_len]; + offset += ifreq_len; + index++; + } + + *lpcbBytesReturned = (DWORD)(numInterfaces * sizeof(INTERFACE_INFO)); + return 0; +} + +SOCKET _accept(SOCKET s, struct sockaddr* addr, int* addrlen) +{ + int status = 0; + int fd = (int)s; + socklen_t s_addrlen = (socklen_t)*addrlen; + status = accept(fd, addr, &s_addrlen); + *addrlen = (socklen_t)s_addrlen; + return status; +} + +int _bind(SOCKET s, const struct sockaddr* addr, int namelen) +{ + int status = 0; + int fd = (int)s; + status = bind(fd, addr, (socklen_t)namelen); + + if (status < 0) + return SOCKET_ERROR; + + return status; +} + +int closesocket(SOCKET s) +{ + int status = 0; + int fd = (int)s; + status = close(fd); + return status; +} + +int _connect(SOCKET s, const struct sockaddr* name, int namelen) +{ + int status = 0; + int fd = (int)s; + status = connect(fd, name, (socklen_t)namelen); + + if (status < 0) + return SOCKET_ERROR; + + return status; +} + +int _ioctlsocket(SOCKET s, long cmd, u_long* argp) +{ + int fd = (int)s; + + if (cmd == FIONBIO) + { + int flags = 0; + + if (!argp) + return SOCKET_ERROR; + + flags = fcntl(fd, F_GETFL); + + if (flags == -1) + return SOCKET_ERROR; + + if (*argp) + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + else + fcntl(fd, F_SETFL, flags & ~(O_NONBLOCK)); + } + + return 0; +} + +int _getpeername(SOCKET s, struct sockaddr* name, int* namelen) +{ + int status = 0; + int fd = (int)s; + socklen_t s_namelen = (socklen_t)*namelen; + status = getpeername(fd, name, &s_namelen); + *namelen = (int)s_namelen; + return status; +} + +int _getsockname(SOCKET s, struct sockaddr* name, int* namelen) +{ + int status = 0; + int fd = (int)s; + socklen_t s_namelen = (socklen_t)*namelen; + status = getsockname(fd, name, &s_namelen); + *namelen = (int)s_namelen; + return status; +} + +int _getsockopt(SOCKET s, int level, int optname, char* optval, int* optlen) +{ + int status = 0; + int fd = (int)s; + socklen_t s_optlen = (socklen_t)*optlen; + status = getsockopt(fd, level, optname, (void*)optval, &s_optlen); + *optlen = (socklen_t)s_optlen; + return status; +} + +u_long _htonl(u_long hostlong) +{ + return htonl(hostlong); +} + +u_short _htons(u_short hostshort) +{ + return htons(hostshort); +} + +unsigned long _inet_addr(const char* cp) +{ + return (long)inet_addr(cp); +} + +char* _inet_ntoa(struct in_addr in) +{ + return inet_ntoa(in); +} + +int _listen(SOCKET s, int backlog) +{ + int status = 0; + int fd = (int)s; + status = listen(fd, backlog); + return status; +} + +u_long _ntohl(u_long netlong) +{ + return ntohl(netlong); +} + +u_short _ntohs(u_short netshort) +{ + return ntohs(netshort); +} + +int _recv(SOCKET s, char* buf, int len, int flags) +{ + int status = 0; + int fd = (int)s; + status = (int)recv(fd, (void*)buf, (size_t)len, flags); + return status; +} + +int _recvfrom(SOCKET s, char* buf, int len, int flags, struct sockaddr* from, int* fromlen) +{ + int status = 0; + int fd = (int)s; + socklen_t s_fromlen = (socklen_t)*fromlen; + status = (int)recvfrom(fd, (void*)buf, (size_t)len, flags, from, &s_fromlen); + *fromlen = (int)s_fromlen; + return status; +} + +int _select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, + const struct timeval* timeout) +{ + int status = 0; + union + { + const struct timeval* cpv; + struct timeval* pv; + } cnv; + cnv.cpv = timeout; + do + { + status = select(nfds, readfds, writefds, exceptfds, cnv.pv); + } while ((status < 0) && (errno == EINTR)); + + return status; +} + +int _send(SOCKET s, const char* buf, int len, int flags) +{ + int status = 0; + int fd = (int)s; + flags |= MSG_NOSIGNAL; + status = (int)send(fd, (const void*)buf, (size_t)len, flags); + return status; +} + +int _sendto(SOCKET s, const char* buf, int len, int flags, const struct sockaddr* to, int tolen) +{ + int status = 0; + int fd = (int)s; + status = (int)sendto(fd, (const void*)buf, (size_t)len, flags, to, (socklen_t)tolen); + return status; +} + +int _setsockopt(SOCKET s, int level, int optname, const char* optval, int optlen) +{ + int status = 0; + int fd = (int)s; + status = setsockopt(fd, level, optname, (const void*)optval, (socklen_t)optlen); + return status; +} + +int _shutdown(SOCKET s, int how) +{ + int status = 0; + int fd = (int)s; + int s_how = -1; + + switch (how) + { + case SD_RECEIVE: + s_how = SHUT_RD; + break; + + case SD_SEND: + s_how = SHUT_WR; + break; + + case SD_BOTH: + s_how = SHUT_RDWR; + break; + } + + if (s_how < 0) + return SOCKET_ERROR; + + status = shutdown(fd, s_how); + return status; +} + +SOCKET _socket(int af, int type, int protocol) +{ + int fd = 0; + SOCKET s = 0; + fd = socket(af, type, protocol); + + if (fd < 0) + return INVALID_SOCKET; + + s = (SOCKET)fd; + return s; +} + +struct hostent* _gethostbyaddr(const char* addr, int len, int type) +{ + struct hostent* host = NULL; + host = gethostbyaddr((const void*)addr, (socklen_t)len, type); + return host; +} + +struct hostent* _gethostbyname(const char* name) +{ + struct hostent* host = NULL; + host = gethostbyname(name); + return host; +} + +int _gethostname(char* name, int namelen) +{ + int status = 0; + status = gethostname(name, (size_t)namelen); + return status; +} + +struct servent* _getservbyport(int port, const char* proto) +{ + struct servent* serv = NULL; + serv = getservbyport(port, proto); + return serv; +} + +struct servent* _getservbyname(const char* name, const char* proto) +{ + struct servent* serv = NULL; + serv = getservbyname(name, proto); + return serv; +} + +struct protoent* _getprotobynumber(int number) +{ + struct protoent* proto = NULL; + proto = getprotobynumber(number); + return proto; +} + +struct protoent* _getprotobyname(const char* name) +{ + struct protoent* proto = NULL; + proto = getprotobyname(name); + return proto; +} + +#endif /* _WIN32 */ diff --git a/winpr/libwinpr/wtsapi/CMakeLists.txt b/winpr/libwinpr/wtsapi/CMakeLists.txt new file mode 100644 index 0000000..793e05f --- /dev/null +++ b/winpr/libwinpr/wtsapi/CMakeLists.txt @@ -0,0 +1,30 @@ +# WinPR: Windows Portable Runtime +# libwinpr-wtsapi cmake build script +# +# Copyright 2013 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add(wtsapi.c) + +if(WIN32) + winpr_module_add(wtsapi_win32.c wtsapi_win32.h) + + if (MINGW) + winpr_library_add_private(ntdll.lib) # Only required with MINGW + endif() +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/wtsapi/ModuleOptions.cmake b/winpr/libwinpr/wtsapi/ModuleOptions.cmake new file mode 100644 index 0000000..0575367 --- /dev/null +++ b/winpr/libwinpr/wtsapi/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "wtsapi") +set(MINWIN_LONG_NAME "Windows Terminal Services API") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/wtsapi/test/CMakeLists.txt b/winpr/libwinpr/wtsapi/test/CMakeLists.txt new file mode 100644 index 0000000..d5bf76c --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/CMakeLists.txt @@ -0,0 +1,74 @@ + +set(MODULE_NAME "TestWtsApi") +set(MODULE_PREFIX "TEST_WTSAPI") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(UNIX_ONLY + TestWtsApiShutdownSystem.c + TestWtsApiWaitSystemEvent.c + ) + +set(${MODULE_PREFIX}_TESTS + TestWtsApiEnumerateProcesses.c + TestWtsApiEnumerateSessions.c + TestWtsApiQuerySessionInformation.c + TestWtsApiSessionNotification.c + ) + +if(NOT WIN32) + set(${MODULE_PREFIX}_TESTS ${${MODULE_PREFIX}_TESTS} ${UNIX_ONLY}) +endif() + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + +if(TESTS_WTSAPI_EXTRA) + +set(MODULE_NAME "TestWtsApiExtra") +set(MODULE_PREFIX "TEST_WTSAPI_EXTRA") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestWtsApiExtraDisconnectSession.c + TestWtsApiExtraDynamicVirtualChannel.c + TestWtsApiExtraLogoffSession.c + TestWtsApiExtraSendMessage.c + TestWtsApiExtraVirtualChannel.c + TestWtsApiExtraStartRemoteSessionEx.c + ) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) + set_tests_properties(${TestName} PROPERTIES LABELS "WTSAPI_EXTRA") +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") +endif() diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c new file mode 100644 index 0000000..f26646c --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateProcesses.c @@ -0,0 +1,49 @@ + +#include +#include +#include +#include + +int TestWtsApiEnumerateProcesses(int argc, char* argv[]) +{ + DWORD count = 0; + BOOL bSuccess = 0; + HANDLE hServer = NULL; + PWTS_PROCESS_INFOA pProcessInfo = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + +#ifndef _WIN32 + if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0)) + { + printf("%s: No RDS environment detected, skipping test\n", __func__); + return 0; + } +#endif + + hServer = WTS_CURRENT_SERVER_HANDLE; + + count = 0; + pProcessInfo = NULL; + + bSuccess = WTSEnumerateProcessesA(hServer, 0, 1, &pProcessInfo, &count); + + if (!bSuccess) + { + printf("WTSEnumerateProcesses failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + +#if 0 + { + printf("WTSEnumerateProcesses enumerated %"PRIu32" processs:\n", count); + for (DWORD i = 0; i < count; i++) + printf("\t[%"PRIu32"]: %s (%"PRIu32")\n", i, pProcessInfo[i].pProcessName, pProcessInfo[i].ProcessId); + } +#endif + + WTSFreeMemory(pProcessInfo); + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c new file mode 100644 index 0000000..afe48a9 --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiEnumerateSessions.c @@ -0,0 +1,50 @@ + +#include +#include +#include +#include + +int TestWtsApiEnumerateSessions(int argc, char* argv[]) +{ + DWORD count = 0; + BOOL bSuccess = 0; + HANDLE hServer = NULL; + PWTS_SESSION_INFOA pSessionInfo = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + +#ifndef _WIN32 + if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0)) + { + printf("%s: No RDS environment detected, skipping test\n", __func__); + return 0; + } +#endif + + hServer = WTS_CURRENT_SERVER_HANDLE; + + count = 0; + pSessionInfo = NULL; + + bSuccess = WTSEnumerateSessionsA(hServer, 0, 1, &pSessionInfo, &count); + + if (!bSuccess) + { + printf("WTSEnumerateSessions failed: %" PRIu32 "\n", GetLastError()); + return 0; + } + + printf("WTSEnumerateSessions count: %" PRIu32 "\n", count); + + for (DWORD index = 0; index < count; index++) + { + printf("[%" PRIu32 "] SessionId: %" PRIu32 " WinstationName: '%s' State: %s (%u)\n", index, + pSessionInfo[index].SessionId, pSessionInfo[index].pWinStationName, + WTSSessionStateToString(pSessionInfo[index].State), pSessionInfo[index].State); + } + + WTSFreeMemory(pSessionInfo); + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c new file mode 100644 index 0000000..4d04e59 --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDisconnectSession.c @@ -0,0 +1,22 @@ + +#include +#include +#include + +int TestWtsApiExtraDisconnectSession(int argc, char* argv[]) +{ + BOOL bSuccess; + HANDLE hServer; + + hServer = WTS_CURRENT_SERVER_HANDLE; + + bSuccess = WTSDisconnectSession(hServer, WTS_CURRENT_SESSION, FALSE); + + if (!bSuccess) + { + printf("WTSDisconnectSession failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c new file mode 100644 index 0000000..d0bd87e --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraDynamicVirtualChannel.c @@ -0,0 +1,53 @@ + +#include +#include +#include + +int TestWtsApiExtraDynamicVirtualChannel(int argc, char* argv[]) +{ + BOOL bSuccess; + ULONG length; + ULONG bytesRead; + ULONG bytesWritten; + BYTE buffer[1024]; + HANDLE hVirtualChannel; + + length = sizeof(buffer); + + hVirtualChannel = + WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC); + + if (hVirtualChannel == INVALID_HANDLE_VALUE) + { + printf("WTSVirtualChannelOpen failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + printf("WTSVirtualChannelOpen opend"); + bytesWritten = 0; + bSuccess = WTSVirtualChannelWrite(hVirtualChannel, (PCHAR)buffer, length, &bytesWritten); + + if (!bSuccess) + { + printf("WTSVirtualChannelWrite failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + printf("WTSVirtualChannelWrite written"); + + bytesRead = 0; + bSuccess = WTSVirtualChannelRead(hVirtualChannel, 5000, (PCHAR)buffer, length, &bytesRead); + + if (!bSuccess) + { + printf("WTSVirtualChannelRead failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + printf("WTSVirtualChannelRead read"); + + if (!WTSVirtualChannelClose(hVirtualChannel)) + { + printf("WTSVirtualChannelClose failed\n"); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c new file mode 100644 index 0000000..2af5002 --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraLogoffSession.c @@ -0,0 +1,22 @@ + +#include +#include +#include + +int TestWtsApiExtraLogoffSession(int argc, char* argv[]) +{ + BOOL bSuccess; + HANDLE hServer; + + hServer = WTS_CURRENT_SERVER_HANDLE; + + bSuccess = WTSLogoffSession(hServer, WTS_CURRENT_SESSION, FALSE); + + if (!bSuccess) + { + printf("WTSLogoffSession failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c new file mode 100644 index 0000000..f311af8 --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraSendMessage.c @@ -0,0 +1,30 @@ + +#include +#include +#include +#include + +#define TITLE "thats the title" +#define MESSAGE "thats the message" + +int TestWtsApiExtraSendMessage(int argc, char* argv[]) +{ + BOOL bSuccess; + HANDLE hServer; + DWORD result; + + hServer = WTS_CURRENT_SERVER_HANDLE; + + bSuccess = WTSSendMessageA(hServer, WTS_CURRENT_SESSION, TITLE, strlen(TITLE) + 1, MESSAGE, + strlen(MESSAGE) + 1, MB_CANCELTRYCONTINUE, 3, &result, TRUE); + + if (!bSuccess) + { + printf("WTSSendMessage failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + printf("WTSSendMessage got result: %" PRIu32 "\n", result); + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c new file mode 100644 index 0000000..179d33b --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraStartRemoteSessionEx.c @@ -0,0 +1,31 @@ + +#include +#include +#include +#include +#include + +int TestWtsApiExtraStartRemoteSessionEx(int argc, char* argv[]) +{ + BOOL bSuccess; + ULONG logonId = 0; + char logonIdStr[10]; + + bSuccess = GetEnvironmentVariableA("TEST_SESSION_LOGON_ID", logonIdStr, 10); + if (bSuccess) + { + sscanf(logonIdStr, "%u\n", &logonId); + } + + bSuccess = WTSStartRemoteControlSessionEx( + NULL, logonId, VK_F10, REMOTECONTROL_KBDSHIFT_HOTKEY | REMOTECONTROL_KBDCTRL_HOTKEY, + REMOTECONTROL_FLAG_DISABLE_INPUT); + + if (!bSuccess) + { + printf("WTSStartRemoteControlSessionEx failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c new file mode 100644 index 0000000..c528e51 --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiExtraVirtualChannel.c @@ -0,0 +1,53 @@ + +#include +#include +#include + +int TestWtsApiExtraVirtualChannel(int argc, char* argv[]) +{ + BOOL bSuccess; + ULONG length; + ULONG bytesRead; + ULONG bytesWritten; + BYTE buffer[1024]; + HANDLE hVirtualChannel; + + length = sizeof(buffer); + + hVirtualChannel = + WTSVirtualChannelOpen(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, "sample"); + + if (hVirtualChannel == INVALID_HANDLE_VALUE) + { + printf("WTSVirtualChannelOpen failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + printf("WTSVirtualChannelOpen opend"); + bytesWritten = 0; + bSuccess = WTSVirtualChannelWrite(hVirtualChannel, (PCHAR)buffer, length, &bytesWritten); + + if (!bSuccess) + { + printf("WTSVirtualChannelWrite failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + printf("WTSVirtualChannelWrite written"); + + bytesRead = 0; + bSuccess = WTSVirtualChannelRead(hVirtualChannel, 5000, (PCHAR)buffer, length, &bytesRead); + + if (!bSuccess) + { + printf("WTSVirtualChannelRead failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + printf("WTSVirtualChannelRead read"); + + if (!WTSVirtualChannelClose(hVirtualChannel)) + { + printf("WTSVirtualChannelClose failed\n"); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c b/winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c new file mode 100644 index 0000000..bc232f0 --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiQuerySessionInformation.c @@ -0,0 +1,225 @@ + +#include +#include +#include +#include + +int TestWtsApiQuerySessionInformation(int argc, char* argv[]) +{ + DWORD count = 0; + BOOL bSuccess = 0; + HANDLE hServer = NULL; + LPSTR pBuffer = NULL; + DWORD sessionId = 0; + DWORD bytesReturned = 0; + PWTS_SESSION_INFOA pSessionInfo = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + +#ifndef _WIN32 + if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0)) + { + printf("%s: No RDS environment detected, skipping test\n", __func__); + return 0; + } +#endif + + hServer = WTS_CURRENT_SERVER_HANDLE; + + count = 0; + pSessionInfo = NULL; + + bSuccess = WTSEnumerateSessionsA(hServer, 0, 1, &pSessionInfo, &count); + + if (!bSuccess) + { + printf("WTSEnumerateSessions failed: %" PRIu32 "\n", GetLastError()); + return 0; + } + + printf("WTSEnumerateSessions count: %" PRIu32 "\n", count); + + for (DWORD index = 0; index < count; index++) + { + char* Username = NULL; + char* Domain = NULL; + char* ClientName = NULL; + ULONG ClientBuildNumber = 0; + USHORT ClientProductId = 0; + ULONG ClientHardwareId = 0; + USHORT ClientProtocolType = 0; + PWTS_CLIENT_DISPLAY ClientDisplay = NULL; + PWTS_CLIENT_ADDRESS ClientAddress = NULL; + WTS_CONNECTSTATE_CLASS ConnectState = WTSInit; + + pBuffer = NULL; + bytesReturned = 0; + + sessionId = pSessionInfo[index].SessionId; + + printf("[%" PRIu32 "] SessionId: %" PRIu32 " State: %s (%u) WinstationName: '%s'\n", index, + pSessionInfo[index].SessionId, WTSSessionStateToString(pSessionInfo[index].State), + pSessionInfo[index].State, pSessionInfo[index].pWinStationName); + + /* WTSUserName */ + + bSuccess = + WTSQuerySessionInformationA(hServer, sessionId, WTSUserName, &pBuffer, &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSUserName failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + Username = (char*)pBuffer; + printf("\tWTSUserName: '%s'\n", Username); + + /* WTSDomainName */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSDomainName, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSDomainName failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + Domain = (char*)pBuffer; + printf("\tWTSDomainName: '%s'\n", Domain); + + /* WTSConnectState */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSConnectState, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSConnectState failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ConnectState = *((WTS_CONNECTSTATE_CLASS*)pBuffer); + printf("\tWTSConnectState: %u (%s)\n", ConnectState, WTSSessionStateToString(ConnectState)); + + /* WTSClientBuildNumber */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientBuildNumber, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientBuildNumber failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientBuildNumber = *((ULONG*)pBuffer); + printf("\tWTSClientBuildNumber: %" PRIu32 "\n", ClientBuildNumber); + + /* WTSClientName */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientName, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientName failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientName = (char*)pBuffer; + printf("\tWTSClientName: '%s'\n", ClientName); + + /* WTSClientProductId */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientProductId, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientProductId failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientProductId = *((USHORT*)pBuffer); + printf("\tWTSClientProductId: %" PRIu16 "\n", ClientProductId); + + /* WTSClientHardwareId */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientHardwareId, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientHardwareId failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientHardwareId = *((ULONG*)pBuffer); + printf("\tWTSClientHardwareId: %" PRIu32 "\n", ClientHardwareId); + + /* WTSClientAddress */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientAddress, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientAddress failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientAddress = (PWTS_CLIENT_ADDRESS)pBuffer; + printf("\tWTSClientAddress: AddressFamily: %" PRIu32 " Address: ", + ClientAddress->AddressFamily); + for (DWORD i = 0; i < sizeof(ClientAddress->Address); i++) + printf("%02" PRIX8 "", ClientAddress->Address[i]); + printf("\n"); + + /* WTSClientDisplay */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientDisplay, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientDisplay failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientDisplay = (PWTS_CLIENT_DISPLAY)pBuffer; + printf("\tWTSClientDisplay: HorizontalResolution: %" PRIu32 " VerticalResolution: %" PRIu32 + " ColorDepth: %" PRIu32 "\n", + ClientDisplay->HorizontalResolution, ClientDisplay->VerticalResolution, + ClientDisplay->ColorDepth); + + /* WTSClientProtocolType */ + + bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientProtocolType, &pBuffer, + &bytesReturned); + + if (!bSuccess) + { + printf("WTSQuerySessionInformation WTSClientProtocolType failed: %" PRIu32 "\n", + GetLastError()); + return -1; + } + + ClientProtocolType = *((USHORT*)pBuffer); + printf("\tWTSClientProtocolType: %" PRIu16 "\n", ClientProtocolType); + } + + WTSFreeMemory(pSessionInfo); + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c b/winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c new file mode 100644 index 0000000..e83c27b --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiSessionNotification.c @@ -0,0 +1,62 @@ + +#include +#include +#include +#include + +int TestWtsApiSessionNotification(int argc, char* argv[]) +{ + HWND hWnd = NULL; + BOOL bSuccess = 0; + DWORD dwFlags = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + +#ifndef _WIN32 + if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0)) + { + printf("%s: No RDS environment detected, skipping test\n", __func__); + return 0; + } +#else + /* We create a message-only window and use the predefined class name "STATIC" for simplicity */ + hWnd = CreateWindowA("STATIC", "TestWtsApiSessionNotification", 0, 0, 0, 0, 0, HWND_MESSAGE, + NULL, NULL, NULL); + if (!hWnd) + { + printf("%s: error creating message-only window: %" PRIu32 "\n", __func__, GetLastError()); + return -1; + } +#endif + + dwFlags = NOTIFY_FOR_ALL_SESSIONS; + + bSuccess = WTSRegisterSessionNotification(hWnd, dwFlags); + + if (!bSuccess) + { + printf("%s: WTSRegisterSessionNotification failed: %" PRIu32 "\n", __func__, + GetLastError()); + return -1; + } + + bSuccess = WTSUnRegisterSessionNotification(hWnd); + +#ifdef _WIN32 + if (hWnd) + { + DestroyWindow(hWnd); + hWnd = NULL; + } +#endif + + if (!bSuccess) + { + printf("%s: WTSUnRegisterSessionNotification failed: %" PRIu32 "\n", __func__, + GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c b/winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c new file mode 100644 index 0000000..431424b --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiShutdownSystem.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +int TestWtsApiShutdownSystem(int argc, char* argv[]) +{ + BOOL bSuccess = 0; + HANDLE hServer = NULL; + DWORD ShutdownFlag = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + +#ifndef _WIN32 + if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0)) + { + printf("%s: No RDS environment detected, skipping test\n", __func__); + return 0; + } +#endif + + hServer = WTS_CURRENT_SERVER_HANDLE; + ShutdownFlag = WTS_WSD_SHUTDOWN; + + bSuccess = WTSShutdownSystem(hServer, ShutdownFlag); + + if (!bSuccess) + { + printf("WTSShutdownSystem failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c b/winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c new file mode 100644 index 0000000..389c0be --- /dev/null +++ b/winpr/libwinpr/wtsapi/test/TestWtsApiWaitSystemEvent.c @@ -0,0 +1,39 @@ + +#include +#include +#include +#include + +int TestWtsApiWaitSystemEvent(int argc, char* argv[]) +{ + BOOL bSuccess = 0; + HANDLE hServer = NULL; + DWORD eventMask = 0; + DWORD eventFlags = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + +#ifndef _WIN32 + if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", NULL, 0)) + { + printf("%s: No RDS environment detected, skipping test\n", __func__); + return 0; + } +#endif + + hServer = WTS_CURRENT_SERVER_HANDLE; + + eventMask = WTS_EVENT_ALL; + eventFlags = 0; + + bSuccess = WTSWaitSystemEvent(hServer, eventMask, &eventFlags); + + if (!bSuccess) + { + printf("WTSWaitSystemEvent failed: %" PRIu32 "\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/wtsapi/wtsapi.c b/winpr/libwinpr/wtsapi/wtsapi.c new file mode 100644 index 0000000..46bf9b9 --- /dev/null +++ b/winpr/libwinpr/wtsapi/wtsapi.c @@ -0,0 +1,802 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Terminal Services API + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2015 Copyright 2015 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _WIN32 +#include "wtsapi_win32.h" +#endif + +#include "../log.h" +#define TAG WINPR_TAG("wtsapi") + +/** + * Remote Desktop Services API Functions: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa383464/ + */ + +static HMODULE g_WtsApiModule = NULL; + +static const WtsApiFunctionTable* g_WtsApi = NULL; + +#if defined(_WIN32) +static HMODULE g_WtsApi32Module = NULL; +static WtsApiFunctionTable WtsApi32_WtsApiFunctionTable = { 0 }; + +#ifdef __MINGW32__ +#define WTSAPI32_LOAD_PROC(NAME, TYPE) \ + WtsApi32_WtsApiFunctionTable.p##NAME = (TYPE)GetProcAddress(g_WtsApi32Module, "WTS" #NAME); +#else +#define WTSAPI32_LOAD_PROC(NAME, TYPE) \ + WtsApi32_WtsApiFunctionTable.p##NAME = (##TYPE)GetProcAddress(g_WtsApi32Module, "WTS" #NAME); +#endif + +static BOOL WtsApi32_InitializeWtsApi(void) +{ + g_WtsApi32Module = LoadLibraryA("wtsapi32.dll"); + + if (!g_WtsApi32Module) + return FALSE; + + WTSAPI32_LOAD_PROC(StopRemoteControlSession, WTS_STOP_REMOTE_CONTROL_SESSION_FN); + WTSAPI32_LOAD_PROC(StartRemoteControlSessionW, WTS_START_REMOTE_CONTROL_SESSION_FN_W); + WTSAPI32_LOAD_PROC(StartRemoteControlSessionA, WTS_START_REMOTE_CONTROL_SESSION_FN_A); + WTSAPI32_LOAD_PROC(ConnectSessionW, WTS_CONNECT_SESSION_FN_W); + WTSAPI32_LOAD_PROC(ConnectSessionA, WTS_CONNECT_SESSION_FN_A); + WTSAPI32_LOAD_PROC(EnumerateServersW, WTS_ENUMERATE_SERVERS_FN_W); + WTSAPI32_LOAD_PROC(EnumerateServersA, WTS_ENUMERATE_SERVERS_FN_A); + WTSAPI32_LOAD_PROC(OpenServerW, WTS_OPEN_SERVER_FN_W); + WTSAPI32_LOAD_PROC(OpenServerA, WTS_OPEN_SERVER_FN_A); + WTSAPI32_LOAD_PROC(OpenServerExW, WTS_OPEN_SERVER_EX_FN_W); + WTSAPI32_LOAD_PROC(OpenServerExA, WTS_OPEN_SERVER_EX_FN_A); + WTSAPI32_LOAD_PROC(CloseServer, WTS_CLOSE_SERVER_FN); + WTSAPI32_LOAD_PROC(EnumerateSessionsW, WTS_ENUMERATE_SESSIONS_FN_W); + WTSAPI32_LOAD_PROC(EnumerateSessionsA, WTS_ENUMERATE_SESSIONS_FN_A); + WTSAPI32_LOAD_PROC(EnumerateSessionsExW, WTS_ENUMERATE_SESSIONS_EX_FN_W); + WTSAPI32_LOAD_PROC(EnumerateSessionsExA, WTS_ENUMERATE_SESSIONS_EX_FN_A); + WTSAPI32_LOAD_PROC(EnumerateProcessesW, WTS_ENUMERATE_PROCESSES_FN_W); + WTSAPI32_LOAD_PROC(EnumerateProcessesA, WTS_ENUMERATE_PROCESSES_FN_A); + WTSAPI32_LOAD_PROC(TerminateProcess, WTS_TERMINATE_PROCESS_FN); + WTSAPI32_LOAD_PROC(QuerySessionInformationW, WTS_QUERY_SESSION_INFORMATION_FN_W); + WTSAPI32_LOAD_PROC(QuerySessionInformationA, WTS_QUERY_SESSION_INFORMATION_FN_A); + WTSAPI32_LOAD_PROC(QueryUserConfigW, WTS_QUERY_USER_CONFIG_FN_W); + WTSAPI32_LOAD_PROC(QueryUserConfigA, WTS_QUERY_USER_CONFIG_FN_A); + WTSAPI32_LOAD_PROC(SetUserConfigW, WTS_SET_USER_CONFIG_FN_W); + WTSAPI32_LOAD_PROC(SetUserConfigA, WTS_SET_USER_CONFIG_FN_A); + WTSAPI32_LOAD_PROC(SendMessageW, WTS_SEND_MESSAGE_FN_W); + WTSAPI32_LOAD_PROC(SendMessageA, WTS_SEND_MESSAGE_FN_A); + WTSAPI32_LOAD_PROC(DisconnectSession, WTS_DISCONNECT_SESSION_FN); + WTSAPI32_LOAD_PROC(LogoffSession, WTS_LOGOFF_SESSION_FN); + WTSAPI32_LOAD_PROC(ShutdownSystem, WTS_SHUTDOWN_SYSTEM_FN); + WTSAPI32_LOAD_PROC(WaitSystemEvent, WTS_WAIT_SYSTEM_EVENT_FN); + WTSAPI32_LOAD_PROC(VirtualChannelOpen, WTS_VIRTUAL_CHANNEL_OPEN_FN); + WTSAPI32_LOAD_PROC(VirtualChannelOpenEx, WTS_VIRTUAL_CHANNEL_OPEN_EX_FN); + WTSAPI32_LOAD_PROC(VirtualChannelClose, WTS_VIRTUAL_CHANNEL_CLOSE_FN); + WTSAPI32_LOAD_PROC(VirtualChannelRead, WTS_VIRTUAL_CHANNEL_READ_FN); + WTSAPI32_LOAD_PROC(VirtualChannelWrite, WTS_VIRTUAL_CHANNEL_WRITE_FN); + WTSAPI32_LOAD_PROC(VirtualChannelPurgeInput, WTS_VIRTUAL_CHANNEL_PURGE_INPUT_FN); + WTSAPI32_LOAD_PROC(VirtualChannelPurgeOutput, WTS_VIRTUAL_CHANNEL_PURGE_OUTPUT_FN); + WTSAPI32_LOAD_PROC(VirtualChannelQuery, WTS_VIRTUAL_CHANNEL_QUERY_FN); + WTSAPI32_LOAD_PROC(FreeMemory, WTS_FREE_MEMORY_FN); + WTSAPI32_LOAD_PROC(RegisterSessionNotification, WTS_REGISTER_SESSION_NOTIFICATION_FN); + WTSAPI32_LOAD_PROC(UnRegisterSessionNotification, WTS_UNREGISTER_SESSION_NOTIFICATION_FN); + WTSAPI32_LOAD_PROC(RegisterSessionNotificationEx, WTS_REGISTER_SESSION_NOTIFICATION_EX_FN); + WTSAPI32_LOAD_PROC(UnRegisterSessionNotificationEx, WTS_UNREGISTER_SESSION_NOTIFICATION_EX_FN); + WTSAPI32_LOAD_PROC(QueryUserToken, WTS_QUERY_USER_TOKEN_FN); + WTSAPI32_LOAD_PROC(FreeMemoryExW, WTS_FREE_MEMORY_EX_FN_W); + WTSAPI32_LOAD_PROC(FreeMemoryExA, WTS_FREE_MEMORY_EX_FN_A); + WTSAPI32_LOAD_PROC(EnumerateProcessesExW, WTS_ENUMERATE_PROCESSES_EX_FN_W); + WTSAPI32_LOAD_PROC(EnumerateProcessesExA, WTS_ENUMERATE_PROCESSES_EX_FN_A); + WTSAPI32_LOAD_PROC(EnumerateListenersW, WTS_ENUMERATE_LISTENERS_FN_W); + WTSAPI32_LOAD_PROC(EnumerateListenersA, WTS_ENUMERATE_LISTENERS_FN_A); + WTSAPI32_LOAD_PROC(QueryListenerConfigW, WTS_QUERY_LISTENER_CONFIG_FN_W); + WTSAPI32_LOAD_PROC(QueryListenerConfigA, WTS_QUERY_LISTENER_CONFIG_FN_A); + WTSAPI32_LOAD_PROC(CreateListenerW, WTS_CREATE_LISTENER_FN_W); + WTSAPI32_LOAD_PROC(CreateListenerA, WTS_CREATE_LISTENER_FN_A); + WTSAPI32_LOAD_PROC(SetListenerSecurityW, WTS_SET_LISTENER_SECURITY_FN_W); + WTSAPI32_LOAD_PROC(SetListenerSecurityA, WTS_SET_LISTENER_SECURITY_FN_A); + WTSAPI32_LOAD_PROC(GetListenerSecurityW, WTS_GET_LISTENER_SECURITY_FN_W); + WTSAPI32_LOAD_PROC(GetListenerSecurityA, WTS_GET_LISTENER_SECURITY_FN_A); + WTSAPI32_LOAD_PROC(EnableChildSessions, WTS_ENABLE_CHILD_SESSIONS_FN); + WTSAPI32_LOAD_PROC(IsChildSessionsEnabled, WTS_IS_CHILD_SESSIONS_ENABLED_FN); + WTSAPI32_LOAD_PROC(GetChildSessionId, WTS_GET_CHILD_SESSION_ID_FN); + WTSAPI32_LOAD_PROC(GetActiveConsoleSessionId, WTS_GET_ACTIVE_CONSOLE_SESSION_ID_FN); + + Win32_InitializeWinSta(&WtsApi32_WtsApiFunctionTable); + + g_WtsApi = &WtsApi32_WtsApiFunctionTable; + + return TRUE; +} +#endif + +/* WtsApi Functions */ + +static BOOL CALLBACK InitializeWtsApiStubs(PINIT_ONCE once, PVOID param, PVOID* context); +static INIT_ONCE wtsapiInitOnce = INIT_ONCE_STATIC_INIT; + +#define WTSAPI_STUB_CALL_VOID(_name, ...) \ + InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); \ + if (!g_WtsApi || !g_WtsApi->p##_name) \ + return; \ + g_WtsApi->p##_name(__VA_ARGS__) + +#define WTSAPI_STUB_CALL_BOOL(_name, ...) \ + InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); \ + if (!g_WtsApi || !g_WtsApi->p##_name) \ + return FALSE; \ + return g_WtsApi->p##_name(__VA_ARGS__) + +#define WTSAPI_STUB_CALL_HANDLE(_name, ...) \ + InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); \ + if (!g_WtsApi || !g_WtsApi->p##_name) \ + return NULL; \ + return g_WtsApi->p##_name(__VA_ARGS__) + +BOOL WINAPI WTSStartRemoteControlSessionW(LPWSTR pTargetServerName, ULONG TargetLogonId, + BYTE HotkeyVk, USHORT HotkeyModifiers) +{ + WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionW, pTargetServerName, TargetLogonId, HotkeyVk, + HotkeyModifiers); +} + +BOOL WINAPI WTSStartRemoteControlSessionA(LPSTR pTargetServerName, ULONG TargetLogonId, + BYTE HotkeyVk, USHORT HotkeyModifiers) +{ + WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionA, pTargetServerName, TargetLogonId, HotkeyVk, + HotkeyModifiers); +} + +BOOL WINAPI WTSStartRemoteControlSessionExW(LPWSTR pTargetServerName, ULONG TargetLogonId, + BYTE HotkeyVk, USHORT HotkeyModifiers, DWORD flags) +{ + WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionExW, pTargetServerName, TargetLogonId, HotkeyVk, + HotkeyModifiers, flags); +} + +BOOL WINAPI WTSStartRemoteControlSessionExA(LPSTR pTargetServerName, ULONG TargetLogonId, + BYTE HotkeyVk, USHORT HotkeyModifiers, DWORD flags) +{ + WTSAPI_STUB_CALL_BOOL(StartRemoteControlSessionExA, pTargetServerName, TargetLogonId, HotkeyVk, + HotkeyModifiers, flags); +} + +BOOL WINAPI WTSStopRemoteControlSession(ULONG LogonId) +{ + WTSAPI_STUB_CALL_BOOL(StopRemoteControlSession, LogonId); +} + +BOOL WINAPI WTSConnectSessionW(ULONG LogonId, ULONG TargetLogonId, PWSTR pPassword, BOOL bWait) +{ + WTSAPI_STUB_CALL_BOOL(ConnectSessionW, LogonId, TargetLogonId, pPassword, bWait); +} + +BOOL WINAPI WTSConnectSessionA(ULONG LogonId, ULONG TargetLogonId, PSTR pPassword, BOOL bWait) +{ + WTSAPI_STUB_CALL_BOOL(ConnectSessionA, LogonId, TargetLogonId, pPassword, bWait); +} + +BOOL WINAPI WTSEnumerateServersW(LPWSTR pDomainName, DWORD Reserved, DWORD Version, + PWTS_SERVER_INFOW* ppServerInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateServersW, pDomainName, Reserved, Version, ppServerInfo, pCount); +} + +BOOL WINAPI WTSEnumerateServersA(LPSTR pDomainName, DWORD Reserved, DWORD Version, + PWTS_SERVER_INFOA* ppServerInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateServersA, pDomainName, Reserved, Version, ppServerInfo, pCount); +} + +HANDLE WINAPI WTSOpenServerW(LPWSTR pServerName) +{ + WTSAPI_STUB_CALL_HANDLE(OpenServerW, pServerName); +} + +HANDLE WINAPI WTSOpenServerA(LPSTR pServerName) +{ + WTSAPI_STUB_CALL_HANDLE(OpenServerA, pServerName); +} + +HANDLE WINAPI WTSOpenServerExW(LPWSTR pServerName) +{ + WTSAPI_STUB_CALL_HANDLE(OpenServerExW, pServerName); +} + +HANDLE WINAPI WTSOpenServerExA(LPSTR pServerName) +{ + WTSAPI_STUB_CALL_HANDLE(OpenServerExA, pServerName); +} + +VOID WINAPI WTSCloseServer(HANDLE hServer) +{ + WTSAPI_STUB_CALL_VOID(CloseServer, hServer); +} + +BOOL WINAPI WTSEnumerateSessionsW(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_SESSION_INFOW* ppSessionInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateSessionsW, hServer, Reserved, Version, ppSessionInfo, pCount); +} + +BOOL WINAPI WTSEnumerateSessionsA(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_SESSION_INFOA* ppSessionInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateSessionsA, hServer, Reserved, Version, ppSessionInfo, pCount); +} + +BOOL WINAPI WTSEnumerateSessionsExW(HANDLE hServer, DWORD* pLevel, DWORD Filter, + PWTS_SESSION_INFO_1W* ppSessionInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateSessionsExW, hServer, pLevel, Filter, ppSessionInfo, pCount); +} + +BOOL WINAPI WTSEnumerateSessionsExA(HANDLE hServer, DWORD* pLevel, DWORD Filter, + PWTS_SESSION_INFO_1A* ppSessionInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateSessionsExA, hServer, pLevel, Filter, ppSessionInfo, pCount); +} + +BOOL WINAPI WTSEnumerateProcessesW(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_PROCESS_INFOW* ppProcessInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateProcessesW, hServer, Reserved, Version, ppProcessInfo, pCount); +} + +BOOL WINAPI WTSEnumerateProcessesA(HANDLE hServer, DWORD Reserved, DWORD Version, + PWTS_PROCESS_INFOA* ppProcessInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateProcessesA, hServer, Reserved, Version, ppProcessInfo, pCount); +} + +BOOL WINAPI WTSTerminateProcess(HANDLE hServer, DWORD ProcessId, DWORD ExitCode) +{ + WTSAPI_STUB_CALL_BOOL(TerminateProcess, hServer, ProcessId, ExitCode); +} + +BOOL WINAPI WTSQuerySessionInformationW(HANDLE hServer, DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, LPWSTR* ppBuffer, + DWORD* pBytesReturned) +{ + WTSAPI_STUB_CALL_BOOL(QuerySessionInformationW, hServer, SessionId, WTSInfoClass, ppBuffer, + pBytesReturned); +} + +BOOL WINAPI WTSQuerySessionInformationA(HANDLE hServer, DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, LPSTR* ppBuffer, + DWORD* pBytesReturned) +{ + WTSAPI_STUB_CALL_BOOL(QuerySessionInformationA, hServer, SessionId, WTSInfoClass, ppBuffer, + pBytesReturned); +} + +BOOL WINAPI WTSQueryUserConfigW(LPWSTR pServerName, LPWSTR pUserName, + WTS_CONFIG_CLASS WTSConfigClass, LPWSTR* ppBuffer, + DWORD* pBytesReturned) +{ + WTSAPI_STUB_CALL_BOOL(QueryUserConfigW, pServerName, pUserName, WTSConfigClass, ppBuffer, + pBytesReturned); +} + +BOOL WINAPI WTSQueryUserConfigA(LPSTR pServerName, LPSTR pUserName, WTS_CONFIG_CLASS WTSConfigClass, + LPSTR* ppBuffer, DWORD* pBytesReturned) +{ + WTSAPI_STUB_CALL_BOOL(QueryUserConfigA, pServerName, pUserName, WTSConfigClass, ppBuffer, + pBytesReturned); +} + +BOOL WINAPI WTSSetUserConfigW(LPWSTR pServerName, LPWSTR pUserName, WTS_CONFIG_CLASS WTSConfigClass, + LPWSTR pBuffer, DWORD DataLength) +{ + WTSAPI_STUB_CALL_BOOL(SetUserConfigW, pServerName, pUserName, WTSConfigClass, pBuffer, + DataLength); +} + +BOOL WINAPI WTSSetUserConfigA(LPSTR pServerName, LPSTR pUserName, WTS_CONFIG_CLASS WTSConfigClass, + LPSTR pBuffer, DWORD DataLength) +{ + WTSAPI_STUB_CALL_BOOL(SetUserConfigA, pServerName, pUserName, WTSConfigClass, pBuffer, + DataLength); +} + +BOOL WINAPI WTSSendMessageW(HANDLE hServer, DWORD SessionId, LPWSTR pTitle, DWORD TitleLength, + LPWSTR pMessage, DWORD MessageLength, DWORD Style, DWORD Timeout, + DWORD* pResponse, BOOL bWait) +{ + WTSAPI_STUB_CALL_BOOL(SendMessageW, hServer, SessionId, pTitle, TitleLength, pMessage, + MessageLength, Style, Timeout, pResponse, bWait); +} + +BOOL WINAPI WTSSendMessageA(HANDLE hServer, DWORD SessionId, LPSTR pTitle, DWORD TitleLength, + LPSTR pMessage, DWORD MessageLength, DWORD Style, DWORD Timeout, + DWORD* pResponse, BOOL bWait) +{ + WTSAPI_STUB_CALL_BOOL(SendMessageA, hServer, SessionId, pTitle, TitleLength, pMessage, + MessageLength, Style, Timeout, pResponse, bWait); +} + +BOOL WINAPI WTSDisconnectSession(HANDLE hServer, DWORD SessionId, BOOL bWait) +{ + WTSAPI_STUB_CALL_BOOL(DisconnectSession, hServer, SessionId, bWait); +} + +BOOL WINAPI WTSLogoffSession(HANDLE hServer, DWORD SessionId, BOOL bWait) +{ + WTSAPI_STUB_CALL_BOOL(LogoffSession, hServer, SessionId, bWait); +} + +BOOL WINAPI WTSShutdownSystem(HANDLE hServer, DWORD ShutdownFlag) +{ + WTSAPI_STUB_CALL_BOOL(ShutdownSystem, hServer, ShutdownFlag); +} + +BOOL WINAPI WTSWaitSystemEvent(HANDLE hServer, DWORD EventMask, DWORD* pEventFlags) +{ + WTSAPI_STUB_CALL_BOOL(WaitSystemEvent, hServer, EventMask, pEventFlags); +} + +HANDLE WINAPI WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, LPSTR pVirtualName) +{ + WTSAPI_STUB_CALL_HANDLE(VirtualChannelOpen, hServer, SessionId, pVirtualName); +} + +HANDLE WINAPI WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags) +{ + WTSAPI_STUB_CALL_HANDLE(VirtualChannelOpenEx, SessionId, pVirtualName, flags); +} + +BOOL WINAPI WTSVirtualChannelClose(HANDLE hChannelHandle) +{ + WTSAPI_STUB_CALL_BOOL(VirtualChannelClose, hChannelHandle); +} + +BOOL WINAPI WTSVirtualChannelRead(HANDLE hChannelHandle, ULONG TimeOut, PCHAR Buffer, + ULONG BufferSize, PULONG pBytesRead) +{ + WTSAPI_STUB_CALL_BOOL(VirtualChannelRead, hChannelHandle, TimeOut, Buffer, BufferSize, + pBytesRead); +} + +BOOL WINAPI WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, ULONG Length, + PULONG pBytesWritten) +{ + WTSAPI_STUB_CALL_BOOL(VirtualChannelWrite, hChannelHandle, Buffer, Length, pBytesWritten); +} + +BOOL WINAPI WTSVirtualChannelPurgeInput(HANDLE hChannelHandle) +{ + WTSAPI_STUB_CALL_BOOL(VirtualChannelPurgeInput, hChannelHandle); +} + +BOOL WINAPI WTSVirtualChannelPurgeOutput(HANDLE hChannelHandle) +{ + WTSAPI_STUB_CALL_BOOL(VirtualChannelPurgeOutput, hChannelHandle); +} + +BOOL WINAPI WTSVirtualChannelQuery(HANDLE hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, + PVOID* ppBuffer, DWORD* pBytesReturned) +{ + WTSAPI_STUB_CALL_BOOL(VirtualChannelQuery, hChannelHandle, WtsVirtualClass, ppBuffer, + pBytesReturned); +} + +VOID WINAPI WTSFreeMemory(PVOID pMemory) +{ + WTSAPI_STUB_CALL_VOID(FreeMemory, pMemory); +} + +BOOL WINAPI WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, ULONG NumberOfEntries) +{ + WTSAPI_STUB_CALL_BOOL(FreeMemoryExW, WTSTypeClass, pMemory, NumberOfEntries); +} + +BOOL WINAPI WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, ULONG NumberOfEntries) +{ + WTSAPI_STUB_CALL_BOOL(FreeMemoryExA, WTSTypeClass, pMemory, NumberOfEntries); +} + +BOOL WINAPI WTSRegisterSessionNotification(HWND hWnd, DWORD dwFlags) +{ + WTSAPI_STUB_CALL_BOOL(RegisterSessionNotification, hWnd, dwFlags); +} + +BOOL WINAPI WTSUnRegisterSessionNotification(HWND hWnd) +{ + WTSAPI_STUB_CALL_BOOL(UnRegisterSessionNotification, hWnd); +} + +BOOL WINAPI WTSRegisterSessionNotificationEx(HANDLE hServer, HWND hWnd, DWORD dwFlags) +{ + WTSAPI_STUB_CALL_BOOL(RegisterSessionNotificationEx, hServer, hWnd, dwFlags); +} + +BOOL WINAPI WTSUnRegisterSessionNotificationEx(HANDLE hServer, HWND hWnd) +{ + WTSAPI_STUB_CALL_BOOL(UnRegisterSessionNotificationEx, hServer, hWnd); +} + +BOOL WINAPI WTSQueryUserToken(ULONG SessionId, PHANDLE phToken) +{ + WTSAPI_STUB_CALL_BOOL(QueryUserToken, SessionId, phToken); +} + +BOOL WINAPI WTSEnumerateProcessesExW(HANDLE hServer, DWORD* pLevel, DWORD SessionId, + LPWSTR* ppProcessInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateProcessesExW, hServer, pLevel, SessionId, ppProcessInfo, pCount); +} + +BOOL WINAPI WTSEnumerateProcessesExA(HANDLE hServer, DWORD* pLevel, DWORD SessionId, + LPSTR* ppProcessInfo, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateProcessesExA, hServer, pLevel, SessionId, ppProcessInfo, pCount); +} + +BOOL WINAPI WTSEnumerateListenersW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + PWTSLISTENERNAMEW pListeners, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateListenersW, hServer, pReserved, Reserved, pListeners, pCount); +} + +BOOL WINAPI WTSEnumerateListenersA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + PWTSLISTENERNAMEA pListeners, DWORD* pCount) +{ + WTSAPI_STUB_CALL_BOOL(EnumerateListenersA, hServer, pReserved, Reserved, pListeners, pCount); +} + +BOOL WINAPI WTSQueryListenerConfigW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, PWTSLISTENERCONFIGW pBuffer) +{ + WTSAPI_STUB_CALL_BOOL(QueryListenerConfigW, hServer, pReserved, Reserved, pListenerName, + pBuffer); +} + +BOOL WINAPI WTSQueryListenerConfigA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, PWTSLISTENERCONFIGA pBuffer) +{ + WTSAPI_STUB_CALL_BOOL(QueryListenerConfigA, hServer, pReserved, Reserved, pListenerName, + pBuffer); +} + +BOOL WINAPI WTSCreateListenerW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, PWTSLISTENERCONFIGW pBuffer, DWORD flag) +{ + WTSAPI_STUB_CALL_BOOL(CreateListenerW, hServer, pReserved, Reserved, pListenerName, pBuffer, + flag); +} + +BOOL WINAPI WTSCreateListenerA(HANDLE hServer, PVOID pReserved, DWORD Reserved, LPSTR pListenerName, + PWTSLISTENERCONFIGA pBuffer, DWORD flag) +{ + WTSAPI_STUB_CALL_BOOL(CreateListenerA, hServer, pReserved, Reserved, pListenerName, pBuffer, + flag); +} + +BOOL WINAPI WTSSetListenerSecurityW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + WTSAPI_STUB_CALL_BOOL(SetListenerSecurityW, hServer, pReserved, Reserved, pListenerName, + SecurityInformation, pSecurityDescriptor); +} + +BOOL WINAPI WTSSetListenerSecurityA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + WTSAPI_STUB_CALL_BOOL(SetListenerSecurityA, hServer, pReserved, Reserved, pListenerName, + SecurityInformation, pSecurityDescriptor); +} + +BOOL WINAPI WTSGetListenerSecurityW(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPWSTR pListenerName, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, + LPDWORD lpnLengthNeeded) +{ + WTSAPI_STUB_CALL_BOOL(GetListenerSecurityW, hServer, pReserved, Reserved, pListenerName, + SecurityInformation, pSecurityDescriptor, nLength, lpnLengthNeeded); +} + +BOOL WINAPI WTSGetListenerSecurityA(HANDLE hServer, PVOID pReserved, DWORD Reserved, + LPSTR pListenerName, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, + LPDWORD lpnLengthNeeded) +{ + WTSAPI_STUB_CALL_BOOL(GetListenerSecurityA, hServer, pReserved, Reserved, pListenerName, + SecurityInformation, pSecurityDescriptor, nLength, lpnLengthNeeded); +} + +BOOL CDECL WTSEnableChildSessions(BOOL bEnable) +{ + WTSAPI_STUB_CALL_BOOL(EnableChildSessions, bEnable); +} + +BOOL CDECL WTSIsChildSessionsEnabled(PBOOL pbEnabled) +{ + WTSAPI_STUB_CALL_BOOL(IsChildSessionsEnabled, pbEnabled); +} + +BOOL CDECL WTSGetChildSessionId(PULONG pSessionId) +{ + WTSAPI_STUB_CALL_BOOL(GetChildSessionId, pSessionId); +} + +BOOL CDECL WTSLogonUser(HANDLE hServer, LPCSTR username, LPCSTR password, LPCSTR domain) +{ + WTSAPI_STUB_CALL_BOOL(LogonUser, hServer, username, password, domain); +} + +BOOL CDECL WTSLogoffUser(HANDLE hServer) +{ + WTSAPI_STUB_CALL_BOOL(LogoffUser, hServer); +} + +#ifndef _WIN32 + +/** + * WTSGetActiveConsoleSessionId is declared in WinBase.h and exported by kernel32.dll + */ + +DWORD WINAPI WTSGetActiveConsoleSessionId(void) +{ + InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, NULL, NULL); + + if (!g_WtsApi || !g_WtsApi->pGetActiveConsoleSessionId) + return 0xFFFFFFFF; + + return g_WtsApi->pGetActiveConsoleSessionId(); +} + +#endif + +const CHAR* WTSErrorToString(UINT error) +{ + switch (error) + { + case CHANNEL_RC_OK: + return "CHANNEL_RC_OK"; + + case CHANNEL_RC_ALREADY_INITIALIZED: + return "CHANNEL_RC_ALREADY_INITIALIZED"; + + case CHANNEL_RC_NOT_INITIALIZED: + return "CHANNEL_RC_NOT_INITIALIZED"; + + case CHANNEL_RC_ALREADY_CONNECTED: + return "CHANNEL_RC_ALREADY_CONNECTED"; + + case CHANNEL_RC_NOT_CONNECTED: + return "CHANNEL_RC_NOT_CONNECTED"; + + case CHANNEL_RC_TOO_MANY_CHANNELS: + return "CHANNEL_RC_TOO_MANY_CHANNELS"; + + case CHANNEL_RC_BAD_CHANNEL: + return "CHANNEL_RC_BAD_CHANNEL"; + + case CHANNEL_RC_BAD_CHANNEL_HANDLE: + return "CHANNEL_RC_BAD_CHANNEL_HANDLE"; + + case CHANNEL_RC_NO_BUFFER: + return "CHANNEL_RC_NO_BUFFER"; + + case CHANNEL_RC_BAD_INIT_HANDLE: + return "CHANNEL_RC_BAD_INIT_HANDLE"; + + case CHANNEL_RC_NOT_OPEN: + return "CHANNEL_RC_NOT_OPEN"; + + case CHANNEL_RC_BAD_PROC: + return "CHANNEL_RC_BAD_PROC"; + + case CHANNEL_RC_NO_MEMORY: + return "CHANNEL_RC_NO_MEMORY"; + + case CHANNEL_RC_UNKNOWN_CHANNEL_NAME: + return "CHANNEL_RC_UNKNOWN_CHANNEL_NAME"; + + case CHANNEL_RC_ALREADY_OPEN: + return "CHANNEL_RC_ALREADY_OPEN"; + + case CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY: + return "CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY"; + + case CHANNEL_RC_NULL_DATA: + return "CHANNEL_RC_NULL_DATA"; + + case CHANNEL_RC_ZERO_LENGTH: + return "CHANNEL_RC_ZERO_LENGTH"; + + case CHANNEL_RC_INVALID_INSTANCE: + return "CHANNEL_RC_INVALID_INSTANCE"; + + case CHANNEL_RC_UNSUPPORTED_VERSION: + return "CHANNEL_RC_UNSUPPORTED_VERSION"; + + case CHANNEL_RC_INITIALIZATION_ERROR: + return "CHANNEL_RC_INITIALIZATION_ERROR"; + + default: + return "UNKNOWN"; + } +} + +const CHAR* WTSSessionStateToString(WTS_CONNECTSTATE_CLASS state) +{ + switch (state) + { + case WTSActive: + return "WTSActive"; + case WTSConnected: + return "WTSConnected"; + case WTSConnectQuery: + return "WTSConnectQuery"; + case WTSShadow: + return "WTSShadow"; + case WTSDisconnected: + return "WTSDisconnected"; + case WTSIdle: + return "WTSIdle"; + case WTSListen: + return "WTSListen"; + case WTSReset: + return "WTSReset"; + case WTSDown: + return "WTSDown"; + case WTSInit: + return "WTSInit"; + } + return "INVALID_STATE"; +} + +BOOL WTSRegisterWtsApiFunctionTable(const WtsApiFunctionTable* table) +{ + /* Use InitOnceExecuteOnce here as well - otherwise a table set with this + function is overriden on the first use of a WTS* API call (due to + wtsapiInitOnce not being set). */ + union + { + const void* cpv; + void* pv; + } cnv; + cnv.cpv = table; + InitOnceExecuteOnce(&wtsapiInitOnce, InitializeWtsApiStubs, cnv.pv, NULL); + if (!g_WtsApi) + return FALSE; + return TRUE; +} + +static BOOL LoadAndInitialize(char* library) +{ + INIT_WTSAPI_FN pInitWtsApi = NULL; + g_WtsApiModule = LoadLibraryX(library); + + if (!g_WtsApiModule) + return FALSE; + + pInitWtsApi = (INIT_WTSAPI_FN)GetProcAddress(g_WtsApiModule, "InitWtsApi"); + + if (!pInitWtsApi) + { + return FALSE; + } + + g_WtsApi = pInitWtsApi(); + return TRUE; +} + +static void InitializeWtsApiStubs_Env(void) +{ + DWORD nSize = 0; + char* env = NULL; + LPCSTR wts = "WTSAPI_LIBRARY"; + + if (g_WtsApi) + return; + + nSize = GetEnvironmentVariableA(wts, NULL, 0); + + if (!nSize) + return; + + env = (LPSTR)malloc(nSize); + if (env) + { + if (GetEnvironmentVariableA(wts, env, nSize) == nSize - 1) + LoadAndInitialize(env); + free(env); + } +} + +#define FREERDS_LIBRARY_NAME "libfreerds-fdsapi.so" + +static void InitializeWtsApiStubs_FreeRDS(void) +{ + wIniFile* ini = NULL; + const char* prefix = NULL; + const char* libdir = NULL; + + if (g_WtsApi) + return; + + ini = IniFile_New(); + + if (IniFile_ReadFile(ini, "/var/run/freerds.instance") < 0) + { + IniFile_Free(ini); + WLog_ERR(TAG, "failed to parse freerds.instance"); + LoadAndInitialize(FREERDS_LIBRARY_NAME); + return; + } + + prefix = IniFile_GetKeyValueString(ini, "FreeRDS", "prefix"); + libdir = IniFile_GetKeyValueString(ini, "FreeRDS", "libdir"); + WLog_INFO(TAG, "FreeRDS (prefix / libdir): %s / %s", prefix, libdir); + + if (prefix && libdir) + { + char* prefix_libdir = NULL; + char* wtsapi_library = NULL; + prefix_libdir = GetCombinedPath(prefix, libdir); + wtsapi_library = GetCombinedPath(prefix_libdir, FREERDS_LIBRARY_NAME); + + if (wtsapi_library) + { + LoadAndInitialize(wtsapi_library); + } + + free(prefix_libdir); + free(wtsapi_library); + } + + IniFile_Free(ini); +} + +static BOOL CALLBACK InitializeWtsApiStubs(PINIT_ONCE once, PVOID param, PVOID* context) +{ + WINPR_UNUSED(once); + WINPR_UNUSED(context); + if (param) + { + g_WtsApi = (const WtsApiFunctionTable*)param; + return TRUE; + } + + InitializeWtsApiStubs_Env(); + +#ifdef _WIN32 + WtsApi32_InitializeWtsApi(); +#endif + + if (!g_WtsApi) + InitializeWtsApiStubs_FreeRDS(); + + return TRUE; +} diff --git a/winpr/libwinpr/wtsapi/wtsapi_win32.c b/winpr/libwinpr/wtsapi/wtsapi_win32.c new file mode 100644 index 0000000..cac1624 --- /dev/null +++ b/winpr/libwinpr/wtsapi/wtsapi_win32.c @@ -0,0 +1,808 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Terminal Services API + * + * Copyright 2013-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include + +#include "wtsapi_win32.h" + +#include "../log.h" + +#include + +#pragma comment(lib, "ntdll.lib") + +#define WTSAPI_CHANNEL_MAGIC 0x44484356 +#define TAG WINPR_TAG("wtsapi") + +typedef struct +{ + UINT32 magic; + HANDLE hServer; + DWORD SessionId; + HANDLE hFile; + HANDLE hEvent; + char* VirtualName; + + DWORD flags; + BYTE* chunk; + BOOL dynamic; + BOOL readSync; + BOOL readAsync; + BOOL readDone; + UINT32 readSize; + UINT32 readOffset; + BYTE* readBuffer; + BOOL showProtocol; + BOOL waitObjectMode; + OVERLAPPED overlapped; + CHANNEL_PDU_HEADER* header; +} WTSAPI_CHANNEL; + +static BOOL g_Initialized = FALSE; +static HMODULE g_WinStaModule = NULL; + +typedef HANDLE(WINAPI* fnWinStationVirtualOpen)(HANDLE hServer, DWORD SessionId, + LPSTR pVirtualName); +typedef HANDLE(WINAPI* fnWinStationVirtualOpenEx)(HANDLE hServer, DWORD SessionId, + LPSTR pVirtualName, DWORD flags); + +static fnWinStationVirtualOpen pfnWinStationVirtualOpen = NULL; +static fnWinStationVirtualOpenEx pfnWinStationVirtualOpenEx = NULL; + +BOOL WINAPI Win32_WTSVirtualChannelClose(HANDLE hChannel); + +/** + * NOTE !! + * An application using the WinPR wtsapi frees memory via WTSFreeMemory, which + * might be mapped to Win32_WTSFreeMemory. Latter does not know if the passed + * pointer was allocated by a function in wtsapi32.dll or in some internal + * code below. The WTSFreeMemory implementation in all Windows wtsapi32.dll + * versions up to Windows 10 uses LocalFree since all its allocating functions + * use LocalAlloc() internally. + * For that reason we also have to use LocalAlloc() for any memory returned by + * our WinPR wtsapi functions. + * + * To be safe we only use the _wts_malloc, _wts_calloc, _wts_free wrappers + * for memory managment the code below. + */ + +static void* _wts_malloc(size_t size) +{ +#ifdef _UWP + return malloc(size); +#else + return (PVOID)LocalAlloc(LMEM_FIXED, size); +#endif +} + +static void* _wts_calloc(size_t nmemb, size_t size) +{ +#ifdef _UWP + return calloc(nmemb, size); +#else + return (PVOID)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, nmemb * size); +#endif +} + +static void _wts_free(void* ptr) +{ +#ifdef _UWP + free(ptr); +#else + LocalFree((HLOCAL)ptr); +#endif +} + +BOOL Win32_WTSVirtualChannelReadAsync(WTSAPI_CHANNEL* pChannel) +{ + BOOL status = TRUE; + DWORD numBytes = 0; + + if (pChannel->readAsync) + return TRUE; + + ZeroMemory(&(pChannel->overlapped), sizeof(OVERLAPPED)); + pChannel->overlapped.hEvent = pChannel->hEvent; + ResetEvent(pChannel->hEvent); + + if (pChannel->showProtocol) + { + ZeroMemory(pChannel->header, sizeof(CHANNEL_PDU_HEADER)); + + status = ReadFile(pChannel->hFile, pChannel->header, sizeof(CHANNEL_PDU_HEADER), &numBytes, + &(pChannel->overlapped)); + } + else + { + status = ReadFile(pChannel->hFile, pChannel->chunk, CHANNEL_CHUNK_LENGTH, &numBytes, + &(pChannel->overlapped)); + + if (status) + { + pChannel->readOffset = 0; + pChannel->header->length = numBytes; + + pChannel->readDone = TRUE; + SetEvent(pChannel->hEvent); + + return TRUE; + } + } + + if (status) + { + WLog_ERR(TAG, "Unexpected ReadFile status: %" PRId32 " numBytes: %" PRIu32 "", status, + numBytes); + return FALSE; /* ReadFile should return FALSE and set ERROR_IO_PENDING */ + } + + if (GetLastError() != ERROR_IO_PENDING) + { + WLog_ERR(TAG, "ReadFile: GetLastError() = %" PRIu32 "", GetLastError()); + return FALSE; + } + + pChannel->readAsync = TRUE; + + return TRUE; +} + +HANDLE WINAPI Win32_WTSVirtualChannelOpen_Internal(HANDLE hServer, DWORD SessionId, + LPSTR pVirtualName, DWORD flags) +{ + HANDLE hFile; + HANDLE hChannel; + WTSAPI_CHANNEL* pChannel; + size_t virtualNameLen; + + virtualNameLen = pVirtualName ? strlen(pVirtualName) : 0; + + if (!virtualNameLen) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + if (!pfnWinStationVirtualOpenEx) + { + SetLastError(ERROR_INVALID_FUNCTION); + return NULL; + } + + hFile = pfnWinStationVirtualOpenEx(hServer, SessionId, pVirtualName, flags); + + if (!hFile) + return NULL; + + pChannel = (WTSAPI_CHANNEL*)_wts_calloc(1, sizeof(WTSAPI_CHANNEL)); + + if (!pChannel) + { + CloseHandle(hFile); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + hChannel = (HANDLE)pChannel; + pChannel->magic = WTSAPI_CHANNEL_MAGIC; + pChannel->hServer = hServer; + pChannel->SessionId = SessionId; + pChannel->hFile = hFile; + pChannel->VirtualName = _wts_calloc(1, virtualNameLen + 1); + if (!pChannel->VirtualName) + { + CloseHandle(hFile); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + _wts_free(pChannel); + return NULL; + } + memcpy(pChannel->VirtualName, pVirtualName, virtualNameLen); + + pChannel->flags = flags; + pChannel->dynamic = (flags & WTS_CHANNEL_OPTION_DYNAMIC) ? TRUE : FALSE; + + pChannel->showProtocol = pChannel->dynamic; + + pChannel->readSize = CHANNEL_PDU_LENGTH; + pChannel->readBuffer = (BYTE*)_wts_malloc(pChannel->readSize); + + pChannel->header = (CHANNEL_PDU_HEADER*)pChannel->readBuffer; + pChannel->chunk = &(pChannel->readBuffer[sizeof(CHANNEL_PDU_HEADER)]); + + pChannel->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + pChannel->overlapped.hEvent = pChannel->hEvent; + + if (!pChannel->hEvent || !pChannel->VirtualName || !pChannel->readBuffer) + { + Win32_WTSVirtualChannelClose(hChannel); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + return hChannel; +} + +HANDLE WINAPI Win32_WTSVirtualChannelOpen(HANDLE hServer, DWORD SessionId, LPSTR pVirtualName) +{ + return Win32_WTSVirtualChannelOpen_Internal(hServer, SessionId, pVirtualName, 0); +} + +HANDLE WINAPI Win32_WTSVirtualChannelOpenEx(DWORD SessionId, LPSTR pVirtualName, DWORD flags) +{ + return Win32_WTSVirtualChannelOpen_Internal(0, SessionId, pVirtualName, flags); +} + +BOOL WINAPI Win32_WTSVirtualChannelClose(HANDLE hChannel) +{ + BOOL status = TRUE; + WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel; + + if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (pChannel->hFile) + { + if (pChannel->readAsync) + { + CancelIo(pChannel->hFile); + pChannel->readAsync = FALSE; + } + + status = CloseHandle(pChannel->hFile); + pChannel->hFile = NULL; + } + + if (pChannel->hEvent) + { + CloseHandle(pChannel->hEvent); + pChannel->hEvent = NULL; + } + + if (pChannel->VirtualName) + { + _wts_free(pChannel->VirtualName); + pChannel->VirtualName = NULL; + } + + if (pChannel->readBuffer) + { + _wts_free(pChannel->readBuffer); + pChannel->readBuffer = NULL; + } + + pChannel->magic = 0; + _wts_free(pChannel); + + return status; +} + +BOOL WINAPI Win32_WTSVirtualChannelRead_Static(WTSAPI_CHANNEL* pChannel, DWORD dwMilliseconds, + LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesTransferred) +{ + if (pChannel->readDone) + { + DWORD numBytesRead = 0; + DWORD numBytesToRead = 0; + + *lpNumberOfBytesTransferred = 0; + + numBytesToRead = nNumberOfBytesToRead; + + if (numBytesToRead > (pChannel->header->length - pChannel->readOffset)) + numBytesToRead = (pChannel->header->length - pChannel->readOffset); + + CopyMemory(lpBuffer, &(pChannel->chunk[pChannel->readOffset]), numBytesToRead); + *lpNumberOfBytesTransferred += numBytesToRead; + pChannel->readOffset += numBytesToRead; + + if (pChannel->readOffset != pChannel->header->length) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + else + { + pChannel->readDone = FALSE; + Win32_WTSVirtualChannelReadAsync(pChannel); + } + + return TRUE; + } + else if (pChannel->readSync) + { + BOOL bSuccess; + OVERLAPPED overlapped = { 0 }; + DWORD numBytesRead = 0; + DWORD numBytesToRead = 0; + + *lpNumberOfBytesTransferred = 0; + + numBytesToRead = nNumberOfBytesToRead; + + if (numBytesToRead > (pChannel->header->length - pChannel->readOffset)) + numBytesToRead = (pChannel->header->length - pChannel->readOffset); + + if (ReadFile(pChannel->hFile, lpBuffer, numBytesToRead, &numBytesRead, &overlapped)) + { + *lpNumberOfBytesTransferred += numBytesRead; + pChannel->readOffset += numBytesRead; + + if (pChannel->readOffset != pChannel->header->length) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + pChannel->readSync = FALSE; + Win32_WTSVirtualChannelReadAsync(pChannel); + + return TRUE; + } + + if (GetLastError() != ERROR_IO_PENDING) + return FALSE; + + bSuccess = GetOverlappedResult(pChannel->hFile, &overlapped, &numBytesRead, TRUE); + + if (!bSuccess) + return FALSE; + + *lpNumberOfBytesTransferred += numBytesRead; + pChannel->readOffset += numBytesRead; + + if (pChannel->readOffset != pChannel->header->length) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + pChannel->readSync = FALSE; + Win32_WTSVirtualChannelReadAsync(pChannel); + + return TRUE; + } + else if (pChannel->readAsync) + { + BOOL bSuccess; + DWORD numBytesRead = 0; + DWORD numBytesToRead = 0; + + *lpNumberOfBytesTransferred = 0; + + if (WaitForSingleObject(pChannel->hEvent, dwMilliseconds) != WAIT_TIMEOUT) + { + bSuccess = + GetOverlappedResult(pChannel->hFile, &(pChannel->overlapped), &numBytesRead, TRUE); + + pChannel->readOffset = 0; + pChannel->header->length = numBytesRead; + + if (!bSuccess && (GetLastError() != ERROR_MORE_DATA)) + return FALSE; + + numBytesToRead = nNumberOfBytesToRead; + + if (numBytesRead < numBytesToRead) + { + numBytesToRead = numBytesRead; + nNumberOfBytesToRead = numBytesRead; + } + + CopyMemory(lpBuffer, pChannel->chunk, numBytesToRead); + *lpNumberOfBytesTransferred += numBytesToRead; + lpBuffer = (BYTE*)lpBuffer + numBytesToRead; + nNumberOfBytesToRead -= numBytesToRead; + pChannel->readOffset += numBytesToRead; + + pChannel->readAsync = FALSE; + + if (!nNumberOfBytesToRead) + { + Win32_WTSVirtualChannelReadAsync(pChannel); + return TRUE; + } + + pChannel->readSync = TRUE; + + numBytesRead = 0; + + bSuccess = Win32_WTSVirtualChannelRead_Static(pChannel, dwMilliseconds, lpBuffer, + nNumberOfBytesToRead, &numBytesRead); + + *lpNumberOfBytesTransferred += numBytesRead; + return bSuccess; + } + else + { + SetLastError(ERROR_IO_INCOMPLETE); + return FALSE; + } + } + + return FALSE; +} + +BOOL WINAPI Win32_WTSVirtualChannelRead_Dynamic(WTSAPI_CHANNEL* pChannel, DWORD dwMilliseconds, + LPVOID lpBuffer, DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesTransferred) +{ + if (pChannel->readSync) + { + BOOL bSuccess; + OVERLAPPED overlapped = { 0 }; + DWORD numBytesRead = 0; + DWORD numBytesToRead = 0; + + *lpNumberOfBytesTransferred = 0; + + numBytesToRead = nNumberOfBytesToRead; + + if (numBytesToRead > (pChannel->header->length - pChannel->readOffset)) + numBytesToRead = (pChannel->header->length - pChannel->readOffset); + + if (ReadFile(pChannel->hFile, lpBuffer, numBytesToRead, &numBytesRead, &overlapped)) + { + *lpNumberOfBytesTransferred += numBytesRead; + pChannel->readOffset += numBytesRead; + + if (pChannel->readOffset != pChannel->header->length) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + pChannel->readSync = FALSE; + Win32_WTSVirtualChannelReadAsync(pChannel); + + return TRUE; + } + + if (GetLastError() != ERROR_IO_PENDING) + return FALSE; + + bSuccess = GetOverlappedResult(pChannel->hFile, &overlapped, &numBytesRead, TRUE); + + if (!bSuccess) + return FALSE; + + *lpNumberOfBytesTransferred += numBytesRead; + pChannel->readOffset += numBytesRead; + + if (pChannel->readOffset != pChannel->header->length) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + pChannel->readSync = FALSE; + Win32_WTSVirtualChannelReadAsync(pChannel); + + return TRUE; + } + else if (pChannel->readAsync) + { + BOOL bSuccess; + DWORD numBytesRead = 0; + + *lpNumberOfBytesTransferred = 0; + + if (WaitForSingleObject(pChannel->hEvent, dwMilliseconds) != WAIT_TIMEOUT) + { + bSuccess = + GetOverlappedResult(pChannel->hFile, &(pChannel->overlapped), &numBytesRead, TRUE); + + if (pChannel->showProtocol) + { + if (numBytesRead != sizeof(CHANNEL_PDU_HEADER)) + return FALSE; + + if (!bSuccess && (GetLastError() != ERROR_MORE_DATA)) + return FALSE; + + CopyMemory(lpBuffer, pChannel->header, numBytesRead); + *lpNumberOfBytesTransferred += numBytesRead; + lpBuffer = (BYTE*)lpBuffer + numBytesRead; + nNumberOfBytesToRead -= numBytesRead; + } + + pChannel->readAsync = FALSE; + + if (!pChannel->header->length) + { + Win32_WTSVirtualChannelReadAsync(pChannel); + return TRUE; + } + + pChannel->readSync = TRUE; + pChannel->readOffset = 0; + + if (!nNumberOfBytesToRead) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + numBytesRead = 0; + + bSuccess = Win32_WTSVirtualChannelRead_Dynamic(pChannel, dwMilliseconds, lpBuffer, + nNumberOfBytesToRead, &numBytesRead); + + *lpNumberOfBytesTransferred += numBytesRead; + return bSuccess; + } + else + { + SetLastError(ERROR_IO_INCOMPLETE); + return FALSE; + } + } + + return FALSE; +} + +BOOL WINAPI Win32_WTSVirtualChannelRead(HANDLE hChannel, DWORD dwMilliseconds, LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, + LPDWORD lpNumberOfBytesTransferred) +{ + WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel; + + if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!pChannel->waitObjectMode) + { + OVERLAPPED overlapped = { 0 }; + + if (ReadFile(pChannel->hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesTransferred, + &overlapped)) + return TRUE; + + if (GetLastError() != ERROR_IO_PENDING) + return FALSE; + + if (!dwMilliseconds) + { + CancelIo(pChannel->hFile); + *lpNumberOfBytesTransferred = 0; + return TRUE; + } + + if (WaitForSingleObject(pChannel->hFile, dwMilliseconds) != WAIT_TIMEOUT) + return GetOverlappedResult(pChannel->hFile, &overlapped, lpNumberOfBytesTransferred, + FALSE); + + CancelIo(pChannel->hFile); + SetLastError(ERROR_IO_INCOMPLETE); + + return FALSE; + } + else + { + if (pChannel->dynamic) + { + return Win32_WTSVirtualChannelRead_Dynamic(pChannel, dwMilliseconds, lpBuffer, + nNumberOfBytesToRead, + lpNumberOfBytesTransferred); + } + else + { + return Win32_WTSVirtualChannelRead_Static(pChannel, dwMilliseconds, lpBuffer, + nNumberOfBytesToRead, + lpNumberOfBytesTransferred); + } + } + + return FALSE; +} + +BOOL WINAPI Win32_WTSVirtualChannelWrite(HANDLE hChannel, LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, + LPDWORD lpNumberOfBytesTransferred) +{ + OVERLAPPED overlapped = { 0 }; + WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannel; + + if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (WriteFile(pChannel->hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesTransferred, + &overlapped)) + return TRUE; + + if (GetLastError() == ERROR_IO_PENDING) + return GetOverlappedResult(pChannel->hFile, &overlapped, lpNumberOfBytesTransferred, TRUE); + + return FALSE; +} + +#ifndef FILE_DEVICE_TERMSRV +#define FILE_DEVICE_TERMSRV 0x00000038 +#endif + +BOOL Win32_WTSVirtualChannelPurge_Internal(HANDLE hChannelHandle, ULONG IoControlCode) +{ + DWORD error; + NTSTATUS ntstatus; + IO_STATUS_BLOCK ioStatusBlock; + WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannelHandle; + + if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + ntstatus = + NtDeviceIoControlFile(pChannel->hFile, 0, 0, 0, &ioStatusBlock, IoControlCode, 0, 0, 0, 0); + + if (ntstatus == STATUS_PENDING) + { + ntstatus = NtWaitForSingleObject(pChannel->hFile, 0, 0); + + if (ntstatus >= 0) + ntstatus = ioStatusBlock.Status; + } + + if (ntstatus == STATUS_BUFFER_OVERFLOW) + { + ntstatus = STATUS_BUFFER_TOO_SMALL; + error = RtlNtStatusToDosError(ntstatus); + SetLastError(error); + return FALSE; + } + + if (ntstatus < 0) + { + error = RtlNtStatusToDosError(ntstatus); + SetLastError(error); + return FALSE; + } + + return TRUE; +} + +BOOL WINAPI Win32_WTSVirtualChannelPurgeInput(HANDLE hChannelHandle) +{ + return Win32_WTSVirtualChannelPurge_Internal(hChannelHandle, + (FILE_DEVICE_TERMSRV << 16) | 0x0107); +} + +BOOL WINAPI Win32_WTSVirtualChannelPurgeOutput(HANDLE hChannelHandle) +{ + return Win32_WTSVirtualChannelPurge_Internal(hChannelHandle, + (FILE_DEVICE_TERMSRV << 16) | 0x010B); +} + +BOOL WINAPI Win32_WTSVirtualChannelQuery(HANDLE hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, + PVOID* ppBuffer, DWORD* pBytesReturned) +{ + WTSAPI_CHANNEL* pChannel = (WTSAPI_CHANNEL*)hChannelHandle; + + if (!pChannel || (pChannel->magic != WTSAPI_CHANNEL_MAGIC)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (WtsVirtualClass == WTSVirtualClientData) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + else if (WtsVirtualClass == WTSVirtualFileHandle) + { + *pBytesReturned = sizeof(HANDLE); + *ppBuffer = _wts_calloc(1, *pBytesReturned); + + if (*ppBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + CopyMemory(*ppBuffer, &(pChannel->hFile), *pBytesReturned); + } + else if (WtsVirtualClass == WTSVirtualEventHandle) + { + *pBytesReturned = sizeof(HANDLE); + *ppBuffer = _wts_calloc(1, *pBytesReturned); + + if (*ppBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + CopyMemory(*ppBuffer, &(pChannel->hEvent), *pBytesReturned); + + Win32_WTSVirtualChannelReadAsync(pChannel); + pChannel->waitObjectMode = TRUE; + } + else + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return TRUE; +} + +VOID WINAPI Win32_WTSFreeMemory(PVOID pMemory) +{ + _wts_free(pMemory); +} + +BOOL WINAPI Win32_WTSFreeMemoryExW(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, + ULONG NumberOfEntries) +{ + return FALSE; +} + +BOOL WINAPI Win32_WTSFreeMemoryExA(WTS_TYPE_CLASS WTSTypeClass, PVOID pMemory, + ULONG NumberOfEntries) +{ + return WTSFreeMemoryExW(WTSTypeClass, pMemory, NumberOfEntries); +} + +BOOL Win32_InitializeWinSta(PWtsApiFunctionTable pWtsApi) +{ + g_WinStaModule = LoadLibraryA("winsta.dll"); + + if (!g_WinStaModule) + return FALSE; + + pfnWinStationVirtualOpen = + (fnWinStationVirtualOpen)GetProcAddress(g_WinStaModule, "WinStationVirtualOpen"); + pfnWinStationVirtualOpenEx = + (fnWinStationVirtualOpenEx)GetProcAddress(g_WinStaModule, "WinStationVirtualOpenEx"); + + if (!pfnWinStationVirtualOpen | !pfnWinStationVirtualOpenEx) + return FALSE; + + pWtsApi->pVirtualChannelOpen = Win32_WTSVirtualChannelOpen; + pWtsApi->pVirtualChannelOpenEx = Win32_WTSVirtualChannelOpenEx; + pWtsApi->pVirtualChannelClose = Win32_WTSVirtualChannelClose; + pWtsApi->pVirtualChannelRead = Win32_WTSVirtualChannelRead; + pWtsApi->pVirtualChannelWrite = Win32_WTSVirtualChannelWrite; + pWtsApi->pVirtualChannelPurgeInput = Win32_WTSVirtualChannelPurgeInput; + pWtsApi->pVirtualChannelPurgeOutput = Win32_WTSVirtualChannelPurgeOutput; + pWtsApi->pVirtualChannelQuery = Win32_WTSVirtualChannelQuery; + pWtsApi->pFreeMemory = Win32_WTSFreeMemory; + // pWtsApi->pFreeMemoryExW = Win32_WTSFreeMemoryExW; + // pWtsApi->pFreeMemoryExA = Win32_WTSFreeMemoryExA; + + return TRUE; +} diff --git a/winpr/libwinpr/wtsapi/wtsapi_win32.h b/winpr/libwinpr/wtsapi/wtsapi_win32.h new file mode 100644 index 0000000..7d43165 --- /dev/null +++ b/winpr/libwinpr/wtsapi/wtsapi_win32.h @@ -0,0 +1,27 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Terminal Services API + * + * Copyright 2013-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WTSAPI_WIN32_PRIVATE_H +#define WINPR_WTSAPI_WIN32_PRIVATE_H + +#include + +BOOL Win32_InitializeWinSta(PWtsApiFunctionTable pWtsApi); + +#endif /* WINPR_WTSAPI_WIN32_PRIVATE_H */ diff --git a/winpr/test/CMakeLists.txt b/winpr/test/CMakeLists.txt new file mode 100644 index 0000000..c7837a1 --- /dev/null +++ b/winpr/test/CMakeLists.txt @@ -0,0 +1,24 @@ + +set(MODULE_NAME "TestWinPR") +set(MODULE_PREFIX "TEST_WINPR") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS TestIntrinsics.c TestTypes.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/test/TestIntrinsics.c b/winpr/test/TestIntrinsics.c new file mode 100644 index 0000000..2198d67 --- /dev/null +++ b/winpr/test/TestIntrinsics.c @@ -0,0 +1,121 @@ +#include +#include +#include + +#include + +static BOOL g_LZCNT = FALSE; + +static INLINE UINT32 lzcnt_s(UINT32 x) +{ + if (!x) + return 32; + + if (!g_LZCNT) + { + UINT32 y = 0; + int n = 32; + y = x >> 16; + if (y != 0) + { + n = n - 16; + x = y; + } + y = x >> 8; + if (y != 0) + { + n = n - 8; + x = y; + } + y = x >> 4; + if (y != 0) + { + n = n - 4; + x = y; + } + y = x >> 2; + if (y != 0) + { + n = n - 2; + x = y; + } + y = x >> 1; + if (y != 0) + return n - 2; + return n - x; + } + + return __lzcnt(x); +} + +static int test_lzcnt(void) +{ + if (lzcnt_s(0x1) != 31) + { + fprintf(stderr, "__lzcnt(0x1) != 31: %" PRIu32 "\n", __lzcnt(0x1)); + return -1; + } + + if (lzcnt_s(0xFF) != 24) + { + fprintf(stderr, "__lzcnt(0xFF) != 24\n"); + return -1; + } + + if (lzcnt_s(0xFFFF) != 16) + { + fprintf(stderr, "__lzcnt(0xFFFF) != 16\n"); + return -1; + } + + if (lzcnt_s(0xFFFFFF) != 8) + { + fprintf(stderr, "__lzcnt(0xFFFFFF) != 8\n"); + return -1; + } + + if (lzcnt_s(0xFFFFFFFF) != 0) + { + fprintf(stderr, "__lzcnt(0xFFFFFFFF) != 0\n"); + return -1; + } + + return 0; +} + +static int test_lzcnt16(void) +{ + if (__lzcnt16(0x1) != 15) + { + fprintf(stderr, "__lzcnt16(0x1) != 15\n"); + return -1; + } + + if (__lzcnt16(0xFF) != 8) + { + fprintf(stderr, "__lzcnt16(0xFF) != 8\n"); + return -1; + } + + if (__lzcnt16(0xFFFF) != 0) + { + fprintf(stderr, "__lzcnt16(0xFFFF) != 0\n"); + return -1; + } + + return 0; +} + +int TestIntrinsics(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + g_LZCNT = IsProcessorFeaturePresentEx(PF_EX_LZCNT); + + printf("LZCNT available: %" PRId32 "\n", g_LZCNT); + + // test_lzcnt16(); + return test_lzcnt(); +} diff --git a/winpr/test/TestTypes.c b/winpr/test/TestTypes.c new file mode 100644 index 0000000..482c87c --- /dev/null +++ b/winpr/test/TestTypes.c @@ -0,0 +1,214 @@ +/** + * CTest for winpr types and macros + * + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 Norbert Federa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +static BOOL test_co_errors(void) +{ + const LONG should[] = { + (LONG)0x80004006l, (LONG)0x80004007l, (LONG)0x80004008l, (LONG)0x80004009l, + (LONG)0x8000400Al, (LONG)0x8000400Bl, (LONG)0x8000400Cl, (LONG)0x8000400Dl, + (LONG)0x8000400El, (LONG)0x8000400Fl, (LONG)0x80004010l, (LONG)0x80004011l, + (LONG)0x80004012l, (LONG)0x80004013l, (LONG)0x80004014l, (LONG)0x80004015l, + (LONG)0x80004016l, (LONG)0x80004017l, (LONG)0x80004018l, (LONG)0x80004019l, + (LONG)0x8000401Al, (LONG)0x8000401Bl, (LONG)0x8000401Cl, (LONG)0x8000401Dl, + (LONG)0x8000401El, (LONG)0x8000401Fl, (LONG)0x80004020l, (LONG)0x80004021l, + (LONG)0x80004022l, (LONG)0x80004023l, (LONG)0x80004024l, (LONG)0x80004025l, + (LONG)0x80004026l, (LONG)0x80004027l, (LONG)0x80004028l, (LONG)0x80004029l, + (LONG)0x8000402Al, (LONG)0x8000402Bl, (LONG)0x80004030l, (LONG)0x80004031l, + (LONG)0x80004032l, (LONG)0x80004033l, (LONG)0x8000FFFFL, (LONG)0x80070005L, + (LONG)0x80070006L, (LONG)0x8007000EL, (LONG)0x80070057L, (LONG)0x80004001L, + (LONG)0x80004002L, (LONG)0x80004003L, (LONG)0x80004004L, (LONG)0x80004005L + }; + const LONG are[] = { CO_E_INIT_TLS, + CO_E_INIT_SHARED_ALLOCATOR, + CO_E_INIT_MEMORY_ALLOCATOR, + CO_E_INIT_CLASS_CACHE, + CO_E_INIT_RPC_CHANNEL, + CO_E_INIT_TLS_SET_CHANNEL_CONTROL, + CO_E_INIT_TLS_CHANNEL_CONTROL, + CO_E_INIT_UNACCEPTED_USER_ALLOCATOR, + CO_E_INIT_SCM_MUTEX_EXISTS, + CO_E_INIT_SCM_FILE_MAPPING_EXISTS, + CO_E_INIT_SCM_MAP_VIEW_OF_FILE, + CO_E_INIT_SCM_EXEC_FAILURE, + CO_E_INIT_ONLY_SINGLE_THREADED, + CO_E_CANT_REMOTE, + CO_E_BAD_SERVER_NAME, + CO_E_WRONG_SERVER_IDENTITY, + CO_E_OLE1DDE_DISABLED, + CO_E_RUNAS_SYNTAX, + CO_E_CREATEPROCESS_FAILURE, + CO_E_RUNAS_CREATEPROCESS_FAILURE, + CO_E_RUNAS_LOGON_FAILURE, + CO_E_LAUNCH_PERMSSION_DENIED, + CO_E_START_SERVICE_FAILURE, + CO_E_REMOTE_COMMUNICATION_FAILURE, + CO_E_SERVER_START_TIMEOUT, + CO_E_CLSREG_INCONSISTENT, + CO_E_IIDREG_INCONSISTENT, + CO_E_NOT_SUPPORTED, + CO_E_RELOAD_DLL, + CO_E_MSI_ERROR, + CO_E_ATTEMPT_TO_CREATE_OUTSIDE_CLIENT_CONTEXT, + CO_E_SERVER_PAUSED, + CO_E_SERVER_NOT_PAUSED, + CO_E_CLASS_DISABLED, + CO_E_CLRNOTAVAILABLE, + CO_E_ASYNC_WORK_REJECTED, + CO_E_SERVER_INIT_TIMEOUT, + CO_E_NO_SECCTX_IN_ACTIVATE, + CO_E_TRACKER_CONFIG, + CO_E_THREADPOOL_CONFIG, + CO_E_SXS_CONFIG, + CO_E_MALFORMED_SPN, + E_UNEXPECTED, + E_ACCESSDENIED, + E_HANDLE, + E_OUTOFMEMORY, + E_INVALIDARG, + E_NOTIMPL, + E_NOINTERFACE, + E_POINTER, + E_ABORT, + E_FAIL }; + + if (ARRAYSIZE(should) != ARRAYSIZE(are)) + { + const size_t a = ARRAYSIZE(should); + const size_t b = ARRAYSIZE(are); + printf("mismatch: %" PRIuz " vs %" PRIuz "\n", a, b); + return FALSE; + } + for (size_t x = 0; x < ARRAYSIZE(are); x++) + { + const LONG a = are[x]; + const LONG b = should[x]; + if (a != b) + { + printf("mismatch[%" PRIuz "]: %08" PRIx32 " vs %08" PRIx32 "\n", x, a, b); + return FALSE; + } + } + return TRUE; +} + +static BOOL TestSucceededFailedMacros(HRESULT hr, char* sym, BOOL isSuccess) +{ + BOOL rv = TRUE; + + if (SUCCEEDED(hr) && !isSuccess) + { + printf("Error: SUCCEEDED with \"%s\" must be false\n", sym); + rv = FALSE; + } + if (!SUCCEEDED(hr) && isSuccess) + { + printf("Error: SUCCEEDED with \"%s\" must be true\n", sym); + rv = FALSE; + } + if (!FAILED(hr) && !isSuccess) + { + printf("Error: FAILED with \"%s\" must be true\n", sym); + rv = FALSE; + } + if (FAILED(hr) && isSuccess) + { + printf("Error: FAILED with \"%s\" must be false\n", sym); + rv = FALSE; + } + + return rv; +} + +int TestTypes(int argc, char* argv[]) +{ + BOOL ok = TRUE; + HRESULT hr = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_co_errors()) + goto err; + + if (S_OK != 0L) + { + printf("Error: S_OK should be 0\n"); + goto err; + } + if (S_FALSE != 1L) + { + printf("Error: S_FALSE should be 1\n"); + goto err; + } + + /* Test HRESULT success codes */ + ok &= TestSucceededFailedMacros(S_OK, "S_OK", TRUE); + ok &= TestSucceededFailedMacros(S_FALSE, "S_FALSE", TRUE); + + /* Test some HRESULT error codes */ + ok &= TestSucceededFailedMacros(E_NOTIMPL, "E_NOTIMPL", FALSE); + ok &= TestSucceededFailedMacros(E_OUTOFMEMORY, "E_OUTOFMEMORY", FALSE); + ok &= TestSucceededFailedMacros(E_INVALIDARG, "E_INVALIDARG", FALSE); + ok &= TestSucceededFailedMacros(E_FAIL, "E_FAIL", FALSE); + ok &= TestSucceededFailedMacros(E_ABORT, "E_ABORT", FALSE); + + /* Test some WIN32 error codes converted to HRESULT*/ + hr = HRESULT_FROM_WIN32(ERROR_SUCCESS); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_SUCCESS)", TRUE); + + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION)", FALSE); + + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)", FALSE); + + hr = HRESULT_FROM_WIN32(ERROR_NOACCESS); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_NOACCESS)", FALSE); + + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_NOT_FOUND)", FALSE); + + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_TIMEOUT)", FALSE); + + hr = HRESULT_FROM_WIN32(RPC_S_ZERO_DIVIDE); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(RPC_S_ZERO_DIVIDE)", FALSE); + + hr = HRESULT_FROM_WIN32(ERROR_STATIC_INIT); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_STATIC_INIT)", FALSE); + + hr = HRESULT_FROM_WIN32(ERROR_ENCRYPTION_FAILED); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(ERROR_ENCRYPTION_FAILED)", FALSE); + + hr = HRESULT_FROM_WIN32(WSAECANCELLED); + ok &= TestSucceededFailedMacros(hr, "HRESULT_FROM_WIN32(WSAECANCELLED)", FALSE); + + if (ok) + { + printf("Test completed successfully\n"); + return 0; + } + +err: + printf("Error: Test failed\n"); + return -1; +} diff --git a/winpr/tools/CMakeLists.txt b/winpr/tools/CMakeLists.txt new file mode 100644 index 0000000..ed7734d --- /dev/null +++ b/winpr/tools/CMakeLists.txt @@ -0,0 +1,149 @@ +# WinPR: Windows Portable Runtime +# winpr cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# Copyright 2016 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Soname versioning - use winpr version +set(WINPR_TOOLS_VERSION_MAJOR "${WINPR_VERSION_MAJOR}") +set(WINPR_TOOLS_VERSION_MINOR "${WINPR_VERSION_MINOR}") +set(WINPR_TOOLS_VERSION_REVISION "${WINPR_VERSION_REVISION}") + +set(WINPR_TOOLS_API_VERSION "${WINPR_TOOLS_VERSION_MAJOR}") +set(WINPR_TOOLS_VERSION "${WINPR_TOOLS_VERSION_MAJOR}.${WINPR_TOOLS_VERSION_MINOR}.${WINPR_TOOLS_VERSION_REVISION}") +set(WINPR_TOOLS_VERSION_FULL "${WINPR_TOOLS_VERSION}") +set(WINPR_TOOLS_API_VERSION "${WINPR_TOOLS_VERSION_MAJOR}") + +set(WINPR_TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(WINPR_TOOLS_SRCS "") +set(WINPR_TOOLS_LIBS "") +set(WINPR_TOOLS_INCLUDES "") +set(WINPR_TOOLS_DEFINITIONS "") + +macro (winpr_tools_module_add) + file (RELATIVE_PATH _relPath "${WINPR_TOOLS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") + foreach (_src ${ARGN}) + if (_relPath) + list (APPEND WINPR_TOOLS_SRCS "${_relPath}/${_src}") + else() + list (APPEND WINPR_TOOLS_SRCS "${_src}") + endif() + endforeach() + if (_relPath) + set (WINPR_TOOLS_SRCS ${WINPR_TOOLS_SRCS} PARENT_SCOPE) + endif() +endmacro() + +macro (winpr_tools_include_directory_add) + file (RELATIVE_PATH _relPath "${WINPR_TOOLS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") + foreach (_inc ${ARGN}) + if (IS_ABSOLUTE ${_inc}) + list (APPEND WINPR_TOOLS_INCLUDES "${_inc}") + else() + if (_relPath) + list (APPEND WINPR_TOOLS_INCLUDES "${_relPath}/${_inc}") + else() + list (APPEND WINPR_TOOLS_INCLUDES "${_inc}") + endif() + endif() + endforeach() + if (_relPath) + set (WINPR_TOOLS_INCLUDES ${WINPR_TOOLS_INCLUDES} PARENT_SCOPE) + endif() +endmacro() + +macro (winpr_tools_library_add) + foreach (_lib ${ARGN}) + list (APPEND WINPR_TOOLS_LIBS "${_lib}") + endforeach() + set (WINPR_TOOLS_LIBS ${WINPR_TOOLS_LIBS} PARENT_SCOPE) +endmacro() + +macro (winpr_tools_definition_add) + foreach (_define ${ARGN}) + list (APPEND WINPR_TOOLS_DEFINITONS "${_define}") + endforeach() + set (WINPR_TOOLS_DEFINITONS ${WINPR_TOOLS_DEFINITONS} PARENT_SCOPE) +endmacro() + +add_subdirectory(makecert) + +set(MODULE_NAME winpr-tools) +list(REMOVE_DUPLICATES WINPR_TOOLS_DEFINITIONS) +list(REMOVE_DUPLICATES WINPR_TOOLS_LIBS) +list(REMOVE_DUPLICATES WINPR_TOOLS_INCLUDES) +include_directories(${WINPR_TOOLS_INCLUDES}) + +# On windows create dll version information. +# Vendor, product and year are already set in top level CMakeLists.txt +if (WIN32) + set (RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR}) + set (RC_VERSION_MINOR ${WINPR_VERSION_MINOR}) + set (RC_VERSION_BUILD ${WINPR_VERSION_REVISION}) + set (RC_VERSION_FILE "${CMAKE_SHARED_LIBRARY_PREFIX}${MODULE_NAME}${WINPR_TOOLS_API_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}" ) + + configure_file( + ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set (WINPR_TOOLS_SRCS ${WINPR_TOOLS_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + +add_library(${MODULE_NAME} ${WINPR_TOOLS_SRCS}) +set_target_properties(${MODULE_NAME} PROPERTIES LINKER_LANGUAGE C) +set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${WINPR_TOOLS_API_VERSION}) +if (WITH_LIBRARY_VERSIONING) + set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_TOOLS_VERSION} SOVERSION ${WINPR_TOOLS_API_VERSION}) +endif() + +add_definitions(${WINPR_DEFINITIONS}) +target_include_directories(${MODULE_NAME} INTERFACE $) +target_link_libraries(${MODULE_NAME} PRIVATE ${WINPR_TOOLS_LIBS}) + +install(TARGETS ${MODULE_NAME} COMPONENT libraries EXPORT WinPR-toolsTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS) + get_target_property(OUTPUT_FILENAME ${MODULE_NAME} OUTPUT_NAME) + install(FILES ${CMAKE_PDB_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols) +endif() +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Tools") + +# Add all command line utilities +add_subdirectory(makecert-cli) +add_subdirectory(hash-cli) + +include(pkg-config-install-prefix) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr-tools.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr-tools${WINPR_TOOLS_VERSION_MAJOR}.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr-tools${WINPR_TOOLS_VERSION_MAJOR}.pc DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR}) + +export(PACKAGE ${MODULE_NAME}) + +SetFreeRDPCMakeInstallDir(WINPR_CMAKE_INSTALL_DIR "WinPR-tools${WINPR_VERSION_MAJOR}") + +configure_package_config_file(WinPR-toolsConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfig.cmake + INSTALL_DESTINATION ${WINPR_CMAKE_INSTALL_DIR} + PATH_VARS WINPR_INCLUDE_DIR) + +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfigVersion.cmake + VERSION ${WINPR_VERSION} COMPATIBILITY SameMajorVersion) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/WinPR-toolsConfigVersion.cmake + DESTINATION ${WINPR_CMAKE_INSTALL_DIR}) + +install(EXPORT WinPR-toolsTargets DESTINATION ${WINPR_CMAKE_INSTALL_DIR}) diff --git a/winpr/tools/WinPR-toolsConfig.cmake.in b/winpr/tools/WinPR-toolsConfig.cmake.in new file mode 100644 index 0000000..65f9f48 --- /dev/null +++ b/winpr/tools/WinPR-toolsConfig.cmake.in @@ -0,0 +1,12 @@ +include(CMakeFindDependencyMacro) +find_dependency(WinPR @FREERDP_VERSION@) + +@PACKAGE_INIT@ + +set(WinPR-tools_VERSION_MAJOR "@WINPR_VERSION_MAJOR@") +set(WinPR-tools_VERSION_MINOR "@WINPR_VERSION_MINOR@") +set(WinPR-tools_VERSION_REVISION "@WINPR_VERSION_REVISION@") + +set_and_check(WinPR-tools_INCLUDE_DIR "@PACKAGE_WINPR_INCLUDE_DIR@") + +include("${CMAKE_CURRENT_LIST_DIR}/WinPR-toolsTargets.cmake") diff --git a/winpr/tools/hash-cli/CMakeLists.txt b/winpr/tools/hash-cli/CMakeLists.txt new file mode 100644 index 0000000..8f583d3 --- /dev/null +++ b/winpr/tools/hash-cli/CMakeLists.txt @@ -0,0 +1,59 @@ +# WinPR: Windows Portable Runtime +# winpr-hash cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "winpr-hash") +set(MODULE_PREFIX "WINPR_TOOLS_HASH") + +set(${MODULE_PREFIX}_SRCS + hash.c) + +# On windows create dll version information. +# Vendor, product and year are already set in top level CMakeLists.txt +if (WIN32) + set(RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR}) + set(RC_VERSION_MINOR ${WINPR_VERSION_MINOR}) + set(RC_VERSION_BUILD ${WINPR_VERSION_REVISION}) + set(RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}") + + configure_file( + ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set(${MODULE_PREFIX}_LIBS winpr) + +set(MANPAGE_NAME "${MODULE_NAME}") +if (WITH_BINARY_VERSIONING) + set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${MODULE_NAME}${WINPR_API_VERSION}") + set(MANPAGE_NAME "${MODULE_NAME}${WINPR_API_VERSION}") +endif() +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT tools EXPORT WinPRTargets) + +if (WITH_DEBUG_SYMBOLS AND MSVC) + install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols) +endif() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Tools") +configure_file(winpr-hash.1.in ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1) +install_freerdp_man(${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1 1) diff --git a/winpr/tools/hash-cli/hash.c b/winpr/tools/hash-cli/hash.c new file mode 100644 index 0000000..b98f8e9 --- /dev/null +++ b/winpr/tools/hash-cli/hash.c @@ -0,0 +1,216 @@ +/** + * WinPR: Windows Portable Runtime + * NTLM Hashing Tool + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include + +/** + * Define NTOWFv1(Password, User, Domain) as + * MD4(UNICODE(Password)) + * EndDefine + * + * Define LMOWFv1(Password, User, Domain) as + * ConcatenationOf(DES(UpperCase(Password)[0..6], "KGS!@#$%"), + * DES(UpperCase(Password)[7..13], "KGS!@#$%")) + * EndDefine + * + * Define NTOWFv2(Password, User, Domain) as + * HMAC_MD5(MD4(UNICODE(Password)), + * UNICODE(ConcatenationOf(UpperCase(User), Domain))) + * EndDefine + * + * Define LMOWFv2(Password, User, Domain) as + * NTOWFv2(Password, User, Domain) + * EndDefine + * + */ + +static WINPR_NORETURN(void usage_and_exit(void)) +{ + printf("winpr-hash: NTLM hashing tool\n"); + printf("Usage: winpr-hash -u -p [-d ] [-f <_default_,sam>] [-v " + "<_1_,2>]\n"); + exit(1); +} + +int main(int argc, char* argv[]) +{ + int index = 1; + int format = 0; + unsigned long version = 1; + BYTE NtHash[16]; + char* User = NULL; + size_t UserLength = 0; + char* Domain = NULL; + size_t DomainLength = 0; + char* Password = NULL; + size_t PasswordLength = 0; + errno = 0; + + while (index < argc) + { + if (strcmp("-d", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing domain\n\n"); + usage_and_exit(); + } + + Domain = argv[index]; + } + else if (strcmp("-u", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing username\n\n"); + usage_and_exit(); + } + + User = argv[index]; + } + else if (strcmp("-p", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing password\n\n"); + usage_and_exit(); + } + + Password = argv[index]; + } + else if (strcmp("-v", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing version parameter\n\n"); + usage_and_exit(); + } + + version = strtoul(argv[index], NULL, 0); + + if (((version != 1) && (version != 2)) || (errno != 0)) + { + printf("unknown version %lu \n\n", version); + usage_and_exit(); + } + } + else if (strcmp("-f", argv[index]) == 0) + { + index++; + + if (index == argc) + { + printf("missing format\n\n"); + usage_and_exit(); + } + + if (strcmp("default", argv[index]) == 0) + format = 0; + else if (strcmp("sam", argv[index]) == 0) + format = 1; + } + else if (strcmp("-h", argv[index]) == 0) + { + usage_and_exit(); + } + + index++; + } + + if ((!User) || (!Password)) + { + printf("missing username or password\n\n"); + usage_and_exit(); + } + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + + UserLength = strlen(User); + PasswordLength = strlen(Password); + DomainLength = (Domain) ? strlen(Domain) : 0; + + WINPR_ASSERT(UserLength <= UINT32_MAX); + WINPR_ASSERT(PasswordLength <= UINT32_MAX); + WINPR_ASSERT(DomainLength <= UINT32_MAX); + + if (version == 2) + { + if (!Domain) + { + printf("missing domain (version 2 requires a domain to specified)\n\n"); + usage_and_exit(); + } + + if (!NTOWFv2A(Password, (UINT32)PasswordLength, User, (UINT32)UserLength, Domain, + (UINT32)DomainLength, NtHash)) + { + fprintf(stderr, "Hash creation failed\n"); + return 1; + } + } + else + { + if (!NTOWFv1A(Password, (UINT32)PasswordLength, NtHash)) + { + fprintf(stderr, "Hash creation failed\n"); + return 1; + } + } + + if (format == 0) + { + for (int index = 0; index < 16; index++) + printf("%02" PRIx8 "", NtHash[index]); + + printf("\n"); + } + else if (format == 1) + { + printf("%s:", User); + + if (DomainLength > 0) + printf("%s:", Domain); + else + printf(":"); + + printf(":"); + + for (int index = 0; index < 16; index++) + printf("%02" PRIx8 "", NtHash[index]); + + printf(":::"); + printf("\n"); + } + + return 0; +} diff --git a/winpr/tools/hash-cli/winpr-hash.1.in b/winpr/tools/hash-cli/winpr-hash.1.in new file mode 100644 index 0000000..0b1f36a --- /dev/null +++ b/winpr/tools/hash-cli/winpr-hash.1.in @@ -0,0 +1,42 @@ +.TH @MANPAGE_NAME@ 1 2017-01-11 "@WINPR_VERSION_FULL@" "FreeRDP" +.SH NAME +@MANPAGE_NAME@ \- NTLM hashing tool +.SH SYNOPSIS +.B @MANPAGE_NAME@ +\fB-u\fP username +\fB-p\fP password +[\fB-d\fP domain] +[\fB-f\fP { \fIdefault\fP | sam }] +[\fB-v\fP { \fI1\fP | 2 }] +.SH DESCRIPTION +.B @MANPAGE_NAME@ +is a small utility that can be used to create a NTLM hash from a username and password pair. The created hash can be outputed as plain hash or in SAM format. +.SH OPTIONS +.IP "-u username" +The username to use. +.IP "-p password" +Password to use. +.IP "-d domain" +A optional parameter to specify the domain of the user. +.IP "-f format" +Specify the output format. The \fIdefault\fP outputs only the plain NTLM +hash. The second output format available is \fIsam\fP which outputs the +created hash in a format that it can be used in SAM file: + +user:domain::hash::: +.IP "-v version" +Version allows it to specify the NTLM version to use. The default is to use version 1. In case +version 2 is used a domain needs to be specified. +.SH EXAMPLES +@MANPAGE_NAME@ -u \fIuser\fP -p \fIpassword\fP -d \fIdomain\fP -f \fIsam\fP -v \fI2\fP + +Create a version \fI2\fP NTLM hash for \fIuser\fP with \fIdomain\fP and \fIpassword\fP and output it in \fIsam\fP format. +.SH EXIT STATUS +.TP +.B 0 +Successful program execution. +.TP +.B 1 +Missing or invalid arguments. +.SH AUTHOR +FreeRDP diff --git a/winpr/tools/makecert-cli/CMakeLists.txt b/winpr/tools/makecert-cli/CMakeLists.txt new file mode 100644 index 0000000..e92d6f2 --- /dev/null +++ b/winpr/tools/makecert-cli/CMakeLists.txt @@ -0,0 +1,63 @@ +# WinPR: Windows Portable Runtime +# winpr-makecert cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# Copyright 2016 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "winpr-makecert") +set(MODULE_PREFIX "WINPR_MAKECERT") + +set(${MODULE_PREFIX}_SRCS + main.c) + +# On windows create dll version information. +# Vendor, product and year are already set in top level CMakeLists.txt +if (WIN32) + set(RC_VERSION_MAJOR ${WINPR_VERSION_MAJOR}) + set(RC_VERSION_MINOR ${WINPR_VERSION_MINOR}) + set(RC_VERSION_BUILD ${WINPR_VERSION_REVISION}) + set(RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}") + + configure_file( + ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set(${MODULE_PREFIX}_LIBS winpr-tools) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} winpr) + +set(MANPAGE_NAME ${MODULE_NAME}) +if (WITH_BINARY_VERSIONING) + set_target_properties(${MODULE_NAME} + PROPERTIES + OUTPUT_NAME "${MODULE_NAME}${WINPR_API_VERSION}" + ) + set(MANPAGE_NAME ${MODULE_NAME}${WINPR_API_VERSION}) +endif() +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Tools") + +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT tools EXPORT WinPRTargets) +if (WITH_DEBUG_SYMBOLS AND MSVC) + install(FILES ${CMAKE_PDB_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols) +endif() + +configure_file(winpr-makecert.1.in ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1) +install_freerdp_man(${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1 1) diff --git a/winpr/tools/makecert-cli/main.c b/winpr/tools/makecert-cli/main.c new file mode 100644 index 0000000..fa01f7e --- /dev/null +++ b/winpr/tools/makecert-cli/main.c @@ -0,0 +1,45 @@ +/** + * WinPR: Windows Portable Runtime + * makecert replacement + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +int main(int argc, char* argv[]) +{ + MAKECERT_CONTEXT* context = NULL; + int ret = 0; + + context = makecert_context_new(); + if (!context) + return 1; + + if (makecert_context_process(context, argc, argv) < 0) + ret = 1; + + makecert_context_free(context); + + return ret; +} diff --git a/winpr/tools/makecert-cli/winpr-makecert.1.in b/winpr/tools/makecert-cli/winpr-makecert.1.in new file mode 100644 index 0000000..a50c82c --- /dev/null +++ b/winpr/tools/makecert-cli/winpr-makecert.1.in @@ -0,0 +1,116 @@ +.de URL +\\$2 \(laURL: \\$1 \(ra\\$3 +.. +.if \n[.g] .mso www.tmac +.TH @MANPAGE_NAME@ 1 2017-01-11 "@WINPR_VERSION_FULL@" "FreeRDP" +.SH NAME +@MANPAGE_NAME@ \- A tool to create X.509 certificates. +.SH SYNOPSIS +.B @MANPAGE_NAME@ +[\fB-rdp\fP] +[\fB-silent\fP] +[\fB-live\fP] +[\fB-format\fP { \fIcrt\fP | \fIpem\fP | \fIpfx\fP }] +[\fB-p\fP password] +[\fB-n\fP common_name] +[\fB-y\fP years] +[\fB-m\fP months] +[\fB-len\fP length] +[\fB-#\fP serial] +[\fB-a\fP { \fImd5\fP | \fIsha1\fP | \fIsha256\fP | \fIs384\fP | \fIsha512\fP }] +[\fB-path\fP outputpath] +[outputname] +.SH DESCRIPTION +.B @MANPAGE_NAME@ +is a tool for generating X.509 certificates modeled after the Windows command +MakeCert. @MANPAGE_NAME@ aims to be command line compatible with MakeCert +however not all options are supported or implemented yet. + +Unimplemented features are not described here. They are marked as "Unsupported" +in @MANPAGE_NAME@s help. + +In contrast to it's Windows counterpart @MANPAGE_NAME@ does, unless the +\fB\-live\fP option is given, always creates and save a certificate. +If \fIoutputname\fP isn't set it is tried to determine the host name of the +computer the command is run on. +.br +\fBWarning:\fP if the file already exists it will be overwritten without asking. + +Without further options the generated certificates have the following properties: + +* 2048 bit long +.br +* sha256 as hash algorithm +.br +* the detected host name is used as common name +.br +* a time stamp is used as serial number +.br +* validity period of one year +.br +* saved in the current working directory in crt format +.SH OPTIONS +.IP "-rdp" +Dummy parameter. Can be used to quickly generate a certificate with default +properties without specifying any further parameters. +.IP "-silent" +Don't print the generated certificate to stdout. +.IP "-f format" +Three formats are supported: crt, pem and pfx. +.br +\fIcrt\fP outputs the key and the certificate in a separate file each with the file +endings .key and .crt. +.br +\fIpem\fP outputs the key and certificate into a single file with the file ending pem. +.br +And \fIpfx\fP outputs key and certificate into a pkcs12 file with the ending .pfx. +.IP "-p password" +Password to use if the pfx format is used as format. +.IP "-live" +Don't write the key/certificate to disk. When used from the command line this +can be thought as "dummy" mode. +.IP "-n common_name" +The common name to use in the certificate. +.IP "-m months" +Validity period in months. +.IP "-y years" +Validity period in years. If months and years are specified the specified +month parameter will take precedence. +.IP "-len length" +Key length in bits to use. +.IP "-a { \fImd5\fP | \fIsha1\fP | \fIsha256\fP | \fIs384\fP | \fIsha512\fP }" +The hashing algorithm to use. +.IP "-# serial" +The serial number to use for the certificate. +.IP "-path" +A directory where the certificate should be created in. +.IP "outputname" +The base name of the created file(s). A suffix, the format specific suffix is +appended to this name. +.SH EXAMPLES +@MANPAGE_NAME@ -rdp + +Creates a certificate with the default properties, saved to a file in the +current working directory in crt format named like the host. If the host is +named freerdp the created files are called freerdp.key and freerdp.crt. + + +@MANPAGE_NAME@ -len 4096 -a sha384 -path /tmp -# 22 -m 144 -y 1 -format crt mycert + +The command above creates the file /tmp/mycert.pem containing a key and a +certificate with a length of 4096. It will use sha384 as hash algorithm. +The certificate has the serial number 22 and is valid for 12 years (144 months). +.SH EXIT STATUS +.TP +.B 0 +Successful program execution. +.TP +.B 1 +Otherwise. + +.SH SEE ALSO + +.URL "https://msdn.microsoft.com/library/windows/desktop/aa386968.aspx" "MakeCert help page" + +.SH AUTHOR +FreeRDP diff --git a/winpr/tools/makecert/CMakeLists.txt b/winpr/tools/makecert/CMakeLists.txt new file mode 100644 index 0000000..a41cccd --- /dev/null +++ b/winpr/tools/makecert/CMakeLists.txt @@ -0,0 +1,49 @@ +# WinPR: Windows Portable Runtime +# winpr-makecert cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "winpr-makecert-tool") +set(MODULE_PREFIX "WINPR_MAKECERT_TOOL") + +set(${MODULE_PREFIX}_SRCS makecert.c) + +if(OPENSSL_FOUND) + winpr_tools_include_directory_add(${OPENSSL_INCLUDE_DIR}) +endif() + +if(MBEDTLS_FOUND) + winpr_tools_include_directory_add(${MBEDTLS_INCLUDE_DIR}) +endif() + + +winpr_tools_module_add(${${MODULE_PREFIX}_SRCS}) + +if(OPENSSL_FOUND) + if(WIN32) + list(APPEND ${MODULE_PREFIX}_LIBS ${OPENSSL_LIBRARIES}) + else() + # if ${OPENSSL_LIBRARIES} libssl and libcrypto is linked + # therefor explicitly link against libcrypto + list(APPEND ${MODULE_PREFIX}_LIBS ${OPENSSL_CRYPTO_LIBRARIES}) + endif() +endif() + +if(MBEDTLS_FOUND) + list(APPEND ${MODULE_PREFIX}_LIBS ${MBEDTLS_LIBRARIES}) +endif() + + +winpr_tools_library_add(${${MODULE_PREFIX}_LIBS} winpr) diff --git a/winpr/tools/makecert/makecert.c b/winpr/tools/makecert/makecert.c new file mode 100644 index 0000000..85baeef --- /dev/null +++ b/winpr/tools/makecert/makecert.c @@ -0,0 +1,1165 @@ +/** + * WinPR: Windows Portable Runtime + * makecert replacement + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +struct S_MAKECERT_CONTEXT +{ + int argc; + char** argv; + +#ifdef WITH_OPENSSL + X509* x509; + EVP_PKEY* pkey; + PKCS12* pkcs12; +#endif + + BOOL live; + BOOL silent; + + BOOL crtFormat; + BOOL pemFormat; + BOOL pfxFormat; + + char* password; + + char* output_file; + char* output_path; + char* default_name; + char* common_name; + + int duration_years; + int duration_months; +}; + +static char* makecert_read_str(BIO* bio, size_t* pOffset) +{ + int status = -1; + size_t offset = 0; + size_t length = 0; + char* x509_str = NULL; + + while (offset >= length) + { + size_t new_len = 0; + size_t readBytes = 0; + char* new_str = NULL; + new_len = length * 2; + if (new_len == 0) + new_len = 2048; + + if (new_len > INT_MAX) + { + status = -1; + break; + } + + new_str = (char*)realloc(x509_str, new_len); + + if (!new_str) + { + status = -1; + break; + } + + length = new_len; + x509_str = new_str; + ERR_clear_error(); +#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER) + status = BIO_read_ex(bio, &x509_str[offset], length - offset, &readBytes); +#else + status = BIO_read(bio, &x509_str[offset], length - offset); + readBytes = status; +#endif + if (status <= 0) + break; + + offset += (size_t)readBytes; + } + + if (status < 0) + { + free(x509_str); + if (pOffset) + *pOffset = 0; + return NULL; + } + + x509_str[offset] = '\0'; + if (pOffset) + *pOffset = offset + 1; + return x509_str; +} + +static int makecert_print_command_line_help(COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv) +{ + char* str = NULL; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + + if (!argv || (argc < 1)) + return -1; + + printf("Usage: %s [options] [output file]\n", argv[0]); + printf("\n"); + arg = args; + + do + { + if (arg->Flags & COMMAND_LINE_VALUE_FLAG) + { + printf(" %s", "-"); + printf("%-20s", arg->Name); + printf("\t%s\n", arg->Text); + } + else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) || + (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL)) + { + printf(" %s", "-"); + + if (arg->Format) + { + size_t length = strlen(arg->Name) + strlen(arg->Format) + 2; + str = malloc(length + 1); + + if (!str) + return -1; + + sprintf_s(str, length + 1, "%s %s", arg->Name, arg->Format); + printf("%-20s", str); + free(str); + } + else + { + printf("%-20s", arg->Name); + } + + printf("\t%s\n", arg->Text); + } + } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + return 1; +} + +#ifdef WITH_OPENSSL +static int x509_add_ext(X509* cert, int nid, char* value) +{ + X509V3_CTX ctx; + X509_EXTENSION* ext = NULL; + + if (!cert || !value) + return 0; + + X509V3_set_ctx_nodb(&ctx) X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); + ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); + + if (!ext) + return 0; + + X509_add_ext(cert, ext, -1); + X509_EXTENSION_free(ext); + return 1; +} +#endif + +static char* x509_name_parse(char* name, char* txt, size_t* length) +{ + char* p = NULL; + char* entry = NULL; + + if (!name || !txt || !length) + return NULL; + + p = strstr(name, txt); + + if (!p) + return NULL; + + entry = p + strlen(txt) + 1; + p = strchr(entry, '='); + + if (!p) + *length = strlen(entry); + else + *length = (size_t)(p - entry); + + return entry; +} + +static char* x509_get_default_name(void) +{ + CHAR* computerName = NULL; + DWORD nSize = 0; + + if (GetComputerNameExA(ComputerNamePhysicalDnsFullyQualified, NULL, &nSize) || + GetLastError() != ERROR_MORE_DATA) + goto fallback; + + computerName = (CHAR*)calloc(1, nSize); + + if (!computerName) + goto fallback; + + if (!GetComputerNameExA(ComputerNamePhysicalDnsFullyQualified, computerName, &nSize)) + goto fallback; + + return computerName; +fallback: + free(computerName); + + if (GetComputerNameExA(ComputerNamePhysicalNetBIOS, NULL, &nSize) || + GetLastError() != ERROR_MORE_DATA) + return NULL; + + computerName = (CHAR*)calloc(1, nSize); + + if (!computerName) + return NULL; + + if (!GetComputerNameExA(ComputerNamePhysicalNetBIOS, computerName, &nSize)) + { + free(computerName); + return NULL; + } + + return computerName; +} + +static int command_line_pre_filter(MAKECERT_CONTEXT* context, int index, int argc, LPCSTR* argv) +{ + if (!context || !argv || (index < 0) || (argc < 0)) + return -1; + + if (index == (argc - 1)) + { + if (argv[index][0] != '-') + { + context->output_file = _strdup(argv[index]); + + if (!context->output_file) + return -1; + + return 1; + } + } + + return 0; +} + +static int makecert_context_parse_arguments(MAKECERT_CONTEXT* context, + COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv) +{ + int status = 0; + DWORD flags = 0; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + + if (!context || !argv || (argc < 0)) + return -1; + + /** + * makecert -r -pe -n "CN=%COMPUTERNAME%" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr LocalMachine + * -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 + */ + CommandLineClearArgumentsA(args); + flags = COMMAND_LINE_SEPARATOR_SPACE | COMMAND_LINE_SIGIL_DASH; + status = + CommandLineParseArgumentsA(argc, argv, args, flags, context, + (COMMAND_LINE_PRE_FILTER_FN_A)command_line_pre_filter, NULL); + + if (status & COMMAND_LINE_STATUS_PRINT_HELP) + { + makecert_print_command_line_help(args, argc, argv); + return 0; + } + + arg = args; + errno = 0; + + do + { + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + CommandLineSwitchStart(arg) + /* Basic Options */ + CommandLineSwitchCase(arg, "silent") + { + context->silent = TRUE; + } + CommandLineSwitchCase(arg, "live") + { + context->live = TRUE; + } + CommandLineSwitchCase(arg, "format") + { + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + if (strcmp(arg->Value, "crt") == 0) + { + context->crtFormat = TRUE; + context->pemFormat = FALSE; + context->pfxFormat = FALSE; + } + else if (strcmp(arg->Value, "pem") == 0) + { + context->crtFormat = FALSE; + context->pemFormat = TRUE; + context->pfxFormat = FALSE; + } + else if (strcmp(arg->Value, "pfx") == 0) + { + context->crtFormat = FALSE; + context->pemFormat = FALSE; + context->pfxFormat = TRUE; + } + else + return -1; + } + CommandLineSwitchCase(arg, "path") + { + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + context->output_path = _strdup(arg->Value); + + if (!context->output_path) + return -1; + } + CommandLineSwitchCase(arg, "p") + { + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + context->password = _strdup(arg->Value); + + if (!context->password) + return -1; + } + CommandLineSwitchCase(arg, "n") + { + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + context->common_name = _strdup(arg->Value); + + if (!context->common_name) + return -1; + } + CommandLineSwitchCase(arg, "y") + { + long val = 0; + + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + val = strtol(arg->Value, NULL, 0); + + if ((errno != 0) || (val < 0) || (val > INT32_MAX)) + return -1; + + context->duration_years = (int)val; + } + CommandLineSwitchCase(arg, "m") + { + long val = 0; + + if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + continue; + + val = strtol(arg->Value, NULL, 0); + + if ((errno != 0) || (val < 1) || (val > 12)) + return -1; + + context->duration_months = (int)val; + } + CommandLineSwitchDefault(arg) + { + } + CommandLineSwitchEnd(arg) + } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + return 1; +} + +int makecert_context_set_output_file_name(MAKECERT_CONTEXT* context, const char* name) +{ + if (!context) + return -1; + + free(context->output_file); + context->output_file = NULL; + + if (name) + context->output_file = _strdup(name); + + if (!context->output_file) + return -1; + + return 1; +} + +int makecert_context_output_certificate_file(MAKECERT_CONTEXT* context, const char* path) +{ +#ifdef WITH_OPENSSL + FILE* fp = NULL; + int status = 0; + size_t length = 0; + size_t offset = 0; + char* filename = NULL; + char* fullpath = NULL; + char* ext = NULL; + int ret = -1; + BIO* bio = NULL; + char* x509_str = NULL; + + if (!context || !path) + return -1; + + if (!context->output_file) + { + context->output_file = _strdup(context->default_name); + + if (!context->output_file) + return -1; + } + + /* + * Output Certificate File + */ + length = strlen(context->output_file); + filename = malloc(length + 8); + + if (!filename) + return -1; + + if (context->crtFormat) + ext = "crt"; + else if (context->pemFormat) + ext = "pem"; + else if (context->pfxFormat) + ext = "pfx"; + else + goto out_fail; + + sprintf_s(filename, length + 8, "%s.%s", context->output_file, ext); + + if (path) + fullpath = GetCombinedPath(path, filename); + else + fullpath = _strdup(filename); + + if (!fullpath) + goto out_fail; + + fp = winpr_fopen(fullpath, "w+"); + + if (fp) + { + if (context->pfxFormat) + { + if (!context->password) + { + context->password = _strdup("password"); + + if (!context->password) + goto out_fail; + + printf("Using default export password \"password\"\n"); + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); +#else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | + OPENSSL_INIT_LOAD_CONFIG, + NULL); +#endif + context->pkcs12 = PKCS12_create(context->password, context->default_name, context->pkey, + context->x509, NULL, 0, 0, 0, 0, 0); + + if (!context->pkcs12) + goto out_fail; + + bio = BIO_new(BIO_s_mem()); + + if (!bio) + goto out_fail; + + status = i2d_PKCS12_bio(bio, context->pkcs12); + + if (status != 1) + goto out_fail; + + x509_str = makecert_read_str(bio, &offset); + + if (!x509_str) + goto out_fail; + + length = offset; + + if (fwrite((void*)x509_str, length, 1, fp) != 1) + goto out_fail; + } + else + { + bio = BIO_new(BIO_s_mem()); + + if (!bio) + goto out_fail; + + if (!PEM_write_bio_X509(bio, context->x509)) + goto out_fail; + + x509_str = makecert_read_str(bio, &offset); + + if (!x509_str) + goto out_fail; + + length = offset; + + if (fwrite(x509_str, length, 1, fp) != 1) + goto out_fail; + + free(x509_str); + x509_str = NULL; + BIO_free_all(bio); + bio = NULL; + + if (context->pemFormat) + { + bio = BIO_new(BIO_s_mem()); + + if (!bio) + goto out_fail; + + status = PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL); + + if (status < 0) + goto out_fail; + + x509_str = makecert_read_str(bio, &offset); + if (!x509_str) + goto out_fail; + + length = offset; + + if (fwrite(x509_str, length, 1, fp) != 1) + goto out_fail; + } + } + } + + ret = 1; +out_fail: + BIO_free_all(bio); + + if (fp) + fclose(fp); + + free(x509_str); + free(filename); + free(fullpath); + return ret; +#else + WLog_ERR(TAG, "%s only supported with OpenSSL", __func__); + return -1; +#endif +} + +int makecert_context_output_private_key_file(MAKECERT_CONTEXT* context, const char* path) +{ +#ifdef WITH_OPENSSL + FILE* fp = NULL; + size_t length = 0; + size_t offset = 0; + char* filename = NULL; + char* fullpath = NULL; + int ret = -1; + BIO* bio = NULL; + char* x509_str = NULL; + + if (!context->crtFormat) + return 1; + + if (!context->output_file) + { + context->output_file = _strdup(context->default_name); + + if (!context->output_file) + return -1; + } + + /** + * Output Private Key File + */ + length = strlen(context->output_file); + filename = malloc(length + 8); + + if (!filename) + return -1; + + sprintf_s(filename, length + 8, "%s.key", context->output_file); + + if (path) + fullpath = GetCombinedPath(path, filename); + else + fullpath = _strdup(filename); + + if (!fullpath) + goto out_fail; + + fp = winpr_fopen(fullpath, "w+"); + + if (!fp) + goto out_fail; + + bio = BIO_new(BIO_s_mem()); + + if (!bio) + goto out_fail; + + if (!PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL)) + goto out_fail; + + x509_str = makecert_read_str(bio, &offset); + + if (!x509_str) + goto out_fail; + + length = offset; + + if (fwrite((void*)x509_str, length, 1, fp) != 1) + goto out_fail; + + ret = 1; +out_fail: + + if (fp) + fclose(fp); + + BIO_free_all(bio); + free(x509_str); + free(filename); + free(fullpath); + return ret; +#else + WLog_ERR(TAG, "%s only supported with OpenSSL", __func__); + return -1; +#endif +} + +#ifdef WITH_OPENSSL +static BOOL makecert_create_rsa(EVP_PKEY** ppkey, size_t key_length) +{ + BOOL rc = FALSE; + + WINPR_ASSERT(ppkey); + +#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3) + RSA* rsa = NULL; +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + rsa = RSA_generate_key(key_length, RSA_F4, NULL, NULL); +#else + { + BIGNUM* bn = BN_secure_new(); + + if (!bn) + return FALSE; + + rsa = RSA_new(); + + if (!rsa) + { + BN_clear_free(bn); + return FALSE; + } + + BN_set_word(bn, RSA_F4); + const int res = RSA_generate_key_ex(rsa, key_length, bn, NULL); + BN_clear_free(bn); + + if (res != 1) + return FALSE; + } +#endif + + if (!EVP_PKEY_assign_RSA(*ppkey, rsa)) + { + RSA_free(rsa); + return FALSE; + } + rc = TRUE; +#else + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (!pctx) + return FALSE; + + if (EVP_PKEY_keygen_init(pctx) != 1) + goto fail; + + WINPR_ASSERT(key_length <= UINT_MAX); + unsigned int keylen = (unsigned int)key_length; + const OSSL_PARAM params[] = { OSSL_PARAM_construct_uint("bits", &keylen), + OSSL_PARAM_construct_end() }; + if (EVP_PKEY_CTX_set_params(pctx, params) != 1) + goto fail; + + if (EVP_PKEY_generate(pctx, ppkey) != 1) + goto fail; + + rc = TRUE; +fail: + EVP_PKEY_CTX_free(pctx); +#endif + return rc; +} +#endif + +int makecert_context_process(MAKECERT_CONTEXT* context, int argc, char** argv) +{ + COMMAND_LINE_ARGUMENT_A args[] = { + /* Custom Options */ + + { "rdp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Unsupported - Generate certificate with required options for RDP usage." }, + { "silent", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Silently generate certificate without verbose output." }, + { "live", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Generate certificate live in memory when used as a library." }, + { "format", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specify certificate file format" }, + { "path", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specify certificate file output path" }, + { "p", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specify certificate export password" }, + + /* Basic Options */ + + { "n", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specifies the subject's certificate name. This name must conform to the X.500 standard. " + "The simplest method is to specify the name in double quotes, preceded by CN=; for " + "example, " + "-n \"CN=myName\"." }, + { "pe", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Unsupported - Marks the generated private key as exportable. This allows the private " + "key to " + "be included in the certificate." }, + { "sk", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's key container location, which contains the " + "private " + "key. " + "If a key container does not exist, it will be created." }, + { "sr", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's certificate store location. location can be " + "either " + "currentuser (the default) or localmachine." }, + { "ss", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's certificate store name that stores the output " + "certificate." }, + { "#", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specifies a serial number from 1 to 2,147,483,647. The default is a unique value " + "generated " + "by Makecert.exe." }, + { "$", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the signing authority of the certificate, which must be set to " + "either commercial " + "(for certificates used by commercial software publishers) or individual (for " + "certificates " + "used by individual software publishers)." }, + + /* Extended Options */ + + { "a", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specifies the signature algorithm. algorithm must be md5, sha1, sha256 (the default), " + "sha384, or sha512." }, + { "b", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the start of the validity period. Defaults to the current " + "date." }, + { "crl", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Unsupported - Generates a certificate relocation list (CRL) instead of a certificate." }, + { "cy", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the certificate type. Valid values are end for end-entity and " + "authority for certification authority." }, + { "e", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the end of the validity period. Defaults to 12/31/2039 11:59:59 " + "GMT." }, + { "eku", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Inserts a list of comma-separated, enhanced key usage object identifiers " + "(OIDs) into the certificate." }, + { "h", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the maximum height of the tree below this certificate." }, + { "ic", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's certificate file." }, + { "ik", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's key container name." }, + { "iky", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's key type, which must be one of the following: " + "signature (which indicates that the key is used for a digital signature), " + "exchange (which indicates that the key is used for key encryption and key exchange), " + "or an integer that represents a provider type. " + "By default, you can pass 1 for an exchange key or 2 for a signature key." }, + { "in", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's certificate common name." }, + { "ip", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's CryptoAPI provider name. For information about the " + "CryptoAPI provider name, see the –sp option." }, + { "ir", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the location of the issuer's certificate store. location can be " + "either currentuser (the default) or localmachine." }, + { "is", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's certificate store name." }, + { "iv", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's .pvk private key file." }, + { "iy", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the issuer's CryptoAPI provider type. For information about the " + "CryptoAPI provider type, see the –sy option." }, + { "l", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Links to policy information (for example, to a URL)." }, + { "len", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specifies the generated key length, in bits." }, + { "m", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specifies the duration, in months, of the certificate validity period." }, + { "y", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Specifies the duration, in years, of the certificate validity period." }, + { "nscp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Unsupported - Includes the Netscape client-authorization extension." }, + { "r", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, + "Unsupported - Creates a self-signed certificate." }, + { "sc", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's certificate file." }, + { "sky", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's key type, which must be one of the following: " + "signature (which indicates that the key is used for a digital signature), " + "exchange (which indicates that the key is used for key encryption and key exchange), " + "or an integer that represents a provider type. " + "By default, you can pass 1 for an exchange key or 2 for a signature key." }, + { "sp", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's CryptoAPI provider name, which must be defined in " + "the " + "registry subkeys of " + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider. If both –sp " + "and " + "–sy are present, " + "the type of the CryptoAPI provider must correspond to the Type value of the provider's " + "subkey." }, + { "sv", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's .pvk private key file. The file is created if " + "none " + "exists." }, + { "sy", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the subject's CryptoAPI provider type, which must be defined in " + "the " + "registry subkeys of " + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider Types. If " + "both " + "–sy and –sp are present, " + "the name of the CryptoAPI provider must correspond to the Name value of the provider " + "type " + "subkey." }, + { "tbs", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, + "Unsupported - Specifies the certificate or CRL file to be signed." }, + + /* Help */ + + { "?", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help", + "print help" }, + { "!", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help-ext", + "print extended help" }, + { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } + }; +#ifdef WITH_OPENSSL + size_t length = 0; + char* entry = NULL; + int key_length = 0; + long serial = 0; + X509_NAME* name = NULL; + const EVP_MD* md = NULL; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + int ret = 0; + ret = makecert_context_parse_arguments(context, args, argc, argv); + + if (ret < 1) + { + return ret; + } + + if (!context->default_name && !context->common_name) + { + context->default_name = x509_get_default_name(); + + if (!context->default_name) + return -1; + } + else + { + context->default_name = _strdup(context->common_name); + + if (!context->default_name) + return -1; + } + + if (!context->common_name) + { + context->common_name = _strdup(context->default_name); + + if (!context->common_name) + return -1; + } + + if (!context->pkey) + context->pkey = EVP_PKEY_new(); + + if (!context->pkey) + return -1; + + if (!context->x509) + context->x509 = X509_new(); + + if (!context->x509) + return -1; + + key_length = 2048; + arg = CommandLineFindArgumentA(args, "len"); + + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + unsigned long val = strtoul(arg->Value, NULL, 0); + + if ((errno != 0) || (val > INT_MAX)) + return -1; + key_length = (int)val; + } + + if (!makecert_create_rsa(&context->pkey, key_length)) + return -1; + + X509_set_version(context->x509, 2); + arg = CommandLineFindArgumentA(args, "#"); + + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + serial = strtol(arg->Value, NULL, 0); + + if (errno != 0) + return -1; + } + else + serial = (long)GetTickCount64(); + + ASN1_INTEGER_set(X509_get_serialNumber(context->x509), serial); + { + ASN1_TIME* before = NULL; + ASN1_TIME* after = NULL; +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + before = X509_get_notBefore(context->x509); + after = X509_get_notAfter(context->x509); +#else + before = X509_getm_notBefore(context->x509); + after = X509_getm_notAfter(context->x509); +#endif + X509_gmtime_adj(before, 0); + + if (context->duration_months) + X509_gmtime_adj(after, (long)(60 * 60 * 24 * 31 * context->duration_months)); + else if (context->duration_years) + X509_gmtime_adj(after, (long)(60 * 60 * 24 * 365 * context->duration_years)); + } + X509_set_pubkey(context->x509, context->pkey); + name = X509_get_subject_name(context->x509); + arg = CommandLineFindArgumentA(args, "n"); + + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + entry = x509_name_parse(arg->Value, "C", &length); + + if (entry) + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + + entry = x509_name_parse(arg->Value, "ST", &length); + + if (entry) + X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + + entry = x509_name_parse(arg->Value, "L", &length); + + if (entry) + X509_NAME_add_entry_by_txt(name, "L", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + + entry = x509_name_parse(arg->Value, "O", &length); + + if (entry) + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + + entry = x509_name_parse(arg->Value, "OU", &length); + + if (entry) + X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + + entry = context->common_name; + length = strlen(entry); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + } + else + { + entry = context->common_name; + length = strlen(entry); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry, + (int)length, -1, 0); + } + + X509_set_issuer_name(context->x509, name); + x509_add_ext(context->x509, NID_ext_key_usage, "serverAuth"); + arg = CommandLineFindArgumentA(args, "a"); + md = EVP_sha256(); + + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + md = EVP_get_digestbyname(arg->Value); + if (!md) + return -1; + } + + if (!X509_sign(context->x509, context->pkey, md)) + return -1; + + /** + * Print certificate + */ + + if (!context->silent) + { + BIO* bio = NULL; + int status = 0; + char* x509_str = NULL; + bio = BIO_new(BIO_s_mem()); + + if (!bio) + return -1; + + status = X509_print(bio, context->x509); + + if (status < 0) + { + BIO_free_all(bio); + return -1; + } + + x509_str = makecert_read_str(bio, NULL); + if (!x509_str) + { + BIO_free_all(bio); + return -1; + } + + printf("%s", x509_str); + free(x509_str); + BIO_free_all(bio); + } + + /** + * Output certificate and private key to files + */ + + if (!context->live) + { + if (!winpr_PathFileExists(context->output_path)) + { + if (!CreateDirectoryA(context->output_path, NULL)) + return -1; + } + + if (makecert_context_output_certificate_file(context, context->output_path) != 1) + return -1; + + if (context->crtFormat) + { + if (makecert_context_output_private_key_file(context, context->output_path) < 0) + return -1; + } + } + + return 0; +#else + WLog_ERR(TAG, "%s only supported with OpenSSL", __func__); + return -1; +#endif +} + +MAKECERT_CONTEXT* makecert_context_new(void) +{ + MAKECERT_CONTEXT* context = (MAKECERT_CONTEXT*)calloc(1, sizeof(MAKECERT_CONTEXT)); + + if (context) + { + context->crtFormat = TRUE; + context->duration_years = 1; + } + + return context; +} + +void makecert_context_free(MAKECERT_CONTEXT* context) +{ + if (context) + { + free(context->password); + free(context->default_name); + free(context->common_name); + free(context->output_file); + free(context->output_path); +#ifdef WITH_OPENSSL + X509_free(context->x509); + EVP_PKEY_free(context->pkey); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + CRYPTO_cleanup_all_ex_data(); +#endif +#endif + free(context); + } +} diff --git a/winpr/tools/winpr-tools.pc.in b/winpr/tools/winpr-tools.pc.in new file mode 100644 index 0000000..6898253 --- /dev/null +++ b/winpr/tools/winpr-tools.pc.in @@ -0,0 +1,15 @@ +prefix=@PKG_CONFIG_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@WINPR_INCLUDE_DIR@ +libs=-lwinpr-tools@WINPR_TOOLS_API_VERSION@ + +Name: WinPR +Description: WinPR: Windows Portable Runtime +URL: http://www.freerdp.com/ +Version: @WINPR_TOOLS_VERSION@ +Requires: +Requires.private: winpr@WINPR_VERSION_MAJOR@ libssl +Libs: -L${libdir} ${libs} +Libs.private: -lcrypto +Cflags: -I${includedir} diff --git a/winpr/winpr.pc.in b/winpr/winpr.pc.in new file mode 100644 index 0000000..d4f9c08 --- /dev/null +++ b/winpr/winpr.pc.in @@ -0,0 +1,15 @@ +prefix=@PKG_CONFIG_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@WINPR_INCLUDE_DIR@ +libs=-lwinpr@WINPR_API_VERSION@ + +Name: WinPR +Description: WinPR: Windows Portable Runtime +URL: http://www.freerdp.com/ +Version: @WINPR_VERSION@ +Requires: +Requires.private: libssl +Libs: -L${libdir} ${libs} +Libs.private: -ldl -lrt -lm -lpthread +Cflags: -I${includedir} diff --git a/winpr/wlog.7.in b/winpr/wlog.7.in new file mode 100644 index 0000000..f1b9dde --- /dev/null +++ b/winpr/wlog.7.in @@ -0,0 +1,149 @@ +.\" Written by David Fort (contact@hardening-consulting.com) +.\" Process this file with +.\" groff -man -Tascii wlog.7 +.\" +.TH wLog 7 "June 2016" Version "2.0" +.SH NAME +wLog \- WinPR logging facility + +.SH DESCRIPTION +wLog is a configurable and flexible logging system used throughout WinPR and +FreeRDP. + +The primary concept is to have a hierarchy of loggers that can be be configured +independently. + +.SH Appenders + +WLog uses different appenders that define where the log output should be written +to. If the application doesn't explicitly configure the appenders the below +described variable WLOG_APPENDER can be used to choose one appender. + +The following kind of appenders are available: + +.IP Binary +Write the log data into a binary format file. + +.IP Console +The console appender writes to the console. Depending of the operating system +the application runs on, the output might be handled differently. For example +on android log print would be used. + +.IP File +The file appender writes the textual output to a file. + +.IP Udp +This appender sends the logging messages to a pre-defined remote host via UDP. + +If no target is set the default one 127.0.0.1:20000 is used. To receive the +log messages one can use netcat. To receive the default target the following +command can be used: +nc -u 127.0.0.1 -p 20000 -l +.IP Syslog +Use syslog for outputting the debug messages. +.IP Journald +This appender outputs messages to journald. + +.SH Levels +The WLog are complementary, the higher level always includes the lower ones. +The level list below is top down. Top the highest level. + +.IP TRACE +print everything including packets dumps +.IP DEBUG +debug messages +.IP INFO +general information +.IP WARN +warnings +.IP ERROR +errors +.IP FATAL +fatal problems +.IP OFF +completely disable the wlog output + +.SH Formats +The format a logger prints has the following possible options: + +.IP lv +log level +.IP mn +module name +.IP fl +file name +.IP fn +function +.IP ln +line number +.IP pid +process id +.IP tid +thread id +.IP yr +year +.IP mo +month +.IP dw +day of week +.IP hr +hour +.IP mi +minute +.IP se +second +.IP ml +millisecond +.PP +A maximum of 16 options can be used per format string. + +An example that generally sets the WLOG_PREFIX for xfreerdp would look like: +WLOG_PREFIX="pid=%pid:tid=%tid:fn=%fn -" xfreerdp /v:xxx + + +.SH ENVIRONMENT +.IP WLOG_APPENDER +The kind of appender, the accepted values are: CONSOLE, FILE, BINARY, SYSLOG, JOURNALD or UDP + +.IP WLOG_PREFIX +configure the prefix used for outputting the message (see Format for more details and examples) + +.IP WLOG_LEVEL +the level to output messages for + +.IP WLOG_FILTER +sets a filter for WLog messages. Only the filtered messages are +printed. The format of the filter is a series of \:\ separated by +comas + +example: WLOG_FILTER=core.channel:DEBUG,dummy:TRACE +will display debug messages for the core.channel logger and trace level for the dummy logger + +.IP WLOG_FILEAPPENDER_OUTPUT_FILE_PATH +When using the file appender it may contains the output log file's path + +.IP WLOG_FILEAPPENDER_OUTPUT_FILE_NAME +When using the file appender it may contains the output log file's name + +.IP WLOG_JOURNALD_ID +When using the systemd journal appender, this variable contains the id used with +the journal (by default the executable's name) + +.IP WLOG_UDP_TARGET +target to use for the UDP appender in the format +.B host:port + +.SH BUGS +Please report any bugs using the bug reporting form on the +.B FreeRDP +web site + +.SH "SEE ALSO" +Additional information and the latest version is available +at the web site: +.B http://www.freerdp.com + +.SH AUTHOR +David Fort wrote this manpage from materials written +by Bernhard Miklautz . + -- cgit v1.2.3