写一个与DNS通信获取MX记录的C++类

上一篇文章用telnet发送电子邮件提到通过nslookup获取域名的MX记录。
如果在程序里实现SMTP协议就不可能依赖于nslookup程序了。写一个与DNS通信获取MX记录的C++类。
效果如图所示:

DNS通信协议请参见RFC1035

实现代码如下:

#include <stdio.h>
#ifdef __WIN32__
#include <winsock.h>
#include <wininet.h>
#define SHUT_RDWR SD_BOTH
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#define closesocket close
#endif
#include <assert.h>
#include <string.h>
#include <string>
#include <vector>
using namespace std;

struct DnsMxRecord {
	int		preference;
	string	address;
};

class DnsQuery
{
private:
	int		socketHandle;
	string	nameServer;
	char	buffer[512+4];
	int		bufferPos;
	int 	errorCode;
	vector<DnsMxRecord>	mxRecords;

	string	parseName(int &pos) {
		string name;
		char temp[256];
		while(buffer[pos]) {
			int length = (unsigned char)buffer[pos ++];
			if(length == 0xC0) {
				int newPos = (unsigned char)buffer[pos ++];
				if(name.empty())
					name = parseName(newPos);
				else
					name = name + "." + parseName(newPos);
				break;
			} else {
				memcpy(temp, &buffer[pos], length);
				pos += length;
				temp[length] = '\0';
				if(name.empty())
					name = temp;
				else
					name = name + "." + temp;
			}
		}
		return name;
	}
public:
	DnsQuery() {
		socketHandle = -1;
	}
	~DnsQuery() {
		if(socketHandle != -1)
			closesocket(socketHandle);
	}

	void	setNameServer(string ns) {
		this->nameServer = ns;
	}

	string	getErrorString() {
		static char errors[][32] = {
			"No error.",	"Incorrect message format.",	"DNS error.",
			"Domain name not found.",	"Unimplemented command.",	"Denied."
		};
		if(errorCode < 6)
			return errors[errorCode];
		else
			return "Unknown error.";
	}

	bool	connect() {
		/* Create a UDP socket */
		socketHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		assert(socketHandle != -1);
		/* Connect to the name server */
		struct sockaddr_in addr = {0,};
		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = inet_addr(nameServer.c_str());
		addr.sin_port = htons(53);
		return (0 == ::connect(socketHandle, (sockaddr*)&addr, sizeof(addr)));
	}

	bool	queryMx(string name) {
		printf("queryMx: %s\n", name.c_str());
		errorCode = 0;
		/* Initialize MX query header */
		static const char queryHeader[] = {0x00,0x01,0x01,0x00,0x00,0x01,
		                                   0x00,0x00,0x00,0x00,0x00,0x00
		                                  };
		memcpy(buffer, queryHeader, sizeof(queryHeader));
		bufferPos = sizeof(queryHeader);
		/* put domain name */
		for(int next, current = 0; current<name.length(); ) {
			/* divide by dot */
			next = name.find('.', current);
			if(next == -1)
				next = name.length();
			/* length of part */
			int length = next - current;
			buffer[bufferPos ++] = length;
			memcpy(buffer + bufferPos, name.c_str() + current, length);
			bufferPos += length;
			current = next + 1;
		}
		buffer[bufferPos ++] = '\0';
		/* query type and class */
		*((unsigned int*)&buffer[bufferPos]) = 0x01000F00;
		bufferPos += sizeof(unsigned int);
		/* send & wait for reply */
		send(this->socketHandle, buffer, bufferPos, 0);
		int ret = recv(this->socketHandle, buffer, 512, 0);
		if(ret < bufferPos) {
			errorCode = 2;
			return false;
		}
		if((errorCode = buffer[3] & 0xf) != 0)
			return false;
		/* Parse MX record data */
		mxRecords.clear();
		int mxCount = ntohs(*((unsigned short*)&buffer[6]));
		for(int i=0; i<mxCount; i++) {
			string domain = parseName(bufferPos);
			int type = ntohs(*((unsigned short*)&buffer[bufferPos]));
			/* pass class & ttl */
			bufferPos += 8;
			int rdLength = ntohs(*((unsigned short*)&buffer[bufferPos]));
			bufferPos += 2;
			if(type == 0xf) {
				DnsMxRecord item;
				item.preference = ntohs(*((unsigned short*)&buffer[bufferPos]));
				bufferPos += 2;
				item.address = parseName(bufferPos);
				mxRecords.push_back(item);
				printf("found MX record: %s, preference: %d\n", item.address.c_str(), item.preference);
			} else {
				bufferPos += rdLength;
			}
		}
		return true;
	}
};

int main(int argc, char **argv)
{
#ifdef __WIN32__
	WSADATA wsaData;
	assert(WSAStartup(MAKEWORD(2,1), static_cast<WSADATA*>(&wsaData)) == 0);
#endif
	DnsQuery query;
	query.setNameServer("8.8.8.8");
	if(!query.connect()) {
		perror("failed to connect to name server.");
		_exit(1);
	}
	if(!query.queryMx("gmail.com")) {
		printf("failed to get mx records: %s\n", query.getErrorString().c_str());
		_exit(1);
	}
	return 0;
}

写一个与DNS通信获取MX记录的C++类》有10个想法

        1. Xiaoxia 文章作者

          嗯,你说得有道理~!
          感觉我这样写,是让connect这个步骤显得累赘了。
          哈哈,向你学习了!

          回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据