cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
net_trans.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Network Stream Transport Functions *
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 /* Network streams can work on multiple levels. At the lowest level we have
17  the raw network I/O layer, handled by calling setAccessMethodXXX(), which
18  hooks up the transport-level I/O functions. If there's a requirement to
19  replace the built-in network I/O it can be done by replacing the
20  functionality at this level.
21 
22  Layered on top of the transport-level I/O via setStreamLayerXXX() is an
23  optional higher layer protocol such as HTTP which is added by calling
24  the appropriate function to layer the higher-level protocol over the
25  transport-level I/O. Alternatively, we can use setStreamLayerDirect()
26  to just pass the call straight down to the transport layer.
27 
28  In addition to these two layers the higher level read requires an extra
29  buffering layer in order to avoid making repeated calls to the transport-
30  level I/O function, which is a particular problem for HTTP which has to
31  take input a character at a time. To avoid this issue we use the
32  bufferedRead layer which reads ahead as far as it can and then feeds the
33  buffered result back to the caller as required. We also need to use write
34  buffering to avoid potential problems with interactions with some
35  transport layers, details are given in the comment for the buffered write
36  function.
37 
38  The layering looks as follows:
39 
40  --- httpRead ---+-- bufferedRead ---+--- tcpRead
41  cmpRead |
42  +--- clibRead
43  |
44  ------------------------------------+--- otherRead
45 
46  --- httpWrite --+-- bufferedWrite --+---- tcpWrite
47  cmpWrite |
48  +---- clibWrite
49  |
50  ------------------------------------+---- otherWrite */
51 
52 /****************************************************************************
53 * *
54 * Utility Functions *
55 * *
56 ****************************************************************************/
57 
58 /* Sanity-check the stream state */
59 
61 static BOOLEAN sanityCheckFunction( const STREAM *stream )
62  {
63  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
64 
65  assert( isReadPtr( stream, sizeof( STREAM ) ) );
66  assert( isReadPtr( netStream, sizeof( NET_STREAM_INFO ) ) );
67 
68  REQUIRES_B( netStream != NULL );
69 
70  /* Make sure that it's an initialised network stream with valid
71  parameters */
72  if( stream->type != STREAM_TYPE_NETWORK || \
73  stream->netStreamInfo == NULL )
74  return( FALSE );
75  if( netStream->timeout < 0 || netStream->timeout > 300 )
76  return( FALSE );
77 
78  /* It's an unbuffered network stream, all buffer values must be zero */
79  if( stream->buffer != NULL || stream->bufPos != 0 || \
80  stream->bufSize != 0 || stream->bufEnd != 0 )
81  return( FALSE );
82  if( netStream->writeBuffer != NULL || netStream->writeBufSize != 0 || \
83  netStream->writeBufEnd != 0 )
84  return( FALSE );
85 
86  return( TRUE );
87  }
88 
90 static BOOLEAN sanityCheckBufferedFunction( const STREAM *stream )
91  {
92  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
93 
94  assert( isReadPtr( stream, sizeof( STREAM ) ) );
95  assert( isReadPtr( netStream, sizeof( NET_STREAM_INFO ) ) );
96 
97  REQUIRES_B( netStream != NULL );
98 
99  /* Make sure that it's an initialised network stream with valid
100  parameters */
101  if( stream->type != STREAM_TYPE_NETWORK || \
102  stream->netStreamInfo == NULL )
103  return( FALSE );
104  if( netStream->timeout < 0 || netStream->timeout > 300 )
105  return( FALSE );
106 
107  /* Make sure that the buffer position is within bounds:
108 
109  bufSize
110  |
111  <------ buffer ------> v
112  +---------------------------+
113  | | |
114  +---------------------------+
115  ^ ^
116  | |
117  bufPos bufEnd */
118  if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd || \
119  stream->bufEnd < 0 || stream->bufEnd > stream->bufSize || \
120  stream->bufSize <= 0 || stream->bufSize >= MAX_INTLENGTH )
121  return( FALSE );
122 
123  /* Network streams have a second buffer used for writes, make sure that
124  the write buffer position is within bounds */
125  if( netStream->writeBuffer == NULL || \
126  netStream->writeBufSize <= 0 || \
127  netStream->writeBufSize >= MAX_INTLENGTH )
128  return( FALSE );
129  if( netStream->writeBufEnd < 0 || \
130  netStream->writeBufEnd > netStream->writeBufSize )
131  return( FALSE );
132 
133  return( TRUE );
134  }
135 
136 /****************************************************************************
137 * *
138 * Transport-layer Direct Access Functions *
139 * *
140 ****************************************************************************/
141 
142 /* Map the upper-layer I/O functions directly to the transport-layer
143  equivalent. This is used if we're performing raw I/O without any
144  intermediate protocol layers or buffering */
145 
146 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
147 static int transportDirectReadFunction( INOUT STREAM *stream,
149  void *buffer,
150  IN_LENGTH const int maxLength,
151  OUT_LENGTH_Z int *length )
152  {
153  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
154 
155  assert( isWritePtr( stream, sizeof( STREAM ) ) );
156  assert( isWritePtr( buffer, maxLength ) );
157  assert( isWritePtr( length, sizeof( int ) ) );
158  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
159 
160  REQUIRES_S( netStream != NULL );
161  REQUIRES_S( netStream->sanityCheckFunction( stream ) );
162  REQUIRES_S( maxLength > 0 && maxLength < MAX_INTLENGTH );
163 
164  return( netStream->transportReadFunction( stream, buffer, maxLength,
165  length,
167  }
168 
169 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
170 static int transportDirectWriteFunction( INOUT STREAM *stream,
171  IN_BUFFER( maxLength ) const void *buffer,
172  IN_LENGTH const int maxLength,
173  OUT_LENGTH_Z int *length )
174  {
175  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
176 
177  assert( isWritePtr( stream, sizeof( STREAM ) ) );
178  assert( isReadPtr( buffer, maxLength ) );
179  assert( isWritePtr( length, sizeof( int ) ) );
180  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
181 
182  REQUIRES_S( netStream != NULL );
183  REQUIRES_S( netStream->sanityCheckFunction( stream ) );
184  REQUIRES_S( maxLength > 0 && maxLength < MAX_INTLENGTH );
185 
186  return( netStream->transportWriteFunction( stream, buffer, maxLength,
187  length,
189  }
190 
191 STDC_NONNULL_ARG( ( 1 ) ) \
192 void setStreamLayerDirect( INOUT NET_STREAM_INFO *netStream )
193  {
194  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
195 
196  netStream->writeFunction = transportDirectWriteFunction;
197  netStream->readFunction = transportDirectReadFunction;
198  }
199 
200 /****************************************************************************
201 * *
202 * Transport-layer Session Access Functions *
203 * *
204 ****************************************************************************/
205 
206 /* This facility is currently unused so we disable it to avoid inadvertent
207  use by users who try to play with undocumented features. Note that when
208  enabling it it'll be necessary to change the annotation for the connect
209  function in stream.h since the parameters are currently marked as
210  STDC_NONNULL_ARG */
211 
212 #if 0
213 
214 /* Send and receive data with a cryptlib session as the transport layer */
215 
216 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
217 static int transportSessionConnectFunction( INOUT STREAM *stream,
218  STDC_UNUSED const char *host,
219  STDC_UNUSED const int hostLen,
220  STDC_UNUSED const int port )
221  {
222  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
223  int isActive, status;
224 
225  assert( isWritePtr( stream, sizeof( STREAM ) ) );
226  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
227 
228  REQUIRES_S( netStream != NULL );
229  REQUIRES_S( netStream->sanityCheckFunction( stream ) );
230  REQUIRES_S( host == NULL && hostLen == 0 );
231  REQUIRES_S( port == 0 );
232 
233  /* If the transport session hasn't been activated yet, activate it now */
234  status = krnlSendMessage( netStream->iTransportSession,
235  IMESSAGE_GETATTRIBUTE, &isActive,
237  if( cryptStatusOK( status ) && isActive )
238  {
239  /* The session has been activated, there's nothing to do */
240  return( CRYPT_OK );
241  }
242  status = krnlSendMessage( netStream->iTransportSession,
245  if( cryptStatusError( status ) )
246  return( getSessionErrorInfo( stream, status ) );
247  return( CRYPT_OK );
248  }
249 
250 STDC_NONNULL_ARG( ( 1 ) ) \
251 static void transportSessionDisconnectFunction( INOUT STREAM *stream,
252  const BOOLEAN fullDisconnect )
253  {
254  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
255 
256  assert( isWritePtr( stream, sizeof( STREAM ) ) );
257  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
258 
259  REQUIRES_V( netStream != NULL );
260  REQUIRES_V( netStream->sanityCheckFunction( stream ) );
261 
262  krnlSendNotifier( netStream->iTransportSession, IMESSAGE_DECREFCOUNT );
263  }
264 
265 CHECK_RETVAL_BOOL \
266 static BOOLEAN transportSessionOKFunction( void )
267  {
268  return( TRUE );
269  }
270 
271 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
272 static int transportSessionReadFunction( INOUT STREAM *stream,
273  OUT_BUFFER( maxLength, *length ) \
274  BYTE *buffer,
275  IN_LENGTH const int maxLength,
276  OUT_LENGTH_Z int *length,
277  IN_FLAGS_Z( TRANSPORT ) const int flags )
278  {
279  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
281  int newTimeout = CRYPT_UNUSED, status;
282 
283  assert( isWritePtr( stream, sizeof( STREAM ) ) );
284  assert( isWritePtr( buffer, maxLength ) );
285  assert( isWritePtr( length, sizeof( int ) ) );
286  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
287 
288  REQUIRES_S( netStream != NULL );
289  REQUIRES_S( stream->sanityCheckFunction( stream ) );
290  REQUIRES_S( maxLength > 0 && maxLength < MAX_INTLENGTH );
291  REQUIRES_S( flags >= TRANSPORT_FLAG_NONE && \
292  flags <= TRANSPORT_FLAG_MAX );
293 
294  /* Clear return value */
295  *length = 0;
296 
297  /* Read data from the session, overriding the timeout handling if
298  requested */
299  if( ( flags & TRANSPORT_FLAG_NONBLOCKING ) && netStream->timeout > 0 )
300  newTimeout = 0;
301  else
302  {
303  if( ( flags & TRANSPORT_FLAG_BLOCKING ) && netStream->timeout == 0 )
304  newTimeout = 30;
305  }
306  if( newTimeout != CRYPT_UNUSED )
307  ( void ) krnlSendMessage( netStream->iTransportSession,
308  IMESSAGE_SETATTRIBUTE, &newTimeout,
310  setMessageData( &msgData, buffer, maxLength );
311  status = krnlSendMessage( netStream->iTransportSession,
313  if( newTimeout != CRYPT_UNUSED )
314  ( void ) krnlSendMessage( netStream->iTransportSession,
315  IMESSAGE_SETATTRIBUTE, &stream->timeout,
317  if( cryptStatusError( status ) )
318  return( getSessionErrorInfo( stream, status ) );
319  if( msgData.length < maxLength )
320  {
323  "Only read %d out of %d bytes via cryptlib session "
324  "object", msgData.length, maxLength ) );
325  }
326  *length = maxLength;
327 
328  return( CRYPT_OK );
329  }
330 
331 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
332 static int transportSessionWriteFunction( INOUT STREAM *stream,
333  IN_BUFFER( length ) const BYTE *buffer,
334  IN_LENGTH const int maxLength,
335  OUT_LENGTH_Z int *length,
336  IN_FLAGS_Z( TRANSPORT ) \
337  const int flags )
338  {
339  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
341  int status;
342 
343  assert( isWritePtr( stream, sizeof( STREAM ) ) );
344  assert( isReadPtr( buffer, maxLength ) );
345  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
346 
347  REQUIRES_S( netStream != NULL );
348  REQUIRES_S( netStream->sanityCheckFunction( stream ) );
349  REQUIRES_S( maxLength > 0 && maxLength < MAX_INTLENGTH );
350  REQUIRES_S( flags >= TRANSPORT_FLAG_NONE && \
351  flags <= TRANSPORT_FLAG_MAX );
352 
353  /* Clear return value */
354  *length = 0;
355 
356  setMessageData( &msgData, ( MESSAGE_CAST ) buffer, maxLength );
357  status = krnlSendMessage( netStream->iTransportSession,
359  if( cryptStatusOK( status ) )
360  {
361  setMessageData( &msgData, NULL, 0 );
362  status = krnlSendMessage( netStream->iTransportSession,
364  }
365  if( cryptStatusError( status ) )
366  return( getSessionErrorInfo( stream, status ) );
367  *length = maxLength;
368 
369  return( CRYPT_OK );
370  }
371 
373  {
374  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
375 
376  assert( isWritePtr( stream, sizeof( STREAM ) ) );
377  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
378 
379  REQUIRES_V( netStream != NULL );
380  REQUIRES_V( stream->type == STREAM_TYPE_NETWORK );
381 
382  netStream->transportConnectFunction = transportSessionConnectFunction;
383  netStream->transportDisconnectFunction = transportSessionDisconnectFunction;
384  netStream->transportWriteFunction = transportSessionWriteFunction;
385  netStream->transportReadFunction = transportSessionReadFunction;
386  netStream->transportOKFunction = transportSessionOKFunction;
387  }
388 #endif /* 0 */
389 
390 /****************************************************************************
391 * *
392 * Buffering Functions *
393 * *
394 ****************************************************************************/
395 
396 /* Buffered transport-layer read function. This sits on top of the
397  transport-layer read function and performs speculative read-ahead
398  buffering to improve performance in protocols such as HTTP that have to
399  read a byte at a time in places:
400 
401  bPos bEnd
402  | |
403  v v
404  +-------+-----------+-------+
405  | |///////////| |
406  +-------+-----------+-------+
407  -- Read -->
408 
409  We fill the buffer to bEnd and then empty it by advancing bPos until
410  there isn't enough data left to satisfy the read, whereupon we move the
411  data down and refill from bEnd:
412 
413  bPos bEnd
414  | |
415  v v
416  +-----------+---------------+
417  |///////////| |
418  +-----------+---------------+
419  -- Write --> */
420 
421 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
422 static int bufferedTransportReadFunction( INOUT STREAM *stream,
423  OUT_BUFFER( maxLength, *length ) \
424  BYTE *buffer,
425  IN_LENGTH const int maxLength,
426  OUT_LENGTH_Z int *length,
427  IN_FLAGS_Z( TRANSPORT ) \
428  const int flags )
429  {
430  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
431  const int bytesLeft = stream->bufEnd - stream->bufPos;
432  int bufferBytesRead, bytesRead, status;
433 
434  assert( isWritePtr( stream, sizeof( STREAM ) ) );
435  assert( isWritePtr( buffer, maxLength ) );
436  assert( isWritePtr( length, sizeof( int ) ) );
437  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
438 
439  REQUIRES_S( netStream != NULL );
440  REQUIRES_S( netStream->sanityCheckFunction( stream ) );
441  REQUIRES_S( maxLength > 0 && maxLength < MAX_INTLENGTH );
442  REQUIRES_S( bytesLeft >= 0 && bytesLeft < MAX_INTLENGTH_SHORT );
443  REQUIRES_S( flags >= TRANSPORT_FLAG_NONE && \
444  flags <= TRANSPORT_FLAG_MAX );
445 
446  /* Clear return value */
447  *length = 0;
448 
449  /* If there's enough data in the buffer to satisfy the request, return it
450  directly */
451  if( maxLength <= bytesLeft )
452  {
453  if( maxLength == 1 )
454  {
455  /* Optimisation for char-at-a-time HTTP header reads */
456  *buffer = stream->buffer[ stream->bufPos++ ];
457  }
458  else
459  {
460  memcpy( buffer, stream->buffer + stream->bufPos, maxLength );
461  stream->bufPos += maxLength;
462  }
463  *length = maxLength;
464 
465  ENSURES_S( netStream->sanityCheckFunction( stream ) );
466 
467  return( CRYPT_OK );
468  }
469 
470  /* We're about to refill the buffer, if there's a gap at the start move
471  everything down to make room for the new data */
472  if( stream->bufPos > 0 )
473  {
474  if( bytesLeft > 0 )
475  {
476  REQUIRES_S( rangeCheck( stream->bufPos, bytesLeft,
477  stream->bufEnd ) );
478  memmove( stream->buffer, stream->buffer + stream->bufPos,
479  bytesLeft );
480  }
481  stream->bufEnd = bytesLeft;
482  stream->bufPos = 0;
483  }
484 
485  ENSURES_S( stream->bufPos == 0 );
486  ENSURES_S( maxLength > bytesLeft );
487 
488  /* If there's more room in the buffer, refill it */
489  if( stream->bufEnd < stream->bufSize )
490  {
491  int bytesToRead;
492 
493  /* Calculate how many bytes we still need to read from the network into
494  the buffer and how much room there is in it. If the read count is
495  less than the available buffer space we only read that much, any
496  further space will be filled (if possible) by the opportunistic
497  read that follows */
498  bytesToRead = stream->bufSize - stream->bufEnd;
499  if( bytesToRead > maxLength )
500  bytesToRead = maxLength;
501 
502  /* Perform an explicitly blocking read of as many bytes as we can/are
503  asked for. Since there may be data already present from an
504  earlier speculative read we only read as much as we actually need
505  in order to fulfill the request */
506  status = netStream->transportReadFunction( stream,
507  stream->buffer + stream->bufEnd,
508  bytesToRead, &bytesRead,
510  if( cryptStatusError( status ) )
511  return( status );
512  stream->bufEnd += bytesRead;
513 
514  /* If there's room for more, perform an opportunistic nonblocking
515  read for whatever might still be there. An error at this point
516  isn't fatal since this was only a speculative read */
517  if( stream->bufEnd < stream->bufSize )
518  {
519  status = netStream->transportReadFunction( stream,
520  stream->buffer + stream->bufEnd,
521  stream->bufSize - stream->bufEnd,
522  &bytesRead, TRANSPORT_FLAG_NONBLOCKING );
523  if( cryptStatusOK( status ) )
524  stream->bufEnd += bytesRead;
525  }
526  }
527  ENSURES_S( netStream->sanityCheckFunction( stream ) );
528 
529  /* Read as much as we can from the buffer */
530  bufferBytesRead = min( maxLength, stream->bufEnd );
531  memcpy( buffer, stream->buffer, bufferBytesRead );
532  stream->bufPos = bufferBytesRead;
533  *length = bufferBytesRead;
534 
535  /* If we could satisfy the entire read from the buffer, we're done */
536  if( maxLength <= bufferBytesRead ) /* Actually length == bufferBytesRead */
537  {
538  ENSURES_S( netStream->sanityCheckFunction( stream ) );
539 
540  return( CRYPT_OK );
541  }
542 
543  /* We've drained the stream buffer and there's more to go, read the
544  remainder directly into the caller's buffer. What to return in case
545  there's a failure at this point is a bit tricky since we can
546  successfully return some data from the internal buffer but then fail
547  when we try and replenish the buffer from the network. For now we
548  simply force the operation to be atomic since we're reading PKI
549  datagrams that have to be read in their entirety */
550  status = netStream->transportReadFunction( stream,
551  buffer + bufferBytesRead, maxLength - bufferBytesRead,
552  &bytesRead, TRANSPORT_FLAG_BLOCKING );
553  if( cryptStatusError( status ) )
554  return( status );
555  *length += bytesRead;
556 
557  ENSURES_S( netStream->sanityCheckFunction( stream ) );
558 
559  return( CRYPT_OK );
560  }
561 
562 /* Buffered transport-layer write function. This sits on top of the
563  transport-layer write function and combines two (or more, although in
564  practice only two ever occur) writes into a single write. The reason for
565  this is that when using TCP transport the delayed-ACK handling means
566  that performing two writes followed by a read (typical for HTTP and CMP
567  messages) leads to very poor performance, usually made even worse by TCP
568  slow-start.
569 
570  The reason for this is that the TCP MSS is typically 1460 bytes on a LAN
571  (Ethernet) or 512/536 bytes on a WAN while HTTP headers are ~200-300
572  bytes, far less than the MSS. When an HTTP message is first sent the TCP
573  congestion window begins at one segment with the TCP slow-start then
574  doubling its size for each ACK. Sending the headers separately will send
575  one short segment and a second MSS-size segment whereupon the TCP stack
576  will wait for the responder's ACK before continuing. The responder gets
577  both segments and then delays its ACK for 200ms in the hopes of
578  piggybacking it on responder data, which is never sent since it's still
579  waiting for the rest of the HTTP body from the initiator. This results
580  in a 200ms (+ assorted RTT) delay in each message sent.
581 
582  There's a somewhat related situation that occurs as a result of TCP slow-
583  start and that can't be avoided programmatically in which we can't send
584  more than a single request initially, however most BSD-derived
585  implementations set the server's congestion window to two segments in
586  response to receiving the TCP handshake ACK so for the initial message
587  exchange the client can send a request of 1MSS and the server a response
588  of 2MSS without running into congestion-control problems.
589 
590  A related problem is the fact that many TCP implementations will reset the
591  congestion window after one retransmission timeout period if all data sent
592  at that point has been acked, which means that both sides now restart with
593  a congestion window of size 1. Unfortunately there's nothing that can be
594  done about this however hopefully at some point TCP implementations will
595  start to fall into line with RFC 3390 and allow initial windows of ~4K,
596  which will fix this particular problem.
597 
598  There are other, non-portable workarounds for this as well but they're so
599  non-portable that they often don't even work across different versions of
600  the same OS (e.g. different versions of the Linux kernel) let alone
601  variants of one OS type (e.g. OpenBSD vs. FreeBSD). The least nonportable
602  one is using writev() to combine a seperate header and body, which exists
603  in most Unix versions and Win32. Easier-to-use but almost totally non-
604  portable are facilities like TCP_CORK (newer Linux kernels) and
605  TCP_NOPUSH (some *BSDs) which delay sending buffer contents until the
606  flag is reset again (so the use is "set TCP_CORK, write, write, write,
607  reset TCP_CORK"). Because all of these are far more trouble than they're
608  worth and in any case we're only sending very small data quantities via
609  these functions (PKI messages) we just assemble the whole datagram
610  ourselves, which works across all OSes */
611 
612 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
613 static int processIncompleteWrite( INOUT NET_STREAM_INFO *netStream,
614  IN_LENGTH const int bytesWritten,
615  IN_LENGTH_Z const int newDataToWrite,
616  OUT_LENGTH_Z int *newDataWritten )
617  {
618  const int bytesLeftToWrite = netStream->writeBufEnd - bytesWritten;
619 
620  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
621 
622  REQUIRES( bytesWritten > 0 && bytesWritten < netStream->writeBufEnd && \
623  bytesWritten < MAX_INTLENGTH );
624  REQUIRES( newDataToWrite >= 0 && newDataToWrite < MAX_INTLENGTH );
625  /* May be zero if the write buffer was already full */
626 
627  /* Clear return value */
628  *newDataWritten = 0;
629 
630  /* Determine how much was written from what the user gave us. This is
631  complicated by the fact that the write buffer may already contain
632  buffered data from a previous write so we want to report to the
633  caller only what was written from the new data that was supplied:
634 
635  |<-- newDataToWrite --->|
636  |<---------------------- bufEnd ------------------->|
637  +---------------------------+-----------------------+
638  | Existing data in buffer | New data copied in |
639  +---------------------------+-----------------------+
640  |<-- bytesWritten --> ........ <-- bytesLeftToWr -->|
641 
642  We can tell whether only existing data or newly-copied-in data was
643  written based on whether bytesLeftToWrite covers only the new data
644  or whether it reaches back into the existing data in the buffer. If
645  bytesLeftToWrite reaches back into the existing data then no new data
646  could be written */
647  if( bytesLeftToWrite < newDataToWrite )
648  *newDataWritten = newDataToWrite - bytesLeftToWrite;
649 
650  /* We couldn't write all of the data in the buffer, move what's left
651  down to the start. This shouldn't be needed since the caller will
652  convert the failure to write the full amount into a write timeout but
653  we do it anyway just to be neat */
654  REQUIRES( rangeCheck( bytesWritten, bytesLeftToWrite,
655  netStream->writeBufEnd ) );
656  memmove( netStream->writeBuffer, netStream->writeBuffer + bytesWritten,
657  bytesLeftToWrite );
658  netStream->writeBufEnd = bytesLeftToWrite;
659 
660  return( CRYPT_OK );
661  }
662 
663 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
664 static int bufferedTransportWriteFunction( INOUT STREAM *stream,
665  IN_BUFFER( maxLength ) const BYTE *buffer,
666  IN_LENGTH const int maxLength,
667  OUT_LENGTH_Z int *length,
668  IN_FLAGS_Z( TRANSPORT ) \
669  const int flags )
670  {
671  NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
672  const BYTE *bufPtr = buffer;
673  int byteCount = maxLength, bytesWritten, status;
674 
675  assert( isWritePtr( stream, sizeof( STREAM ) ) );
676  assert( isReadPtr( buffer, maxLength ) );
677  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
678 
679  REQUIRES_S( netStream != NULL );
680  REQUIRES_S( netStream->sanityCheckFunction( stream ) );
681  REQUIRES_S( maxLength > 0 && maxLength < MAX_INTLENGTH );
682  REQUIRES_S( flags >= TRANSPORT_FLAG_NONE && \
683  flags <= TRANSPORT_FLAG_MAX );
684 
685  /* Clear return value */
686  *length = 0;
687 
688  /* If it's not a flush and the buffer can absorb the data, copy it in and
689  exit */
690  if( !( flags & TRANSPORT_FLAG_FLUSH ) && \
691  netStream->writeBufEnd + byteCount <= netStream->writeBufSize )
692  {
693  memcpy( netStream->writeBuffer + netStream->writeBufEnd, buffer,
694  byteCount );
695  netStream->writeBufEnd += byteCount;
696  *length = byteCount;
697 
698  ENSURES_S( netStream->sanityCheckFunction( stream ) );
699 
700  return( CRYPT_OK );
701  }
702 
703  /* It's a flush or there's too much data to buffer, assemble a complete
704  buffer and write it */
705  if( netStream->writeBufEnd > 0 )
706  {
707  int bytesToCopy;
708 
709  /* Calculate how much data we can still add to the buffer. If the write
710  count is less than the available buffer size we only write that much */
711  bytesToCopy = netStream->writeBufSize - netStream->writeBufEnd;
712  if( bytesToCopy > byteCount )
713  bytesToCopy = byteCount;
714  if( bytesToCopy > 0 )
715  {
716  memcpy( netStream->writeBuffer + netStream->writeBufEnd, buffer,
717  bytesToCopy );
718  netStream->writeBufEnd += bytesToCopy;
719  }
720  status = netStream->transportWriteFunction( stream,
721  netStream->writeBuffer, netStream->writeBufEnd,
722  &bytesWritten, TRANSPORT_FLAG_NONE );
723  if( cryptStatusError( status ) )
724  return( status );
725  if( bytesWritten < netStream->writeBufEnd )
726  {
727  status = processIncompleteWrite( netStream, bytesWritten,
728  bytesToCopy, length );
729  if( cryptStatusError( status ) )
730  return( status );
731 
732  ENSURES_S( netStream->sanityCheckFunction( stream ) );
733 
734  return( CRYPT_OK );
735  }
736  netStream->writeBufEnd = 0;
737  if( bytesToCopy > 0 )
738  {
739  bufPtr += bytesToCopy;
740  byteCount -= bytesToCopy;
741  if( byteCount <= 0 )
742  {
743  /* We've written everything, exit */
744  *length = maxLength;
745 
746  ENSURES_S( netStream->sanityCheckFunction( stream ) );
747 
748  return( CRYPT_OK );
749  }
750  }
751  }
752  ENSURES( netStream->writeBufEnd == 0 );
753 
754  /* Write anything that's left directly */
755  status = netStream->transportWriteFunction( stream, bufPtr, byteCount,
756  &bytesWritten,
758  if( cryptStatusError( status ) )
759  return( status );
760  if( bytesWritten < byteCount )
761  {
762  /* Calculate how much remains to be written. The overall amount
763  written was the total amount to write minus what's left
764  unwritten. We don't have to update the stream buffer
765  information this time because the write buffer has already been
766  emptied */
767  byteCount -= bytesWritten;
768  *length = maxLength - byteCount;
769  }
770  else
771  {
772  /* We managed to write everything */
773  *length = maxLength;
774  }
775 
776  ENSURES_S( netStream->sanityCheckFunction( stream ) );
777 
778  return( CRYPT_OK );
779  }
780 
781 STDC_NONNULL_ARG( ( 1 ) ) \
782 void setStreamLayerBuffering( INOUT NET_STREAM_INFO *netStream,
784  {
785  assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
786 
787  if( useTransportBuffering )
788  {
789  netStream->sanityCheckFunction = sanityCheckBufferedFunction;
790  netStream->bufferedTransportReadFunction = bufferedTransportReadFunction;
791  netStream->bufferedTransportWriteFunction = bufferedTransportWriteFunction;
792  }
793  else
794  {
795  netStream->sanityCheckFunction = sanityCheckFunction;
796  netStream->bufferedTransportReadFunction = netStream->transportReadFunction;
797  netStream->bufferedTransportWriteFunction = netStream->transportWriteFunction;
798  }
799  }
800 #endif /* USE_TCP */