congestion_control_parse_ext_response(const uint8_t *msg,
                                      const size_t msg_len,
                                      circuit_params_t *params_out)
{
  ssize_t ret = 0;
  size_t num_fields = 0;
  trn_extension_t *ext = NULL;
  trn_extension_field_cc_t *cc_field = NULL;

  /* We will only accept this response (and this circuit) if sendme_inc
   * is within a factor of 2 of our consensus value. We should not need
   * to change cc_sendme_inc much, and if we do, we can spread out those
   * changes over smaller increments once every 4 hours. Exits that
   * violate this range should just not be used. */
#define MAX_SENDME_INC_NEGOTIATE_FACTOR 2

  /* Parse extension from payload. */
  ret = trn_extension_parse(&ext, msg, msg_len);
  if (ret < 0) {
    goto end;
  }

  if ((num_fields = trn_extension_get_num(ext)) == 0) {
    ret = 0;
    goto end;
  }

  /* Go over all fields. If any field is TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE,
   * then congestion control is enabled. Ignore unknown fields. */
  for (size_t f = 0; f < num_fields; f++) {
    const trn_extension_field_t *field = trn_extension_get_fields(ext, f);
    if (field == NULL) {
      ret = -1;
      goto end;
    }

    /* Only examine TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE; ignore other fields */
    if (trn_extension_field_get_field_type(field) ==
        TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE) {

      /* Parse the field into the congestion control field. */
      ret = trn_extension_field_cc_parse(&cc_field,
                trn_extension_field_getconstarray_field(field),
                trn_extension_field_getlen_field(field));
      if (ret < 0) {
        goto end;
      }

      uint8_t sendme_inc_cells =
              trn_extension_field_cc_get_sendme_inc(cc_field);
      if (!congestion_control_validate_sendme_increment(sendme_inc_cells)) {
        ret = -1;
        goto end;
      }

      /* All good. Get value and break */
      params_out->sendme_inc_cells = sendme_inc_cells;
      ret = 1;
      break;
    }
  }

 end:
  trn_extension_free(ext);
  trn_extension_field_cc_free(cc_field);

  return (int)ret;
}
congestion_control_parse_ext_response(const uint8_t *msg,
                                      const size_t msg_len,
                                      circuit_params_t *params_out)
{
  ssize_t ret = 0;
  size_t num_fields = 0;
  trn_extension_t *ext = NULL;
  trn_extension_field_cc_t *cc_field = NULL;

   
#define MAX_SENDME_INC_NEGOTIATE_FACTOR 2

   
  ret = trn_extension_parse(&ext, msg, msg_len);
  if (ret < 0) {
    goto end;
  }

  if ((num_fields = trn_extension_get_num(ext)) == 0) {
    ret = 0;
    goto end;
  }

   
  for (size_t f = 0; f < num_fields; f++) {
    const trn_extension_field_t *field = trn_extension_get_fields(ext, f);
    if (field == NULL) {
      ret = -1;
      goto end;
    }

     
    if (trn_extension_field_get_field_type(field) ==
        TRUNNEL_EXT_TYPE_CC_FIELD_RESPONSE) {

       
      ret = trn_extension_field_cc_parse(&cc_field,
                trn_extension_field_getconstarray_field(field),
                trn_extension_field_getlen_field(field));
      if (ret < 0) {
        goto end;
      }

      uint8_t sendme_inc_cells =
              trn_extension_field_cc_get_sendme_inc(cc_field);
      if (!congestion_control_validate_sendme_increment(sendme_inc_cells)) {
        ret = -1;
        goto end;
      }

       
      params_out->sendme_inc_cells = sendme_inc_cells;
      ret = 1;
      break;
    }
  }

 end:
  trn_extension_free(ext);
  trn_extension_field_cc_free(cc_field);

  return (int)ret;
}