cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
http_rd.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib HTTP Read Routines *
4 * Copyright Peter Gutmann 1998-2007 *
5 * *
6 ****************************************************************************/
7 
8 #include <ctype.h>
9 #include <stdio.h>
10 #if defined( INC_ALL )
11  #include "crypt.h"
12  #include "misc_rw.h"
13  #include "http.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/misc_rw.h"
17  #include "io/http.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_HTTP
21 
22 /* HTTP request line parsing information */
23 
24 typedef struct {
25  BUFFER_FIXED( reqNameLen ) \
26  const char *reqName; /* Request name */
27  int reqNameLen; /* Length of request name */
28  STREAM_HTTPREQTYPE_TYPE reqType; /* Request type */
29  int reqTypeFlag; /* Stream flag for this request type */
30  } HTTP_REQUEST_INFO;
31 
32 static const HTTP_REQUEST_INFO FAR_BSS httpReqInfo[] = {
35  { NULL, 0, 0 }, { NULL, 0, 0 }
36  };
37 
38 /****************************************************************************
39 * *
40 * Utility Functions *
41 * *
42 ****************************************************************************/
43 
44 /* Callback function used by readTextLine() to read characters from a
45  stream. When reading text data over a network we don't know how much
46  more data is to come so we have to read a byte at a time looking for an
47  EOL. In addition we can't use the simple optimisation of reading two
48  bytes at a time because some servers only send a LF even though the spec
49  requires a CRLF. This is horribly inefficient but is pretty much
50  eliminated through the use of opportunistic read-ahead buffering */
51 
53 static int readCharFunction( INOUT TYPECAST( STREAM * ) void *streamPtr )
54  {
56  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
57  BYTE ch;
58  int length, status;
59 
60  assert( isWritePtr( stream, sizeof( STREAM ) ) );
61 
62  status = netStream->bufferedTransportReadFunction( stream, &ch, 1,
63  &length,
65  return( cryptStatusError( status ) ? status : ch );
66  }
67 
68 /****************************************************************************
69 * *
70 * Read Request Header *
71 * *
72 ****************************************************************************/
73 
74 /* Read an HTTP request header */
75 
76 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
77 static int readRequestHeader( INOUT STREAM *stream,
78  OUT_BUFFER_FIXED( lineBufSize ) char *lineBuffer,
79  IN_LENGTH_SHORT_MIN( 256 ) const int lineBufSize,
80  INOUT HTTP_DATA_INFO *httpDataInfo,
81  OUT_FLAGS_Z( HTTP ) int *flags )
82  {
83  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
84  HTTP_HEADER_INFO headerInfo;
85  HTTP_URI_INFO *uriInfo = httpDataInfo->reqInfo;
87  BOOLEAN isTextDataError;
88  char *bufPtr;
89  int length, offset, reqNameLen = DUMMY_INIT, i, status;
90 
91  assert( isWritePtr( stream, sizeof( STREAM ) ) );
92  assert( isWritePtr( lineBuffer, lineBufSize ) );
93  assert( isWritePtr( httpDataInfo, sizeof( HTTP_DATA_INFO ) ) );
94  assert( isWritePtr( flags, sizeof( int ) ) );
95 
96  REQUIRES( netStream->nFlags & STREAM_NFLAG_ISSERVER );
97  REQUIRES( lineBufSize >= 256 && lineBufSize < MAX_INTLENGTH_SHORT );
98 
99  /* Clear return value */
100  *flags = HTTP_FLAG_NONE;
101 
102  /* Read the header and check for "POST/GET x HTTP/1.x". In theory this
103  could be a bit risky because the original CERN server required an
104  extra (spurious) CRLF after a POST, so that various early clients sent
105  an extra CRLF that isn't included in the Content-Length header and
106  ends up preceding the start of the next load of data. We don't check
107  for this because it only applies to very old pure-HTTP (rather than
108  HTTP-as-a-transport-layer) clients, which are unlikely to be hitting a
109  PKI responder */
110  status = readTextLine( readCharFunction, stream, lineBuffer,
111  lineBufSize, &length, &isTextDataError );
112  if( cryptStatusError( status ) )
113  {
114  /* If it's an HTTP-level error (e.g. line too long), send back an
115  HTTP-level error response */
117  {
118  sendHTTPError( stream, lineBuffer, lineBufSize,
119  ( status == CRYPT_ERROR_OVERFLOW ) ? \
120  414 : 400 );
121  }
122 
123  return( retTextLineError( stream, status, isTextDataError,
124  "Invalid HTTP request header line 1: ",
125  0 ) );
126  }
127  for( i = 0; httpReqInfo[ i ].reqName != NULL && \
128  i < FAILSAFE_ARRAYSIZE( httpReqInfo, HTTP_REQUEST_INFO );
129  i++ )
130  {
131  const HTTP_REQUEST_INFO *reqInfoPtr = &httpReqInfo[ i ];
132 
133  if( ( reqInfoPtr->reqTypeFlag & netStream->nFlags ) && \
134  length >= reqInfoPtr->reqNameLen && \
135  !strCompare( lineBuffer, reqInfoPtr->reqName, \
136  reqInfoPtr->reqNameLen ) )
137  {
138  reqType = reqInfoPtr->reqType;
139  reqNameLen = reqInfoPtr->reqNameLen;
140  break;
141  }
142  }
143  ENSURES( i < FAILSAFE_ARRAYSIZE( httpReqInfo, HTTP_REQUEST_INFO ) );
144  if( reqType == STREAM_HTTPREQTYPE_NONE )
145  {
146  char reqNameBuffer[ 16 + 8 ];
147 
148  /* Return the extended error information */
149  if( length <= 0 )
150  {
151  sendHTTPError( stream, lineBuffer, lineBufSize, 501 );
154  "Invalid empty HTTP request" ) );
155  }
156  if( ( offset = strSkipNonWhitespace( lineBuffer, length ) ) > 0 )
157  length = offset;
158  memcpy( reqNameBuffer, lineBuffer, min( 16, length ) );
159  sendHTTPError( stream, lineBuffer, lineBufSize, 501 );
162  "Invalid HTTP request '%s'",
163  sanitiseString( reqNameBuffer, 16, length ) ) );
164  }
165  bufPtr = lineBuffer + reqNameLen;
166  length -= reqNameLen;
167 
168  /* Process the ' '* * ' '* and check for the HTTP ID */
169  if( length <= 0 || ( offset = strSkipWhitespace( bufPtr, length ) ) < 0 )
170  {
171  sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
174  "Missing HTTP request URI" ) );
175  }
176  bufPtr += offset;
177  length -= offset;
178  if( reqType == STREAM_HTTPREQTYPE_GET )
179  {
180  /* Safety check, make sure that we can handle the HTTP GET */
181  REQUIRES( uriInfo != NULL );
182 
183  /* If it's an indempotent read the client is sending a GET rather
184  than submitting a POST, process the request details. This
185  performs in-place decoding of (possibly encoded) data, so it
186  returns two length values, the new length after the in-place
187  decoding has occurred, and the offset of the next character of
188  data as usual */
189  offset = parseUriInfo( bufPtr, length, &length, uriInfo );
190  }
191  else
192  {
193  /* For non-idempotent queries we don't care what the location is
194  since it's not relevant for anything, so we just skip the URI.
195  This also avoids complications with absolute vs. relative URLs,
196  character encoding/escape sequences, and so on */
197  offset = strSkipNonWhitespace( bufPtr, length );
198  }
199  if( cryptStatusError( offset ) )
200  {
201  sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
204  "Invalid HTTP GET request URI" ) );
205  }
206  bufPtr += offset;
207  length -= offset;
208  if( length <= 0 || ( offset = strSkipWhitespace( bufPtr, length ) ) < 0 )
209  {
210  sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
213  "Missing HTTP request ID/version" ) );
214  }
215  bufPtr += offset;
216  length -= offset;
217  if( length <= 0 || \
218  cryptStatusError( checkHTTPID( bufPtr, length, stream ) ) )
219  {
220  sendHTTPError( stream, lineBuffer, lineBufSize, 505 );
223  "Invalid HTTP request ID/version" ) );
224  }
225 
226  /* Process the remaining header lines. ~32 bytes is the minimum-size
227  object that can be returned from any HTTP-based message which is
228  exchanged by cryptlib, this being a TSP request */
229  initHeaderInfo( &headerInfo, 32, httpDataInfo->bufSize, *flags );
230  if( reqType == STREAM_HTTPREQTYPE_GET )
231  {
232  /* It's an HTTP get, make sure that we don't try and read a body */
233  headerInfo.flags |= HTTP_FLAG_GET;
234  }
235  status = readHeaderLines( stream, lineBuffer, lineBufSize,
236  &headerInfo );
237  if( cryptStatusError( status ) )
238  {
239  /* We always (try and) send an HTTP error response once we get to
240  this stage since chances are that it'll be a problem with an
241  HTTP header rather than a low-level network read problem */
242  sendHTTPError( stream, lineBuffer, lineBufSize,
243  headerInfo.httpStatus );
244  return( status );
245  }
246 
247  /* Copy any status info back to the caller */
248  httpDataInfo->reqType = reqType;
249  if( reqType != STREAM_HTTPREQTYPE_GET )
250  httpDataInfo->bytesAvail = headerInfo.contentLength;
251  *flags = headerInfo.flags;
252 
253  return( CRYPT_OK );
254  }
255 
256 /****************************************************************************
257 * *
258 * Read Response Header *
259 * *
260 ****************************************************************************/
261 
262 /* Read an HTTP response header */
263 
264 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
265 static int readResponseHeader( INOUT STREAM *stream,
266  OUT_BUFFER_FIXED( lineBufSize ) char *lineBuffer,
267  IN_LENGTH_SHORT_MIN( 256 ) const int lineBufSize,
268  INOUT HTTP_DATA_INFO *httpDataInfo,
269  OUT_FLAGS_Z( HTTP ) int *flags )
270  {
271  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
272  int repeatCount, status;
273 
274  assert( isWritePtr( stream, sizeof( STREAM ) ) );
275  assert( isWritePtr( lineBuffer, lineBufSize ) );
276  assert( isWritePtr( httpDataInfo, sizeof( HTTP_DATA_INFO ) ) );
277  assert( isWritePtr( flags, sizeof( int ) ) );
278 
279  REQUIRES( lineBufSize >= 256 && lineBufSize < MAX_INTLENGTH_SHORT );
280 
281  /* Clear return value */
282  *flags = HTTP_FLAG_NONE;
283 
284  /* If it's a stateless HTTP read, we need to first send the initiating
285  HTTP GET request before we can read anything back */
286  if( netStream->nFlags & STREAM_NFLAG_HTTPGET )
287  {
288  status = writeRequestHeader( stream, httpDataInfo->reqInfo,
289  NULL, 0, 0, FALSE );
290  if( cryptStatusError( status ) )
291  return( status );
292  }
293 
294  /* Read the returned response header from the server, taking various
295  special-case conditions into account. In theory we could also handle
296  the 503 "Retry-After" status, but there's no sensible reason why
297  anyone should send us this, and even if they do it'll screw up a lot
298  of the PKI protocols, which have timeliness constraints built in */
299  for( repeatCount = 0; repeatCount < FAILSAFE_ITERATIONS_SMALL; \
300  repeatCount++ )
301  {
302  HTTP_HEADER_INFO headerInfo;
303  BOOLEAN needsSpecialHandling = FALSE, isSoftError = FALSE;
304  int httpStatus;
305 
306  /* Read the response header */
307  status = readFirstHeaderLine( stream, lineBuffer, lineBufSize,
308  &httpStatus );
309  if( cryptStatusError( status ) )
310  {
311  /* Some errors like an HTTP 404 aren't necessarily fatal in the
312  same way as (say) a CRYPT_ERROR_BADDATA because while the
313  latter means that the stream has been corrupted and we can't
314  continue, the former merely means that the requested item
315  wasn't found but we can still submit further requests. If
316  the caller has indicated that they want certain errors to be
317  treated as nonfatal, continue processing the stream and then
318  report the error later */
319  if( httpDataInfo->softErrors && httpStatus == 404 )
320  isSoftError = TRUE;
321 
322  if( status != OK_SPECIAL && !isSoftError )
323  return( status );
324 
325  /* It's a special-case header (e.g. a 100 Continue), turn the
326  read into a no-op read that drains the input to get to the
327  real data */
328  *flags |= HTTP_FLAG_NOOP;
329  needsSpecialHandling = TRUE;
330  }
331 
332  /* Process the remaining header lines. 5 bytes is the minimum-size
333  object that can be returned from any HTTP-based message which is
334  exchanged by cryptlib, this being an OCSP response containing a
335  single-byte status value, i.e. SEQUENCE { ENUM x }.
336 
337  If the read buffer is dynamically allocated then we allow an
338  effectively arbitrary content length, otherwise it has to fit
339  into the fixed-size read buffer. Unfortunately since CRLs can
340  reach > 100MB in size it's not really possible to provide any
341  sensible limit on the length for dynamic-buffer reads, however
342  to avoid DoS issues we limit it to 8MB until someone complains
343  that they can't read the 150MB CRLs that their CA is issuing
344  (yes, there are CAs that are issuing 150MB CRLs) */
345  initHeaderInfo( &headerInfo, 5,
346  httpDataInfo->bufferResize ? \
347  min( MAX_INTLENGTH, 8388608L ) : \
348  httpDataInfo->bufSize,
349  *flags );
350  status = readHeaderLines( stream, lineBuffer, lineBufSize,
351  &headerInfo );
352  if( cryptStatusError( status ) )
353  return( status );
354 
355  /* Copy any status info back to the caller */
356  *flags = headerInfo.flags & ~HTTP_FLAG_NOOP;
357  httpDataInfo->bytesAvail = headerInfo.contentLength;
358 
359  /* If this was a soft error due to not finding the requested item,
360  pass the status on to the caller. The low-level error
361  information will still be present from readFirstHeaderLine() */
362  if( isSoftError )
363  return( CRYPT_ERROR_NOTFOUND );
364 
365  /* If it's not something like a redirect that needs special-case
366  handling, we're done */
367  if( !needsSpecialHandling )
368  return( CRYPT_OK );
369 
370  REQUIRES( httpStatus == 100 || httpStatus == 301 || \
371  httpStatus == 302 || httpStatus == 307 );
372 
373  /* If we got a 100 Continue response, try for another header that
374  follows the first one */
375  if( httpStatus == 100 )
376  continue;
377 
378  /* If we got a 301, 302, or 307 Redirect then in theory we should
379  proceed roughly as per the code below, however in practice it's
380  not nearly as simple as this, because what we're in effect doing
381  is taking a stream and replacing it with a completely new stream
382  (different host/abs-path/query info, new socket with optional
383  proxy handling, etc etc). One way to do this would be to read
384  the new location into the current stream buffer and pass it back
385  with a special status telling the stream-level code to create a
386  new stream, clean up the old one, and perform a deep copy of the
387  new stream over to the old one. We'll leave this for a time when
388  it's really needed.
389 
390  In addition the semantics of the following pseudocode don't quite
391  match those of RFC 2616 because of the HTTP-as-a-substrate use
392  rather than direct use in a browser. Specifically, anything
393  other than a GET for a 302 or 307 isn't supposed to perform an
394  automatic redirect without asking the user, because of concerns
395  that it'll change the semantics of the request. However since
396  we're not an interactive web browser there's no way that we can
397  ask a user for redirect permission, and in any case since we're
398  merely using HTTP as a substrate for a cryptographically
399  protected PKI message (and specifically assuming that the HTTP
400  layer is completely insecure), any problems will be caught by the
401  crypto protocol layer */
402 #if 0
403  if( !*location )
404  return( CRYPT_ERROR_READ );
405  netStream->closeSocketFunction( stream );
406  clFree( "readResponseHeader", netStream->host );
407  netStream->host = NULL;
408  status = parseLocation( stream, location );
409  if( cryptStatusError( status ) )
410  return( CRYPT_ERROR_READ );
411 #endif /* 0 */
414  "Unable to process HTTP %d redirect", httpStatus ) );
415  }
416 
417  /* We used up our maximum number of retries, bail out */
420  "HTTP retry/redirection loop detected" ) );
421  }
422 
423 /****************************************************************************
424 * *
425 * HTTP Access Functions *
426 * *
427 ****************************************************************************/
428 
429 /* Read data from an HTTP stream */
430 
431 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
432 static int readFunction( INOUT STREAM *stream,
433  OUT_BUFFER( maxLength, *length ) void *buffer,
434  IN_LENGTH_FIXED( sizeof( HTTP_DATA_INFO ) ) \
435  const int maxLength,
436  OUT_LENGTH_Z int *length )
437  {
438  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
439  HTTP_DATA_INFO *httpDataInfo = ( HTTP_DATA_INFO * ) buffer;
440  char headerBuffer[ HTTP_LINEBUF_SIZE + 8 ];
441  int flags = HTTP_FLAG_NONE, status;
442 
443  assert( isWritePtr( stream, sizeof( STREAM ) ) );
444  assert( isReadPtr( buffer, maxLength ) );
445  assert( isWritePtr( length, sizeof( int ) ) );
446 
447  REQUIRES( maxLength == sizeof( HTTP_DATA_INFO ) );
448 
449  /* Clear return value */
450  *length = 0;
451 
452  /* Read the HTTP packet header */
453  if( netStream->nFlags & STREAM_NFLAG_ISSERVER )
454  {
455  assert( !( netStream->nFlags & STREAM_NFLAG_HTTPGET ) || \
456  isWritePtr( httpDataInfo->reqInfo, \
457  sizeof( HTTP_URI_INFO ) ) );
458 
459  status = readRequestHeader( stream, headerBuffer, HTTP_LINEBUF_SIZE,
460  httpDataInfo, &flags );
461  }
462  else
463  {
464  status = readResponseHeader( stream, headerBuffer, HTTP_LINEBUF_SIZE,
465  httpDataInfo, &flags );
466  if( cryptStatusOK( status ) && \
467  httpDataInfo->bytesAvail > httpDataInfo->bufSize )
468  {
469  void *newBuffer;
470 
471  REQUIRES( httpDataInfo->bytesAvail > 0 && \
472  httpDataInfo->bytesAvail < MAX_INTLENGTH );
473 
474  /* readResponseHeader() will only allow content larger than the
475  buffer size if it's marked as a resizeable buffer */
476  REQUIRES( httpDataInfo->bufferResize );
477 
478  /* Adjust the read buffer size to handle the extra data and
479  record the details of the resized buffer */
480  if( ( newBuffer = clAlloc( "readFunction", \
481  httpDataInfo->bytesAvail ) ) == NULL )
482  return( CRYPT_ERROR_MEMORY );
483  zeroise( httpDataInfo->buffer, httpDataInfo->bufSize );
484  clFree( "readFunction", httpDataInfo->buffer );
485  httpDataInfo->buffer = newBuffer;
486  httpDataInfo->bufSize = httpDataInfo->bytesAvail;
487  }
488  else
489  {
490  /* We didn't dynically resize the buffer, let the caller know */
491  httpDataInfo->bufferResize = FALSE;
492  }
493  }
494  if( cryptStatusError( status ) )
495  return( status );
496  ENSURES( httpDataInfo->bytesAvail <= httpDataInfo->bufSize );
497 
498  REQUIRES( !( netStream->nFlags & STREAM_NFLAG_ISSERVER ) || \
499  ( httpDataInfo->reqType != STREAM_HTTPREQTYPE_NONE ) );
500 
501  /* If we're the server and the client sends us an HTTP GET, all of the
502  information was contained in the header and we're done */
503  if( ( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
504  ( httpDataInfo->reqType == STREAM_HTTPREQTYPE_GET ) )
505  {
506  *length = maxLength;
507  return( CRYPT_OK );
508  }
509 
510  /* Read the payload data from the client/server */
511  status = netStream->bufferedTransportReadFunction( stream,
512  httpDataInfo->buffer, httpDataInfo->bytesAvail,
513  &httpDataInfo->bytesTransferred,
515  if( cryptStatusError( status ) )
516  return( status );
517  if( httpDataInfo->bytesTransferred < httpDataInfo->bytesAvail )
518  {
519  /* We timed out before reading all of the data. Usually this will
520  be reported as a CRYPT_ERROR_TIMEOUT by the lower-level read
521  routines, however due to the multiple layers of I/O and special
522  case timeout handling when (for example) a cryptlib transport
523  session is layered over the network I/O layer, we perform an
524  explicit check here to make sure that we got everything */
527  "HTTP read timed out before all data could be read, only "
528  "got %d of %d bytes", httpDataInfo->bytesTransferred,
529  httpDataInfo->bytesAvail ) );
530  }
531 
532  /* If it's a plain-text error message, return it to the caller */
533  if( flags & HTTP_FLAG_TEXTMSG )
534  {
535  BYTE *byteBufPtr = httpDataInfo->buffer;
536 
537  /* Usually a body returned as plain text is an error message that
538  (for some reason) is sent as content rather than an HTTP error,
539  however in some unusual cases the content will be the requested
540  object marked as plain text. We try and filter out genuine PKI
541  data erroneously marked as text by requiring that the request is
542  over a minimum size (most error messages are quite short) and
543  that the first bytes match what would be seen in a PKI object
544  such as a cert or CRL */
545  if( httpDataInfo->bytesAvail < 256 || ( byteBufPtr[ 0 ] != 0x30 ) || \
546  !( byteBufPtr[ 1 ] & 0x80 ) || \
547  ( isAlpha( byteBufPtr[ 2 ] ) && isAlpha( byteBufPtr[ 3 ] ) && \
548  isAlpha( byteBufPtr[ 4 ] ) ) )
549  {
552  "HTTP server reported: '%s'",
553  sanitiseString( byteBufPtr, \
554  httpDataInfo->bufSize,
555  min( httpDataInfo->bytesTransferred, \
556  MAX_ERRMSG_SIZE - 32 ) ) ) );
557  }
558  }
559 
560  /* If we're reading chunked data, drain the input by processing the
561  trailer. The reason why there can be extra header lines at the end
562  of the chunked data is because it's designed to be an indefinite-
563  length streamable format that doesn't require buffering the entire
564  message before emitting it. Since some header information may not be
565  available until the entire message has been generated, the HTTP spec.
566  makes provisions for adding further header lines as a trailer. In
567  theory we should check for the HTTP_FLAG_TRAILER flag before reading
568  trailer lines rather than just swallowing the last CRLF, however the
569  "Trailer:" header wasn't added until RFC 2616 (RFC 2068 didn't have
570  it) so we can't rely on its presence. Normally we wouldn't have to
571  worry about trailer data, but if it's an HTTP 1.1 persistent
572  connection we then need to clear the way for the next lot of data */
573  if( flags & HTTP_FLAG_CHUNKED )
574  {
575  status = readTrailerLines( stream, headerBuffer,
576  HTTP_LINEBUF_SIZE );
577  if( cryptStatusError( status ) )
578  return( status );
579  }
580 
581  *length = maxLength;
582  return( CRYPT_OK );
583  }
584 
585 STDC_NONNULL_ARG( ( 1 ) ) \
586 void setStreamLayerHTTP( INOUT NET_STREAM_INFO *netStream )
587  {
588  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
589 
590  /* Set the access method pointers */
591  netStream->readFunction = readFunction;
592  setStreamLayerHTTPwrite( netStream );
593 
594  /* The default HTTP operation type is POST, since in most cases it's
595  being used as a substrate by a PKI protocol */
596  netStream->nFlags |= STREAM_NFLAG_HTTPPOST;
597 
598  /* HTTP provides its own data-size and flow-control indicators so we
599  don't want the higher-level code to try and do this for us */
600  netStream->nFlags |= STREAM_NFLAG_ENCAPS;
601  }
602 #endif /* USE_HTTP */