├── .gitattributes ├── .github └── workflows │ └── msbuild.yml ├── .gitignore ├── Article ├── Article.md ├── mmc.jpg └── servercert.jpg ├── Common ├── BaseSock.cpp ├── CertHelper.cpp ├── CertRAII.cpp ├── Common.cpp ├── Include │ ├── AppVersion.h │ ├── BaseSock.h │ ├── CertHelper.h │ ├── CertRAII.h │ ├── Common.h │ ├── EventWrapper.h │ ├── Handle.h │ ├── ISocketStream.h │ ├── SSLHelper.h │ ├── SecBufferDescriptor.h │ ├── SecurityHandle.h │ └── Utilities.h ├── SSLHelper.cpp ├── SecBufferDescriptor.cpp └── Utilities.cpp ├── Developer.md ├── License.md ├── README.md ├── Repository ├── .gitattributes └── .gitignore ├── SSLClient ├── ActiveSock.cpp ├── Include │ ├── ActiveSock.h │ └── SSLClient.h ├── SSLClient.cpp ├── SSLClient.vcxproj ├── SSLClient.vcxproj.filters ├── framework.h ├── pch.cpp ├── pch.h └── targetver.h ├── SSLServer ├── Include │ └── Listener.h ├── Listener.cpp ├── PassiveSock.cpp ├── PassiveSock.h ├── SSLServer.cpp ├── SSLServer.h ├── SSLServer.vcxproj ├── SSLServer.vcxproj.filters ├── ServerCert.cpp ├── ServerCert.h ├── framework.h ├── pch.cpp ├── pch.h └── targetver.h ├── Samples ├── SimpleClient │ ├── SimpleClient.cpp │ ├── SimpleClient.vcxproj │ └── SimpleClient.vcxproj.filters ├── StreamClient │ ├── StreamClient.cpp │ ├── StreamClient.rc │ ├── StreamClient.vcxproj │ ├── StreamClient.vcxproj.filters │ ├── framework.h │ ├── pch.cpp │ ├── pch.h │ ├── resource.h │ └── targetver.h └── StreamServer │ ├── StreamServer.cpp │ ├── StreamServer.rc │ ├── StreamServer.vcxproj │ ├── StreamServer.vcxproj.filters │ ├── framework.h │ ├── pch.cpp │ ├── pch.h │ ├── resource.h │ └── targetver.h ├── SimpleClientCS ├── SimpleClientCs.cs └── SimpleClientCs.csproj ├── StreamClient.sln ├── StreamClientCs ├── StreamClientCs.cs └── StreamClientCs.csproj ├── StreamSSL.sln └── StreamSSL ├── StreamSSL.vcxproj ├── StreamSSL.vcxproj.filters ├── framework.h ├── pch.cpp ├── pch.h └── targetver.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/msbuild.yml: -------------------------------------------------------------------------------- 1 | name: MSBuild 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - develop 8 | 9 | env: 10 | # Path to the solution file relative to the root of the project. 11 | SOLUTION_FILE_PATH: StreamSSL.sln 12 | 13 | # Configuration type to build. 14 | # You can convert this to a build matrix if you need coverage of multiple configuration types. 15 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | BUILD_CONFIGURATION: Release 17 | 18 | jobs: 19 | build: 20 | runs-on: windows-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: Add MSBuild to PATH 26 | uses: microsoft/setup-msbuild@v1 27 | 28 | - name: Build 29 | working-directory: ${{env.GITHUB_WORKSPACE}} 30 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 31 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 32 | run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Dd]ebug [Aa]nsi/ 13 | [Rr]elease/ 14 | x64/ 15 | build/ 16 | bld/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # Roslyn cache directories 21 | *.ide/ 22 | 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | 27 | #NUNIT 28 | *.VisualState.xml 29 | TestResult.xml 30 | 31 | # Build Results of an ATL Project 32 | [Dd]ebugPS/ 33 | [Rr]eleasePS/ 34 | dlldata.c 35 | 36 | *_i.c 37 | *_p.c 38 | *_i.h 39 | *.ilk 40 | *.meta 41 | *.obj 42 | *.pch 43 | *.pdb 44 | *.pgc 45 | *.pgd 46 | *.rsp 47 | *.sbr 48 | *.tlb 49 | *.tli 50 | *.tlh 51 | *.tmp 52 | *.tmp_proj 53 | *.log 54 | *.vspscc 55 | *.vssscc 56 | .builds 57 | *.pidb 58 | *.svclog 59 | *.scc 60 | 61 | # Chutzpah Test files 62 | _Chutzpah* 63 | 64 | # Visual C++ cache files 65 | ipch/ 66 | *.aps 67 | *.ncb 68 | *.opendb 69 | *.opensdf 70 | *.sdf 71 | *.cachefile 72 | 73 | 74 | # Visual Studio profiler 75 | *.psess 76 | *.vsp 77 | *.vspx 78 | 79 | # TFS 2012 Local Workspace 80 | $tf/ 81 | 82 | # Guidance Automation Toolkit 83 | *.gpState 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper*/ 87 | *.[Rr]e[Ss]harper 88 | *.DotSettings.user 89 | 90 | # JustCode is a .NET coding addin-in 91 | .JustCode 92 | 93 | # TeamCity is a build add-in 94 | _TeamCity* 95 | 96 | # DotCover is a Code Coverage Tool 97 | *.dotCover 98 | 99 | # NCrunch 100 | _NCrunch_* 101 | .*crunch*.local.xml 102 | 103 | # MightyMoose 104 | *.mm.* 105 | AutoTest.Net/ 106 | 107 | # Web workbench (sass) 108 | .sass-cache/ 109 | 110 | # Installshield output folder 111 | [Ee]xpress/ 112 | 113 | # DocProject is a documentation generator add-in 114 | DocProject/buildhelp/ 115 | DocProject/Help/*.HxT 116 | DocProject/Help/*.HxC 117 | DocProject/Help/*.hhc 118 | DocProject/Help/*.hhk 119 | DocProject/Help/*.hhp 120 | DocProject/Help/Html2 121 | DocProject/Help/html 122 | 123 | # Click-Once directory 124 | publish/ 125 | 126 | # Publish Web Output 127 | *.[Pp]ublish.xml 128 | *.azurePubxml 129 | ## TODO: Comment the next line if you want to checkin your 130 | ## web deploy settings but do note that will include unencrypted 131 | ## passwords 132 | #*.pubxml 133 | 134 | # NuGet Packages Directory 135 | packages/* 136 | ## TODO: If the tool you use requires repositories.config 137 | ## uncomment the next line 138 | #!packages/repositories.config 139 | 140 | # Enable "build/" folder in the NuGet Packages folder since 141 | # NuGet packages use it for MSBuild targets. 142 | # This line needs to be after the ignore of the build folder 143 | # (and the packages folder if the line above has been uncommented) 144 | !packages/build/ 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | sql/ 155 | *.Cache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # LightSwitch generated files 190 | GeneratedArtifacts/ 191 | _Pvt_Extensions/ 192 | ModelManifest.xml 193 | 194 | # Zip files 195 | *.zip 196 | # build artifacts 197 | *.vs/ 198 | /Zipped/ 199 | /StreamSSL.VC.db 200 | /.vs5/ 201 | Debug Static Library 202 | Release Static Library 203 | -------------------------------------------------------------------------------- /Article/mmc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-maw/StreamSSL/a5c310381b4d78c382fd9b2a1a4d7faf3b9c6ecf/Article/mmc.jpg -------------------------------------------------------------------------------- /Article/servercert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-maw/StreamSSL/a5c310381b4d78c382fd9b2a1a4d7faf3b9c6ecf/Article/servercert.jpg -------------------------------------------------------------------------------- /Common/BaseSock.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | 4 | #include "BaseSock.h" 5 | #include "Utilities.h" 6 | 7 | CBaseSock::CBaseSock(SOCKET s, HANDLE StopEvent) 8 | : CBaseSock(StopEvent) 9 | { 10 | ActualSocket = s; 11 | if FAILED(Setup()) 12 | throw("Setup failed"); 13 | } 14 | 15 | 16 | CBaseSock::CBaseSock(HANDLE StopEvent) 17 | : m_hStopEvent(StopEvent) 18 | { 19 | // 20 | // Initialize the WinSock subsystem. 21 | // 22 | WSADATA wsadata; 23 | if (WSAStartup(MAKEWORD(1, 1), &wsadata) == SOCKET_ERROR) 24 | { 25 | DebugMsg("Error %d returned by WSAStartup", GetLastError()); 26 | throw "WSAStartup error"; 27 | } 28 | } 29 | 30 | CBaseSock::~CBaseSock() 31 | { 32 | Disconnect(); 33 | WSACleanup(); 34 | } 35 | 36 | DWORD CBaseSock::GetLastError() const 37 | { 38 | return LastError; 39 | } 40 | 41 | HRESULT CBaseSock::Setup() 42 | { 43 | int rc = 1; 44 | setsockopt(ActualSocket, IPPROTO_TCP, TCP_NODELAY, (char*)& rc, sizeof(int)); 45 | if (!read_event) 46 | read_event = WSACreateEvent(); 47 | if (read_event != WSA_INVALID_EVENT) 48 | { 49 | if (!write_event) 50 | write_event = WSACreateEvent(); 51 | if (write_event != WSA_INVALID_EVENT) 52 | { 53 | if (WSAResetEvent(read_event) && WSAResetEvent(write_event)) 54 | DebugMsg("CBaseSock::Initialize - Events initialized"); 55 | } 56 | } 57 | 58 | LastError = WSAGetLastError(); 59 | return HRESULT_FROM_WIN32(LastError); 60 | } 61 | 62 | HRESULT CBaseSock::Disconnect(bool CloseUnderlyingConnection) 63 | { 64 | LastError = ERROR_SUCCESS; 65 | 66 | if (ActualSocket == INVALID_SOCKET || !CloseUnderlyingConnection) 67 | return S_OK; 68 | else if (WSACloseEvent(read_event) && WSACloseEvent(write_event) && CloseAndInvalidateSocket()) 69 | DebugMsg("Disconnect succeeded"); 70 | else 71 | { 72 | LastError = ::WSAGetLastError(); 73 | DebugMsg("Disconnect failed, WSAGetLastError returned 0x%.8x", LastError); 74 | } 75 | return HRESULT_FROM_WIN32(LastError); 76 | } 77 | 78 | bool CBaseSock::CloseAndInvalidateSocket() 79 | { 80 | const auto nRet = closesocket(ActualSocket); 81 | ActualSocket = INVALID_SOCKET; 82 | return nRet == 0; 83 | } 84 | 85 | // The general model of timer logic is this - if you want to control the timer, call StartxxxTimer and it will 86 | // be started immediately and used when RecvPartial or SendPartial is called. It times whole messages, not the fragments TCP/IP 87 | // may deliver. So receiving an SSL message should happen within seconds, whether it arrives in one 88 | // piece or many. 89 | // 90 | // Setting the send or recv timeout generally switches back to automated operation (meaning Send and Recv start 91 | // the relevant timer whenever they are called) but there's an extra parameter to SetxxxTimoutSeconds so you can 92 | // specify manual behaviour explicitly if you wish. 93 | 94 | void CBaseSock::StartRecvTimerInternal() 95 | { 96 | RecvEndTime = std::chrono::steady_clock::now() + std::chrono::seconds(RecvTimeoutSeconds); 97 | } 98 | 99 | void CBaseSock::StartSendTimerInternal() 100 | { 101 | SendEndTime = std::chrono::steady_clock::now() + std::chrono::seconds(SendTimeoutSeconds); 102 | } 103 | 104 | void CBaseSock::StartRecvTimer() 105 | { 106 | RecvTimerAutomatic = false; // It will be set manually (by calling this method) from now on 107 | StartRecvTimerInternal(); 108 | } 109 | 110 | void CBaseSock::StartSendTimer() 111 | { 112 | SendTimerAutomatic = false; // It will be set manually (by callong this method) from now on 113 | StartSendTimerInternal(); 114 | } 115 | 116 | void CBaseSock::SetRecvTimeoutSeconds(int NewRecvTimeoutSeconds, bool NewTimerAutomatic) 117 | { 118 | if (NewRecvTimeoutSeconds == INFINITE) 119 | NewRecvTimeoutSeconds = MAXINT; 120 | if (NewRecvTimeoutSeconds > 0) 121 | { 122 | RecvTimeoutSeconds = NewRecvTimeoutSeconds; 123 | // RecvEndTime is untouched because a receive may be in process 124 | } 125 | RecvTimerAutomatic = NewTimerAutomatic; 126 | } 127 | 128 | int CBaseSock::GetRecvTimeoutSeconds() const 129 | { 130 | return RecvTimeoutSeconds; 131 | } 132 | 133 | void CBaseSock::SetSendTimeoutSeconds(int NewSendTimeoutSeconds, bool NewTimerAutomatic) 134 | { 135 | if (NewSendTimeoutSeconds == INFINITE) 136 | NewSendTimeoutSeconds = MAXINT; 137 | if (NewSendTimeoutSeconds > 0) 138 | { 139 | SendTimeoutSeconds = NewSendTimeoutSeconds; 140 | // SendEndTime is untouched, because a Send may be in process 141 | } 142 | SendTimerAutomatic = NewTimerAutomatic; 143 | } 144 | 145 | int CBaseSock::GetSendTimeoutSeconds() const 146 | { 147 | return SendTimeoutSeconds; 148 | } 149 | 150 | // Receives no more than Len bytes of data and returns the amount received - or SOCKET_ERROR if it times out before receiving MinLen 151 | int CBaseSock::Recv(LPVOID lpBuf, const size_t Len, const size_t MinLen) 152 | { 153 | if (RecvTimerAutomatic) 154 | StartRecvTimerInternal(); 155 | size_t total_bytes_received = 0; 156 | while (total_bytes_received < MinLen) 157 | { 158 | const size_t bytes_received = RecvPartial((char*)lpBuf + total_bytes_received, Len - total_bytes_received); 159 | if (bytes_received == SOCKET_ERROR) 160 | return SOCKET_ERROR; 161 | else if (bytes_received == 0) 162 | break; // socket is closed, no data left to receive 163 | else 164 | total_bytes_received += bytes_received; 165 | }; // loop 166 | return (static_cast(total_bytes_received)); 167 | } 168 | 169 | // Receives up to Len bytes of data and returns the amount received - or SOCKET_ERROR if it times out 170 | int CBaseSock::RecvPartial(LPVOID lpBuf, const size_t Len) 171 | { 172 | DWORD bytes_read = 0; 173 | DWORD msg_flags = 0; 174 | int rc = 0; 175 | 176 | // Setup up the events to wait on 177 | WSAEVENT hEvents[2] = { m_hStopEvent, read_event }; 178 | 179 | const auto TimeLeft = RecvEndTime - std::chrono::steady_clock::now(); 180 | const auto SecondsLeft = std::chrono::duration_cast(TimeLeft).count(); 181 | if (SecondsLeft <= 0) 182 | { 183 | LastError = ERROR_TIMEOUT; 184 | return SOCKET_ERROR; 185 | } 186 | 187 | if (RecvInitiated) 188 | { 189 | // Special case, the previous read was left active so we are trying again, maybe it completed in the meantime 190 | rc = SOCKET_ERROR; 191 | LastError = WSA_IO_PENDING; 192 | } 193 | else 194 | { 195 | // Normal case, the last read completed, so we need to initiate another 196 | 197 | // Create the overlapped I/O event and structures 198 | memset(&os, 0, sizeof(OVERLAPPED)); 199 | ZeroMemory(&os, sizeof(os)); 200 | os.hEvent = hEvents[1]; 201 | if (!WSAResetEvent(os.hEvent)) 202 | { 203 | LastError = WSAGetLastError(); 204 | return SOCKET_ERROR; 205 | } 206 | RecvInitiated = true; 207 | // Setup the buffers array 208 | WSABUF buffer{ static_cast(Len), static_cast(lpBuf) }; 209 | rc = WSARecv(ActualSocket, &buffer, 1, &bytes_read, &msg_flags, &os, nullptr); // Start an asynchronous read 210 | LastError = WSAGetLastError(); 211 | } 212 | 213 | bool IOCompleted = !rc; // if rc is zero, the read was completed immediately 214 | 215 | // Now wait for the I/O to complete if necessary, and see what happened 216 | 217 | if ((rc == SOCKET_ERROR) && (LastError == WSA_IO_PENDING)) // Read in progress, normal case 218 | { 219 | const DWORD dwWait = WaitForMultipleObjects(2, hEvents, false, static_cast(SecondsLeft) * 1000); 220 | switch (dwWait) 221 | { 222 | case WAIT_OBJECT_0 + 1: // The read event 223 | IOCompleted = true; 224 | LastError = 0; 225 | break; 226 | case WAIT_ABANDONED_0: 227 | case WAIT_ABANDONED_0 + 1: 228 | break; 229 | case WAIT_TIMEOUT: 230 | LastError = ERROR_TIMEOUT; 231 | break; 232 | case WAIT_FAILED: 233 | LastError = ::GetLastError(); 234 | break; 235 | default: 236 | break; 237 | } 238 | } 239 | 240 | if (IOCompleted) 241 | { 242 | RecvInitiated = false; 243 | if (WSAGetOverlappedResult(ActualSocket, &os, &bytes_read, true, &msg_flags) && (bytes_read > 0)) 244 | { 245 | return bytes_read; // Normal case, we read some bytes, it's all good 246 | } 247 | else 248 | { // A bad thing happened 249 | const int e = WSAGetLastError(); 250 | if (e == 0) // The socket was closed 251 | return 0; 252 | else if (LastError == 0) 253 | LastError = e; 254 | } 255 | } 256 | return SOCKET_ERROR; 257 | } 258 | 259 | //sends all the data requested or returns a timeout 260 | int CBaseSock::Send(LPCVOID lpBuf, const size_t Len) 261 | { 262 | if (SendTimerAutomatic) 263 | StartSendTimerInternal(); 264 | ULONG total_bytes_sent = 0; 265 | while (total_bytes_sent < Len) 266 | { 267 | const ULONG bytes_sent = SendPartial((char*)lpBuf + total_bytes_sent, Len - total_bytes_sent); 268 | if ((bytes_sent == SOCKET_ERROR)) 269 | return SOCKET_ERROR; 270 | else if (bytes_sent == 0) 271 | if (total_bytes_sent == 0) 272 | return SOCKET_ERROR; 273 | else 274 | break; // socket is closed, no chance of sending more 275 | else 276 | total_bytes_sent += bytes_sent; 277 | }; // loop 278 | return (total_bytes_sent); 279 | } 280 | 281 | //sends a message, or part of one 282 | int CBaseSock::SendPartial(LPCVOID lpBuf, const size_t Len) 283 | { 284 | DebugMsg("CBaseSock::SendPartial, Len = %d", Len); 285 | 286 | DWORD bytes_sent = 0; 287 | 288 | // Setup up the events to wait on 289 | WSAEVENT hEvents[2] = { m_hStopEvent, write_event }; 290 | 291 | // Create the overlapped I/O event and structures 292 | memset(&os, 0, sizeof(OVERLAPPED)); 293 | os.hEvent = hEvents[1]; 294 | if (!WSAResetEvent(os.hEvent)) 295 | { 296 | LastError = WSAGetLastError(); 297 | return SOCKET_ERROR; 298 | } 299 | 300 | // Setup the buffer array 301 | WSABUF buffer{ static_cast(Len), static_cast(const_cast(lpBuf)) }; 302 | 303 | const int rc = WSASend(ActualSocket, &buffer, 1, &bytes_sent, 0, &os, nullptr); 304 | LastError = WSAGetLastError(); 305 | 306 | const auto TimeLeft = SendEndTime - std::chrono::steady_clock::now(); 307 | const auto SecondsLeft = std::chrono::duration_cast(TimeLeft).count(); 308 | if (SecondsLeft <= 0) 309 | { 310 | LastError = ERROR_TIMEOUT; 311 | return SOCKET_ERROR; 312 | } 313 | 314 | bool IOCompleted = !rc; // if rc is zero, the write was completed immediately, which is common 315 | 316 | // Now wait for the I/O to complete if necessary, and see what happened 317 | 318 | if ((rc == SOCKET_ERROR) && (LastError == WSA_IO_PENDING)) // Write in progress 319 | { 320 | const DWORD dwWait = WaitForMultipleObjects(2, hEvents, false, static_cast(SecondsLeft) * 1000); 321 | switch (dwWait) 322 | { 323 | case WAIT_OBJECT_0 + 1: // The write event 324 | IOCompleted = true; 325 | LastError = 0; 326 | break; 327 | case WAIT_ABANDONED_0: 328 | case WAIT_ABANDONED_0 + 1: 329 | break; 330 | case WAIT_TIMEOUT: 331 | LastError = ERROR_TIMEOUT; 332 | break; 333 | case WAIT_FAILED: 334 | LastError = ::GetLastError(); 335 | break; 336 | default: 337 | break; 338 | } 339 | } 340 | 341 | if (IOCompleted) 342 | { 343 | DWORD msg_flags = 0; 344 | if (WSAGetOverlappedResult(ActualSocket, &os, &bytes_sent, true, &msg_flags)) 345 | { 346 | return bytes_sent; 347 | } 348 | else 349 | { // A bad thing happened 350 | const int e = WSAGetLastError(); 351 | if (e == 0) // The socket was closed 352 | return 0; 353 | else if (LastError == 0) 354 | LastError = e; 355 | } 356 | } 357 | return SOCKET_ERROR; 358 | } 359 | 360 | bool CBaseSock::Connect(LPCWSTR HostName, USHORT PortNumber) 361 | { 362 | int iResult; 363 | BOOL bSuccess; 364 | SOCKADDR_STORAGE LocalAddr = { 0 }; 365 | SOCKADDR_STORAGE RemoteAddr = { 0 }; 366 | DWORD dwLocalAddr = sizeof(LocalAddr); 367 | DWORD dwRemoteAddr = sizeof(RemoteAddr); 368 | WCHAR PortName[10] = { 0 }; 369 | timeval Timeout = { 0 }; 370 | 371 | Timeout.tv_sec = GetSendTimeoutSeconds(); 372 | 373 | _itow_s(PortNumber, PortName, _countof(PortName), 10); 374 | 375 | ActualSocket = socket(AF_INET, SOCK_STREAM, 0); 376 | if (ActualSocket == INVALID_SOCKET) { 377 | DebugMsg("socket failed with error: %d\n", WSAGetLastError()); 378 | return false; 379 | } 380 | 381 | // Note that WSAConnectByName requires Vista or Server 2008 382 | bSuccess = WSAConnectByName(ActualSocket, const_cast(HostName), 383 | PortName, &dwLocalAddr, 384 | (SOCKADDR*)& LocalAddr, 385 | &dwRemoteAddr, 386 | (SOCKADDR*)& RemoteAddr, 387 | &Timeout, 388 | nullptr); 389 | 390 | if (!bSuccess) { 391 | LastError = WSAGetLastError(); 392 | DebugMsg("**** WsaConnectByName Error %d connecting to \"%S\" (%S)", 393 | LastError, 394 | HostName, 395 | PortName); 396 | CloseAndInvalidateSocket(); 397 | return false; 398 | } 399 | iResult = setsockopt(ActualSocket, SOL_SOCKET, 400 | 0x7010 /*SO_UPDATE_CONNECT_CONTEXT*/, nullptr, 0); 401 | if (iResult == SOCKET_ERROR) { 402 | LastError = WSAGetLastError(); 403 | DebugMsg("setsockopt for SO_UPDATE_CONNECT_CONTEXT failed with error: %d", LastError); 404 | CloseAndInvalidateSocket(); 405 | return false; 406 | } 407 | //// At this point we have a connection, so set up keepalives so we can detect if the host disconnects 408 | //// This code is commented out because it does not seen to be helpful 409 | //BOOL so_keepalive = TRUE; 410 | //int iResult = setsockopt(ActualSocket, SOL_SOCKET, SO_KEEPALIVE, (const char *)&so_keepalive, sizeof(so_keepalive)); 411 | // if (iResult == SOCKET_ERROR){ 412 | // LastError = WSAGetLastError(); 413 | // wprintf(L"setsockopt for SO_KEEPALIVE failed with error: %d\n", 414 | // LastError); 415 | // CloseAndInvalidateSocket(); 416 | // return false; 417 | // } 418 | 419 | //// Now set keepalive timings 420 | 421 | //DWORD dwBytes = 0; 422 | //tcp_keepalive sKA_Settings = {0}, sReturned = {0} ; 423 | 424 | //sKA_Settings.onoff = 1 ; 425 | //sKA_Settings.keepalivetime = 1000; // Keep Alive in 1 sec. 426 | //sKA_Settings.keepaliveinterval = 1000 ; // Resend if No-Reply 427 | //if (WSAIoctl(ActualSocket, SIO_KEEPALIVE_VALS, &sKA_Settings, 428 | // sizeof(sKA_Settings), &sReturned, sizeof(sReturned), &dwBytes, 429 | // NULL, NULL) != 0) 430 | //{ 431 | // LastError = WSAGetLastError() ; 432 | // wprintf(L"WSAIoctl to set keepalive failed with error: %d\n", LastError); 433 | // CloseAndInvalidateSocket(); 434 | // return false; 435 | //} 436 | 437 | return SUCCEEDED(Setup()); 438 | } 439 | -------------------------------------------------------------------------------- /Common/CertRAII.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | 4 | #include "CertRAII.h" 5 | #include "Utilities.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #pragma comment(lib, "Rpcrt4.lib") 11 | 12 | CSP::~CSP() 13 | { 14 | if (hCryptProvOrNCryptKey) 15 | { 16 | DebugMsg(("CryptReleaseContext... ")); 17 | CryptReleaseContext(hCryptProvOrNCryptKey, 0); 18 | DebugMsg("Success"); 19 | } 20 | } 21 | 22 | bool CSP::AcquirePrivateKey(PCCERT_CONTEXT pCertContext) 23 | { 24 | BOOL fCallerFreeProvOrNCryptKey = FALSE; 25 | DWORD dwKeySpec; 26 | return FALSE != CryptAcquireCertificatePrivateKey(pCertContext, 0, nullptr, &hCryptProvOrNCryptKey, &dwKeySpec, &fCallerFreeProvOrNCryptKey); 27 | } 28 | 29 | CryptProvider::CryptProvider() 30 | { 31 | // We always want a new keycontainer, so give it a unique name 32 | UUID uuid; 33 | RPC_STATUS ret_val = ::UuidCreate(&uuid); 34 | 35 | if (ret_val == RPC_S_OK) 36 | { 37 | // convert UUID to LPWSTR 38 | ret_val = ::UuidToString(&uuid, (RPC_WSTR*)&KeyContainerName); 39 | if (FAILED(ret_val) || !KeyContainerName) 40 | DebugMsg("CryptProvider constructor could not initialize KeyContainerName"); 41 | } 42 | else 43 | DebugMsg("CryptProvider constructor UuidCreate failed"); 44 | // end of naming keycontainer 45 | } 46 | 47 | CryptProvider::~CryptProvider() 48 | { 49 | if (hCryptProv) 50 | { 51 | DebugMsg(("CryptReleaseContext... ")); 52 | CryptReleaseContext(hCryptProv, 0); 53 | DebugMsg("Success"); 54 | } 55 | if (KeyContainerName) 56 | { 57 | // free up the allocated string 58 | ::RpcStringFree((RPC_WSTR*)&KeyContainerName); 59 | KeyContainerName = nullptr; 60 | } 61 | } 62 | 63 | 64 | BOOL CryptProvider::AcquireContext(DWORD dwFlags) 65 | { 66 | return CryptAcquireContextW(&hCryptProv, KeyContainerName, nullptr, PROV_RSA_FULL, dwFlags); 67 | } 68 | 69 | CryptKey::~CryptKey() 70 | { 71 | if (hKey) 72 | { 73 | DebugMsg(("Destructor calling CryptDestroyKey... ")); 74 | CryptDestroyKey(hKey); 75 | DebugMsg("Success"); 76 | } 77 | } 78 | 79 | BOOL CryptKey::CryptGenKey(CryptProvider& prov) 80 | { 81 | return ::CryptGenKey(prov.hCryptProv, AT_SIGNATURE, 0x08000000 /*RSA-2048-BIT_KEY*/, &hKey); 82 | } 83 | 84 | 85 | CertStore::~CertStore() 86 | { 87 | if (hStore) 88 | { 89 | DebugMsg("CertStore destructor calling CertCloseStore(0x%.8x)", hStore); 90 | CertCloseStore(hStore, 0); 91 | } 92 | } 93 | 94 | bool CertStore::CertOpenStore(DWORD dwFlags) 95 | { 96 | hStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, dwFlags, L"My"); 97 | return hStore != nullptr; 98 | } 99 | 100 | bool CertStore::AddCertificateContext(PCCERT_CONTEXT pCertContext) 101 | { 102 | return (FALSE != ::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, nullptr)); 103 | } 104 | 105 | CertStore::operator bool() const 106 | { 107 | return hStore != nullptr; 108 | } 109 | 110 | HCERTSTORE CertStore::get() const 111 | { 112 | return hStore; 113 | } 114 | -------------------------------------------------------------------------------- /Common/Common.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | 4 | #include "Common.h" 5 | 6 | 7 | // Global value to optimize access since it is set only once 8 | PSecurityFunctionTable CSSLCommon::g_pSSPI = InitSecurityInterface(); 9 | 10 | ; 11 | 12 | -------------------------------------------------------------------------------- /Common/Include/AppVersion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define VERSION_MAJOR 2 3 | #define VERSION_MINOR 1 4 | #define VERSION_PATCH 7 5 | -------------------------------------------------------------------------------- /Common/Include/BaseSock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "ISocketStream.h" 5 | 6 | class CBaseSock: public virtual ISocketStream 7 | { 8 | public: 9 | // Constructor 10 | CBaseSock() = delete; 11 | CBaseSock(CBaseSock&) = delete; 12 | CBaseSock(CBaseSock&&) = delete; 13 | explicit CBaseSock(HANDLE StopEvent); 14 | explicit CBaseSock(SOCKET s, HANDLE StopEvent); 15 | virtual ~CBaseSock(); 16 | bool Connect(LPCWSTR HostName, USHORT PortNumber); 17 | 18 | // Methods used for ISocketStream 19 | int Recv(LPVOID lpBuf, const size_t Len, const size_t MinLen = 1) override; 20 | int Send(LPCVOID lpBuf, const size_t Len) override; 21 | DWORD GetLastError() const override; 22 | HRESULT Disconnect(bool CloseUnderlyingConnection = true) override; 23 | void SetRecvTimeoutSeconds(int NewTimeoutSeconds, bool NewTimerAutomatic = true) override; 24 | int GetRecvTimeoutSeconds() const override; 25 | void SetSendTimeoutSeconds(int NewTimeoutSeconds, bool NewTimerAutomatic = true) override; 26 | int GetSendTimeoutSeconds() const override; 27 | void StartRecvTimer() override; 28 | void StartSendTimer() override; 29 | int GetTlsVersion() const override { return 0; }; 30 | 31 | virtual int RecvPartial(LPVOID lpBuf, const size_t Len); 32 | virtual int SendPartial(LPCVOID lpBuf, const size_t Len); 33 | 34 | protected: 35 | SOCKET ActualSocket{ INVALID_SOCKET }; 36 | HANDLE m_hStopEvent{ nullptr }; 37 | virtual void StartRecvTimerInternal(); 38 | virtual void StartSendTimerInternal(); 39 | 40 | private: 41 | HRESULT Setup(); 42 | bool CloseAndInvalidateSocket(); 43 | DWORD LastError = 0; 44 | bool RecvInitiated = false; 45 | WSAEVENT write_event{ nullptr }; 46 | WSAEVENT read_event{ nullptr }; 47 | WSAOVERLAPPED os{}; 48 | std::chrono::steady_clock::time_point RecvEndTime; 49 | std::chrono::steady_clock::time_point SendEndTime; 50 | bool RecvTimerAutomatic = true; 51 | bool SendTimerAutomatic = true; 52 | int SendTimeoutSeconds{ 1 }, RecvTimeoutSeconds{ 1 }; // Default timeout is 1 seconds, encourages callers to set it 53 | }; 54 | -------------------------------------------------------------------------------- /Common/Include/CertHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | static bool g_ShowCertInfo = false; 4 | bool MatchCertificateName(PCCERT_CONTEXT pCertContext, LPCWSTR pszRequiredName); 5 | HRESULT ShowCertInfo(PCCERT_CONTEXT pCertContext, std::wstring Title); 6 | HRESULT CertTrusted(PCCERT_CONTEXT pCertContext, const bool isClientCert); 7 | std::wstring GetCertName(PCCERT_CONTEXT pCertContext); 8 | std::wstring GetCertFriendlyName(PCCERT_CONTEXT pCertContext); 9 | std::wstring GetCertSubject(PCCERT_CONTEXT pCertContext); 10 | SECURITY_STATUS CertFindClientCertificate(PCCERT_CONTEXT & pCertContext, const LPCWSTR pszSubjectName = nullptr, bool fUserStore = true); 11 | SECURITY_STATUS CertFindFromIssuerList(PCCERT_CONTEXT & pCertContext, SecPkgContext_IssuerListInfoEx & IssuerListInfo, bool fUserStore = false); 12 | SECURITY_STATUS CertFindServerCertificateUI(PCCERT_CONTEXT & pCertContext, LPCWSTR pszSubjectName, bool fUserStore = false); 13 | SECURITY_STATUS CertFindServerCertificateByName(PCCERT_CONTEXT & pCertContext, LPCWSTR pszSubjectName, bool fUserStore = false); 14 | SECURITY_STATUS CertFindCertificateBySignature(PCCERT_CONTEXT & pCertContext, char const * const signature, bool fUserStore = false); 15 | 16 | HRESULT CertFindByName(PCCERT_CONTEXT & pCertContext, const LPCWSTR pszSubjectName, bool fUserStore = false); 17 | 18 | // defined in source file CreateCertificate.cpp 19 | PCCERT_CONTEXT CreateCertificate(bool MachineCert = false, LPCWSTR Subject = nullptr, LPCWSTR FriendlyName = nullptr, LPCWSTR Description = nullptr, bool forClient = false); 20 | -------------------------------------------------------------------------------- /Common/Include/CertRAII.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #pragma comment(lib, "crypt32.lib") 4 | 5 | /* 6 | This is one of 2 different approaches to handling old Windows handle classes. 7 | 8 | The approach in "SecurityHandle.h" encapsulates the native handles but leaves the class usage 9 | somewhat untouched (certainly not quite untouched, since handle usages tend to be replaced by 10 | "get" or "set" calls, but generally the notion of a handle to an object is preserved with lifetime 11 | management using RAII added. 12 | 13 | The approach in "CertRAII...." also replaces handles with objects encapsulating the handles, but 14 | these behave as if they were the underlying object. So, for example instead of a "CertContextHandle" 15 | class, we have a "CertStore" class with a CertStore::AddCertificateContext method, which ultimately 16 | calls ::CertAddCertificateContextToStore. This works fine if only a relatively small number 17 | of methods are to be called, but since each one has to be added to the class, it can be a pain 18 | if a lot of methods are needed so the SecurityHandle.h approach works better then. 19 | */ 20 | 21 | class CSP 22 | { 23 | public: 24 | CSP() = default; 25 | ~CSP(); 26 | bool AcquirePrivateKey(PCCERT_CONTEXT pCertContext); 27 | private: 28 | HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey = NULL; 29 | }; 30 | 31 | class CryptProvider 32 | { 33 | public: 34 | WCHAR * KeyContainerName = nullptr; // will create a random one in constructor 35 | CryptProvider(); 36 | ~CryptProvider(); 37 | BOOL AcquireContext(DWORD dwFlags); 38 | 39 | public: 40 | HCRYPTPROV hCryptProv = NULL; 41 | }; 42 | 43 | class CryptKey 44 | { 45 | public: 46 | CryptKey() = default; 47 | ~CryptKey(); 48 | BOOL CryptGenKey(CryptProvider& prov); 49 | 50 | private: 51 | HCRYPTKEY hKey = NULL; 52 | }; 53 | 54 | class CertStore 55 | { 56 | public: 57 | CertStore() = default; 58 | ~CertStore(); 59 | HCERTSTORE get() const; 60 | operator bool() const; 61 | bool CertOpenStore(DWORD dwFlags); 62 | bool AddCertificateContext(PCCERT_CONTEXT pCertContext); 63 | 64 | private: 65 | HCERTSTORE hStore = nullptr; 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /Common/Include/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CSSLCommon 4 | { 5 | public: 6 | static PSecurityFunctionTableW SSPI() 7 | { 8 | return g_pSSPI; 9 | } 10 | 11 | protected: 12 | static PSecurityFunctionTableW g_pSSPI; 13 | 14 | }; -------------------------------------------------------------------------------- /Common/Include/EventWrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CEventWrapper 4 | { 5 | public: 6 | 7 | CEventWrapper(LPSECURITY_ATTRIBUTES lpEventAttributes = nullptr, 8 | BOOL bManualReset = TRUE, 9 | BOOL bInitialState = FALSE, 10 | LPCTSTR lpName = nullptr) 11 | { 12 | m_Event = CreateEvent(lpEventAttributes, bManualReset, bInitialState, lpName); 13 | if (!m_Event) 14 | throw "no event"; 15 | } 16 | CEventWrapper(const CEventWrapper&) = delete; 17 | CEventWrapper& operator=(const CEventWrapper&) = delete; 18 | 19 | HANDLE Event() const 20 | { 21 | return m_Event; 22 | } 23 | 24 | operator const HANDLE() const 25 | { 26 | return m_Event; 27 | } 28 | 29 | void SetEvent() 30 | { 31 | ::SetEvent(m_Event); 32 | } 33 | 34 | void ResetEvent() 35 | { 36 | ::ResetEvent(m_Event); 37 | } 38 | 39 | void PulseEvent() 40 | { 41 | ::PulseEvent(m_Event); 42 | } 43 | 44 | ~CEventWrapper() 45 | { 46 | if (m_Event) 47 | { 48 | CloseHandle(m_Event); 49 | } 50 | } 51 | 52 | private: 53 | HANDLE 54 | m_Event{ nullptr }; 55 | }; 56 | -------------------------------------------------------------------------------- /Common/Include/Handle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Loosely based on Kenny Kerr's handle class from 2011 - https://msdn.microsoft.com/%20magazine/hh288076 3 | 4 | template 5 | struct HandleTraits 6 | { 7 | using Type = T; 8 | 9 | constexpr static Type Invalid() noexcept 10 | { 11 | return nullptr; 12 | } 13 | 14 | // static void Close(Type value) noexcept has to be provided, the rest can default 15 | 16 | static bool Equal(Type left, Type right) 17 | { 18 | return left == right; 19 | } 20 | 21 | static bool Less(Type left, Type right) 22 | { 23 | return left < right; 24 | } 25 | }; 26 | 27 | 28 | template 29 | class Handle 30 | { 31 | public: 32 | 33 | using Type = typename T::Type; 34 | 35 | Handle() noexcept = default; 36 | Handle(Handle const &) = delete; 37 | Handle & operator=(Handle const &) = delete; 38 | 39 | explicit Handle(Type value) noexcept : 40 | m_value(value) 41 | {} 42 | 43 | Handle(Handle && other) noexcept : 44 | m_value(detach(other)) 45 | {} 46 | 47 | Handle & operator=(Handle && other) noexcept 48 | { 49 | if (this != &other) 50 | { 51 | attach(other.detach()); 52 | } 53 | 54 | return *this; 55 | } 56 | 57 | ~Handle() noexcept 58 | { 59 | Close(); 60 | } 61 | 62 | void Close() noexcept 63 | { 64 | if (*this) 65 | { 66 | T::Close(m_value); 67 | m_value = T::Invalid(); 68 | } 69 | } 70 | 71 | explicit operator bool() const noexcept 72 | { 73 | return !T::Equal(m_value, T::Invalid()); 74 | } 75 | 76 | Type get() const noexcept 77 | { 78 | return m_value; 79 | } 80 | 81 | Type * set() noexcept 82 | { 83 | _ASSERTE(!*this); 84 | return &m_value; 85 | } 86 | 87 | Type* getunsaferef() const noexcept 88 | { 89 | //_ASSERTE(bool(*this)); 90 | return const_cast(&m_value); 91 | } 92 | 93 | void attach(Type value) noexcept 94 | { 95 | Close(); 96 | m_value = value; 97 | } 98 | 99 | Type detach() noexcept 100 | { 101 | Type value = m_value; 102 | m_value = T::Invalid(); 103 | return value; 104 | } 105 | 106 | // The syntax swap(x,y) seems more natural than x.swap(y) so use a friend function, not a method 107 | friend void swap(Handle & left, Handle & right) noexcept 108 | { 109 | std::exchange(left.m_value, right.m_value); 110 | } 111 | 112 | // Define equality operators 113 | bool operator==(Handle const & right) const noexcept 114 | { 115 | return T::Equal(get(), right.get()); 116 | } 117 | 118 | bool operator!=(Handle const & right) const noexcept 119 | { 120 | return !(*this == right); 121 | } 122 | 123 | // Ordering operators don't make much sense for handles, but allow STL containers 124 | 125 | bool operator<(Handle const & right) const noexcept 126 | { 127 | return T::Less(get(), right.get()); 128 | } 129 | 130 | bool operator>(Handle const & right) const noexcept 131 | { 132 | return right < *this; 133 | } 134 | 135 | bool operator<=(Handle const & right) const noexcept 136 | { 137 | return !(right < *this); 138 | } 139 | 140 | bool operator>=(Handle const & right) const noexcept 141 | { 142 | return !(*this < right); 143 | } 144 | 145 | private: 146 | 147 | Type m_value = T::Invalid(); 148 | 149 | }; 150 | -------------------------------------------------------------------------------- /Common/Include/ISocketStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class ISocketStream 3 | { 4 | protected: 5 | ~ISocketStream() = default; // Disallow polymorphic destruction 6 | public: 7 | virtual int Recv(LPVOID lpBuf, const size_t Len, const size_t MinLen = 1) = 0; 8 | virtual int Send(LPCVOID lpBuf, const size_t Len) = 0; 9 | virtual DWORD GetLastError() const = 0; 10 | virtual HRESULT Disconnect(bool CloseUnderlyingConnection = true) = 0; 11 | virtual void SetRecvTimeoutSeconds(int NewTimeoutSeconds, bool NewTimerAutomatic = true) = 0; 12 | virtual int GetRecvTimeoutSeconds() const = 0; 13 | virtual void SetSendTimeoutSeconds(int NewTimeoutSeconds, bool NewTimerAutomatic = true) = 0; 14 | virtual int GetSendTimeoutSeconds() const = 0; 15 | virtual void StartRecvTimer() = 0; 16 | virtual void StartSendTimer() = 0; 17 | virtual int GetTlsVersion() const = 0; 18 | }; 19 | -------------------------------------------------------------------------------- /Common/Include/SSLHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class CSSLHelper 3 | { 4 | private: 5 | const byte * const OriginalBufPtr; 6 | const byte * DataPtr; // Points to data inside message 7 | const byte* BufEnd = nullptr; 8 | const int MaxBufBytes; 9 | UINT8 contentType = 0, major = 0, minor = 0; 10 | UINT16 length = 0; 11 | UINT8 handshakeType = 0; 12 | UINT16 handshakeLength = 0; 13 | bool CanDecode(); 14 | bool decoded; 15 | public: 16 | CSSLHelper(const byte * BufPtr, const int BufBytes); 17 | ~CSSLHelper() = default; 18 | // Max length of handshake data buffer 19 | void TraceHandshake(); 20 | static size_t TracePacket(const void * const Ptr, const size_t MaxBufBytes); 21 | // Convert a protocol mask to a two digit protocol version 22 | static int getTlsVersionFromProtocol(DWORD protocol); 23 | // Is this packet a complete client initialize packet 24 | bool IsClientInitialize() const; 25 | // Get SNI provided hostname 26 | std::wstring GetSNI() const; 27 | }; 28 | -------------------------------------------------------------------------------- /Common/Include/SecBufferDescriptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #define SECURITY_WIN32 5 | #include 6 | #include 7 | 8 | class SecBufferDescriptor { 9 | public: 10 | // Constructor - creates descriptor with specified number of buffers 11 | explicit SecBufferDescriptor(ULONG bufferCount = 1); 12 | 13 | // Move constructor and assignment 14 | SecBufferDescriptor(SecBufferDescriptor&& other) noexcept; 15 | SecBufferDescriptor& operator=(SecBufferDescriptor&& other) noexcept; 16 | 17 | // Prevent copying as SecBufferDesc typically owns resources 18 | SecBufferDescriptor(const SecBufferDescriptor&) = delete; 19 | SecBufferDescriptor& operator=(const SecBufferDescriptor&) = delete; 20 | 21 | // Destructor 22 | ~SecBufferDescriptor(); 23 | 24 | // Access methods 25 | SecBufferDesc* get() { return &m_desc; } 26 | const SecBufferDesc* get() const { return &m_desc; } 27 | SecBufferDesc* operator->() { return &m_desc; } 28 | const SecBufferDesc* operator->() const { return &m_desc; } 29 | 30 | // Buffer manipulation methods - does not take ownership of pvBuffer 31 | void SetBuffer(ULONG index, ULONG bufferType, ULONG cbBuffer, PVOID pvBuffer); 32 | 33 | // Reset all buffers to empty state without freeing memory 34 | void Clear(); 35 | 36 | // Get individual buffer 37 | SecBuffer* GetBuffer(ULONG index); 38 | const SecBuffer* GetBuffer(ULONG index) const; 39 | 40 | // Get individual buffer 41 | SecBuffer* GetBufferByType(ULONG bufferType); 42 | const SecBuffer* GetBufferByType(ULONG bufferType) const; 43 | 44 | // Utility methods 45 | ULONG GetBufferCount() const { return m_desc.cBuffers; } 46 | bool IsEmpty() const; 47 | 48 | private: 49 | SecBufferDesc m_desc; 50 | std::vector m_buffers; 51 | }; -------------------------------------------------------------------------------- /Common/Include/SecurityHandle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Handle.h" 3 | 4 | #include 5 | #pragma comment(lib, "crypt32.lib") 6 | #include 7 | #include 8 | #define SECURITY_WIN32 9 | #include 10 | #pragma comment(lib, "secur32.lib") 11 | 12 | #include "Common.h" 13 | 14 | /* 15 | This is one of 2 different approaches to handling old Windows handle classes. 16 | 17 | The approach in "SecurityHandle.h" encapsulates the native handles but leaves the class usage 18 | somewhat untouched (certainly not quite untouched, since handle usages tend to be replaced by 19 | "get" or "set" calls, but generally the notion of a handle to an object is preserved with lifetime 20 | management using RAII added. 21 | 22 | The approach in "CertRAII...." also replaces handles with objects encapsulating the handles, but 23 | these behave as if they were the underlying object. So, for example instead of a "CertContextHandle" 24 | class, we have a "CertStore" class with a CertStore::AddCertificateContext method, which ultimately 25 | calls ::CertAddCertificateContextToStore. This works fine if only a relatively small number 26 | of methods are to be called, but since each one has to be added to the class, it can be a pain 27 | if a lot of methods are needed so the SecurityHandle.h approach works better then. 28 | */ 29 | 30 | // Concrete handle type for certificates (traits first, then the actual class, defined using the traits class) 31 | 32 | struct CertContextTraits : public HandleTraits 33 | { 34 | static void Close(Type value) 35 | { 36 | CertFreeCertificateContext(value); 37 | } 38 | }; 39 | 40 | class CertContextHandle : public Handle 41 | { 42 | using Handle::Handle; // Inherit constructors 43 | }; 44 | 45 | // CredentialHandle class, similar to a handle class, but a CtxtHandle is a SecHandle 2 word structure 46 | 47 | struct CredentialTraits : public HandleTraits 48 | { 49 | static void Close(Type value) 50 | { 51 | CSSLCommon::SSPI()->DeleteSecurityContext(&value); 52 | } 53 | 54 | constexpr static Type Invalid() noexcept 55 | { 56 | return { (ULONG_PTR)((INT_PTR)-1), (ULONG_PTR)((INT_PTR)-1) }; 57 | } 58 | 59 | static bool Equal(Type left, Type right) 60 | { 61 | return (left.dwUpper == right.dwUpper) && (left.dwLower == right.dwLower); 62 | } 63 | 64 | static bool Less(Type left, Type right) 65 | { 66 | return left.dwUpper == right.dwUpper ? left.dwLower < right.dwLower : left.dwUpper < right.dwUpper; 67 | } 68 | }; 69 | 70 | class CredentialHandle : public Handle 71 | { 72 | using Handle::Handle; // Inherit constructors 73 | }; 74 | 75 | // SecurityContextHandle class, similar to left handle class, but left CtxtHandle is left SecHandle 2 word structure 76 | 77 | struct SecurityContextTraits : public HandleTraits 78 | { 79 | static void Close(Type value) 80 | { 81 | CSSLCommon::SSPI()->DeleteSecurityContext(&value); 82 | } 83 | 84 | constexpr static Type Invalid() noexcept 85 | { 86 | return { (ULONG_PTR)((INT_PTR)-1), (ULONG_PTR)((INT_PTR)-1) }; 87 | } 88 | 89 | static bool Equal(Type left, Type right) 90 | { 91 | return (left.dwUpper == right.dwUpper) && (left.dwLower == right.dwLower); 92 | } 93 | 94 | static bool Less(Type left, Type right) 95 | { 96 | return left.dwUpper == right.dwUpper ? left.dwLower < right.dwLower : left.dwUpper < right.dwUpper; 97 | } 98 | }; 99 | 100 | class SecurityContextHandle : public Handle 101 | { 102 | using Handle::Handle; // Inherit constructors 103 | }; 104 | -------------------------------------------------------------------------------- /Common/Include/Utilities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // General purpose utilities for use both at compile time and run time 3 | #define Stringize(L) #L 4 | #define MakeString(M, L) M(L) 5 | #define $Line \ 6 | MakeString(Stringize, __LINE__) 7 | #define Reminder \ 8 | __FILE__ "(" $Line ") : Reminder: " 9 | // usage #pragma message(Reminder "your message here") 10 | 11 | // Handy structure to store version information 12 | struct VersionInfo 13 | { 14 | VersionInfo() : Major(0), Minor(0), BuildNum(0) {} 15 | unsigned int Major; 16 | unsigned int Minor; 17 | unsigned int BuildNum; 18 | }; 19 | bool GetWindowsVersion(VersionInfo& info); 20 | bool IsWindows11OrGreater(); 21 | std::wstring string_format(const WCHAR* pszFormat, ...); 22 | std::wstring WinErrorMsg(int nErrorCode); 23 | void PrintHexDump(const void *const buf, size_t length); 24 | void PrintHexDump(size_t length, const void * const buf); 25 | void PrintFullHexDump(size_t length, const void * const buf); 26 | void SetThreadName(std::string const &threadName); 27 | void SetThreadName(std::string const &threadName, DWORD dwThreadID); 28 | void DebugBeginMsg(); 29 | void DebugBeginMsg(const CHAR* pszFormat, ...); 30 | void DebugContinueMsg(const CHAR* pszFormat, ...); 31 | void DebugEndMsg(); 32 | void DebugEndMsg(const CHAR* pszFormat, ...); 33 | void DebugMsg(const CHAR* pszFormat, ...); 34 | void DebugMsg(const WCHAR* pszFormat, ...); 35 | void DebugHresult(const char* msg, HRESULT hr); 36 | void DebugHresult(const WCHAR* msg, HRESULT hr); 37 | std::string HexDigits(const void* const buf, size_t len); 38 | bool IsUserAdmin(); 39 | std::wstring GetHostName(COMPUTER_NAME_FORMAT WhichName = ComputerNameDnsHostname); 40 | std::wstring GetCurrentUserName(); 41 | const char* const GetVersionText(); 42 | -------------------------------------------------------------------------------- /Common/SecBufferDescriptor.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "SecBufferDescriptor.h" 3 | #include 4 | 5 | SecBufferDescriptor::SecBufferDescriptor(ULONG bufferCount) 6 | : m_buffers(bufferCount) 7 | { 8 | m_desc.ulVersion = SECBUFFER_VERSION; 9 | m_desc.cBuffers = bufferCount; 10 | m_desc.pBuffers = m_buffers.data(); 11 | 12 | // Initialize all buffers to empty 13 | Clear(); 14 | } 15 | 16 | SecBufferDescriptor::SecBufferDescriptor(SecBufferDescriptor&& other) noexcept 17 | : m_desc(other.m_desc) 18 | , m_buffers(std::move(other.m_buffers)) 19 | { 20 | // Update the buffer pointer to point to our moved buffer array 21 | m_desc.pBuffers = m_buffers.data(); 22 | 23 | // Clear the source descriptor 24 | other.m_desc.cBuffers = 0; 25 | other.m_desc.pBuffers = nullptr; 26 | } 27 | 28 | SecBufferDescriptor& SecBufferDescriptor::operator=(SecBufferDescriptor&& other) noexcept 29 | { 30 | if (this != &other) 31 | { 32 | m_desc = other.m_desc; 33 | m_buffers = std::move(other.m_buffers); 34 | 35 | // Update the buffer pointer to point to our moved buffer array 36 | m_desc.pBuffers = m_buffers.data(); 37 | 38 | // Clear the source descriptor 39 | other.m_desc.cBuffers = 0; 40 | other.m_desc.pBuffers = nullptr; 41 | } 42 | return *this; 43 | } 44 | 45 | SecBufferDescriptor::~SecBufferDescriptor() 46 | { 47 | Clear(); 48 | } 49 | 50 | void SecBufferDescriptor::SetBuffer(ULONG index, ULONG bufferType, ULONG cbBuffer, PVOID pvBuffer) 51 | { 52 | if (index >= m_desc.cBuffers) 53 | throw std::out_of_range("Buffer index out of range"); 54 | 55 | m_buffers[index].BufferType = bufferType; 56 | m_buffers[index].cbBuffer = cbBuffer; 57 | m_buffers[index].pvBuffer = pvBuffer; 58 | } 59 | 60 | void SecBufferDescriptor::Clear() 61 | { 62 | for (ULONG i = 0; i < m_desc.cBuffers; i++) 63 | { 64 | // Reset to empty state without freeing memory 65 | m_buffers[i].BufferType = SECBUFFER_EMPTY; 66 | m_buffers[i].cbBuffer = 0; 67 | m_buffers[i].pvBuffer = nullptr; 68 | } 69 | } 70 | 71 | SecBuffer* SecBufferDescriptor::GetBuffer(ULONG index) 72 | { 73 | if (index >= m_desc.cBuffers) 74 | throw std::out_of_range("Buffer index out of range"); 75 | 76 | return &m_buffers[index]; 77 | } 78 | 79 | const SecBuffer* SecBufferDescriptor::GetBuffer(ULONG index) const 80 | { 81 | if (index >= m_desc.cBuffers) 82 | throw std::out_of_range("Buffer index out of range"); 83 | 84 | return &m_buffers[index]; 85 | } 86 | 87 | SecBuffer* SecBufferDescriptor::GetBufferByType(ULONG bufferType) 88 | { 89 | for (ULONG i = 0; i < m_desc.cBuffers; i++) 90 | { 91 | if (m_buffers[i].BufferType == bufferType) 92 | return &m_buffers[i]; 93 | } 94 | return nullptr; 95 | } 96 | 97 | const SecBuffer* SecBufferDescriptor::GetBufferByType(ULONG bufferType) const 98 | { 99 | for (ULONG i = 0; i < m_desc.cBuffers; i++) 100 | { 101 | if (m_buffers[i].BufferType == bufferType) 102 | return &m_buffers[i]; 103 | } 104 | return nullptr; 105 | } 106 | 107 | bool SecBufferDescriptor::IsEmpty() const 108 | { 109 | for (ULONG i = 0; i < m_desc.cBuffers; i++) 110 | { 111 | if (m_buffers[i].BufferType != SECBUFFER_EMPTY) 112 | return false; 113 | } 114 | return true; 115 | } -------------------------------------------------------------------------------- /Common/Utilities.cpp: -------------------------------------------------------------------------------- 1 | #include "Utilities.h" 2 | #include "pch.h" 3 | 4 | #include "Utilities.h" 5 | #include "AppVersion.h" 6 | 7 | #include 8 | #include // For std::unique_ptr 9 | #include 10 | 11 | // General purpose functions 12 | 13 | std::wstring& rtrim(std::wstring& str, const std::wstring& chars = L"\t\n\v\f\r ") 14 | { 15 | str.erase(str.find_last_not_of(chars) + 1); 16 | return str; 17 | } 18 | 19 | std::wstring string_format(const WCHAR* pszFormat, ...) { 20 | int n = lstrlen(pszFormat) * 2; /* Reserve two times as much as the length of the fmt_str */ 21 | if (n < 10) n = 10; 22 | std::unique_ptr formatted; 23 | va_list ap; 24 | while (true) { 25 | formatted = std::make_unique(n); /* Wrap the plain char array into the unique_ptr */ 26 | wcscpy_s(&formatted[0], n, pszFormat); 27 | va_start(ap, pszFormat); 28 | const int final_n = _vsnwprintf_s(&formatted[0],n, n, pszFormat, ap); 29 | va_end(ap); 30 | if (final_n < 0 || final_n >= n) 31 | n += abs(final_n - n + 1); 32 | else 33 | break; 34 | } 35 | return std::wstring(formatted.get()); 36 | } 37 | // Utility functions to handle Windows Versions 38 | bool GetWindowsVersion(VersionInfo& info) 39 | { 40 | // Thanks to https://www.codeproject.com/Articles/5336372/Windows-Version-Detection for this method 41 | auto sharedUserData = (BYTE*)0x7FFE0000; 42 | info.Major = *(ULONG*)(sharedUserData + 0x26c); 43 | info.Minor = *(ULONG*)(sharedUserData + 0x270); 44 | info.BuildNum = *(ULONG*)(sharedUserData + 0x260); 45 | return true; 46 | } 47 | 48 | bool IsWindows11OrGreater() 49 | { 50 | VersionInfo info; 51 | return GetWindowsVersion(info) && (info.Major > 10 || (info.Major == 10 && info.BuildNum >= 22000)); 52 | } 53 | // Utility function to get the hostname of the host I am running on 54 | std::wstring GetHostName(COMPUTER_NAME_FORMAT WhichName) 55 | { 56 | DWORD NameLength = 0; 57 | if (ERROR_SUCCESS == ::GetComputerNameEx(WhichName, nullptr, &NameLength)) 58 | { 59 | std::wstring ComputerName; 60 | ComputerName.resize(NameLength); 61 | if (::GetComputerNameEx(WhichName, &ComputerName[0], &NameLength)) 62 | { 63 | return ComputerName; 64 | } 65 | } 66 | return std::wstring(); 67 | } 68 | 69 | // Utility function to return the user name I'm running under 70 | std::wstring GetCurrentUserName() 71 | { 72 | DWORD NameLength = 0; 73 | if (ERROR_SUCCESS == ::GetUserName(nullptr, &NameLength)) 74 | { 75 | std::wstring UserName; 76 | UserName.resize(NameLength); 77 | if (::GetUserName(&UserName[0], &NameLength)) 78 | { 79 | return UserName; 80 | } 81 | } 82 | return std::wstring(); 83 | } 84 | 85 | std::wstring WinErrorMsg(int nErrorCode) 86 | { 87 | std::wstring theMsg; 88 | constexpr int MaxMsgLen = 200; 89 | theMsg.resize(MaxMsgLen); // Reserve enough space to allow the message to fit inside the string 90 | try 91 | { 92 | auto len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 93 | nullptr, nErrorCode, 94 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 95 | &theMsg[0], 96 | MaxMsgLen, 97 | nullptr); 98 | theMsg.resize(len); 99 | rtrim(theMsg); 100 | if (theMsg.empty()) 101 | theMsg = string_format(L"Error code %u (0x%.8x)", nErrorCode, nErrorCode); 102 | } 103 | catch (...) 104 | { 105 | } 106 | return theMsg; 107 | } 108 | 109 | // 110 | // Usage: SetThreadName ("MainThread"[, threadID]); 111 | // 112 | const DWORD MS_VC_EXCEPTION = 0x406D1388; 113 | 114 | #pragma pack(push,8) 115 | typedef struct tagTHREADNAME_INFO 116 | { 117 | DWORD dwType; // Must be 0x1000. 118 | LPCSTR szName; // Pointer to name (in user addr space). 119 | DWORD dwThreadID; // Thread ID (MAXDWORD=caller thread). 120 | DWORD dwFlags; // Reserved for future use, must be zero. 121 | } THREADNAME_INFO; 122 | #pragma pack(pop) 123 | 124 | void SetThreadName(std::string const &threadName) 125 | { 126 | SetThreadName(threadName, MAXDWORD); 127 | } 128 | 129 | void SetThreadName(std::string const &threadName, DWORD dwThreadID) 130 | { 131 | THREADNAME_INFO info; 132 | info.dwType = 0x1000; 133 | info.szName = threadName.c_str(); 134 | info.dwThreadID = dwThreadID; 135 | info.dwFlags = 0; 136 | 137 | __try 138 | { 139 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); 140 | } 141 | __except (EXCEPTION_EXECUTE_HANDLER) 142 | { 143 | } 144 | } 145 | 146 | void DebugEndMsg() 147 | { 148 | if (debug) 149 | OutputDebugStringA("\n"); 150 | } 151 | 152 | void DebugEndMsg(const CHAR* pszFormat, ...) 153 | { 154 | if (debug) 155 | { 156 | va_list arglist; 157 | va_start(arglist, pszFormat); 158 | CHAR buf[1024]; 159 | StringCchVPrintfA(buf, _countof(buf), pszFormat, arglist); 160 | va_end(arglist); 161 | OutputDebugStringA(buf); 162 | DebugEndMsg(); 163 | } 164 | } 165 | 166 | void DebugContinueMsg(const CHAR* pszFormat, ...) 167 | { 168 | if (debug) 169 | { 170 | CHAR buf[1024]; 171 | va_list arglist; 172 | va_start(arglist, pszFormat); 173 | StringCchVPrintfA(buf, _countof(buf), pszFormat, arglist); 174 | va_end(arglist); 175 | OutputDebugStringA(buf); 176 | } 177 | } 178 | void DebugBeginMsg() 179 | { 180 | if (debug) 181 | { 182 | CHAR buf[20]; 183 | StringCchPrintfA(buf, _countof(buf), "(%lu): ", GetCurrentThreadId()); 184 | OutputDebugStringA(buf); 185 | } 186 | } 187 | 188 | void DebugBeginMsg(const CHAR* pszFormat, ...) 189 | { 190 | if (debug) 191 | { 192 | DebugBeginMsg(); 193 | 194 | va_list arglist; 195 | va_start(arglist, pszFormat); 196 | CHAR buf[1024]; 197 | StringCchVPrintfA(buf, _countof(buf), pszFormat, arglist); 198 | va_end(arglist); 199 | OutputDebugStringA(buf); 200 | } 201 | } 202 | 203 | void DebugMsg(const CHAR* pszFormat, ...) 204 | { 205 | if (debug) 206 | { 207 | DebugBeginMsg(); 208 | va_list arglist; 209 | va_start(arglist, pszFormat); 210 | CHAR buf[1024]; 211 | StringCchVPrintfA(buf, _countof(buf), pszFormat, arglist); 212 | //DebugContinueMsg(pszFormat, arglist); 213 | va_end(arglist); 214 | OutputDebugStringA(buf); 215 | DebugEndMsg(); 216 | } 217 | } 218 | 219 | void DebugMsg(const WCHAR* pszFormat, ...) 220 | { 221 | if (debug) 222 | { 223 | WCHAR buf[1024]; 224 | StringCchPrintfW(buf, sizeof(buf) / sizeof(WCHAR), L"(%lu): ", GetCurrentThreadId()); 225 | va_list arglist; 226 | va_start(arglist, pszFormat); 227 | StringCchVPrintfW(&buf[wcslen(buf)], sizeof(buf) / sizeof(WCHAR), pszFormat, arglist); 228 | va_end(arglist); 229 | StringCchCatW(buf, sizeof(buf) / sizeof(WCHAR), L"\n"); 230 | OutputDebugStringW(buf); 231 | } 232 | } 233 | 234 | void DebugHresult(const char* msg, HRESULT hr) 235 | { 236 | if (debug) 237 | { 238 | char buf[1024]; 239 | StringCchPrintfA(buf, _countof(buf), "(%lu): %s returned % #x (% S)\n", GetCurrentThreadId(), msg, hr, WinErrorMsg(hr).c_str()); 240 | OutputDebugStringA(buf); 241 | } 242 | } 243 | 244 | void DebugHresult(const WCHAR* msg, HRESULT hr) 245 | { 246 | if (debug) 247 | { 248 | WCHAR buf[1024]; 249 | StringCchPrintfW(buf, _countof(buf), L"(%lu): %s returned % #x (% s)\n", GetCurrentThreadId(), msg, hr, WinErrorMsg(hr).c_str()); 250 | OutputDebugStringW(buf); 251 | } 252 | } 253 | 254 | std::string HexDigits(const void* const buf, size_t len) 255 | { 256 | CHAR rgbDigits[] = "0123456789abcdef"; 257 | char formattedText[100]; 258 | const auto* buffer = static_cast(buf); 259 | char formattedTextIndex = 0; 260 | 261 | size_t length = min(len, 16); // in c++17 this would be std::clamp((int)len, 0, 16); 262 | 263 | formattedText[formattedTextIndex++] = ' '; 264 | formattedText[formattedTextIndex++] = ':'; 265 | formattedText[formattedTextIndex++] = ' '; 266 | 267 | for (size_t i = 0; i < length; i++) // step through each hexade 268 | { 269 | formattedText[formattedTextIndex++] = rgbDigits[buffer[i] >> 4]; 270 | formattedText[formattedTextIndex++] = rgbDigits[buffer[i] & 0x0f]; 271 | formattedText[formattedTextIndex++] = ' '; 272 | if (i == 7 && length > 8) 273 | { 274 | formattedText[formattedTextIndex++] = ':'; 275 | formattedText[formattedTextIndex++] = ' '; 276 | } 277 | } 278 | if (false) 279 | { 280 | // Representation as ASCII characters 281 | formattedText[formattedTextIndex++] = ' '; 282 | formattedText[formattedTextIndex++] = '\"'; 283 | for (size_t i = 0; i < length; i++) 284 | { 285 | formattedText[formattedTextIndex++] = (buffer[i] < 32 || buffer[i] > 126) ? '.' : buffer[i]; 286 | } 287 | formattedText[formattedTextIndex++] = '\"'; 288 | } 289 | formattedText[formattedTextIndex++] = 0; 290 | std::string res(formattedText); 291 | return res; 292 | } 293 | 294 | static void PrintHexDumpActual(size_t length, const void * const buf, const bool verbose) 295 | { 296 | size_t i, count, index; 297 | CHAR rgbDigits[] = "0123456789abcdef"; 298 | CHAR rgbLine[100]; 299 | const auto * buffer = static_cast(buf); 300 | 301 | if (!verbose && (length > 16)) 302 | length = 16; 303 | 304 | for (index = 0; length; length -= count, buffer += count, index += count) 305 | { 306 | count = (length > 16) ? 16 : length; 307 | 308 | sprintf_s(rgbLine, sizeof(rgbLine), "%4Ix: ", index); 309 | char cbLine = 6; 310 | 311 | for (i = 0; i < count; i++) 312 | { 313 | rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4]; 314 | rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f]; 315 | if (i == 7) 316 | { 317 | rgbLine[cbLine++] = ':'; 318 | } 319 | else 320 | { 321 | rgbLine[cbLine++] = ' '; 322 | } 323 | } 324 | for (; i < 16; i++) 325 | { 326 | rgbLine[cbLine++] = ' '; 327 | rgbLine[cbLine++] = ' '; 328 | rgbLine[cbLine++] = ' '; 329 | } 330 | 331 | rgbLine[cbLine++] = ' '; 332 | 333 | for (i = 0; i < count; i++) 334 | { 335 | if (buffer[i] < 32 || buffer[i] > 126 || buffer[i] == '%') 336 | rgbLine[cbLine++] = '.'; 337 | else 338 | rgbLine[cbLine++] = buffer[i]; 339 | } 340 | rgbLine[cbLine++] = 0; 341 | DebugMsg(rgbLine); 342 | } 343 | } 344 | 345 | void PrintHexDump(const void *const buf, size_t length) 346 | { 347 | if (debug) PrintHexDumpActual(length, buf, false); 348 | } 349 | 350 | void PrintHexDump(size_t length, const void * const buf) 351 | { 352 | if (debug) PrintHexDumpActual(length, buf, false); 353 | } 354 | 355 | void PrintFullHexDump(size_t length, const void * const buf) 356 | { 357 | if (debug) PrintHexDumpActual(length, buf, true); 358 | } 359 | 360 | bool IsUserAdmin() 361 | { 362 | BOOL b; 363 | SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; 364 | PSID AdministratorsGroup; 365 | b = AllocateAndInitializeSid( 366 | &NtAuthority, 367 | 2, 368 | SECURITY_BUILTIN_DOMAIN_RID, 369 | DOMAIN_ALIAS_RID_ADMINS, 370 | 0, 0, 0, 0, 0, 0, 371 | &AdministratorsGroup); 372 | if (b) 373 | { 374 | if (!CheckTokenMembership(nullptr, AdministratorsGroup, &b)) 375 | { 376 | b = FALSE; 377 | } 378 | FreeSid(AdministratorsGroup); 379 | } 380 | 381 | return (b == TRUE); 382 | } 383 | 384 | #define STRINGIZE(n) Stringize(n) 385 | 386 | // Utility function to get the version of the application 387 | const char* const GetVersionText() 388 | { 389 | return STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "." STRINGIZE(VERSION_PATCH) "\0"; 390 | } -------------------------------------------------------------------------------- /Developer.md: -------------------------------------------------------------------------------- 1 | # Developer Notes 2 | 3 | Don't forget to update version information in AppVersion.h before a push to master. -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | # The Code Project Open License (CPOL) 1.02 2 | 3 |
4 | 5 |
6 | 7 | ## Preamble 8 | 9 | This License governs Your use of the Work. This License is intended to allow developers to use the Source Code and Executable Files provided as part of the Work in any application in any form. 10 | 11 | The main points subject to the terms of the License are: 12 | 13 | * Source Code and Executable Files can be used in commercial applications; 14 | * Source Code and Executable Files can be redistributed; and 15 | * Source Code can be modified to create derivative works. 16 | * No claim of suitability, guarantee, or any warranty whatsoever is provided. The software is provided "as-is". 17 | * The Article accompanying the Work may not be distributed or republished without the Author's consent 18 | 19 | This License is entered between You, the individual or other entity reading or otherwise making use of the Work licensed pursuant to this License and the individual or other entity which offers the Work under the terms of this License ("Author"). 20 | 21 | ## License 22 | 23 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT OPEN LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 24 | 25 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HEREIN, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO ACCEPT AND BE BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY USE OF THE WORK. 26 | 27 | 1. **Definitions.** 28 | 1. **"Articles"** means, collectively, all articles written by Author which describes how the Source Code and Executable Files for the Work may be used by a user. 29 | 2. **"Author"** means the individual or entity that offers the Work under the terms of this License. 30 | 3. **"Derivative Work"** means a work based upon the Work or upon the Work and other pre-existing works. 31 | 4. **"Executable Files"** refer to the executables, binary files, configuration and any required data files included in the Work. 32 | 5. "**Publisher**" means the provider of the website, magazine, CD-ROM, DVD or other medium from or by which the Work is obtained by You. 33 | 6. **"Source Code"** refers to the collection of source code and configuration files used to create the Executable Files. 34 | 7. **"Standard Version"** refers to such a Work if it has not been modified, or has been modified in accordance with the consent of the Author, such consent being in the full discretion of the Author. 35 | 8. **"Work"** refers to the collection of files distributed by the Publisher, including the Source Code, Executable Files, binaries, data files, documentation, whitepapers and the Articles. 36 | 9. **"You"** is you, an individual or entity wishing to use the Work and exercise your rights under this License. 37 | 2. **Fair Use/Fair Use Rights.** Nothing in this License is intended to reduce, limit, or restrict any rights arising from fair use, fair dealing, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws. 38 | 3. **License Grant.** Subject to the terms and conditions of this License, the Author hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: 39 | 1. You may use the standard version of the Source Code or Executable Files in Your own applications. 40 | 2. You may apply bug fixes, portability fixes and other modifications obtained from the Public Domain or from the Author. A Work modified in such a way shall still be considered the standard version and will be subject to this License. 41 | 3. You may otherwise modify Your copy of this Work (excluding the Articles) in any way to create a Derivative Work, provided that You insert a prominent notice in each changed file stating how, when and where You changed that file. 42 | 4. You may distribute the standard version of the Executable Files and Source Code or Derivative Work in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution. 43 | 5. The Articles discussing the Work published in any form by the author may not be distributed or republished without the Author's consent. The author retains copyright to any such Articles. You may use the Executable Files and Source Code pursuant to this License but you may not repost or republish or otherwise distribute or make available the Articles, without the prior written consent of the Author.Any subroutines or modules supplied by You and linked into the Source Code or Executable Files of this Work shall not be considered part of this Work and will not be subject to the terms of this License. 44 | 4. **Patent License.** Subject to the terms and conditions of this License, each Author hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, import, and otherwise transfer the Work. 45 | 5. **Restrictions.** The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: 46 | 1. You agree not to remove any of the original copyright, patent, trademark, and attribution notices and associated disclaimers that may appear in the Source Code or Executable Files. 47 | 2. You agree not to advertise or in any way imply that this Work is a product of Your own. 48 | 3. The name of the Author may not be used to endorse or promote products derived from the Work without the prior written consent of the Author. 49 | 4. You agree not to sell, lease, or rent any part of the Work. This does not restrict you from including the Work or any part of the Work inside a larger software distribution that itself is being sold. The Work by itself, though, cannot be sold, leased or rented. 50 | 5. You may distribute the Executable Files and Source Code only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy of the Executable Files or Source Code You distribute and ensure that anyone receiving such Executable Files and Source Code agrees that the terms of this License apply to such Executable Files and/or Source Code. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute the Executable Files or Source Code with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License. 51 | 6. You agree not to use the Work for illegal, immoral or improper purposes, or on pages containing illegal, immoral or improper material. The Work is subject to applicable export laws. You agree to comply with all such laws and regulations that may apply to the Work after Your receipt of the Work. 52 | 6. **Representations, Warranties and Disclaimer.** THIS WORK IS PROVIDED "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OR CONDITIONS OR GUARANTEES. YOU, THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING COPYRIGHT INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT, OR THAT THE WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE WORKS. 53 | 7. **Indemnity.** You agree to defend, indemnify and hold harmless the Author and the Publisher from and against any claims, suits, losses, damages, liabilities, costs, and expenses (including reasonable legal or attorneys’ fees) resulting from or relating to any use of the Work by You. 54 | 8. **Limitation on Liability.** EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL THE AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE AUTHOR OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 55 | 9. **Termination.** 56 | 1. This License and the rights granted hereunder will terminate automatically upon any breach by You of any term of this License. Individuals or entities who have received Derivative Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any termination of this License. 57 | 2. If You bring a copyright, trademark, patent or any other infringement claim against any contributor over infringements You claim are made by the Work, your License from such contributor to the Work ends automatically. 58 | 3. Subject to the above terms and conditions, this License is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, the Author reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 59 | 10. **Publisher**. The parties hereby confirm that the Publisher shall not, under any circumstances, be responsible for and shall not have any liability in respect of the subject matter of this License. The Publisher makes no warranty whatsoever in connection with the Work and shall not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. The Publisher reserves the right to cease making the Work available to You at any time without notice 60 | 11. **Miscellaneous** 61 | 1. This License shall be governed by the laws of the location of the head office of the Author or if the Author is an individual, the laws of location of the principal place of residence of the Author. 62 | 2. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this License, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 63 | 3. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. 64 | 4. This License constitutes the entire agreement between the parties with respect to the Work licensed herein. There are no understandings, agreements or representations with respect to the Work not specified herein. The Author shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Author and You. 65 | 66 |
67 | 68 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MSBuild](https://github.com/david-maw/StreamSSL/actions/workflows/msbuild.yml/badge.svg)](https://github.com/david-maw/StreamSSL/actions/workflows/msbuild.yml) 2 | # StreamSSL 3 | 4 | This is a framework which allows you to implement a TLS client or multi-threaded server in C++ relatively easily. It includes a working example and allows for server and optional client authentication. 5 | 6 | The whole project is fully described [here](Article/Article.md), it was originally a CodeProject article [here]( 7 | http://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL). 8 | 9 | It is licensed under the CodeProject Open License (see the [License.md](License.md) file in the project) also available [here](https://www.codeproject.com/info/cpol10.aspx). 10 | -------------------------------------------------------------------------------- /Repository/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Repository/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # LightSwitch generated files 187 | GeneratedArtifacts/ 188 | _Pvt_Extensions/ 189 | ModelManifest.xml -------------------------------------------------------------------------------- /SSLClient/ActiveSock.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | #include "ActiveSock.h" 4 | 5 | CActiveSock::CActiveSock(HANDLE StopEvent) 6 | : CBaseSock(StopEvent) 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /SSLClient/Include/ActiveSock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BaseSock.h" 3 | 4 | class CActiveSock : public CBaseSock 5 | { 6 | public: 7 | // Active socket which will use "Connect" to a specified destination 8 | explicit CActiveSock(HANDLE StopEvent); 9 | // Do not allow passive socket binding to an existing socket 10 | CActiveSock(SOCKET s, HANDLE StopEvent) = delete; 11 | }; 12 | -------------------------------------------------------------------------------- /SSLClient/Include/SSLClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SecurityHandle.h" 4 | #include "Common.h" 5 | #include 6 | 7 | class CActiveSock; // forward declaration 8 | 9 | class CSSLClient : public CSSLCommon 10 | { 11 | public: 12 | explicit CSSLClient(CActiveSock*); 13 | ~CSSLClient() = default; 14 | // ISocketStream Methods 15 | int Recv(LPVOID lpBuf, const size_t Len, const size_t MinLen = 1); 16 | int Send(LPCVOID lpBuf, const size_t Len); 17 | DWORD GetLastError() const; 18 | HRESULT Disconnect(bool closeUnderlyingSocket = true); 19 | void SetRecvTimeoutSeconds(int NewRecvTimeoutSeconds, bool NewTimerAutomatic = true); 20 | int GetRecvTimeoutSeconds() const; 21 | void SetSendTimeoutSeconds(int NewSendTimeoutSeconds, bool NewTimerAutomatic = true); 22 | int GetSendTimeoutSeconds() const; 23 | void StartRecvTimer(); 24 | void StartSendTimer(); 25 | int GetTlsVersion() const { return TlsVersion; } 26 | std::wstring ServerName; 27 | // Regular class interface 28 | // Set up state for this connection 29 | HRESULT Initialize(std::wstring ServerName, const void * const lpBuf = nullptr, const int Len = 0); 30 | // Attributes 31 | std::function ServerCertAcceptable; 32 | std::function SelectClientCertificate; 33 | bool getServerCertNameMatches() const; 34 | bool getServerCertTrusted() const; 35 | 36 | private: 37 | CredentialHandle m_ClientCreds; 38 | CActiveSock *m_SocketStream; 39 | int m_LastError{ 0 }; 40 | int TlsVersion{ 0 }; // Two digit TLS version, e.g. 12 for TLS 1.2 41 | bool m_encrypting = false; 42 | static HRESULT InitializeClass(); 43 | SECURITY_STATUS SSPINegotiate(LPCWCHAR ServerName); 44 | SECURITY_STATUS ActualSSPINegotiateLoop(LPCWCHAR ServerName, SecBuffer* pInitialBuffer = nullptr); 45 | int GetDataFromSocket(); 46 | static const int MaxMsgSize = 16000; // Arbitrary but less than 16384 limit, including MaxExtraSize 47 | static const int MaxExtraSize = 50; // Also arbitrary, current header is 5 bytes, trailer 36 48 | CHAR writeBuffer[MaxMsgSize + MaxExtraSize]{}; // Enough for a whole encrypted message 49 | CHAR readBuffer[(MaxMsgSize + MaxExtraSize) *2]{}; // Enough for two whole messages so we don't need to move data around in buffers 50 | size_t readBufferBytes = 0; // Bytes read from socket but not yet consumed 51 | CHAR plainText[MaxMsgSize *2]{}; // Extra plaintext data not yet delivered 52 | CHAR *plainTextPtr = nullptr; 53 | size_t plainTextBytes = 0; 54 | void *readPtr = nullptr; 55 | SecurityContextHandle m_hContext; 56 | SecPkgContext_StreamSizes Sizes{}; 57 | static SECURITY_STATUS CreateCredentialsFromCertificate(PCredHandle phCreds, const PCCERT_CONTEXT pCertContext); 58 | SECURITY_STATUS GetNewClientCredentials(); 59 | int RecvPartialEncrypted(LPVOID lpBuf, const size_t Len); 60 | bool ServerCertNameMatches{ false }; 61 | bool ServerCertTrusted{ false }; 62 | HRESULT DisconnectSSL(); 63 | }; 64 | -------------------------------------------------------------------------------- /SSLClient/SSLClient.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {71D079B7-91C6-4CA2-8BFE-E369835DA8F3} 23 | Win32Proj 24 | StreamClient 25 | 10.0 26 | SSLClient 27 | 28 | 29 | 30 | StaticLibrary 31 | true 32 | Unicode 33 | v143 34 | 35 | 36 | StaticLibrary 37 | true 38 | Unicode 39 | v143 40 | false 41 | 42 | 43 | StaticLibrary 44 | false 45 | v143 46 | true 47 | Unicode 48 | 49 | 50 | StaticLibrary 51 | false 52 | v143 53 | true 54 | Unicode 55 | false 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)$(Platform)\$(Configuration)\ 76 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 77 | 78 | 79 | true 80 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 81 | 82 | 83 | false 84 | $(SolutionDir)$(Platform)\$(Configuration)\ 85 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 86 | 87 | 88 | false 89 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 90 | 91 | 92 | 93 | Use 94 | Level4 95 | Disabled 96 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 97 | true 98 | true 99 | pch.h 100 | .\;Include;..\Common\Include 101 | stdcpplatest 102 | true 103 | true 104 | $(OutDir)$(TargetName).pdb 105 | 106 | 107 | Console 108 | true 109 | 110 | 111 | 112 | 113 | Use 114 | Level4 115 | Disabled 116 | _DEBUG;_LIB;%(PreprocessorDefinitions) 117 | true 118 | true 119 | pch.h 120 | .\;Include;..\Common\Include 121 | stdcpplatest 122 | true 123 | true 124 | $(OutDir)$(TargetName).pdb 125 | 126 | 127 | Console 128 | true 129 | 130 | 131 | 132 | 133 | Level4 134 | Use 135 | MaxSpeed 136 | true 137 | true 138 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 139 | true 140 | true 141 | pch.h 142 | .\;Include;..\Common\Include 143 | stdcpplatest 144 | true 145 | true 146 | $(OutDir)$(TargetName).pdb 147 | ProgramDatabase 148 | true 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | 156 | 157 | 158 | 159 | Level4 160 | Use 161 | MaxSpeed 162 | true 163 | true 164 | NDEBUG;_LIB;%(PreprocessorDefinitions) 165 | true 166 | true 167 | pch.h 168 | .\;Include;..\Common\Include 169 | stdcpplatest 170 | true 171 | true 172 | $(OutDir)$(TargetName).pdb 173 | ProgramDatabase 174 | true 175 | 176 | 177 | Console 178 | true 179 | true 180 | true 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | Create 210 | Create 211 | Create 212 | Create 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /SSLClient/SSLClient.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {17883f1f-bcff-41e8-a73d-12d627f7f227} 14 | 15 | 16 | {671b7d48-41ef-4a34-a15d-db6e477bb90f} 17 | 18 | 19 | 20 | 21 | Header Files 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files\Common Headers 31 | 32 | 33 | Header Files\Common Headers 34 | 35 | 36 | Header Files\Common Headers 37 | 38 | 39 | Header Files\Common Headers 40 | 41 | 42 | Header Files\Common Headers 43 | 44 | 45 | Header Files\Common Headers 46 | 47 | 48 | Header Files\Common Headers 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files\Common Headers 58 | 59 | 60 | Header Files\Common Headers 61 | 62 | 63 | Header Files\Common Headers 64 | 65 | 66 | 67 | 68 | Source Files 69 | 70 | 71 | Source Files 72 | 73 | 74 | Source Files 75 | 76 | 77 | Source Files\Common Sources 78 | 79 | 80 | Source Files\Common Sources 81 | 82 | 83 | Source Files\Common Sources 84 | 85 | 86 | Source Files\Common Sources 87 | 88 | 89 | Source Files\Common Sources 90 | 91 | 92 | -------------------------------------------------------------------------------- /SSLClient/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 5 | 6 | // The following commented code is for debugging memory leaks 7 | //#define _CRTDBG_MAP_ALLOC 8 | //#include 9 | //#include 10 | //#ifdef _DEBUG 11 | //#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) 12 | //// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the 13 | //// allocations to be of _CLIENT_BLOCK type 14 | //#else 15 | //#define DBG_NEW new 16 | //#endif 17 | 18 | // Define a bool to check if this is a DEBUG or RELEASE build 19 | #ifndef DEBUGFLAG_DEFINED 20 | #define DEBUGFLAG_DEFINED 21 | #if defined(_DEBUG) 22 | const bool debug = true; 23 | #else 24 | const bool debug = false; 25 | #endif 26 | #endif // DEBUGFLAG_DEFINED 27 | 28 | #ifndef VC_EXTRALEAN 29 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | #pragma comment(lib, "Ws2_32.lib") 37 | 38 | #define SECURITY_WIN32 39 | #include 40 | #include 41 | 42 | // Standard C++ 43 | #include 44 | typedef unsigned char byte; -------------------------------------------------------------------------------- /SSLClient/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /SSLClient/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /SSLClient/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // // Including SDKDDKVer.h defines the highest available Windows platform. 4 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 5 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 6 | #define _WIN32_WINNT 0x0600 // Allow use of features specific to Windows 6 (Vista) or later 7 | #include 8 | -------------------------------------------------------------------------------- /SSLServer/Include/Listener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "EventWrapper.h" 7 | 8 | class ISocketStream; 9 | 10 | class CListener 11 | { 12 | public: 13 | enum class ErrorType { 14 | NoError, 15 | UnknownError, 16 | SocketInuse, 17 | SocketUnusable 18 | }; 19 | CListener(); 20 | ~CListener(); 21 | private: 22 | SOCKET m_iListenSockets[FD_SETSIZE]{}; 23 | HANDLE m_hSocketEvents[FD_SETSIZE]{}; 24 | int m_iNumListenSockets{ 0 }; 25 | uintptr_t m_ListenerThread { 0 }; 26 | static void __cdecl Worker(LPVOID); 27 | static void __cdecl ListenerWorker(LPVOID); 28 | void Listen(); 29 | std::function m_actualwork; 30 | public: 31 | static void LogWarning(const WCHAR* const); 32 | static void LogWarning(const CHAR* const); 33 | std::atomic_int volatile m_WorkerCount{ 0 }; 34 | CEventWrapper m_StopEvent; 35 | // Initialize the listener 36 | ErrorType Initialize(int TCPSocket); 37 | std::function SelectServerCert; 38 | std::function ClientCertAcceptable; 39 | void EndListening(); 40 | void BeginListening(std::function actualwork); 41 | void IncrementWorkerCount(int i = 1); 42 | }; 43 | -------------------------------------------------------------------------------- /SSLServer/Listener.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | 4 | #include "Listener.h" 5 | #include "SSLServer.h" 6 | #include "Utilities.h" 7 | 8 | // CListener object, listens for connections on one thread, and initiates a worker 9 | // thread each time a client connects. 10 | CListener::CListener() 11 | { 12 | for (int i = 0; i < FD_SETSIZE; i++) 13 | { 14 | m_iListenSockets[i] = INVALID_SOCKET; 15 | m_hSocketEvents[i] = nullptr; 16 | } 17 | } 18 | 19 | CListener::~CListener() 20 | { 21 | for (int i = 0; i < FD_SETSIZE; i++) 22 | { 23 | if (m_iListenSockets[i] != INVALID_SOCKET) 24 | closesocket(m_iListenSockets[i]); 25 | if (m_hSocketEvents[i]) 26 | CloseHandle(m_hSocketEvents[i]); 27 | } 28 | } 29 | 30 | // This is the individual worker process, all it does is start, change its name to something useful, 31 | // then call the Lambda function passed in via the BeginListening method 32 | void __cdecl CListener::Worker(LPVOID v) 33 | { 34 | std::unique_ptr SSLServer(reinterpret_cast(v)); 35 | SetThreadName("Connection Worker"); 36 | // Invoke the caller provided function defining the work to do, passing an interface which 37 | // allows the user code to send and receive messages and so on. 38 | (SSLServer->GetListener()->m_actualwork)(SSLServer->GetSocketStream()); 39 | } 40 | 41 | // Worker process for connection listening 42 | void __cdecl CListener::ListenerWorker(LPVOID v) 43 | { 44 | auto * Listener = static_cast(v); // See _beginthread call for parameter definition 45 | 46 | SetThreadName("Listener"); 47 | Listener->Listen(); 48 | } 49 | 50 | // Initialize the listener, set up the socket to listen on, or return an error 51 | CListener::ErrorType CListener::Initialize(int TCPSocket) 52 | { 53 | std::wstring TCPSocketText = string_format(L"%i", TCPSocket); 54 | 55 | WSADATA wsadata; 56 | if (WSAStartup(MAKEWORD(1, 1), &wsadata)) 57 | return CListener::ErrorType::UnknownError; 58 | 59 | // Get list of addresses to listen on 60 | ADDRINFOT Hints, *AddrInfo, *AI; 61 | memset(&Hints, 0, sizeof(Hints)); 62 | Hints.ai_family = PF_UNSPEC; 63 | Hints.ai_socktype = SOCK_STREAM; 64 | Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; 65 | if (GetAddrInfo(nullptr, TCPSocketText.c_str(), &Hints, &AddrInfo) != 0) 66 | { 67 | WCHAR MsgText[100]; 68 | StringCchPrintf(MsgText, _countof(MsgText), L"getaddressinfo error: %i", GetLastError()); 69 | LogWarning(MsgText); 70 | return CListener::ErrorType::UnknownError; 71 | } 72 | 73 | // Create one or more passive sockets to listen on 74 | int i; 75 | for (i = 0, AI = AddrInfo; AI != nullptr; AI = AI->ai_next) 76 | { 77 | // Did we receive more addresses than we can handle? Highly unlikely, but check anyway. 78 | if (i == FD_SETSIZE) break; 79 | 80 | // Only support PF_INET and PF_INET6. If something else, skip to next address. 81 | if ((AI->ai_family != AF_INET) && (AI->ai_family != AF_INET6)) continue; 82 | 83 | // StringCchPrintf(MsgText, _countof(MsgText), L"::OnInit i = %d, ai_family = %d"), i, AI->ai_family; 84 | // LogWarning(MsgText); 85 | 86 | m_hSocketEvents[i] = CreateEvent( 87 | nullptr, // no security attributes 88 | true, // manual reset event 89 | false, // not signaled 90 | nullptr); // no name 91 | 92 | if (!(m_hSocketEvents[i])) 93 | return CListener::ErrorType::UnknownError; 94 | 95 | // StringCchPrintf(MsgText, _countof(MsgText), L"::OnInit Created m_hSocketEvents[%d], handle=%d"), i, m_hSocketEvents[i]; 96 | // LogWarning(MsgText); 97 | 98 | m_iListenSockets[i] = WSASocket(AI->ai_family, SOCK_STREAM, 0, nullptr, 0, WSA_FLAG_OVERLAPPED); 99 | if (m_iListenSockets[i] == INVALID_SOCKET) 100 | return CListener::ErrorType::SocketUnusable; 101 | 102 | // StringCchPrintf(MsgText, _countof(MsgText), L"::OnInit binding m_iListenSockets[%d] to sa_family=%u sa_data=%s len=%d"), i, AI->ai_addr->sa_family, AI->ai_addr->sa_data, AI->ai_addrlen; 103 | // LogWarning(MsgText); 104 | 105 | int rc = bind(m_iListenSockets[i], AI->ai_addr, (int)AI->ai_addrlen); 106 | if (rc) 107 | { 108 | if (WSAGetLastError() == WSAEADDRINUSE) 109 | return CListener::ErrorType::SocketInuse; 110 | else 111 | return CListener::ErrorType::SocketUnusable; 112 | } 113 | 114 | if (listen(m_iListenSockets[i], 10)) 115 | return CListener::ErrorType::SocketUnusable; 116 | if (WSAEventSelect(m_iListenSockets[i], m_hSocketEvents[i], FD_ACCEPT)) 117 | return CListener::ErrorType::SocketUnusable; 118 | i++; 119 | } 120 | 121 | m_iNumListenSockets = i; 122 | 123 | // StringCchPrintf(MsgText, _countof(MsgText), L"::OnInit no errors, m_iNumListenSockets = %d"), m_iNumListenSockets; 124 | // LogWarning(MsgText); 125 | 126 | return CListener::ErrorType::NoError; 127 | } 128 | 129 | // Start listening for connections, if a timeout is specified keep listening until then 130 | void CListener::BeginListening(std::function actualwork) 131 | { 132 | m_actualwork = actualwork; 133 | m_ListenerThread = _beginthread(ListenerWorker, 0, this); 134 | } 135 | 136 | void CListener::IncrementWorkerCount(int i) 137 | { 138 | m_WorkerCount += i; 139 | } 140 | 141 | // Stop listening, tells the listener thread it can stop, then waits for it to terminate 142 | void CListener::EndListening() 143 | { 144 | m_StopEvent.SetEvent(); 145 | if (m_ListenerThread) 146 | { 147 | WaitForSingleObject((HANDLE)m_ListenerThread, INFINITE); // Will auto delete 148 | } 149 | m_ListenerThread = 0; 150 | } 151 | 152 | // Log a warning 153 | void CListener::LogWarning(const WCHAR* const msg) 154 | { 155 | DebugMsg(ATL::CW2A(msg)); 156 | } 157 | void CListener::LogWarning(const CHAR* const msg) 158 | { 159 | DebugMsg(msg); 160 | } 161 | 162 | // Listen for connections until the "stop" event is caused, this is invoked on 163 | // its own thread 164 | void CListener::Listen() 165 | { 166 | HANDLE hEvents[FD_SETSIZE + 1]; 167 | SOCKET iReadSocket = NULL; 168 | //WCHAR MsgText[100]; 169 | 170 | m_WorkerCount = 0; 171 | 172 | DebugMsg("Start CListener::Listen method"); 173 | 174 | // StringCchPrintf(MsgText, _countof(MsgText), L"CListener::Listen m_iNumListenSockets= %d"), m_iNumListenSockets; 175 | // LogWarning(MsgText); 176 | 177 | hEvents[0] = m_StopEvent; 178 | // StringCchPrintf(MsgText, _countof(MsgText), L"CListener::Listen hEvents[0] = m_StopEvent = %d"), m_StopEvent; 179 | // LogWarning(MsgText); 180 | 181 | // Add the events for each socket type (two at most, one for IPv4, one for IPv6) 182 | for (int i = 0; i < m_iNumListenSockets; i++) 183 | { 184 | hEvents[i + 1] = m_hSocketEvents[i]; 185 | // StringCchPrintf(MsgText, _countof(MsgText), L"CListener::Listen hEvents[%d] = m_hSocketEvents[%d] = %d"), i+1, i, m_hSocketEvents[i]; 186 | // LogWarning(MsgText); 187 | } 188 | 189 | // Loop until there is a problem or the shutdown event is caused 190 | while (true) 191 | { 192 | // StringCchPrintf(MsgText, _countof(MsgText), L"CListener::Listen entering WaitForMultipleObjects for %d objects"), m_iNumListenSockets+1; 193 | // LogWarning(MsgText); 194 | 195 | const DWORD dwWait = WaitForMultipleObjects(m_iNumListenSockets + 1, hEvents, false, INFINITE); 196 | 197 | if (dwWait == WAIT_OBJECT_0) 198 | { 199 | // LogWarning("CListener::Listen received a stop event"); 200 | break; // Received a stop event 201 | } 202 | int iMyIndex = dwWait - 1; 203 | // StringCchPrintf(MsgText, _countof(MsgText), L"CListener::Listen event %d triggered, iMyIndex = %d"), dwWait, iMyIndex; 204 | // LogWarning(MsgText); 205 | 206 | WSAResetEvent(m_hSocketEvents[iMyIndex]); 207 | iReadSocket = accept(m_iListenSockets[iMyIndex], nullptr, nullptr); 208 | if (iReadSocket == INVALID_SOCKET) 209 | { 210 | LogWarning("iReadSocket == INVALID_SOCKET"); 211 | break; 212 | } 213 | 214 | // A request to open a socket has been received, begin a thread to handle that connection 215 | DebugMsg("Starting worker"); 216 | 217 | auto SSLServer = CSSLServer::Create(iReadSocket, this); 218 | if (SSLServer && SSLServer->IsConnected) 219 | _beginthread(Worker, 0, SSLServer); 220 | else 221 | delete SSLServer; 222 | iReadSocket = INVALID_SOCKET; 223 | } 224 | // Either we're done, or there has been a problem, wait for all the worker threads to terminate 225 | Sleep(500); 226 | while (m_WorkerCount) 227 | { 228 | Sleep(1000); 229 | DebugMsg("Waiting for all workers to terminate: worker thread count = %i", (int)m_WorkerCount); 230 | }; 231 | if ((iReadSocket != NULL) && (iReadSocket != INVALID_SOCKET)) 232 | closesocket(iReadSocket); 233 | DebugMsg("End Listen method"); 234 | } 235 | -------------------------------------------------------------------------------- /SSLServer/PassiveSock.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | #include "PassiveSock.h" 4 | 5 | CPassiveSock::CPassiveSock(SOCKET s, HANDLE StopEvent) 6 | :CBaseSock(s, StopEvent) 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /SSLServer/PassiveSock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BaseSock.h" 3 | 4 | class CPassiveSock : public CBaseSock 5 | { 6 | public: 7 | // Allow creation from a received SOCKET 8 | explicit CPassiveSock(SOCKET, HANDLE); 9 | // Disallow creation of an active socket 10 | CPassiveSock(HANDLE) = delete; 11 | 12 | private: 13 | // Disallow the active socket connect function 14 | using CBaseSock::Connect; 15 | }; 16 | -------------------------------------------------------------------------------- /SSLServer/SSLServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PassiveSock.h" 3 | #include "SecurityHandle.h" 4 | #include "Common.h" 5 | #include "SecBufferDescriptor.h" 6 | 7 | #include 8 | 9 | class CListener; 10 | 11 | class CSSLServer : public ISocketStream, public CSSLCommon 12 | { 13 | public: 14 | // The constructor is private, the only way to get hold of a CSSLServer is by calling Create. 15 | // This call is normally from a CListener. 16 | static CSSLServer* Create(SOCKET s, CListener* Listener); 17 | ~CSSLServer(); 18 | // ISocketStream functions 19 | int Recv(LPVOID lpBuf, const size_t Len, const size_t MinLen = 1) override; 20 | int Send(LPCVOID lpBuf, const size_t Len) override; 21 | DWORD GetLastError() const override; 22 | HRESULT Disconnect(bool CloseUnderlyingConnection) override; 23 | void SetRecvTimeoutSeconds(int NewRecvTimeoutSeconds, bool NewTimerAutomatic = true) override; 24 | int GetRecvTimeoutSeconds() const override; 25 | void SetSendTimeoutSeconds(int NewSendTimeoutSeconds, bool NewTimerAutomatic = true) override; 26 | int GetSendTimeoutSeconds() const override; 27 | void StartRecvTimer() override; 28 | void StartSendTimer() override; 29 | int GetTlsVersion() const override { return TlsVersion; } 30 | 31 | ISocketStream* GetSocketStream(); 32 | // Set up state for this connection 33 | HRESULT Initialize(const void * const lpBuf = nullptr, const size_t Len = 0); 34 | std::function SelectServerCert; 35 | std::function ClientCertAcceptable; 36 | CListener* GetListener() const; 37 | bool IsConnected{ false }; 38 | 39 | private: 40 | // Note, private constructor 41 | explicit CSSLServer(CPassiveSock *); 42 | HRESULT ShutDownSSL(); 43 | CListener* m_Listener{ nullptr }; 44 | CredHandle hServerCreds{}; 45 | std::unique_ptr m_SocketStream; 46 | int m_LastError{ 0 }; 47 | int TlsVersion{ 0 }; // Two digit TLS version, e.g. 12 for TLS 1.2 48 | static HRESULT InitializeClass(); 49 | SECURITY_STATUS DecryptAndHandleConcatenatedShutdownMessage(SecBufferDescriptor& Message); 50 | int RecvEncrypted(void* const lpBuf, const size_t Len); 51 | bool SSPINegotiateLoop(); 52 | static const int MaxMsgSize = 16000; // Arbitrary but less than 16384 limit, including MaxExtraSize 53 | static const int MaxExtraSize = 50; // Also arbitrary, current SSL header is 5 bytes, trailer 36 54 | CHAR writeBuffer[MaxMsgSize + MaxExtraSize]{}; // Enough for a whole encrypted message 55 | CHAR readBuffer[(MaxMsgSize + MaxExtraSize) * 2]{}; // Enough for two whole messages so we don't need to move data around in buffers 56 | DWORD readBufferBytes{ 0 }; 57 | void* readPtr{}; 58 | SecurityContextHandle m_hContext{}; 59 | SecPkgContext_StreamSizes Sizes{}; 60 | bool m_encrypting = false; // Is channel currently encrypting 61 | }; 62 | -------------------------------------------------------------------------------- /SSLServer/SSLServer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {287B68D2-7381-4E78-AC72-79A49738228D} 23 | Win32Proj 24 | StreamServer 25 | 10.0 26 | SSLServer 27 | 28 | 29 | 30 | StaticLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | StaticLibrary 37 | true 38 | v143 39 | Unicode 40 | false 41 | 42 | 43 | StaticLibrary 44 | false 45 | v143 46 | true 47 | Unicode 48 | 49 | 50 | StaticLibrary 51 | false 52 | v143 53 | true 54 | Unicode 55 | false 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)$(Platform)\$(Configuration)\ 76 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 77 | 78 | 79 | true 80 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 81 | 82 | 83 | false 84 | $(SolutionDir)$(Platform)\$(Configuration)\ 85 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 86 | 87 | 88 | false 89 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 90 | 91 | 92 | 93 | Use 94 | Level4 95 | Disabled 96 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 97 | true 98 | false 99 | MultiThreadedDebugDLL 100 | true 101 | pch.h 102 | .\;Include;..\Common\Include 103 | true 104 | stdcpplatest 105 | $(OutDir)$(TargetName).pdb 106 | 107 | 108 | Console 109 | true 110 | 111 | 112 | 113 | 114 | Use 115 | Level4 116 | Disabled 117 | _DEBUG;_LIB;%(PreprocessorDefinitions) 118 | true 119 | false 120 | MultiThreadedDebugDLL 121 | true 122 | pch.h 123 | .\;Include;..\Common\Include 124 | true 125 | stdcpplatest 126 | $(OutDir)$(TargetName).pdb 127 | 128 | 129 | Console 130 | true 131 | 132 | 133 | 134 | 135 | Level4 136 | Use 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 141 | true 142 | true 143 | pch.h 144 | .\;Include;..\Common\Include 145 | true 146 | stdcpplatest 147 | ProgramDatabase 148 | true 149 | $(OutDir)$(TargetName).pdb 150 | 151 | 152 | Console 153 | true 154 | true 155 | true 156 | 157 | 158 | 159 | 160 | Level4 161 | Use 162 | MaxSpeed 163 | true 164 | true 165 | NDEBUG;_LIB;%(PreprocessorDefinitions) 166 | true 167 | true 168 | pch.h 169 | .\;Include;..\Common\Include 170 | true 171 | stdcpplatest 172 | $(OutDir)$(TargetName).pdb 173 | ProgramDatabase 174 | true 175 | 176 | 177 | Console 178 | true 179 | true 180 | true 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | Create 214 | Create 215 | Create 216 | Create 217 | 218 | 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /SSLServer/SSLServer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {0b3feaae-7fbb-43c0-bdee-4d71defb9281} 14 | 15 | 16 | {43b68f23-ddb4-4ed5-971d-cf14217fa73e} 17 | 18 | 19 | 20 | 21 | Header Files 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files\Common Headers 43 | 44 | 45 | Header Files\Common Headers 46 | 47 | 48 | Header Files\Common Headers 49 | 50 | 51 | Header Files\Common Headers 52 | 53 | 54 | Header Files\Common Headers 55 | 56 | 57 | Header Files\Common Headers 58 | 59 | 60 | Header Files\Common Headers 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files\Common Headers 67 | 68 | 69 | Header Files\Common Headers 70 | 71 | 72 | 73 | 74 | Source Files 75 | 76 | 77 | Source Files 78 | 79 | 80 | Source Files 81 | 82 | 83 | Source Files 84 | 85 | 86 | Source Files 87 | 88 | 89 | Source Files 90 | 91 | 92 | Source Files\Common Sources 93 | 94 | 95 | Source Files\Common Sources 96 | 97 | 98 | Source Files\Common Sources 99 | 100 | 101 | Source Files\Common Sources 102 | 103 | 104 | Source Files\Common Sources 105 | 106 | 107 | -------------------------------------------------------------------------------- /SSLServer/ServerCert.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | 4 | typedef struct _UNICODE_STRING { 5 | USHORT Length; 6 | USHORT MaximumLength; 7 | PWSTR Buffer; 8 | } UNICODE_STRING, * PUNICODE_STRING; 9 | #define SCHANNEL_USE_BLACKLISTS 10 | #include 11 | 12 | #include "ServerCert.h" 13 | #include "CertHelper.h" 14 | #include "SSLServer.h" 15 | #include "Utilities.h" 16 | 17 | #include 18 | #include 19 | 20 | // Create credentials (a handle to a credential context) from a certificate 21 | SECURITY_STATUS CreateCredentialsFromCertificate(PCredHandle phCreds, PCCERT_CONTEXT pCertContext) 22 | { 23 | DebugMsg("In CreateCredentialsFromCertificate, certificate %S", GetCertName(pCertContext).c_str()); 24 | // Build Schannel credential structure. 25 | 26 | TLS_PARAMETERS Tlsp = { 0 }; 27 | // Always allow TLS1.2, only allow TLS1.3+ in Windows 11 or greater. Made more complex by the fact this is a list of protocols NOT to use 28 | Tlsp.grbitDisabledProtocols = SP_PROT_SSL2 | SP_PROT_SSL3 | SP_PROT_TLS1_0 | SP_PROT_TLS1_1; // All protocols prior to TLS 1.2 disabled 29 | if (!IsWindows11OrGreater()) // Microsoft say TLS 1.3 is not supported prior to Windows 11 / Server 2022 so do not use it 30 | Tlsp.grbitDisabledProtocols |= SP_PROT_TLS1_3PLUS; 31 | 32 | SCH_CREDENTIALS Schc = { 0 }; 33 | Schc.dwVersion = SCH_CREDENTIALS_VERSION; 34 | if (pCertContext) 35 | { 36 | Schc.cCreds = 1; 37 | Schc.paCred = &pCertContext; 38 | } 39 | Schc.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS | SCH_USE_STRONG_CRYPTO; 40 | Schc.cTlsParameters = 1; 41 | Schc.pTlsParameters = &Tlsp; 42 | 43 | SECURITY_STATUS Status; 44 | TimeStamp tsExpiry; 45 | // Get a handle to the SSPI credential 46 | Status = CSSLServer::SSPI()->AcquireCredentialsHandle( 47 | nullptr, // Name of principal 48 | const_cast(UNISP_NAME), // Name of package 49 | SECPKG_CRED_INBOUND, // Flags indicating use 50 | nullptr, // Pointer to logon ID 51 | &Schc, // Package specific data 52 | nullptr, // Pointer to GetKey() func 53 | nullptr, // Value to pass to GetKey() 54 | phCreds, // (out) Cred Handle 55 | &tsExpiry); // (out) Lifetime (optional) 56 | 57 | if (Status != SEC_E_OK) 58 | { 59 | DWORD dw = GetLastError(); 60 | if (Status == SEC_E_UNKNOWN_CREDENTIALS) 61 | DebugMsg("**** Error: 'Unknown Credentials' returned by AcquireCredentialsHandle. Be sure app has administrator rights. LastError=%d", dw); 62 | else 63 | DebugMsg("**** Error 0x%x returned by AcquireCredentialsHandle. LastError=%d.", Status, dw); 64 | return Status; 65 | } 66 | 67 | return SEC_E_OK; 68 | } 69 | 70 | // Global items used by the GetCredHandleFor function 71 | std::mutex GetCredHandleForLock; 72 | std::unordered_map credMap = std::unordered_map(); 73 | 74 | SECURITY_STATUS GetCredHandleFor(std::wstring serverName, SelectServerCertType SelectServerCert, PCredHandle phCreds) 75 | { 76 | std::wstring localServerName; 77 | if (serverName.empty()) // There was no hostname supplied 78 | localServerName = GetHostName(); 79 | else 80 | localServerName = serverName; 81 | 82 | std::lock_guard lock(GetCredHandleForLock); // unordered_map is not thread safe, so make this function single thread 83 | 84 | auto got = credMap.find(localServerName); 85 | 86 | if (got == credMap.end()) 87 | { 88 | // There were no credentials stored for that host, create some and add them 89 | PCCERT_CONTEXT pCertContext = nullptr; 90 | SECURITY_STATUS status = NTE_INTERNAL_ERROR; 91 | if (SelectServerCert) 92 | { 93 | status = SelectServerCert(pCertContext, serverName.c_str()); 94 | if (FAILED(status)) 95 | { 96 | DebugMsg("SelectServerCert returned an error = 0x%08x", status); 97 | return NTE_INTERNAL_ERROR; 98 | } 99 | } 100 | else 101 | status = CertFindServerCertificateByName(pCertContext, serverName.c_str()); // Add "true" to look in user store, "false", or nothing looks in machine store 102 | if (SUCCEEDED(status)) 103 | { 104 | CredHandle hServerCred{}; 105 | status = CreateCredentialsFromCertificate(&hServerCred, pCertContext); 106 | if SUCCEEDED(status) 107 | { 108 | credMap.emplace(localServerName, hServerCred); // The server credentials are owned by the map now 109 | *phCreds = hServerCred; 110 | } 111 | return status; 112 | } 113 | else 114 | { 115 | DebugMsg("Failed handling server initialization, error = 0x%08x", status); 116 | return NTE_INTERNAL_ERROR; 117 | } 118 | } 119 | else // We already have credentials for this one 120 | { 121 | *phCreds = (got->second).get(); 122 | return SEC_E_OK; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /SSLServer/ServerCert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "SecurityHandle.h" 3 | 4 | #include 5 | #include 6 | #pragma comment(lib, "secur32.lib") 7 | 8 | using SelectServerCertType = std::function; 9 | SECURITY_STATUS GetCredHandleFor(std::wstring serverName, SelectServerCertType SelectServerCert, PCredHandle phCreds); 10 | -------------------------------------------------------------------------------- /SSLServer/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 5 | 6 | // The following commented code is for debugging memory leaks 7 | //#define _CRTDBG_MAP_ALLOC 8 | //#include 9 | //#include 10 | //#ifdef _DEBUG 11 | //#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) 12 | //// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the 13 | //// allocations to be of _CLIENT_BLOCK type 14 | //#else 15 | //#define DBG_NEW new 16 | //#endif 17 | 18 | // Define a bool to check if this is a DEBUG or RELEASE build 19 | #ifndef DEBUGFLAG_DEFINED 20 | #define DEBUGFLAG_DEFINED 21 | #if defined(_DEBUG) 22 | const bool debug = true; 23 | #else 24 | const bool debug = false; 25 | #endif 26 | #endif // DEBUGFLAG_DEFINED 27 | 28 | #ifndef VC_EXTRALEAN 29 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #pragma comment(lib, "Ws2_32.lib") 38 | #define SECURITY_WIN32 39 | #include 40 | #include 41 | 42 | // Standard C++ 43 | #include 44 | -------------------------------------------------------------------------------- /SSLServer/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /SSLServer/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /SSLServer/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // // Including SDKDDKVer.h defines the highest available Windows platform. 4 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 5 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 6 | #define _WIN32_WINNT 0x0600 // Allow use of features specific to Windows 6 (Vista) or later 7 | #include 8 | -------------------------------------------------------------------------------- /Samples/SimpleClient/SimpleClient.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 3 | #include 4 | #include 5 | #include 6 | 7 | #include "EventWrapper.h" 8 | 9 | // These are included so the headers below will compile 10 | #include // for CTime used compiling sslclient.h 11 | #include // For SOCKET used compiling ActiveSock.h 12 | 13 | #include "sslclient.h" 14 | #include "ActiveSock.h" 15 | 16 | using namespace std; 17 | 18 | // This is a simple example using SSLClient with a minimum of dependencies 19 | // It connects to a web server, exchanges a couple of messages, then exits 20 | // it works as a 32 or 64 bit build, with Unicode ar Multibyte characters 21 | // note that the multibyte build uses a UNICODE (wide character) build of 22 | // SSLClient (in the SSLClient.lib file) 23 | int main() 24 | { 25 | wstring HostName(L"www.google.com"); 26 | int Port = 443; 27 | 28 | CEventWrapper ShutDownEvent; 29 | 30 | auto pActiveSock = make_unique(ShutDownEvent); 31 | pActiveSock->SetRecvTimeoutSeconds(30); 32 | pActiveSock->SetSendTimeoutSeconds(60); 33 | wcout << "StreamClient connecting to " << HostName.c_str() << ":" << Port << endl; 34 | bool b = pActiveSock->Connect(HostName.c_str(), static_cast(Port)); 35 | if (b) 36 | { 37 | // Drive the server side by sending messages it expects 38 | cout << "Socket connected to server, initializing SSL" << endl; 39 | auto pSSLClient = make_unique(pActiveSock.get()); 40 | HRESULT hr = pSSLClient->Initialize(HostName.c_str()); 41 | if (SUCCEEDED(hr)) 42 | { 43 | int tlsVersion = pSSLClient->GetTlsVersion(); 44 | cout << "Connected, cert name matches=" << pSSLClient->getServerCertNameMatches() 45 | << ", TLS version = " << tlsVersion / 10 << "." << tlsVersion % 10 46 | << ", cert is trusted=" << pSSLClient->getServerCertTrusted() << endl; 47 | cout << "Sending greeting" << endl; 48 | string msg("GET HTTP/1.1\n"); // Minimum needed to get a response from a web server 49 | if (pSSLClient->Send(msg.c_str(), msg.length()) != msg.length()) 50 | cout << "Wrong number of characters sent" << endl; 51 | cout << "Listening for message from server" << endl; 52 | int len = 0; 53 | char Msg[100]; 54 | if (0 < (len = pSSLClient->Recv(Msg, sizeof(Msg)))) 55 | { 56 | cout << "Received '" << string(Msg, len).c_str(); 57 | if (len == sizeof(Msg)) // probably truncated 58 | cout << "..."; 59 | cout << "'" << endl << "Shutting down" << endl; 60 | } 61 | else 62 | cout << "Recv reported an error" << endl; 63 | } 64 | else 65 | { 66 | wcout << L"SSL client initialize failed" << endl; 67 | } 68 | pActiveSock->Disconnect(); 69 | } 70 | else 71 | { 72 | cout << "Socket failed to connect to server" << endl; 73 | } 74 | cout << "Press enter when finished" << endl; 75 | if (getchar()) 0; // The fake test is to avoid a compiler warning for ignoring the result 76 | return 0; 77 | } -------------------------------------------------------------------------------- /Samples/SimpleClient/SimpleClient.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /Samples/StreamClient/StreamClient.cpp: -------------------------------------------------------------------------------- 1 | // StreamClient.cpp : This is a sample calling program that uses the SSL client side capabilities. 2 | // 3 | 4 | #include "pch.h" 5 | #include "framework.h" 6 | 7 | #include "Utilities.h" 8 | #include "ActiveSock.h" 9 | #include "SSLClient.h" 10 | #include "EventWrapper.h" 11 | #include "CertHelper.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | // Function to evaluate the certificate returned from the server 20 | // if you want to keep it around call CertDuplicateCertificateContext, then CertFreeCertificateContext to free it 21 | bool CertAcceptable(PCCERT_CONTEXT pCertContext, const bool trusted, const bool matchingName) 22 | { 23 | if (trusted) 24 | cout << "A trusted"; 25 | else 26 | cout << "An untrusted"; 27 | wcout << " server certificate SN=\"" << GetCertSubject(pCertContext).c_str() << "\" was returned with a name "; // wcout for WCHAR* handling 28 | if (matchingName) 29 | cout << "match" << endl; 30 | else 31 | cout << "mismatch" << endl; 32 | if (g_ShowCertInfo && debug && pCertContext) 33 | ShowCertInfo(pCertContext, L"Client Received Server Certificate"); 34 | return true; // Any certificate will do 35 | } 36 | 37 | // This will get called once, or twice, the first call with "Required" false, which can return any 38 | // certificate it likes, or none at all. If it returns one, that will be sent to the server. 39 | // If that call did not return an acceptable certificate, the procedure may be called again if the server requests a 40 | // client certificate, whatever is returned on the first call (including null) is sent to the server which gets to decide 41 | // whether or not it is acceptable. If there is a second call (which will have "Required" true and may have 42 | // pIssuerListInfo non-NULL) it MUST return a certificate or the handshake will fail. 43 | 44 | SECURITY_STATUS SelectClientCertificate(PCCERT_CONTEXT & pCertContext, SecPkgContext_IssuerListInfoEx * pIssuerListInfo, bool Required) 45 | { 46 | SECURITY_STATUS Status = SEC_E_CERT_UNKNOWN; 47 | 48 | if (Required) 49 | { 50 | // A client certificate must be returned or the handshake will fail 51 | if (pIssuerListInfo) 52 | { 53 | if (pIssuerListInfo->cIssuers == 0) 54 | cout << "Client certificate required, issuer list is empty"; 55 | else 56 | { 57 | cout << "Client certificate required, issuer list provided"; 58 | Status = CertFindFromIssuerList(pCertContext, *pIssuerListInfo); 59 | if (!pCertContext) 60 | cout << " but no certificates matched"; 61 | } 62 | } 63 | if (!pCertContext) 64 | Status = CertFindClientCertificate(pCertContext); // Select any valid certificate, regardless of issuer 65 | // If a search for a required client certificate failed, just make one 66 | if (!pCertContext) 67 | { 68 | cout << ", none found, creating one"; 69 | pCertContext = CreateCertificate(false, (GetCurrentUserName() + L" at " + GetHostName()).c_str(), L"StreamSSL client", nullptr, true); 70 | if (pCertContext) 71 | Status = S_OK; 72 | else 73 | { 74 | DWORD LastError = GetLastError(); 75 | wcout << endl << L"**** Error 0x" << std::hex << std::setw(8) << std::setfill(L'0') << LastError 76 | << L"(" << WinErrorMsg(LastError) << L") in CreateCertificate" << endl 77 | << "Client certificate"; 78 | Status = HRESULT_FROM_WIN32(LastError); 79 | } 80 | } 81 | } 82 | else 83 | { 84 | cout << "Optional client certificate requested (without issuer list)"; 85 | // Enable the next line to preemptively guess at an appropriate certificate 86 | // if (FAILED(Status)) 87 | // Status = CertFindClientCertificate(pCertContext); // Select any valid certificate 88 | } 89 | if (pCertContext) 90 | wcout << ", selected cert: " << GetCertName(pCertContext).c_str() << endl; // wcout for WCHAR* handling 91 | else 92 | cout << ", no certificate found." << endl; 93 | if (g_ShowCertInfo && debug && pCertContext) 94 | ShowCertInfo(pCertContext, L"Client certificate being returned"); 95 | return Status; 96 | } 97 | 98 | BOOL FlushConsoleInputBufferAlternate(HANDLE h) 99 | { // Needed because FlushConsoleInputBuffer did not work in Windows 10 as of October 2017 100 | // but by July 2018 it was working again, so, for example version 10.0.17134 works. 101 | INPUT_RECORD inRec; 102 | DWORD recsRead; 103 | BOOL rslt = TRUE; 104 | while (rslt && ((rslt = GetNumberOfConsoleInputEvents(h, &recsRead)) == TRUE) && (recsRead > 0)) 105 | rslt = ReadConsoleInput(h, &inRec, 1, &recsRead); 106 | return rslt; 107 | } 108 | 109 | WORD WaitForAnyKey(DWORD TimeOutMilliSeconds = 5000) 110 | { 111 | //printf("Press a key within %i ms\n", TimeOutMilliSeconds); 112 | HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); 113 | ULONGLONG endTime = GetTickCount64() + TimeOutMilliSeconds; 114 | // flush to remove existing events 115 | if (!FlushConsoleInputBufferAlternate(hStdin)) 116 | cout << "FlushConsoleInputBuffer failed LastError=" << GetLastError() << endl; 117 | // Now wait for input or timeout 118 | while (WaitForSingleObject(hStdin, (DWORD)max(0, endTime - GetTickCount64())) == WAIT_OBJECT_0) 119 | { 120 | INPUT_RECORD inRec; 121 | DWORD recsRead = 0; 122 | while (GetNumberOfConsoleInputEvents(hStdin, &recsRead) && (recsRead > 0)) 123 | { 124 | ReadConsoleInput(hStdin, &inRec, 1, &recsRead); 125 | if (inRec.EventType == KEY_EVENT && inRec.Event.KeyEvent.bKeyDown == 0) 126 | return inRec.Event.KeyEvent.wVirtualKeyCode; // a key was released, return its identity 127 | } 128 | } 129 | //printf("Done waiting for key release, continuing\n"); 130 | //Sleep(2000); // Enough time to read the message 131 | return 0; 132 | } 133 | 134 | int wmain(int argc, WCHAR * argv[]) 135 | { 136 | //_CrtSetBreakAlloc(225); // Catch a memory leak 137 | std::wstring HostName(GetHostName(ComputerNameDnsFullyQualified)); 138 | if (argc >= 2) 139 | HostName = std::wstring(argv[1]); 140 | int Port = 41000; 141 | 142 | CEventWrapper ShutDownEvent; 143 | 144 | auto pActiveSock = make_unique(ShutDownEvent); 145 | pActiveSock->SetRecvTimeoutSeconds(30); 146 | pActiveSock->SetSendTimeoutSeconds(60); 147 | wcout << "StreamClient version " << GetVersionText() << " connecting to " << HostName.c_str() << ":" << Port << endl; 148 | bool b = pActiveSock->Connect(HostName.c_str(), static_cast(Port)); 149 | if (b) 150 | { 151 | // Drive the server side by sending messages it expects 152 | cout << "Socket connected to server, initializing SSL" << endl; 153 | auto pSSLClient = make_unique(pActiveSock.get()); 154 | pSSLClient->ServerCertAcceptable = CertAcceptable; 155 | pSSLClient->SelectClientCertificate = SelectClientCertificate; 156 | HRESULT hr = pSSLClient->Initialize(HostName.c_str()); 157 | if (SUCCEEDED(hr)) 158 | { 159 | int tlsVersion = pSSLClient->GetTlsVersion(); 160 | cout << "Connected, cert name matches = " << pSSLClient->getServerCertNameMatches() 161 | << ", TLS version = " << tlsVersion / 10 << "." << tlsVersion % 10 162 | << ", cert is trusted = " << pSSLClient->getServerCertTrusted() << endl; 163 | std::string sentMsg("Hello from client"); 164 | cout << "Sending greeting '" << sentMsg << "'" << endl; 165 | if (pSSLClient->Send(sentMsg.c_str(), sentMsg.length()) != (int)sentMsg.length()) 166 | cout << "Wrong number of characters sent" << endl; 167 | sentMsg ="Hello again from client"; 168 | cout << "Sending second greeting '" << sentMsg << "'" << endl; 169 | if (pSSLClient->Send(sentMsg.c_str(), sentMsg.length()) != (int)sentMsg.length()) 170 | cout << "Wrong number of characters sent" << endl; 171 | cout << "Listening for message from server" << endl; 172 | int len = 0; 173 | char Msg[100]; 174 | if (0 < (len = pSSLClient->Recv(Msg, sizeof(Msg)))) 175 | { 176 | cout << "Received '" << string(Msg, len) << "'" << endl; 177 | // Receive a second message, ignore errors 178 | if (0 < (len = pSSLClient->Recv(Msg, sizeof(Msg)))) 179 | cout << "Received '" << string(Msg, len) << "'" << endl; 180 | cout << "Shutting down SSL" << endl; 181 | ::Sleep(1000); // Give the next message a chance to arrive at the server separately 182 | pSSLClient->Disconnect(false); 183 | // The TCP connection still exists and can be used to send messages, though 184 | // this is rarely done, here's an example of doing it 185 | sentMsg = "First block of unencrypted data from client"; 186 | cout << "Sending first unencrypted data message '" << sentMsg << "'" << endl; 187 | if (pActiveSock->Send(sentMsg.c_str(), sentMsg.length()) != (int)sentMsg.length()) 188 | cout << "Wrong number of characters sent" << endl; 189 | else 190 | { 191 | cout << "Sleeping before sending second unencrypted data message" << endl; 192 | ::Sleep(1000); // Give the previous message time to arrive at the server, for the server socket to receive it and hand to the caller 193 | ::Sleep(4000); // Allow the next server-side receive to time out 194 | sentMsg = "Second block of unencrypted data from client"; 195 | cout << "Sending second unencrypted data message '" << sentMsg << "'" << endl; 196 | if (pActiveSock->Send(sentMsg.c_str(), sentMsg.length()) != (int)sentMsg.length()) 197 | cout << "Wrong number of characters sent" << endl; 198 | else 199 | { 200 | cout << "Sleeping before sending termination to give the last message time to arrive" << endl; 201 | ::Sleep(3000); // Give the previous message time to arrive at the server and for the server socket to receive it and hand to the caller 202 | } 203 | } 204 | } 205 | else 206 | cout << "Recv reported an error" << endl; 207 | } 208 | else 209 | { 210 | wcout << L"SSL client initialize failed: " << WinErrorMsg(hr) << endl; 211 | } 212 | ::SetEvent(ShutDownEvent); // Used to early exit any async send or receive that are in process 213 | pActiveSock->Disconnect(); 214 | } 215 | else 216 | { 217 | cout << "Socket failed to connect to server" << endl; 218 | } 219 | cout << "Press any key to pause, Q to exit immediately" << endl; 220 | WORD key = WaitForAnyKey(30000); 221 | if (!(key == 'Q' || key == 0)) 222 | { 223 | cout << "The program will pause until you press enter" << endl; 224 | key = (WORD)getchar(); // Assign result to avoid warning 225 | } 226 | return 0; 227 | } 228 | -------------------------------------------------------------------------------- /Samples/StreamClient/StreamClient.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-maw/StreamSSL/a5c310381b4d78c382fd9b2a1a4d7faf3b9c6ecf/Samples/StreamClient/StreamClient.rc -------------------------------------------------------------------------------- /Samples/StreamClient/StreamClient.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {4a64686f-1901-49f4-a7b6-e10b372debd5} 18 | 19 | 20 | {c9750aa6-47e2-4238-af94-71cca0a7e29a} 21 | 22 | 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files\SSLCommon Headers 35 | 36 | 37 | Header Files\SSLCommon Headers 38 | 39 | 40 | Header Files\SSLClientLib Headers 41 | 42 | 43 | Header Files\SSLClientLib Headers 44 | 45 | 46 | Header Files\SSLClientLib Headers 47 | 48 | 49 | Header Files\SSLCommon Headers 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files\SSLCommon Headers 56 | 57 | 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | 67 | 68 | Resource Files 69 | 70 | 71 | -------------------------------------------------------------------------------- /Samples/StreamClient/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 5 | 6 | // The following commented code is for debugging memory leaks 7 | //#define _CRTDBG_MAP_ALLOC 8 | //#include 9 | //#include 10 | //#ifdef _DEBUG 11 | //#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) 12 | //// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the 13 | //// allocations to be of _CLIENT_BLOCK type 14 | //#else 15 | //#define DBG_NEW new 16 | //#endif 17 | 18 | // Define a bool to check if this is a DEBUG or RELEASE build 19 | #ifndef DEBUGFLAG_DEFINED 20 | #define DEBUGFLAG_DEFINED 21 | #if defined(_DEBUG) 22 | const bool debug = true; 23 | #else 24 | const bool debug = false; 25 | #endif 26 | #endif // DEBUGFLAG_DEFINED 27 | 28 | #ifndef VC_EXTRALEAN 29 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #define SECURITY_WIN32 38 | #include 39 | #include 40 | 41 | // Standard C++ 42 | #include 43 | -------------------------------------------------------------------------------- /Samples/StreamClient/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to pre-compiled header; necessary for compilation to succeed 2 | 3 | #include "pch.h" 4 | 5 | // In general, ignore this file, but keep it around if you are using pre-compiled headers. 6 | -------------------------------------------------------------------------------- /Samples/StreamClient/pch.h: -------------------------------------------------------------------------------- 1 | // Tips for Getting Started: 2 | // 1. Use the Solution Explorer window to add/manage files 3 | // 2. Use the Team Explorer window to connect to source control 4 | // 3. Use the Output window to see build output and other messages 5 | // 4. Use the Error List window to view errors 6 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 7 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 8 | 9 | #ifndef PCH_H 10 | #define PCH_H 11 | 12 | // TODO: add headers that you want to pre-compile here 13 | 14 | #endif //PCH_H 15 | -------------------------------------------------------------------------------- /Samples/StreamClient/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by StreamClient.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /Samples/StreamClient/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // // Including SDKDDKVer.h defines the highest available Windows platform. 4 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 5 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 6 | #define _WIN32_WINNT 0x0600 // Allow use of features specific to Windows 6 (Vista) or later 7 | #include 8 | -------------------------------------------------------------------------------- /Samples/StreamServer/StreamServer.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "framework.h" 3 | 4 | #include "CertHelper.h" 5 | #include "Listener.h" 6 | #include "ISocketStream.h" 7 | #include "Utilities.h" 8 | 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | // This method is called when the first client tries to connect in order to allow a certificate to be selected to send to the client 15 | // It has to wait for the client connect request because the client tells the server what identity it expects it to present 16 | // This is called SNI (Server Name Indication) and it is a relatively new (it began to become available about 2005) SSL/TLS feature 17 | SECURITY_STATUS SelectServerCert(PCCERT_CONTEXT & pCertContext, LPCWSTR pszSubjectName) 18 | { 19 | SECURITY_STATUS status = SEC_E_INVALID_HANDLE; 20 | 21 | wcout << "Server certificate requested for " << pszSubjectName << endl; 22 | // The next line invokes a UI to let the user select a certificate manually 23 | //status = CertFindServerCertificateUI(pCertContext, pszSubjectName, false); 24 | if (!pCertContext) // If we don't already have a certificate, try and select a specific one 25 | status = CertFindCertificateBySignature(pCertContext, 26 | "a9 f4 6e bf 4e 1d 6d 67 2d 2b 39 14 ee ee 58 97 d1 d7 e9 d0", true); // "true" looks in user store, "false", or nothing looks in machine store 27 | if (!pCertContext) // If we don't already have a certificate, try and select a likely looking one 28 | status = CertFindServerCertificateByName(pCertContext, pszSubjectName); // Add "true" to look in user store, "false", or nothing looks in machine store 29 | if (pCertContext) 30 | wcout << "Server certificate found: " << GetCertName(pCertContext).c_str() << endl; 31 | else 32 | wcout << "Server certificate could not be found or created" << endl; 33 | // Uncomment the next 2 lines if you want to see details of the selected certificate 34 | //if (pCertContext) 35 | // ShowCertInfo(pCertContext, "Server Certificate In Use"); 36 | return status; 37 | } 38 | 39 | 40 | // This method is called when a client connection is offered, it returns an indication of whether the certificate (or lack of one) is acceptable 41 | bool ClientCertAcceptable(PCCERT_CONTEXT pCertContext, const bool trusted) 42 | { 43 | if (trusted) 44 | cout << "A trusted"; 45 | else 46 | cout << "An untrusted"; 47 | wcout << " client certificate was returned:" << GetCertName(pCertContext).c_str() << endl; 48 | return nullptr != pCertContext; // Meaning any certificate is fine, trusted or not, but there must be one 49 | } 50 | 51 | // This function simply runs arbitrary code and returns process information to the caller, it's just a handy utility function 52 | bool RunApp(std::wstring app, PROCESS_INFORMATION& pi) 53 | { // Not strictly needed but it makes testing easier 54 | STARTUPINFOW si = {}; 55 | si.cb = sizeof si; 56 | ZeroMemory(&pi, sizeof(pi)); 57 | #pragma warning(suppress:6335) 58 | if (CreateProcessW(nullptr, &app[0], nullptr, FALSE, 0, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi)) 59 | return true; 60 | else 61 | { 62 | cerr << "CreateProcess failed (" << GetLastError() << ").\n"; 63 | return false; 64 | } 65 | } 66 | // Run the sample client application just to simplify testing (that way you needn't run both server and client separately) 67 | void RunClient(std::wstring toHost = L"", PROCESS_INFORMATION * ppi = nullptr) 68 | { 69 | cout << "Initiating a client instance for testing.\n" << endl; 70 | WCHAR acPathName[MAX_PATH + 1]; 71 | GetModuleFileNameW(nullptr, acPathName, _countof(acPathName)); 72 | std::wstring appName(acPathName); 73 | const auto len = appName.find_last_of(L'\\'); 74 | appName = appName.substr(0, len + 1) + L"StreamClient.exe " + toHost; 75 | PROCESS_INFORMATION pi = {}, *localPpi = ppi ? ppi : π // Just use a local one if one is not passed 76 | 77 | if (RunApp(appName, *localPpi) && !ppi && pi.hProcess && pi.hThread) 78 | { 79 | cout << "Waiting on StreamClient" << endl; 80 | WaitForSingleObject(pi.hProcess, INFINITE); 81 | wcout << "Client completed." << endl; 82 | CloseHandle(pi.hProcess); 83 | CloseHandle(pi.hThread); 84 | } 85 | } 86 | 87 | // If the elapsed time since the time specified by the parameter is a second or more, display it, otherwise do nothing 88 | void ShowDelay() 89 | { 90 | static DWORD DelayStarted = 0; 91 | 92 | DWORD CurTime = GetTickCount(); 93 | if (DelayStarted != 0) 94 | { 95 | DWORD Waited = CurTime - DelayStarted; 96 | if (Waited > 1000) 97 | cout << "Waited " << Waited/1000 << " seconds" << endl; 98 | } 99 | DelayStarted = CurTime; // Restart the timer 100 | } 101 | 102 | void ShowResult(int len, DWORD LastError) 103 | { 104 | ShowDelay(); 105 | if (len == 0) 106 | cout << "socket shutting down" << endl; 107 | else 108 | wcout << L"socket operation returned an error, LastError: " << WinErrorMsg(LastError) << endl; 109 | } 110 | 111 | // The function called first by the operating system when the codefile is run 112 | int _tmain(int argc, TCHAR* argv[]) 113 | { 114 | UNREFERENCED_PARAMETER(argc); 115 | UNREFERENCED_PARAMETER(argv); 116 | if (!IsUserAdmin()) 117 | cout << "WARNING: The server is not running as an administrator." << endl; 118 | const int Port = 41000; 119 | auto Listener = std::make_unique(); 120 | Listener->SelectServerCert = SelectServerCert; 121 | Listener->ClientCertAcceptable = ClientCertAcceptable; 122 | Listener->Initialize(Port); 123 | cout << "StreamServer version " << GetVersionText() << " starting to listen on port " << Port << ", will find certificate for first connection." << endl; 124 | Listener->BeginListening([](ISocketStream * const StreamSock) { 125 | // This is the code to be executed each time a socket is opened, basically 126 | // the client drives, this server code listens and responds 127 | std::wstring s; 128 | char MsgText[100]; // Because the simple text messages we exchange are char not wchar 129 | int len = 0; 130 | 131 | ShowDelay(); 132 | int tlsVersion = StreamSock->GetTlsVersion(); 133 | cout << "A connection has been made using TLS " << tlsVersion / 10 << "." << tlsVersion % 10 134 | << ", worker started, awaiting a message from the client" << endl; 135 | 136 | try 137 | { 138 | len = StreamSock->Recv(MsgText, sizeof(MsgText) - 1); 139 | ShowDelay(); 140 | if (len > 0) 141 | { 142 | MsgText[len] = '\0'; // Terminate the string, for convenience 143 | std::string expectedMsg("Hello from client"); 144 | std::string actualMsg(MsgText); 145 | const auto strEnd = actualMsg.find_last_not_of("\n"); 146 | actualMsg.erase(strEnd+1); 147 | if (expectedMsg == actualMsg) 148 | cout << "Received '" << MsgText << "' as expected" << endl; 149 | else 150 | { 151 | std::string errorReport("Received unexpected message '"); 152 | errorReport += actualMsg + "'"; 153 | StreamSock->Send(errorReport.c_str(), errorReport.size()); 154 | throw std::exception(errorReport.c_str()); 155 | } 156 | } 157 | else 158 | { 159 | cout << "Message receive error" << endl; 160 | } 161 | 162 | string sentMsg("Hello from server"); 163 | cout << "Sending '" << sentMsg << "'" << endl; 164 | if ((len = StreamSock->Send(sentMsg.c_str(), sentMsg.length())) != (int)sentMsg.length()) 165 | cout << "Wrong number of characters sent" << endl; 166 | if (len < 0) 167 | { 168 | if (StreamSock->GetLastError() == ERROR_FILE_NOT_ENCRYPTED) 169 | cout << "Send cannot be used unless encrypting" << endl; 170 | else 171 | cout << "Send returned an error" << endl; 172 | } 173 | len = StreamSock->Recv(MsgText, sizeof(MsgText) - 1); 174 | if (len > 0) 175 | { 176 | ShowDelay(); 177 | MsgText[len] = '\0'; // Terminate the string, for convenience 178 | cout << "Received '" << MsgText << "'" << endl; 179 | // At this point the client is just waiting for a message or for the connection to close 180 | cout << "Sending 'Goodbye from server' and listening for client messages" << endl; 181 | StreamSock->Send("Goodbye from server", 19); 182 | ::Sleep(1000); // Give incoming messages chance to pile up 183 | // Now loop receiving and decrypting messages until an error (probably SSL shutdown) is received 184 | while ((len = StreamSock->Recv(MsgText, sizeof(MsgText) - 1)) > 0) 185 | { 186 | MsgText[len] = '\0'; // Terminate the string, for convenience 187 | ShowDelay(); 188 | cout << "Received '" << MsgText << "'" << endl; 189 | } 190 | if (StreamSock->GetLastError() == SEC_I_CONTEXT_EXPIRED) 191 | { 192 | cout << "Recv returned notification that SSL shut down" << endl; 193 | // Now loop receiving any unencrypted messages until an error (probably socket shutdown) is received 194 | StreamSock->SetRecvTimeoutSeconds(4); 195 | while (true) 196 | { 197 | if ((len = StreamSock->Recv(MsgText, sizeof(MsgText) - 1)) <= 0) 198 | { 199 | if (len == SOCKET_ERROR && StreamSock->GetLastError() == ERROR_TIMEOUT) 200 | { 201 | // Just a timeout, it's ok to retry that, so just do so 202 | ShowDelay(); 203 | cout << "Initial receive timed out, retrying" << endl; 204 | if ((len = StreamSock->Recv(MsgText, sizeof(MsgText) - 1)) <= 0) 205 | break; 206 | } 207 | else 208 | break; 209 | } 210 | MsgText[len] = '\0'; // Terminate the string, for convenience 211 | ShowDelay(); 212 | cout << "Received plaintext '" << MsgText << "'" << endl; 213 | } 214 | ShowDelay(); 215 | ShowResult(len, StreamSock->GetLastError()); 216 | } 217 | else 218 | ShowResult(len, StreamSock->GetLastError()); 219 | } 220 | else 221 | cout << "No response data received" << endl; 222 | } 223 | catch (const std::exception & ex) 224 | { 225 | cout << ex.what() << endl; 226 | } 227 | cout << "Exiting worker" << endl << endl; 228 | cout << "Listening for client connections, press enter key to terminate." << endl << endl; 229 | }); 230 | 231 | cout << "Listening for client connections." << endl << endl; 232 | 233 | PROCESS_INFORMATION pi = {}; 234 | 235 | RunClient(L"localhost", &pi); // run a client point it at "localhost" 236 | if (pi.hProcess && pi.hThread) 237 | { 238 | cout << "Waiting on StreamClient to localhost" << endl; 239 | WaitForSingleObject(pi.hProcess, INFINITE); 240 | cout << "StreamClient to localhost completed." << endl << endl; 241 | CloseHandle(pi.hProcess); 242 | CloseHandle(pi.hThread); 243 | cout << "Still listening for additional client connections, press enter key to terminate." << endl << endl; 244 | } 245 | // Run additional copies, do not wait, and let the hostname default 246 | PROCESS_INFORMATION pi1 = {}; 247 | RunClient(L"", &pi1); 248 | //PROCESS_INFORMATION pi2 = {}; 249 | //RunClient(L"", &pi2); 250 | //PROCESS_INFORMATION pi3 = {}; 251 | //RunClient(L"", &pi3); 252 | 253 | cout << "Additional test clients initiated, press enter key to terminate server." << endl << endl; 254 | #pragma warning(suppress: 6031) // Do not care about unchecked result 255 | getchar(); 256 | Listener->EndListening(); 257 | return 0; 258 | } 259 | -------------------------------------------------------------------------------- /Samples/StreamServer/StreamServer.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-maw/StreamSSL/a5c310381b4d78c382fd9b2a1a4d7faf3b9c6ecf/Samples/StreamServer/StreamServer.rc -------------------------------------------------------------------------------- /Samples/StreamServer/StreamServer.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {04ff3f49-3992-4da1-a63e-71bd5f15ebb3} 18 | 19 | 20 | {dc9f01d7-975b-469b-9007-110cf90e2185} 21 | 22 | 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files\SSLServerLib Headers 32 | 33 | 34 | Header Files\SSLCommon Headers 35 | 36 | 37 | Header Files\SSLCommon Headers 38 | 39 | 40 | Header Files\SSLCommon Headers 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files\SSLCommon Headers 47 | 48 | 49 | Header Files 50 | 51 | 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | 61 | 62 | Resource Files 63 | 64 | 65 | -------------------------------------------------------------------------------- /Samples/StreamServer/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 5 | 6 | // The following commented code is for debugging memory leaks 7 | //#define _CRTDBG_MAP_ALLOC 8 | //#include 9 | //#include 10 | //#ifdef _DEBUG 11 | //#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) 12 | //// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the 13 | //// allocations to be of _CLIENT_BLOCK type 14 | //#else 15 | //#define DBG_NEW new 16 | //#endif 17 | 18 | // Define a bool to check if this is a DEBUG or RELEASE build 19 | #ifndef DEBUGFLAG_DEFINED 20 | #define DEBUGFLAG_DEFINED 21 | #if defined(_DEBUG) 22 | const bool debug = true; 23 | #else 24 | const bool debug = false; 25 | #endif 26 | #endif // DEBUGFLAG_DEFINED 27 | 28 | #ifndef VC_EXTRALEAN 29 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #define SECURITY_WIN32 38 | #include 39 | #include 40 | 41 | // Standard C++ 42 | #include 43 | -------------------------------------------------------------------------------- /Samples/StreamServer/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to pre-compiled header; necessary for compilation to succeed 2 | 3 | #include "pch.h" 4 | 5 | // In general, ignore this file, but keep it around if you are using pre-compiled headers. 6 | -------------------------------------------------------------------------------- /Samples/StreamServer/pch.h: -------------------------------------------------------------------------------- 1 | // Tips for Getting Started: 2 | // 1. Use the Solution Explorer window to add/manage files 3 | // 2. Use the Team Explorer window to connect to source control 4 | // 3. Use the Output window to see build output and other messages 5 | // 4. Use the Error List window to view errors 6 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 7 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 8 | 9 | #ifndef PCH_H 10 | #define PCH_H 11 | 12 | // TODO: add headers that you want to pre-compile here 13 | 14 | #endif //PCH_H 15 | -------------------------------------------------------------------------------- /Samples/StreamServer/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by StreamServer.rc 4 | // 5 | 6 | // Next default values for new objects 7 | // 8 | #ifdef APSTUDIO_INVOKED 9 | #ifndef APSTUDIO_READONLY_SYMBOLS 10 | #define _APS_NEXT_RESOURCE_VALUE 102 11 | #define _APS_NEXT_COMMAND_VALUE 40001 12 | #define _APS_NEXT_CONTROL_VALUE 1001 13 | #define _APS_NEXT_SYMED_VALUE 101 14 | #endif 15 | #endif 16 | -------------------------------------------------------------------------------- /Samples/StreamServer/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // // Including SDKDDKVer.h defines the highest available Windows platform. 4 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 5 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 6 | #define _WIN32_WINNT 0x0600 // Allow use of features specific to Windows 6 (Vista) or later 7 | #include 8 | -------------------------------------------------------------------------------- /SimpleClientCS/SimpleClientCs.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Security; 2 | using System.Net.Sockets; 3 | using System.Text; 4 | 5 | // This is a simple example using .NET SslStream 6 | // It connects to a web server, exchanges a couple of messages, then exits 7 | // This corresponds to the SimpleClient C++ example. 8 | const string serverName = "www.google.com"; 9 | // Create a TCP/IP client socket. 10 | TcpClient tcpClient = new TcpClient(serverName, 443); 11 | Console.WriteLine("Socket connected to server, initializing SSL."); 12 | // Create an SSL stream that will close the client's stream. 13 | SslStream sslStream = new SslStream(tcpClient.GetStream()); 14 | sslStream.AuthenticateAsClient(serverName); 15 | Console.WriteLine("Connected to {0}, protocol: {1}", serverName, sslStream.SslProtocol); 16 | // Encode a test message into a byte array. 17 | byte[] message = Encoding.UTF8.GetBytes("GET HTTP/1.1\n"); 18 | // Send hello message to the server. 19 | sslStream.Write(message); 20 | sslStream.Flush(); 21 | // Read message from the server. 22 | byte[] buffer = new byte[100]; 23 | int bytes = sslStream.Read(buffer, 0, buffer.Length); 24 | string serverMessage = Encoding.UTF8.GetString(buffer, 0, bytes); 25 | int newlineIndex = serverMessage.IndexOf('\n'); 26 | if (newlineIndex != -1) 27 | serverMessage = serverMessage.Remove(newlineIndex); 28 | Console.WriteLine("Server says: {0}", serverMessage); 29 | // Close the client connection. 30 | tcpClient.Close(); 31 | Console.WriteLine("Client closed. Press Enter to finish"); 32 | Console.ReadKey(); // wait 33 | return 0; -------------------------------------------------------------------------------- /SimpleClientCS/SimpleClientCs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | disable 8 | 2.1.7 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /StreamClient.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.10.34607.79 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StreamClient", "Samples\StreamClient\StreamClient.vcxproj", "{144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}" 6 | EndProject 7 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StreamSSL", "StreamSSL\StreamSSL.vcxproj", "{8526BED2-CB4E-4058-AFEB-357367F3C773}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Win32 = Debug|Win32 12 | Debug|x64 = Debug|x64 13 | Release|Win32 = Release|Win32 14 | Release|x64 = Release|x64 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|Win32.ActiveCfg = Debug|Win32 18 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|Win32.Build.0 = Debug|Win32 19 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|x64.ActiveCfg = Debug|x64 20 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|x64.Build.0 = Debug|x64 21 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|Win32.ActiveCfg = Release|Win32 22 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|Win32.Build.0 = Release|Win32 23 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|x64.ActiveCfg = Release|x64 24 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|x64.Build.0 = Release|x64 25 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|Win32.ActiveCfg = Debug|Win32 26 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|Win32.Build.0 = Debug|Win32 27 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|x64.ActiveCfg = Debug|x64 28 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|x64.Build.0 = Debug|x64 29 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|Win32.ActiveCfg = Release|Win32 30 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|Win32.Build.0 = Release|Win32 31 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|x64.ActiveCfg = Release|x64 32 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|x64.Build.0 = Release|x64 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | GlobalSection(ExtensibilityGlobals) = postSolution 38 | SolutionGuid = {E2735530-C231-462D-AFF1-BD154690364B} 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /StreamClientCs/StreamClientCs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Net.Security; 3 | using System.Net.Sockets; 4 | using System.Security.Authentication; 5 | using System.Text; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Diagnostics; 8 | 9 | namespace StreamClientCs; 10 | 11 | public class SslTcpClient 12 | { 13 | private static Hashtable certificateErrors = new(); 14 | 15 | // The following method is invoked by the RemoteCertificateValidationDelegate. 16 | public static bool ValidateServerCertificate( 17 | object sender, 18 | X509Certificate certificate, 19 | X509Chain chain, 20 | SslPolicyErrors sslPolicyErrors) 21 | { 22 | if (sslPolicyErrors == SslPolicyErrors.None) 23 | { 24 | Console.WriteLine("Server certificate returned with no errors, {0}", certificate.Subject); 25 | return true; 26 | } 27 | else 28 | { 29 | Console.WriteLine("Certificate error: {0}", sslPolicyErrors); 30 | // Do not allow this client to communicate with unauthenticated servers. 31 | return false; 32 | } 33 | } 34 | public static X509Certificate SelectLocalCertificate( 35 | object sender, 36 | string targetHost, 37 | X509CertificateCollection localCertificates, 38 | X509Certificate remoteCertificate, 39 | string[] acceptableIssuers) 40 | { 41 | // Helper function to check for client authentication EKU 42 | static bool HasClientAuthEku(X509Certificate2 cert2) 43 | { 44 | foreach (var ext in cert2.Extensions.OfType()) 45 | { 46 | foreach (var oid in ext.EnhancedKeyUsages) 47 | { 48 | if (oid.Value == "1.3.6.1.5.5.7.3.2") // Client Authentication OID 49 | return true; 50 | } 51 | } 52 | return false; 53 | } 54 | X509Certificate cert = null; 55 | Console.WriteLine("SelectLocalCertificate called to select a local certificate, remoteCertificate {0}", (remoteCertificate == null) ? "" : remoteCertificate.Subject); 56 | if (acceptableIssuers?.Length > 0 && localCertificates?.Count > 0) 57 | { 58 | // Use the first certificate that is from an acceptable issuer. 59 | foreach (X509Certificate certificate in localCertificates) 60 | { 61 | string issuer = certificate.Issuer; 62 | if (Array.IndexOf(acceptableIssuers, issuer) != -1) 63 | { 64 | cert = certificate; 65 | break; 66 | } 67 | } 68 | } 69 | else if (localCertificates?.Count > 0) 70 | cert = localCertificates[0]; 71 | 72 | if (remoteCertificate != null) // second call for a local cert 73 | { 74 | Console.WriteLine("SelectLocalCertificate could not locate a certificate easily, so try and pick any certificate good for client authentication"); 75 | using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) 76 | { 77 | store.Open(OpenFlags.ReadOnly); 78 | foreach (var cert2 in store.Certificates.OfType()) 79 | { 80 | if (cert2.HasPrivateKey && 81 | //DateTime.Now >= cert2.NotBefore && 82 | //DateTime.Now <= cert2.NotAfter && 83 | HasClientAuthEku(cert2)) 84 | { 85 | cert = cert2; 86 | break; 87 | } 88 | } 89 | } 90 | } 91 | Console.WriteLine("SelectLocalCertificate is returning cert {0}", (cert == null) ? "" : cert.Subject); 92 | return cert; 93 | } 94 | public static int RunClient(string serverName, string serverCertificateName) 95 | { 96 | // Create a TCP/IP client socket. 97 | // machineName is the host running the server application. 98 | TcpClient tcpClient = new(serverName, 41000); 99 | NetworkStream tcpStream = tcpClient.GetStream(); 100 | Console.WriteLine("Client connected."); 101 | // Create an SSL stream that will close the client's stream. 102 | SslStream sslStream = new( 103 | tcpStream, 104 | false, 105 | new RemoteCertificateValidationCallback(ValidateServerCertificate), 106 | new LocalCertificateSelectionCallback(SelectLocalCertificate) 107 | ); 108 | // The server name must match the name on the server certificate. 109 | try 110 | { 111 | sslStream.AuthenticateAsClient(serverCertificateName); 112 | } 113 | catch (AuthenticationException e) 114 | { 115 | Console.WriteLine("Exception: {0}", e.Message); 116 | if (e.InnerException != null) 117 | { 118 | Console.WriteLine("Inner exception: {0}", e.InnerException.Message); 119 | } 120 | Console.WriteLine("Authentication failed - closing the connection."); 121 | tcpClient.Close(); 122 | return 1; 123 | } 124 | Console.WriteLine($"Connected to {serverName}, protocol: {sslStream.SslProtocol}"); 125 | 126 | string sentMsg = "Hello from client"; 127 | Console.WriteLine("Sending greeting '{0}'", sentMsg); 128 | sslStream.Write(Encoding.ASCII.GetBytes(sentMsg)); 129 | sslStream.Flush(); 130 | 131 | sentMsg = "Hello again from client"; 132 | Console.WriteLine("Sending second greeting '{0}'", sentMsg); 133 | sslStream.Write(Encoding.ASCII.GetBytes(sentMsg)); 134 | sslStream.Flush(); 135 | 136 | Console.WriteLine("Listening for message from server"); 137 | byte[] readBuffer = new byte[200]; 138 | int readBytes = sslStream.Read(readBuffer); 139 | string serverMessage = Encoding.ASCII.GetString(readBuffer, 0, readBytes); 140 | Console.WriteLine("Received'{0}'", serverMessage); 141 | 142 | if (serverMessage.Length > 30) // A long message probably means the two responses were concatenated 143 | Console.WriteLine("Looks like the two server responses were concatenated"); 144 | else 145 | { 146 | Console.WriteLine("Listening for message from server"); 147 | readBytes = sslStream.Read(readBuffer); 148 | serverMessage = Encoding.ASCII.GetString(readBuffer, 0, readBytes); 149 | Console.WriteLine("Received'{0}'", serverMessage); 150 | } 151 | 152 | // Shut down SSL without closing the client TCP connection. 153 | Console.WriteLine("Shutting down SSL"); 154 | Thread.Sleep(1000); // Give the shutdown message a chance to arrive at the server separately 155 | sslStream.ShutdownAsync().Wait(); 156 | 157 | sentMsg = "First block of unencrypted data from client"; 158 | Console.WriteLine("Sending first unencrypted data message '{0}'", sentMsg); 159 | tcpStream.Write(Encoding.ASCII.GetBytes(sentMsg)); 160 | tcpStream.Flush(); 161 | Console.WriteLine("Sleeping before sending second unencrypted data message"); 162 | Thread.Sleep(1000); // Give the previous message time to arrive at the server, for the server socket to receive it and hand to the caller 163 | Thread.Sleep(4000); // Allow the next server-side receive to time out 164 | 165 | sentMsg = "Second block of unencrypted data from client"; 166 | Console.WriteLine("Sending second unencrypted data message '{0}'", sentMsg); 167 | tcpStream.Write(Encoding.ASCII.GetBytes(sentMsg)); 168 | tcpStream.Flush(); 169 | 170 | Console.WriteLine("Sleeping before sending termination to give the last message time to arrive"); 171 | Thread.Sleep(3000); // Give the previous message time to arrive at the server and for the server socket to receive it and hand to the caller 172 | sslStream.Close(); // not strictly necessary, because garbage collection would eventually handle it 173 | return 0; 174 | } 175 | private static void DisplayUsage() 176 | { 177 | Console.WriteLine("To start the client specify:"); 178 | Console.WriteLine("StreamClientCs [serverName]"); 179 | Environment.Exit(1); 180 | } 181 | public static int Main(string[] args) 182 | { 183 | string serverCertificateName = null; 184 | string serverName = null; 185 | if (args.Length > 1) 186 | DisplayUsage(); 187 | // User can specify the server name (DNS name of the server). the server name must match the name on the server's certificate. 188 | serverName = args.Length > 0 ? args[0] : "localhost"; 189 | serverCertificateName = serverName; 190 | SslTcpClient.RunClient(serverName, serverCertificateName); 191 | 192 | Console.WriteLine("Press any key to pause, Q to exit immediately"); 193 | Stopwatch sw = new(); 194 | sw.Start(); 195 | while ((!Console.KeyAvailable) && sw.ElapsedMilliseconds < 30_000) 196 | Task.Delay(250).Wait(); // Loop until input is entered. 197 | if (Console.KeyAvailable && Console.ReadKey().Key != ConsoleKey.Q) 198 | { 199 | Console.WriteLine("The program will pause until you press enter"); 200 | Console.ReadKey(); 201 | } 202 | return 0; 203 | } 204 | } -------------------------------------------------------------------------------- /StreamClientCs/StreamClientCs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0-windows 6 | enable 7 | disable 8 | 2.1.7 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /StreamSSL.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.10.34607.79 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{1CB7EA77-6290-4FCE-A1F9-FA162C4562F8}" 6 | EndProject 7 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StreamClient", "Samples\StreamClient\StreamClient.vcxproj", "{144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}" 8 | EndProject 9 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StreamServer", "Samples\StreamServer\StreamServer.vcxproj", "{6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}" 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleClient", "Samples\SimpleClient\SimpleClient.vcxproj", "{A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}" 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7A125773-1807-469D-A2AD-2145250CDB4F}" 14 | ProjectSection(SolutionItems) = preProject 15 | Developer.md = Developer.md 16 | License.md = License.md 17 | README.md = README.md 18 | EndProjectSection 19 | EndProject 20 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StreamSSL", "StreamSSL\StreamSSL.vcxproj", "{8526BED2-CB4E-4058-AFEB-357367F3C773}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleClientCs", "SimpleClientCS\SimpleClientCs.csproj", "{70C51721-F7B8-43E1-8926-4BD0C6C61A61}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StreamClientCs", "StreamClientCs\StreamClientCs.csproj", "{0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}" 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Article", "Article", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" 27 | ProjectSection(SolutionItems) = preProject 28 | Article\Article.md = Article\Article.md 29 | Article\mmc.jpg = Article\mmc.jpg 30 | Article\servercert.jpg = Article\servercert.jpg 31 | EndProjectSection 32 | EndProject 33 | Global 34 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 35 | Debug Ansi|Any CPU = Debug Ansi|Any CPU 36 | Debug Ansi|Win32 = Debug Ansi|Win32 37 | Debug Ansi|x64 = Debug Ansi|x64 38 | Debug|Any CPU = Debug|Any CPU 39 | Debug|Win32 = Debug|Win32 40 | Debug|x64 = Debug|x64 41 | Release|Any CPU = Release|Any CPU 42 | Release|Win32 = Release|Win32 43 | Release|x64 = Release|x64 44 | EndGlobalSection 45 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 46 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug Ansi|Any CPU.ActiveCfg = Debug Ansi|x64 47 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug Ansi|Any CPU.Build.0 = Debug Ansi|x64 48 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug Ansi|Win32.ActiveCfg = Debug Ansi|Win32 49 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug Ansi|Win32.Build.0 = Debug Ansi|Win32 50 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug Ansi|x64.ActiveCfg = Debug Ansi|x64 51 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug Ansi|x64.Build.0 = Debug Ansi|x64 52 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|Any CPU.ActiveCfg = Debug|x64 53 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|Any CPU.Build.0 = Debug|x64 54 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|Win32.ActiveCfg = Debug|Win32 55 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|Win32.Build.0 = Debug|Win32 56 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|x64.ActiveCfg = Debug|x64 57 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Debug|x64.Build.0 = Debug|x64 58 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|Any CPU.ActiveCfg = Release|x64 59 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|Any CPU.Build.0 = Release|x64 60 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|Win32.ActiveCfg = Release|Win32 61 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|Win32.Build.0 = Release|Win32 62 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|x64.ActiveCfg = Release|x64 63 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6}.Release|x64.Build.0 = Release|x64 64 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug Ansi|Any CPU.ActiveCfg = Debug Ansi|x64 65 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug Ansi|Any CPU.Build.0 = Debug Ansi|x64 66 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug Ansi|Win32.ActiveCfg = Debug Ansi|Win32 67 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug Ansi|Win32.Build.0 = Debug Ansi|Win32 68 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug Ansi|x64.ActiveCfg = Debug Ansi|x64 69 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug Ansi|x64.Build.0 = Debug Ansi|x64 70 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug|Any CPU.ActiveCfg = Debug|x64 71 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug|Any CPU.Build.0 = Debug|x64 72 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug|Win32.ActiveCfg = Debug|Win32 73 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug|Win32.Build.0 = Debug|Win32 74 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug|x64.ActiveCfg = Debug|x64 75 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Debug|x64.Build.0 = Debug|x64 76 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Release|Any CPU.ActiveCfg = Release|x64 77 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Release|Any CPU.Build.0 = Release|x64 78 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Release|Win32.ActiveCfg = Release|Win32 79 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Release|Win32.Build.0 = Release|Win32 80 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Release|x64.ActiveCfg = Release|x64 81 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415}.Release|x64.Build.0 = Release|x64 82 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug Ansi|Any CPU.ActiveCfg = Debug Ansi|x64 83 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug Ansi|Any CPU.Build.0 = Debug Ansi|x64 84 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug Ansi|Win32.ActiveCfg = Debug Ansi|Win32 85 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug Ansi|Win32.Build.0 = Debug Ansi|Win32 86 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug Ansi|x64.ActiveCfg = Debug Ansi|x64 87 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug Ansi|x64.Build.0 = Debug Ansi|x64 88 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug|Any CPU.ActiveCfg = Debug|x64 89 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug|Any CPU.Build.0 = Debug|x64 90 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug|Win32.ActiveCfg = Debug|Win32 91 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug|Win32.Build.0 = Debug|Win32 92 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug|x64.ActiveCfg = Debug|x64 93 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Debug|x64.Build.0 = Debug|x64 94 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Release|Any CPU.ActiveCfg = Release|x64 95 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Release|Any CPU.Build.0 = Release|x64 96 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Release|Win32.ActiveCfg = Release|Win32 97 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Release|Win32.Build.0 = Release|Win32 98 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Release|x64.ActiveCfg = Release|x64 99 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E}.Release|x64.Build.0 = Release|x64 100 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug Ansi|Any CPU.ActiveCfg = Debug|x64 101 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug Ansi|Any CPU.Build.0 = Debug|x64 102 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug Ansi|Win32.ActiveCfg = Debug|Win32 103 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug Ansi|Win32.Build.0 = Debug|Win32 104 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug Ansi|x64.ActiveCfg = Debug|x64 105 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug Ansi|x64.Build.0 = Debug|x64 106 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|Any CPU.ActiveCfg = Debug|x64 107 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|Any CPU.Build.0 = Debug|x64 108 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|Win32.ActiveCfg = Debug|Win32 109 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|Win32.Build.0 = Debug|Win32 110 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|x64.ActiveCfg = Debug|x64 111 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Debug|x64.Build.0 = Debug|x64 112 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|Any CPU.ActiveCfg = Release|x64 113 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|Any CPU.Build.0 = Release|x64 114 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|Win32.ActiveCfg = Release|Win32 115 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|Win32.Build.0 = Release|Win32 116 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|x64.ActiveCfg = Release|x64 117 | {8526BED2-CB4E-4058-AFEB-357367F3C773}.Release|x64.Build.0 = Release|x64 118 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug Ansi|Any CPU.ActiveCfg = Debug|Any CPU 119 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug Ansi|Any CPU.Build.0 = Debug|Any CPU 120 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug Ansi|Win32.ActiveCfg = Debug|Any CPU 121 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug Ansi|Win32.Build.0 = Debug|Any CPU 122 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug Ansi|x64.ActiveCfg = Debug|Any CPU 123 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug Ansi|x64.Build.0 = Debug|Any CPU 124 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 125 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug|Any CPU.Build.0 = Debug|Any CPU 126 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug|Win32.ActiveCfg = Debug|Any CPU 127 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug|Win32.Build.0 = Debug|Any CPU 128 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug|x64.ActiveCfg = Debug|Any CPU 129 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Debug|x64.Build.0 = Debug|Any CPU 130 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Release|Any CPU.ActiveCfg = Release|Any CPU 131 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Release|Any CPU.Build.0 = Release|Any CPU 132 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Release|Win32.ActiveCfg = Release|Any CPU 133 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Release|Win32.Build.0 = Release|Any CPU 134 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Release|x64.ActiveCfg = Release|Any CPU 135 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61}.Release|x64.Build.0 = Release|Any CPU 136 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug Ansi|Any CPU.ActiveCfg = Debug|Any CPU 137 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug Ansi|Any CPU.Build.0 = Debug|Any CPU 138 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug Ansi|Win32.ActiveCfg = Debug|Any CPU 139 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug Ansi|Win32.Build.0 = Debug|Any CPU 140 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug Ansi|x64.ActiveCfg = Debug|Any CPU 141 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug Ansi|x64.Build.0 = Debug|Any CPU 142 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 143 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug|Any CPU.Build.0 = Debug|Any CPU 144 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug|Win32.ActiveCfg = Debug|Any CPU 145 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug|Win32.Build.0 = Debug|Any CPU 146 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug|x64.ActiveCfg = Debug|Any CPU 147 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Debug|x64.Build.0 = Debug|Any CPU 148 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Release|Any CPU.ActiveCfg = Release|Any CPU 149 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Release|Any CPU.Build.0 = Release|Any CPU 150 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Release|Win32.ActiveCfg = Release|Any CPU 151 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Release|Win32.Build.0 = Release|Any CPU 152 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Release|x64.ActiveCfg = Release|Any CPU 153 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC}.Release|x64.Build.0 = Release|Any CPU 154 | EndGlobalSection 155 | GlobalSection(SolutionProperties) = preSolution 156 | HideSolutionNode = FALSE 157 | EndGlobalSection 158 | GlobalSection(NestedProjects) = preSolution 159 | {144CCC0E-0CE9-42D2-887B-4210DEAA2BC6} = {1CB7EA77-6290-4FCE-A1F9-FA162C4562F8} 160 | {6FC978B9-9CC8-4C8A-A5E3-D575B59ED415} = {1CB7EA77-6290-4FCE-A1F9-FA162C4562F8} 161 | {A96158C0-9879-4D2C-BBE6-ED2B86A1E93E} = {1CB7EA77-6290-4FCE-A1F9-FA162C4562F8} 162 | {70C51721-F7B8-43E1-8926-4BD0C6C61A61} = {1CB7EA77-6290-4FCE-A1F9-FA162C4562F8} 163 | {0006B3AC-95E5-4FC7-92FF-AD7C9FF22DBC} = {1CB7EA77-6290-4FCE-A1F9-FA162C4562F8} 164 | {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {7A125773-1807-469D-A2AD-2145250CDB4F} 165 | EndGlobalSection 166 | GlobalSection(ExtensibilityGlobals) = postSolution 167 | SolutionGuid = {E2735530-C231-462D-AFF1-BD154690364B} 168 | EndGlobalSection 169 | EndGlobal 170 | -------------------------------------------------------------------------------- /StreamSSL/StreamSSL.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {8526BED2-CB4E-4058-AFEB-357367F3C773} 24 | Win32Proj 25 | 10.0 26 | 27 | 28 | 29 | StaticLibrary 30 | true 31 | v143 32 | Unicode 33 | 34 | 35 | StaticLibrary 36 | false 37 | v143 38 | true 39 | Unicode 40 | 41 | 42 | StaticLibrary 43 | true 44 | v143 45 | Unicode 46 | 47 | 48 | StaticLibrary 49 | false 50 | v143 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | .;..\SSLClient\include;..\SSLServer\include;..\Common\include;$(IncludePath) 75 | $(SolutionDir)$(Platform)\$(Configuration)\ 76 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 77 | 78 | 79 | true 80 | .;..\SSLClient\include;..\SSLServer\include;..\Common\include;$(IncludePath) 81 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 82 | 83 | 84 | false 85 | .;..\SSLClient\include;..\SSLServer\include;..\Common\include;$(IncludePath) 86 | $(SolutionDir)$(Platform)\$(Configuration)\ 87 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 88 | 89 | 90 | false 91 | .;..\SSLClient\include;..\SSLServer\include;..\Common\include;$(IncludePath) 92 | $(SolutionDir)$(Platform)\$(Configuration)\_intermediate_files_\$(ProjectName)\ 93 | 94 | 95 | 96 | Use 97 | Level3 98 | Disabled 99 | true 100 | WIN32;_DEBUG;STREAMSSL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 101 | true 102 | pch.h 103 | $(OutDir)$(TargetName).pdb 104 | stdcpp14 105 | 106 | 107 | Windows 108 | true 109 | false 110 | 111 | 112 | 113 | 114 | Use 115 | Level3 116 | Disabled 117 | true 118 | _DEBUG;STREAMSSL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 119 | true 120 | pch.h 121 | $(OutDir)$(TargetName).pdb 122 | 123 | 124 | Windows 125 | true 126 | false 127 | 128 | 129 | 130 | 131 | Use 132 | Level3 133 | MaxSpeed 134 | true 135 | true 136 | true 137 | WIN32;NDEBUG;STREAMSSL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 138 | true 139 | pch.h 140 | $(OutDir)$(TargetName).pdb 141 | 142 | 143 | Windows 144 | true 145 | true 146 | true 147 | false 148 | 149 | 150 | 151 | 152 | Use 153 | Level3 154 | MaxSpeed 155 | true 156 | true 157 | true 158 | NDEBUG;STREAMSSL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 159 | true 160 | pch.h 161 | $(OutDir)$(TargetName).pdb 162 | 163 | 164 | Windows 165 | true 166 | true 167 | true 168 | false 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | Create 210 | Create 211 | Create 212 | Create 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /StreamSSL/StreamSSL.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {2da9f0cb-f4fb-4b5c-8cf5-90b63b50250a} 18 | 19 | 20 | {affdf6d7-687d-4117-89e9-9c15a17df6fc} 21 | 22 | 23 | {24d9d374-4217-4d35-978b-1b30685d387d} 24 | 25 | 26 | {c4388698-1dce-4888-9178-11d224695af6} 27 | 28 | 29 | {0580d549-2c3e-4d9e-a6dc-a4e974856e23} 30 | 31 | 32 | {f83560d6-95b5-4bc7-a9a3-fab754f62c81} 33 | 34 | 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files\Common Headers 44 | 45 | 46 | Header Files\Common Headers 47 | 48 | 49 | Header Files\Common Headers 50 | 51 | 52 | Header Files\Common Headers 53 | 54 | 55 | Header Files\Common Headers 56 | 57 | 58 | Header Files\Common Headers 59 | 60 | 61 | Header Files\Common Headers 62 | 63 | 64 | Header Files\Common Headers 65 | 66 | 67 | Header Files\Client Headers 68 | 69 | 70 | Header Files\Client Headers 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files\Server Headers 77 | 78 | 79 | Header Files\Server Headers 80 | 81 | 82 | Header Files\Server Headers 83 | 84 | 85 | Header Files\Server Headers 86 | 87 | 88 | Header Files\Common Headers 89 | 90 | 91 | Header Files\Common Headers 92 | 93 | 94 | Header Files\Common Headers 95 | 96 | 97 | Header Files\Common Headers 98 | 99 | 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files\Client Sources 106 | 107 | 108 | Source Files\Client Sources 109 | 110 | 111 | Source Files\Common Sources 112 | 113 | 114 | Source Files\Common Sources 115 | 116 | 117 | Source Files\Common Sources 118 | 119 | 120 | Source Files\Common Sources 121 | 122 | 123 | Source Files\Server Sources 124 | 125 | 126 | Source Files\Server Sources 127 | 128 | 129 | Source Files\Server Sources 130 | 131 | 132 | Source Files\Server Sources 133 | 134 | 135 | Source Files\Common Sources 136 | 137 | 138 | Source Files\Common Sources 139 | 140 | 141 | Source Files\Common Sources 142 | 143 | 144 | -------------------------------------------------------------------------------- /StreamSSL/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 5 | 6 | // The following commented code is for debugging memory leaks 7 | //#define _CRTDBG_MAP_ALLOC 8 | //#include 9 | //#include 10 | //#ifdef _DEBUG 11 | //#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) 12 | //// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the 13 | //// allocations to be of _CLIENT_BLOCK type 14 | //#else 15 | //#define DBG_NEW new 16 | //#endif 17 | 18 | // Define a bool to check if this is a DEBUG or RELEASE build 19 | #ifndef DEBUGFLAG_DEFINED 20 | #define DEBUGFLAG_DEFINED 21 | #if defined(_DEBUG) 22 | const bool debug = true; 23 | #else 24 | const bool debug = false; 25 | #endif 26 | #endif // DEBUGFLAG_DEFINED 27 | 28 | #ifndef VC_EXTRALEAN 29 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | #pragma comment(lib, "Ws2_32.lib") 37 | 38 | #define SECURITY_WIN32 39 | #include 40 | #include 41 | 42 | // Standard C++ 43 | #include 44 | typedef unsigned char byte; -------------------------------------------------------------------------------- /StreamSSL/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /StreamSSL/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /StreamSSL/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // // Including SDKDDKVer.h defines the highest available Windows platform. 4 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 5 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 6 | #define _WIN32_WINNT 0x0600 // Allow use of features specific to Windows 6 (Vista) or later 7 | #include 8 | --------------------------------------------------------------------------------