1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
#include "precomp.h"
// okay, this may look a little weird, but metautil.h cannot be in the
// pre-compiled header because we need to #define these things so the
// correct GUID's get pulled into this object file
#include <initguid.h>
#include "metautil.h"
// Exit macros
#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__)
#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__)
#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__)
#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__)
#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__)
#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__)
// prototypes
static void Sort(
__in_ecount(cArray) DWORD dwArray[],
__in int cArray
);
/********************************************************************
MetaFindWebBase - finds a metabase base string that matches IP, Port and Header
********************************************************************/
extern "C" HRESULT DAPI MetaFindWebBase(
__in IMSAdminBaseW* piMetabase,
__in_z LPCWSTR wzIP,
__in int iPort,
__in_z LPCWSTR wzHeader,
__in BOOL fSecure,
__out_ecount(cchWebBase) LPWSTR wzWebBase,
__in DWORD cchWebBase
)
{
Assert(piMetabase && cchWebBase);
HRESULT hr = S_OK;
BOOL fFound = FALSE;
WCHAR wzKey[METADATA_MAX_NAME_LEN];
WCHAR wzSubkey[METADATA_MAX_NAME_LEN];
DWORD dwIndex = 0;
METADATA_RECORD mr;
METADATA_RECORD mrAddress;
LPWSTR pwzExists = NULL;
LPWSTR pwzIPExists = NULL;
LPWSTR pwzPortExists = NULL;
int iPortExists = 0;
LPCWSTR pwzHeaderExists = NULL;
memset(&mr, 0, sizeof(mr));
mr.dwMDIdentifier = MD_KEY_TYPE;
mr.dwMDAttributes = METADATA_INHERIT;
mr.dwMDUserType = IIS_MD_UT_SERVER;
mr.dwMDDataType = ALL_METADATA;
memset(&mrAddress, 0, sizeof(mrAddress));
mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS;
mrAddress.dwMDAttributes = METADATA_INHERIT;
mrAddress.dwMDUserType = IIS_MD_UT_SERVER;
mrAddress.dwMDDataType = ALL_METADATA;
// loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb
for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex)
{
hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex);
if (FAILED(hr))
break;
::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey);
hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr);
if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
hr = S_FALSE; // didn't find anything, try next one
continue;
}
MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers");
// if we have an IIsWebServer store the key
if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData))
{
hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress);
if (MD_ERROR_DATA_NOT_FOUND == hr)
hr = S_FALSE;
MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers");
// break down the first address into parts
pwzIPExists = reinterpret_cast<LPWSTR>(mrAddress.pbMDData);
pwzExists = wcsstr(pwzIPExists, L":");
if (NULL == pwzExists)
continue;
*pwzExists = L'\0';
pwzPortExists = pwzExists + 1;
pwzExists = wcsstr(pwzPortExists, L":");
if (NULL == pwzExists)
continue;
*pwzExists = L'\0';
iPortExists = wcstol(pwzPortExists, NULL, 10);
pwzHeaderExists = pwzExists + 1;
// compare the passed in address with the address listed for this web
if (S_OK == hr &&
(0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) &&
iPort == iPortExists &&
0 == lstrcmpW(wzHeader, pwzHeaderExists))
{
// if the passed in buffer wasn't big enough
hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey);
MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey);
fFound = TRUE;
break;
}
}
}
if (E_NOMOREITEMS == hr)
{
Assert(!fFound);
hr = S_FALSE;
}
LExit:
MetaFreeValue(&mrAddress);
MetaFreeValue(&mr);
if (!fFound && SUCCEEDED(hr))
hr = S_FALSE;
return hr;
}
/********************************************************************
MetaFindFreeWebBase - finds the next metabase base string
********************************************************************/
extern "C" HRESULT DAPI MetaFindFreeWebBase(
__in IMSAdminBaseW* piMetabase,
__out_ecount(cchWebBase) LPWSTR wzWebBase,
__in DWORD cchWebBase
)
{
Assert(piMetabase);
HRESULT hr = S_OK;
WCHAR wzKey[METADATA_MAX_NAME_LEN];
WCHAR wzSubkey[METADATA_MAX_NAME_LEN];
DWORD dwSubKeys[100];
int cSubKeys = 0;
DWORD dwIndex = 0;
int i;
DWORD dwKey;
METADATA_RECORD mr;
memset(&mr, 0, sizeof(mr));
mr.dwMDIdentifier = MD_KEY_TYPE;
mr.dwMDAttributes = 0;
mr.dwMDUserType = IIS_MD_UT_SERVER;
mr.dwMDDataType = STRING_METADATA;
// loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb
for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex)
{
hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex);
if (FAILED(hr))
break;
::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey);
hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr);
if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
hr = S_FALSE; // didn't find anything, try next one
continue;
}
MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root");
// if we have a IIsWebServer get the address information
if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast<LPCWSTR>(mr.pbMDData)))
{
if (cSubKeys >= countof(dwSubKeys))
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites");
}
dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10);
++cSubKeys;
Sort(dwSubKeys, cSubKeys);
}
}
if (E_NOMOREITEMS == hr)
hr = S_OK;
MetaExitOnFailure(hr, "failed to find free web root");
// find the lowest free web root
dwKey = 1;
for (i = 0; i < cSubKeys; ++i)
{
if (dwKey < dwSubKeys[i])
break;
dwKey = dwSubKeys[i] + 1;
}
hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey);
LExit:
MetaFreeValue(&mr);
return hr;
}
/********************************************************************
MetaOpenKey - open key
********************************************************************/
extern "C" HRESULT DAPI MetaOpenKey(
__in IMSAdminBaseW* piMetabase,
__in METADATA_HANDLE mhKey,
__in_z LPCWSTR wzKey,
__in DWORD dwAccess,
__in DWORD cRetries,
__out METADATA_HANDLE* pmh
)
{
Assert(piMetabase && pmh);
HRESULT hr = S_OK;
// loop while the key is busy
do
{
hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh);
if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr)
::SleepEx(1000, TRUE);
} while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--);
return hr;
}
/********************************************************************
MetaGetValue - finds the next metabase base string
NOTE: piMetabase is optional
********************************************************************/
extern "C" HRESULT DAPI MetaGetValue(
__in IMSAdminBaseW* piMetabase,
__in METADATA_HANDLE mhKey,
__in_z LPCWSTR wzKey,
__inout METADATA_RECORD* pmr
)
{
Assert(pmr);
HRESULT hr = S_OK;
BOOL fInitialized = FALSE;
DWORD cbRequired = 0;
if (!piMetabase)
{
hr = ::CoInitialize(NULL);
MetaExitOnFailure(hr, "failed to initialize COM");
fInitialized = TRUE;
hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast<LPVOID*>(&piMetabase));
MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object");
}
if (!pmr->pbMDData)
{
pmr->dwMDDataLen = 256;
pmr->pbMDData = static_cast<BYTE*>(MemAlloc(pmr->dwMDDataLen, TRUE));
MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value");
}
else // set the size of the data to the actual size of the memory
{
SIZE_T cb = 0;
hr = MemSizeChecked(pmr->pbMDData, &cb);
MetaExitOnFailure(hr, "failed to get metabase size");
if (cb > DWORD_MAX)
{
MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb);
}
pmr->dwMDDataLen = (DWORD)cb;
}
hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired);
if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
{
pmr->dwMDDataLen = cbRequired;
BYTE* pb = static_cast<BYTE*>(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE));
MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value");
pmr->pbMDData = pb;
hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired);
}
MetaExitOnFailure(hr, "failed to get metabase data");
LExit:
if (fInitialized)
{
ReleaseObject(piMetabase);
::CoUninitialize();
}
return hr;
}
/********************************************************************
MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue()
NOTE: METADATA_RECORD must have been returned from MetaGetValue() above
********************************************************************/
extern "C" void DAPI MetaFreeValue(
__in METADATA_RECORD* pmr
)
{
Assert(pmr);
ReleaseNullMem(pmr->pbMDData);
}
//
// private
//
/********************************************************************
Sort - quick and dirty insertion sort
********************************************************************/
static void Sort(
__in_ecount(cArray) DWORD dwArray[],
__in int cArray
)
{
int i, j;
DWORD dwData;
for (i = 1; i < cArray; ++i)
{
dwData = dwArray[i];
j = i - 1;
while (0 <= j && dwArray[j] > dwData)
{
dwArray[j + 1] = dwArray[j];
j--;
}
dwArray[j + 1] = dwData;
}
}
|