int transport_write(rdpTransport* transport, wStream* s) { int length; int status = -1; int writtenlength = 0; EnterCriticalSection(&(transport->WriteLock)); length = Stream_GetPosition(s); writtenlength = length; Stream_SetPosition(s, 0); if (length > 0) { WLog_Packet(WLog_Get(TAG), WLOG_TRACE, Stream_Buffer(s), length, WLOG_PACKET_OUTBOUND); } while (length > 0) { status = BIO_write(transport->frontBio, Stream_Pointer(s), length); if (status <= 0) { /* the buffered BIO that is at the end of the chain always says OK for writing, * so a retry means that for any reason we need to read. The most probable * is a SSL or TSG BIO in the chain. */ if (!BIO_should_retry(transport->frontBio)) goto out_cleanup; /* non-blocking can live with blocked IOs */ if (!transport->blocking) goto out_cleanup; if (BIO_wait_write(transport->frontBio, 100) < 0) { WLog_ERR(TAG, "error when selecting for write"); status = -1; goto out_cleanup; } continue; } if (transport->blocking || transport->settings->WaitForOutputBufferFlush) { while (BIO_write_blocked(transport->frontBio)) { if (BIO_wait_write(transport->frontBio, 100) < 0) { WLog_ERR(TAG, "error when selecting for write"); status = -1; goto out_cleanup; } if (BIO_flush(transport->frontBio) < 1) { WLog_ERR(TAG, "error when flushing outputBuffer"); status = -1; goto out_cleanup; } } } length -= status; Stream_Seek(s, status); } transport->written += writtenlength; out_cleanup: if (status < 0) { /* A write error indicates that the peer has dropped the connection */ transport->layer = TRANSPORT_LAYER_CLOSED; } if (s->pool) Stream_Release(s); LeaveCriticalSection(&(transport->WriteLock)); return status; }
/** * @brief Try to read a complete PDU (NLA, fast-path or tpkt) from the underlying transport. * * If possible a complete PDU is read, in case of non blocking transport this might not succeed. * Except in case of an error the passed stream will point to the last byte read (correct * position). When the pdu read is completed the stream is sealed and the pointer set to 0 * * @param[in] transport rdpTransport * @param[in] s wStream * @return < 0 on error; 0 if not enough data is available (non blocking mode); > 0 number of * bytes of the *complete* pdu read */ int transport_read_pdu(rdpTransport* transport, wStream* s) { int status; int position; int pduLength; BYTE* header; position = 0; pduLength = 0; if (!transport) return -1; if (!s) return -1; position = Stream_GetPosition(s); /* Make sure there is enough space for the longest header within the stream */ if (!Stream_EnsureCapacity(s, 4)) return -1; /* Make sure at least two bytes are read for further processing */ if (position < 2 && (status = transport_read_layer_bytes(transport, s, 2 - position)) != 1) { /* No data available at the moment */ return status; } /* update position value for further checks */ position = Stream_GetPosition(s); header = Stream_Buffer(s); if (transport->NlaMode) { /* * In case NlaMode is set TSRequest package(s) are expected * 0x30 = DER encoded data with these bits set: * bit 6 P/C constructed * bit 5 tag number - sequence */ if (header[0] == 0x30) { /* TSRequest (NLA) */ if (header[1] & 0x80) { if ((header[1] & ~(0x80)) == 1) { /* check for header bytes already was readed in previous calls */ if (position < 3 && (status = transport_read_layer_bytes(transport, s, 3 - position)) != 1) return status; pduLength = header[2]; pduLength += 3; } else if ((header[1] & ~(0x80)) == 2) { /* check for header bytes already was readed in previous calls */ if (position < 4 && (status = transport_read_layer_bytes(transport, s, 4 - position)) != 1) return status; pduLength = (header[2] << 8) | header[3]; pduLength += 4; } else { WLog_ERR(TAG, "Error reading TSRequest!"); return -1; } } else { pduLength = header[1]; pduLength += 2; } } } else { if (header[0] == 0x03) { /* TPKT header */ /* check for header bytes already was readed in previous calls */ if (position < 4 && (status = transport_read_layer_bytes(transport, s, 4 - position)) != 1) return status; pduLength = (header[2] << 8) | header[3]; /* min and max values according to ITU-T Rec. T.123 (01/2007) section 8 */ if (pduLength < 7 || pduLength > 0xFFFF) { WLog_ERR(TAG, "tpkt - invalid pduLength: %d", pduLength); return -1; } } else { /* Fast-Path Header */ if (header[1] & 0x80) { /* check for header bytes already was readed in previous calls */ if (position < 3 && (status = transport_read_layer_bytes(transport, s, 3 - position)) != 1) return status; pduLength = ((header[1] & 0x7F) << 8) | header[2]; } else pduLength = header[1]; /* * fast-path has 7 bits for length so the maximum size, including headers is 0x8000 * The theoretical minimum fast-path PDU consists only of two header bytes plus one * byte for data (e.g. fast-path input synchronize pdu) */ if (pduLength < 3 || pduLength > 0x8000) { WLog_ERR(TAG, "fast path - invalid pduLength: %d", pduLength); return -1; } } } if (!Stream_EnsureCapacity(s, Stream_GetPosition(s) + pduLength)) return -1; status = transport_read_layer_bytes(transport, s, pduLength - Stream_GetPosition(s)); if (status != 1) return status; if (Stream_GetPosition(s) >= pduLength) WLog_Packet(WLog_Get(TAG), WLOG_TRACE, Stream_Buffer(s), pduLength, WLOG_PACKET_INBOUND); Stream_SealLength(s); Stream_SetPosition(s, 0); return Stream_Length(s); }
int transport_write(rdpTransport* transport, wStream* s) { int length; int status = -1; EnterCriticalSection(&(transport->WriteLock)); length = Stream_GetPosition(s); Stream_SetPosition(s, 0); #ifdef WITH_DEBUG_TRANSPORT if (length > 0) { fprintf(stderr, "Local > Remote\n"); winpr_HexDump(Stream_Buffer(s), length); } #endif if (length > 0) { WLog_Packet(transport->log, WLOG_TRACE, Stream_Buffer(s), length, WLOG_PACKET_OUTBOUND); } while (length > 0) { status = BIO_write(transport->frontBio, Stream_Pointer(s), length); if (status <= 0) { /* the buffered BIO that is at the end of the chain always says OK for writing, * so a retry means that for any reason we need to read. The most probable * is a SSL or TSG BIO in the chain. */ if (!BIO_should_retry(transport->frontBio)) return status; /* non-blocking can live with blocked IOs */ if (!transport->blocking) return status; if (transport_wait_for_write(transport) < 0) { fprintf(stderr, "%s: error when selecting for write\n", __FUNCTION__); return -1; } continue; } if (transport->blocking || transport->settings->WaitForOutputBufferFlush) { /* blocking transport, we must ensure the write buffer is really empty */ rdpTcp *out = transport->TcpOut; while (out->writeBlocked) { if (transport_wait_for_write(transport) < 0) { fprintf(stderr, "%s: error when selecting for write\n", __FUNCTION__); return -1; } if (!transport_bio_buffered_drain(out->bufferedBio)) { fprintf(stderr, "%s: error when draining outputBuffer\n", __FUNCTION__); return -1; } } } length -= status; Stream_Seek(s, status); } if (status < 0) { /* A write error indicates that the peer has dropped the connection */ transport->layer = TRANSPORT_LAYER_CLOSED; } if (s->pool) Stream_Release(s); LeaveCriticalSection(&(transport->WriteLock)); return status; }
/** * @brief Try to read a complete PDU (NLA, fast-path or tpkt) from the underlying transport. * * If possible a complete PDU is read, in case of non blocking transport this might not succeed. * Except in case of an error the passed stream will point to the last byte read (correct * position). When the pdu read is completed the stream is sealed and the pointer set to 0 * * @param[in] transport rdpTransport * @param[in] s wStream * @return < 0 on error; 0 if not enough data is available (non blocking mode); > 0 number of * bytes of the *complete* pdu read */ int transport_read_pdu(rdpTransport* transport, wStream* s) { int status; int position; int pduLength; BYTE *header; position = 0; pduLength = 0; if (!transport) return -1; if (!s) return -1; position = Stream_GetPosition(s); /* Make sure there is enough space for the longest header within the stream */ Stream_EnsureCapacity(s, 4); /* Make sure at least two bytes are read for futher processing */ if (position < 2 && (status = transport_read_layer_bytes(transport, s, 2 - position)) != 1) { /* No data available at the moment */ return status; } header = Stream_Buffer(s); if (transport->NlaMode) { /* * In case NlaMode is set TSRequest package(s) are expected * 0x30 = DER encoded data with these bits set: * bit 6 P/C constructed * bit 5 tag number - sequence */ if (header[0] == 0x30) { /* TSRequest (NLA) */ if (header[1] & 0x80) { if ((header[1] & ~(0x80)) == 1) { if ((status = transport_read_layer_bytes(transport, s, 1)) != 1) return status; pduLength = header[2]; pduLength += 3; } else if ((header[1] & ~(0x80)) == 2) { if ((status = transport_read_layer_bytes(transport, s, 2)) != 1) return status; pduLength = (header[2] << 8) | header[3]; pduLength += 4; } else { fprintf(stderr, "Error reading TSRequest!\n"); return -1; } } else { pduLength = header[1]; pduLength += 2; } } } else { if (header[0] == 0x03) { /* TPKT header */ if ((status = transport_read_layer_bytes(transport, s, 2)) != 1) return status; pduLength = (header[2] << 8) | header[3]; /* min and max values according to ITU-T Rec. T.123 (01/2007) section 8 */ if (pduLength < 7 || pduLength > 0xFFFF) { fprintf(stderr, "%s: tpkt - invalid pduLength: %d\n", __FUNCTION__, pduLength); return -1; } } else { /* Fast-Path Header */ if (header[1] & 0x80) { if ((status = transport_read_layer_bytes(transport, s, 1)) != 1) return status; pduLength = ((header[1] & 0x7F) << 8) | header[2]; } else pduLength = header[1]; /* * fast-path has 7 bits for length so the maximum size, including headers is 0x8000 * The theoretical minimum fast-path PDU consists only of two header bytes plus one * byte for data (e.g. fast-path input synchronize pdu) */ if (pduLength < 3 || pduLength > 0x8000) { fprintf(stderr, "%s: fast path - invalid pduLength: %d\n", __FUNCTION__, pduLength); return -1; } } } Stream_EnsureCapacity(s, Stream_GetPosition(s) + pduLength); status = transport_read_layer_bytes(transport, s, pduLength - Stream_GetPosition(s)); if (status != 1) return status; #ifdef WITH_DEBUG_TRANSPORT /* dump when whole PDU is read */ if (Stream_GetPosition(s) >= pduLength) { fprintf(stderr, "Local < Remote\n"); winpr_HexDump(Stream_Buffer(s), pduLength); } #endif if (Stream_GetPosition(s) >= pduLength) WLog_Packet(transport->log, WLOG_TRACE, Stream_Buffer(s), pduLength, WLOG_PACKET_INBOUND); Stream_SealLength(s); Stream_SetPosition(s, 0); return Stream_Length(s); }
int transport_write(rdpTransport* transport, wStream* s) { int length; int status = -1; WaitForSingleObject(transport->WriteMutex, INFINITE); length = Stream_GetPosition(s); Stream_SetPosition(s, 0); #ifdef WITH_DEBUG_TRANSPORT if (length > 0) { fprintf(stderr, "Local > Remote\n"); winpr_HexDump(Stream_Buffer(s), length); } #endif if (length > 0) { WLog_Packet(transport->log, WLOG_TRACE, Stream_Buffer(s), length, WLOG_PACKET_OUTBOUND); } while (length > 0) { if (transport->layer == TRANSPORT_LAYER_TLS) status = tls_write(transport->TlsOut, Stream_Pointer(s), length); else if (transport->layer == TRANSPORT_LAYER_TCP) status = tcp_write(transport->TcpOut, Stream_Pointer(s), length); else if (transport->layer == TRANSPORT_LAYER_TSG) status = tsg_write(transport->tsg, Stream_Pointer(s), length); else if (transport->layer == TRANSPORT_LAYER_TSG_TLS) status = tls_write(transport->TsgTls, Stream_Pointer(s), length); if (status < 0) break; /* error occurred */ if (status == 0) { /* when sending is blocked in nonblocking mode, the receiving buffer should be checked */ if (!transport->blocking) { /* and in case we do have buffered some data, we set the event so next loop will get it */ if (transport_read_nonblocking(transport) > 0) SetEvent(transport->ReceiveEvent); } if (transport->layer == TRANSPORT_LAYER_TLS) tls_wait_write(transport->TlsOut); else if (transport->layer == TRANSPORT_LAYER_TCP) tcp_wait_write(transport->TcpOut); else if (transport->layer == TRANSPORT_LAYER_TSG_TLS) tls_wait_write(transport->TsgTls); else USleep(transport->SleepInterval); } length -= status; Stream_Seek(s, status); } if (status < 0) { /* A write error indicates that the peer has dropped the connection */ transport->layer = TRANSPORT_LAYER_CLOSED; } if (s->pool) Stream_Release(s); ReleaseMutex(transport->WriteMutex); return status; }
int transport_read(rdpTransport* transport, wStream* s) { int status; int pduLength; int streamPosition; int transport_status; pduLength = 0; transport_status = 0; if (!transport) return -1; if (!s) return -1; /* first check if we have header */ streamPosition = Stream_GetPosition(s); if (streamPosition < 4) { status = transport_read_layer(transport, Stream_Buffer(s) + streamPosition, 4 - streamPosition); if (status < 0) return status; transport_status += status; if ((status + streamPosition) < 4) return transport_status; streamPosition += status; } /* if header is present, read in exactly one PDU */ if (s->buffer[0] == 0x03) { /* TPKT header */ pduLength = (s->buffer[2] << 8) | s->buffer[3]; } else if (s->buffer[0] == 0x30) { /* TSRequest (NLA) */ if (s->buffer[1] & 0x80) { if ((s->buffer[1] & ~(0x80)) == 1) { pduLength = s->buffer[2]; pduLength += 3; } else if ((s->buffer[1] & ~(0x80)) == 2) { pduLength = (s->buffer[2] << 8) | s->buffer[3]; pduLength += 4; } else { fprintf(stderr, "Error reading TSRequest!\n"); } } else { pduLength = s->buffer[1]; pduLength += 2; } } else { /* Fast-Path Header */ if (s->buffer[1] & 0x80) pduLength = ((s->buffer[1] & 0x7F) << 8) | s->buffer[2]; else pduLength = s->buffer[1]; } status = transport_read_layer(transport, Stream_Buffer(s) + streamPosition, pduLength - streamPosition); if (status < 0) return status; transport_status += status; #ifdef WITH_DEBUG_TRANSPORT /* dump when whole PDU is read */ if (streamPosition + status >= pduLength) { fprintf(stderr, "Local < Remote\n"); winpr_HexDump(Stream_Buffer(s), pduLength); } #endif if (streamPosition + status >= pduLength) { WLog_Packet(transport->log, WLOG_TRACE, Stream_Buffer(s), pduLength, WLOG_PACKET_INBOUND); } return transport_status; }