// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)

package org.xbill.DNS;

import java.io.IOException;
import org.xbill.DNS.utils.base16;

/**
 * Transport Layer Security Authentication
 *
 * @author Brian Wellington
 * @see <a href="https://datatracker.ietf.org/doc/html/rfc6698">RFC 6698: The DNS-Based
 *     Authentication of Named Entities (DANE) Transport Layer Security (TLS) Protocol: TLSA</a>
 */
public class TLSARecord extends Record {
  public static class CertificateUsage {
    private CertificateUsage() {}

    public static final int CA_CONSTRAINT = 0;
    public static final int SERVICE_CERTIFICATE_CONSTRAINT = 1;
    public static final int TRUST_ANCHOR_ASSERTION = 2;
    public static final int DOMAIN_ISSUED_CERTIFICATE = 3;
  }

  public static class Selector {
    private Selector() {}

    /**
     * Full certificate; the Certificate binary structure.
     *
     * @see <a href="https://datatracker.ietf.org/doc/html/rfc5280">RFC 5280</a>
     */
    public static final int FULL_CERTIFICATE = 0;

    /**
     * SubjectPublicKeyInfo; DER-encoded binary structure.
     *
     * @see <a href="https://datatracker.ietf.org/doc/html/rfc5280">RFC 5280</a>
     */
    public static final int SUBJECT_PUBLIC_KEY_INFO = 1;
  }

  public static class MatchingType {
    private MatchingType() {}

    /** Exact match on selected content */
    public static final int EXACT = 0;

    /**
     * SHA-256 hash of selected content.
     *
     * @see <a href="https://datatracker.ietf.org/doc/html/rfc6234">RFC 6234</a>
     */
    public static final int SHA256 = 1;

    /**
     * SHA-512 hash of selected content.
     *
     * @see <a href="https://datatracker.ietf.org/doc/html/rfc6234">RFC 6234</a>
     */
    public static final int SHA512 = 2;
  }

  private int certificateUsage;
  private int selector;
  private int matchingType;
  private byte[] certificateAssociationData;

  TLSARecord() {}

  /**
   * Creates an TLSA Record from the given data
   *
   * @param certificateUsage The provided association that will be used to match the certificate
   *     presented in the TLS handshake.
   * @param selector The part of the TLS certificate presented by the server that will be matched
   *     against the association data.
   * @param matchingType How the certificate association is presented.
   * @param certificateAssociationData The "certificate association data" to be matched.
   */
  protected TLSARecord(
      Name name,
      int type,
      int dclass,
      long ttl,
      int certificateUsage,
      int selector,
      int matchingType,
      byte[] certificateAssociationData) {
    super(name, type, dclass, ttl);
    this.certificateUsage = checkU8("certificateUsage", certificateUsage);
    this.selector = checkU8("selector", selector);
    this.matchingType = checkU8("matchingType", matchingType);
    if (certificateAssociationData == null || certificateAssociationData.length == 0) {
      throw new IllegalArgumentException("certificateAssociationData must not be null or empty");
    }
    this.certificateAssociationData =
        checkByteArrayLength("certificateAssociationData", certificateAssociationData, 0xFFFF);
  }

  /**
   * Creates an TLSA Record from the given data
   *
   * @param certificateUsage The provided association that will be used to match the certificate
   *     presented in the TLS handshake.
   * @param selector The part of the TLS certificate presented by the server that will be matched
   *     against the association data.
   * @param matchingType How the certificate association is presented.
   * @param certificateAssociationData The "certificate association data" to be matched.
   */
  public TLSARecord(
      Name name,
      int dclass,
      long ttl,
      int certificateUsage,
      int selector,
      int matchingType,
      byte[] certificateAssociationData) {
    this(
        name,
        Type.TLSA,
        dclass,
        ttl,
        certificateUsage,
        selector,
        matchingType,
        certificateAssociationData);
  }

  @Override
  protected void rrFromWire(DNSInput in) throws IOException {
    certificateUsage = in.readU8();
    selector = in.readU8();
    matchingType = in.readU8();
    certificateAssociationData = in.readByteArray();
    if (certificateAssociationData.length == 0) {
      throw new WireParseException("end of input");
    }
  }

  @Override
  protected void rdataFromString(Tokenizer st, Name origin) throws IOException {
    certificateUsage = st.getUInt8();
    selector = st.getUInt8();
    matchingType = st.getUInt8();
    certificateAssociationData = st.getHex(true);
  }

  /** Converts rdata to a String */
  @Override
  protected String rrToString() {
    return certificateUsage
        + " "
        + selector
        + " "
        + matchingType
        + " "
        + base16.toString(certificateAssociationData);
  }

  @Override
  protected void rrToWire(DNSOutput out, Compression c, boolean canonical) {
    out.writeU8(certificateUsage);
    out.writeU8(selector);
    out.writeU8(matchingType);
    out.writeByteArray(certificateAssociationData);
  }

  /** Returns the certificate usage of the TLSA record */
  public int getCertificateUsage() {
    return certificateUsage;
  }

  /** Returns the selector of the TLSA record */
  public int getSelector() {
    return selector;
  }

  /** Returns the matching type of the TLSA record */
  public int getMatchingType() {
    return matchingType;
  }

  /** Returns the certificate associate data of this TLSA record */
  public final byte[] getCertificateAssociationData() {
    return certificateAssociationData;
  }
}
