您的位置:首页 > 数据库

[C#]纯真IP数据库解析器

2011-05-16 15:04 786 查看
这个星期六日研究了下“纯真IP数据库”的格式,并且自己用C#写了个解析器,参考了LumaQQ作者的文章《纯真IP数据库格式详解》:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
和二分搜索法的C++描述http://baike.baidu.com/view/1881881.htm

数据库的格式在LumaQQ的文章中已有详细描述,下面放上主要的代码:

//选择数据库文件并显示数据库的版本等信息
private void btnBrowse_Click(object sender, EventArgs e)
{
if (ofdQQWry.ShowDialog() != DialogResult.Cancel)
{
txtQQWry.Text = ofdQQWry.FileName;
}

tvwWryInfo.Nodes.Clear();
tvwWryInfo.Nodes.Add("VER", "纯真数据库版本");
tvwWryInfo.Nodes.Add("FI", "第一条索引的绝对偏移");
tvwWryInfo.Nodes.Add("LI", "最后一条索引的绝对偏移");
tvwWryInfo.Nodes.Add("IPCNT", "可查询的 IP 段数量");

try
{
//读取数据库内容
FileStream fsQQWry = new FileStream(txtQQWry.Text, FileMode.Open);
if (m_abQQWry != null) m_abQQWry = null;
m_abQQWry = new byte[fsQQWry.Length];
fsQQWry.Read(m_abQQWry, 0, (int)fsQQWry.Length);
fsQQWry.Close();

//获取文件头(索引绝对偏移(8 字节) = 第一条索引偏移(4 字节) + 第二条索引偏移(4 字节))
iFI = BitConverter.ToInt32(m_abQQWry, 0);
iLI = BitConverter.ToInt32(m_abQQWry, 4);
tvwWryInfo.Nodes["FI"].Nodes.Add(iFI.ToString("#,#"));
tvwWryInfo.Nodes["LI"].Nodes.Add(iLI.ToString("#,#"));

//可查询的 IP 段数量(每条索引(7 字节) = IP(4 字节) + 绝对偏移(3 字节))
tvwWryInfo.Nodes["IPCNT"].Nodes.Add(((iLI - iFI) / 7 + 1).ToString("#,# 个"));

//获取版本(其实就是最后一条索引指向的地址信息,IP 地址为 255.255.255.0)
int pVer = 0;
byte[] abVer = new byte[4];
Array.Copy(m_abQQWry, iLI + 4, abVer, 0, 3);
pVer = BitConverter.ToInt32(abVer, 0);
string sVer = GetAddress(pVer, (m_abQQWry[pVer + 4] > 0x02), true);
tvwWryInfo.Nodes["VER"].Nodes.Add(sVer.Split("/t".ToCharArray())[1]);
tvwWryInfo.Nodes["VER"].Nodes.Add(sVer.Split("/t".ToCharArray())[2]);

//记录所有可查询 IP 条目,稍后用来搜索使用
m_aiIP = new int[(iLI - iFI) / 7 + 1];
for (int i = iFI; i < iLI + 7; i += 7)
{
m_aiIP[(i - iFI) / 7] = BitConverter.ToInt32(m_abQQWry, i);
}

btnDecompress.Enabled = true;
btnQuery.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}


/// <summary>
/// 获取指定偏移对应的 IP 信息
/// </summary>
/// <param name="iOffset">绝对偏移地址</param>
/// <param name="bSimple">是否简单的记录方式(即国家和地区都没有使用重定向)</param>
/// <param name="bGetIP">是否获取 IP</param>
/// <returns>用 '/0' 分割的国家名和地区名</returns>
private string GetAddress(int iOffset, bool bSimple, bool bGetIP)
{
int i = 0;
string sRst = "";
byte[] abOffset = new byte[4];

if (bGetIP)
{
byte[] abIP = new byte[4];
i = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(m_abQQWry, iOffset));
abIP = BitConverter.GetBytes(i);
sRst = new IPAddress(abIP).ToString() + '/t';
iOffset += 4;
}

switch (m_abQQWry[iOffset])
{
case 0x01: //重定向模式 1(国家名和地区名均在同一偏移处)

Array.Copy(m_abQQWry, iOffset + 1, abOffset, 0, 3);
i = BitConverter.ToInt32(abOffset, 0);

if (i == 0)
{
sRst += "未知国家/t未知地区/t";
}
else
{
string sTmp = GetAddress(i, false, false);
sRst += sTmp;
sRst += GetAddress(i + ((m_abQQWry[i] > 0x02) ?
Encoding.Default.GetByteCount(sTmp) :
4), false, false);
}

break;
case 0x02: //重定向模式 2(国家名为偏移值,地区名为字符串)

Array.Copy(m_abQQWry, iOffset + 1, abOffset, 0, 3);
i = BitConverter.ToInt32(abOffset, 0);

//获取国家
if (i == 0)
{
sRst += "未知国家/t";
}
else
{
sRst += GetAddress(i, false, false);
}

//获取地区
if (bGetIP) sRst += GetAddress(iOffset + 4, false, false);

break;
default: //直接字符串

i = iOffset;
byte bStr = 0;
List<byte> lstStr = new List<byte>();
do
{
bStr = m_abQQWry[i++];
if (bStr == 0) break;
lstStr.Add(bStr);
} while (true);
sRst += Encoding.Default.GetString(lstStr.ToArray()) + '/t';

//继续读取地区名
if (bGetIP)
{
sRst += GetAddress(i, false, false);
}

break;
}

return sRst;
}


//查询指定 IP/域名的信息
private void btnQuery_Click(object sender, EventArgs e)
{
IPAddress IPQuery = Dns.GetHostAddresses(txtIP.Text)[0];
if (IPQuery == null)
{
MessageBox.Show("请填入正确的 IP 地址或域名!");
return;
}

lstIPInfo.Items.Clear();

int iOffset = 0;
DateTime nt = DateTime.Now;
byte[] abOffset = new byte[4];
uint uiIP = BitConverter.ToUInt32(IPQuery.GetAddressBytes(), 0);
uiIP = (uint)IPAddress.NetworkToHostOrder((int)uiIP);

//二分法查找 IP 所在段
int j, l, r;
j = 0;
l = 0;
r = m_aiIP.Length - 1;
iOffset = (l + r) / 2;
while (l <= r)
{
if ((uint)m_aiIP[iOffset] < uiIP) l = iOffset + 1;
else r = iOffset - 1;
iOffset = (l + r) / 2;
if ((uint)m_aiIP[iOffset] == uiIP) break;
j++;
}

string sIP = new IPAddress(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(m_aiIP[iOffset]))).ToString();
Array.Copy(m_abQQWry, iFI + iOffset * 7 + 4, abOffset, 0, 3);
iOffset = BitConverter.ToInt32(abOffset, 0);

string sRst = GetAddress(iOffset, (m_abQQWry[iOffset + 4] > 0x02), true);
try
{
lstIPInfo.Items.Add("您查询的计算机名为:" + Dns.GetHostEntry(IPQuery).HostName);
}
catch (Exception)
{
lstIPInfo.Items.Add("您查询的计算机名无法解析!");
}
lstIPInfo.Items.Add("对应的 IP 为:" + IPQuery.ToString());
lstIPInfo.Items.Add("数据库中匹配的 IP 段为:" + sIP + " - " + sRst.Split('/t')[0]);
lstIPInfo.Items.Add("您查询的信息如下:");
lstIPInfo.Items.Add("");
lstIPInfo.Items.Add(sRst.Split('/t')[1]);
lstIPInfo.Items.Add(sRst.Split('/t')[2]);
lstIPInfo.Items.Add("");
lstIPInfo.Items.Add("索引查询次数:" + j.ToString() + " 次");
lstIPInfo.Items.Add("信息查询耗时:" + ((DateTime.Now.Ticks - nt.Ticks) / 1000).ToString("#,#0") + " 毫秒");
}


//将数据库解压为txt格式的文件
private void btnDecompress_Click(object sender, EventArgs e)
{
if ((txtQQWry.Text.Length == 0) | (!File.Exists(txtQQWry.Text)))
{
MessageBox.Show("请先选择一个正确的纯真数据库路径!");
return;
}

try
{
if (sfdDecompress.ShowDialog() == DialogResult.Cancel) return;

FileStream fsTXT = new FileStream(sfdDecompress.FileName, FileMode.Create);
StreamWriter swTXT = new StreamWriter(fsTXT, System.Text.Encoding.Default);

string sRecord = "";
IPAddress ipRecord;
int iOffset = 0;
byte[] abOffset = new byte[4];
for (int i = iFI; i < iLI + 7; i += 7)
{
//获取 IP 信息偏移
Array.Copy(m_abQQWry, i + 4, abOffset, 0, 3);
iOffset = BitConverter.ToInt32(abOffset, 0);

//起始 IP(终止 IP 在索引指向处)
ipRecord = new IPAddress(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(BitConverter.ToInt32(m_abQQWry, i))));
sRecord = ipRecord.ToString() + " - ";

//获取 IP 信息条目
sRecord += GetAddress(iOffset, (m_abQQWry[iOffset + 4] > 0x02), true);

swTXT.WriteLine(sRecord.TrimEnd("/t".ToCharArray()));
}

swTXT.Flush(); fsTXT.Flush();
swTXT.Close(); fsTXT.Close();

MessageBox.Show("文件已解压到 '" + sfdDecompress.FileName + "'!");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}


代码在VS2010+Windows 7下测试成功。



完整的代码在这里下载:


/Uploads/Images/Content/202010/10/de2c3679d7baf547b54c0e776654a177.gif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: