ESP8266开发之旅 网络篇⑮ DNSServer——真正的域名服务
1. 前言
Arduino for esp8266中有两个DNS服务相关的库:
- ESP8266mDNS库
- 这个库是mDNS库,使用这个库的时候ESP8266可以在AP模式或是以STA模式接入局域网;
- 局域网中的其他开启mDNS服务的设备就可以通过网址访问ESP8266;
- 这个博主在之前的博文中有讲解过 —— ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库;
- 有个明显缺点,需要其他设备也开启mDNS服务,像window系统需要安装一个 Bonjour,同时域名为 xxx.local;
- DNSServer库
- 这个库就是本文将用到的建立DNS服务的方式,使用该库时ESP8266必须工作在AP模式下;
- 这个属于真正意义的精简版DNS服务器;
- DNSServer运行于 UDP服务,请回顾:ESP8266开发之旅 网络篇⑩ UDP服务;
看看DNSServer具体工作原理:
- 在这里,DNS服务器唯一的作用就是把域名转成对应映射的地址
2. DNS server库
ESP8266使用DNS服务(一般和WebServer服务一起使用,WebServer请回顾 ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用),请在代码中加入以下头文件:
#include <DNSServer.h>
讲解方法之前,先来看看博主总结的百度脑图:
常用方法非常简单,就4个方法,毕竟DNS服务器的功能比较单一。
2.1 start —— 启动DNS服务器
函数说明:
/**
* 启动DNS服务器
* @param port 端口号 DNS端口一般占用53
* @param domainName 映射域名
* @param resolvedIP 映射IP地址
* @return bool 是否启动成功
*/
bool start(const uint16_t &port,
const String &domainName,
const IPAddress &resolvedIP);
源码说明:
bool DNSServer::start(const uint16_t &port, const String &domainName,
const IPAddress &resolvedIP)
{
_port = port;
_buffer = NULL;
_domainName = domainName;
_resolvedIP[0] = resolvedIP[0];
_resolvedIP[1] = resolvedIP[1];
_resolvedIP[2] = resolvedIP[2];
_resolvedIP[3] = resolvedIP[3];
downcaseAndRemoveWwwPrefix(_domainName);
//启动了UDP服务 监听客户端向DNS服务器查询域名
return _udp.begin(_port) == 1;
}
2.2 stop —— 停止DNS服务器
函数说明:
/**
* 停止DNS服务器
*/
void stop();
源码说明:
void DNSServer::stop()
{
//停止udp服务
_udp.stop();
free(_buffer);
_buffer = NULL;
}
2.3 setErrorReplyCode —— 设置错误响应码
函数说明:
/**
* 设置错误响应码
* @param DNSReplyCode 错误响应码
*/
void setErrorReplyCode(const DNSReplyCode &replyCode);
DNSReplyCode 定义如下:
enum class DNSReplyCode
{
NoError = 0,
FormError = 1,
ServerFailure = 2, //服务错误
NonExistentDomain = 3,
NotImplemented = 4,//未定义
Refused = 5,//拒绝访问
YXDomain = 6,
YXRRSet = 7,
NXRRSet = 8
};
2.4 processNextRequest —— 处理DNS请求服务
函数说明:
/**
* 处理DNS请求服务
*/
void processNextRequest();
源码说明:
/**
* 处理DNS请求服务
*/
void DNSServer::processNextRequest()
{
//获取UDP请求内容
_currentPacketSize = _udp.parsePacket();
if (_currentPacketSize)
{
if (_buffer != NULL) free(_buffer);
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
if (_buffer == NULL) return;
_udp.read(_buffer, _currentPacketSize);
_dnsHeader = (DNSHeader*) _buffer;
//判断请求是否查找域名映射的IP地址 *在这里有非常特殊作用 读者请注意
if (_dnsHeader->QR == DNS_QR_QUERY &&
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
requestIncludesOnlyOneQuestion() &&
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName)
)
{
//返回IP地址
replyWithIP();
}
else if (_dnsHeader->QR == DNS_QR_QUERY)
{
//响应错误码
replyWithCustomCode();
}
free(_buffer);
_buffer = NULL;
}
}
/**
* 响应域名对应的IP地址
*/
void DNSServer::replyWithIP()
{
if (_buffer == NULL) return;
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->ANCount = _dnsHeader->QDCount;
_dnsHeader->QDCount = _dnsHeader->QDCount;
//_dnsHeader->RA = 1;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, _currentPacketSize);
_udp.write((uint8_t)192); // answer name is a pointer
_udp.write((uint8_t)12); // pointer to offset at 0x00c
_udp.write((uint8_t)0); // 0x0001 answer is type A query (host address)
_udp.write((uint8_t)1);
_udp.write((uint8_t)0); //0x0001 answer is class IN (internet address)
_udp.write((uint8_t)1);
_udp.write((unsigned char*)&_ttl, 4);
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
_udp.write((uint8_t)0);
_udp.write((uint8_t)4);
_udp.write(_resolvedIP, sizeof(_resolvedIP));
_udp.endPacket();
#ifdef DEBUG_ESP_DNS
DEBUG_ESP_PORT.printf("DNS responds: %s for %s\n",
IPAddress(_resolvedIP).toString().c_str(), getDomainNameWithoutWwwPrefix().c_str() );
#endif
}
/**
* 响应错误码
*/
void DNSServer::replyWithCustomCode()
{
if (_buffer == NULL) return;
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->RCode = (unsigned char)_errorReplyCode;
_dnsHeader->QDCount = 0;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, sizeof(DNSHeader));
_udp.endPacket();
}
注意点:
- ESP8266 DNSServer 运行于UDP协议之上;
- ESP8266 DNSServer只能支持一个域名映射;
- 当ESP8266设置的域名为“*”,意味着所有请求都会被链接到该IP地址,我们可以利用这一点做一些特殊操作;
3. 实例
3.1 访问主机名
实验说明
在手机浏览器访问 "www.danpianji.com"会显示“Hello World”
实验源码
/**
* 功能描述:在手机浏览器访问 "www.danpianji.com"会显示“Hello World”
*/
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);
void setup() {
DebugBegin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer example");
// modify TTL associated with the domain name (in seconds)
// default is 60 seconds
dnsServer.setTTL(300);
// set which return code will be used for all other domains (e.g. sending
// ServerFailure instead of NonExistentDomain will reduce number of queries
// sent by clients)
// default is DNSReplyCode::NonExistentDomain
dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure);
// 启动DNS server,映射主机名为 www.danpianji.com
bool status = dnsServer.start(DNS_PORT, "www.danpianji.com", apIP);
if(status){
DebugPrintln("start dnsserver success.");
}else{
DebugPrintln("start dnsserver failed.");
}
// simple HTTP server to see that DNS server is working
webServer.onNotFound([]() {
String message = "Hello World!\n\n";
message += "URI: ";
message += webServer.uri();
webServer.send(200, "text/plain", message);
});
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}
实验结果
会看到一个DNSServer example开放式AP热点,连接上:
然后在手机浏览器访问 www.danpianji.com
3.2 Portal 认证
实验说明
通常,当我们连上一些wifi热点,只要没有认证手机号码信息,无论访问哪个页面都会弹出一个web认证页面(这就是商家用来收集手机用户信息的一种手段,慎重),这就是 Portal 认证。
Portal服务器也就是接收Portal客户端认证请求的服务器端系统,其主要作用是提供免费的门户服务和基于Web认证的界面,以及接入设备交互认证客户端的认证信息。其中的Web认证方案首先需要给用户分配一个地址,用于访问门户网站。
Portal 基于浏览器,采用的是B/S构架, 对不同权限的用户下发不同的VLAN 访问不同的服务器资源,当通过认证后才能访问internet资源,Portal认证方式不需要安装认证客户端, 减少了客户端的维护工作量,便于运营。
可以在Portal页面上开展业务拓展,如展示商家广告, 联系方式等基本信息。Portal广泛应用于运营商、学校等网络。
通过DNSServer,我们也可以使用到Portal认证
实验源码
/**
* 功能描述:portal认证
*/
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
//调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintF(...) Serial.printf( __VA_ARGS__ )
const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(80);
String responseHTML = ""
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
"be redirected here.</p></body></html>";
void setup() {
DebugBegin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP("DNSServer CaptivePortal example");
// 所有请求都映射到一个具体地址
dnsServer.start(DNS_PORT, "*", apIP);
// replay to all requests with same HTML
webServer.onNotFound([]() {
DebugPrintln("webServer handle.");
webServer.send(200, "text/html", responseHTML);
});
webServer.begin();
}
void loop() {
dnsServer.processNextRequest();
webServer.handleClient();
}
实验结果
会看到一个 DNSServer CaptivePortal example 开放式AP热点,连接上:
然后在手机浏览器访问 www.danpianji.com
4. 总结
DNSServer也是相对来说非常重要的一章,特别对于web配网,需要使用到它,请读者认真理解使用。
ESP8266开发之旅 网络篇⑮ DNSServer——真正的域名服务的更多相关文章
- ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑩ UDP服务
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 SPIFFS文件系统
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...
- ESP8266开发之旅 网络篇⑭ web配网
1. 前言 目前,市面上流行多种配网方式: WIFI模块的智能配网(SmartConfig以及微信AirKiss配网) SmartConfig 配网方式 请参考博主之前的博文 ESP8266开 ...
- ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
1. 前言 在前面的篇章中,博主给大家讲解了ESP8266的软硬件配置以及基本功能使用,目的就是想让大家有个初步认识.并且,博主一直重点强调 ESP8266 WiFi模块有三种工作模式: St ...
随机推荐
- Asp.Net Core2.2 源码阅读系列——控制台日志源码解析
为了让我们第一时间知道程序的运行状态,Asp.Net Core 添加了默认的日志输出服务.这看起来并没有什么问题,对于开发人员也相当友好,但如果不了解日志输出的细节,也有可能因为错误的日志级别配置 ...
- OAuth2.0摘要
一.简介 不使用oauth2.0协议,资源所有者直接给需要使用资源的第三方应用共享凭据时,有这些问题: 需要直接共享给第三方应用凭据 需要服务器支持密码身份验证 凭据的访问权限过大,失去对访问时间和范 ...
- Hadoop入门 之 Hadoop的安装
1.安装Hadoop的三大步骤 答:1.Linux环境,2.JDK环境,3.配置Hadoop. 2.安装Linux 答:利用阿里云,腾讯云等公有云.选择Ubuntu进行安装,然后利用小putty进行操 ...
- php根据经纬度排序,根据经纬度筛选距离段
SQL 语句:select location.* from (select *,round(6378.138*2*asin(sqrt(pow(sin( (36.668530*pi()/180-px_l ...
- 2019年9月末周java面试总结
不知不觉离职已经2个月了,这周开始投简历找工作,本来也做好了被打击的心理准备了,毕竟这么久没敲代码,也没怎么准备,基本上是属于裸面. 总结一下简历投递情况: 不知道是简历写得太敷衍,还是要求太高,总之 ...
- 从壹开始学习 NetCore 新篇章 ║ Blog.Core 开发社之招募计划书
宫 哈喽大家好,国庆马上就要来了,在新的第四季度来临之际,祝大家年末能顺顺利利,解决所有的难题.大家可能从我的标题里也能看的出来,老张又要耍花样,搞事情了,近来随着 netcore 3.0 的正式推出 ...
- 为RecyclerView定制可滑动的Item
最近项目有需要弄一个可以像手机QQ会话页一样可以滑动的小菜单,每一个Item当用户在向左滑动的时候右侧会出现一个小菜单当时就想在也不是很难心想着找个开源的使用就好呢,但是我的项目是用的Recycler ...
- 深度解密Go语言之反射
目录 什么是反射 为什么要用反射 反射是如何实现的 types 和 interface 反射的基本函数 反射的三大定律 反射相关函数的使用 代码样例 未导出成员 反射的实际应用 json 序列化 De ...
- Kubernetes 系列(四):使用Traefik访问.net core api
一. 准备 本篇的要求是在前三篇的基础上已经搭建好的本地k8s以及部署了Traefik,我们将会使用Traefik Ingress来访问.net core api,比较简单,做个记录,如果还没有搭建k ...
- Git基础概念与Flow流程介绍
目录 Git相关 基本概念 常见客户端 TortoiseGit Sourcetree Intellij Idea 命令行 常用命令 存储区域 命令之 add & commit &pus ...