catalinux / ip2clue (public) (License: GPLv3) (since 2016-03-01) (hash sha1)
High-performance IPv4 and IPv6 daemon to retrieve IPv4/6 country information.

/i_util.c (c9658a4f24bd96c55c3a0b48b470020851e21e51) (11075 bytes) (mode 100644) (type blob)

/*
 * Author: Catalin(ux) M. BOIE
 * Description: parser for IP files
 */

#include "i_config.h"

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdarg.h>

#include "i_util.h"

__thread char		ip2clue_error[256];

/*
 * Returns ip2clue_error content
 */
char *ip2clue_strerror(void)
{
	return ip2clue_error;
}

/*
 * Set error message
 */
void ip2clue_set_error(const char *format, ...)
{
	va_list ap;

        va_start(ap, format);
	vsnprintf(ip2clue_error, sizeof(ip2clue_error), format, ap);
        va_end(ap);
}

/*
 * Show a nice IPv6 address
 * TODO: Replace with inet_ntop!
 */
int ip2clue_addr_v6(char *out, size_t out_size, const unsigned int *a)
{
	snprintf(out, out_size, "%x:%x:%x:%x:%x:%x:%x:%x",
		a[0] >> 16, a[0] & 0xFFFF,
		a[1] >> 16, a[1] & 0xFFFF,
		a[2] >> 16, a[2] & 0xFFFF,
		a[3] >> 16, a[3] & 0xFFFF);

	return 0;
}

/*
 * Compare two IPv6 addresses
 */
static int ip2clue_compare_v6(const unsigned int *a, const unsigned int *b)
{
	unsigned int i;

	for (i = 0; i < 4; i++) {
		if (a[i] > b[i])
			return 1;
		else if (a[i] < b[i])
			return -1;
	}

	return 0;
}

/*
 * Split a line in fields.
 * @sep is an string that contains all possible separators
 * Returns number of fields.
 */
int ip2clue_split(struct ip2clue_split *s, const char *line, const char *sep)
{
	int len, i, j, inside_quote, separator_area;

	len = strlen(line);
	if (len == 0) {
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"empty string passed for splitting");
		return -1;
	}

	inside_quote = 0;
	separator_area = 0;
	s->count = 1;	/* There is at least one field! */
	j = 0;
	s->fields[s->count - 1][j] = '\0';
	for (i = 0; i < len; i++) {
		/* Parse only first 32 fields */
		if (s->count == 32)
			break;

		if ((line[i] == '\r') || (line[i] == '\n'))
			break;

		if (line[i] == '"') {
			if (inside_quote == 1) {
				inside_quote = 0;
			} else {
				inside_quote = 1;
			}
			continue;
		}

		if ((inside_quote == 0) && (strchr(sep, line[i]))) {
			/* Found separator */
			if (separator_area == 1)
				continue;

			/* Terminate previous string */
			s->fields[s->count - 1][j] = '\0';
			/* Move to next field */
			s->count++;
			j = 0;
			s->fields[s->count - 1][j] = '\0';
			separator_area = 1;
			continue;
		}

		separator_area = 0;

		/* Field unusually large? */
		if (j == sizeof(s->fields[s->count - 1]) - 2) {
			snprintf(ip2clue_error, sizeof(ip2clue_error),
				"field is too long");
			return -1;
		}

		s->fields[s->count - 1][j++] = line[i];
		s->fields[s->count - 1][j] = '\0';
	}

	/* Unterminated line? */
	if (inside_quote == 1) {
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"invalid number of quotes");
		return -1;
	}

	return s->count;
}

/*
 * Returns the number of lines for a file
 */
long ip2clue_file_lines(const char *file)
{
	char buf[1024 * 1024];
	int fd;
	ssize_t n, i;
	long ret = -1;

	fd = open(file, O_RDONLY);
	if (fd == -1) {
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"cannot open file [%s] (%s)",
				file, strerror(errno));
		return -1;
	}

	ret = 0;
	while (1) {
		n = read(fd, buf, sizeof(buf));
		if (n == -1) {
			snprintf(ip2clue_error, sizeof(ip2clue_error),
				"cannot read (%s)", strerror(errno));
			close(fd);
			return -1;
		} else if (n == 0) {
			break;
		}

		for (i = 0; i < n; i++)
			if (buf[i] == '\n')
				ret++;
	}

	close(fd);

	return ret;
}

/*
 * Destroy data
 */
void ip2clue_destroy(struct ip2clue_db *db)
{
	int i;
	struct ip2clue_cell_v4 *p_cell4, *cell4;
	struct ip2clue_cell_v6 *p_cell6, *cell6;

	if (db == NULL)
		return;

	db->usage_count--;
	if (db->usage_count > 0)
		return;

	/* TODO: put 'for' under 'p_cell4 = ' */
	for (i = 0; i < db->no_of_cells; i++) {
		if (db->v4_or_v6 == IP2CLUE_TYPE_V4) {
			p_cell4 = (struct ip2clue_cell_v4 *) db->cells;
			cell4 = &p_cell4[i];
			if (cell4->extra != NULL)
				free(cell4->extra);
		} else {
			p_cell6 = (struct ip2clue_cell_v6 *) db->cells;
			cell6 = &p_cell6[i];
			if (cell6->extra != NULL)
				free(cell6->extra);
		}
	}

	if (db->cells != NULL)
		free(db->cells);

	free(db);
}


/*
 * Search for an IPv4
 */
struct ip2clue_cell_v4 *ip2clue_search_v4(struct ip2clue_db *db,
	const char *s_ip)
{
	unsigned int ip;
	struct in_addr in;
	long left, middle, right;
	struct ip2clue_cell_v4 *cells;

	if (inet_pton(AF_INET, s_ip, &in) != 1) {
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"malformed address");
		db->lookup_malformed++;
		return NULL;
	}

	ip = ntohl(in.s_addr);

	left = 0;
	right = db->no_of_cells - 1;
	cells = (struct ip2clue_cell_v4 *) db->cells;
	while (right >= left) {
		middle = (right + left + 1) / 2;

		/*
		printf("ip=%u left=%ld (%u->%u), middle=%ld (%u->%u), right=%ld (%u->%u)\n",
			ip,
			left, db->cells[left].ip_start, db->cells[left].ip_end,
			middle, db->cells[middle].ip_start, db->cells[middle].ip_end,
			right, db->cells[right].ip_start, db->cells[right].ip_end);
		*/

		if (ip > cells[middle].ip_end) {
			left = middle + 1;
			if (left == db->no_of_cells)
				break;
		} else if (ip < cells[middle].ip_start) {
			right = middle - 1;
			if (right == -1)
				break;
		} else {
			db->lookup_ok++;
			return &cells[middle];
		}
	}

	snprintf(ip2clue_error, sizeof(ip2clue_error),
		"cannot find address");
	db->lookup_notfound++;

	return NULL;
}

/*
 * search for an IPv6
 */
struct ip2clue_cell_v6 *ip2clue_search_v6(struct ip2clue_db *db, const char *s_ip)
{
	unsigned int ip[4], i;
	struct in6_addr in;
	long left, middle, right;
	struct ip2clue_cell_v6 *cells;
	/*char a1[64], a2[64], a3[64];*/

	if (inet_pton(AF_INET6, s_ip, &in) != 1) {
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"malformed address");
		db->lookup_malformed++;
		return NULL;
	}

	for (i = 0; i < 4; i++)
		ip[i] = ntohl(in.s6_addr32[i]);

	left = 0;
	right = db->no_of_cells - 1;
	cells = (struct ip2clue_cell_v6 *) db->cells;
	while (right >= left) {
		middle = (right + left + 1) / 2;

		/*
		ip2clue_addr_v6(a1, sizeof(a1), ip);
		ip2clue_addr_v6(a2, sizeof(a2), cells[middle].ip_start);
		ip2clue_addr_v6(a3, sizeof(a3), cells[middle].ip_end);
		printf("Comparing ip=%s with ip_start=%s ip_end=%s...\n",
			a1, a2, a3);
		*/
		if (ip2clue_compare_v6(ip, cells[middle].ip_end) > 0) {
			left = middle + 1;
			if (left == db->no_of_cells)
				break;
		} else if (ip2clue_compare_v6(ip, cells[middle].ip_start) < 0) {
			right = middle - 1;
			if (right == -1)
				break;
		} else {
			db->lookup_ok++;
			return &cells[middle];
		}
	}

	snprintf(ip2clue_error, sizeof(ip2clue_error),
		"cannot find address");
	db->lookup_notfound++;

	return NULL;
}

/*
 * Dump info about a cell
 */
void ip2clue_dump_cell_v6(char *out, size_t out_size, struct ip2clue_cell_v6 *cell)
{
	char s[64], e[64];

	ip2clue_addr_v6(s, sizeof(s), cell->ip_start);
	ip2clue_addr_v6(e, sizeof(e), cell->ip_end);
	snprintf(out, out_size, "%s %s -> %s", cell->country_short, s, e);
	/* TODO: dump extra! */
}

/*
 * Print extra structure
 */
void ip2clue_print_extra(char *out, size_t out_size, const struct ip2clue_extra *e)
{
	snprintf(out, out_size, "country_long=%s region=%s city=%s isp=%s latitude=%f"
		" longitude=%f zip=%s domain=%s timezone=%s netspeed=%s"
		" idd=%s areacode=%s ws_code=%s ws_name=%s\n",
		e->country_long, e->region, e->city, e->isp, e->latitude,
		e->longitude, e->zip, e->domain, e->timezone, e->netspeed,
		e->idd, e->areacode, e->ws_code, e->ws_name);
}

/*
 * Search an IP in a list
 */
struct ip2clue_cell_v4 *ip2clue_list_search_v4(struct ip2clue_list *list,
	const char *ip)
{
	unsigned int i;
	struct ip2clue_db *db;
	struct ip2clue_cell_v4 *ret = NULL;

	for (i = 0; i < list->number; i++) {
		db = list->entries[i];
		if (db->v4_or_v6 != IP2CLUE_TYPE_V4)
			continue;

		ret = ip2clue_search_v4(db, ip);
		if (ret != NULL)
			break;
	}

	if (ret == NULL)
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"not found");

	return ret;
}

/*
 * Returns a IPv4 address from a special IPv6 address
 * 2002:xxyy:zztt::/48, ::a.b.c.d and ::ffff:a.b.c.d
 */
/* TODO */

/*
 * Search an IP in a list
 */
struct ip2clue_cell_v6 *ip2clue_list_search_v6(struct ip2clue_list *list,
	const char *ip)
{
	unsigned int i;
	struct ip2clue_db *db;
	struct ip2clue_cell_v6 *ret = NULL;

	for (i = 0; i < list->number; i++) {
		db = list->entries[i];
		if (db->v4_or_v6 != IP2CLUE_TYPE_V6)
			continue;

		ret = ip2clue_search_v6(db, ip);
		if (ret != NULL)
			break;
	}

	if (ret == NULL) {
		/* TODO: Now, try special IPv4 encapsulated in IPv6 addresses */
	}

	if (ret == NULL)
		snprintf(ip2clue_error, sizeof(ip2clue_error),
			"not found");

	return ret;
}

/*
 * Builds a string answer
 * Returns 0 if address not found or other errors received, else 1.
 */
int ip2clue_list_search(struct ip2clue_list *list, char *out,
	const unsigned int out_size, const char *format, const char *ip)
{
	struct ip2clue_cell_v4 *v4;
	struct ip2clue_cell_v6 *v6;
	struct ip2clue_extra *e;
	char cs[4];
	unsigned int rest, i, format_size, special, a_len;
	char a[256];

	if (strchr(ip, '.')) {
		v4 = ip2clue_list_search_v4(list, ip);
		if (v4 == NULL)
			return 0;

		strcpy(cs, v4->country_short);
		e = v4->extra;
	} else {
		v6 = ip2clue_list_search_v6(list, ip);
		if (v6 == NULL)
			return 0;

		strcpy(cs, v6->country_short);
		e = v6->extra;
	}

	strcpy(out, "");
	rest = out_size - 1;
	format_size = strlen(format);
	special = 0;
	for (i = 0; i < format_size; i++) {
		strcpy(a, "");

		if (format[i] == '%') {
			if (special == 1) {
				strcpy(a, "%");
				special = 0;
			} else {
				special = 1;
				continue;
			}
		} else if (special == 1) {
			if (format[i] == 's') {
				snprintf(a, sizeof(a), "%s", cs);
			} else if (format[i] == 'P') {
				snprintf(a, sizeof(a), "%s", ip);
			} else if (e != NULL) {
				switch (format[i]) {
				case 'L': snprintf(a, sizeof(a), "%s", e->country_long); break;
				case 'r': snprintf(a, sizeof(a), "%s", e->region); break;
				case 'c': snprintf(a, sizeof(a), "%s", e->city); break;
				case 'i': snprintf(a, sizeof(a), "%s", e->isp); break;
				case 'x': snprintf(a, sizeof(a), "%f", e->latitude); break;
				case 'y': snprintf(a, sizeof(a), "%f", e->longitude); break;
				case 'z': snprintf(a, sizeof(a), "%s", e->zip); break;
				case 'd': snprintf(a, sizeof(a), "%s", e->domain); break;
				case 't': snprintf(a, sizeof(a), "%s", e->timezone); break;
				case 'n': snprintf(a, sizeof(a), "%s", e->netspeed); break;
				case 'k': snprintf(a, sizeof(a), "%s", e->idd); break;
				case 'a': snprintf(a, sizeof(a), "%s", e->areacode); break;
				case 'w': snprintf(a, sizeof(a), "%s", e->ws_code); break;
				case 'W': snprintf(a, sizeof(a), "%s", e->ws_name); break;
				}
			}
		} else {
			snprintf(a, 2, "%c", format[i]);
		}

		special = 0;

		a_len = strlen(a);
		if (a_len > rest) {
			snprintf(ip2clue_error, sizeof(ip2clue_error),
				"output buffer too short to add"
				" more %u bytes (%s)",
				a_len, a);
			return 0;
		}

		strcat(out, a);
		rest -= a_len;
	}

	return 1;
}



Mode Type Size Ref File
100644 blob 47 a9a866602d44352da048bd6ae6970951275c9568 .exclude
100644 blob 115 8c323b0915d6cfb17e770cea790c7a688adf546f .gitignore
100644 blob 35147 94a9ed024d3859793618152ea559a168bbcbb5e2 LICENSE
100644 blob 1614 a1a71bb742e512b346775f8c9d5e34f1dbe7f0ef Makefile.in
100644 blob 1780 89d08247cf7dd7e38b83fd9da500f6eee926e1ae README
100644 blob 965 c06153174a6dbae1d92908e79493c6a2288bcc4b TODO
040000 tree - 4183fb09e7128f53343a3a1c187af50171c19895 clients
100755 blob 30 92c4bc48245c00408cd7e1fd89bc1a03058f4ce4 configure
040000 tree - 866ff01beda9a20ef5068a5a117504a1b2b496cf crons
100755 blob 15674 c93b35dad5dedf498b90aafcbf409a4844b1bc8c duilder
100644 blob 728 bf74b5b4239eac406de35ff628ebb00456335c15 duilder.conf
040000 tree - ab6bf593a73be7b2cb422c2bf75a81958524b530 etc
100644 blob 1758 4a76efe2ee5125d6d19cee5d9f5abe06cde4883c i_conf.c
100644 blob 448 fb8429f7ea61c8669a83f1279fce35eee492a6fc i_conf.h
100644 blob 90 8bc7f81e7fff1a5c7b350cb464131cb8f2fae6eb i_config.h
100644 blob 2140 f029ed1d41c3b4cf9be580d4b39438c2dda2c803 i_types.h
100644 blob 11075 c9658a4f24bd96c55c3a0b48b470020851e21e51 i_util.c
100644 blob 1293 619bd9eda37a3c700d2fa03dcda5c627f685551e i_util.h
100644 blob 2258 e830c971aca34462dbcf4aed1c5f1b68d804d124 ip2clue.c
100644 blob 1200 eadfb9d5cd34bf7004ad4710d59762316ad5c7f1 ip2clue.spec.in
100644 blob 6717 d4d5cbc67f02e5e154bacc2beadfc23b470db2a1 ip2clued.c
100644 blob 630 58bdcc5108d0bc8b7e6d3e54515c5ff1027fcb2c my.conf
100644 blob 7035 88d5eb5f8b8e63ee404e7c292496bdd1fa57c1c0 parser.c
100644 blob 780 af2e94d9ca6889d29c0dc89726f5ccfac6c01587 parser.h
100644 blob 548 92e5dd7d252934f699e58212eba66fe805079d5a parser_core.c
100644 blob 118 2130d9f9da8bf36eae902ad3ba11c570d647739f parser_core.h
100644 blob 11633 d4620ff6accb1dd1057dd897ec465b304058e4b8 parser_ip2location.c
100644 blob 185 4775d2e989d7e830f71144d2492658214cb31c0a parser_ip2location.h
100644 blob 5064 7e96815b92eadb0cd89a1b6bd5684b80ad048557 parser_text.c
100644 blob 240 1643d56047dfd0609d6827ac475451e2db4bbef2 parser_text.h
100644 blob 4317 d920b0b0e9574b4ffc9976ad3bdc7db7eab9b3b0 test1.c
100644 blob 4415 94663e1cd87b5a7d1cfda5c0230a972a05f7cbd9 test2.c
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/ip2clue

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/ip2clue

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/ip2clue

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main