]> git.ipfire.org Git - location/libloc.git/blame - src/country.c
importer: Drop EDROP as it has been merged into DROP
[location/libloc.git] / src / country.c
CommitLineData
ec684c1a
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2019 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
17#include <errno.h>
18#include <stdlib.h>
19#include <string.h>
20
c12a1385
MT
21#include <libloc/libloc.h>
22#include <libloc/compat.h>
23#include <libloc/country.h>
3eb1eed6 24#include <libloc/network.h>
c12a1385 25#include <libloc/private.h>
ec684c1a 26
3eb1eed6
MT
27static const struct loc_special_country {
28 const char code[3];
29 enum loc_network_flags flags;
30} loc_special_countries[] = {
31 { "A1", LOC_NETWORK_FLAG_ANONYMOUS_PROXY },
32 { "A2", LOC_NETWORK_FLAG_SATELLITE_PROVIDER },
33 { "A3", LOC_NETWORK_FLAG_ANYCAST },
34 { "XD", LOC_NETWORK_FLAG_DROP },
35 { "", 0 },
36};
37
ec684c1a
MT
38struct loc_country {
39 struct loc_ctx* ctx;
40 int refcount;
41
32b16159
MT
42 // Store the country code in a 3 byte buffer. Two bytes for the code, and NULL so
43 // that we can use strcmp() and return a pointer.
44 char code[3];
45 char continent_code[3];
ec684c1a
MT
46
47 char* name;
48};
49
50LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
e73b7709
MT
51 // Check of the country code is valid
52 if (!loc_country_code_is_valid(country_code)) {
53 errno = EINVAL;
54 return 1;
55 }
e646a8f3 56
ec684c1a
MT
57 struct loc_country* c = calloc(1, sizeof(*c));
58 if (!c)
198e382c 59 return 1;
ec684c1a
MT
60
61 c->ctx = loc_ref(ctx);
62 c->refcount = 1;
63
32b16159
MT
64 // Set the country code
65 loc_country_code_copy(c->code, country_code);
ec684c1a
MT
66
67 DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
68 *country = c;
69
70 return 0;
71}
72
73LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
74 country->refcount++;
75
76 return country;
77}
78
79static void loc_country_free(struct loc_country* country) {
80 DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
81
ec684c1a
MT
82 if (country->name)
83 free(country->name);
84
85 loc_unref(country->ctx);
86 free(country);
87}
88
89LOC_EXPORT struct loc_country* loc_country_unref(struct loc_country* country) {
90 if (--country->refcount > 0)
91 return NULL;
92
93 loc_country_free(country);
ec684c1a
MT
94 return NULL;
95}
96
97LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
98 return country->code;
99}
100
101LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
4ec8f610
MT
102 if (!*country->continent_code)
103 return NULL;
104
ec684c1a
MT
105 return country->continent_code;
106}
107
108LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
32b16159
MT
109 // Check for valid input
110 if (!continent_code || strlen(continent_code) != 2) {
111 errno = EINVAL;
112 return 1;
113 }
ec684c1a 114
32b16159
MT
115 // Store the code
116 loc_country_code_copy(country->continent_code, continent_code);
ec684c1a
MT
117
118 return 0;
119}
120
121LOC_EXPORT const char* loc_country_get_name(struct loc_country* country) {
122 return country->name;
123}
124
125LOC_EXPORT int loc_country_set_name(struct loc_country* country, const char* name) {
126 if (country->name)
127 free(country->name);
128
32b16159 129 if (name) {
ec684c1a
MT
130 country->name = strdup(name);
131
32b16159
MT
132 // Report error if we could not copy the string
133 if (!country->name)
134 return 1;
135 }
136
ec684c1a
MT
137 return 0;
138}
139
af208e26 140LOC_EXPORT int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
32b16159 141 return strncmp(country1->code, country2->code, 2);
ec684c1a
MT
142}
143
b904896a
MT
144int loc_country_new_from_database_v1(struct loc_ctx* ctx, struct loc_stringpool* pool,
145 struct loc_country** country, const struct loc_database_country_v1* dbobj) {
32b16159 146 char buffer[3] = "XX";
ec684c1a
MT
147
148 // Read country code
a7a3d958 149 loc_country_code_copy(buffer, dbobj->code);
ec684c1a
MT
150
151 // Create a new country object
152 int r = loc_country_new(ctx, country, buffer);
153 if (r)
154 return r;
155
32b16159
MT
156 // Copy continent code
157 if (*dbobj->continent_code)
158 loc_country_code_copy((*country)->continent_code, dbobj->continent_code);
ec684c1a
MT
159
160 // Set name
ec684c1a
MT
161 const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
162 if (name) {
163 r = loc_country_set_name(*country, name);
164 if (r)
165 goto FAIL;
166 }
ec684c1a
MT
167
168 return 0;
169
170FAIL:
171 loc_country_unref(*country);
172 return r;
173}
174
b904896a
MT
175int loc_country_to_database_v1(struct loc_country* country,
176 struct loc_stringpool* pool, struct loc_database_country_v1* dbobj) {
32b16159
MT
177 off_t name = 0;
178
ec684c1a 179 // Add country code
32b16159
MT
180 if (*country->code)
181 loc_country_code_copy(dbobj->code, country->code);
ec684c1a
MT
182
183 // Add continent code
32b16159
MT
184 if (*country->continent_code)
185 loc_country_code_copy(dbobj->continent_code, country->continent_code);
ec684c1a
MT
186
187 // Save the name string in the string pool
32b16159
MT
188 if (country->name)
189 name = loc_stringpool_add(pool, country->name);
190
ec684c1a
MT
191 dbobj->name = htobe32(name);
192
193 return 0;
194}
0f0829ef
MT
195
196LOC_EXPORT int loc_country_code_is_valid(const char* cc) {
197 // It cannot be NULL
198 if (!cc || !*cc)
199 return 0;
200
201 // It must be 2 characters long
202 if (strlen(cc) != 2)
203 return 0;
204
205 // It must only contain A-Z
206 for (unsigned int i = 0; i < 2; i++) {
207 if (cc[i] < 'A' || cc[i] > 'Z')
208 return 0;
209 }
210
e8ebd079
MT
211 // The code cannot begin with an X (those are reserved for private use)
212 if (*cc == 'X')
213 return 0;
214
0f0829ef
MT
215 // Looks valid
216 return 1;
217}
3eb1eed6
MT
218
219LOC_EXPORT int loc_country_special_code_to_flag(const char* cc) {
220 // Check if we got some input
221 if (!cc || !*cc) {
222 errno = EINVAL;
223 return -1;
224 }
225
226 // Return flags for any known special country
227 for (const struct loc_special_country* country = loc_special_countries;
228 country->flags; country++) {
32b16159 229 if (strncmp(country->code, cc, 2) == 0)
3eb1eed6
MT
230 return country->flags;
231 }
232
233 return 0;
234}