c++ 解析纯真IP数据库qqwry

http://www.cdut-boy.com/2011/09/22/parseqqwry/

网上流传的IP数据库等,如纯真,其实是cnss制定的格式.
在项目中有需求,网上找到的几份已有的实现,都不太满意.

索性自己写了一份,以前都只是用用,这次总算弄明白整个格式是怎么样的.
我写的IPInfo是C++ 类的方式,内存映射的方式操作数据,兼容Ansi /Unicode ,自动识别常用的IP数据库名(QQwry.dat 等).
关键的地方注释的比较详细,可以看注释.如果你的项目中复用到这份代码,请注明!

调用起来很简单

char	szAddr[100]={0};
CIPInfo NewIPInfo;
 
NewIPInfo.OpenA("D:\\qqwry.dat";);
NewIPInfo.QueryIPA("1.2.3.4",szAddr,sizeof(szAddr));
NewIPInfo.Close();


cnss设计的这个IP数据库的格式,很简洁精妙,字符串复用的方式压缩了很大一部分空间.

其格式描述可以见cnss的blog

经过我的实际分析,有几个网上介绍格式的文章没提到,但是要注意的地方.

1.对于重定位模式1之后地区名也有可能也需要重定位.

2.混合模式1国家名二次重定位之后的地区名也有可能需要重定位.

3.IP存的是小尾字节序.

详细的看我的实现吧.

为什么贴到wordpress格式就乱了呢??

IPInfo.h 声明部分

/*
 File:		\Src\common\IPInfo.h
 Description:	解析IP地址
 Author:	cdutboy@gmail.com
 Build:		2011.09.21
*/
 
#ifndef IPINFO_HEADER_FILE
	#define IPINFO_HEADER_FILE
 
/*
QQWry.dat中都是小尾字节序
 
文件夹
 
记录区
 
索引区
*/
 
#pragma pack(push,1)
 
#define IPINFO_INDEX_SIZE	7	//一条索引的大小
 
#pragma pack(pop)
 
class CIPInfo
{
public:
	CIPInfo(void);
	~CIPInfo();
 
	//在当前目录搜索默认的几个名字,QQWry.dat,IPwry.dat,等
	BOOL OpenDefDat(void);
 
	BOOL OpenA(IN char*  pszDatPath);
	BOOL OpenW(IN WCHAR* pszDatPath);
 
	BOOL Close(void);
 
	BOOL QueryIPA(IN char* pszIP,OUT char* pszAddr,IN ULONG ulAddrBuffSize);
	BOOL QueryIPW(IN WCHAR* pszIP,OUT WCHAR* pszAddr,IN ULONG ulAddrBuffSize);
 
	BOOL QueryIPA(IN ULONG ulIP,OUT char* pszAddr,IN ULONG ulAddrBuffSize);
	BOOL QueryIPW(IN ULONG ulIP,OUT WCHAR* pszAddr,IN ULONG ulAddrBuffSize);
 
	#ifdef _UNICODE
		#define QueryIP	QueryIPW
	#else
		#define QueryIP	QueryIPA
	#endif
 
	//查找指定IP所在记录的偏移
	ULONG FindIP(IN ULONG ulIP);
 
	//检查IP有效性
	BOOL CheckIP(IN char* pszIP);
 
private:
	//Map数据库文件
	BOOL Map(IN ULONG ulMapSize);
 
	//UnMap数据库文件
	BOOL UnMap(void);
 
private:
	ULONG	m_ulFileSize;
	HANDLE	m_hDatFile;
	HANDLE	m_hDatMap;
	void*	m_pDatBase;
 
	void*	m_pIndex;				//指向索引区
	ULONG	m_ulFirstIndexOffset;	//第一条索引的偏移
	ULONG	m_ulLastIndexOffset;	//最后一条索引的偏移
	ULONG	m_ulRecordNum;			//记录条数
};
 
#endif


代码到底被Windows Live Writer还是wordpress转义了?

/*
 File:		\Src\common\IPInfo.h
 Description:	解析IP地址
 Author:	cdutboy@gmail.com
 Build:		2011.09.21
*/
 
#include <Windows.h>
#include <stdio.h>
#include "IPInfo.h"
 
CIPInfo::CIPInfo()
{
	m_hDatFile	= INVALID_HANDLE_VALUE;
	m_hDatMap	= NULL;
	m_pDatBase	= NULL;
 
	m_pIndex			 = NULL;	//指向索引区
	m_ulFirstIndexOffset =0;	//第一条索引的偏移
	m_ulLastIndexOffset	 =0;	//最后一条索引的偏移
	m_ulRecordNum		 =0;	//记录条数
}
 
CIPInfo::~CIPInfo()
{
	//UnMap();
	//Close();
}
 
//在当前目录搜索默认的几个名字,QQWry.dat,IPwry.dat,等
BOOL CIPInfo::OpenDefDat(void)
{
	BOOL bReturnFlag=FALSE;
	int	nIndex=0;
 
	char DefNameArray[][20] = 
	{
		"QQWry.dat",
		"CoralWry.dat",
		"MyIP.dat",
		"IPWry.dat",
		"IP.dat",
		"Coral.dat",
		"Wry.dat"
	};
 
	char	szSelfFolder[MAX_PATH]={0};
	char	szDatFile[MAX_PATH]={0};
 
 
	do 
	{
		GetModuleFileNameA(GetModuleHandle(NULL),szSelfFolder,MAX_PATH);
 
		*(strrchr(szSelfFolder,'\\')) = '\0';
 
		for (nIndex=0; nIndex < sizeof(DefNameArray)/sizeof(DefNameArray[0]); nIndex++)
		{
			RtlZeroMemory(szDatFile,sizeof(szDatFile));
 
			_snprintf(szDatFile,MAX_PATH-1,"%s\\%s",szSelfFolder,DefNameArray[nIndex]);
 
			if (OpenA(szDatFile))
			{
				bReturnFlag =TRUE;
				break;
			}
		}
 
	} while (FALSE);
 
	return bReturnFlag;
}
 
BOOL CIPInfo::OpenA(IN char* pszDatPath)
{
 
	#if 0
		m_hDatFile = CreateFileA(pszDatPath,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,\
			NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	#else
		m_hDatFile = CreateFileA(pszDatPath,GENERIC_READ,FILE_SHARE_READ,\
			NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	#endif
 
	if (INVALID_HANDLE_VALUE == m_hDatFile)
	{
		return FALSE;
	}
 
	return this->Map(0);
}
 
BOOL CIPInfo::OpenW(IN WCHAR* pszDatPath)
{
 
	#if 0
		m_hDatFile = CreateFileW(pszDatPath,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,\
			NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	#else
		m_hDatFile = CreateFileW(pszDatPath,GENERIC_READ,FILE_SHARE_READ,\
			NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	#endif
 
	if (INVALID_HANDLE_VALUE == m_hDatFile)
	{
		return FALSE;
	}
 
	return this->Map(0);
}
 
BOOL CIPInfo::Map(IN ULONG ulMapSize)
{
	BOOL bReturnFlag=FALSE;
 
	do 
	{
		if (INVALID_HANDLE_VALUE == m_hDatFile)
		{
			break;
		}
 
		m_ulFileSize = GetFileSize(m_hDatFile,NULL);
		if (0 == m_ulFileSize)
		{
			break;
		}
 
		#if 0
			m_hDatMap = CreateFileMapping(m_hDatFile,NULL,PAGE_READWRITE,0,ulMapSize,NULL);
			if(NULL == m_hDatMap)
			{
				break;
			}
 
			m_pDatBase = MapViewOfFile(m_hDatMap,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
			if (NULL == m_pDatBase)
			{
				//XDebug((_T("map错误? %s"),xGetLastErrorString() ));
				break;
			}
		#else
			m_hDatMap = CreateFileMapping(m_hDatFile,NULL,PAGE_READONLY,0,ulMapSize,NULL);
			if(NULL == m_hDatMap)
			{
				break;
			}
 
			m_pDatBase = MapViewOfFile(m_hDatMap,FILE_MAP_READ,0,0,0);
			if (NULL == m_pDatBase)
			{
				//XDebug((_T("map错误? %s"),xGetLastErrorString() ));
				break;
			}
		#endif
 
		//
		//第一条索引的偏移
		m_ulFirstIndexOffset = *( (ULONG*)m_pDatBase );
 
		//最后一条索引的偏移
		m_ulLastIndexOffset =  *( (ULONG*) ( (BYTE*)m_pDatBase + sizeof(ULONG) ) );
 
		//指向索引区
		m_pIndex	= ((BYTE*)m_pDatBase + m_ulFirstIndexOffset);
 
		//记录条数
		m_ulRecordNum = 1 + (m_ulLastIndexOffset - m_ulFirstIndexOffset) / IPINFO_INDEX_SIZE;
 
		//
 
 
		bReturnFlag =TRUE;
	} while (FALSE);
 
	return bReturnFlag;
}
 
 
BOOL CIPInfo::UnMap(void)
{
	BOOL	bReturnFlag=FALSE;
 
	do 
	{	
		if (NULL ==  m_pDatBase)
		{
			break;
		}
 
		if (!FlushViewOfFile(m_pDatBase,m_ulFileSize))
		{
			break;
		}
 
		if (!UnmapViewOfFile(m_pDatBase))
		{
			break;
		}
 
		if (NULL == m_hDatMap)
		{
			break;
		}
 
		if (!CloseHandle(m_hDatMap))
		{
			break;
		}
 
		bReturnFlag =TRUE;
	} while (FALSE);
 
	return bReturnFlag;
}
 
BOOL CIPInfo::Close(void)
{
	BOOL	bReturnFlag=FALSE;
 
	do 
	{
		if (INVALID_HANDLE_VALUE != m_hDatFile)
		{
			break;
		}
 
		if (!CloseHandle(m_hDatFile))
		{
			break;
		}
 
		bReturnFlag =TRUE;
	} while (FALSE);
 
	return bReturnFlag;
}
 
BOOL CIPInfo::QueryIPA(IN char* pszIP,OUT char* pszAddr,IN ULONG ulAddrBuffSize)
{
	BOOL	bReturnFlag=FALSE;
	ULONG	ulTemp=0;
	ULONG	ulNetByteOrderAddr=0;
 
	do 
	{
		if ( (NULL==pszIP) || (0==strlen(pszIP)) || (NULL==pszAddr) || (0==ulAddrBuffSize) )
		{
			break;
		}
 
		if (!CheckIP(pszIP))
		{
			//IP格式不正确
			break;
		}
 
		ulTemp=inet_addr(pszIP);
		if (INADDR_NONE == ulTemp)
		{
			break;
		}
		ulNetByteOrderAddr = htonl(ulTemp);
 
		bReturnFlag = this->QueryIPA(ulNetByteOrderAddr,pszAddr,ulAddrBuffSize);
 
	} while (FALSE);
 
	return bReturnFlag;
}
 
BOOL CIPInfo::QueryIPA(IN ULONG ulIP,OUT char* pszAddr,IN ULONG ulAddrBuffSize)
{
	BOOL	bReturnFlag=FALSE;
	ULONG	ulRecordOffset=0;
	ULONG	ulSecondOffset=0;
	BYTE	bMode=0;
 
	ULONG	ulRedirect=0;
 
	char*	pszMajor=NULL;
	char*	pszMinor=NULL;
	BYTE*	pRecord=NULL;
 
	do 
	{
		if ( (0==ulIP) || (NULL==pszAddr) || (0==ulAddrBuffSize) )
		{
			break;
		}
		RtlZeroMemory(pszAddr,ulAddrBuffSize);
 
		ulRecordOffset =FindIP(ulIP);
		if( 0 == ulRecordOffset)
		{
			break;
		}
 
		pRecord = (BYTE*)m_pDatBase + ulRecordOffset;
		bMode = *( pRecord+ sizeof(ULONG) );
 
		if ( 0 == bMode)
		{
			//未知国家记录
		}
		else if ( 1 == bMode)
		{
			//国家名需要重定向,
			memcpy(&ulRedirect,pRecord + sizeof(ULONG) + sizeof(BYTE),3);
 
			//判断国家名重定向后的国家名是否需要再次重定向,重定向最多2次
			memcpy(&bMode,(BYTE*)m_pDatBase + ulRedirect,1);
 
			//如果国家名发生了第二次重定向,则其第二次重定向一定为模式2
			if( 2 != bMode)
			{
				//国家名不需要二次重定向,就是简单的重定向模式1
				//重定向后的国家记录192.168.1.1
				pszMajor = (char*)((BYTE*)m_pDatBase + ulRedirect);
 
				memcpy(&bMode,(BYTE*)m_pDatBase+ ulRedirect + strlen(pszMajor) +1,1);
				//判断地区是否需要重定向
				if ( 0 == bMode)
				{
					//未知地区
					pszMinor =NULL;
				}
				else if ( (1==bMode) || (2==bMode) )
				{
					//地区需要重定向1.2.3.4
					memcpy(&ulRedirect,(BYTE*)m_pDatBase+ ulRedirect + strlen(pszMajor) +1 + sizeof(BYTE),3);
					pszMinor = (char*)m_pDatBase + ulRedirect;
				}
				else
				{	
					pszMinor = (char*)( (BYTE*)m_pDatBase+ ulRedirect + strlen(pszMajor) +1);
				}
			}
			else
			{
				//国家名需要2次重定向,第二次重定向之后一定为模式2,只会发生在国家记录上
				memcpy(&ulSecondOffset,(BYTE*)m_pDatBase + ulRedirect + sizeof(BYTE),3);
 
				//国家名
				pszMajor = (char*)((BYTE*)m_pDatBase + ulSecondOffset);
 
				//判断地区是否需要重定向
				memcpy(&bMode,(BYTE*)m_pDatBase + ulRedirect + sizeof(ULONG),1);
				if ( 0 == bMode)
				{
					//未知地区
				}
				else if ( (1== bMode) || (2 ==bMode) )
				{
					//地区需要重定向 119.119.119.119
					memcpy(&ulSecondOffset,(BYTE*)m_pDatBase + ulRedirect + sizeof(ULONG) +1,3);
 
					//地区名
					pszMinor = (char*)((BYTE*)m_pDatBase + ulSecondOffset);
				}
				else
				{
					//普通的,跟重定向模式2一样,直接跟在国家偏移之后
					//12.34.56.78
					/*
					BYTE  bMode=2;	//一个字节表示是重定向模式2
					BYTE  CountryOffset[3];	//3个字节,存放国家名的绝对偏移
					char[不定长] 的地区名,以\0结尾.
					*/
					pszMinor = (char*)((BYTE*)m_pDatBase + ulRedirect + sizeof(ULONG));
				}
			}
		}
		else if ( 2 == bMode)			//国家需要重定向,地区不需要,国家和地区不是在一块的
		{
			/*
			重定向模式2		202.115.128.13
 
			ULONG ulEndIP;
			BYTE  bMode=2;	//一个字节表示是重定向模式2
			BYTE  CountryOffset[3];	//3个字节,存放国家名的绝对偏移
			char[不定长] 的地区名,以\0结尾.
			*/
 
			memcpy(&ulRedirect,pRecord + sizeof(ULONG) + sizeof(BYTE),3);			
 
			//国家名需要重定向
			pszMajor = (char*)((BYTE*)m_pDatBase + ulRedirect);
 
			//判断地区是否需要重定向
			memcpy(&bMode,(BYTE*)pRecord+ sizeof(ULONG) + sizeof(ULONG),1);
			if ( 0 == bMode)
			{
				//未知地区
				pszMinor =NULL;
			}
			else if ( (1==bMode) || (2==bMode) )
			{
				//地区需要重定向1.2.3.4
				memcpy(&ulRedirect,(BYTE*)pRecord+ sizeof(ULONG) + sizeof(ULONG) +sizeof(BYTE),3);
				pszMinor = (char*)m_pDatBase + ulRedirect;
			}
			else
			{	
				//地区名没有重定向
				pszMinor = (char*)( pRecord+ sizeof(ULONG) + sizeof(ULONG));
			}
 
		}
		else
		{
			//最简单的类型,国家不需要重定向
			pszMajor = (char*)pRecord+ sizeof(ULONG);
 
			//判断地区是否需要重定向
			memcpy(&bMode,(BYTE*)pRecord + sizeof(ULONG) + strlen(pszMajor) +1,1);
			if ( 0 == bMode)
			{
				//未知地区
				pszMinor =NULL;
			}
			else if ( (1==bMode) || (2==bMode) )
			{
				//地区需要重定向1.2.3.4
				memcpy(&ulRedirect,(BYTE*)pRecord + sizeof(ULONG) + strlen(pszMajor) +1 +sizeof(BYTE),3);
				pszMinor = (char*)m_pDatBase + ulRedirect;
			}
			else
			{	
				/*
				ULONG ulEndIP;	//4个字节的结束IP
				char[不定长] 的国家名.后面以\0结尾
				char[不定长] 的地区名,后面以\0结尾.
				*/
				pszMinor = (char*)pRecord + sizeof(ULONG) + strlen(pszMajor) +1;
			}
		}
 
		//综合国家名和地区名
		if ( NULL== pszMajor )
		{
			//strcpy(pszAddr,"未知国家");
			break;
		}
 
		RtlZeroMemory(pszAddr,ulAddrBuffSize);
		strncpy(pszAddr,pszMajor,strlen(pszMajor));
 
		if (NULL != pszMinor)
		{
			strcat(pszAddr,pszMinor);
		}
 
		bReturnFlag =TRUE;
	} while (FALSE);
 
	return bReturnFlag;
}
 
BOOL CIPInfo::QueryIPW(IN WCHAR* pszWideIP,OUT WCHAR* pszWideAddr,IN ULONG ulWideBuffSize)
{
	char	szAnsiIP[40]={0};
	char*	pszAnsiAddr=NULL;
	ULONG	ulAnsiBuffSize=0;
	BOOL	bFlag=FALSE;
 
	do 
	{
		if ( (NULL==pszWideIP) || (NULL==pszWideAddr) || (0==ulWideBuffSize) )
		{
			break;
		}
 
		if ( 0 == WideCharToMultiByte(CP_ACP,0,pszWideIP,-1,szAnsiIP,sizeof(szAnsiIP),0,0))
		{
			break;
		}
 
		ulAnsiBuffSize = ulWideBuffSize * sizeof(WCHAR);
		pszAnsiAddr = new char[ulAnsiBuffSize];
		if (NULL == pszAnsiAddr)
		{
			break;
		}
		RtlZeroMemory(pszAnsiAddr,ulAnsiBuffSize);
 
		if (!QueryIPA(szAnsiIP,pszAnsiAddr,ulAnsiBuffSize))
		{
			break;
		}
 
		if ( 0 == MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,pszAnsiAddr,-1,pszWideAddr,ulWideBuffSize))
		{
			break;
		}
 
		bFlag =TRUE;
	} while (FALSE);
 
	if (NULL != pszAnsiAddr)
	{
		delete[] pszAnsiAddr;
		pszAnsiAddr = NULL;
	}
 
	return bFlag;
}
 
BOOL CIPInfo::QueryIPW(IN ULONG ulIP,OUT WCHAR* pszWideAddr,IN ULONG ulWideBuffSize)
{
	char*	pszAnsiAddr=NULL;
	ULONG	ulAnsiBuffSize=0;
	BOOL	bFlag=FALSE;
 
	do 
	{
		if ( (0==ulIP) || (NULL==pszWideAddr) || (0==ulWideBuffSize) )
		{
			break;
		}
 
		ulAnsiBuffSize = ulWideBuffSize * sizeof(WCHAR);
		pszAnsiAddr = new char[ulAnsiBuffSize];
		if (NULL == pszAnsiAddr)
		{
			break;
		}
		RtlZeroMemory(pszAnsiAddr,ulAnsiBuffSize);
 
		if (!QueryIPA(ulIP,pszAnsiAddr,ulAnsiBuffSize))
		{
			break;
		}
 
		if ( 0 == MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,pszAnsiAddr,-1,pszWideAddr,ulWideBuffSize))
		{
			break;
		}
 
		bFlag =TRUE;
	} while (FALSE);
 
	if (NULL != pszAnsiAddr)
	{
		delete[] pszAnsiAddr;
		pszAnsiAddr = NULL;
	}
 
	return bFlag;
}
 
//查找指定IP,返回记录的偏移
ULONG CIPInfo::FindIP(IN ULONG ulIP)
{
	ULONG			ulBegin=0;
	ULONG			ulEnd=m_ulRecordNum;
	ULONG			ulMid=0;
	ULONG			ulStartIP=0;	//索引区,起始IP
	ULONG			ulEndIP=0;		//记录区,结束IP
	ULONG			ulOffset=0;
 
 
	//索引结构共7个字节
	//前4个字节表示一条记录的起始IP
	//后3个字节表示这条记录在文件中的绝对偏移.
 
	//二分法查找索引
	while(ulBegin < ulEnd-1)
	{
		ulMid = ulBegin + ( (ulEnd - ulBegin)/2 );
 
		//索引的前四个字节就是起始IP
		ulStartIP = * ( (ULONG*)( (BYTE*)m_pIndex +  ulMid * IPINFO_INDEX_SIZE ));
 
		//找到了,返回
		if (ulIP < ulStartIP)
		{
			ulEnd = ulMid;
		}
		else
		{	
			ulBegin = ulMid;	
		}			
	}
 
	//ulOffset 就是对应的记录在
	memcpy(&ulOffset,(BYTE*)m_pIndex + ulBegin*IPINFO_INDEX_SIZE + sizeof(ULONG),3);
 
	//记录结构
	//结束IP(4字节) + 国家名(不定长) + 地区名(不定长)
	//偏移记录的第五个字节是1或者2,就表示需要重定向,QueryIPA里有处理
 
	//记录的前4个字节就是结束IP
	ulEndIP = *( (ULONG*)((BYTE*)m_pDatBase + ulOffset));
 
	if (ulIP >  ulEndIP )
	{
		return 0;
	}
 
	return ulOffset;
}
 
//检查IP有效性
BOOL CIPInfo::CheckIP(IN char* pszIP)
{	
	BOOL bReturnFlag=FALSE;
	int  a=0;
	int  b=0;
	int  c=0;
	int  d=0;
	int  e=0;
	int  nIPLen=0;
 
 
	do 
	{
		if ( (NULL == pszIP) || (0==strlen(pszIP)) )
		{
			break;
		}
 
		//255.255.255.255
		//0.0.0.0
		nIPLen = strlen(pszIP);
		if ( (7 > nIPLen) || (nIPLen > 15 ) )
		{
			break;
		}
 
		if ( 4 != sscanf(pszIP,"%d.%d.%d.%d%c",&a,&b,&c,&d,&e))
		{
			break;
		}
 
		if ( (0>a) || (a>255) )
		{
			break;
		}
 
		if ( (0>b) || (b>255) )
		{
			break;
		}
 
		if ( (0>c) || (c>255) )
		{
			break;
		}
 
		if ( (0>d) || (d>255) )
		{
			break;
		}
 
		bReturnFlag =TRUE;
	} while (FALSE);
 
	return bReturnFlag;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值