geoloc

Small C program to get geolocation info from ipapi.co
git clone https://git.e1e0.net/geoloc.git
Log | Files | Refs | README | LICENSE

commit 550e25e32cb5a4b4084ee14a4cc81114fe9374d0
Author: Paco Esteban <paco@onna.be>
Date:   Wed, 24 Jul 2019 16:28:41 +0200

initial commit

Diffstat:
A.gitignore | 3+++
AMakefile | 24++++++++++++++++++++++++
Ageoloc.c | 219+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ageoloc.in.1 | 35+++++++++++++++++++++++++++++++++++
4 files changed, 281 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +geoloc +geoloc.1 +*.o diff --git a/Makefile b/Makefile @@ -0,0 +1,24 @@ +CC = cc +CFLAGS != curl-config --cflags +LIBS != curl-config --libs +LIBS += -ljson-c +PREFIX = /usr/local + +.PHONY: all clean +.SUFFIXES: .in.1 .1 + +all: geoloc geoloc.1 + +clean: + rm -f geoloc geoloc.o geoloc.1 + +install: all + install -m 0755 geoloc $(PREFIX)/bin + install -m 0644 geoloc.1 $(PREFIX)/man/man1 + +geoloc: geoloc.o + $(CC) -o geoloc geoloc.o $(LIBS) + +.in.1.1: + mandoc -Tlint -Wstyle $< + cp -f $< $@ diff --git a/geoloc.c b/geoloc.c @@ -0,0 +1,219 @@ +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <curl/curl.h> +#include <json-c/json.h> + +struct MemoryStruct { + char *memory; + size_t size; +}; + +// Usage function +void +usage() +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\tgeoloc [-h] [-f] <ip>\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "\t-h\n"); + fprintf(stderr, "\t\tprints help and exits\n"); + fprintf(stderr, "\t-f\n"); + fprintf(stderr, "\t\tprints full output instead of just summary\n"); +} + +// libcurl callback to write contents to memory +static size_t +WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + char *ptr = realloc(mem->memory, mem->size + realsize + 1); + if(ptr == NULL) { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +int +main(int argc, char *argv[]) +{ + int ch; + int f_help = 0; + int f_full = 0; + + int is_v4, is_v6; + void *inet_dst; // we do not actually use this. Only to check valid ip + + CURL *curl_handle; + CURLcode res; + struct MemoryStruct chunk; + char url[64]; + char *base_url = "https://ipapi.co/"; + char *json_url = "/json"; + + // we get back a json object in key/value pairs, nothing complex + struct json_object *parsed_json; + struct json_object *ip; + struct json_object *city; + struct json_object *region; + // struct json_object *region_code; + // struct json_object *country; + struct json_object *country_name; + struct json_object *continent_code; + // struct json_object *in_eu; + struct json_object *postal; + struct json_object *latitude; + struct json_object *longitude; + struct json_object *timezone; + struct json_object *utc_offset; + // struct json_object *country_calling_code; + struct json_object *currency; + // struct json_object *languages; + struct json_object *asn; + struct json_object *org; + + chunk.memory = malloc(1); /* will be grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ + + while ((ch = getopt(argc, argv, "hf")) != -1) { + switch (ch) { + case 'h': + f_help = 1; + break; + case 'f': + f_full = 1; + break; + default: + usage(); + return 1; + } + } + // remove already processed arg count and advance index in vector + argc -= optind; + argv += optind; + + if (f_help == 1) { + usage(); + return 0; + } + + if (argc < 1) { + usage(); + return 1; + } + + // validate ip input + is_v4 = inet_pton(AF_INET, argv[0], inet_dst); + is_v6 = inet_pton(AF_INET6, argv[0], inet_dst); + if ((is_v4 != 1) && (is_v6 != 1)) { + fprintf(stderr, "Not a valid IP address\n"); + return 3; + } + + // if ip is ok, create the API url we'll call + strlcpy(url, base_url, sizeof(url)); + strlcat(url, argv[0], sizeof(url)); + strlcat(url, json_url, sizeof(url)); + + curl_global_init(CURL_GLOBAL_ALL); + curl_handle = curl_easy_init(); + curl_easy_setopt(curl_handle, CURLOPT_URL, url); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, + WriteMemoryCallback); /* send all data to this function */ + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + res = curl_easy_perform(curl_handle); + + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + } + else { + /* + * Now, our chunk.memory points to a memory block that is + * chunk.size bytes big and contains the remote file. + * We'll now parse the json response. + */ + + parsed_json = json_tokener_parse(chunk.memory); + json_object_object_get_ex(parsed_json, "ip", &ip); + json_object_object_get_ex(parsed_json, "city", &city); + json_object_object_get_ex(parsed_json, "region", &region); + /* json_object_object_get_ex(parsed_json, "region_code", + &region_code); */ + // json_object_object_get_ex(parsed_json, "country", &country); + json_object_object_get_ex(parsed_json, "country_name", + &country_name); + json_object_object_get_ex(parsed_json, "continent_code", + &continent_code); + // json_object_object_get_ex(parsed_json, "in_eu", &in_eu); + json_object_object_get_ex(parsed_json, "postal", &postal); + json_object_object_get_ex(parsed_json, "latitude", &latitude); + json_object_object_get_ex(parsed_json, "longitude", &longitude); + json_object_object_get_ex(parsed_json, "timezone", &timezone); + json_object_object_get_ex(parsed_json, "utc_offset", + &utc_offset); + /* json_object_object_get_ex(parsed_json, "country_calling_code", + &country_calling_code); */ + json_object_object_get_ex(parsed_json, "currency", &currency); + // json_object_object_get_ex(parsed_json, "languages", &languages); + json_object_object_get_ex(parsed_json, "asn", &asn); + json_object_object_get_ex(parsed_json, "org", &org); + + // and print the requested info + printf("Requested ip:\t%s\n", json_object_get_string(ip)); + if (f_full == 1) { + printf("Location:\t%s - %s\n", + json_object_get_string(postal), + json_object_get_string(city)); + printf("\t\t%s - %s (%s)\n", + json_object_get_string(region), + json_object_get_string(country_name), + json_object_get_string(continent_code)); + printf("\t\t%s (%s)\n", + json_object_get_string(timezone), + json_object_get_string(utc_offset)); + printf("\t\t%s\n", + json_object_get_string(currency)); + } else { + printf("Country:\t%s (%s)\n", + json_object_get_string(country_name), + json_object_get_string(postal)); + } + printf("Org:\t\t%s (%s)\n", json_object_get_string(org), + json_object_get_string(asn)); + printf("Coordinates:\t%f, %f\n", json_object_get_double(latitude), + json_object_get_double(longitude)); + printf( + "See on map:\thttps://www.openstreetmap.org/search?query=%f%%2C%f#map=12/%f/%f\n", + json_object_get_double(latitude), json_object_get_double(longitude), + json_object_get_double(latitude), json_object_get_double(longitude)); + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + free(chunk.memory); + + /* we're done with libcurl, so clean it up */ + curl_global_cleanup(); + + return 0; +} diff --git a/geoloc.in.1 b/geoloc.in.1 @@ -0,0 +1,35 @@ +.Dd July 24, 2019 +.Dt GEOLOC 1 +.Os +.Sh NAME +.Nm geoloc +.Nd get geolocation data from IP addresses +.Sh SYNOPSIS +.Nm +.Op Fl h +.Op Fl f +.Ar ip +.Sh DESCRIPTION +.Nm +uses +.Lk https://ipapi.co/ +to get geolocation information about the provided +.Ar ip +It accepts IPv4 or IPv6 addresses. +.Pp +Its arguments are as follows: +.Bl -tag -width Ds +.It Fl h +Prints help and exists. +.It Fl f +.Dq Full , +gives more information. +.It Ar ip +The ip to look for. +This is mandatory. +.El +.Sh EXIT STATUS +.Ex -std +.Sh AUTHORS +.An Paco Esteban +.Mt paco@onna.be