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}