NBIoT服务器端(1)实现UDP访问DNS服务获取IP地址

背景

最近在准备NB-IoT相关的一个毕设,用到了BC95模块,使用的是BC-95的B5版本(也就是电信专用)。如今已经实现了通过UDP转发服务器将自己的数据上传到自己的服务器;在服务器端构建了一个简单的回传UDP服务,并输出了一个简单的log信息,这部分内容在随后的笔记中我会再做详细的补充。 
由于NB-IoT现阶段访问一个服务器只能通过IP地址加端口的方式,省去了DNS解析,如果产品以后的IP变化或者改变了服务端的IP地址,就需要一个DNS解析的功能。 
UDP实现DNS解析我想到了两个方法:

  • 直接通过转发服务器,将要进行的主机名发给我自己的服务器进行解析,这样的好处是,数据可以自定义,核心主要在服务器端进行DNS解析的处理;
  • 第二个方法,直接在物端采用socket方式实现DNS协议的客户端,使用单片机将UDP的数据经过NB-IoT直接发送到DNS服务器(例如114.114.114.114,国外可以使用Google的8.8.8.8和4.4.4.4),直接得到IP地址不经过中间服务器转发,这同样需要在购买NB卡时绑定一到多个DNS服务器。

综合考虑以上两种方法的优劣后,决定采用第二种比较稳妥的方法,万一自己的服务器突然宕机就GG了 (<·_<·)滑稽脸。 
最终的功能将会在MCU上实现,通过NB-IOT访问服务器。 
首先,参考了网上现有的一些博客,使用socket方法在linux主机上实现UDP获取IP,编程语言采用python。

预备知识

关于DNS解析服务,贴一段百度百科的词条:

人们习惯记忆域名,但机器间互相只认IP地址,域名与IP地址之间是多对一的关系,一个ip地址不一定只对应一个域名,且一个域名只可以对应一个ip地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,整个过程是自动进行的

如果还是不懂的话,就真的没救了~~

其实还可以救一下,windows系统按下win+R输入“cmd”后回车,在出来的DOS窗口中输入 “ping baidu.com”回车,你就会看到百度服务器的IP了,当然多试几次可能不同,毕竟百度大佬有好几天服务器,他也怕宕机嘛不是。贴个图感受下百度大佬的IP地址: 
cmd里面进行DNS解析 
这里的IP可以直接输入到任意一个浏览器的地址栏,你就会看到熟悉的一幕了233333 
正文马上就到。。。。 
查阅相关资料后发现,DNS服务是在53端口,使用UDP进行传输,当然也有TCP的53传输但主要是在域名服务器与辅域名服务器之间传输数据同步时采用。 
DNS数据包包括DNS协议头+DNS正文段,详细内容可参考文章《DNS协议及应用》。协议头为固定的12字节,结构如下:

typedef struct _DNS_HDR 
{

  U16 id; 
  U16 tag; 

  U16 numq; 
  U16 numa; 
  U16 numa1; 

  U16 numa2; 
}DNS_HDR;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

严格的结构体中是使用的位字段。初始化的时候置id为1,tag为网络字节序的0x0100,numq为1即可。其他都为0。即请求一次递归查询。至于查询什么内容,则由DNS数据包的正文决定。查询ip的正文包括域名值和请求类型标识字段。其中请求类型定义为结构体:

typedef struct _DNS_QER 
{ 

  U16 type; 
  U16 classes; 
}DNS_QER;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

type表明请求类型,我们是获取域名ip即请求主机A记录,此值为网络字节序的1,classes表示internet问题类,通常也是网络字节序的1。如此我们的DNS数据包格式就是:DNS_HDR+域名+DNS_QER。 
以上的是从socket编程实现简单DNS协议实现获取域名ip(UDP)找到的,如果想通过C语言实现的话,可以参考这篇文章。 
最后找到一个使用python实现的,源代码出处:python:简单的DNS解析恬悦园新浪博客,当然我做了一点修改,实现了通过命令行调用参数。

代码

直接上代码:

#!/usr/bin/env python
# -*- coding:utf8 -*-

import socket
import os
import sys
import re

def reply_to_iplist(data):
    assert isinstance(data, basestring)
    iplist = ['.'.join(str(ord(x)) for x in s) for s in re.findall('\xc0.\x00\x01\x00\x01.{6}(.{4})', data) if all(ord(x) <= 255 for x in s)]
    return iplist

def domain_to_ip(dnsserver,domain):
    dnsserver = dnsserver
    seqid = os.urandom(2)
    host = ''.join(chr(len(x))+x for x in domain.split('.'))
    data = '%s\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00%s\x00\x00\x01\x00\x01' % (seqid, host)
    sock = socket.socket(socket.AF_INET,type=socket.SOCK_DGRAM)
    sock.settimeout(None)
    sock.sendto(data, (dnsserver, 53))
    data = sock.recv(512)
    return reply_to_iplist(data)

Server = "114.114.114.114"

def main(argv):
    if len(argv)!=2:
        print('please follow a host name! \neg:python dns_client.py baidu.com')
    else:
        p = domain_to_ip(dnsServer,argv[1])
        print "the ip address of "+argv[1]+" are as follows"
        print p
if __name__ == '__main__':
    main(sys.argv)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

运行结果如下: 
运行结果 
再贴一个参数错误的情况: 
错误调用
最最最最后,欢迎小伙伴们交流,一起学习

转载请注明出处

转载自https://blog.csdn.net/a1668659995

猜你喜欢

转载自blog.csdn.net/sim_stone/article/details/80354007