/* Buffer for handshake packets. Keeps the packets in order * for finished messages to use them. Used in HMAC calculation * and finished messages. */ int _gnutls_handshake_buffer_put (gnutls_session_t session, opaque * data, size_t length) { if (length == 0) return 0; if ((session->internals.max_handshake_data_buffer_size > 0) && ((length + session->internals.handshake_hash_buffer.length) > session->internals.max_handshake_data_buffer_size)) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } _gnutls_buffers_log ("BUF[HSK]: Inserted %d bytes of Data\n", length); if (_gnutls_buffer_append (&session->internals.handshake_hash_buffer, data, length) < 0) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } return 0; }
int _gnutls_gen_supplemental (gnutls_session_t session, gnutls_buffer * buf) { gnutls_supplemental_entry *p; int ret; /* Make room for 3 byte length field. */ ret = _gnutls_buffer_append (buf, "\0\0\0", 3); if (ret < 0) { gnutls_assert (); return ret; } for (p = _gnutls_supplemental; p->name; p++) { supp_send_func supp_send = p->supp_send_func; size_t sizepos = buf->length; /* Make room for supplement type and length byte length field. */ ret = _gnutls_buffer_append (buf, "\0\0\0\0", 4); if (ret < 0) { gnutls_assert (); return ret; } ret = supp_send (session, buf); if (ret < 0) { gnutls_assert (); return ret; } /* If data were added, store type+length, otherwise reset. */ if (buf->length > sizepos + 4) { buf->data[sizepos] = 0; buf->data[sizepos + 1] = p->type; buf->data[sizepos + 2] = ((buf->length - sizepos - 4) >> 8) & 0xFF; buf->data[sizepos + 3] = (buf->length - sizepos - 4) & 0xFF; } else
/* Buffers received packets of type APPLICATION DATA and * HANDSHAKE DATA. */ int _gnutls_record_buffer_put (content_type_t type, gnutls_session_t session, opaque * data, size_t length) { gnutls_buffer *buf; if (length == 0) return 0; switch (type) { case GNUTLS_APPLICATION_DATA: buf = &session->internals.application_data_buffer; _gnutls_buffers_log ("BUF[REC]: Inserted %d bytes of Data(%d)\n", length, type); break; case GNUTLS_HANDSHAKE: buf = &session->internals.handshake_data_buffer; _gnutls_buffers_log ("BUF[HSK]: Inserted %d bytes of Data(%d)\n", length, type); break; case GNUTLS_INNER_APPLICATION: buf = &session->internals.ia_data_buffer; _gnutls_buffers_log ("BUF[IA]: Inserted %d bytes of Data(%d)\n", length, type); break; default: gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (_gnutls_buffer_append (buf, data, length) < 0) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } return 0; }
inline static int _gnutls_buffer_insert (gnutls_buffer * buffer, const opaque * _data, size_t data_size) { if ((MEMSUB (_data, buffer->data) >= 0) && (MEMSUB (_data, buffer->data) < (ssize_t) buffer->length)) { /* the given _data is part of the buffer. */ if (data_size > buffer->length) { gnutls_assert (); /* this shouldn't have happened */ return GNUTLS_E_INTERNAL_ERROR; } if (_data == buffer->data) { /* then don't even memmove */ buffer->length = data_size; return 0; } memmove (buffer->data, _data, data_size); buffer->length = data_size; return 0; } if (_gnutls_buffer_append (buffer, _data, data_size) < 0) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } return 0; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, void *iptr, size_t sizeOfPtr) { size_t left; ssize_t i; opaque *ptr; size_t dsize; ptr = iptr; left = sizeOfPtr; if (sizeOfPtr == 0 || iptr == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (session->internals.handshake_recv_buffer.length > 0) { size_t tmp; /* if we have already received some data */ if (sizeOfPtr <= session->internals.handshake_recv_buffer.length) { /* if requested less data then return it. */ gnutls_assert (); tmp = sizeOfPtr; _gnutls_string_get_data( &session->internals.handshake_recv_buffer, iptr, &tmp); return tmp; } gnutls_assert (); tmp = sizeOfPtr; _gnutls_string_get_data( &session->internals.handshake_recv_buffer, iptr, &tmp); left -= tmp; htype = session->internals.handshake_recv_buffer_htype; type = session->internals.handshake_recv_buffer_type; } while (left > 0) { dsize = sizeOfPtr - left; i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left); if (i < 0) { if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN)) { gnutls_assert (); _gnutls_buffer_append (&session->internals.handshake_recv_buffer, iptr, dsize); session->internals.handshake_recv_buffer_htype = htype; session->internals.handshake_recv_buffer_type = type; } gnutls_assert (); return i; } else { if (i == 0) break; /* EOF */ } left -= i; } session->internals.handshake_recv_buffer.length = 0; return sizeOfPtr - left; }
/* 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 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; }