xref: /plan9/sys/src/cmd/ip/httpd/classify.c (revision 57837e0bd5c9fc29e2017ad8430ad11f4e09d373)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ndb.h>
5 #include "whois.h"
6 
7 typedef struct Country Country;
8 
9 struct Country
10 {
11 	char *code;
12 	char *name;
13 };
14 
15 Country badc[] =
16 {
17 	{"af", "afghanistan"},
18 	{"cu", "cuba"},
19 	{"ir", "iran"},
20 	{"iq", "iraq"},
21 	{"ly", "libya"},
22 	{"kp", "north korea"},
23 	{"sd", "sudan"},
24 	{"sy", "syria"},
25 	{ 0, 0 }
26 };
27 
28 Country goodc[] =
29 {
30 	// the original, us and canada
31 	{"us", "united states of america"},
32 	{"ca", "canada"},
33 	{"gov", "gov"},
34 	{"mil", "mil"},
35 
36 	// the european union
37 	{ "eu",	"european union" },
38 	{ "be",	"belgium" },
39 	{ "de",	"germany" },
40 	{ "fr",	"france" },
41 	{ "it",	"italy" },
42 	{ "lu",	"luxembourg" },
43 	{ "nl",	"netherlands" },
44 	{ "dk",	"denmark" },
45 	{ "ie",	"ireland" },
46 	{ "gb",	"great britain" },
47 	{ "uk",	"united kingdom" },
48 	{ "gr",	"greece" },
49 	{ "es",	"spain" },
50 	{ "pt",	"portugal" },
51 	{ "au",	"australia" },
52 	{ "fi",	"finland" },
53 	{ "se",	"sweden" },
54 
55 	// the rest
56 	{"au", "australia"},
57 	{"no", "norway"},
58 	{"cz", "czech republic"},
59 	{"hu", "hungary"},
60 	{"pl", "poland"},
61 	{"jp", "japan"},
62 	{"ch", "switzerland"},
63 	{"nz", "new zealand"},
64 	{ 0, 0 }
65 };
66 
67 char *gov[] =
68 {
69 	"gov",
70 	"gouv",
71 	"mil",
72 	"government",
73 	0,
74 };
75 
76 Country allc[] =
77 {
78 	{ "ad",	"andorra" },
79 	{ "ae",	"united arab emirates" },
80 	{ "af",	"afghanistan" },
81 	{ "ag",	"antigua and barbuda" },
82 	{ "ai",	"anguilla" },
83 	{ "al",	"albania" },
84 	{ "am",	"armenia" },
85 	{ "an",	"netherlands antilles" },
86 	{ "ao",	"angola" },
87 	{ "aq",	"antarctica" },
88 	{ "ar",	"argentina" },
89 	{ "as",	"american samoa" },
90 	{ "at",	"austria" },
91 	{ "au",	"australia" },
92 	{ "aw",	"aruba" },
93 	{ "az",	"azerbaijan" },
94 	{ "ba",	"bosnia and herzegovina" },
95 	{ "bb",	"barbados" },
96 	{ "bd",	"bangladesh" },
97 	{ "be",	"belgium" },
98 	{ "bf",	"burkina faso" },
99 	{ "bg",	"bulgaria" },
100 	{ "bh",	"bahrain" },
101 	{ "bi",	"burundi" },
102 	{ "bj",	"benin" },
103 	{ "bm",	"bermuda" },
104 	{ "bn",	"brunei darussalam" },
105 	{ "bo",	"bolivia" },
106 	{ "br",	"brazil" },
107 	{ "bs",	"bahamas" },
108 	{ "bt",	"bhutan" },
109 	{ "bu",	"burma" },
110 	{ "bv",	"bouvet island" },
111 	{ "bw",	"botswana" },
112 	{ "by",	"belarus" },
113 	{ "bz",	"belize" },
114 	{ "ca",	"canada" },
115 	{ "cc",	"cocos (keeling) islands" },
116 	{ "cf",	"central african republic" },
117 	{ "cg",	"congo" },
118 	{ "ch",	"switzerland" },
119 	{ "ci",	"cote d'ivoire (ivory coast)" },
120 	{ "ck",	"cook islands" },
121 	{ "cl",	"chile" },
122 	{ "cm",	"cameroon" },
123 	{ "cn",	"china" },
124 	{ "co",	"colombia" },
125 	{ "cr",	"costa rica" },
126 	{ "cs",	"czechoslovakia (former)" },
127 	{ "ct",	"canton and enderbury island" },
128 	{ "cu",	"cuba" },
129 	{ "cv",	"cape verde" },
130 	{ "cx",	"christmas island" },
131 	{ "cy",	"cyprus" },
132 	{ "cz",	"czech republic" },
133 	{ "dd",	"german democratic republic" },
134 	{ "de",	"germany" },
135 	{ "dj",	"djibouti" },
136 	{ "dk",	"denmark" },
137 	{ "dm",	"dominica" },
138 	{ "do",	"dominican republic" },
139 	{ "dz",	"algeria" },
140 	{ "ec",	"ecuador" },
141 	{ "ee",	"estonia" },
142 	{ "eg",	"egypt" },
143 	{ "eh",	"western sahara" },
144 	{ "er",	"eritrea" },
145 	{ "es",	"spain" },
146 	{ "et",	"ethiopia" },
147 	{ "eu",	"european union" },
148 	{ "fi",	"finland" },
149 	{ "fj",	"fiji" },
150 	{ "fk",	"falkland islands (malvinas)" },
151 	{ "fm",	"micronesia" },
152 	{ "fo",	"faroe islands" },
153 	{ "fr",	"france" },
154 	{ "fx",	"france, metropolitan" },
155 	{ "ga",	"gabon" },
156 	{ "gb",	"great britain (uk)" },
157 	{ "gd",	"grenada" },
158 	{ "ge",	"georgia" },
159 	{ "gf",	"french guiana" },
160 	{ "gh",	"ghana" },
161 	{ "gi",	"gibraltar" },
162 	{ "gl",	"greenland" },
163 	{ "gm",	"gambia" },
164 	{ "gn",	"guinea" },
165 	{ "gp",	"guadeloupe" },
166 	{ "gq",	"equatorial guinea" },
167 	{ "gr",	"greece" },
168 	{ "gs",	"s. georgia and s. sandwich isls." },
169 	{ "gt",	"guatemala" },
170 	{ "gu",	"guam" },
171 	{ "gw",	"guinea-bissau" },
172 	{ "gy",	"guyana" },
173 	{ "hk",	"hong kong" },
174 	{ "hm",	"heard and mcdonald islands" },
175 	{ "hn",	"honduras" },
176 	{ "hr",	"croatia (hrvatska)" },
177 	{ "ht",	"haiti" },
178 	{ "hu",	"hungary" },
179 	{ "id",	"indonesia" },
180 	{ "ie",	"ireland" },
181 	{ "il",	"israel" },
182 	{ "in",	"india" },
183 	{ "io",	"british indian ocean territory" },
184 	{ "iq",	"iraq" },
185 	{ "ir",	"iran" },
186 	{ "is",	"iceland" },
187 	{ "it",	"italy" },
188 	{ "jm",	"jamaica" },
189 	{ "jo",	"jordan" },
190 	{ "jp",	"japan" },
191 	{ "jt",	"johnston island" },
192 	{ "ke",	"kenya" },
193 	{ "kg",	"kyrgyzstan" },
194 	{ "kh",	"cambodia (democratic kampuchea)" },
195 	{ "ki",	"kiribati" },
196 	{ "km",	"comoros" },
197 	{ "kn",	"saint kitts and nevis" },
198 	{ "kp",	"korea (north)" },
199 	{ "kr",	"korea (south)" },
200 	{ "kw",	"kuwait" },
201 	{ "ky",	"cayman islands" },
202 	{ "kz",	"kazakhstan" },
203 	{ "la",	"laos" },
204 	{ "lb",	"lebanon" },
205 	{ "lc",	"saint lucia" },
206 	{ "li",	"liechtenstein" },
207 	{ "lk",	"sri lanka" },
208 	{ "lr",	"liberia" },
209 	{ "ls",	"lesotho" },
210 	{ "lt",	"lithuania" },
211 	{ "lu",	"luxembourg" },
212 	{ "lv",	"latvia" },
213 	{ "ly",	"libya" },
214 	{ "ma",	"morocco" },
215 	{ "mc",	"monaco" },
216 	{ "md",	"moldova" },
217 	{ "mg",	"madagascar" },
218 	{ "mh",	"marshall islands" },
219 	{ "mi",	"midway islands" },
220 	{ "mk",	"macedonia" },
221 	{ "ml",	"mali" },
222 	{ "mm",	"myanmar" },
223 	{ "mn",	"mongolia" },
224 	{ "mo",	"macau" },
225 	{ "mp",	"northern mariana islands" },
226 	{ "mq",	"martinique" },
227 	{ "mr",	"mauritania" },
228 	{ "ms",	"montserrat" },
229 	{ "mt",	"malta" },
230 	{ "mu",	"mauritius" },
231 	{ "mv",	"maldives" },
232 	{ "mw",	"malawi" },
233 	{ "mx",	"mexico" },
234 	{ "my",	"malaysia" },
235 	{ "mz",	"mozambique" },
236 	{ "na",	"namibia" },
237 	{ "nc",	"new caledonia" },
238 	{ "ne",	"niger" },
239 	{ "nf",	"norfolk island" },
240 	{ "ng",	"nigeria" },
241 	{ "ni",	"nicaragua" },
242 	{ "nl",	"netherlands" },
243 	{ "no",	"norway" },
244 	{ "np",	"nepal" },
245 	{ "nq",	"dronning maud land" },
246 	{ "nr",	"nauru" },
247 	{ "nt",	"neutral zone" },
248 	{ "nu",	"niue" },
249 	{ "nz",	"new zealand (aotearoa)" },
250 	{ "om",	"oman" },
251 	{ "pa",	"panama" },
252 	{ "pc",	"pacific islands" },
253 	{ "pe",	"peru" },
254 	{ "pf",	"french polynesia" },
255 	{ "pg",	"papua new guinea" },
256 	{ "ph",	"philippines" },
257 	{ "pk",	"pakistan" },
258 	{ "pl",	"poland" },
259 	{ "pm",	"st. pierre and miquelon" },
260 	{ "pn",	"pitcairn" },
261 	{ "pr",	"puerto rico" },
262 	{ "pu",	"united states misc. pacific islands" },
263 	{ "pt",	"portugal" },
264 	{ "pw",	"palau" },
265 	{ "py",	"paraguay" },
266 	{ "qa",	"qatar" },
267 	{ "re",	"reunion" },
268 	{ "ro",	"romania" },
269 	{ "ru",	"russian federation" },
270 	{ "rw",	"rwanda" },
271 	{ "sa",	"saudi arabia" },
272 	{ "sb",	"solomon islands" },
273 	{ "sc",	"seychelles" },
274 	{ "sd",	"sudan" },
275 	{ "se",	"sweden" },
276 	{ "sg",	"singapore" },
277 	{ "sh",	"st. helena" },
278 	{ "si",	"slovenia" },
279 	{ "sj",	"svalbard and jan mayen islands" },
280 	{ "sk",	"slovak republic" },
281 	{ "sl",	"sierra leone" },
282 	{ "sm",	"san marino" },
283 	{ "sn",	"senegal" },
284 	{ "so",	"somalia" },
285 	{ "sr",	"suriname" },
286 	{ "st",	"sao tome and principe" },
287 	{ "su",	"ussr (former)" },
288 	{ "sv",	"el salvador" },
289 	{ "sy",	"syria" },
290 	{ "sz",	"swaziland" },
291 	{ "tc",	"turks and caicos islands" },
292 	{ "td",	"chad" },
293 	{ "tf",	"french southern territories" },
294 	{ "tg",	"togo" },
295 	{ "th",	"thailand" },
296 	{ "tj",	"tajikistan" },
297 	{ "tk",	"tokelau" },
298 	{ "tm",	"turkmenistan" },
299 	{ "tn",	"tunisia" },
300 	{ "to",	"tonga" },
301 	{ "tp",	"east timor" },
302 	{ "tr",	"turkey" },
303 	{ "tt",	"trinidad and tobago" },
304 	{ "tv",	"tuvalu" },
305 	{ "tw",	"taiwan" },
306 	{ "tz",	"tanzania" },
307 	{ "ua",	"ukraine" },
308 	{ "ug",	"uganda" },
309 	{ "uk",	"united kingdom" },
310 	{ "um",	"us minor outlying islands" },
311 	{ "us",	"united states" },
312 	{ "uy",	"uruguay" },
313 	{ "uz",	"uzbekistan" },
314 	{ "va",	"vatican city state (holy see)" },
315 	{ "vc",	"saint vincent and the grenadines" },
316 	{ "ve",	"venezuela" },
317 	{ "vg",	"virgin islands (british)" },
318 	{ "vi",	"virgin islands (u.s.)" },
319 	{ "vn",	"viet nam" },
320 	{ "vu",	"vanuatu" },
321 	{ "wf",	"wallis and futuna islands" },
322 	{ "wk",	"wake island" },
323 	{ "ws",	"samoa" },
324 	{ "yd",	"democratic yemen" },
325 	{ "ye",	"yemen" },
326 	{ "yt",	"mayotte" },
327 	{ "yu",	"yugoslavia" },
328 	{ "za",	"south africa" },
329 	{ "zm",	"zambia" },
330 	{ "zr",	"zaire" },
331 	{ "zw",	"zimbabwe" },
332 
333 	{"gov", "gov"},
334 	{"mil", "mil"},
335 
336 	{ 0, 0 }
337 };
338 
339 int classdebug;
340 
341 static int
incountries(char * s,Country * cp)342 incountries(char *s, Country *cp)
343 {
344 	for(; cp->code != 0; cp++)
345 		if(cistrcmp(s, cp->code) == 0
346 		|| cistrcmp(s, cp->name) == 0)
347 			return 1;
348 	return 0;
349 }
350 
351 static int
indomains(char * s,char ** dp)352 indomains(char *s, char **dp)
353 {
354 	for(; *dp != nil; dp++)
355 		if(cistrcmp(s, *dp) == 0)
356 			return 1;
357 
358 	return 0;
359 }
360 
361 int
classify(char * ip,Ndbtuple * t)362 classify(char *ip, Ndbtuple *t)
363 {
364 	int isgov, iscountry, isbadc, isgoodc;
365 	char dom[256];
366 	char *df[128];
367 	Ndbtuple *nt, *x;
368 	int n;
369 
370 	isgov = iscountry = isbadc = 0;
371 	isgoodc = 1;
372 
373 	for(nt = t; nt != nil; nt = nt->entry){
374 		if(strcmp(nt->attr, "country") == 0){
375 			iscountry = 1;
376 			if(incountries(nt->val, badc)){
377 				if(classdebug)fprint(2, "isbadc\n");
378 				isbadc = 1;
379 				isgoodc = 0;
380 			} else if(!incountries(nt->val, goodc)){
381 				if(classdebug)fprint(2, "!isgoodc\n");
382 				isgoodc = 0;
383 			}
384 		}
385 
386 		/* domain names can always hurt, even without forward verification */
387 		if(strcmp(nt->attr, "dom") == 0){
388 			strncpy(dom, nt->val, sizeof dom);
389 			dom[sizeof(dom)-1] = 0;
390 			n = getfields(dom, df, nelem(df), 0, ".");
391 
392 			/* a bad country in a domain name is always believed */
393 			if(incountries(df[n-1], badc)){
394 				if(classdebug)fprint(2, "isbadc dom\n");
395 				isbadc = 1;
396 				isgoodc = 0;
397 			}
398 
399 			/* a goverment in a domain name is always believed */
400 			if(n > 1 && indomains(df[n-2], gov))
401 				isgov = 1;
402 		}
403 	}
404 	if(iscountry == 0){
405 		/* did the forward lookup work? */
406 		for(nt = t; nt != nil; nt = nt->entry){
407 			if(strcmp(nt->attr, "ip") == 0 && strcmp(nt->val, ip) == 0)
408 				break;
409 		}
410 
411 		/* see if the domain name ends in a country code */
412 		if(nt != nil && (x = ndbfindattr(t, nt, "dom")) != nil){
413 			strncpy(dom, x->val, sizeof dom);
414 			dom[sizeof(dom)-1] = 0;
415 			n = getfields(dom, df, nelem(df), 0, ".");
416 			if(incountries(df[n-1], allc))
417 				iscountry = 1;
418 		}
419 	}
420 	if(iscountry == 0)
421 		return Cunknown;
422 	if(isbadc)
423 		return Cbadc;
424 	if(!isgoodc && isgov)
425 		return Cbadgov;
426 	return Cok;
427 }
428