001/*
002 * Copyright 2015-2024 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.record;
012
013import java.io.DataInputStream;
014import java.io.DataOutputStream;
015import java.io.IOException;
016import java.nio.charset.StandardCharsets;
017import java.util.ArrayList;
018import java.util.Arrays;
019import java.util.Collections;
020import java.util.Iterator;
021import java.util.List;
022
023import org.minidns.record.Record.TYPE;
024
025/**
026 *  A TXT record. Actually a binary blob containing extents, each of which is a one-byte count
027 *  followed by that many bytes of data, which can usually be interpreted as ASCII strings
028 *  but not always.
029 */
030public class TXT extends Data {
031
032    private final byte[] blob;
033
034    public static TXT parse(DataInputStream dis, int length) throws IOException {
035        byte[] blob = new byte[length];
036        dis.readFully(blob);
037        return new TXT(blob);
038    }
039
040    public TXT(byte[] blob) {
041        this.blob = blob;
042    }
043
044    public byte[] getBlob() {
045        return blob.clone();
046    }
047
048    private transient String textCache;
049
050    public String getText() {
051        if (textCache == null) {
052            StringBuilder sb = new StringBuilder();
053            Iterator<String> it = getCharacterStrings().iterator();
054            while (it.hasNext()) {
055                sb.append(it.next());
056                if (it.hasNext()) {
057                    sb.append(" / ");
058                }
059            }
060            textCache = sb.toString();
061        }
062        return textCache;
063    }
064
065    private transient List<String> characterStringsCache;
066
067    public List<String> getCharacterStrings() {
068        if (characterStringsCache == null) {
069            List<byte[]> extents = getExtents();
070            List<String> characterStrings = new ArrayList<>(extents.size());
071            for (byte[] extent : extents) {
072                characterStrings.add(new String(extent, StandardCharsets.UTF_8));
073            }
074
075            characterStringsCache = Collections.unmodifiableList(characterStrings);
076        }
077        return characterStringsCache;
078    }
079
080    public List<byte[]> getExtents() {
081        ArrayList<byte[]> extents = new ArrayList<byte[]>();
082        int segLength = 0;
083        for (int used = 0; used < blob.length; used += segLength) {
084            segLength = 0x00ff & blob[used];
085            int end = ++used + segLength;
086            byte[] extent = Arrays.copyOfRange(blob, used, end);
087            extents.add(extent);
088        }
089        return extents;
090    }
091
092    @Override
093    public void serialize(DataOutputStream dos) throws IOException {
094        dos.write(blob);
095    }
096
097    @Override
098    public TYPE getType() {
099        return TYPE.TXT;
100    }
101
102    @Override
103    public String toString() {
104        return "\"" + getText() + "\"";
105    }
106
107}