/* This function is like write. But it does not return -1 on error. * It does return gnutls_errno instead. * * In case of E_AGAIN and E_INTERRUPTED errors, you must call gnutls_write_flush(), * until it returns ok (0). * * We need to push exactly the data in n, since we cannot send less * data. In TLS the peer must receive the whole packet in order * to decrypt and verify the integrity. * */ ssize_t _gnutls_io_write_buffered (gnutls_session_t session, const void *iptr, size_t n) { size_t left; unsigned j, x, sum = 0; ssize_t retval, i; const opaque *ptr; int ret; gnutls_transport_ptr_t fd = session->internals.transport_send_ptr; /* to know where the procedure was interrupted. */ session->internals.direction = 1; ptr = iptr; /* In case the previous write was interrupted, check if the * iptr != NULL and we have data in the buffer. * If this is true then return an error. */ if (session->internals.record_send_buffer.length > 0 && iptr != NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } /* If data in the buffer exist */ if (iptr == NULL) { /* checking is handled above */ ret = _gnutls_buffer_get (&session->internals.record_send_buffer, &ptr, &n); if (ret < 0) { gnutls_assert (); return ret; } _gnutls_write_log ("WRITE: Restoring old write. (%d bytes to send)\n", n); } _gnutls_write_log ("WRITE: Will write %d bytes to %d.\n", n, fd); i = 0; left = n; while (left > 0) { if (session->internals._gnutls_push_func == NULL) i = send (GNUTLS_POINTER_TO_INT(fd), &ptr[n - left], left, 0); else i = session->internals._gnutls_push_func (fd, &ptr[n - left], left); if (i == -1) { if (errno == EAGAIN || errno == EINTR) { session->internals.record_send_buffer_prev_size += n - left; retval = _gnutls_buffer_insert (&session->internals. record_send_buffer, &ptr[n - left], left); if (retval < 0) { gnutls_assert (); return retval; } _gnutls_write_log ("WRITE: Interrupted. Stored %d bytes to buffer. Already sent %d bytes.\n", left, n - left); retval = RET (errno); return retval; } else { gnutls_assert (); return GNUTLS_E_PUSH_ERROR; } } left -= i; if (_gnutls_log_level >= 7) { char line[128]; char tmp[16]; _gnutls_write_log ("WRITE: wrote %d bytes to %d. Left %d bytes. Total %d bytes.\n", i, fd, left, n); for (x = 0; x < (unsigned) ((i) / 16) + 1; x++) { line[0] = 0; if (sum > n - left) break; sprintf (tmp, "%.4x - ", x); _gnutls_str_cat (line, sizeof (line), tmp); for (j = 0; j < 16; j++) { if (sum < n - left) { sprintf (tmp, "%.2x ", ((unsigned char *) ptr)[sum++]); _gnutls_str_cat (line, sizeof (line), tmp); } else break; } _gnutls_write_log ("%s\n", line); } } } retval = n + session->internals.record_send_buffer_prev_size; session->internals.record_send_buffer.length = 0; session->internals.record_send_buffer_prev_size = 0; return retval; }
/* This is a send function for the gnutls handshake * protocol. Just makes sure that all data have been sent. */ ssize_t _gnutls_handshake_io_send_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, const void *iptr, size_t n) { size_t left; ssize_t ret = 0; const opaque *ptr; ssize_t retval = 0; ptr = iptr; if (session->internals.handshake_send_buffer.length > 0 && ptr == NULL && n == 0) { gnutls_datum bdata; /* resuming previously interrupted write */ gnutls_assert (); /* checking is handled above */ _gnutls_buffer_get_datum (&session->internals.handshake_send_buffer, &bdata, session->internals.handshake_send_buffer.length); ptr = bdata.data; n = bdata.size; type = session->internals.handshake_send_buffer_type; htype = session->internals.handshake_send_buffer_htype; } else if (session->internals.handshake_send_buffer.length > 0) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } #ifdef WRITE_DEBUG else { size_t sum = 0, x, j; _gnutls_write_log ("HWRITE: will write %d bytes to %d.\n", n, gnutls_transport_get_ptr (session)); for (x = 0; x < ((n) / 16) + 1; x++) { if (sum > n) break; _gnutls_write_log ("%.4x - ", x); for (j = 0; j < 16; j++) { if (sum < n) { _gnutls_write_log ("%.2x ", ((unsigned char *) ptr)[sum++]); } else break; } _gnutls_write_log ("\n"); } _gnutls_write_log ("\n"); } #endif if (n == 0) { /* if we have no data to send */ gnutls_assert (); return 0; } else if (ptr == NULL) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } left = n; while (left > 0) { ret = _gnutls_send_int (session, type, htype, &ptr[n - left], left); if (ret <= 0) { if (ret == 0) { gnutls_assert (); ret = GNUTLS_E_INTERNAL_ERROR; } if (left > 0 && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)) { gnutls_assert (); retval = _gnutls_buffer_append (&session->internals. handshake_send_buffer, &ptr[n - left], left); if (retval < 0) { gnutls_assert (); return retval; } session->internals.handshake_send_buffer_prev_size += n - left; session->internals.handshake_send_buffer_type = type; session->internals.handshake_send_buffer_htype = htype; } else { session->internals.handshake_send_buffer_prev_size = 0; session->internals.handshake_send_buffer.length = 0; } gnutls_assert (); return ret; } left -= ret; } retval = n + session->internals.handshake_send_buffer_prev_size; session->internals.handshake_send_buffer.length = 0; session->internals.handshake_send_buffer_prev_size = 0; return retval; }
/* This function writes the data that are left in the * TLS write buffer (ie. because the previous write was * interrupted. */ ssize_t _gnutls_io_write_flush(gnutls_session_t session) { gnutls_datum_t msg; mbuffer_head_st *send_buffer = &session->internals.record_send_buffer; int ret; ssize_t sent = 0, tosend = 0; giovec_t iovec[MAX_QUEUE]; int i = 0; mbuffer_st *cur; session->internals.direction = 1; _gnutls_write_log("WRITE FLUSH: %d bytes in buffer.\n", (int) send_buffer->byte_length); for (cur = _mbuffer_head_get_first(send_buffer, &msg); cur != NULL; cur = _mbuffer_head_get_next(cur, &msg)) { iovec[i].iov_base = msg.data; iovec[i++].iov_len = msg.size; tosend += msg.size; /* we buffer up to MAX_QUEUE messages */ if (i >= MAX_QUEUE) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } } if (tosend == 0) { gnutls_assert(); return 0; } ret = _gnutls_writev(session, iovec, i, tosend); if (ret >= 0) { _mbuffer_head_remove_bytes(send_buffer, ret); _gnutls_write_log ("WRITE: wrote %d bytes, %d bytes left.\n", ret, (int) send_buffer->byte_length); sent += ret; } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { _gnutls_write_log("WRITE interrupted: %d bytes left.\n", (int) send_buffer->byte_length); return ret; } else if (ret == GNUTLS_E_LARGE_PACKET) { _mbuffer_head_remove_bytes(send_buffer, tosend); _gnutls_write_log ("WRITE cannot send large packet (%u bytes).\n", (unsigned int) tosend); return ret; } else { _gnutls_write_log("WRITE error: code %d, %d bytes left.\n", ret, (int) send_buffer->byte_length); gnutls_assert(); return ret; } if (sent < tosend) { return gnutls_assert_val(GNUTLS_E_AGAIN); } return sent; }
/* This function is like write. But it does not return -1 on error. * It does return gnutls_errno instead. * * In case of E_AGAIN and E_INTERRUPTED errors, you must call gnutls_write_flush(), * until it returns ok (0). * * We need to push exactly the data in n, since we cannot send less * data. In TLS the peer must receive the whole packet in order * to decrypt and verify the integrity. * */ ssize_t _gnutls_io_write_buffered (gnutls_session_t session, const void *iptr, size_t n) { size_t left; unsigned j, x, sum = 0; ssize_t retval, i; const opaque *ptr; gnutls_transport_ptr_t fd = session->internals.transport_send_ptr; /* to know where the procedure was interrupted. */ session->internals.direction = 1; ptr = iptr; /* In case the previous write was interrupted, check if the * iptr != NULL and we have data in the buffer. * If this is true then return an error. */ if (session->internals.record_send_buffer.length > 0 && iptr != NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } /* If data in the buffer exist */ if (iptr == NULL) { gnutls_datum bdata; /* checking is handled above */ _gnutls_buffer_get_datum (&session->internals.record_send_buffer, &bdata, session->internals.record_send_buffer.length); ptr = bdata.data; n = bdata.size; _gnutls_write_log ("WRITE: Restoring old write. (%d bytes to send)\n", n); } _gnutls_write_log ("WRITE: Will write %d bytes to %p.\n", n, fd); i = 0; left = n; while (left > 0) { session->internals.errnum = 0; if (session->internals._gnutls_push_func == NULL) { i = send (GNUTLS_POINTER_TO_INT (fd), &ptr[n - left], left, 0); #if HAVE_WINSOCK2_H if (i < 0) { int tmperr = WSAGetLastError (); switch (tmperr) { case WSAEWOULDBLOCK: session->internals.errnum = EAGAIN; break; case WSAEINTR: session->internals.errnum = EINTR; break; default: session->internals.errnum = EIO; break; } WSASetLastError (tmperr); } #endif } else i = session->internals._gnutls_push_func (fd, &ptr[n - left], left); if (i == -1) { int err = session->internals.errnum ? session->internals.errnum : errno; if (err == EAGAIN || err == EINTR) { session->internals.record_send_buffer_prev_size += n - left; retval = _gnutls_buffer_append (&session->internals.record_send_buffer, &ptr[n - left], left); if (retval < 0) { gnutls_assert (); return retval; } _gnutls_write_log ("WRITE: Interrupted. Stored %d bytes to buffer. Already sent %d bytes.\n", left, n - left); if (err == EAGAIN) return GNUTLS_E_AGAIN; return GNUTLS_E_INTERRUPTED; } else { gnutls_assert (); return GNUTLS_E_PUSH_ERROR; } } left -= i; if (_gnutls_log_level >= 7) { char line[128]; char tmp[16]; _gnutls_write_log ("WRITE: wrote %d bytes to %p. Left %d bytes. Total %d bytes.\n", i, fd, left, n); for (x = 0; x < (unsigned) ((i) / 16) + 1; x++) { line[0] = 0; if (sum > n - left) break; sprintf (tmp, "%.4x - ", x); _gnutls_str_cat (line, sizeof (line), tmp); for (j = 0; j < 16; j++) { if (sum < n - left) { sprintf (tmp, "%.2x ", ((const unsigned char *) ptr)[sum++]); _gnutls_str_cat (line, sizeof (line), tmp); } else break; } _gnutls_write_log ("%s\n", line); } } } retval = n + session->internals.record_send_buffer_prev_size; session->internals.record_send_buffer.length = 0; session->internals.record_send_buffer_prev_size = 0; return retval; }