commit 7125cc3eda3bd95d63ec07f09cf310e7bf50b09f
Author: Paco Esteban <paco@e1e0.net>
Date: Mon, 10 Feb 2020 19:28:26 +0100
first commit with geoloc
Diffstat:
6 files changed, 330 insertions(+), 0 deletions(-)
diff --git a/geoloc/.gitignore b/geoloc/.gitignore
@@ -0,0 +1,2 @@
+geoloc
+*.o
diff --git a/geoloc/LICENSE b/geoloc/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2019 Paco Esteban
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/geoloc/Makefile b/geoloc/Makefile
@@ -0,0 +1,20 @@
+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
+
+clean:
+ rm -f geoloc geoloc.o
+
+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)
diff --git a/geoloc/README b/geoloc/README
@@ -0,0 +1,39 @@
+# geoloc
+
+## description
+
+`geoloc` is a small utility to get geolocalization information given an ip
+address.
+
+It's written in C and uses [libcurl][1] and [json-c][2] do do its job.
+
+## installation
+
+```
+make
+make install # with enough privileges
+```
+
+You'll need `libcurl` and `json-c` on the _"usual locations"_
+
+If you need to make some adjustments, take a look at the `Makefile`, is
+quite simple.
+
+## disclaimer
+
+This is a personal project to play with C. It is definitely not ready for any
+kind of production use. This is a toy.
+
+## license
+
+Distributed under the [ISC][3] license
+
+## bugs
+
+Probably more than lines of code.
+Patches are welcome. Send them to <patches@e1e0.net>
+
+---
+[1]: https://curl.haxx.se/libcurl/
+[2]: https://github.com/json-c/json-c
+[3]: https://opensource.org/licenses/ISC
diff --git a/geoloc/geoloc.1 b/geoloc/geoloc.1
@@ -0,0 +1,39 @@
+.Dd July 29, 2019
+.Dt GEOLOC 1
+.Os
+.Sh NAME
+.Nm geoloc
+.Nd get geolocation data for IP addresses
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.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
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl v
+Prints version 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@e1e0.net
+.Sh BUGS
+Probably many.
+If you find one and want to send a patch, please to so to:
+.Mt patches@e1e0.net
diff --git a/geoloc/geoloc.c b/geoloc/geoloc.c
@@ -0,0 +1,217 @@
+#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>
+
+const char *version = "v0.1.0";
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+// Usage function
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: geoloc [-v] [-f] <ip>\n");
+ exit(1);
+}
+
+// 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 f_version = 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, "fv")) != -1) {
+ switch (ch) {
+ case 'f':
+ f_full = 1;
+ break;
+ case 'v':
+ f_version = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ // remove already processed arg count and advance index in vector
+ argc -= optind;
+ argv += optind;
+
+ if (f_version == 1) {
+ (void)fprintf(stderr, "%s\n", version);
+ 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", ®ion);
+ /* json_object_object_get_ex(parsed_json, "region_code",
+ ®ion_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", ¤cy);
+ // 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;
+}