初始提交

This commit is contained in:
matrixy
2019-04-27 21:21:26 +08:00
commit e5ea15a064
22 changed files with 1453 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target
.idea
*.iml
*test*

13
LICENSE Normal file
View 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.

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
## DNS-Cheater
自建DNS服务器用来做点什么好呢

121
pom.xml Normal file
View 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>

View 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;
}
}

View 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;
}
}

View 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();
}
}

View 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);
}
}
}
}

View 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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);
}
}
}

View 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;
}
}

View 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;
}
}

View 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)
{
// ...
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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