telize

High performance JSON IP and GeoIP REST API (IP Geolocation)
Log | Files | Refs | README | LICENSE

location.c (5102B)


      1 /*
      2  * Telize 3.0.0
      3  * Copyright (c) 2013-2020, Frederic Cambus
      4  * https://www.telize.com
      5  *
      6  * Created:      2013-08-15
      7  * Last Updated: 2020-10-21
      8  *
      9  * Telize is released under the BSD 2-Clause license.
     10  * See LICENSE file for details.
     11  */
     12 
     13 #include <time.h>
     14 
     15 #include "telize.h"
     16 #include "location.h"
     17 
     18 #include "assets.h"
     19 
     20 static int	response_sent(struct netbuf *);
     21 
     22 int
     23 request_location(struct http_request *req)
     24 {
     25 	struct tm		*info;
     26 	struct sockaddr_in	ipv4;
     27 	struct sockaddr_in6	ipv6;
     28 	MMDB_lookup_result_s	lookup;
     29 	time_t			rawtime;
     30 	struct kore_buf		*json, buf;
     31 	MMDB_entry_data_s	entry_data;
     32 	size_t			tz_len, len;
     33 	char			*ptr, *callback, *tz;
     34 	int			slen, gai_error, mmdb_error;
     35 	char			ip[INET6_ADDRSTRLEN], tzenv[256];
     36 
     37 	if (!telize_request_ip(req, ip, sizeof(ip))) {
     38 		http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
     39 		return (KORE_RESULT_OK);
     40 	}
     41 
     42 	http_populate_get(req);
     43 
     44 	/* Set response headers */
     45 	http_response_header(req, "Access-Control-Allow-Origin", "*");
     46 	http_response_header(req, "Cache-Control", "no-cache");
     47 	http_response_header(req, "Content-Type",
     48 	    "application/json; charset=utf-8");
     49 
     50 	/* Specific IP passed in the URL */
     51 	if (req->hdlr->type == HANDLER_TYPE_DYNAMIC) {
     52 		if ((ptr = strrchr(req->path, '/')) != NULL) {
     53 			ptr++;
     54 			len = strlen(ptr);
     55 
     56 			if (len == 0 || len > sizeof(ip) - 1) {
     57 				http_response(req, HTTP_STATUS_BAD_REQUEST,
     58 				    asset_bad_ip_json, asset_len_bad_ip_json);
     59 				return (KORE_RESULT_OK);
     60 			}
     61 
     62 			memcpy(ip, ptr, len);
     63 			ip[len] = '\0';
     64 
     65 			if (!inet_pton(AF_INET, ip, &(ipv4.sin_addr)) &&
     66 			    !inet_pton(AF_INET6, ip, &(ipv6.sin6_addr))) {
     67 				http_response(req, HTTP_STATUS_BAD_REQUEST,
     68 				    asset_bad_ip_json, asset_len_bad_ip_json);
     69 				return (KORE_RESULT_OK);
     70 			}
     71 		}
     72 	}
     73 
     74 	callback = NULL;
     75 	json = kore_buf_alloc(4096);
     76 
     77 	/* Handle callback parameter */
     78 	if (http_argument_get_string(req, "callback", &callback))
     79 		kore_buf_appendf(json, "%s(", callback);
     80 
     81 	kore_buf_appendf(json, "{\"ip\":\"%s\"", ip);
     82 
     83 	/* GeoLite2 City lookup */
     84 	lookup = MMDB_lookup_string(&telize_city, ip, &gai_error, &mmdb_error);
     85 
     86 	telize_getdata(json, &lookup, &entry_data,
     87 	    "continent_code", ENTRY_TYPE_STRING, "continent", "code", NULL);
     88 	telize_getdata(json, &lookup, &entry_data,
     89 	    "country", ENTRY_TYPE_STRING, "country", "names", "en", NULL);
     90 
     91 	MMDB_get_value(&lookup.entry, &entry_data, "country", "iso_code", NULL);
     92 
     93 	if (entry_data.has_data) {
     94 		kore_buf_appendf(json, ",\"country_code\":\"%.*s\"",
     95 		    entry_data.data_size, entry_data.utf8_string);
     96 
     97 		for (size_t loop = 0; loop < COUNTRIES; loop++) {
     98 			if (!strncmp(country_code_array[loop],
     99 			    entry_data.utf8_string, 2)) {
    100 				kore_buf_appendf(json,
    101 				    ",\"country_code3\":\"%s\"",
    102 				    country_code3_array[loop]);
    103 				break;
    104 			}
    105 		}
    106 	}
    107 
    108 	telize_getdata(json, &lookup, &entry_data, "region",
    109 	    ENTRY_TYPE_STRING, "subdivisions", "0", "names", "en", NULL);
    110 
    111 	telize_getdata(json, &lookup, &entry_data, "region_code",
    112 	    ENTRY_TYPE_STRING, "subdivisions", "0", "iso_code", NULL);
    113 
    114 	telize_getdata(json, &lookup, &entry_data, "city",
    115 	    ENTRY_TYPE_STRING, "city", "names", "en", NULL);
    116 
    117 	telize_getdata(json, &lookup, &entry_data, "postal_code",
    118 	    ENTRY_TYPE_STRING, "postal", "code", NULL);
    119 
    120 	telize_getdata(json, &lookup, &entry_data, "latitude",
    121 	    ENTRY_TYPE_DOUBLE, "location", "latitude", NULL);
    122 
    123 	telize_getdata(json, &lookup, &entry_data, "longitude",
    124 	    ENTRY_TYPE_DOUBLE, "location", "longitude", NULL);
    125 
    126 	MMDB_get_value(&lookup.entry, &entry_data,
    127 	    "location", "time_zone", NULL);
    128 
    129 	if (entry_data.has_data) {
    130 		tz_len = entry_data.data_size;
    131 		tz = strndup(entry_data.utf8_string, tz_len);
    132 		if (!tz) {
    133 			kore_buf_free(json);
    134 			http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
    135 			return (KORE_RESULT_OK);
    136 		}
    137 
    138 		kore_buf_init(&buf, tz_len);
    139 		kore_buf_append(&buf, tz, tz_len);
    140 		kore_buf_replace_string(&buf, "/", "\\/", 2);
    141 		kore_buf_appendf(json, ",\"timezone\":\"%s\"",
    142 		    kore_buf_stringify(&buf, NULL));
    143 		kore_buf_cleanup(&buf);
    144 
    145 		slen = snprintf(tzenv, sizeof(tzenv), "TZ=%s", tz);
    146 		if (slen == -1 || (size_t)slen >= sizeof(tzenv)) {
    147 			kore_buf_free(json);
    148 			http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
    149 			return (KORE_RESULT_OK);
    150 		}
    151 
    152 		putenv(tzenv);
    153 		tzset();
    154 		time(&rawtime);
    155 		info = localtime(&rawtime);
    156 		kore_buf_appendf(json, ",\"offset\":%d", info->tm_gmtoff);
    157 
    158 		free(tz);
    159 	}
    160 
    161 	/* GeoLite2 ASN lookup */
    162 	lookup = MMDB_lookup_string(&telize_asn, ip, &gai_error, &mmdb_error);
    163 
    164 	telize_getdata(json, &lookup, &entry_data, "asn",
    165 	    ENTRY_TYPE_UINT32, "autonomous_system_number", NULL);
    166 
    167 	telize_getdata(json, &lookup, &entry_data, "organization",
    168 	    ENTRY_TYPE_STRING, "autonomous_system_organization", NULL);
    169 
    170 	kore_buf_append(json, callback != NULL ? "});\n" : "}\n",
    171 	    callback != NULL ? 4 : 2);
    172 
    173 	http_response_stream(req, HTTP_STATUS_OK, json->data,
    174 	    json->offset, response_sent, json);
    175 
    176 	return (KORE_RESULT_OK);
    177 }
    178 
    179 static int
    180 response_sent(struct netbuf *nb)
    181 {
    182 	struct kore_buf		*buf = nb->extra;
    183 
    184 	kore_buf_free(buf);
    185 
    186 	return (KORE_RESULT_OK);
    187 }