001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021package org.apache.directory.server.dns.io.encoder; 022 023 024import java.io.IOException; 025import java.util.Collections; 026import java.util.EnumMap; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.directory.server.dns.messages.DnsMessage; 032import org.apache.directory.server.dns.messages.MessageType; 033import org.apache.directory.server.dns.messages.OpCode; 034import org.apache.directory.server.dns.messages.QuestionRecord; 035import org.apache.directory.server.dns.messages.RecordType; 036import org.apache.directory.server.dns.messages.ResourceRecord; 037import org.apache.directory.server.dns.messages.ResponseCode; 038import org.apache.directory.server.i18n.I18n; 039import org.apache.mina.core.buffer.IoBuffer; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * An encoder for DNS messages. The primary usage of the DnsMessageEncoder is 046 * to call the <code>encode(ByteBuffer, DnsMessage)</code> method which will 047 * write the message to the outgoing ByteBuffer according to the DnsMessage 048 * encoding in RFC-1035. 049 * 050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 051 */ 052public class DnsMessageEncoder 053{ 054 /** the log for this class */ 055 private static final Logger LOG = LoggerFactory.getLogger( DnsMessageEncoder.class ); 056 057 /** 058 * A Hashed Adapter mapping record types to their encoders. 059 */ 060 private static final Map<RecordType, RecordEncoder> DEFAULT_ENCODERS; 061 062 static 063 { 064 EnumMap<RecordType, RecordEncoder> map = new EnumMap<>( RecordType.class ); 065 066 map.put( RecordType.SOA, new StartOfAuthorityRecordEncoder() ); 067 map.put( RecordType.A, new AddressRecordEncoder() ); 068 map.put( RecordType.NS, new NameServerRecordEncoder() ); 069 map.put( RecordType.CNAME, new CanonicalNameRecordEncoder() ); 070 map.put( RecordType.PTR, new PointerRecordEncoder() ); 071 map.put( RecordType.MX, new MailExchangeRecordEncoder() ); 072 map.put( RecordType.SRV, new ServerSelectionRecordEncoder() ); 073 map.put( RecordType.TXT, new TextRecordEncoder() ); 074 075 DEFAULT_ENCODERS = Collections.unmodifiableMap( map ); 076 } 077 078 079 /** 080 * Encodes the {@link DnsMessage} into the {@link IoBuffer}. 081 * 082 * @param byteBuffer 083 * @param message 084 */ 085 public void encode( IoBuffer byteBuffer, DnsMessage message ) 086 { 087 byteBuffer.putShort( ( short ) message.getTransactionId() ); 088 089 byte header = ( byte ) 0x00; 090 header |= encodeMessageType( message.getMessageType() ); 091 header |= encodeOpCode( message.getOpCode() ); 092 header |= encodeAuthoritativeAnswer( message.isAuthoritativeAnswer() ); 093 header |= encodeTruncated( message.isTruncated() ); 094 header |= encodeRecursionDesired( message.isRecursionDesired() ); 095 byteBuffer.put( header ); 096 097 header = ( byte ) 0x00; 098 header |= encodeRecursionAvailable( message.isRecursionAvailable() ); 099 header |= encodeResponseCode( message.getResponseCode() ); 100 byteBuffer.put( header ); 101 102 byteBuffer 103 .putShort( ( short ) ( message.getQuestionRecords() != null ? message.getQuestionRecords().size() : 0 ) ); 104 byteBuffer.putShort( ( short ) ( message.getAnswerRecords() != null ? message.getAnswerRecords().size() : 0 ) ); 105 byteBuffer.putShort( ( short ) ( message.getAuthorityRecords() != null ? message.getAuthorityRecords().size() 106 : 0 ) ); 107 byteBuffer.putShort( ( short ) ( message.getAdditionalRecords() != null ? message.getAdditionalRecords().size() 108 : 0 ) ); 109 110 putQuestionRecords( byteBuffer, message.getQuestionRecords() ); 111 putResourceRecords( byteBuffer, message.getAnswerRecords() ); 112 putResourceRecords( byteBuffer, message.getAuthorityRecords() ); 113 putResourceRecords( byteBuffer, message.getAdditionalRecords() ); 114 } 115 116 117 private void putQuestionRecords( IoBuffer byteBuffer, List<QuestionRecord> questions ) 118 { 119 if ( questions == null ) 120 { 121 return; 122 } 123 124 QuestionRecordEncoder encoder = new QuestionRecordEncoder(); 125 126 Iterator<QuestionRecord> it = questions.iterator(); 127 128 while ( it.hasNext() ) 129 { 130 QuestionRecord question = it.next(); 131 encoder.put( byteBuffer, question ); 132 } 133 } 134 135 136 private void putResourceRecords( IoBuffer byteBuffer, List<ResourceRecord> records ) 137 { 138 if ( records == null ) 139 { 140 return; 141 } 142 143 Iterator<ResourceRecord> it = records.iterator(); 144 145 while ( it.hasNext() ) 146 { 147 ResourceRecord record = it.next(); 148 149 try 150 { 151 put( byteBuffer, record ); 152 } 153 catch ( IOException ioe ) 154 { 155 LOG.error( ioe.getLocalizedMessage(), ioe ); 156 } 157 } 158 } 159 160 161 private void put( IoBuffer byteBuffer, ResourceRecord record ) throws IOException 162 { 163 RecordType type = record.getRecordType(); 164 165 RecordEncoder encoder = DEFAULT_ENCODERS.get( type ); 166 167 if ( encoder == null ) 168 { 169 throw new IOException( I18n.err( I18n.ERR_597, type ) ); 170 } 171 172 encoder.put( byteBuffer, record ); 173 } 174 175 176 private byte encodeMessageType( MessageType messageType ) 177 { 178 byte oneBit = ( byte ) ( messageType.convert() & 0x01 ); 179 return ( byte ) ( oneBit << 7 ); 180 } 181 182 183 private byte encodeOpCode( OpCode opCode ) 184 { 185 byte fourBits = ( byte ) ( opCode.convert() & 0x0F ); 186 return ( byte ) ( fourBits << 3 ); 187 } 188 189 190 private byte encodeAuthoritativeAnswer( boolean authoritative ) 191 { 192 if ( authoritative ) 193 { 194 return ( byte ) ( ( byte ) 0x01 << 2 ); 195 } 196 return ( byte ) 0; 197 } 198 199 200 private byte encodeTruncated( boolean truncated ) 201 { 202 if ( truncated ) 203 { 204 return ( byte ) ( ( byte ) 0x01 << 1 ); 205 } 206 return 0; 207 } 208 209 210 private byte encodeRecursionDesired( boolean recursionDesired ) 211 { 212 if ( recursionDesired ) 213 { 214 return ( byte ) 0x01; 215 } 216 return 0; 217 } 218 219 220 private byte encodeRecursionAvailable( boolean recursionAvailable ) 221 { 222 if ( recursionAvailable ) 223 { 224 return ( byte ) ( ( byte ) 0x01 << 7 ); 225 } 226 return 0; 227 } 228 229 230 private byte encodeResponseCode( ResponseCode responseCode ) 231 { 232 return ( byte ) ( responseCode.convert() & 0x0F ); 233 } 234}