in apache2/msc_geo.c [261:505]
int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **error_msg)
{
apr_sockaddr_t *addr;
long ipnum = 0;
char *targetip = NULL;
geo_db *geo = msr->txcfg->geo;
char errstr[1024];
unsigned char buf[2* GEO_MAX_RECORD_LEN];
const int reclen = 3; /* Algorithm needs changed if this changes */
apr_size_t nbytes;
unsigned int rec_val = 0;
apr_off_t seekto = 0;
apr_status_t ret;
int rc;
int country = 0;
int level;
double dtmp;
int itmp;
*error_msg = NULL;
/* init */
georec->country_code = geo_country_code[0];
georec->country_code3 = geo_country_code3[0];
georec->country_name = geo_country_name[0];
georec->country_continent = geo_country_continent[0];
georec->region = "";
georec->city = "";
georec->postal_code = "";
georec->latitude = 0;
georec->longitude = 0;
georec->dma_code = 0;
georec->area_code = 0;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: Looking up \"%s\".", log_escape(msr->mp, target));
}
/* NOTE: This only works with ipv4 */
if ((rc = apr_sockaddr_info_get(&addr, target, APR_INET, 0, 0, msr->mp)) != APR_SUCCESS) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
msr_log(msr, 4, "%s", *error_msg);
return 0;
}
if ((rc = apr_sockaddr_ip_get(&targetip, addr)) != APR_SUCCESS) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
msr_log(msr, 4, "%s", *error_msg);
return 0;
};
/* Why is this in host byte order? */
ipnum = ntohl(addr->sa.sin.sin_addr.s_addr);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08lx). %lu", targetip, ipnum, ipnum);
}
ret = waf_get_exclusive_lock(msr->modsecurity->geo_lock);
if (waf_lock_is_error(ret)) {
msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
get_apr_error(msr->mp, ret));
}
for (level = 31; level >= 0; level--) {
/* Read the record */
seekto = 2 * reclen * rec_val;
apr_file_seek(geo->db, APR_SET, &seekto);
/* TODO: check rc */
rc = apr_file_read_full(geo->db, &buf, (2 * reclen), &nbytes);
/* NOTE: This is hard-coded for size 3 records */
/* Left */
if ((ipnum & (1 << level)) == 0) {
rec_val = (buf[3*0 + 0] << (0*8)) +
(buf[3*0 + 1] << (1*8)) +
(buf[3*0 + 2] << (2*8));
}
/* Right */
else {
rec_val = (buf[3*1 + 0] << (0*8)) +
(buf[3*1 + 1] << (1*8)) +
(buf[3*1 + 2] << (2*8));
}
/* If we are past the country offset, then we are done */
if (rec_val >= geo->ctry_offset) {
break;
}
}
if (rec_val == geo->ctry_offset) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\").", log_escape(msr->mp, target));
msr_log(msr, 4, "%s", *error_msg);
ret = waf_free_exclusive_lock(msr->modsecurity->geo_lock);
if (waf_lock_is_error(ret)) {
msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
get_apr_error(msr->mp, ret));
}
return 0;
}
if (geo->dbtype == GEO_COUNTRY_DATABASE) {
country = rec_val;
country -= geo->ctry_offset;
if ((country <= 0) || (country > GEO_COUNTRY_LAST)) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country);
msr_log(msr, 4, "%s", *error_msg);
ret = waf_free_exclusive_lock(msr->modsecurity->geo_lock);
if (waf_lock_is_error(ret)) {
msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
get_apr_error(msr->mp, ret));
}
return 0;
}
/* Country */
georec->country_code = geo_country_code[country];
georec->country_code3 = geo_country_code3[country];
georec->country_name = geo_country_name[country];
georec->country_continent = geo_country_continent[country];
}
else {
int field_len = 0;
int rec_offset = 0;
int remaining = GEO_CITY_RECORD_LEN;
unsigned char cbuf[GEO_CITY_RECORD_LEN];
seekto = rec_val + (2 * reclen - 1) * geo->ctry_offset;
apr_file_seek(geo->db, APR_SET, &seekto);
/* TODO: check rc */
rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes);
country = cbuf[0];
if ((country <= 0) || (country > GEO_COUNTRY_LAST)) {
*error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country);
msr_log(msr, 4, "%s", *error_msg);
ret = waf_free_exclusive_lock(msr->modsecurity->geo_lock);
if (waf_lock_is_error(ret)) {
msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
get_apr_error(msr->mp, ret));
}
return 0;
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: rec=\"%s\"", log_escape_raw(msr->mp, cbuf, sizeof(cbuf)));
}
/* Country */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: country=\"%.*s\"", (1*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf)));
}
georec->country_code = geo_country_code[country];
georec->country_code3 = geo_country_code3[country];
georec->country_name = geo_country_name[country];
georec->country_continent = geo_country_continent[country];
rec_offset++;
remaining -= rec_offset;
/* Region */
field_len = field_length((const char *)cbuf+rec_offset, remaining);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: region=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
georec->region = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
rec_offset += field_len + 1;
remaining -= field_len + 1;
/* City */
field_len = field_length((const char *)cbuf+rec_offset, remaining);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: city=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
georec->city = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
rec_offset += field_len + 1;
remaining -= field_len + 1;
/* Postal Code */
field_len = field_length((const char *)cbuf+rec_offset, remaining);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: postal_code=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
georec->postal_code = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
rec_offset += field_len + 1;
remaining -= field_len + 1;
/* Latitude */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: latitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
dtmp = cbuf[rec_offset] +
(cbuf[rec_offset+1] << 8) +
(cbuf[rec_offset+2] << 16);
georec->latitude = dtmp/10000 - 180;
rec_offset += 3;
remaining -= 3;
/* Longitude */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: longitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
dtmp = cbuf[rec_offset] +
(cbuf[rec_offset+1] << 8) +
(cbuf[rec_offset+2] << 16);
georec->longitude = dtmp/10000 - 180;
rec_offset += 3;
remaining -= 3;
/* dma/area codes are in city rev1 and US only */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: dma/area=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
if (geo->dbtype == GEO_CITY_DATABASE_1
&& georec->country_code[0] == 'U'
&& georec->country_code[1] == 'S')
{
/* DMA Code */
itmp = cbuf[rec_offset] +
(cbuf[rec_offset+1] << 8) +
(cbuf[rec_offset+2] << 16);
georec->dma_code = itmp / 1000;
georec->area_code = itmp % 1000;
rec_offset += 6;
remaining -= 6;
}
}
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded.", log_escape(msr->mp, target));
ret = waf_free_exclusive_lock(msr->modsecurity->geo_lock);
if (waf_lock_is_error(ret)) {
msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s",
get_apr_error(msr->mp, ret));
}
return 1;
}