/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.dns;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.StringUtil;
import java.nio.charset.Charset;
import java.util.List;
/**
* DnsQueryEncoder accepts {@link DnsQuery} and encodes to {@link ByteBuf}. This
* class also contains methods for encoding parts of DnsQuery's such as the
* header and questions.
*/
@ChannelHandler.Sharable
public class DnsQueryEncoder extends MessageToMessageEncoder<DnsQuery> {
@Override
protected void encode(ChannelHandlerContext ctx, DnsQuery query, List<Object> out) throws Exception {
ByteBuf buf = ctx.alloc().buffer();
encodeHeader(query.header(), buf);
List<DnsQuestion> questions = query.questions();
for (DnsQuestion question : questions) {
encodeQuestion(question, CharsetUtil.US_ASCII, buf);
}
for (DnsResource resource: query.additionalResources()) {
encodeResource(resource, CharsetUtil.US_ASCII, buf);
}
out.add(new DatagramPacket(buf, query.recipient(), null));
}
/**
* Encodes the information in a {@link DnsHeader} and writes it to the
* specified {@link ByteBuf}. The header is always 12 bytes long.
*
* @param header
* the query header being encoded
* @param buf
* the buffer the encoded data should be written to
*/
private static void encodeHeader(DnsHeader header, ByteBuf buf) {
buf.writeShort(header.id());
int flags = 0;
flags |= header.type() << 15;
flags |= header.opcode() << 14;
flags |= header.isRecursionDesired() ? 1 << 8 : 0;
buf.writeShort(flags);
buf.writeShort(header.questionCount());
buf.writeShort(0); // answerCount
buf.writeShort(0); // authorityResourceCount
buf.writeShort(header.additionalResourceCount());
}
/**
* Encodes the information in a {@link DnsQuestion} and writes it to the
* specified {@link ByteBuf}.
*
* @param question
* the question being encoded
* @param charset
* charset names are encoded in
* @param buf
* the buffer the encoded data should be written to
*/
private static void encodeQuestion(DnsQuestion question, Charset charset, ByteBuf buf) {
encodeName(question.name(), charset, buf);
buf.writeShort(question.type().intValue());
buf.writeShort(question.dnsClass().intValue());
}
private static void encodeResource(DnsResource resource, Charset charset, ByteBuf buf) {
encodeName(resource.name(), charset, buf);
buf.writeShort(resource.type().intValue());
buf.writeShort(resource.dnsClass().intValue());
buf.writeInt((int) resource.timeToLive());
ByteBuf content = resource.content();
int contentLen = content.readableBytes();
buf.writeShort(contentLen);
buf.writeBytes(content, content.readerIndex(), contentLen);
}
private static void encodeName(String name, Charset charset, ByteBuf buf) {
String[] parts = StringUtil.split(name, '.');
for (String part: parts) {
final int partLen = part.length();
if (partLen == 0) {
continue;
}
buf.writeByte(partLen);
buf.writeBytes(part.getBytes(charset));
}
buf.writeByte(0); // marks end of name field
}
}