static int transport_bio_buffered_write(BIO* bio, const char* buf, int num) { int i, ret; int status; int nchunks; int committedBytes; DataChunk chunks[2]; WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*) bio->ptr; ret = num; ptr->writeBlocked = FALSE; BIO_clear_flags(bio, BIO_FLAGS_WRITE); /* we directly append extra bytes in the xmit buffer, this could be prevented * but for now it makes the code more simple. */ if (buf && num && !ringbuffer_write(&ptr->xmitBuffer, (const BYTE*) buf, num)) { WLog_ERR(TAG, "an error occured when writing (num: %d)", num); return -1; } committedBytes = 0; nchunks = ringbuffer_peek(&ptr->xmitBuffer, chunks, ringbuffer_used(&ptr->xmitBuffer)); for (i = 0; i < nchunks; i++) { while (chunks[i].size) { status = BIO_write(bio->next_bio, chunks[i].data, chunks[i].size); if (status <= 0) { if (!BIO_should_retry(bio->next_bio)) { BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY); ret = -1; /* fatal error */ goto out; } if (BIO_should_write(bio->next_bio)) { BIO_set_flags(bio, BIO_FLAGS_WRITE); ptr->writeBlocked = TRUE; goto out; /* EWOULDBLOCK */ } } committedBytes += status; chunks[i].size -= status; chunks[i].data += status; } } out: ringbuffer_commit_read_bytes(&ptr->xmitBuffer, committedBytes); return ret; }
static int transport_bio_buffered_write(BIO* bio, const char* buf, int num) { int status, ret; rdpTcp* tcp = (rdpTcp*) bio->ptr; int nchunks, committedBytes, i; DataChunk chunks[2]; ret = num; tcp->writeBlocked = FALSE; BIO_clear_flags(bio, BIO_FLAGS_WRITE); /* we directly append extra bytes in the xmit buffer, this could be prevented * but for now it makes the code more simple. */ if (buf && num && !ringbuffer_write(&tcp->xmitBuffer, (const BYTE*) buf, num)) { fprintf(stderr, "%s: an error occured when writing(toWrite=%d)\n", __FUNCTION__, num); return -1; } committedBytes = 0; nchunks = ringbuffer_peek(&tcp->xmitBuffer, chunks, ringbuffer_used(&tcp->xmitBuffer)); for (i = 0; i < nchunks; i++) { while (chunks[i].size) { status = BIO_write(bio->next_bio, chunks[i].data, chunks[i].size); if (status <= 0) { if (!BIO_should_retry(bio->next_bio)) { BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY); ret = -1; /* fatal error */ goto out; } if (BIO_should_write(bio->next_bio)) { BIO_set_flags(bio, BIO_FLAGS_WRITE); tcp->writeBlocked = TRUE; goto out; /* EWOULDBLOCK */ } } committedBytes += status; chunks[i].size -= status; chunks[i].data += status; } } out: ringbuffer_commit_read_bytes(&tcp->xmitBuffer, committedBytes); return ret; }
BOOL transport_bio_buffered_drain(BIO *bio) { int status; rdpTcp* tcp = (rdpTcp*) bio->ptr; if (!ringbuffer_used(&tcp->xmitBuffer)) return 1; status = transport_bio_buffered_write(bio, NULL, 0); return status >= 0; }
static long transport_bio_buffered_ctrl(BIO* bio, int cmd, long arg1, void* arg2) { int status = -1; WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*) BIO_get_data(bio); switch (cmd) { case BIO_CTRL_FLUSH: if (!ringbuffer_used(&ptr->xmitBuffer)) status = 1; else status = (transport_bio_buffered_write(bio, NULL, 0) >= 0) ? 1 : -1; break; case BIO_CTRL_WPENDING: status = ringbuffer_used(&ptr->xmitBuffer); break; case BIO_CTRL_PENDING: status = 0; break; case BIO_C_READ_BLOCKED: status = (int) ptr->readBlocked; break; case BIO_C_WRITE_BLOCKED: status = (int) ptr->writeBlocked; break; default: status = BIO_ctrl(BIO_next(bio), cmd, arg1, arg2); break; } return status; }
void ringbuffer_commit_read_bytes(RingBuffer* rb, size_t sz) { DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %d", rb, sz); if (sz < 1) return; assert(rb->size - rb->freeSize >= sz); rb->readPtr = (rb->readPtr + sz) % rb->size; rb->freeSize += sz; /* when we reach a reasonable free size, we can go back to the original size */ if ((rb->size != rb->initialSize) && (ringbuffer_used(rb) < rb->initialSize / 2)) ringbuffer_realloc(rb, rb->initialSize); }
static long transport_bio_buffered_ctrl(BIO* bio, int cmd, long arg1, void* arg2) { rdpTcp* tcp = (rdpTcp*) bio->ptr; switch (cmd) { case BIO_CTRL_FLUSH: return 1; case BIO_CTRL_WPENDING: return ringbuffer_used(&tcp->xmitBuffer); case BIO_CTRL_PENDING: return 0; default: return BIO_ctrl(bio->next_bio, cmd, arg1, arg2); } return 0; }
int tcp_attach(rdpTcp* tcp, int sockfd) { tcp->sockfd = sockfd; SetEventFileDescriptor(tcp->event, tcp->sockfd); ringbuffer_commit_read_bytes(&tcp->xmitBuffer, ringbuffer_used(&tcp->xmitBuffer)); if (tcp->socketBio) { if (BIO_set_fd(tcp->socketBio, sockfd, 1) < 0) return -1; } else { tcp->socketBio = BIO_new(BIO_s_simple_socket()); if (!tcp->socketBio) return -1; BIO_set_fd(tcp->socketBio, sockfd, BIO_CLOSE); } if (!tcp->bufferedBio) { tcp->bufferedBio = BIO_new(BIO_s_buffered_socket()); if (!tcp->bufferedBio) return FALSE; tcp->bufferedBio->ptr = tcp; tcp->bufferedBio = BIO_push(tcp->bufferedBio, tcp->socketBio); } return 0; }
int tls_write_all(rdpTls* tls, const BYTE* data, int length) { int status, nchunks, commitedBytes; rdpTcp *tcp; #ifdef HAVE_POLL_H struct pollfd pollfds; #else fd_set rset, wset; fd_set *rsetPtr, *wsetPtr; struct timeval tv; #endif BIO* bio = tls->bio; DataChunk chunks[2]; BIO* bufferedBio = findBufferedBio(bio); if (!bufferedBio) { DEBUG_WARN( "%s: error unable to retrieve the bufferedBio in the BIO chain\n", __FUNCTION__); return -1; } tcp = (rdpTcp*) bufferedBio->ptr; do { status = BIO_write(bio, data, length); if (status > 0) break; if (!BIO_should_retry(bio)) return -1; #ifdef HAVE_POLL_H pollfds.fd = tcp->sockfd; pollfds.revents = 0; pollfds.events = 0; if (tcp->writeBlocked) { pollfds.events |= POLLOUT; } else if (tcp->readBlocked) { pollfds.events |= POLLIN; } else { DEBUG_WARN( "%s: weird we're blocked but the underlying is not read or write blocked !\n", __FUNCTION__); USleep(10); continue; } do { status = poll(&pollfds, 1, 100); } while ((status < 0) && (errno == EINTR)); #else /* we try to handle SSL want_read and want_write nicely */ rsetPtr = wsetPtr = NULL; if (tcp->writeBlocked) { wsetPtr = &wset; FD_ZERO(&wset); FD_SET(tcp->sockfd, &wset); } else if (tcp->readBlocked) { rsetPtr = &rset; FD_ZERO(&rset); FD_SET(tcp->sockfd, &rset); } else { DEBUG_WARN( "%s: weird we're blocked but the underlying is not read or write blocked !\n", __FUNCTION__); USleep(10); continue; } tv.tv_sec = 0; tv.tv_usec = 100 * 1000; status = _select(tcp->sockfd + 1, rsetPtr, wsetPtr, NULL, &tv); #endif if (status < 0) return -1; } while (TRUE); /* make sure the output buffer is empty */ commitedBytes = 0; while ((nchunks = ringbuffer_peek(&tcp->xmitBuffer, chunks, ringbuffer_used(&tcp->xmitBuffer)))) { int i; for (i = 0; i < nchunks; i++) { while (chunks[i].size) { status = BIO_write(tcp->socketBio, chunks[i].data, chunks[i].size); if (status > 0) { chunks[i].size -= status; chunks[i].data += status; commitedBytes += status; continue; } if (!BIO_should_retry(tcp->socketBio)) goto out_fail; #ifdef HAVE_POLL_H pollfds.fd = tcp->sockfd; pollfds.events = POLLIN; pollfds.revents = 0; do { status = poll(&pollfds, 1, 100); } while ((status < 0) && (errno == EINTR)); #else FD_ZERO(&rset); FD_SET(tcp->sockfd, &rset); tv.tv_sec = 0; tv.tv_usec = 100 * 1000; status = _select(tcp->sockfd + 1, &rset, NULL, NULL, &tv); #endif if (status < 0) goto out_fail; } } } ringbuffer_commit_read_bytes(&tcp->xmitBuffer, commitedBytes); return length; out_fail: ringbuffer_commit_read_bytes(&tcp->xmitBuffer, commitedBytes); return -1; }
static BOOL ringbuffer_realloc(RingBuffer* rb, size_t targetSize) { BYTE* newData; DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %d", rb, targetSize); if (rb->writePtr == rb->readPtr) { /* when no size is used we can realloc() and set the heads at the * beginning of the buffer */ newData = (BYTE*) realloc(rb->buffer, targetSize); if (!newData) return FALSE; rb->readPtr = rb->writePtr = 0; rb->buffer = newData; } else if ((rb->writePtr >= rb->readPtr) && (rb->writePtr < targetSize)) { /* we reallocate only if we're in that case, realloc don't touch read * and write heads * * readPtr writePtr * | | * v v * [............|XXXXXXXXXXXXXX|..........] */ newData = (BYTE*) realloc(rb->buffer, targetSize); if (!newData) return FALSE; rb->buffer = newData; } else { /* in case of malloc the read head is moved at the beginning of the new buffer * and the write head is set accordingly */ newData = (BYTE*) malloc(targetSize); if (!newData) return FALSE; if (rb->readPtr < rb->writePtr) { /* readPtr writePtr * | | * v v * [............|XXXXXXXXXXXXXX|..........] */ memcpy(newData, rb->buffer + rb->readPtr, ringbuffer_used(rb)); } else { /* writePtr readPtr * | | * v v * [XXXXXXXXXXXX|..............|XXXXXXXXXX] */ BYTE* dst = newData; memcpy(dst, rb->buffer + rb->readPtr, rb->size - rb->readPtr); dst += (rb->size - rb->readPtr); if (rb->writePtr) memcpy(dst, rb->buffer, rb->writePtr); } rb->writePtr = rb->size - rb->freeSize; rb->readPtr = 0; free(rb->buffer); rb->buffer = newData; } rb->freeSize += (targetSize - rb->size); rb->size = targetSize; return TRUE; }