mirror of
https://github.com/lionsoul2014/ip2region.git
synced 2025-12-08 19:25:22 +00:00
442 lines
11 KiB
C
442 lines
11 KiB
C
/**
|
|
* default ip2region implementation
|
|
*
|
|
* @see #ip2region.h
|
|
* @author chenxin<chenxin619315@gmail.com>
|
|
* @date 2015-10-30
|
|
*/
|
|
|
|
#include "ip2region.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
/**
|
|
* create a new ip2region object
|
|
*
|
|
* @param dbFile path
|
|
*/
|
|
IP2R_API uint_t ip2region_create(ip2region_t ip2rObj, const char *dbFile)
|
|
{
|
|
memset(ip2rObj, 0x00, sizeof(ip2region_entry));
|
|
ip2rObj->headerLen = 0;
|
|
ip2rObj->HeaderSip = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH);
|
|
if ( ip2rObj->HeaderSip == NULL ) {
|
|
return 0;
|
|
}
|
|
|
|
ip2rObj->HeaderPtr = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH);
|
|
if ( ip2rObj->HeaderPtr == NULL ) {
|
|
IP2R_FREE(ip2rObj->HeaderSip);
|
|
return 0;
|
|
}
|
|
|
|
//open the db file
|
|
ip2rObj->dbHandler = fopen(dbFile, "rb");
|
|
if ( ip2rObj->dbHandler == NULL ) {
|
|
IP2R_FREE(ip2rObj->HeaderSip);
|
|
IP2R_FREE(ip2rObj->HeaderPtr);
|
|
//fprintf(stderr, "Fail to open the db file %s\n", ip2rObj>dbFile);
|
|
//exit(-1);
|
|
return 0;
|
|
}
|
|
|
|
ip2rObj->firstIndexPtr = 0;
|
|
ip2rObj->lastIndexPtr = 0;
|
|
ip2rObj->totalBlocks = 0;
|
|
ip2rObj->dbBinStr = NULL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* destroy the specifield ip2region object
|
|
*
|
|
* @param ip2region_t
|
|
*/
|
|
IP2R_API uint_t ip2region_destroy(ip2region_t ip2rObj)
|
|
{
|
|
if ( ip2rObj->HeaderSip != NULL ) {
|
|
IP2R_FREE(ip2rObj->HeaderSip);
|
|
ip2rObj->HeaderSip = NULL;
|
|
}
|
|
|
|
if ( ip2rObj->HeaderPtr != NULL ) {
|
|
IP2R_FREE(ip2rObj->HeaderPtr);
|
|
ip2rObj->HeaderPtr = NULL;
|
|
}
|
|
|
|
// close the db file resource
|
|
if ( ip2rObj->dbHandler != NULL ) {
|
|
fclose(ip2rObj->dbHandler);
|
|
ip2rObj->dbHandler = NULL;
|
|
}
|
|
|
|
// free the db binary string
|
|
if ( ip2rObj->dbBinStr != NULL ) {
|
|
IP2R_FREE(ip2rObj->dbBinStr);
|
|
ip2rObj->dbBinStr = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* get the region associated with the specified ip address with the memory binary search algorithm
|
|
*
|
|
* @param ip2rObj
|
|
* @param ip
|
|
* @param datablock
|
|
*/
|
|
IP2R_API uint_t ip2region_memory_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
|
|
{
|
|
int l, h, m, p;
|
|
uint_t sip, eip, dptr;
|
|
int dataLen, dataptr;
|
|
long filesize;
|
|
char *buffer;
|
|
|
|
if ( ip2rObj->dbBinStr == NULL ) {
|
|
//get the size of the file
|
|
fseek(ip2rObj->dbHandler, 0, SEEK_END);
|
|
filesize = ftell(ip2rObj->dbHandler);
|
|
fseek(ip2rObj->dbHandler, 0, SEEK_SET);
|
|
|
|
//alloc the buffer size
|
|
ip2rObj->dbBinStr = IP2R_MALLOC(filesize);
|
|
if ( ip2rObj->dbBinStr == NULL ) {
|
|
return 0;
|
|
}
|
|
|
|
//now read the whole file
|
|
if ( fread(ip2rObj->dbBinStr, filesize, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
buffer = ip2rObj->dbBinStr;
|
|
ip2rObj->firstIndexPtr = getUnsignedInt(buffer, 0);
|
|
ip2rObj->lastIndexPtr = getUnsignedInt(buffer, 4);
|
|
ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
|
|
}
|
|
|
|
l = 0; h = ip2rObj->totalBlocks; dptr = 0;
|
|
while ( l <= h ) {
|
|
m = (l + h) >> 1;
|
|
p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH;
|
|
|
|
buffer = ip2rObj->dbBinStr + p;
|
|
sip = getUnsignedInt(buffer, 0);
|
|
if ( ip < sip ) {
|
|
h = m - 1;
|
|
} else {
|
|
eip = getUnsignedInt(buffer, 4);
|
|
if ( ip > eip ) {
|
|
l = m + 1;
|
|
} else {
|
|
dptr = getUnsignedInt(buffer, 8);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( dptr == 0 ) return 0;
|
|
|
|
//get the data
|
|
dataLen = ((dptr >> 24) & 0xFF);
|
|
dataptr = (dptr & 0x00FFFFFF);
|
|
buffer = ip2rObj->dbBinStr + dataptr;
|
|
|
|
//fill the data to the datablock
|
|
datablock->city_id = getUnsignedInt(buffer, 0);
|
|
dataLen -= 4; //reduce the length of the city_id
|
|
memcpy(datablock->region, buffer + 4, dataLen);
|
|
datablock->region[dataLen] = '\0';
|
|
|
|
return 1;
|
|
}
|
|
|
|
IP2R_API uint_t ip2region_memory_search_string(ip2region_t ip2rObj, const char *ip, datablock_t datablock)
|
|
{
|
|
return ip2region_memory_search(ip2rObj, ip2long(ip), datablock);
|
|
}
|
|
|
|
/**
|
|
* get the region associated with the specifield ip address with binary search algorithm
|
|
*
|
|
* @param ip2rObj
|
|
* @param ip
|
|
* @param datablock
|
|
* @return uint_t
|
|
*/
|
|
IP2R_API uint_t ip2region_binary_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
|
|
{
|
|
int l, h, m, p;
|
|
uint_t sip, eip, dptr;
|
|
char buffer[256];
|
|
int dataLen, dataptr;
|
|
|
|
if ( ip2rObj->totalBlocks == 0 ) {
|
|
fseek(ip2rObj->dbHandler, 0, 0);
|
|
if ( fread(buffer, 8, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
ip2rObj->firstIndexPtr = getUnsignedInt(buffer, 0);
|
|
ip2rObj->lastIndexPtr = getUnsignedInt(buffer, 4);
|
|
ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
|
|
}
|
|
|
|
//binary search the index blocks to define the data block
|
|
l = 0; h = ip2rObj->totalBlocks; dptr = 0;
|
|
while ( l <= h ) {
|
|
m = (l + h) >> 1;
|
|
p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH;
|
|
|
|
fseek(ip2rObj->dbHandler, p, 0);
|
|
if ( fread(buffer, INDEX_BLOCK_LENGTH, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
sip = getUnsignedInt(buffer, 0);
|
|
if ( ip < sip ) {
|
|
h = m - 1;
|
|
} else {
|
|
eip = getUnsignedInt(buffer, 4);
|
|
if ( ip > eip ) {
|
|
l = m + 1;
|
|
} else {
|
|
dptr = getUnsignedInt(buffer, 8);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( dptr == 0 ) return 0;
|
|
|
|
//get the data
|
|
dataLen = ((dptr >> 24) & 0xFF);
|
|
dataptr = (dptr & 0x00FFFFFF);
|
|
|
|
//memset(data, 0x00, sizeof(data));
|
|
fseek(ip2rObj->dbHandler, dataptr, 0);
|
|
if ( fread(buffer, dataLen, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
//fill the data to the datablock
|
|
datablock->city_id = getUnsignedInt(buffer, 0);
|
|
dataLen -= 4; //reduce the length of the city_id
|
|
memcpy(datablock->region, buffer + 4, dataLen);
|
|
datablock->region[dataLen] = '\0';
|
|
|
|
return 1;
|
|
}
|
|
|
|
IP2R_API uint_t ip2region_binary_search_string(ip2region_t ip2rObj, const char *ip, datablock_t datablock)
|
|
{
|
|
return ip2region_binary_search(ip2rObj, ip2long(ip), datablock);
|
|
}
|
|
|
|
/**
|
|
* get the region associated with the specifield ip address with b-tree algorithm
|
|
*
|
|
* @param ip2rObj
|
|
* @param ip
|
|
* @param datablock
|
|
* @return uint_t
|
|
*/
|
|
IP2R_API uint_t ip2region_btree_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
|
|
{
|
|
int i, idx;
|
|
int l, m, h, p, sptr, eptr, indexBlockLen, dataLen, dataptr;
|
|
uint_t sip, eip, idxptr, dptr;
|
|
char buffer[TOTAL_HEADER_LENGTH];
|
|
|
|
if ( ip2rObj->headerLen == 0 ) {
|
|
idx = 0;
|
|
fseek(ip2rObj->dbHandler, 8, 0); //pass the super block
|
|
if ( fread(buffer, TOTAL_HEADER_LENGTH, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
for ( i = 0; i < TOTAL_HEADER_LENGTH; i += 8 ) {
|
|
sip = getUnsignedInt(buffer, i);
|
|
idxptr = getUnsignedInt(buffer, i + 4);
|
|
if ( idxptr == 0 ) break;
|
|
|
|
ip2rObj->HeaderSip[idx] = sip;
|
|
ip2rObj->HeaderPtr[idx] = idxptr;
|
|
idx++;
|
|
}
|
|
|
|
ip2rObj->headerLen = idx;
|
|
}
|
|
|
|
//search the header block to define the index block
|
|
l = 0; h = ip2rObj->headerLen; sptr = 0; eptr = 0;
|
|
while ( l <= h ) {
|
|
m = ((l + h) >> 1);
|
|
|
|
//perfetc matched, just return it
|
|
if ( ip == ip2rObj->HeaderSip[m] ) {
|
|
if ( m > 0 ) {
|
|
sptr = ip2rObj->HeaderPtr[m-1];
|
|
eptr = ip2rObj->HeaderPtr[m ];
|
|
} else {
|
|
sptr = ip2rObj->HeaderPtr[m ];
|
|
eptr = ip2rObj->HeaderPtr[m+1];
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//less then the middle value
|
|
if ( ip < ip2rObj->HeaderSip[m] ) {
|
|
if ( m == 0 ) {
|
|
sptr = ip2rObj->HeaderPtr[m ];
|
|
eptr = ip2rObj->HeaderPtr[m+1];
|
|
break;
|
|
} else if ( ip > ip2rObj->HeaderSip[m-1] ) {
|
|
sptr = ip2rObj->HeaderPtr[m-1];
|
|
eptr = ip2rObj->HeaderPtr[m ];
|
|
break;
|
|
}
|
|
h = m - 1;
|
|
} else {
|
|
if ( m == ip2rObj->headerLen - 1 ) {
|
|
sptr = ip2rObj->HeaderPtr[m-1];
|
|
eptr = ip2rObj->HeaderPtr[m ];
|
|
break;
|
|
} else if ( ip <= ip2rObj->HeaderSip[m+1] ) {
|
|
sptr = ip2rObj->HeaderPtr[m ];
|
|
eptr = ip2rObj->HeaderPtr[m+1];
|
|
break;
|
|
}
|
|
l = m + 1;
|
|
}
|
|
}
|
|
|
|
//not matched just stop it
|
|
if ( sptr == 0 ) return 0;
|
|
|
|
indexBlockLen = eptr - sptr;
|
|
fseek(ip2rObj->dbHandler, sptr, 0);
|
|
if ( fread(buffer, indexBlockLen + INDEX_BLOCK_LENGTH, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
dptr = 0; l = 0; h = indexBlockLen / INDEX_BLOCK_LENGTH;
|
|
while ( l <= h ) {
|
|
m = ((l + h) >> 1);
|
|
p = m * INDEX_BLOCK_LENGTH;
|
|
sip = getUnsignedInt(buffer, p);
|
|
if ( ip < sip ) {
|
|
h = m - 1;
|
|
} else {
|
|
eip = getUnsignedInt(buffer, p + 4);
|
|
if ( ip > eip ) {
|
|
l = m + 1;
|
|
} else {
|
|
dptr = getUnsignedInt(buffer, p + 8);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( dptr == 0 ) return 0;
|
|
|
|
dataLen = ((dptr >> 24) & 0xFF);
|
|
dataptr = (dptr & 0x00FFFFFF);
|
|
|
|
fseek(ip2rObj->dbHandler, dataptr, 0);
|
|
if ( fread(buffer, dataLen, 1, ip2rObj->dbHandler) != 1 ) {
|
|
return 0;
|
|
}
|
|
|
|
datablock->city_id = getUnsignedInt(buffer, 0);
|
|
dataLen -= 4;
|
|
memcpy(datablock->region, buffer + 4, dataLen);
|
|
datablock->region[dataLen] = '\0';
|
|
|
|
return 1;
|
|
}
|
|
|
|
IP2R_API uint_t ip2region_btree_search_string(ip2region_t ip2rObj, const char *ip, datablock_t datablock)
|
|
{
|
|
return ip2region_btree_search(ip2rObj, ip2long(ip), datablock);
|
|
}
|
|
|
|
/**
|
|
* get a unsinged long(4bytes) from a specifield buffer start from the specifield offset
|
|
*
|
|
* @param buffer
|
|
* @param offset
|
|
* @return uint_t
|
|
*/
|
|
IP2R_API uint_t getUnsignedInt(const char *buffer, int offset)
|
|
{
|
|
return (
|
|
((buffer[offset ]) & 0x000000FF) |
|
|
((buffer[offset+1] << 8) & 0x0000FF00) |
|
|
((buffer[offset+2] << 16) & 0x00FF0000) |
|
|
((buffer[offset+3] << 24) & 0xFF000000)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* string ip to long
|
|
*
|
|
* @param ip
|
|
* @return uint_t
|
|
*/
|
|
IP2R_API uint_t ip2long(const char *ip)
|
|
{
|
|
int i = 0, p = 24;
|
|
char buffer[4];
|
|
const char *cs = ip;
|
|
uint_t ipval = 0;
|
|
|
|
while ( *cs != '\0' ) {
|
|
if ( *cs == ' ' ) {
|
|
cs++;
|
|
continue;
|
|
}
|
|
|
|
if ( i > 3 ) {
|
|
ipval = 0;
|
|
break;
|
|
}
|
|
if ( *cs == '.' ) {
|
|
//single part length limit
|
|
|
|
if ( p < 0 ) break;
|
|
buffer[i] = '\0';
|
|
ipval |= (atoi(buffer) << p);
|
|
p -= 8;
|
|
i = 0;
|
|
} else {
|
|
buffer[i++] = *cs;
|
|
}
|
|
|
|
cs++;
|
|
}
|
|
|
|
//append the rest parts
|
|
if ( i > 3 ) return 0;
|
|
buffer[i] = '\0';
|
|
ipval |= atoi(buffer);
|
|
|
|
return ipval;
|
|
}
|
|
|
|
/**
|
|
* long to string ip
|
|
*
|
|
* @param ip
|
|
* @param buffer
|
|
* @return uint_t(1 for success and 0 for failed)
|
|
*/
|
|
IP2R_API uint_t long2ip(uint_t ip, char *buffer)
|
|
{
|
|
return 0;
|
|
}
|