cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
net_proxy.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Network Stream Proxy Management *
4 * Copyright Peter Gutmann 1993-2007 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "stream_int.h"
10 #else
11  #include "io/stream_int.h"
12 #endif /* Compiler-specific includes */
13 
14 #ifdef USE_TCP
15 
16 /****************************************************************************
17 * *
18 * SOCKS Proxy Management *
19 * *
20 ****************************************************************************/
21 
22 /* Open a connection through a Socks proxy. This is currently disabled
23  since it doesn't appear to be used by anyone */
24 
25 #if 0
26 
28 int connectViaSocksProxy( INOUT STREAM *stream )
29  {
31  BYTE socksBuffer[ 64 + CRYPT_MAX_TEXTSIZE + 8 ], *bufPtr = socksBuffer;
32  char userName[ CRYPT_MAX_TEXTSIZE + 8 ];
33  int length, status;
34 
35  assert( isWritePtr( stream, sizeof( STREAM ) ) );
36 
37  REQUIRES_S( stream->type == STREAM_TYPE_NETWORK );
38 
39  /* Get the SOCKS user name, defaulting to "cryptlib" if there's none
40  set */
41  setMessageData( &msgData, userName, CRYPT_MAX_TEXTSIZE );
43  IMESSAGE_GETATTRIBUTE_S, &msgData,
45  if( cryptStatusOK( status ) )
46  userName[ msgData.length ] = '\0';
47  else
48  strlcpy_s( userName, CRYPT_MAX_TEXTSIZE, "cryptlib" );
49 
50  /* Build up the SOCKSv4 request string:
51 
52  BYTE: version = 4
53  BYTE: command = 1 (connect)
54  WORD: port
55  LONG: IP address
56  STRING: userName + '\0'
57 
58  Note that this has a potential problem in that it requires a DNS
59  lookup by the client, which can lead to problems if the client
60  can't get DNS requests out because only SOCKSified access is allowed.
61  A related problem occurs when SOCKS is being used as a tunnelling
62  interface because the DNS lookup will communicate data about the
63  client to an observer outside the tunnel.
64 
65  To work around this there's a so-called SOCKSv4a protocol that has
66  the SOCKS proxy perform the lookup:
67 
68  BYTE: version = 4
69  BYTE: command = 1 (connect)
70  WORD: port
71  LONG: IP address = 0x00 0x00 0x00 0xFF
72  STRING: userName + '\0'
73  STRING: FQDN + '\0'
74 
75  Unfortunately there's no way to tell whether a SOCKS server supports
76  4a or only 4, but in any case since SOCKS support is currently
77  disabled we leave the poke-and-hope 4a detection until such time as
78  someone actually requests it */
79  *bufPtr++ = 4; *bufPtr++ = 1;
80  mputWord( bufPtr, netStream->port );
81  status = getIPAddress( stream, bufPtr, netStream->host );
82  strlcpy_s( bufPtr + 4, CRYPT_MAX_TEXTSIZE, userName );
83  length = 1 + 1 + 2 + 4 + strlen( userName ) + 1;
84  if( cryptStatusError( status ) )
85  {
86  netStream->transportDisconnectFunction( stream, TRUE );
87  return( status );
88  }
89 
90  /* Send the data to the server and read back the reply */
91  status = netStream->transportWriteFunction( stream, socksBuffer, length,
93  if( cryptStatusOK( status ) )
94  status = netStream->transportReadFunction( stream, socksBuffer, 8,
96  if( cryptStatusError( status ) )
97  {
98  /* The involvement of a proxy complicates matters somewhat because
99  we can usually connect to the proxy OK but may run into problems
100  going from the proxy to the remote server, so if we get an error
101  at this stage (which will typically show up as a read error from
102  the proxy) we report it as an open error instead */
103  if( status == CRYPT_ERROR_READ || status == CRYPT_ERROR_COMPLETE )
104  status = CRYPT_ERROR_OPEN;
105  netStream->transportDisconnectFunction( stream, TRUE );
106  return( status );
107  }
108 
109  /* Make sure that everything is OK:
110 
111  BYTE: null = 0
112  BYTE: status = 90 (OK)
113  WORD: port
114  LONG: IP address */
115  if( socksBuffer[ 1 ] != 90 )
116  {
117  int i;
118 
119  netStream->transportDisconnectFunction( stream, TRUE );
120  strlcpy_s( netStream->errorInfo->errorString, MAX_ERRMSG_SIZE,
121  "Socks proxy returned" );
122  for( i = 0; i < 8; i++ )
123  {
124  sprintf_s( netStream->errorInfo->errorString + 20 + ( i * 3 ),
125  MAX_ERRMSG_SIZE - ( 20 + ( i * 3 ) ), " %02X",
126  socksBuffer[ i ] );
127  }
128  strlcat_s( netStream->errorInfo->errorString, MAX_ERRMSG_SIZE, "." );
129  netStream->errorCode = socksBuffer[ 1 ];
130  return( CRYPT_ERROR_OPEN );
131  }
132 
133  return( CRYPT_OK );
134  }
135 #endif /* 0 */
136 
137 /****************************************************************************
138 * *
139 * HTTP Proxy Management *
140 * *
141 ****************************************************************************/
142 
143 #ifdef USE_HTTP
144 
145 /* Open a connection via an HTTP proxy */
146 
147 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
148 int connectViaHttpProxy( INOUT STREAM *stream, INOUT ERROR_INFO *errorInfo )
149  {
150  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
151  BYTE buffer[ 64 + 8 ];
152  int length, status;
153 
154  assert( isWritePtr( stream, sizeof( STREAM ) ) );
155  assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );
156 
157  REQUIRES_S( netStream != NULL );
158  REQUIRES_S( stream->type == STREAM_TYPE_NETWORK );
159 
160  /* Open the connection via the proxy. To do this we temporarily layer
161  HTTP I/O over the TCP I/O, then once the proxy messaging has been
162  completed we re-set the stream to pure TCP I/O and clear any stream
163  flags that were set during the proxying */
164  setStreamLayerHTTP( netStream );
165  status = netStream->writeFunction( stream, "", 0, &length );
166  if( cryptStatusOK( status ) )
167  status = netStream->readFunction( stream, buffer, 64, &length );
168  setStreamLayerDirect( netStream );
169  stream->flags = 0;
170  if( cryptStatusError( status ) )
171  {
172  /* The involvement of a proxy complicates matters somewhat because
173  we can usually connect to the proxy OK but may run into problems
174  going from the proxy to the remote server so if we get an error
175  at this stage (which will typically show up as a read error from
176  the proxy) we report it as an open error instead */
177  if( status == CRYPT_ERROR_READ || status == CRYPT_ERROR_COMPLETE )
178  status = CRYPT_ERROR_OPEN;
179  copyErrorInfo( errorInfo, NETSTREAM_ERRINFO );
180  netStream->transportDisconnectFunction( netStream, TRUE );
181  }
182 
183  return( status );
184  }
185 #endif /* USE_HTTP */
186 
187 /****************************************************************************
188 * *
189 * Proxy Autoconfig Management *
190 * *
191 ****************************************************************************/
192 
193 /* Try and auto-detect HTTP proxy information */
194 
195 #if defined( __WIN32__ )
196 
197 /* The autoproxy functions were only documented in WinHTTP 5.1 so we have to
198  provide the necessary defines and structures ourselves */
199 
200 #ifndef WINHTTP_ACCESS_TYPE_DEFAULT_PROXY
201 
202 #define HINTERNET HANDLE
203 
204 typedef struct {
205  DWORD dwFlags;
206  DWORD dwAutoDetectFlags;
207  LPCWSTR lpszAutoConfigUrl;
208  LPVOID lpvReserved;
209  DWORD dwReserved;
210  BOOL fAutoLogonIfChallenged;
211  } WINHTTP_AUTOPROXY_OPTIONS;
212 
213 typedef struct {
214  DWORD dwAccessType;
215  LPWSTR lpszProxy;
216  LPWSTR lpszProxyBypass;
217  } WINHTTP_PROXY_INFO;
218 
219 typedef struct {
220  BOOL fAutoDetect;
221  LPWSTR lpszAutoConfigUrl;
222  LPWSTR lpszProxy;
223  LPWSTR lpszProxyBypass;
224  } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
225 
226 #define WINHTTP_AUTOPROXY_AUTO_DETECT 1
227 #define WINHTTP_AUTO_DETECT_TYPE_DHCP 1
228 #define WINHTTP_AUTO_DETECT_TYPE_DNS_A 2
229 #define WINHTTP_ACCESS_TYPE_NO_PROXY 1
230 #define WINHTTP_NO_PROXY_NAME NULL
231 #define WINHTTP_NO_PROXY_BYPASS NULL
232 
233 #endif /* WinHTTP 5.1 defines and structures */
234 
235 typedef HINTERNET ( *WINHTTPOPEN )( LPCWSTR pwszUserAgent, DWORD dwAccessType,
236  LPCWSTR pwszProxyName, LPCWSTR pwszProxyBypass,
237  DWORD dwFlags );
238 typedef BOOL ( *WINHTTPGETDEFAULTPROXYCONFIGURATION )( WINHTTP_PROXY_INFO* pProxyInfo );
239 typedef BOOL ( *WINHTTPGETIEPROXYCONFIGFORCURRENTUSER )(
240  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *pProxyConfig );
241 typedef BOOL ( *WINHTTPGETPROXYFORURL )( HINTERNET hSession, LPCWSTR lpcwszUrl,
242  WINHTTP_AUTOPROXY_OPTIONS *pAutoProxyOptions,
243  WINHTTP_PROXY_INFO *pProxyInfo );
244 typedef BOOL ( *WINHTTPCLOSEHANDLE )( HINTERNET hInternet );
245 
246 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
247 int findProxyUrl( OUT_BUFFER( proxyMaxLen, *proxyLen ) char *proxy,
248  IN_LENGTH_DNS const int proxyMaxLen,
249  OUT_LENGTH_DNS_Z int *proxyLen,
250  IN_BUFFER( urlLen ) const char *url,
251  IN_LENGTH_DNS const int urlLen )
252  {
253  static HMODULE hWinHTTP = NULL;
254  static WINHTTPOPEN pWinHttpOpen = NULL;
255  static WINHTTPGETDEFAULTPROXYCONFIGURATION pWinHttpGetDefaultProxyConfiguration = NULL;
256  static WINHTTPGETIEPROXYCONFIGFORCURRENTUSER pWinHttpGetIEProxyConfigForCurrentUser = NULL;
257  static WINHTTPGETPROXYFORURL pWinHttpGetProxyForUrl = NULL;
258  static WINHTTPCLOSEHANDLE pWinHttpCloseHandle = NULL;
259  WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions = \
260  { WINHTTP_AUTOPROXY_AUTO_DETECT,
261  WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A,
262  NULL, NULL, 0, FALSE };
263  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyInfo;
264  WINHTTP_PROXY_INFO proxyInfo;
265  HINTERNET hSession;
266  char urlBuffer[ MAX_DNS_SIZE + 1 + 8 ];
267  wchar_t unicodeURL[ MAX_DNS_SIZE + 1 + 8 ];
268  size_t unicodeUrlLen, wcsProxyLen;
269  int offset, length, proxyStatus;
270 
271  assert( isWritePtr( proxy, proxyMaxLen ) );
272  assert( isWritePtr( proxyLen, sizeof( int ) ) );
273  assert( isReadPtr( url, urlLen ) );
274 
275  REQUIRES( proxyMaxLen >= 10 && proxyMaxLen <= MAX_DNS_SIZE );
276  REQUIRES( urlLen > 0 && urlLen <= MAX_DNS_SIZE );
277 
278  /* Under Win2K SP3 and Windows XP and newer (2003, Vista, etc), or at
279  least Windows versions with WinHTTP 5.1 installed in some way (it
280  officially shipped with the versions mentioned earlier) we can use
281  WinHTTP AutoProxy support, which implements the Web Proxy Auto-
282  Discovery (WPAD) protocol from an internet draft that expired in May
283  2001. Under older versions of Windows we have to use the WinINet
284  InternetGetProxyInfo, however this consists of a ghastly set of
285  kludges that were never meant to be exposed to the outside world
286  (they were only crowbarred out of MS as part of the DoJ consent
287  decree) and user experience with them is that they don't really work
288  except in the one special way in which MS-internal code calls them.
289  Since we don't know what this is, we use the WinHTTP functions
290  instead */
291  if( hWinHTTP == NULL )
292  {
293  if( ( hWinHTTP = DynamicLoad( "WinHTTP.dll" ) ) == NULL )
294  return( CRYPT_ERROR_NOTFOUND );
295 
296  pWinHttpOpen = ( WINHTTPOPEN ) \
297  GetProcAddress( hWinHTTP, "WinHttpOpen" );
298  pWinHttpGetDefaultProxyConfiguration = ( WINHTTPGETDEFAULTPROXYCONFIGURATION ) \
299  GetProcAddress( hWinHTTP, "WinHttpGetDefaultProxyConfiguration" );
300  pWinHttpGetIEProxyConfigForCurrentUser = ( WINHTTPGETIEPROXYCONFIGFORCURRENTUSER ) \
301  GetProcAddress( hWinHTTP, "WinHttpGetIEProxyConfigForCurrentUser" );
302  pWinHttpGetProxyForUrl = ( WINHTTPGETPROXYFORURL ) \
303  GetProcAddress( hWinHTTP, "WinHttpGetProxyForUrl" );
304  pWinHttpCloseHandle = ( WINHTTPCLOSEHANDLE ) \
305  GetProcAddress( hWinHTTP, "WinHttpCloseHandle" );
306  if( pWinHttpOpen == NULL || pWinHttpGetProxyForUrl == NULL || \
307  pWinHttpCloseHandle == NULL )
308  {
309  DynamicUnload( hWinHTTP );
310  return( CRYPT_ERROR_NOTFOUND );
311  }
312  }
313 
314  /* Autoproxy discovery using WinHttpGetProxyForUrl() can be awfully slow,
315  often taking several seconds since it requires probing for proxy info
316  first using DHCP and then if that fails using DNS. Since this is done
317  via a blocking call everything blocks while it's in progress. To help
318  mitigate this we try for proxy info direct from the registry if it's
319  available, avoiding the lengthy autodiscovery process. This also
320  means that discovery will work if no auto-discovery support is present,
321  for example on servers where the admin has set the proxy config
322  directly with ProxyCfg.exe */
323  if( pWinHttpGetDefaultProxyConfiguration != NULL && \
324  pWinHttpGetDefaultProxyConfiguration( &proxyInfo ) && \
325  proxyInfo.lpszProxy != NULL )
326  {
327  proxyStatus = wcstombs_s( &wcsProxyLen, proxy, proxyMaxLen,
328  proxyInfo.lpszProxy, MAX_DNS_SIZE );
329  GlobalFree( proxyInfo.lpszProxy );
330  if( proxyInfo.lpszProxyBypass != NULL )
331  GlobalFree( proxyInfo.lpszProxyBypass );
332  if( proxyStatus == 0 )
333  {
334  *proxyLen = wcsProxyLen;
335  return( CRYPT_OK );
336  }
337  }
338 
339  /* The next fallback is to get the proxy info from MSIE. This is also
340  usually much quicker than WinHttpGetProxyForUrl() although sometimes
341  it seems to fall back to that, based on the longish delay involved.
342  Another issue with this is that it won't work in a service process
343  that isn't impersonating an interactive user (since there isn't a
344  current user), but in that case we just fall back to
345  WinHttpGetProxyForUrl() */
346  if( pWinHttpGetIEProxyConfigForCurrentUser != NULL && \
347  pWinHttpGetIEProxyConfigForCurrentUser( &ieProxyInfo ) )
348  {
349  proxyStatus = wcstombs_s( &wcsProxyLen, proxy, proxyMaxLen,
350  ieProxyInfo.lpszProxy, MAX_DNS_SIZE );
351  if( ieProxyInfo.lpszAutoConfigUrl != NULL )
352  GlobalFree( ieProxyInfo.lpszAutoConfigUrl );
353  if( ieProxyInfo.lpszProxy != NULL )
354  GlobalFree( ieProxyInfo.lpszProxy );
355  if( ieProxyInfo.lpszProxyBypass != NULL )
356  GlobalFree( ieProxyInfo.lpszProxyBypass );
357  if( proxyStatus == 0 )
358  {
359  *proxyLen = wcsProxyLen;
360  return( CRYPT_OK );
361  }
362  }
363 
364  /* WinHttpGetProxyForUrl() requires a schema for the URL that it's
365  performing a lookup on, if the URL doesn't contain one we use a
366  default value of "http://". In addition we need to convert the
367  raw octet string into a null-terminated string for the mbstowcs_s()
368  Unicode conversion and following WinHttpGetProxyForUrl() lookup */
369  if( strFindStr( url, urlLen, "://", 3 ) < 0 )
370  {
371  strlcpy_s( urlBuffer, MAX_DNS_SIZE, "http://" );
372  offset = 7;
373  length = MAX_DNS_SIZE - offset;
374  }
375  else
376  {
377  /* There's already a schema present, not need to manually add one */
378  offset = 0;
379  length = urlLen;
380  }
381  memcpy( urlBuffer + offset, url, length );
382  urlBuffer[ offset + length ] = '\0';
383 
384  /* Locate the proxy used for accessing the resource at the supplied URL.
385  We have to convert to and from Unicode because the WinHTTP functions
386  all take Unicode strings as args. Note that we use the libc widechar
387  functions rather than the Windows ones since the latter aren't
388  present in Win95 or Win98.
389 
390  WinHttpGetProxyForUrl() can be rather flaky, in some cases it'll fail
391  instantly (without even trying auto-discovery) with GetLastError() =
392  87 (parameter error) but then calling it again some time later works
393  fine. Because of this we leave it as the last resort after trying
394  all of the other get-proxy mechanisms */
395  hSession = pWinHttpOpen( L"cryptlib/1.0",
396  WINHTTP_ACCESS_TYPE_NO_PROXY,
397  WINHTTP_NO_PROXY_NAME,
398  WINHTTP_NO_PROXY_BYPASS, 0 );
399  if( hSession == NULL )
400  return( CRYPT_ERROR_NOTFOUND );
401  if( mbstowcs_s( &unicodeUrlLen, unicodeURL, MAX_DNS_SIZE,
402  urlBuffer, MAX_DNS_SIZE ) != 0 )
403  {
404  pWinHttpCloseHandle( hSession );
405  return( CRYPT_ERROR_NOTFOUND );
406  }
407  unicodeURL[ unicodeUrlLen ] = L'\0';
408  memset( &proxyInfo, 0, sizeof( WINHTTP_PROXY_INFO ) );
409  if( pWinHttpGetProxyForUrl( hSession, unicodeURL, &autoProxyOptions,
410  &proxyInfo ) != TRUE )
411  {
412  pWinHttpCloseHandle( hSession );
413  return( CRYPT_ERROR_NOTFOUND );
414  }
415  proxyStatus = wcstombs_s( &wcsProxyLen, proxy, proxyMaxLen,
416  proxyInfo.lpszProxy, MAX_DNS_SIZE );
417  GlobalFree( proxyInfo.lpszProxy );
418  if( proxyInfo.lpszProxyBypass != NULL )
419  GlobalFree( proxyInfo.lpszProxyBypass );
420  pWinHttpCloseHandle( hSession );
421  if( proxyStatus != 0 )
422  return( CRYPT_ERROR_NOTFOUND );
423  *proxyLen = wcsProxyLen;
424 
425  return( CRYPT_OK );
426  }
427 
428 #if 0
429 
430 typedef BOOL ( WINAPI *INTERNETGETPROXYINFO )( LPCSTR lpszUrl, DWORD dwUrlLength,
431  LPSTR lpszUrlHostName, DWORD dwUrlHostNameLength,
432  LPSTR* lplpszProxyHostName,
433  LPDWORD lpdwProxyHostNameLength );
434 typedef BOOL ( WINAPI *INTERNETINITIALIZEAUTOPROXYDLL )( DWORD dwVersion,
435  LPSTR lpszDownloadedTempFile, LPSTR lpszMime,
436  AutoProxyHelperFunctions* lpAutoProxyCallbacks,
437  LPAUTO_PROXY_SCRIPT_BUFFER lpAutoProxyScriptBuffer );
438 
439 static int findProxyUrl( char *proxy, const int proxyMaxLen,
440  const char *url, const int urlLen )
441  {
442  static INTERNETGETPROXYINFO pInternetGetProxyInfo = NULL;
443  static INTERNETINITIALIZEAUTOPROXYDLL pInternetInitializeAutoProxyDll = NULL;
444  URL_INFO urlInfo;
445  char urlHost[ MAX_DNS_SIZE + 8 ];
446  char *proxyHost = NULL;
447  int proxyHostLen, status;
448 
449  assert( isWritePtr( proxy, proxyMaxLen ) );
450  assert( isReadPtr( url, urlLen ) );
451 
452  REQUIRES( proxyMaxLen > 10 && proxyMaxLen < MAX_INTLENGTH );
453  REQUIRES( urlLen > 0 && urlLen < MAX_INTLENGTH );
454 
455  /* This gets somewhat complicated, under Win2K SP3 and XP and newer (or
456  at least Windows versions with WinHTTP 5.1 installed in some way, it
457  officially shipped with the versions mentioned earlier) we can use
458  WinHTTP AutoProxy support, which implements the Web Proxy Auto-
459  Discovery (WPAD) protocol from an internet draft that expired in May
460  2001. Under older versions of Windows we have to use the WinINet
461  InternetGetProxyInfo.
462 
463  These functions were never meant to be used by the general public
464  (see the comment below) so they work in an extremely peculiar way
465  and only with the exact calling sequence that's used by MS code - it
466  looks like they were only intended as components of Windows-internal
467  implementation of proxy support since they require manual handling
468  of proxy config script downloading, parsing, and all manner of other
469  stuff that really doesn't concern us. Because of the extreme
470  difficulty in doing anything with these functions we use the WinHTTP
471  approach instead */
472  if( pInternetGetProxyInfo == NULL )
473  {
474  HMODULE hModJS;
475 
476  if( ( hModJS = DynamicLoad( "JSProxy.dll" ) ) == NULL )
477  return( CRYPT_ERROR_NOTFOUND );
478 
479  pInternetGetProxyInfo = ( INTERNETGETPROXYINFO ) \
480  GetProcAddress( hModJS, "InternetGetProxyInfo" );
481  pInternetInitializeAutoProxyDll = ( INTERNETINITIALIZEAUTOPROXYDLL ) \
482  GetProcAddress( hModJS, "InternetInitializeAutoProxyDll" );
483  if( pInternetGetProxyInfo == NULL || \
484  pInternetInitializeAutoProxyDll == NULL )
485  {
486  DynamicUnload( hModJS );
487  return( CRYPT_ERROR_NOTFOUND );
488  }
489 
490  pInternetInitializeAutoProxyDll( 0, TempFile, NULL,
491  &HelperFunctions, NULL )
492  }
493 
494  /* InternetGetProxyInfo() is a somewhat screwball undocumented function
495  that was crowbarred out of MS as part of the DoJ consent decree. It
496  takes as input four parameters that do the work of a single
497  parameter, the null-terminated target URL string. The documentation
498  for the function was initially wrong but has since been partially
499  corrected in places after user complaints, although there are still
500  missing parts as well as possible errors (why is it necessary to
501  specify a length for a supposedly null-terminated string?). In order
502  to meet the strange input-parameter requirements we have to pre-
503  parse the target URL in order to provide the various bits and pieces
504  that InternetGetProxyInfo() requires */
505  status = parseURL( &urlInfo, url, strlen( url ), 80, URL_TYPE_HTTP );
506  if( cryptStatusError( status ) )
507  return( status );
508  if( urlInfo.hostLen > MAX_DNS_SIZE )
509  return( CRYPT_ERROR_OVERFLOW );
510  memcpy( urlHost, urlInfo.host, urlInfo.hostLen );
511  urlHost[ urlInfo.hostLen ] = '\0';
512  if( !pInternetGetProxyInfo( url, strlen( url ), urlHost, urlInfo.hostLen,
513  &proxyHost, &proxyHostLen ) )
514  return( CRYPT_ERROR_NOTFOUND );
515  memcpy( proxy, proxyHost, proxyHostLen );
516  proxy[ proxyHostLen ] = '\0';
517  GlobalFree( proxyHost );
518  return( CRYPT_OK );
519  }
520 #endif /* 0 */
521 
522 #endif /* Win32 */
523 
524 #endif /* USE_TCP */