001/* 002 * Copyright 2015-2020 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.io.UnsupportedEncodingException; 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 try { 073 characterStrings.add(new String(extent, "UTF-8")); 074 } catch (UnsupportedEncodingException e) { 075 throw new AssertionError(e); 076 } 077 } 078 079 characterStringsCache = Collections.unmodifiableList(characterStrings); 080 } 081 return characterStringsCache; 082 } 083 084 public List<byte[]> getExtents() { 085 ArrayList<byte[]> extents = new ArrayList<byte[]>(); 086 int segLength = 0; 087 for (int used = 0; used < blob.length; used += segLength) { 088 segLength = 0x00ff & blob[used]; 089 int end = ++used + segLength; 090 byte[] extent = Arrays.copyOfRange(blob, used, end); 091 extents.add(extent); 092 } 093 return extents; 094 } 095 096 @Override 097 public void serialize(DataOutputStream dos) throws IOException { 098 dos.write(blob); 099 } 100 101 @Override 102 public TYPE getType() { 103 return TYPE.TXT; 104 } 105 106 @Override 107 public String toString() { 108 return "\"" + getText() + "\""; 109 } 110 111}