WebRequest对DNS说:没有你我依然可以
前言
标题中的“没有你我依然可以”引用自王杰一首歌的名字《没有你我依然可以》。
WebRequest - 就是大家熟知的System.Net.WebRequest,.NET世界中代码们用的“浏览器”。
DNS - Domain Name System,就是大家熟知的将域名解析为IP地址的系统。
起因
今天园友sixserve在博问上问了一个问题 - C# socket http请求时怎么跟上域名信息:
RT,一个IP上的IIS可能绑定了多个站点,直接请求的话IIS不知道你要请求哪个站点,所以有什么办法可以把域名信息跟上好让IIS知道,不要用WebClient和其他的,这个域名绑定的是内网地址,如果用WebClient、WebRequest这样的话要改host文件了。
问题解读:
1. 如何通过客户端(不是浏览器,是一段C#代码)访问目标Web服务器上绑定的指定域名的站点(比如假设这里是q.cnblogs.com),而这个域名在DNS与客户端的hosts文件中没有对应的IP地址解析,所以只能通过IP地址进行访问(比如这里假设是61.155.169.118)。但是,由于这个Web服务器绑定了多个站点,需要通过不同的主机名(域名)进行区分,仅有IP地址,Web服务器无法知道你要访问的目标主机名。也就是你通过 http://61.155.169.118 无法访问到 http://q.cnblogs.com(由于没有主机名信息);通过 http://q.cnblogs.com 访问,请求根本无法到达目标Web服务器(由于没有DNS解析,客户端不知道目标IP地址)。
2. 问题也就变成了:通过IP地址访问目标Web服务器时,如何将主机名的信息传递给Web服务器?园友sixserve想通过Socket来实现,并强调了不要用WebClient,WebRequest这类的(可能是基于这样的假设:用它们只能在域名与IP地址之间选一个。之前我也是这么认为的)。
问题解决过程
早上看到这个提问时,我立即来了兴趣。因为昨天刚刚学习了阮一峰的互联网协议入门,正在劲头上。
另外,提问者强调了不要用WebRequest,我偏偏要用WebRequest。有首歌叫《偏偏喜欢你》,即使对方说“我一点也不喜欢你”,你也可以继续偏偏喜欢你,执着可以创造奇迹。
这里对WebRequest的执着,并不是偏执,是经过考虑的。只要解决了DNS解析问题,就能用WebRequest通过主机名访问。那如何解决DNS解析的问题呢?不能给DNS添加记录,不能修改hosts文件,那怎么办?
1. 首先,我想到的解决思路是:能不能找到一种方法向通过C#代码向本地DNS缓存中添加一条解析记录。因为在DNS解析过程中,会先在本地DNS缓存中查找。。。但此路不通,未找到实现方法。
2. 后来,在stackoverflow上找到一种解决思路:hook WSOCK32.DLL中对应的API,也就是在客户端进行DNS解析的过程中,会调用WSOCK32.DLL中的API,只要截获这个调用请求,对于要解析的主机名返回对应的IP即可。回答者给出了C++实现代码:
struct hostent FAR * WSAAPI MyGetHostByName(IN const char FAR * name) { // Call the regular function struct hostent* ret = GetHostByNameFunction(name); // Check if it's the hostname you want to reroute if ( strcmp(host, (char*)name) == 0 ) { // Edit the IP returned by the regular gethostbyname ret->h_addr_list[0] = hostIP; ret->h_length = 15; } // Return the result return ret; }
并提供了C#的实现思路,通过 EasyHook 进行hook。
这个方法虽然可行,但太复杂,并没有成为心目中的“她” —— 还是简单点好。
这个复杂的解决方法反而影响了我继续解决问题的兴趣,几乎准备放弃对这个问题的继续研究。
问题解决方法
还好,我没有轻易放弃,继续思考可能的解决方法,并组合各种关键词进行搜索。。。
很多时候,柳暗花明就在你最因难、最无助、最精疲力尽、最想放弃的前方100米。。。
通过关键词“c# add to hosts”,再次来到stackoverflow,在一个提问 Host name resolution without modifying the hosts file 的最佳答案的下方有一个回答,没有任何“推荐”:
当时就眼前一亮,心中隐约感觉到就是“她”。。。接下来只需验证一下刚刚发现的这个“她”是不是就是一直在寻找的“她”。。。
功夫不负有心人,事实证明就是“她”,终于找到了!下面的代码就是见证:
[Fact] //http://q.cnblogs.com/q/38881/ public void Q38881_Test() { var request = WebRequest.Create("http://61.155.169.118") as HttpWebRequest; request.Host = "q.cnblogs.com"; using (var response = request.GetResponse()) { using (var sr = new StreamReader(response.GetResponseStream())) { Console.WriteLine(sr.ReadToEnd()); } } }
代码运行结果:
小结
写了这么多,就是为了这一行代码:request.Host = "q.cnblogs.com"; 等了那么多年,就是为了那一个人。代码如此,生活也是如此。。。结果往往很简单,而真正激动人心的是其中的过程。
这行代码的意义在于可以让WebRequest不依赖DNS,于是才有了这样的故事 —— WebRequest对DNS说:没有你我依然可以。