001/* 002 * Copyright 2015-2018 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 org.minidns.dnsname.DnsName; 014import org.minidns.record.Record.TYPE; 015 016import java.io.ByteArrayInputStream; 017import java.io.ByteArrayOutputStream; 018import java.io.DataInputStream; 019import java.io.DataOutputStream; 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.List; 024 025/** 026 * NSEC record payload. 027 */ 028public class NSEC extends Data { 029 030 /** 031 * The next owner name that contains a authoritative data or a delegation point. 032 */ 033 public final DnsName next; 034 035 private final byte[] typeBitmap; 036 037 /** 038 * The RR types existing at the owner name. 039 */ 040 public final TYPE[] types; 041 042 public static NSEC parse(DataInputStream dis, byte[] data, int length) throws IOException { 043 DnsName next = DnsName.parse(dis, data); 044 045 byte[] typeBitmap = new byte[length - next.size()]; 046 if (dis.read(typeBitmap) != typeBitmap.length) throw new IOException(); 047 TYPE[] types = readTypeBitMap(typeBitmap); 048 return new NSEC(next, types); 049 } 050 051 public NSEC(String next, TYPE[] types) { 052 this(DnsName.from(next), types); 053 } 054 055 public NSEC(DnsName next, TYPE[] types) { 056 this.next = next; 057 this.types = types; 058 this.typeBitmap = createTypeBitMap(types); 059 } 060 061 @Override 062 public TYPE getType() { 063 return TYPE.NSEC; 064 } 065 066 @Override 067 public void serialize(DataOutputStream dos) throws IOException { 068 next.writeToStream(dos); 069 dos.write(typeBitmap); 070 } 071 072 @Override 073 public String toString() { 074 StringBuilder sb = new StringBuilder() 075 .append(next).append('.'); 076 for (TYPE type : types) { 077 sb.append(' ').append(type); 078 } 079 return sb.toString(); 080 } 081 082 static byte[] createTypeBitMap(TYPE[] types) { 083 List<Integer> typeList = new ArrayList<Integer>(); 084 for (TYPE type : types) { 085 typeList.add(type.getValue()); 086 } 087 Collections.sort(typeList); 088 089 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 090 DataOutputStream dos = new DataOutputStream(baos); 091 092 try { 093 int windowBlock = -1; 094 byte[] bitmap = null; 095 for (Integer type : typeList) { 096 if (windowBlock == -1 || (type >> 8) != windowBlock) { 097 if (windowBlock != -1) writeOutBlock(bitmap, dos); 098 windowBlock = (type >> 8); 099 dos.writeByte(windowBlock); 100 bitmap = new byte[32]; 101 } 102 int a = (type >> 3) % 32; 103 int b = type % 8; 104 bitmap[a] |= (128 >> b); 105 } 106 if (windowBlock != -1) writeOutBlock(bitmap, dos); 107 } catch (IOException e) { 108 // Should never happen. 109 throw new RuntimeException(e); 110 } 111 112 return baos.toByteArray(); 113 } 114 115 private static void writeOutBlock(byte[] values, DataOutputStream dos) throws IOException { 116 int n = 0; 117 for (int i = 0; i < values.length; i++) { 118 if (values[i] != 0) n = i + 1; 119 } 120 dos.writeByte(n); 121 for (int i = 0; i < n; i++) { 122 dos.writeByte(values[i]); 123 } 124 } 125 126 static TYPE[] readTypeBitMap(byte[] typeBitmap) throws IOException { 127 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(typeBitmap)); 128 int read = 0; 129 ArrayList<TYPE> typeList = new ArrayList<TYPE>(); 130 while (typeBitmap.length > read) { 131 int windowBlock = dis.readUnsignedByte(); 132 int bitmapLength = dis.readUnsignedByte(); 133 for (int i = 0; i < bitmapLength; i++) { 134 int b = dis.readUnsignedByte(); 135 for (int j = 0; j < 8; j++) { 136 if (((b >> j) & 0x1) > 0) { 137 typeList.add(TYPE.getType((windowBlock << 8) + (i * 8) + (7 - j))); 138 } 139 } 140 } 141 read += bitmapLength + 2; 142 } 143 return typeList.toArray(new TYPE[typeList.size()]); 144 } 145}