telize

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

location.c (5282B)


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