utils

small programs, scripts and utils
git clone https://git.e1e0.net/utils.git
Log | Files | Refs

geoloc.c (6269B)


      1 /*
      2  * Copyright 2019-2020 Paco Esteban <paco@e1e0.net>
      3  *
      4  * Permission to use, copy, modify, and/or distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
      9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     10  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
     11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
     12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
     13  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     14  * PERFORMANCE OF THIS SOFTWARE.
     15 */
     16 
     17 #include <sys/socket.h>
     18 #include <arpa/inet.h>
     19 
     20 #include <err.h>
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <unistd.h>
     25 
     26 #include <curl/curl.h>
     27 #include <json-c/json.h>
     28 
     29 #define BASE_URL	"https://ipapi.co"
     30 #define JSON_ENDPOINT	"json"
     31 #define IN6ADDRSZ	16
     32 
     33 const char *version = "v0.2.0";
     34 
     35 struct MemoryStruct {
     36 	char *memory;
     37 	size_t size;
     38 };
     39 
     40 static size_t	    write_memory_callback(void *, size_t, size_t, void *);
     41 void		    usage(void);
     42 int		    make_api_call(char *);
     43 int		    print_data(void *);
     44 
     45 // Usage function
     46 void
     47 usage(void)
     48 {
     49 	(void)fprintf(stderr,
     50 	    "usage: geoloc [-v] <ip>\n");
     51 	exit(1);
     52 }
     53 
     54 // libcurl callback to write contents to memory
     55 static size_t
     56 write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp)
     57 {
     58 	size_t realsize = size * nmemb;
     59 	struct MemoryStruct *mem = (struct MemoryStruct *)userp;
     60 
     61 	char *ptr = realloc(mem->memory, mem->size + realsize + 1);
     62 	if(ptr == NULL) {
     63 		/* out of memory! */
     64 		printf("not enough memory (realloc returned NULL)\n");
     65 		return 0;
     66 	}
     67 
     68 	mem->memory = ptr;
     69 	memcpy(&(mem->memory[mem->size]), contents, realsize);
     70 	mem->size += realsize;
     71 	mem->memory[mem->size] = 0;
     72 
     73 	return realsize;
     74 }
     75 
     76 int
     77 make_api_call(char *url) {
     78 	CURL *curl_handle;
     79 	CURLcode res;
     80 	struct MemoryStruct *chunk = malloc(sizeof(struct MemoryStruct));
     81 	int ret = 0;
     82 
     83 	chunk->memory = malloc(1);	/* will be grown as needed */
     84 	chunk->size = 0;		/* no data at this point */
     85 
     86 	curl_global_init(CURL_GLOBAL_ALL);
     87 	curl_handle = curl_easy_init();
     88 	curl_easy_setopt(curl_handle, CURLOPT_URL, url);
     89 	curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION,
     90 	    write_memory_callback);  /* send all data to this function  */
     91 	/* we pass our 'chunk' struct to the callback function */
     92 	curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)chunk);
     93 	curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
     94 
     95 	res = curl_easy_perform(curl_handle);
     96 
     97 	if(res != CURLE_OK) {
     98 		ret = -1;
     99 		goto out;
    100 	}
    101 
    102 	/*
    103 	 * Now, our chunk.memory points to a memory block that is
    104 	 * chunk.size bytes big and contains the remote file.
    105 	 * We'll now parse the json response and print the data.
    106 	 */
    107 	if (print_data(chunk->memory) == -1) {
    108 		ret = -1;
    109 		goto out;
    110 	}
    111 
    112 out:
    113 	/* cleanup curl stuff */
    114 	curl_easy_cleanup(curl_handle);
    115 	curl_global_cleanup();
    116 
    117 	free(chunk->memory);
    118 	free(chunk);
    119 
    120 	return ret;
    121 }
    122 
    123 int
    124 print_data(void *json_data) {
    125 	struct json_object *parsed_json = NULL, *ip = NULL, *city = NULL,
    126 			   *region = NULL, *country_name = NULL,
    127 			   *continent_code = NULL, *postal = NULL,
    128 			   *latitude = NULL, *longitude = NULL,
    129 			   *timezone = NULL, *utc_offset = NULL,
    130 			   *currency = NULL, *asn = NULL, *org = NULL;
    131 
    132 	/* Parse json data */
    133 	parsed_json = json_tokener_parse(json_data);
    134 
    135 	json_object_object_get_ex(parsed_json, "ip", &ip);
    136 	json_object_object_get_ex(parsed_json, "city", &city);
    137 	json_object_object_get_ex(parsed_json, "region", &region);
    138 	json_object_object_get_ex(parsed_json, "country_name", &country_name);
    139 	json_object_object_get_ex(parsed_json, "continent_code",
    140 	    &continent_code);
    141 	json_object_object_get_ex(parsed_json, "postal", &postal);
    142 	json_object_object_get_ex(parsed_json, "latitude", &latitude);
    143 	json_object_object_get_ex(parsed_json, "longitude", &longitude);
    144 	json_object_object_get_ex(parsed_json, "timezone", &timezone);
    145 	json_object_object_get_ex(parsed_json, "utc_offset", &utc_offset);
    146 	json_object_object_get_ex(parsed_json, "currency", &currency);
    147 	json_object_object_get_ex(parsed_json, "org", &org);
    148 
    149 	/* and print the requested info */
    150 	printf("Requested ip:\t%s\n", json_object_get_string(ip));
    151 	printf("Location:\t%s - %s\n", json_object_get_string(postal),
    152 	    json_object_get_string(city));
    153 	printf("\t\t%s - %s (%s)\n", json_object_get_string(region),
    154 	    json_object_get_string(country_name),
    155 	    json_object_get_string(continent_code));
    156 	printf("\t\t%s (%s)\n", json_object_get_string(timezone),
    157 	    json_object_get_string(utc_offset));
    158 	printf("\t\t%s\n",
    159 	    json_object_get_string(currency));
    160 	printf("Org:\t\t%s (%s)\n", json_object_get_string(org),
    161 	    json_object_get_string(asn));
    162 	printf("Coordinates:\t%f, %f\n", json_object_get_double(latitude),
    163 	    json_object_get_double(longitude));
    164 	printf(
    165 	    "See on map:\t"
    166 	    "https://www.openstreetmap.org/search?query=%f%%2C%f#map=12/%f/%f\n",
    167 	    json_object_get_double(latitude), json_object_get_double(longitude),
    168 	    json_object_get_double(latitude), json_object_get_double(longitude));
    169 	return 0;
    170 }
    171 
    172 int
    173 main(int argc, char *argv[])
    174 {
    175 	int ch, f_version = 0, is_v4 = 0, is_v6 = 0;
    176 	char inet_dst[IN6ADDRSZ];  // we do not actually use this. Only to check valid ip
    177 
    178 	char *url = NULL;
    179 
    180 	while ((ch = getopt(argc, argv, "v")) != -1) {
    181 		switch (ch) {
    182 		case 'v':
    183 			f_version = 1;
    184 			break;
    185 		default:
    186 			usage();
    187 		}
    188 	}
    189 	// remove already processed arg count and advance index in vector
    190 	argc -= optind;
    191 	argv += optind;
    192 
    193 	if (f_version == 1) {
    194 		(void)fprintf(stderr, "%s\n", version);
    195 		return 0;
    196 	}
    197 
    198 	if (argc < 1) {
    199 		usage();
    200 		return 1;
    201 	}
    202 
    203 	// validate ip input
    204 	is_v4 = inet_pton(AF_INET, argv[0], (void *)inet_dst);
    205 	is_v6 = inet_pton(AF_INET6, argv[0], (void *)inet_dst);
    206 	if ((is_v4 != 1) && (is_v6 != 1))
    207 		err(1, "invalid IP");
    208 
    209 	// if ip is ok, create the API url we'll call
    210 	if (asprintf(&url, "%s/%s/%s", BASE_URL, argv[0], JSON_ENDPOINT) == -1)
    211 		err(1, "url creation");
    212 
    213 	if (make_api_call(url) == -1)
    214 		err(1, "api call");
    215 
    216 	free(url);
    217 
    218 	return 0;
    219 }