int _gnutls_parse_extensions(gnutls_session_t session, gnutls_ext_parse_type_t parse_type, const uint8_t * data, int data_size) { int next, ret; int pos = 0; uint16_t type; const uint8_t *sdata; gnutls_ext_recv_func ext_recv; uint16_t size; #ifdef DEBUG int i; if (session->security_parameters.entity == GNUTLS_CLIENT) for (i = 0; i < session->internals.extensions_sent_size; i++) { _gnutls_handshake_log ("EXT[%d]: expecting extension '%s'\n", session, _gnutls_extension_get_name(session->internals. extensions_sent [i])); } #endif DECR_LENGTH_RET(data_size, 2, 0); next = _gnutls_read_uint16(data); pos += 2; DECR_LENGTH_RET(data_size, next, 0); do { DECR_LENGTH_RET(next, 2, 0); type = _gnutls_read_uint16(&data[pos]); pos += 2; if ((ret = _gnutls_extension_list_check(session, type)) < 0) { gnutls_assert(); return ret; } DECR_LENGTH_RET(next, 2, 0); size = _gnutls_read_uint16(&data[pos]); pos += 2; DECR_LENGTH_RET(next, size, 0); sdata = &data[pos]; pos += size; ext_recv = _gnutls_ext_func_recv(type, parse_type); if (ext_recv == NULL) { _gnutls_handshake_log ("EXT[%p]: Found extension '%s/%d'\n", session, _gnutls_extension_get_name(type), type); continue; } _gnutls_handshake_log ("EXT[%p]: Parsing extension '%s/%d' (%d bytes)\n", session, _gnutls_extension_get_name(type), type, size); if ((ret = ext_recv(session, sdata, size)) < 0) { gnutls_assert(); return ret; } } while (next > 2); return 0; }
int _gnutls_gen_extensions(gnutls_session_t session, gnutls_buffer_st * extdata, gnutls_ext_parse_type_t parse_type) { int size; int pos, size_pos, ret; size_t i, init_size = extdata->length; pos = extdata->length; /* we will store length later on */ ret = _gnutls_buffer_append_prefix(extdata, 16, 0); if (ret < 0) return gnutls_assert_val(ret); for (i = 0; i < extfunc_size; i++) { extension_entry_st *p = &extfunc[i]; if (p->send_func == NULL) continue; if (parse_type != GNUTLS_EXT_ANY && p->parse_type != parse_type) continue; /* ensure we are sending only what we received */ if (session->security_parameters.entity == GNUTLS_SERVER) { if ((ret = _gnutls_extension_list_check(session, p->type)) < 0) { continue; } } ret = _gnutls_buffer_append_prefix(extdata, 16, p->type); if (ret < 0) return gnutls_assert_val(ret); size_pos = extdata->length; ret = _gnutls_buffer_append_prefix(extdata, 16, 0); if (ret < 0) return gnutls_assert_val(ret); size = p->send_func(session, extdata); /* returning GNUTLS_E_INT_RET_0 means to send an empty * extension of this type. */ if (size > 0 || size == GNUTLS_E_INT_RET_0) { if (size == GNUTLS_E_INT_RET_0) size = 0; /* write the real size */ _gnutls_write_uint16(size, &extdata->data[size_pos]); /* add this extension to the extension list */ if (session->security_parameters.entity == GNUTLS_CLIENT) _gnutls_extension_list_add(session, p->type); _gnutls_handshake_log ("EXT[%p]: Sending extension %s (%d bytes)\n", session, p->name, size); } else if (size < 0) { gnutls_assert(); return size; } else if (size == 0) extdata->length -= 4; /* reset type and size */ } /* remove any initial data, and the size of the header */ size = extdata->length - init_size - 2; if (size > 0) _gnutls_write_uint16(size, &extdata->data[pos]); else if (size == 0) extdata->length -= 2; /* the length bytes */ return size; }