mirror of
https://gitee.com/matrixy/dns-cheater.git
synced 2026-05-06 16:50:17 +08:00
初始提交
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target
|
||||
.idea
|
||||
*.iml
|
||||
*test*
|
||||
13
LICENSE
Normal file
13
LICENSE
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright [2019] [matrixy]
|
||||
|
||||
Licensed 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.
|
||||
121
pom.xml
Normal file
121
pom.xml
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>cn.org.hentai</groupId>
|
||||
<artifactId>dns-cheater</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax</groupId>
|
||||
<artifactId>javaee-api</artifactId>
|
||||
<version>7.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
<version>1.4.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.21.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>4.4.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<fork>true</fork>
|
||||
<mainClass>cn.org.hentai.dns.app.DNSCheaterApp</mainClass>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
|
||||
<resource>.xxoo</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/spring.handlers</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/spring.schemas</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Main-Class>cn.org.hentai.server.app.ServerApp</Main-Class>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
42
src/main/java/cn/org/hentai/dns/app/DNSCheaterApp.java
Normal file
42
src/main/java/cn/org/hentai/dns/app/DNSCheaterApp.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package cn.org.hentai.dns.app;
|
||||
|
||||
import cn.org.hentai.dns.util.BeanUtils;
|
||||
import cn.org.hentai.dns.util.Configs;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.sqlite.SQLiteDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/19.
|
||||
*/
|
||||
@ComponentScan(value = {"cn.org.hentai"})
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootApplication
|
||||
public class DNSCheaterApp
|
||||
{
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
ApplicationContext context = SpringApplication.run(DNSCheaterApp.class, args);
|
||||
BeanUtils.init(context);
|
||||
Configs.init("/application.properties");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource()
|
||||
{
|
||||
SQLiteDataSource dataSource = new SQLiteDataSource();
|
||||
dataSource.setUrl(env.getProperty("spring.datasource.url"));
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
43
src/main/java/cn/org/hentai/dns/cache/CacheManager.java
vendored
Normal file
43
src/main/java/cn/org/hentai/dns/cache/CacheManager.java
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package cn.org.hentai.dns.cache;
|
||||
|
||||
import cn.org.hentai.dns.dns.entity.ResourceRecord;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/23.
|
||||
* LRC+k缓存实现
|
||||
*/
|
||||
public final class CacheManager
|
||||
{
|
||||
LRU<String, ResourceRecord[]> cachePool = null;
|
||||
|
||||
public ResourceRecord[] get(String key)
|
||||
{
|
||||
return cachePool.get(key);
|
||||
}
|
||||
|
||||
public void put(String key, ResourceRecord[] records)
|
||||
{
|
||||
cachePool.put(key, records);
|
||||
}
|
||||
|
||||
static volatile CacheManager instance;
|
||||
private CacheManager()
|
||||
{
|
||||
cachePool = new LRU<String, ResourceRecord[]>(4096 * 100);
|
||||
}
|
||||
|
||||
public static CacheManager getInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
synchronized (CacheManager.class)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new CacheManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
48
src/main/java/cn/org/hentai/dns/cache/LRU.java
vendored
Normal file
48
src/main/java/cn/org/hentai/dns/cache/LRU.java
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package cn.org.hentai.dns.cache;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/23.
|
||||
*/
|
||||
public class LRU<K, V>
|
||||
{
|
||||
private static final float hashLoadFactory = 0.75f;
|
||||
private LinkedHashMap<K, V> map;
|
||||
private int cacheSize;
|
||||
|
||||
public LRU(int cacheSize)
|
||||
{
|
||||
this.cacheSize = cacheSize;
|
||||
int capacity = (int) Math.ceil(cacheSize / hashLoadFactory) + 1;
|
||||
map = new LinkedHashMap<K, V>(capacity, hashLoadFactory, true)
|
||||
{
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry eldest)
|
||||
{
|
||||
return size() > LRU.this.cacheSize;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized V get(K key)
|
||||
{
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public synchronized void put(K key, V value)
|
||||
{
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public synchronized void clear()
|
||||
{
|
||||
map.clear();
|
||||
}
|
||||
|
||||
public synchronized int usedSize()
|
||||
{
|
||||
return map.size();
|
||||
}
|
||||
}
|
||||
104
src/main/java/cn/org/hentai/dns/dns/NameResolveWorker.java
Normal file
104
src/main/java/cn/org/hentai/dns/dns/NameResolveWorker.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package cn.org.hentai.dns.dns;
|
||||
|
||||
import cn.org.hentai.dns.cache.CacheManager;
|
||||
import cn.org.hentai.dns.dns.coder.SimpleMessageDecoder;
|
||||
import cn.org.hentai.dns.dns.coder.SimpleMessageEncoder;
|
||||
import cn.org.hentai.dns.dns.entity.*;
|
||||
import cn.org.hentai.dns.util.ByteUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/19.
|
||||
*/
|
||||
public class NameResolveWorker extends Thread
|
||||
{
|
||||
static Logger logger = LoggerFactory.getLogger(NameResolveWorker.class);
|
||||
|
||||
NameServer nameServer;
|
||||
ArrayList<Question> questions = new ArrayList(100);
|
||||
ArrayList answers = new ArrayList();
|
||||
|
||||
public NameResolveWorker(NameServer nameServer)
|
||||
{
|
||||
this.nameServer = nameServer;
|
||||
}
|
||||
|
||||
private void getAndResolve() throws Exception
|
||||
{
|
||||
Request request = this.nameServer.takeRequest();
|
||||
if (request == null) return;
|
||||
|
||||
// 消息包解码
|
||||
Message msg = SimpleMessageDecoder.decode(request.packet);
|
||||
logger.debug("decode: TransactionId: {}, Flags: {}, Questions: {}, AnswerRRs: {}, AuthorityRRs: {}, AdditionalRRs: {}", msg.transactionId, Integer.toBinaryString(msg.flags), msg.questions, msg.answerRRs, msg.authorityRRs, msg.additionalRRs);
|
||||
logger.debug("decode flags: QR: {}, OP: {}, AA: {}, TC: {}, RD: {}, RA: {}, RCode: {}", msg.isQuestion(), msg.getOperateType(), msg.isAuthorityAnswer(), msg.isTruncateable(), msg.isRecursiveExpected(), msg.isRecursively(), msg.getReturnCode());
|
||||
|
||||
if (msg.isQuestion() == false)
|
||||
{
|
||||
logger.debug("skip current question: " + ByteUtils.toString(request.packet.nextBytes()));
|
||||
logger.error("要不是我读错了,嗯,只有可能是读错了。。。");
|
||||
// return;
|
||||
}
|
||||
|
||||
// 遍历每一个要查询的域名
|
||||
request.packet.seek(12);
|
||||
int len = 0;
|
||||
questions.clear();
|
||||
for (int i = 0; i < msg.questions; i++)
|
||||
{
|
||||
StringBuilder name = new StringBuilder(64);
|
||||
while ((len = request.packet.nextByte() & 0xff) > 0)
|
||||
{
|
||||
name.append(new String(request.packet.nextBytes(len)));
|
||||
name.append('.');
|
||||
}
|
||||
int queryType = request.packet.nextShort() & 0xffff;
|
||||
int queryClass = request.packet.nextShort() & 0xffff;
|
||||
questions.add(new Question(name.toString(), queryType));
|
||||
}
|
||||
|
||||
// 依次处理,一般来说,都是单个查询的吧,只有自己写程序才有可能会有批量查询的情况
|
||||
if (questions.size() > 1) throw new RuntimeException("multiple name resolve unsupported");
|
||||
CacheManager cacheManager = CacheManager.getInstance();
|
||||
for (Question question : questions)
|
||||
{
|
||||
logger.debug("resolve: name = {}, type = {}", question.name, question.type);
|
||||
if (question.type != Message.TYPE_A && question.type != Message.TYPE_AAAA)
|
||||
{
|
||||
logger.error("unsupported query type: {}", question.type);
|
||||
continue;
|
||||
}
|
||||
ResourceRecord[] answers = cacheManager.get(question.name);
|
||||
// 规则引擎决定了什么东西?
|
||||
if (answers == null)
|
||||
{
|
||||
// 交给递归解析线程去上游服务器解析
|
||||
}
|
||||
else
|
||||
{
|
||||
// 返回结果
|
||||
logger.debug("resolved: name = {}, answer = {}", question.name, answers[0].ipv4);
|
||||
byte[] resp = SimpleMessageEncoder.encode(msg, question, answers);
|
||||
this.nameServer.putResponse(new Response(request.remoteAddress, resp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
while (!this.isInterrupted())
|
||||
{
|
||||
try
|
||||
{
|
||||
getAndResolve();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
logger.error("domain name resolve error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
src/main/java/cn/org/hentai/dns/dns/NameServer.java
Normal file
146
src/main/java/cn/org/hentai/dns/dns/NameServer.java
Normal file
@@ -0,0 +1,146 @@
|
||||
package cn.org.hentai.dns.dns;
|
||||
|
||||
import cn.org.hentai.dns.dns.entity.Request;
|
||||
import cn.org.hentai.dns.dns.entity.Response;
|
||||
import cn.org.hentai.dns.util.ByteUtils;
|
||||
import cn.org.hentai.dns.util.Configs;
|
||||
import cn.org.hentai.dns.util.Packet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/19.
|
||||
*/
|
||||
public class NameServer extends Thread
|
||||
{
|
||||
static Logger logger = LoggerFactory.getLogger(NameServer.class);
|
||||
|
||||
NameResolveWorker[] resolveWorkers = null;
|
||||
ArrayBlockingQueue<Request> queries = null;
|
||||
ArrayBlockingQueue<Response> responses = null;
|
||||
|
||||
AtomicLong totalQueryCount = new AtomicLong(0);
|
||||
|
||||
public NameServer()
|
||||
{
|
||||
this.setName("nameserver-thread");
|
||||
this.resolveWorkers = new NameResolveWorker[Runtime.getRuntime().availableProcessors() * 2];
|
||||
this.queries = new ArrayBlockingQueue<Request>(65535);
|
||||
this.responses = new ArrayBlockingQueue<Response>(65535);
|
||||
for (int i = 0; i < this.resolveWorkers.length; i++)
|
||||
{
|
||||
this.resolveWorkers[i] = new NameResolveWorker(this);
|
||||
this.resolveWorkers[i].setName("name-resolve-worker-" + i);
|
||||
this.resolveWorkers[i].start();
|
||||
}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
DatagramChannel datagramChannel = null;
|
||||
try
|
||||
{
|
||||
int port = Configs.getInt("dns.port", 53);
|
||||
Selector selector = Selector.open();
|
||||
|
||||
datagramChannel = DatagramChannel.open();
|
||||
datagramChannel.socket().bind(new InetSocketAddress(port));
|
||||
datagramChannel.configureBlocking(false);
|
||||
datagramChannel.register(selector, SelectionKey.OP_READ);
|
||||
|
||||
logger.info("NameServer started at: {}", port);
|
||||
|
||||
datagramChannel.configureBlocking(false);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
|
||||
datagramChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
while (!this.isInterrupted())
|
||||
{
|
||||
selector.select();
|
||||
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
SelectionKey selectionKey = (SelectionKey) iterator.next();
|
||||
if (selectionKey.isReadable())
|
||||
{
|
||||
buffer.clear();
|
||||
SocketAddress addr = datagramChannel.receive(buffer);
|
||||
buffer.flip();
|
||||
byte[] message = new byte[buffer.limit()];
|
||||
buffer.get(message, 0, message.length);
|
||||
|
||||
logger.debug("received: from = {}, length = {}, ", addr.toString(), message.length);
|
||||
queries.put(new Request(addr, Packet.create(message)));
|
||||
totalQueryCount.addAndGet(1);
|
||||
}
|
||||
while (selectionKey.isWritable())
|
||||
{
|
||||
if (responses.size() == 0) break;
|
||||
Response response = responses.poll();
|
||||
if (response != null)
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.put(response.packet);
|
||||
buffer.flip();
|
||||
datagramChannel.send(buffer, response.remoteAddress);
|
||||
logger.debug("send: to = {}, length = {}", response.remoteAddress, response.packet.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("nameserver receive error", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { datagramChannel.close(); } catch(Exception e) { }
|
||||
logger.info("NameServer app exited...");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public Request takeRequest()
|
||||
{
|
||||
try
|
||||
{
|
||||
return queries.take();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean putResponse(Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
responses.put(response);
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
new NameServer().start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.org.hentai.dns.dns.coder;
|
||||
|
||||
import cn.org.hentai.dns.dns.entity.Message;
|
||||
import cn.org.hentai.dns.util.ByteUtils;
|
||||
import cn.org.hentai.dns.util.Packet;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/19.
|
||||
* 查询消息包解码器
|
||||
*/
|
||||
public final class SimpleMessageDecoder
|
||||
{
|
||||
public static Message decode(Packet packet)
|
||||
{
|
||||
Message msg = new Message();
|
||||
msg.transactionId = packet.nextShort() & 0xffff;
|
||||
msg.flags = packet.nextShort() & 0xffff;
|
||||
msg.questions = packet.nextShort() & 0xffff;
|
||||
msg.answerRRs = packet.nextShort() & 0xffff;
|
||||
msg.authorityRRs = packet.nextShort() & 0xffff;
|
||||
msg.additionalRRs = packet.nextShort() & 0xffff;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package cn.org.hentai.dns.dns.coder;
|
||||
|
||||
import cn.org.hentai.dns.dns.entity.Message;
|
||||
import cn.org.hentai.dns.dns.entity.Question;
|
||||
import cn.org.hentai.dns.dns.entity.ResourceRecord;
|
||||
import cn.org.hentai.dns.util.ByteUtils;
|
||||
import cn.org.hentai.dns.util.Packet;
|
||||
|
||||
import static cn.org.hentai.dns.dns.entity.Message.TYPE_A;
|
||||
import static cn.org.hentai.dns.dns.entity.Message.TYPE_AAAA;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/23.
|
||||
*/
|
||||
public final class SimpleMessageEncoder
|
||||
{
|
||||
// 创建回应消息包
|
||||
public static byte[] encode(Message query, Question question, ResourceRecord[] answers)
|
||||
{
|
||||
Packet packet = Packet.create(1024);
|
||||
|
||||
// 会话标识
|
||||
packet.addShort((short)(query.transactionId & 0xffff));
|
||||
|
||||
// 标志,始终为成功应答
|
||||
packet.addShort((short)0x8000);
|
||||
|
||||
// 数量
|
||||
packet.addShort((short)0x01).addShort((short)answers.length).addShort((short)0x00).addShort((short)0x00);
|
||||
|
||||
// Queries区域,这里只处理单域名查询的情况
|
||||
packet.addBytes(encodeName(question.name));
|
||||
packet.addShort((short)question.type); // 查询类型
|
||||
packet.addShort((short)0x01); // 查询类,始终为01
|
||||
|
||||
// Resource Record列表
|
||||
for (int i = 0; i < answers.length; i++)
|
||||
{
|
||||
ResourceRecord answer = answers[i];
|
||||
|
||||
// 始终指向查询区的名称
|
||||
packet.addShort((short)0xc00c);
|
||||
packet.addShort((short)answer.type);
|
||||
packet.addShort((short)0x01);
|
||||
packet.addInt(answer.ttl);
|
||||
packet.addShort((short)answer.dlen);
|
||||
if (answer.type == TYPE_A)
|
||||
{
|
||||
packet.addInt(answer.ipv4);
|
||||
}
|
||||
else if (answer.type == TYPE_AAAA)
|
||||
{
|
||||
packet.addBytes(answer.ipv6.getAddress());
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.addBytes(answer.data);
|
||||
}
|
||||
}
|
||||
|
||||
return packet.getBytes();
|
||||
}
|
||||
|
||||
private static byte[] encodeName(String queryName)
|
||||
{
|
||||
byte[] nameBytes = new byte[queryName.length() + 1];
|
||||
int lastHeadIndex = 0, s = 0;
|
||||
for (int i = 0, k = 1; i < queryName.length(); i++, k++)
|
||||
{
|
||||
char chr = queryName.charAt(i);
|
||||
if (chr == '.')
|
||||
{
|
||||
nameBytes[lastHeadIndex] = (byte)s;
|
||||
s = 0;
|
||||
lastHeadIndex = i + 1;
|
||||
continue;
|
||||
}
|
||||
nameBytes[k] = (byte)chr;
|
||||
s += 1;
|
||||
}
|
||||
nameBytes[lastHeadIndex] = (byte)s;
|
||||
return nameBytes;
|
||||
}
|
||||
}
|
||||
79
src/main/java/cn/org/hentai/dns/dns/entity/Message.java
Normal file
79
src/main/java/cn/org/hentai/dns/dns/entity/Message.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package cn.org.hentai.dns.dns.entity;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/19.
|
||||
*/
|
||||
public class Message
|
||||
{
|
||||
public static final int QR_QUESTION = 0x00; // QR类型:问询
|
||||
public static final int QR_RESPONSE = 0x01; // QR类型:响应
|
||||
|
||||
public static final int OP_STANDARD_QUERY = 0x00; // OP类型:标准查询
|
||||
public static final int OP_REVERSE_QUERY = 0x01; // OP类型:反向查询
|
||||
public static final int OP_STATUS_QUER = 0x02; // OP类型,服务器状态查询
|
||||
|
||||
public static final int RCODE_SUCCESS = 0x00; // 返回类型:成功
|
||||
public static final int RCODE_INVALID_NAME = 0x01; // 返回类型:错误的名字
|
||||
public static final int RCODE_SERVER_FAILURE = 0x02; // 返回类型:服务器错误
|
||||
|
||||
public static final int TYPE_A = 1; // 由域名获得IPv4地址
|
||||
public static final int TYPE_NS = 2; // 查询域名服务器
|
||||
public static final int TYPE_CNAME = 5; // 查询规范名称/别名
|
||||
public static final int TYPE_SOA = 6; // 开始授权
|
||||
public static final int TYPE_WKS = 11; // 熟知服务
|
||||
public static final int TYPE_PTR = 12; // 把IP地址转换成域名
|
||||
public static final int TYPE_HINFO = 13; // 主机信息
|
||||
public static final int TYPE_MX = 15; // 邮件交换
|
||||
public static final int TYPE_AAAA = 28; // 由域名获得IPv6地址
|
||||
public static final int TYPE_AXFR = 252; // 传送整个区的请求
|
||||
public static final int TYPE_ANY = 255; // 对所有记录的请求
|
||||
|
||||
public int transactionId; // 会话标识
|
||||
public int flags; // 标志
|
||||
public int questions; // 问题数
|
||||
public int answerRRs; // 回答资源记录数
|
||||
public int authorityRRs; // 授权资源记录数
|
||||
public int additionalRRs; // 附加资源记录数
|
||||
|
||||
public Message()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public int getReturnCode()
|
||||
{
|
||||
return flags & 0x0f;
|
||||
}
|
||||
|
||||
public boolean isRecursively()
|
||||
{
|
||||
return ((flags >> 7) & 0x01) == 1;
|
||||
}
|
||||
|
||||
public boolean isRecursiveExpected()
|
||||
{
|
||||
return ((flags >> 8) & 0x01) == 1;
|
||||
}
|
||||
|
||||
public boolean isTruncateable()
|
||||
{
|
||||
return ((flags >> 9) & 0x01) == 1;
|
||||
}
|
||||
|
||||
public boolean isAuthorityAnswer()
|
||||
{
|
||||
return ((flags >> 10) & 0x01) == 1;
|
||||
}
|
||||
|
||||
public int getOperateType()
|
||||
{
|
||||
return (flags >> 11) & 0x0f;
|
||||
}
|
||||
|
||||
public boolean isQuestion()
|
||||
{
|
||||
return (flags & 0x8000) == 0;
|
||||
}
|
||||
}
|
||||
16
src/main/java/cn/org/hentai/dns/dns/entity/Question.java
Normal file
16
src/main/java/cn/org/hentai/dns/dns/entity/Question.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package cn.org.hentai.dns.dns.entity;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/19.
|
||||
*/
|
||||
public class Question
|
||||
{
|
||||
public String name;
|
||||
public int type;
|
||||
// public int class; // 因为关键字的原因,就不设置此字段了,反正也都是固定值
|
||||
public Question(String name, int type)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
22
src/main/java/cn/org/hentai/dns/dns/entity/Request.java
Normal file
22
src/main/java/cn/org/hentai/dns/dns/entity/Request.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package cn.org.hentai.dns.dns.entity;
|
||||
|
||||
import cn.org.hentai.dns.util.Packet;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/24.
|
||||
*/
|
||||
public class Request
|
||||
{
|
||||
public Packet packet;
|
||||
public SocketAddress remoteAddress;
|
||||
|
||||
public Request(SocketAddress remoteAddress, Packet packet)
|
||||
{
|
||||
this.packet = packet;
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cn.org.hentai.dns.dns.entity;
|
||||
|
||||
import cn.org.hentai.dns.util.ByteUtils;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/23.
|
||||
* 响应消息的资源记录,一般为域名所对应的IP或CNAME等
|
||||
*/
|
||||
public class ResourceRecord
|
||||
{
|
||||
public String name;
|
||||
public int type;
|
||||
// public int class; // 固定值就不声明了
|
||||
public int ttl;
|
||||
public int dlen;
|
||||
public int ipv4; // 如果type == TYPE_A,则使用IP作为data,省得转来转去
|
||||
public Inet6Address ipv6; // 同上
|
||||
public byte[] data;
|
||||
|
||||
public ResourceRecord(String name, int type, int ttl, int ipv4)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.ttl = ttl;
|
||||
this.ipv4 = ipv4;
|
||||
this.dlen = 4;
|
||||
}
|
||||
|
||||
public ResourceRecord(String name, int type, int ttl, Inet6Address ipv6)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.ttl = ttl;
|
||||
this.ipv6 = ipv6;
|
||||
this.dlen = 16;
|
||||
}
|
||||
|
||||
public ResourceRecord(String name, int type, int ttl, byte[] data)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.ttl = ttl;
|
||||
this.data = data;
|
||||
this.dlen = data.length;
|
||||
}
|
||||
|
||||
public String getAnswerString()
|
||||
{
|
||||
if (type == Message.TYPE_A)
|
||||
{
|
||||
int a = (ipv4 >> 24) & 0xff,
|
||||
b = (ipv4 >> 16) & 0xff,
|
||||
c = (ipv4 >> 8) & 0xff,
|
||||
d = ipv4 & 0xff;
|
||||
return a + "." + b + "." + c + "." + d;
|
||||
}
|
||||
else if (type == Message.TYPE_AAAA)
|
||||
{
|
||||
return ipv6.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ByteUtils.toString(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/main/java/cn/org/hentai/dns/dns/entity/Response.java
Normal file
20
src/main/java/cn/org/hentai/dns/dns/entity/Response.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package cn.org.hentai.dns.dns.entity;
|
||||
|
||||
import cn.org.hentai.dns.util.Packet;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/25.
|
||||
*/
|
||||
public class Response
|
||||
{
|
||||
public SocketAddress remoteAddress;
|
||||
public byte[] packet;
|
||||
|
||||
public Response(SocketAddress remoteAddress, byte[] packet)
|
||||
{
|
||||
this.packet = packet;
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
}
|
||||
159
src/main/java/cn/org/hentai/dns/rex/Rule.java
Normal file
159
src/main/java/cn/org/hentai/dns/rex/Rule.java
Normal file
@@ -0,0 +1,159 @@
|
||||
package cn.org.hentai.dns.rex;
|
||||
|
||||
import cn.org.hentai.dns.util.ByteUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.Inet4Address;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2019/4/25.
|
||||
*/
|
||||
public class Rule implements Serializable
|
||||
{
|
||||
Long id;
|
||||
Long ipFrom;
|
||||
Long ipTo;
|
||||
Integer timeFrom;
|
||||
Integer timeTo;
|
||||
Integer netmask;
|
||||
String matchMode;
|
||||
String name;
|
||||
|
||||
public boolean matches(int now, byte[] addr, String domainName)
|
||||
{
|
||||
// 时间段,07:34:11 -> 08:01:01
|
||||
// Inet4Address.getLoopbackAddress();
|
||||
long ip = addr[0] << 24 | addr[1] << 16 | addr[2] << 8 | addr[3];
|
||||
if (ipFrom != null && ip < ipFrom) return false;
|
||||
if (ipTo != null && ip > ipTo) return false;
|
||||
|
||||
// 时间
|
||||
if (timeFrom != null && now < timeFrom) return false;
|
||||
if (timeTo != null && now > timeTo) return false;
|
||||
|
||||
// 域名匹配
|
||||
if ("prefix".equals(matchMode)) return domainName.startsWith(name);
|
||||
else if ("suffix".equals(matchMode)) return domainName.endsWith(name);
|
||||
else return domainName.indexOf(name) > -1;
|
||||
}
|
||||
|
||||
public String getMatchMode() {
|
||||
return matchMode;
|
||||
}
|
||||
|
||||
public Rule setMatchMode(String matchMode) {
|
||||
this.matchMode = matchMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Integer getNetmask() {
|
||||
return netmask;
|
||||
}
|
||||
|
||||
public Rule setNetmask(Integer netmask) {
|
||||
this.netmask = netmask;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rule setId(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getIpFrom() {
|
||||
return ipFrom;
|
||||
}
|
||||
|
||||
public Rule setIpFrom(Long ipFrom) {
|
||||
this.ipFrom = ipFrom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getIpTo() {
|
||||
return ipTo;
|
||||
}
|
||||
|
||||
public Rule setIpTo(Long ipTo) {
|
||||
this.ipTo = ipTo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getTimeFrom() {
|
||||
return timeFrom;
|
||||
}
|
||||
|
||||
public Rule setTimeFrom(Integer timeFrom) {
|
||||
this.timeFrom = timeFrom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getTimeTo() {
|
||||
return timeTo;
|
||||
}
|
||||
|
||||
public Rule setTimeTo(Integer timeTo) {
|
||||
this.timeTo = timeTo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Rule setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rule{" +
|
||||
"id=" + id +
|
||||
", ipFrom=" + ipFrom +
|
||||
", ipTo=" + ipTo +
|
||||
", timeFrom=" + timeFrom +
|
||||
", timeTo=" + timeTo +
|
||||
", netmask=" + netmask +
|
||||
", matchMode='" + matchMode + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Rule)) return false;
|
||||
|
||||
Rule rule = (Rule) o;
|
||||
|
||||
if (getId() != null ? !getId().equals(rule.getId()) : rule.getId() != null) return false;
|
||||
if (getIpFrom() != null ? !getIpFrom().equals(rule.getIpFrom()) : rule.getIpFrom() != null) return false;
|
||||
if (getIpTo() != null ? !getIpTo().equals(rule.getIpTo()) : rule.getIpTo() != null) return false;
|
||||
if (getTimeFrom() != null ? !getTimeFrom().equals(rule.getTimeFrom()) : rule.getTimeFrom() != null)
|
||||
return false;
|
||||
if (getTimeTo() != null ? !getTimeTo().equals(rule.getTimeTo()) : rule.getTimeTo() != null) return false;
|
||||
if (getNetmask() != null ? !getNetmask().equals(rule.getNetmask()) : rule.getNetmask() != null) return false;
|
||||
if (getMatchMode() != null ? !getMatchMode().equals(rule.getMatchMode()) : rule.getMatchMode() != null)
|
||||
return false;
|
||||
if (getName() != null ? !getName().equals(rule.getName()) : rule.getName() != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = getId() != null ? getId().hashCode() : 0;
|
||||
result = 31 * result + (getIpFrom() != null ? getIpFrom().hashCode() : 0);
|
||||
result = 31 * result + (getIpTo() != null ? getIpTo().hashCode() : 0);
|
||||
result = 31 * result + (getTimeFrom() != null ? getTimeFrom().hashCode() : 0);
|
||||
result = 31 * result + (getTimeTo() != null ? getTimeTo().hashCode() : 0);
|
||||
result = 31 * result + (getNetmask() != null ? getNetmask().hashCode() : 0);
|
||||
result = 31 * result + (getMatchMode() != null ? getMatchMode().hashCode() : 0);
|
||||
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
27
src/main/java/cn/org/hentai/dns/util/BeanUtils.java
Normal file
27
src/main/java/cn/org/hentai/dns/util/BeanUtils.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package cn.org.hentai.dns.util;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Created by Expect on 2018/1/25.
|
||||
*/
|
||||
public final class BeanUtils
|
||||
{
|
||||
private static BeanFactory beanFactory;
|
||||
|
||||
public static void init(ApplicationContext context)
|
||||
{
|
||||
BeanUtils.beanFactory = context;
|
||||
}
|
||||
|
||||
public static <T> T create(Class serviceClass)
|
||||
{
|
||||
return (T)beanFactory.getBean(serviceClass);
|
||||
}
|
||||
|
||||
public static void destroy(Object bean)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
149
src/main/java/cn/org/hentai/dns/util/ByteUtils.java
Normal file
149
src/main/java/cn/org/hentai/dns/util/ByteUtils.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package cn.org.hentai.dns.util;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2017/8/22.
|
||||
*/
|
||||
public final class ByteUtils
|
||||
{
|
||||
public static byte[] parse(String hexString)
|
||||
{
|
||||
String[] hexes = hexString.split(" ");
|
||||
byte[] data = new byte[hexes.length];
|
||||
for (int i = 0; i < hexes.length; i++) data[i] = (byte)(Integer.parseInt(hexes[i], 16) & 0xff);
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void dump(byte[] data)
|
||||
{
|
||||
for (int i = 0, l = data.length; i < l; )
|
||||
{
|
||||
String ascii = "";
|
||||
int k = 0, f = 0;
|
||||
for (; k < 16; k++)
|
||||
{
|
||||
if (k + i < l)
|
||||
{
|
||||
f++;
|
||||
byte d = data[i + k];
|
||||
String hex = Integer.toHexString(d & 0xff).toUpperCase();
|
||||
if (hex.length() == 1) hex = "0" + hex;
|
||||
if (d >= 0x20 && d < 127) ascii += (char)d;
|
||||
else ascii += '.';
|
||||
System.out.print(hex);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.print(' ');
|
||||
System.out.print(' ');
|
||||
}
|
||||
|
||||
if (k % 4 == 3) System.out.print(" ");
|
||||
else System.out.print(' ');
|
||||
}
|
||||
i += f;
|
||||
System.out.println(ascii);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(byte[] data)
|
||||
{
|
||||
return toString(data, data.length);
|
||||
}
|
||||
|
||||
public static String toString(byte[] buff, int length)
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(length * 2);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if ((buff[i] & 0xff) < 0x10) sb.append('0');
|
||||
sb.append(Integer.toHexString(buff[i] & 0xff).toUpperCase());
|
||||
sb.append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean getBit(int val, int pos)
|
||||
{
|
||||
return getBit(new byte[] {
|
||||
(byte)((val >> 0) & 0xff),
|
||||
(byte)((val >> 8) & 0xff),
|
||||
(byte)((val >> 16) & 0xff),
|
||||
(byte)((val >> 24) & 0xff)
|
||||
}, pos);
|
||||
}
|
||||
|
||||
public static int reverse(int val)
|
||||
{
|
||||
byte[] bytes = toBytes(val);
|
||||
byte[] ret = new byte[4];
|
||||
for (int i = 0; i < 4; i++) ret[i] = bytes[3 - i];
|
||||
return toInt(ret);
|
||||
}
|
||||
|
||||
public static int toInt(byte[] bytes)
|
||||
{
|
||||
int val = 0;
|
||||
for (int i = 0; i < 4; i++) val |= (bytes[i] & 0xff) << ((3 - i) * 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(int val)
|
||||
{
|
||||
byte[] bytes = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
bytes[i] = (byte)(val >> ((3 - i) * 8) & 0xff);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(long val)
|
||||
{
|
||||
byte[] bytes = new byte[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bytes[i] = (byte)(val >> ((7 - i) * 8) & 0xff);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static int getInt(byte[] data, int offset, int length)
|
||||
{
|
||||
int val = 0;
|
||||
for (int i = 0; i < length; i++) val |= (data[offset + i] & 0xff) << ((length - i - 1) * 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public static long getLong(byte[] data, int offset, int length)
|
||||
{
|
||||
long val = 0;
|
||||
for (int i = 0; i < length; i++) val |= ((long)data[offset + i] & 0xff) << ((length - i - 1) * 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public static boolean getBit(byte[] data, int pos)
|
||||
{
|
||||
return ((data[pos / 8] >> (pos % 8)) & 0x01) == 0x01;
|
||||
}
|
||||
|
||||
public static byte[] concat(byte[]...byteArrays)
|
||||
{
|
||||
int len = 0, index = 0;
|
||||
for (int i = 0; i < byteArrays.length; i++) len += byteArrays[i].length;
|
||||
byte[] buff = new byte[len];
|
||||
for (int i = 0; i < byteArrays.length; i++)
|
||||
{
|
||||
System.arraycopy(byteArrays[i], 0, buff, index, byteArrays[i].length);
|
||||
index += byteArrays[i].length;
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
||||
public static boolean compare(byte[] data1, byte[] data2)
|
||||
{
|
||||
if (data1.length != data2.length) return false;
|
||||
for (int i = 0; i < data1.length; i++)
|
||||
if ((data1[i] & 0xff) != (data2[i] & 0xff)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
48
src/main/java/cn/org/hentai/dns/util/Configs.java
Normal file
48
src/main/java/cn/org/hentai/dns/util/Configs.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package cn.org.hentai.dns.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2017/8/14.
|
||||
*/
|
||||
public final class Configs
|
||||
{
|
||||
static Properties properties = new Properties();
|
||||
|
||||
public static void init(String configFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
File file = new File((configFilePath.startsWith("/") ? "." : "") + configFilePath);
|
||||
if (file.exists()) properties.load(new FileInputStream(file));
|
||||
else properties.load(Configs.class.getResourceAsStream(configFilePath));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String get(String key)
|
||||
{
|
||||
Object val = properties.get(key);
|
||||
if (null == val) return null;
|
||||
else return String.valueOf(val).trim();
|
||||
}
|
||||
|
||||
public static String get(String key, String defaultVal)
|
||||
{
|
||||
Object val = properties.get(key);
|
||||
if (null == val) return defaultVal;
|
||||
else return String.valueOf(val).trim();
|
||||
}
|
||||
|
||||
public static int getInt(String key, int defaultVal)
|
||||
{
|
||||
String val = get(key, String.valueOf(defaultVal));
|
||||
return Integer.parseInt(val);
|
||||
}
|
||||
}
|
||||
202
src/main/java/cn/org/hentai/dns/util/Packet.java
Normal file
202
src/main/java/cn/org/hentai/dns/util/Packet.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package cn.org.hentai.dns.util;
|
||||
|
||||
/**
|
||||
* Created by matrixy on 2018/4/14.
|
||||
*/
|
||||
public class Packet
|
||||
{
|
||||
int size = 0;
|
||||
int offset = 0;
|
||||
int maxSize = 0;
|
||||
public byte[] data;
|
||||
|
||||
private Packet()
|
||||
{
|
||||
// do nothing here..
|
||||
}
|
||||
|
||||
public static Packet create(int length)
|
||||
{
|
||||
Packet p = new Packet();
|
||||
p.data = new byte[length];
|
||||
p.size = 0;
|
||||
p.maxSize = length;
|
||||
return p;
|
||||
}
|
||||
|
||||
public static Packet create(byte[] data)
|
||||
{
|
||||
Packet p = new Packet();
|
||||
p.data = data;
|
||||
p.size = data.length;
|
||||
p.maxSize = data.length;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public Packet addByte(byte b)
|
||||
{
|
||||
this.data[size++] = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet addShort(short s)
|
||||
{
|
||||
this.data[size++] = (byte)((s >> 8) & 0xff);
|
||||
this.data[size++] = (byte)(s & 0xff);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet addInt(int i)
|
||||
{
|
||||
this.data[size++] = (byte)((i >> 24) & 0xff);
|
||||
this.data[size++] = (byte)((i >> 16) & 0xff);
|
||||
this.data[size++] = (byte)((i >> 8) & 0xff);
|
||||
this.data[size++] = (byte)(i & 0xff);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet addLong(long l)
|
||||
{
|
||||
this.data[size++] = (byte)((l >> 56) & 0xff);
|
||||
this.data[size++] = (byte)((l >> 48) & 0xff);
|
||||
this.data[size++] = (byte)((l >> 40) & 0xff);
|
||||
this.data[size++] = (byte)((l >> 32) & 0xff);
|
||||
this.data[size++] = (byte)((l >> 24) & 0xff);
|
||||
this.data[size++] = (byte)((l >> 16) & 0xff);
|
||||
this.data[size++] = (byte)((l >> 8) & 0xff);
|
||||
this.data[size++] = (byte)(l & 0xff);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet addBytes(byte[] b)
|
||||
{
|
||||
System.arraycopy(b, 0, this.data, size, b.length);
|
||||
size += b.length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet addBytes(byte[] b, int offset, int length)
|
||||
{
|
||||
System.arraycopy(b, offset, this.data, size, length);
|
||||
size += length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet reset()
|
||||
{
|
||||
this.offset = 0;
|
||||
this.size = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet rewind()
|
||||
{
|
||||
this.offset = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int offset()
|
||||
{
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
public byte nextByte()
|
||||
{
|
||||
return this.data[offset++];
|
||||
}
|
||||
|
||||
public short nextShort()
|
||||
{
|
||||
return (short)(((this.data[offset++] & 0xff) << 8) | (this.data[offset++] & 0xff));
|
||||
}
|
||||
|
||||
public int nextInt()
|
||||
{
|
||||
return (this.data[offset++] & 0xff) << 24 | (this.data[offset++] & 0xff) << 16 | (this.data[offset++] & 0xff) << 8 | (this.data[offset++] & 0xff);
|
||||
}
|
||||
|
||||
public long nextLong()
|
||||
{
|
||||
return ((long)this.data[offset++] & 0xff) << 56
|
||||
| ((long)this.data[offset++] & 0xff) << 48
|
||||
| ((long)this.data[offset++] & 0xff) << 40
|
||||
| ((long)this.data[offset++] & 0xff) << 32
|
||||
| ((long)this.data[offset++] & 0xff) << 24
|
||||
| ((long)this.data[offset++] & 0xff) << 16
|
||||
| ((long)this.data[offset++] & 0xff) << 8
|
||||
| ((long)this.data[offset++] & 0xff);
|
||||
}
|
||||
|
||||
public byte[] nextBytes(int length)
|
||||
{
|
||||
byte[] buf = new byte[length];
|
||||
System.arraycopy(this.data, offset, buf, 0, length);
|
||||
offset += length;
|
||||
return buf;
|
||||
}
|
||||
|
||||
public byte[] nextBytes()
|
||||
{
|
||||
byte[] buf = new byte[this.size - this.offset];
|
||||
System.arraycopy(this.data, offset, buf, 0, buf.length);
|
||||
offset += buf.length;
|
||||
return buf;
|
||||
}
|
||||
|
||||
public byte get(int position)
|
||||
{
|
||||
return this.data[position];
|
||||
}
|
||||
|
||||
public byte[] get(int offset, int length)
|
||||
{
|
||||
byte[] buf = new byte[length];
|
||||
System.arraycopy(this.data, offset, buf, 0, length);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public Packet skip(int offset)
|
||||
{
|
||||
this.offset += offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Packet seek(int index)
|
||||
{
|
||||
this.offset = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasMoreBytes()
|
||||
{
|
||||
return this.offset < this.size;
|
||||
}
|
||||
|
||||
public byte[] getBytes()
|
||||
{
|
||||
if (size == maxSize) return this.data;
|
||||
else
|
||||
{
|
||||
byte[] buff = new byte[size];
|
||||
System.arraycopy(this.data, 0, buff, 0, size);
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制len个字节,到dest的offset位置处
|
||||
* @param dest
|
||||
* @param offset
|
||||
* @param len
|
||||
*/
|
||||
public void copyBytes(byte[] dest, int offset, int len)
|
||||
{
|
||||
System.arraycopy(this.data, this.offset, dest, offset, len);
|
||||
this.offset += len;
|
||||
}
|
||||
}
|
||||
32
src/main/resources/application.properties
Normal file
32
src/main/resources/application.properties
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
server.remotePort=80
|
||||
|
||||
dns.remotePort = 53
|
||||
dns.upstream.server = 180.76.76.76
|
||||
|
||||
# 设定ftl文件路径
|
||||
spring.freemarker.template-loader-path=classpath:/templates
|
||||
|
||||
# 设定静态文件路径,js,css等
|
||||
spring.mvc.static-path-pattern=/static/**
|
||||
|
||||
# 最大文件上传大小
|
||||
spring.http.multipart.maxFileSize=1024MB
|
||||
spring.http.multipart.maxRequestSize=1024MB
|
||||
|
||||
# sqlite url
|
||||
spring.datasource.url=jdbc:sqlite:nameserver-cheater.sqlite
|
||||
|
||||
log4j.rootLogger = debug,ServerDailyRollingFile,stdout
|
||||
|
||||
log4j.appender.ServerDailyRollingFile = org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.ServerDailyRollingFile.DatePattern = '.'yyyy-MM-dd
|
||||
log4j.appender.ServerDailyRollingFile.File = cheater.log
|
||||
log4j.appender.ServerDailyRollingFile.layout = org.apache.log4j.PatternLayout
|
||||
log4j.appender.ServerDailyRollingFile.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [%t] %-5p [%c] - %m%n
|
||||
log4j.appender.ServerDailyRollingFile.Append = true
|
||||
|
||||
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern = %d yyyy-MM-dd HH:mm:ss %p [%c] %m%n
|
||||
log4j.cn.org.hentai = debug
|
||||
Reference in New Issue
Block a user