· 6 years ago · Jun 23, 2019, 02:46 PM
1/*
2 Плагин: Bypass Guard
3
4 Основное назначение: Противодействие обходу банов по SteamID/IP
5
6 Использующийся инструментарий:
7 * Агрегатор номера автономной системы (номера провайдера), к которой принадлежит IP (Whois-модуль)
8 * Агрегатор кода страны (GeoIP-модуль)
9 * Агрегатор статуса IP-адреса в онлайн-сервисе проверок на Proxy/VPN [ Curl-модуль + сервис https://iphub.info/ ]
10 * Чёрный и белый списки IP-адресов
11
12 Данный плагин частично основан на функционале двух других плагинов:
13 * Proxy/VPN check 1.1b от juice: https://dev-cs.ru/resources/475/
14 * SubnetBan 2.2 от Lev: https://dev-cs.ru/resources/162/
15
16 Требования:
17 * Amx Mod X 1.9.0, либо новее: https://dev-cs.ru/resources/405/
18 * Reapi: https://dev-cs.ru/resources/73/
19 * Свежая geo-база (country): http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz
20 * AGHL.RU Dev Team Whois Module: http://aghl.ru/forum/viewtopic.php?f=19&p=24798
21 * Amxx Curl: https://github.com/Polarhigh/AmxxCurl/releases
22
23 Доступные команды:
24 * bg_allow_steamid <steamid> - Выдаёт указанному steamid иммунитет ко всем проверкам
25
26 Внимание! Внесение AS/IP в чёрный список не проверяет сервер на наличие игроков, подпадающих под добавленное ограничение.
27 Другими словами, забанив AS/IP игрока, который в данный момент находится на сервере, вам необходимо кикнуть его самостоятельно.
28
29 * bg_as_blacklist_add <as number> "<comment>"
30 * bg_as_blacklist_del <as number>
31 * bg_as_blacklist_show <page>
32
33 * bg_as_whitelist_add <as number> "<comment>"
34 * bg_as_whitelist_del <as number>
35 * bg_as_whitelist_show <page>
36
37 * bg_ip_blacklist_add <start ip> <end ip> "<comment>"
38 * bg_ip_blacklist_del <start ip> <end ip>
39 * bg_ip_blacklist_show <page>
40
41 * bg_ip_whitelist_add <start ip> <end ip> "<comment>"
42 * bg_ip_whitelist_del <start ip> <end ip>
43 * bg_ip_whitelist_show <page>
44
45 * bg_find_ip <ip> - Позволяет проверить наличие указанного IP в диапазонах чёрного и белого списков
46
47 * bg_get_as_by_ip <ip> - Позволяет получить AS-номер для указанного IP
48
49 * bg_find_as <as number> - Позволяет проверить наличие указанного AS-номера в чёрном и белом списках
50
51 * bg_check_ip <ip> - Позволяет проверить IP-адрес на причастность к Proxy/VPN (минуя весь кеш)
52
53 * bg_flush_data <1-5>:
54 1 - Обнулить хранилище, содержащее результаты проверок IP-адресов на Proxy/VPN (sql + trie)
55 2 - Обнулить хранилище, содержащее steamid'ы, имеющие иммунитет ко всем проверкам (nvault)
56 3 - Обнулить кеш проверок AS-номеров (trie, короткий кеш)
57 4 - Обнулить чёрный и белый список IP, удалить файл-список диапазонов IP
58 5 - Обнулить чёрный и белый список AS-номеров, кеш проверок AS-номеров, удалить файл-список AS-номеров
59
60 * bg_status - Выводит информацию о присутствующих игроках
61*/
62
63/* Thx to:
64 fantom за пример работы с форматом JSON
65 juice за плагин 'Proxy/VPN check'
66 Lev за плагин 'SubnetBan' и модуль 'Whois'
67 Polarhigh за модуль 'Amxx Curl'
68*/
69
70/* История обновлений:
71 08.01.2019:
72 * Закрытый релиз
73 09.01.2019:
74 * Добавлен белый список AS
75 * Доработки функционала консольных команд
76 * Улучшена информативность лог-файла
77 * Фиксы обнаруженных ошибок
78 19.01.2019:
79 * Смена сервиса проверки на Proxy/VPN с 'mind-media.com' на 'iphub.info'
80 * Смена метода работы с WEB (HTTP:X -> Amxx Curl)
81 * Теперь использование команд (добавление/удаление) логируется в файл
82 * Логирование разделёно на несколько частей, см. 'LOG_ENUM'
83 * Добавлены квары, предоставляющие некоторую гибкость поведения
84 * Команда 'bg_get_as_by_ip' теперь так же показывает поле 'Description' (провайдер)
85 * Добавлена команда 'bg_status'
86 22.01.2019:
87 * Улучшено поведение при возникновении ошибок при проверке на Proxy/VPN
88 * Для команды 'bg_allow_steamid' добавлен учёт 'VALVE_'
89 * Список разрешённых стран переведён на загрузку из файла
90 * Загрузка API-ключей переведена на загрузку из файла
91 * Информация, выводящаяся игроку при кике, выведена в lang-файл
92 * Добавлен квар 'bypass_guard_show_url'
93 * Добавлен квар 'bypass_guard_check_delay'
94 * Добавлен квар 'bypass_guard_kick_delay'
95 24.01.2019:
96 * Убран код, пропускающий казахов без проверки на Proxy/VPN (проблема была актуальна для mind-media.com)
97 * Устранение ряда незначительных недосмотров
98 * Все команды: srvcmd -> concmd, логирование использования сопровождается информацией о пользователе
99 * Открытый релиз
100 24.02.2019:
101 * Добавлен учёт отсутствия квара 'amx_default_access', спасибо w0w
102 * Пофикшен баг с пустым выводом команды 'bg_status', спасибо Nordic Warrior
103 * Хранение статуса IP-адресов переведено на SQL (не забудьте прописать sqlite или mysql в 'configs/modules.ini'). Спасибо fantom.
104 * Добавлены квары 'bg_sql_'. Рекомендуется удалить конфиг в 'configs/plugins', дабы он пересоздался.
105 * Добавлен квар 'bg_divide_logs_by_month'
106 * Для команды 'bg_get_as_by_ip' добавлен вывод страны
107 * Добавлена команда 'bg_check_ip', позволяющая проверить IP-адрес на причастность к Proxy/VPN
108 * Улучшена информативность поля "тип доступа" для команды 'bg_status', логов 'ALLOW.log', 'DENY.log', и 'PROXY_CHECK.log'
109 * Исправление выявленных недочётов в логике работы проверок
110 * Отдельная благодарность за подсказки: fl0wer, Sonyx, bionext
111 24.05.2019:
112 * Прекращена поддержка AMXX 183, теперь плагин требует AMXX 190+
113 * Потенциальное исправление падений сервера для тех, у кого они наблюдаются
114 * Исправление невозможности ввода нестандартного SteamID (STEAM_/VALVE_ + 2 цифры до ':') для команды 'bg_allow_steamid'
115 * Добавлен квар 'bypass_guard_immunity_flags'. Игроки, имеющие любой из перечисленных в его значении флагов, пропускаются
116 на сервер без каких-либо проверок (аналогично иммунитету по SteamID). Внимание! При использовании данного
117 квара (т.е. когда его значение не "") проверка наличия иммунитета через квар 'amx_default_access' отключается!
118 * Исправление ряда незначительных ошибок
119 07.06.2019:
120 * Добавлен учёт кода возврата "неправильный ключ". Ранее учитывалось только отсутствие ключа.
121*/
122
123new const PLUGIN_DATE[] = "07.06.2019"
124
125/* ----------------------- */
126
127#define AUTO_CFG // Создавать конфиг с кварами в 'configs/plugins', и запускать его ?
128
129#define ACCESS_FLAG ADMIN_CFG // Флаг доступа по-умолчанию ко всем консольным командам
130
131const MAX_KEYS = 5 // Макс. кол-во API-ключей. Увеличить при необходимости.
132
133const MAX_CODES = 32 // Макс. кол-во разрешённых стран. Увеличить при необходимости.
134
135new const DIR_NAME[] = "bypass_guard" // 'configs/%s', 'data/%s', 'logs/%s'
136new const IP_FILE_NAME[] = "ip_list.ini"
137new const AS_FILE_NAME[] = "as_list.ini"
138new const COUNTRY_FILE_NAME[] = "allowed_countries.ini"
139new const KEY_FILE_NAME[] = "api_keys.ini"
140new const IMMUNE_STEAMS_VAULT[] = "bypass_guard_steams"
141stock const DEBUG_VAULT[] = "bg_debug"
142
143enum _:LOG_ENUM { // Don't touch this!
144 LOG__PROXY_CHECK,
145 LOG__CMD,
146 LOG__ERROR,
147 //
148 // <--- new log type goes here!
149 //
150 LOG__LONG_QUERY, // Must be the last before LOG__DENY
151 // Must be the last in the enum --->
152 LOG__DENY,
153 LOG__ALLOW
154 // <---
155}
156
157new const LOG_NAME[LOG_ENUM][] = {
158 "PROXY_CHECK.log",
159 "CMD.log",
160 "ERROR.log",
161 "LONG_QUERY.log",
162 "DENY.log",
163 "ALLOW.log"
164}
165
166/* ----------------------- */
167
168#include <amxmodx>
169#include <sxgeo>
170#include <nvault>
171#include <sqlx>
172#include <reapi>
173#include <whois>
174#include <curl>
175#include <json>
176
177// default is is 4096
178#pragma dynamic 8192
179
180#define chx charsmax
181
182#define MAX_COUNTRY_LEN 64
183#define MAX_COMMENT_LEN 48
184#define MAX_AS_LEN 16
185#define MAX_DESC_LEN 64
186#define MAX_CODE_LEN 3
187#define MAX_ACCESS_LEN 32
188#define MAX_API_KEY_LEN 64
189#define MAX_SQL_ACC_LEN 64
190#define MAX_QUERY_LEN 1472
191#define CURL_BUFFER_SIZE 512
192
193new const _NA_[] = "N/A"
194
195enum ( += MAX_PLAYERS + 1 ) {
196 TASKID__RETRY_INIT_QUERY = 100
197}
198
199/* --- */
200
201enum {
202 QUERY__INIT_SYSTEM,
203 QUERY__CHECK_IP,
204 QUERY__FLUSH_IPS,
205 // Requests without waiting an answer --->
206 QUERY__INSERT_IP
207}
208
209enum _:SQL_DATA_STRUCT {
210 SQL_DATA__QUERY_TYPE,
211 SQL_DATA__PLAYER_INDEX,
212 SQL_DATA__PLAYER_USERID
213}
214
215/* --- */
216
217enum _:SQL_TABLE_COLUMN_ENUM {
218 SQL_COLUMN__ID,
219 SQL_COLUMN__IP,
220 SQL_COLUMN__IP_STATUS,
221 SQL_COLUMN__TIMESTAMP
222}
223
224new const SQL_TABLE_COLUMNS[SQL_TABLE_COLUMN_ENUM][] = {
225 "id",
226 "ip",
227 "ip_status",
228 "timestamp"
229}
230
231/* --- */
232
233enum _:STATES_ENUM {
234 STATE__NO,
235 STATE__NORMAL,
236 STATE__SUSPICIOUS,
237 STATE__BAN,
238 STATE__WHITELIST
239}
240
241new const IP_STATES[STATES_ENUM][] = {
242 "",
243 "Normal",
244 "Suspicious",
245 "Proxy/VPN",
246 ""
247}
248
249enum KICK_TYPE_ENUM {
250 KICK_TYPE__AS,
251 KICK_TYPE__IP_BAN,
252 KICK_TYPE__COUNTRY,
253 KICK_TYPE__PROXY_CHECK,
254 KICK_TYPE__PROXY_SQL,
255 KICK_TYPE__PROXY_CACHE,
256 KICK_TYPE__SUSPICIOUS_CHECK,
257 KICK_TYPE__SUSPICIOUS_SQL,
258 KICK_TYPE__SUSPICIOUS_CACHE,
259 KICK_TYPE__AS_CHECK_FAIL,
260 KICK_TYPE__PROXY_CHECK_FAIL
261}
262
263enum {
264 WHOIS_QUERY_TYPE__AUTO,
265 WHOIS_QUERY_TYPE__MANUAL
266}
267
268enum _:LIST_TYPE_ENUM {
269 LIST_TYPE__BLACKLIST,
270 LIST_TYPE__WHITELIST
271}
272
273new const LIST_NAME[LIST_TYPE_ENUM][] = { "blacklist", "whitelist" }
274
275enum _:RANGE_DATA_STRUCT {
276 RDS__START_IP,
277 RDS__END_IP,
278 RDS__COMMENT[MAX_COMMENT_LEN]
279}
280
281enum _:AS_DATA_STRUCT {
282 ASD__NUMBER[MAX_AS_LEN],
283 ASD__COMMENT[MAX_COMMENT_LEN]
284}
285
286enum _:AS_TRIE_STRUCT {
287 AST__STATE,
288 AST__ARRAY_POS
289}
290
291enum _:WHOIS_DATA_STRUCT {
292 WHOIS_DATA__QUERY_TYPE,
293 WHOIS_DATA__QUERY_TRIES,
294 WHOIS_DATA__PLAYER_USERID
295}
296
297enum _:CURL_DATA_STRUCT {
298 CURL_DATA__FILE_HANDLE,
299 CURL_DATA__SLIST_HANDLE,
300 CURL_DATA__PLAYER_INDEX,
301 CURL_DATA__PLAYER_USERID
302}
303
304enum /*IPHUB_CHECK_STATE*/ {
305 IP_CHECK_STATE__NORMAL,
306 IP_CHECK_STATE__DETECT,
307 IP_CHECK_STATE__SUSPICIOUS
308}
309
310enum _:AGREGATE_TYPE_ENUM {
311 AGREGATE_IP__CACHE,
312 AGREGATE_IP__SQL
313}
314
315enum _:EXPECTED_JSON_KEY_COUNT {
316 JSON_KEY__IP,
317 JSON_KEY__COUNTRY_CODE,
318 JSON_KEY__COUNTRY_NAME,
319 JSON_KEY__AS,
320 JSON_KEY__PROVIDER,
321 JSON_KEY__CHECK_STATE,
322 JSON_KEY__HOSTNAME
323}
324
325enum _:IP_INFO_STRUCT {
326 IP_INFO__AS[MAX_AS_LEN],
327 IP_INFO__DESC[MAX_DESC_LEN]
328}
329
330enum _:PCVAR_ENUM {
331 PCVAR__SQL_MODE,
332 PCVAR__HOST,
333 PCVAR__USER,
334 PCVAR__PWD,
335 PCVAR__DB,
336 PCVAR__TABLE,
337 PCVAR__CONN_TIMEOUT,
338 PCVAR__CREATE_TABLE,
339 PCVAR__PRUNE_OLD_IPS,
340 PCVAR__DIVIDE_LOGS,
341 PCVAR__IMMUNITY_FLAGS,
342 PCVAR__AMX_DEFAULT_ACCESS
343}
344
345enum _:CVAR_ENUM {
346 CVAR__PLUGIN_ENABLED,
347 CVAR__BAN_SUSPICIOUS,
348 CVAR__MAX_WHOIS_QUERY_TRIES,
349 CVAR__KICK_IF_CANT_CHECK,
350 CVAR__ALLOW_STEAM,
351 CVAR__SHOW_URL,
352 CVAR__CHECK_DELAY,
353 CVAR__KICK_DELAY,
354 Float:CVAR_F__REQUEST_TIMEOUT,
355 Float:CVAR_F__LONG_QUERY_TIME
356}
357
358new g_pCvar[PCVAR_ENUM]
359new g_eCvar[CVAR_ENUM]
360new g_eIpInfoData[IP_INFO_STRUCT]
361new g_iManualWhoisState
362new Array:g_eIpArray[LIST_TYPE_ENUM]
363new g_eRangeData[RANGE_DATA_STRUCT]
364new g_iIpCount[LIST_TYPE_ENUM]
365new g_hImmunity = INVALID_HANDLE
366new g_szDataDir[PLATFORM_MAX_PATH]
367new g_szIpListFile[PLATFORM_MAX_PATH]
368new Trie:g_tAsCache
369new Trie:g_tAsNumbers
370new g_szAsListFile[PLATFORM_MAX_PATH]
371new g_eAsData[AS_DATA_STRUCT]
372new g_iAsCount[LIST_TYPE_ENUM]
373new Array:g_aAsArray[LIST_TYPE_ENUM]
374new g_iDefAccFlags
375new g_iImmunityFlags
376new g_szGetWhoisIP[MAX_IP_LENGTH]
377new g_eAsTrieData[AS_TRIE_STRUCT]
378new Trie:g_tCurl
379new bool:g_bPluginEnded
380new g_eLogFile[LOG_ENUM][PLATFORM_MAX_PATH]
381new Trie:g_tIpInfo
382new g_szAccess[MAX_PLAYERS + 1][MAX_ACCESS_LEN]
383new g_szAsNumber[MAX_PLAYERS + 1][MAX_AS_LEN]
384new g_szDesc[MAX_PLAYERS + 1][MAX_DESC_LEN]
385new g_szCode[MAX_PLAYERS + 1][MAX_CODE_LEN * 2]
386new g_szCountry[MAX_PLAYERS + 1][MAX_COUNTRY_LEN]
387new g_szAllowedCodes[MAX_CODES][MAX_CODE_LEN]
388new g_iCodesCount
389new g_iCurrentKey
390new g_iLoadedKeys
391new g_szApiKey[MAX_KEYS][MAX_API_KEY_LEN]
392new Trie:g_tIpProxyStatus
393new Trie:g_tIpMatch
394new bool:g_bSystemAvailable
395new Handle:g_hSqlTuple
396new g_szQuery[MAX_QUERY_LEN]
397new g_szTableName[64]
398new g_szSqlMode[10]
399new g_szGetProxyIP[MAX_IP_LENGTH]
400new bool:g_bMySqlMode
401
402/* -------------------- */
403
404public plugin_init() {
405 register_plugin("Bypass Guard", PLUGIN_DATE, "mx?!")
406 register_dictionary("bypass_guard.txt")
407
408 g_pCvar[PCVAR__SQL_MODE] = create_cvar( "bg_sql_mode", "sqlite",
409 .description = "sqlite - use SQLite; mysql - use MySQL; don't forget to enable module in 'modules.ini'" );
410
411 g_pCvar[PCVAR__HOST] = create_cvar("bg_sql_host", "127.0.0.1", FCVAR_PROTECTED)
412 g_pCvar[PCVAR__USER] = create_cvar("bg_sql_user", "", FCVAR_PROTECTED)
413 g_pCvar[PCVAR__PWD] = create_cvar("bg_sql_pwd", "", FCVAR_PROTECTED)
414 g_pCvar[PCVAR__DB] = create_cvar("bg_sql_db", "bypass_guard", FCVAR_PROTECTED)
415 g_pCvar[PCVAR__TABLE] = create_cvar("bg_sql_table", "bypass_guard_ips", FCVAR_PROTECTED)
416 g_pCvar[PCVAR__CONN_TIMEOUT] = create_cvar("bg_sql_conn_timeout", "30", .has_min = true, .min_val = 1.0)
417 g_pCvar[PCVAR__CREATE_TABLE] = create_cvar("bg_sql_create_table", "1", .description = "Create table automaticaly?")
418
419 g_pCvar[PCVAR__PRUNE_OLD_IPS] = create_cvar( "bg_sql_prune_old_ips", "30",
420 .description = "IP records older than # days will be removed from DB (0 = disable)" );
421
422 bind_pcvar_float( create_cvar("bg_sql_request_timeout", "10.0", .has_min = true, .min_val = 2.0),
423 g_eCvar[CVAR_F__REQUEST_TIMEOUT] );
424
425 bind_pcvar_float(create_cvar("bg_sql_long_query_time", "10.0"), g_eCvar[CVAR_F__LONG_QUERY_TIME])
426
427 bind_pcvar_num( create_cvar("bypass_guard_enabled", "1",
428 .description = "Enable/Disable plugin work"), g_eCvar[CVAR__PLUGIN_ENABLED] );
429
430 bind_pcvar_num( create_cvar("bypass_guard_ban_suspicious", "0",
431 .description = "Ban suspicious IPs?"), g_eCvar[CVAR__BAN_SUSPICIOUS] );
432
433 bind_pcvar_num( create_cvar("bypass_guard_kick_if_cant_check", "0",
434 .description = "Kick player if check fails?"), g_eCvar[CVAR__KICK_IF_CANT_CHECK] );
435
436 bind_pcvar_num( create_cvar("bypass_guard_allow_steam", "1",
437 .description = "Allow steam players to join without checks?"), g_eCvar[CVAR__ALLOW_STEAM] );
438
439 /* --- */
440
441 new pCvar = create_cvar( "bypass_guard_immunity_flags", "",
442 .description = "Allow players with any of specified flags to join without checks^n\
443 If value is non-empty, standart immunity by absence of 'amx_default_access' flag will be disabled" );
444
445 new szFlags[32]; get_pcvar_string(pCvar, szFlags, chx(szFlags))
446 g_iImmunityFlags = read_flags(szFlags)
447 hook_cvar_change(pCvar, "hook_CvarChange")
448 g_pCvar[PCVAR__IMMUNITY_FLAGS] = pCvar
449
450 /* --- */
451
452 bind_pcvar_num( create_cvar("bypass_guard_show_url", "1",
453 .description = "Show URL when player gets kicked?"), g_eCvar[CVAR__SHOW_URL] );
454
455 bind_pcvar_num( create_cvar("bypass_guard_max_whois_tries", "2", .has_min = true, .min_val = 1.0,
456 .description = "Max whois query tries"), g_eCvar[CVAR__MAX_WHOIS_QUERY_TRIES] );
457
458 bind_pcvar_float( create_cvar("bypass_guard_check_delay", "0.1", .has_min = true, .min_val = 0.1,
459 .description = "Player check delay"), Float:g_eCvar[CVAR__CHECK_DELAY] );
460
461 bind_pcvar_float( create_cvar("bypass_guard_kick_delay", "1.0", .has_min = true, .min_val = 0.1,
462 .description = "Player kick delay (low values may break URL showing!)"), Float:g_eCvar[CVAR__KICK_DELAY] );
463
464 g_pCvar[PCVAR__DIVIDE_LOGS] = create_cvar( "bg_divide_logs_by_month", "1",
465 .description = "Divide 'ALLOW' and 'DENY' logs by month?" );
466
467#if defined AUTO_CFG
468 AutoExecConfig()
469#endif
470}
471
472/* -------------------- */
473
474public plugin_cfg() {
475 g_hImmunity = nvault_open(IMMUNE_STEAMS_VAULT)
476
477 if(g_hImmunity == INVALID_HANDLE) {
478 set_fail_state("[Immunity] Error opening nVault!")
479 }
480
481 /* --- */
482
483 new iLen = get_localinfo("amxx_datadir", g_szDataDir, chx(g_szDataDir))
484 formatex(g_szDataDir[iLen], chx(g_szDataDir) - iLen, "/%s", DIR_NAME)
485
486 if(!dir_exists(g_szDataDir)) {
487 mkdir(g_szDataDir)
488 }
489
490 /* --- */
491
492 register_concmd("bg_allow_steamid", "concmd_AllowSteamID", ACCESS_FLAG)
493 register_concmd("bg_as_blacklist_add", "concmd_AddToAsList", ACCESS_FLAG)
494 register_concmd("bg_as_blacklist_del", "concmd_DelFromAsList", ACCESS_FLAG)
495 register_concmd("bg_as_blacklist_show", "concmd_ShowAsList", ACCESS_FLAG)
496 register_concmd("bg_as_whitelist_add", "concmd_AddToAsList", ACCESS_FLAG)
497 register_concmd("bg_as_whitelist_del", "concmd_DelFromAsList", ACCESS_FLAG)
498 register_concmd("bg_as_whitelist_show", "concmd_ShowAsList", ACCESS_FLAG)
499 register_concmd("bg_ip_blacklist_add", "concmd_AddToIpList", ACCESS_FLAG)
500 register_concmd("bg_ip_whitelist_add", "concmd_AddToIpList", ACCESS_FLAG)
501 register_concmd("bg_ip_blacklist_del", "concmd_DelFromIpList", ACCESS_FLAG)
502 register_concmd("bg_ip_whitelist_del", "concmd_DelFromIpList", ACCESS_FLAG)
503 register_concmd("bg_ip_blacklist_show", "concmd_ShowIpList", ACCESS_FLAG)
504 register_concmd("bg_ip_whitelist_show", "concmd_ShowIpList", ACCESS_FLAG)
505 register_concmd("bg_find_ip", "concmd_FindIP", ACCESS_FLAG)
506 register_concmd("bg_get_as_by_ip", "concmd_GetAsByIP", ACCESS_FLAG)
507 register_concmd("bg_find_as", "concmd_FindAS", ACCESS_FLAG)
508 register_concmd("bg_check_ip", "concmd_CheckIP", ACCESS_FLAG)
509 register_concmd("bg_flush_data", "concmd_FlushData", ACCESS_FLAG)
510 register_concmd("bg_status", "concmd_Status", ACCESS_FLAG)
511
512 /* --- */
513
514 new pCvar = get_cvar_pointer("amx_default_access")
515
516 if(pCvar) {
517 new szFlags[32]; get_pcvar_string(pCvar, szFlags, chx(szFlags))
518 g_iDefAccFlags = read_flags(szFlags)
519 hook_cvar_change(pCvar, "hook_CvarChange")
520 g_pCvar[PCVAR__AMX_DEFAULT_ACCESS] = pCvar
521 }
522
523 /* --- */
524
525 g_eIpArray[LIST_TYPE__BLACKLIST] = ArrayCreate(RANGE_DATA_STRUCT, 1)
526 g_eIpArray[LIST_TYPE__WHITELIST] = ArrayCreate(RANGE_DATA_STRUCT, 1)
527 g_tAsCache = TrieCreate()
528 g_tAsNumbers = TrieCreate()
529 g_aAsArray[LIST_TYPE__BLACKLIST] = ArrayCreate(AS_DATA_STRUCT, 1)
530 g_aAsArray[LIST_TYPE__WHITELIST] = ArrayCreate(AS_DATA_STRUCT, 1)
531 g_tCurl = TrieCreate()
532 g_tIpInfo = TrieCreate()
533 g_tIpProxyStatus = TrieCreate()
534 g_tIpMatch = TrieCreate()
535
536 /* --- */
537
538 new szLogDir[PLATFORM_MAX_PATH]
539
540 iLen = get_localinfo("amxx_logs", szLogDir, chx(szLogDir))
541 formatex(szLogDir[iLen], chx(szLogDir) - iLen, "/%s", DIR_NAME)
542
543 if(!dir_exists(szLogDir)) {
544 mkdir(szLogDir)
545 }
546
547 for(new i; i <= LOG__LONG_QUERY; i++) {
548 formatex(g_eLogFile[i], PLATFORM_MAX_PATH - 1, "%s/%s", szLogDir, LOG_NAME[i])
549 }
550
551 /* --- */
552
553 new szCfgDir[PLATFORM_MAX_PATH]
554
555 iLen = get_localinfo("amxx_configsdir", szCfgDir, chx(szCfgDir))
556 iLen += formatex(szCfgDir[iLen], chx(szCfgDir) - iLen, "/%s", DIR_NAME)
557
558 if(!dir_exists(szCfgDir)) {
559 mkdir(szCfgDir)
560 }
561
562 new szKeyFile[PLATFORM_MAX_PATH]
563 formatex(szKeyFile, chx(szKeyFile), "%s/%s", szCfgDir, KEY_FILE_NAME)
564 func_LoadApiKeys(szKeyFile)
565
566 formatex(g_szIpListFile, chx(g_szIpListFile), "%s/%s", szCfgDir, IP_FILE_NAME)
567 formatex(g_szAsListFile, chx(g_szAsListFile), "%s/%s", szCfgDir, AS_FILE_NAME)
568 func_LoadRanges()
569 func_LoadAsNumbers()
570
571 formatex(szCfgDir[iLen], chx(szCfgDir) - iLen, "/%s", COUNTRY_FILE_NAME)
572 func_LoadAllowedCodes(szCfgDir)
573}
574
575/* -------------------- */
576
577public OnConfigsExecuted() {
578 new szLogDir[PLATFORM_MAX_PATH]
579
580 new iLen = get_localinfo("amxx_logs", szLogDir, chx(szLogDir))
581 formatex(szLogDir[iLen], chx(szLogDir) - iLen, "/%s", DIR_NAME)
582
583 new szTime[18], iDivide = get_pcvar_num(g_pCvar[PCVAR__DIVIDE_LOGS])
584
585 if(iDivide) {
586 get_time("%m.%Y_", szTime, chx(szTime))
587 }
588
589 for(new i = LOG__DENY; i <= LOG__ALLOW; i++) {
590 formatex(g_eLogFile[i], PLATFORM_MAX_PATH - 1, "%s/%s%s", szLogDir, szTime, LOG_NAME[i])
591 }
592
593 /* --- */
594
595 new szHost[MAX_SQL_ACC_LEN], szUser[MAX_SQL_ACC_LEN], szPwd[MAX_SQL_ACC_LEN], szDb[MAX_SQL_ACC_LEN]
596
597 get_pcvar_string(g_pCvar[PCVAR__HOST], szHost, chx(szHost))
598 get_pcvar_string(g_pCvar[PCVAR__USER], szUser, chx(szUser))
599 get_pcvar_string(g_pCvar[PCVAR__PWD], szPwd, chx(szPwd))
600 get_pcvar_string(g_pCvar[PCVAR__DB], szDb, chx(szDb))
601
602#if defined AUTO_CFG
603 new const szTemp[] = "*** PROTECTED ***"
604 set_pcvar_string(g_pCvar[PCVAR__USER], szTemp)
605 set_pcvar_string(g_pCvar[PCVAR__PWD], szTemp)
606#endif
607
608 get_pcvar_string(g_pCvar[PCVAR__SQL_MODE], g_szSqlMode, chx(g_szSqlMode))
609
610 if(!SQL_SetAffinity(g_szSqlMode)) {
611 plugin_end()
612 set_fail_state("Failed to set affinity to '%s' (module not loaded?)", g_szSqlMode)
613 }
614
615 get_pcvar_string(g_pCvar[PCVAR__TABLE], g_szTableName, chx(g_szTableName))
616
617 g_hSqlTuple = SQL_MakeDbTuple(szHost, szUser, szPwd, szDb, get_pcvar_num(g_pCvar[PCVAR__CONN_TIMEOUT]))
618
619 SQL_SetCharset(g_hSqlTuple, "utf8")
620
621 func_MakeInitQuery()
622}
623
624/* -------------------- */
625
626stock func_MakeInitQuery() {
627 set_task(g_eCvar[CVAR_F__REQUEST_TIMEOUT], "task_RetryInitQuery", TASKID__RETRY_INIT_QUERY)
628
629 new iCreateTable = get_pcvar_num(g_pCvar[PCVAR__CREATE_TABLE])
630 new iPruneDays = get_pcvar_num(g_pCvar[PCVAR__PRUNE_OLD_IPS])
631 new iLen
632
633 if(equal(g_szSqlMode, "sqlite")) {
634 if(iCreateTable) {
635 iLen = formatex( g_szQuery, chx(g_szQuery),
636 "CREATE TABLE IF NOT EXISTS `%s` (\
637 `%s` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \
638 `%s` INTEGER NOT NULL UNIQUE, \
639 `%s` INTEGER NOT NULL, \
640 `%s` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\
641 );",
642
643 g_szTableName,
644
645 SQL_TABLE_COLUMNS[SQL_COLUMN__ID],
646 SQL_TABLE_COLUMNS[SQL_COLUMN__IP],
647 SQL_TABLE_COLUMNS[SQL_COLUMN__IP_STATUS],
648 SQL_TABLE_COLUMNS[SQL_COLUMN__TIMESTAMP]
649 );
650 }
651
652 if(iPruneDays > 0) {
653 formatex( g_szQuery[iLen], chx(g_szQuery) - iLen,
654 " DELETE FROM `%s` WHERE `%s` <= DATETIME('now','-%i day');",
655
656 g_szTableName, SQL_TABLE_COLUMNS[SQL_COLUMN__TIMESTAMP], iPruneDays
657 );
658 }
659 }
660 else if(equal(g_szSqlMode, "mysql")) {
661 g_bMySqlMode = true
662
663 if(iCreateTable) {
664 iLen = formatex( g_szQuery, chx(g_szQuery),
665 "CREATE TABLE IF NOT EXISTS `%s` (\
666 `%s` int(10) unsigned NOT NULL AUTO_INCREMENT, \
667 `%s` int(11) NOT NULL, \
668 `%s` tinyint(1) unsigned NOT NULL, \
669 `%s` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, \
670 PRIMARY KEY (`%s`), \
671 UNIQUE KEY `%s` (`%s`)\
672 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;",
673
674 g_szTableName,
675
676 SQL_TABLE_COLUMNS[SQL_COLUMN__ID],
677 SQL_TABLE_COLUMNS[SQL_COLUMN__IP],
678 SQL_TABLE_COLUMNS[SQL_COLUMN__IP_STATUS],
679 SQL_TABLE_COLUMNS[SQL_COLUMN__TIMESTAMP],
680
681 SQL_TABLE_COLUMNS[SQL_COLUMN__ID],
682 SQL_TABLE_COLUMNS[SQL_COLUMN__IP], SQL_TABLE_COLUMNS[SQL_COLUMN__IP]
683 );
684 }
685
686 if(iPruneDays > 0) {
687 formatex( g_szQuery[iLen], chx(g_szQuery) - iLen,
688 " DELETE FROM `%s` WHERE `%s` <= DATE_SUB(NOW(),INTERVAL %i DAY);",
689
690 g_szTableName, SQL_TABLE_COLUMNS[SQL_COLUMN__TIMESTAMP], iPruneDays
691 );
692 }
693 }
694 else {
695 plugin_end()
696 set_fail_state("Invalid sql driver '%s' (valid are 'sqlite' or 'mysql')", g_szSqlMode)
697 }
698
699 func_MakeQuery(QUERY__INIT_SYSTEM)
700}
701
702/* -------------------- */
703
704public task_RetryInitQuery() {
705 static iCounter
706 log_to_file(g_eLogFile[LOG__ERROR], "Init timeout #%i, retrying...", ++iCounter)
707 func_MakeInitQuery()
708}
709
710/* -------------------- */
711
712public SQL_Handler(iFailState, Handle:hQueryHandle, szError[], iErrorCode, eSqlData[], iDataSize, Float:fQueryTime) {
713 // In order to avoid errors like ->
714 // Run time error 10: native error (native "TrieGetString")
715 // Invalid map handle provided (0)
716 // This was found during testing. Apparently, the sql callback can be called after plugin_end()
717 if(g_bPluginEnded) {
718 return PLUGIN_HANDLED
719 }
720
721 if(iFailState != TQUERY_SUCCESS) {
722 if(iFailState == TQUERY_CONNECT_FAILED) {
723 log_to_file(g_eLogFile[LOG__ERROR], "[SQL] Can't connect to server [%.2f]", fQueryTime)
724 log_to_file(g_eLogFile[LOG__ERROR], "[SQL] Error #%i, %s", iErrorCode, szError)
725 }
726 else /*if(iFailState == TQUERY_QUERY_FAILED)*/ {
727 SQL_GetQueryString(hQueryHandle, g_szQuery, chx(g_szQuery))
728 log_to_file(g_eLogFile[LOG__ERROR], "[SQL] Query error!")
729 log_to_file(g_eLogFile[LOG__ERROR], "[SQL] Error #%i, %s", iErrorCode, szError)
730 log_to_file(g_eLogFile[LOG__ERROR], "[SQL] Query: %s", g_szQuery)
731 }
732
733 if(g_eCvar[CVAR__KICK_IF_CANT_CHECK] && eSqlData[SQL_DATA__QUERY_TYPE] == QUERY__CHECK_IP) {
734 new pPlayer = eSqlData[SQL_DATA__PLAYER_INDEX]
735
736 new szIP[MAX_IP_LENGTH]
737 TrieGetString(g_tIpMatch, fmt("%i", eSqlData[SQL_DATA__PLAYER_USERID]), szIP, chx(szIP))
738 TrieDeleteKey(g_tIpMatch, fmt("%i", eSqlData[SQL_DATA__PLAYER_USERID]))
739
740 if(!is_user_connected(pPlayer) || eSqlData[SQL_DATA__PLAYER_USERID] != get_user_userid(pPlayer)) {
741 return PLUGIN_HANDLED
742 }
743
744 new szAuthID[MAX_AUTHID_LENGTH]
745 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
746 func_KickPlayer(pPlayer, KICK_TYPE__PROXY_CHECK_FAIL, szIP, szAuthID)
747 }
748
749 return PLUGIN_HANDLED
750 }
751
752 if(fQueryTime > g_eCvar[CVAR_F__LONG_QUERY_TIME]) {
753 SQL_GetQueryString(hQueryHandle, g_szQuery, chx(g_szQuery))
754 log_to_file(g_eLogFile[LOG__LONG_QUERY], "[%.2f] %s", fQueryTime, g_szQuery)
755 }
756
757 /* --------- */
758
759 switch(eSqlData[SQL_DATA__QUERY_TYPE]) {
760 case QUERY__INIT_SYSTEM: {
761 remove_task(TASKID__RETRY_INIT_QUERY)
762
763 /*if(g_bSystemAvailable) {
764 return PLUGIN_HANDLED
765 }*/
766
767 g_bSystemAvailable = true
768 }
769
770 /* --- */
771
772 case QUERY__CHECK_IP: {
773 new pPlayer = eSqlData[SQL_DATA__PLAYER_INDEX]
774
775 if(!is_user_connected(pPlayer) || eSqlData[SQL_DATA__PLAYER_USERID] != get_user_userid(pPlayer)) {
776 pPlayer = 0
777 }
778
779 new szAuthID[MAX_AUTHID_LENGTH]
780
781 if(pPlayer) {
782 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
783 }
784
785 new szIP[MAX_IP_LENGTH]
786 TrieGetString(g_tIpMatch, fmt("%i", eSqlData[SQL_DATA__PLAYER_USERID]), szIP, chx(szIP))
787 TrieDeleteKey(g_tIpMatch, fmt("%i", eSqlData[SQL_DATA__PLAYER_USERID]))
788
789 if(!SQL_NumResults(hQueryHandle)) {
790 func_CheckForProxy(pPlayer, szIP, szAuthID)
791 return PLUGIN_HANDLED
792 }
793
794 new iIpStatus = SQL_ReadResult(hQueryHandle, 0)
795 TrieSetCell(g_tIpProxyStatus, szIP, iIpStatus)
796
797 if(pPlayer) {
798 func_AgregateIpStatus(pPlayer, iIpStatus, AGREGATE_IP__SQL, szIP, szAuthID)
799 }
800 }
801
802 /* --- */
803
804 case QUERY__FLUSH_IPS: {
805 TrieClear(g_tIpProxyStatus)
806 }
807
808 /* --- */
809
810 // Next case goes here
811 }
812
813 return PLUGIN_HANDLED
814}
815
816/* -------------------- */
817
818public client_putinserver(pPlayer) {
819 g_szAccess[pPlayer] = _NA_
820 g_szAsNumber[pPlayer] = _NA_
821 g_szDesc[pPlayer] = _NA_
822
823 if(!g_eCvar[CVAR__PLUGIN_ENABLED]) {
824 g_szCode[pPlayer] = _NA_
825 g_szCountry[pPlayer] = _NA_
826 return
827 }
828
829 if(is_user_bot(pPlayer) || is_user_hltv(pPlayer)) {
830 g_szAccess[pPlayer] = "Bot/HLTV"
831 g_szCode[pPlayer] = _NA_
832 g_szCountry[pPlayer] = _NA_
833 return
834 }
835
836 new szIP[MAX_IP_LENGTH]
837 get_user_ip(pPlayer, szIP, chx(szIP), .without_port = 1)
838
839 func_GetCountryCode(pPlayer, szIP, g_szCode[pPlayer], chx(g_szCode[]))
840 func_GetCountryName(pPlayer, szIP, g_szCountry[pPlayer], chx(g_szCountry[]))
841
842 if(g_eCvar[CVAR__ALLOW_STEAM] && is_user_steam(pPlayer)) {
843 g_szAccess[pPlayer] = "Steam"
844
845 new szAuthID[MAX_AUTHID_LENGTH]
846 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
847
848 log_to_file( g_eLogFile[LOG__ALLOW], "[Legit Steam] %n | %s | %s | %s | %s",
849 pPlayer, szIP, szAuthID, g_szCode[pPlayer], g_szCountry[pPlayer] );
850
851 return
852 }
853
854 set_task(Float:g_eCvar[CVAR__CHECK_DELAY], "task_CheckPlayer_Step1", pPlayer)
855}
856
857/* -------------------- */
858
859public client_disconnected(pPlayer) {
860 remove_task(pPlayer) // task_CheckPlayer_Step1()
861}
862
863/* -------------------- */
864
865public task_CheckPlayer_Step1(pPlayer) {
866 if(!is_user_connected(pPlayer) || is_user_bot(pPlayer) || is_user_hltv(pPlayer)) {
867 return
868 }
869
870 if(!g_bSystemAvailable) {
871 set_task(2.0, "task_CheckPlayer_Step1", pPlayer)
872 return
873 }
874
875 new szIP[MAX_IP_LENGTH], szAuthID[MAX_AUTHID_LENGTH]
876 get_user_ip(pPlayer, szIP, chx(szIP), .without_port = 1)
877 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
878
879 new bool:bImmunity, iFlags = get_user_flags(pPlayer)
880
881 if(g_iImmunityFlags) {
882 if(iFlags & g_iImmunityFlags) {
883 bImmunity = true
884 }
885 }
886 else if(g_iDefAccFlags && !(iFlags & g_iDefAccFlags)) {
887 bImmunity = true
888 }
889
890 if(bImmunity) {
891 g_szAccess[pPlayer] = "Access"
892
893 new szFlags[32]; get_flags(iFlags, szFlags, chx(szFlags))
894
895 log_to_file( g_eLogFile[LOG__ALLOW], "[Access Flags] %n | %s | %s | %s | %s | %s",
896 pPlayer, szIP, szAuthID, szFlags, g_szCode[pPlayer], g_szCountry[pPlayer] );
897
898 return
899 }
900
901 if(nvault_get(g_hImmunity, szAuthID)) {
902 g_szAccess[pPlayer] = "Immunity"
903
904 log_to_file( g_eLogFile[LOG__ALLOW], "[SteamID Immunity] %n | %s | %s | %s | %s",
905 pPlayer, szIP, szAuthID, g_szCode[pPlayer], g_szCountry[pPlayer] );
906
907 return
908 }
909
910 new iIP = func_ParseIP(szIP)
911
912 if(IsIpInList(iIP, LIST_TYPE__WHITELIST)) {
913 g_szAccess[pPlayer] = "IP Whitelist"
914 new szStartIP[MAX_IP_LENGTH], szEndIP[MAX_IP_LENGTH]
915
916 func_ReverseIP(g_eRangeData[RDS__START_IP], szStartIP, chx(szStartIP))
917 func_ReverseIP(g_eRangeData[RDS__END_IP], szEndIP, chx(szEndIP))
918
919 log_to_file( g_eLogFile[LOG__ALLOW], "[IP Whitelist] %n | %s | %s | %s - %s | %s | %s | %s",
920 pPlayer, szIP, szAuthID, szStartIP, szEndIP, g_eRangeData[RDS__COMMENT], g_szCode[pPlayer], g_szCountry[pPlayer] );
921
922 return
923 }
924
925 func_CheckAsNumber(pPlayer, szIP, szAuthID)
926}
927
928/* -------------------- */
929
930stock func_CheckAsNumber(pPlayer, szIP[], szAuthID[]) {
931 g_eAsTrieData[AST__STATE] = STATE__NO
932 TrieGetArray(g_tAsCache, szIP, g_eAsTrieData, sizeof(g_eAsTrieData))
933
934 if(g_eAsTrieData[AST__STATE] == STATE__NO) {
935 new eExtData[WHOIS_DATA_STRUCT]
936
937 eExtData[WHOIS_DATA__QUERY_TYPE] = WHOIS_QUERY_TYPE__AUTO
938 eExtData[WHOIS_DATA__QUERY_TRIES] = g_eCvar[CVAR__MAX_WHOIS_QUERY_TRIES]
939 eExtData[WHOIS_DATA__PLAYER_USERID] = get_user_userid(pPlayer)
940
941 WhoisIpQuery(szIP, "OnWhoisIpQueryComplete", eExtData, sizeof(eExtData))
942 return
943 }
944
945 TrieGetArray(g_tIpInfo, szIP, g_eIpInfoData, sizeof(g_eIpInfoData))
946 copy(g_szAsNumber[pPlayer], MAX_AS_LEN - 1, g_eIpInfoData[IP_INFO__AS])
947
948 if(g_eIpInfoData[IP_INFO__DESC][0]) {
949 copy(g_szDesc[pPlayer], MAX_DESC_LEN - 1, g_eIpInfoData[IP_INFO__DESC])
950 }
951
952 switch(g_eAsTrieData[AST__STATE]) {
953 case STATE__NORMAL, STATE__WHITELIST: {
954 func_CheckPlayer_Step2(pPlayer, szIP, szAuthID)
955 }
956 case STATE__BAN: {
957 func_KickPlayer(pPlayer, KICK_TYPE__AS, szIP, szAuthID)
958 }
959 }
960}
961
962/* -------------------- */
963
964public OnWhoisIpQueryComplete(WhoisQueryResult:iResult, szIP[], szStartIP[], szEndIP[], szAsNumber[], szDesc[], eExtData[], iExtDataSize/*, szRawAnswer[]*/) {
965 // By analogy with SQL_Handler()
966 if(g_bPluginEnded) {
967 return
968 }
969
970 switch(eExtData[WHOIS_DATA__QUERY_TYPE]) {
971 case WHOIS_QUERY_TYPE__AUTO: {
972 new pPlayer = func_FindPlayer(eExtData[WHOIS_DATA__PLAYER_USERID], szIP)
973 new szAuthID[MAX_AUTHID_LENGTH]
974
975 if(pPlayer) {
976 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
977 }
978
979 g_eAsTrieData[AST__STATE] = STATE__NORMAL
980
981 /* If 'szAsNumber' not present in 'g_tAsNumbers' (AS not banned), or it marked as "white", then in
982 'g_tAsCache' will recorded 'g_eAsTrieData[AST__STATE]' with value 'STATE__NORMAL' or 'STATE__WHITELIST',
983 which means value of 'g_eAsTrieData[AST__ARRAY_POS]' does not matter (will not be used).
984 For the future: related logic below (STATE__BAN), and in 'func_CheckAsNumber()' */
985 //g_eAsTrieData[AST__ARRAY_POS] = INVALID_HANDLE
986 switch(iResult) {
987 case WHOIS_NO_ERROR: {
988 // 'g_eAsTrieData[AST__STATE]' does not change if 'szAsNumber' not present in 'g_tAsNumbers'
989 TrieGetArray(g_tAsNumbers, szAsNumber, g_eAsTrieData, sizeof(g_eAsTrieData))
990
991 TrieSetArray(g_tAsCache, szIP, g_eAsTrieData, sizeof(g_eAsTrieData))
992
993 trim(szDesc)
994
995 copy(g_eIpInfoData[IP_INFO__AS], MAX_AS_LEN - 1, szAsNumber)
996 copy(g_eIpInfoData[IP_INFO__DESC], MAX_DESC_LEN - 1, szDesc)
997 TrieSetArray(g_tIpInfo, szIP, g_eIpInfoData, sizeof(g_eIpInfoData))
998
999 if(pPlayer) {
1000 copy(g_szAsNumber[pPlayer], MAX_AS_LEN - 1, szAsNumber)
1001
1002 if(szDesc[0]) {
1003 copy(g_szDesc[pPlayer], MAX_DESC_LEN - 1, szDesc)
1004 }
1005 }
1006
1007 if(g_eAsTrieData[AST__STATE] == STATE__BAN) {
1008 if(pPlayer) {
1009 func_KickPlayer(pPlayer, KICK_TYPE__AS, szIP, szAuthID)
1010 }
1011
1012 return
1013 }
1014 }
1015 default: { // see whois.inc "WhoisQueryResult"
1016 if(--eExtData[WHOIS_DATA__QUERY_TRIES] == 0) {
1017 log_to_file( g_eLogFile[LOG__ERROR], "[Error #%i] Can't get WHOIS info for IP '%s' (%s, %s)",
1018 iResult, szIP, pPlayer ? g_szCode[pPlayer] : _NA_, pPlayer ? g_szCountry[pPlayer] : _NA_ );
1019
1020 if(g_eCvar[CVAR__KICK_IF_CANT_CHECK] && pPlayer) {
1021 func_KickPlayer(pPlayer, KICK_TYPE__AS_CHECK_FAIL, szIP, szAuthID)
1022 return
1023 }
1024 }
1025 else {
1026 WhoisIpQuery(szIP, "OnWhoisIpQueryComplete", eExtData, iExtDataSize)
1027 return
1028 }
1029 }
1030 }
1031
1032 if(pPlayer) {
1033 func_CheckPlayer_Step2(pPlayer, szIP, szAuthID)
1034 }
1035 }
1036
1037 /* --- */
1038
1039 case WHOIS_QUERY_TYPE__MANUAL: {
1040 copy(g_szGetWhoisIP, chx(g_szGetWhoisIP), szIP)
1041
1042 switch(iResult) {
1043 case WHOIS_NO_ERROR: {
1044 trim(szDesc)
1045 copy(g_eIpInfoData[IP_INFO__AS], MAX_AS_LEN - 1, szAsNumber)
1046 copy(g_eIpInfoData[IP_INFO__DESC], MAX_DESC_LEN - 1, szDesc)
1047 TrieSetArray(g_tIpInfo, szIP, g_eIpInfoData, sizeof(g_eIpInfoData))
1048 g_iManualWhoisState = 1
1049 }
1050 default: { // see whois.inc "WhoisQueryResult"
1051 g_iManualWhoisState = INVALID_HANDLE
1052 }
1053 }
1054 }
1055 }
1056}
1057
1058/* -------------------- */
1059
1060stock func_CheckPlayer_Step2(pPlayer, szIP[], szAuthID[]) {
1061 new iIP = func_ParseIP(szIP)
1062
1063 if(IsIpInList(iIP, LIST_TYPE__BLACKLIST)) {
1064 func_KickPlayer(pPlayer, KICK_TYPE__IP_BAN, szIP, szAuthID)
1065 return
1066 }
1067
1068 if(g_eAsTrieData[AST__STATE] == STATE__WHITELIST) {
1069 g_szAccess[pPlayer] = "AS Whitelist"
1070
1071 ArrayGetArray(Array:g_aAsArray[LIST_TYPE__WHITELIST], g_eAsTrieData[AST__ARRAY_POS], g_eAsData)
1072
1073 if(!g_eAsData[ASD__COMMENT][0]) {
1074 g_eAsData[ASD__COMMENT] = _NA_
1075 }
1076
1077 log_to_file( g_eLogFile[LOG__ALLOW], "[AS Whitelist] %n | %s | %s | %s | %s | %s | %s | %s",
1078 pPlayer, szIP, szAuthID, g_szAsNumber[pPlayer], g_szDesc[pPlayer],
1079 g_eAsData[ASD__COMMENT], g_szCode[pPlayer], g_szCountry[pPlayer] );
1080
1081 return
1082 }
1083
1084 if(equal(g_szCode[pPlayer], _NA_)) {
1085 func_KickPlayer(pPlayer, KICK_TYPE__COUNTRY, szIP, szAuthID)
1086 return
1087 }
1088
1089 for(new i; i < g_iCodesCount; i++) {
1090 if(!equal(g_szCode[pPlayer], g_szAllowedCodes[i])) {
1091 continue
1092 }
1093
1094 new iIpStatus
1095
1096 if(TrieGetCell(g_tIpProxyStatus, szIP, iIpStatus)) {
1097 func_AgregateIpStatus(pPlayer, iIpStatus, AGREGATE_IP__CACHE, szIP, szAuthID)
1098 return
1099 }
1100
1101 formatex( g_szQuery, chx(g_szQuery), "SELECT `%s` FROM `%s` WHERE `%s` = %i LIMIT 1",
1102 SQL_TABLE_COLUMNS[SQL_COLUMN__IP_STATUS], g_szTableName,
1103 SQL_TABLE_COLUMNS[SQL_COLUMN__IP], iIP
1104 );
1105
1106 new eSqlData[SQL_DATA_STRUCT]
1107 eSqlData[SQL_DATA__PLAYER_INDEX] = pPlayer
1108 eSqlData[SQL_DATA__PLAYER_USERID] = get_user_userid(pPlayer)
1109 TrieSetString(g_tIpMatch, fmt("%i", eSqlData[SQL_DATA__PLAYER_USERID]), szIP)
1110
1111 func_MakeQuery(QUERY__CHECK_IP, eSqlData)
1112 return
1113 }
1114
1115 func_KickPlayer(pPlayer, KICK_TYPE__COUNTRY, szIP, szAuthID)
1116}
1117
1118/* -------------------- */
1119
1120stock func_AgregateIpStatus(pPlayer, iIpStatus, iAgregateType, szIP[], szAuthID[]) {
1121 if(iIpStatus == STATE__NORMAL || (!g_eCvar[CVAR__BAN_SUSPICIOUS] && iIpStatus == STATE__SUSPICIOUS)) {
1122 static const AGREGATE_TYPE[AGREGATE_TYPE_ENUM][] = { "Cache:", "SQL:" }
1123 copy(g_szAccess[pPlayer], chx(g_szAccess[]), AGREGATE_TYPE[iAgregateType])
1124 add(g_szAccess[pPlayer], chx(g_szAccess[]), (iIpStatus == STATE__NORMAL) ? " Normal" : " Susp.")
1125 func_LogAllow(pPlayer, szIP, szAuthID)
1126
1127 return
1128 }
1129
1130 // if(iIpStatus == STATE__BAN || iIpStatus == STATE__SUSPICIOUS) ->
1131
1132 static const KICK_TYPE_ENUM:KICK_TYPE[AGREGATE_TYPE_ENUM][2] = {
1133 { KICK_TYPE__PROXY_CACHE, KICK_TYPE__SUSPICIOUS_CACHE },
1134 { KICK_TYPE__PROXY_SQL, KICK_TYPE__SUSPICIOUS_SQL }
1135 }
1136
1137 func_KickPlayer( pPlayer, KICK_TYPE[iAgregateType][ (iIpStatus == STATE__BAN) ? 0 : 1 ], szIP, szAuthID );
1138}
1139
1140/* -------------------- */
1141
1142stock func_CheckForProxy(pPlayer, szIP[], szAuthID[]) {
1143 new iUserID = (pPlayer > 0) ? get_user_userid(pPlayer) : random_num(1000, 2000)
1144
1145 static szFile[PLATFORM_MAX_PATH]
1146
1147 formatex(szFile, chx(szFile), "%s/%s_%i", g_szDataDir, szIP, iUserID)
1148
1149 /* The file may exist if the request is sent, the file was created for
1150 writing, and the map was changed before the end of the check */
1151 if(file_exists(szFile)) {
1152 log_to_file(g_eLogFile[LOG__ERROR], "[Error] File '%s' already exists!", szFile)
1153 delete_file(szFile)
1154
1155 if(g_eCvar[CVAR__KICK_IF_CANT_CHECK] && pPlayer > 0) { // pPlayer can be -1, 0, 1-MaxClients
1156 func_KickPlayer(pPlayer, KICK_TYPE__PROXY_CHECK_FAIL, szIP, szAuthID)
1157 }
1158
1159 return
1160 }
1161
1162 new eData[CURL_DATA_STRUCT]
1163 eData[CURL_DATA__FILE_HANDLE] = fopen(szFile, "wb")
1164
1165 if(!eData[CURL_DATA__FILE_HANDLE]) {
1166 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open '%s' for writing!", szFile)
1167 return
1168 }
1169
1170 eData[CURL_DATA__PLAYER_INDEX] = pPlayer
1171 eData[CURL_DATA__PLAYER_USERID] = iUserID
1172
1173 new CURL:hCurl = curl_easy_init()
1174
1175 curl_easy_setopt(hCurl, CURLOPT_BUFFERSIZE, CURL_BUFFER_SIZE)
1176
1177 new szRequest[96]
1178 formatex(szRequest, chx(szRequest), "http://v2.api.iphub.info/ip/%s", szIP)
1179 curl_easy_setopt(hCurl, CURLOPT_URL, szRequest)
1180
1181 new curl_slist:hList
1182 hList = curl_slist_append(hList, g_szApiKey[g_iCurrentKey])
1183 curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, hList)
1184
1185 curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, eData[CURL_DATA__FILE_HANDLE])
1186 curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, "OnWrite")
1187
1188 eData[CURL_DATA__SLIST_HANDLE] = hList
1189 TrieSetString(g_tCurl, fmt("%i", hCurl), szIP)
1190 curl_easy_perform(hCurl, "OnDownloadComplete", eData, sizeof(eData))
1191}
1192
1193/* -------------------- */
1194
1195public OnWrite(szData[], iSize, nmemb, hFile) {
1196 new iActualSize = iSize * nmemb
1197
1198 fwrite_blocks(hFile, szData, iActualSize, BLOCK_CHAR)
1199
1200 return iActualSize
1201}
1202
1203/* -------------------- */
1204
1205public OnDownloadComplete(CURL:hCurl, CURLcode:iCode, eData[]/*, iDataSize*/) { // iDataSize always = 0 (nothing passed?)
1206 /* "Also, if there are active transfers at the time of the map change, the module will try
1207 generate a transmission error to complete the stream as soon as possible, and call the
1208 curl_easy_perform callback (called after plugin_end())"
1209 http://amx-x.ru/viewtopic.php?f=21&t=31672 */
1210 if(g_bPluginEnded) {
1211 return PLUGIN_HANDLED
1212 }
1213
1214 static szBuffer[256]
1215
1216 new szIP[MAX_IP_LENGTH]
1217
1218 TrieGetString(g_tCurl, fmt("%i", hCurl), szIP, chx(szIP))
1219 TrieDeleteKey(g_tCurl, fmt("%i", hCurl))
1220
1221 new iUserID = eData[CURL_DATA__PLAYER_USERID]
1222
1223 static szFile[PLATFORM_MAX_PATH]
1224
1225 formatex(szFile, chx(szFile), "%s/%s_%i", g_szDataDir, szIP, iUserID)
1226
1227 fclose(eData[CURL_DATA__FILE_HANDLE])
1228 curl_slist_free_all(curl_slist:eData[CURL_DATA__SLIST_HANDLE])
1229
1230 curl_easy_cleanup(hCurl)
1231
1232 new pPlayerIndex = eData[CURL_DATA__PLAYER_INDEX]
1233
1234 if(iCode != CURLE_OK) {
1235 curl_easy_strerror(iCode, szBuffer, chx(szBuffer))
1236 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Curl error code #%i, IP '%s'", iCode, szIP)
1237 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Curl error: %s", szBuffer)
1238 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1239 }
1240
1241 /* --- */
1242
1243 if(!file_exists(szFile)) {
1244 log_to_file(g_eLogFile[LOG__ERROR], "[Error] File '%s' not found!", szFile)
1245 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1246 }
1247
1248 new JSON:hJson = json_parse(szFile, true, true)
1249
1250 if(hJson == Invalid_JSON) {
1251 new bool:bError, iFile = fopen(szFile, "r")
1252
1253 if(!iFile) {
1254 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open wrong file '%s' for analysis", szFile)
1255 }
1256 else {
1257 while(!feof(iFile)) {
1258 fgets(iFile, szBuffer, chx(szBuffer))
1259
1260 /* <html>
1261 <head><title>502 Bad Gateway</title></head>
1262 <body>
1263 <center><h1>502 Bad Gateway</h1></center>
1264 <hr><center>nginx/1.15.8</center>
1265 </body>
1266 </html> */
1267 if(contain(szBuffer, "502") != -1) {
1268 bError = true
1269 break
1270 }
1271 }
1272
1273 fclose(iFile)
1274
1275 if(bError) {
1276 log_to_file(g_eLogFile[LOG__ERROR], "[Error] HTTP responce code 502 (Bad Gateway), file '%s'", szFile)
1277 }
1278 else {
1279 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Something wrong with file '%s'", szFile)
1280 }
1281 }
1282
1283 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1284 }
1285
1286 new iCount = json_object_get_count(hJson)
1287
1288 if(iCount < EXPECTED_JSON_KEY_COUNT) {
1289
1290 json_free(hJson)
1291
1292 new iError, iFile = fopen(szFile, "r")
1293
1294 if(!iFile) {
1295 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open wrong file '%s' for analysis", szFile)
1296 }
1297 else {
1298 while(!feof(iFile)) {
1299 fgets(iFile, szBuffer, chx(szBuffer))
1300
1301 // {"code":"TooManyRequests","message":"You've exceeded your rate limit of 1000 request(s) per 86400 second(s)."}
1302 if(contain(szBuffer, "TooManyRequests") != -1) {
1303 iError = 1
1304 break
1305 }
1306
1307 // {"code":"Forbidden","message":"Invalid API key"}
1308 // {"code":"Forbidden","message":"Missing X-Key header"}
1309 // {"error":"Invalid API key (4)"}
1310 if(contain(szBuffer, "Forbidden") != -1 || contain(szBuffer, "Invalid API key") != -1) {
1311 iError = 2
1312 break
1313 }
1314 }
1315
1316 fclose(iFile)
1317
1318 switch(iError) {
1319 case 0: {
1320 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Wrong key count (%i/%i) in file '%s'", iCount, EXPECTED_JSON_KEY_COUNT, szFile)
1321 }
1322 case 1: {
1323 if(g_iCurrentKey < g_iLoadedKeys - 1) {
1324 log_to_file( g_eLogFile[LOG__ERROR], "[Error] Request limit reached for API key '%i', switching to API key '%i' of '%i'",
1325 g_iCurrentKey + 1, ++g_iCurrentKey, g_iLoadedKeys );
1326 }
1327 else {
1328 g_iCurrentKey = 0
1329 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Request limit reached, and no more available API keys!")
1330 }
1331 }
1332 case 2: {
1333 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Invalid API Key!")
1334 }
1335 }
1336 }
1337
1338 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1339 }
1340
1341 new szKey[32]
1342
1343 json_object_get_name(hJson, JSON_KEY__CHECK_STATE, szKey, chx(szKey))
1344
1345 new JSON:hVal = json_object_get_value(hJson, szKey, false)
1346
1347 if(hVal == Invalid_JSON) {
1348 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Json object 'JSON_KEY__CHECK_STATE' is invalid (IP %s)", szIP)
1349 json_free(hJson)
1350 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1351 }
1352
1353 if(json_get_type(hVal) != JSONNumber) {
1354 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Json object 'JSON_KEY__CHECK_STATE' is not a number (IP %s)", szIP)
1355 json_free(hVal)
1356 json_free(hJson)
1357 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1358 }
1359
1360 new iState = json_get_number(hVal)
1361
1362 json_free(hVal)
1363 json_free(hJson)
1364
1365 /* --- */
1366
1367 new pPlayer = func_FindPlayer(iUserID, szIP)
1368
1369 if(!pPlayer) {
1370 func_GetCountryCode(0, szIP, g_szCode[0], chx(g_szCode[]))
1371 func_GetCountryName(0, szIP, g_szCountry[0], chx(g_szCountry[]))
1372
1373 if(TrieGetArray(g_tIpInfo, szIP, g_eIpInfoData, sizeof(g_eIpInfoData))) {
1374 copy(g_szAsNumber[0], MAX_AS_LEN - 1, g_eIpInfoData[IP_INFO__AS])
1375
1376 if(g_eIpInfoData[IP_INFO__DESC][0]) {
1377 copy(g_szDesc[0], MAX_DESC_LEN - 1, g_eIpInfoData[IP_INFO__DESC])
1378 }
1379 }
1380 else {
1381 g_szAsNumber[0] = _NA_
1382 g_szDesc[0] = _NA_
1383 }
1384 }
1385
1386 if(iState == IP_CHECK_STATE__NORMAL || (!g_eCvar[CVAR__BAN_SUSPICIOUS] && iState == IP_CHECK_STATE__SUSPICIOUS)) {
1387 new iIpState = (iState == IP_CHECK_STATE__NORMAL) ? STATE__NORMAL : STATE__SUSPICIOUS;
1388 new bool:bKeySet = bool:TrieSetCell(g_tIpProxyStatus, szIP, iIpState, .replace = false)
1389
1390 if(pPlayerIndex == INVALID_HANDLE) {
1391 copy(g_szGetProxyIP, chx(g_szGetProxyIP), szIP)
1392 }
1393 else {
1394 if(bKeySet) { // Prevent sql insert error, that may occur if two players with same IP connects ~ at the same time
1395 func_WriteIpToSqlTable(iIpState, szIP)
1396 }
1397
1398 log_to_file( g_eLogFile[LOG__PROXY_CHECK], "[Allow: %s] %s | %s | %s | %s | %s | %s",
1399 iState == IP_CHECK_STATE__NORMAL ? "Normal" : "Susp.", func_GetPlayerName(pPlayer), szIP,
1400 g_szCode[pPlayer], g_szCountry[pPlayer], g_szAsNumber[pPlayer], g_szDesc[pPlayer] );
1401
1402 if(pPlayer) {
1403 if(iState == IP_CHECK_STATE__NORMAL) {
1404 g_szAccess[pPlayer] = "Check: Normal"
1405 }
1406 else {
1407 g_szAccess[pPlayer] = "Check: Susp."
1408 }
1409
1410 new szAuthID[MAX_AUTHID_LENGTH]
1411 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
1412 func_LogAllow(pPlayer, szIP, szAuthID)
1413 }
1414 }
1415
1416 delete_file(szFile)
1417 return PLUGIN_HANDLED
1418 }
1419
1420 if(iState == IP_CHECK_STATE__DETECT || iState == IP_CHECK_STATE__SUSPICIOUS) {
1421 new iIpState = (iState == IP_CHECK_STATE__DETECT) ? STATE__BAN : STATE__SUSPICIOUS;
1422 new bool:bKeySet = bool:TrieSetCell(g_tIpProxyStatus, szIP, iIpState, .replace = false)
1423
1424 if(pPlayerIndex == INVALID_HANDLE) {
1425 copy(g_szGetProxyIP, chx(g_szGetProxyIP), szIP)
1426 }
1427 else {
1428 if(bKeySet) { // Prevent sql insert error, that may occur if two players with same IP connects ~ at the same time
1429 func_WriteIpToSqlTable(STATE__BAN, szIP)
1430 }
1431
1432 log_to_file( g_eLogFile[LOG__PROXY_CHECK], "[%s] %s | %s | %s | %s | %s | %s",
1433 iState == IP_CHECK_STATE__DETECT ? "Proxy/VPN" : "Deny: Susp.", func_GetPlayerName(pPlayer),
1434 szIP, g_szCode[pPlayer], g_szCountry[pPlayer], g_szAsNumber[pPlayer], g_szDesc[pPlayer] );
1435
1436 if(pPlayer) {
1437 new szAuthID[MAX_AUTHID_LENGTH]
1438 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
1439
1440 func_KickPlayer( pPlayer,
1441 (iState == IP_CHECK_STATE__DETECT) ? KICK_TYPE__PROXY_CHECK : KICK_TYPE__SUSPICIOUS_CHECK,
1442 szIP, szAuthID
1443 );
1444 }
1445 }
1446
1447 delete_file(szFile)
1448 return PLUGIN_HANDLED
1449 }
1450
1451 log_to_file( g_eLogFile[LOG__ERROR], "[Error] Wrong check state value '%i', IP '%s' (%s | %s | %s | %s)",
1452 iState, szIP, g_szCode[pPlayer], g_szCountry[pPlayer], g_szAsNumber[pPlayer], g_szDesc[pPlayer] );
1453
1454 return func_CurlError(pPlayerIndex, iUserID, szIP, szFile)
1455}
1456
1457/* -------------------- */
1458
1459stock func_WriteIpToSqlTable(iIpState, szIP[]) {
1460 formatex( g_szQuery, chx(g_szQuery), "INSERT %s IGNORE INTO `%s` (`%s`, `%s`) VALUES (%i, %i)",
1461 g_bMySqlMode ? "" : "OR",
1462
1463 g_szTableName, SQL_TABLE_COLUMNS[SQL_COLUMN__IP], SQL_TABLE_COLUMNS[SQL_COLUMN__IP_STATUS],
1464 func_ParseIP(szIP), iIpState
1465 );
1466
1467 func_MakeQuery(QUERY__INSERT_IP)
1468}
1469
1470/* -------------------- */
1471
1472stock func_CurlError(pPlayerIndex, iUserID, szIP[], szFile[]) {
1473 new szNewFileName[PLATFORM_MAX_PATH]
1474 formatex(szNewFileName, chx(szNewFileName), "%s_error", szFile)
1475 rename_file(szFile, szNewFileName, .relative = 1)
1476
1477 if(!g_eCvar[CVAR__KICK_IF_CANT_CHECK] || pPlayerIndex == INVALID_HANDLE) {
1478 return PLUGIN_HANDLED
1479 }
1480
1481 new pPlayer = func_FindPlayer(iUserID, szIP)
1482
1483 if(pPlayer) {
1484 new szAuthID[MAX_AUTHID_LENGTH]
1485 get_user_authid(pPlayer, szAuthID, chx(szAuthID))
1486 func_KickPlayer(pPlayer, KICK_TYPE__PROXY_CHECK_FAIL, szIP, szAuthID)
1487 }
1488
1489 return PLUGIN_HANDLED
1490}
1491
1492/* -------------------- */
1493
1494stock func_FindPlayer(iUserID, szIP[]) {
1495 new pPlayer = find_player("k", iUserID)
1496
1497 if(!pPlayer) {
1498 pPlayer = find_player("d", szIP)
1499 }
1500
1501 return pPlayer
1502}
1503
1504/* -------------------- */
1505
1506stock func_KickPlayer(pPlayer, KICK_TYPE_ENUM:iKickType, szIP[], szAuthID[]) {
1507 static const KICK_TYPE[KICK_TYPE_ENUM][] = {
1508 "Banned AS",
1509 "IP Blacklist",
1510 "Bad Country",
1511 "Check: Proxy/VPN",
1512 "SQL: Proxy/VPN",
1513 "Cache: Proxy/VPN",
1514 "Check: Suspicious IP",
1515 "SQL: Suspicious IP",
1516 "Cache: Suspicious IP",
1517 "AS Check Fail",
1518 "Proxy Check Fail"
1519 }
1520
1521 new szMsg[320]
1522
1523 new iLen = formatex(szMsg, chx(szMsg), "[%s] %n | %s | %s", KICK_TYPE[iKickType], pPlayer, szIP, szAuthID)
1524
1525 switch(iKickType) {
1526 case KICK_TYPE__AS: {
1527 ArrayGetArray(Array:g_aAsArray[LIST_TYPE__BLACKLIST], g_eAsTrieData[AST__ARRAY_POS], g_eAsData)
1528
1529 if(!g_eAsData[ASD__COMMENT][0]) {
1530 g_eAsData[ASD__COMMENT] = _NA_
1531 }
1532
1533 iLen += formatex(szMsg[iLen], chx(szMsg) - iLen, " | %s | %s | %s", g_szAsNumber[pPlayer], g_szDesc[pPlayer], g_eAsData[ASD__COMMENT])
1534 }
1535 case KICK_TYPE__IP_BAN: {
1536 new szStartIP[MAX_IP_LENGTH], szEndIP[MAX_IP_LENGTH]
1537
1538 func_ReverseIP(g_eRangeData[RDS__START_IP], szStartIP, chx(szStartIP))
1539 func_ReverseIP(g_eRangeData[RDS__END_IP], szEndIP, chx(szEndIP))
1540
1541 if(!g_eRangeData[RDS__COMMENT][0]) {
1542 g_eRangeData[RDS__COMMENT] = _NA_
1543 }
1544
1545 iLen += formatex( szMsg[iLen], chx(szMsg) - iLen, " | %s - %s | %s | %s | %s",
1546 szStartIP, szEndIP, g_eRangeData[RDS__COMMENT], g_szAsNumber[pPlayer], g_szDesc[pPlayer] );
1547 }
1548 case KICK_TYPE__AS_CHECK_FAIL: {
1549 // AS and Desc not obtained, no reason to print "N/A"
1550 }
1551 default: { // KICK_TYPE__COUNTRY, KICK_TYPE__SUSPICIOUS_..., KICK_TYPE__PROXY_..., KICK_TYPE__PROXY_CHECK_FAIL
1552 iLen += formatex(szMsg[iLen], chx(szMsg) - iLen, " | %s | %s", g_szAsNumber[pPlayer], g_szDesc[pPlayer])
1553 }
1554 }
1555
1556 formatex(szMsg[iLen], chx(szMsg) - iLen, " | %s | %s", g_szCode[pPlayer], g_szCountry[pPlayer])
1557
1558 log_to_file(g_eLogFile[LOG__DENY], szMsg)
1559
1560 console_print(pPlayer, "^n%L %n | %s | %s", pPlayer, "BG__YOUR_NAME_STEAMID_AND_IP", pPlayer, szAuthID, szIP)
1561
1562 if(g_eCvar[CVAR__SHOW_URL]) {
1563 console_print(pPlayer, "%L^n", pPlayer, "BG__URL")
1564 }
1565
1566 new iData[1]
1567
1568 switch(iKickType) {
1569 case KICK_TYPE__AS_CHECK_FAIL, KICK_TYPE__PROXY_CHECK_FAIL: {
1570 iData[0] = 1
1571 }
1572 }
1573
1574 set_task(Float:g_eCvar[CVAR__KICK_DELAY], "task_DelayedKick", pPlayer, iData, sizeof(iData))
1575}
1576
1577/* -------------------- */
1578
1579public task_DelayedKick(iData[], pPlayer) {
1580 if(!is_user_connected(pPlayer)) {
1581 return
1582 }
1583
1584 server_cmd("kick #%i ^"%L^"", get_user_userid(pPlayer), pPlayer, iData[0] ? "BG__KICK_REASON_CHECK_FAILED" : "BG__KICK_REASON_ACCESS")
1585}
1586
1587/* -------------------- */
1588
1589stock func_GetPlayerName(pPlayer) {
1590 new szName[MAX_NAME_LENGTH]
1591
1592 if(pPlayer) {
1593 get_user_name(pPlayer, szName, chx(szName))
1594 }
1595 else {
1596 copy(szName, chx(szName), _NA_)
1597 }
1598
1599 return szName
1600}
1601
1602/* -------------------- */
1603
1604public concmd_AllowSteamID(pPlayer, iAccess) {
1605 if(!UserHasAccess(pPlayer, iAccess)) {
1606 return PLUGIN_HANDLED
1607 }
1608
1609 if(read_argc() == 1) {
1610 console_print(pPlayer, "* Usage: bg_allow_steamid <steamid> - Add immunity to all checks by SteamID")
1611 return PLUGIN_HANDLED
1612 }
1613
1614 new szBuffer[64]
1615
1616 read_args(szBuffer, chx(szBuffer))
1617 remove_quotes(szBuffer)
1618 trim(szBuffer)
1619
1620 if(strlen(szBuffer) > MAX_AUTHID_LENGTH || !IsValidSteamID(szBuffer)) {
1621 console_print(pPlayer, "* Wrong SteamID provided, check your input!")
1622 }
1623 else if(nvault_get(g_hImmunity, szBuffer)) {
1624 console_print(pPlayer, "* SteamID '%s' already exists in immunity list!", szBuffer)
1625 }
1626 else {
1627 nvault_set(g_hImmunity, szBuffer, "1")
1628 console_print(pPlayer, "* SteamID '%s' added to immunity list", szBuffer)
1629 log_to_file(g_eLogFile[LOG__CMD], "Steamid '%s' added to immunity list by %N", szBuffer, pPlayer)
1630 }
1631
1632 return PLUGIN_HANDLED
1633}
1634
1635/* -------------------- */
1636
1637public concmd_AddToAsList(pPlayer, iAccess) {
1638 if(!UserHasAccess(pPlayer, iAccess)) {
1639 return PLUGIN_HANDLED
1640 }
1641
1642 new szBuffer[64], iType
1643
1644 read_argv(0, szBuffer, chx(szBuffer))
1645
1646 if(equali(szBuffer, "bg_as_blacklist_add")) {
1647 iType = LIST_TYPE__BLACKLIST
1648 }
1649 else {
1650 iType = LIST_TYPE__WHITELIST
1651 }
1652
1653 new iArgCount = read_argc()
1654
1655 if(iArgCount == 1) {
1656 console_print( pPlayer, "* Usage: %s <as_number> <^"comment^">[optional]",
1657 iType == LIST_TYPE__BLACKLIST ? "bg_as_blacklist_add" : "bg_as_whitelist_add" );
1658
1659 return PLUGIN_HANDLED
1660 }
1661
1662 read_argv(1, szBuffer, chx(szBuffer))
1663
1664 if(!IsValidAsNumber(szBuffer)) {
1665 console_print(pPlayer, "* Wrong AS number, check your input!")
1666 return PLUGIN_HANDLED
1667 }
1668
1669 g_eAsTrieData[AST__STATE] = STATE__NO
1670 TrieGetArray(g_tAsNumbers, szBuffer, g_eAsTrieData, sizeof(g_eAsTrieData))
1671
1672 if(g_eAsTrieData[AST__STATE] != STATE__NO) {
1673 console_print( pPlayer, "* AS number '%s' already in %s!", szBuffer,
1674 LIST_NAME[g_eAsTrieData[AST__STATE] == STATE__BAN ? LIST_TYPE__BLACKLIST : LIST_TYPE__WHITELIST] );
1675
1676 return PLUGIN_HANDLED
1677 }
1678
1679 copy(g_eAsData[ASD__NUMBER], MAX_AS_LEN - 1, szBuffer)
1680
1681 if(iArgCount > 2) {
1682 read_argv(2, szBuffer, chx(szBuffer))
1683 trim(szBuffer)
1684
1685 if(strlen(szBuffer) > MAX_COMMENT_LEN) {
1686 console_print(pPlayer, "* Comment is too long, max %i chars!", MAX_COMMENT_LEN)
1687 return PLUGIN_HANDLED
1688 }
1689 }
1690 else {
1691 szBuffer[0] = EOS
1692 }
1693
1694 copy(g_eAsData[ASD__COMMENT], MAX_COMMENT_LEN - 1, szBuffer)
1695
1696 new iFile = fopen(g_szAsListFile, "a")
1697
1698 if(!iFile) {
1699 console_print(pPlayer, "* Error, can't open AS list file!")
1700 return PLUGIN_HANDLED
1701 }
1702
1703 fprintf(iFile, "^n%s %s", LIST_NAME[iType], g_eAsData[ASD__NUMBER])
1704
1705 if(szBuffer[0]) {
1706 fprintf(iFile, " ^"%s^"", szBuffer)
1707 }
1708
1709 fclose(iFile)
1710
1711 TrieClear(g_tAsCache)
1712
1713 g_eAsTrieData[AST__STATE] = (iType == LIST_TYPE__BLACKLIST) ? STATE__BAN : STATE__WHITELIST;
1714 g_eAsTrieData[AST__ARRAY_POS] = g_iAsCount[iType]
1715 TrieSetArray(g_tAsNumbers, g_eAsData[ASD__NUMBER], g_eAsTrieData, sizeof(g_eAsTrieData))
1716
1717 ArrayPushArray(g_aAsArray[iType], g_eAsData)
1718 g_iAsCount[iType]++
1719
1720 console_print(pPlayer, "* AS number '%s' successfully added to %s!", g_eAsData[ASD__NUMBER], LIST_NAME[iType])
1721
1722 log_to_file(g_eLogFile[LOG__CMD], "AS number '%s' added to %s by %N", g_eAsData[ASD__NUMBER], LIST_NAME[iType], pPlayer)
1723
1724 return PLUGIN_HANDLED
1725}
1726
1727/* -------------------- */
1728
1729public concmd_DelFromAsList(pPlayer, iAccess) {
1730 if(!UserHasAccess(pPlayer, iAccess)) {
1731 return PLUGIN_HANDLED
1732 }
1733
1734 new szBuffer[64], iType
1735
1736 read_argv(0, szBuffer, chx(szBuffer))
1737
1738 if(equali(szBuffer, "bg_as_blacklist_del")) {
1739 iType = LIST_TYPE__BLACKLIST
1740 }
1741 else {
1742 iType = LIST_TYPE__WHITELIST
1743 }
1744
1745 if(read_argc() == 1) {
1746 console_print( pPlayer, "* Usage: %s <as_number>",
1747 iType == LIST_TYPE__BLACKLIST ? "bg_as_blacklist_del" : "bg_as_whitelist_del" );
1748
1749 return PLUGIN_HANDLED
1750 }
1751
1752 read_argv(1, szBuffer, chx(szBuffer))
1753
1754 if(!IsValidAsNumber(szBuffer)) {
1755 console_print(pPlayer, "* Wrong AS number, check your input!")
1756 return PLUGIN_HANDLED
1757 }
1758
1759 g_eAsTrieData[AST__STATE] = STATE__NO
1760 TrieGetArray(g_tAsNumbers, szBuffer, g_eAsTrieData, sizeof(g_eAsTrieData))
1761
1762 if(g_eAsTrieData[AST__STATE] == STATE__NO) {
1763 console_print(pPlayer, "* AS number '%s' NOT found in %s!", szBuffer, LIST_NAME[iType])
1764 return PLUGIN_HANDLED
1765 }
1766
1767 TrieClear(g_tAsCache)
1768 TrieClear(g_tAsNumbers)
1769 g_iAsCount[LIST_TYPE__BLACKLIST] = 0
1770 g_iAsCount[LIST_TYPE__WHITELIST] = 0
1771 ArrayClear(Array:g_aAsArray[LIST_TYPE__BLACKLIST])
1772 ArrayClear(Array:g_aAsArray[LIST_TYPE__WHITELIST])
1773
1774 new szOldFileName[PLATFORM_MAX_PATH]
1775 formatex(szOldFileName, chx(szOldFileName), "%s_old", g_szAsListFile)
1776
1777 if(!rename_file(g_szAsListFile, szOldFileName, .relative = 1)) {
1778 console_print(pPlayer, "* Error, can't rename AS list file!")
1779 return PLUGIN_HANDLED
1780 }
1781
1782 new iOldFile = fopen(szOldFileName, "r")
1783
1784 if(!iOldFile) {
1785 console_print(pPlayer, "* Error, can't open renamed AS list file!")
1786 return PLUGIN_HANDLED
1787 }
1788
1789 new iNewFile = fopen(g_szAsListFile, "w")
1790
1791 if(!iNewFile) {
1792 fclose(iOldFile)
1793 console_print(pPlayer, "* Error, can't open new AS list file!")
1794 return PLUGIN_HANDLED
1795 }
1796
1797 new szString[128], bool:bFound, iCfgType
1798
1799 func_AddDefSting_AS(iNewFile)
1800
1801 while(!feof(iOldFile)) {
1802 fgets(iOldFile, szString, chx(szString))
1803 trim(szString)
1804
1805 switch(szString[0]) {
1806 case 'b': iCfgType = LIST_TYPE__BLACKLIST
1807 case 'w': iCfgType = LIST_TYPE__WHITELIST
1808 default: continue // ';', '/', EOS, etc.
1809 }
1810
1811 g_eAsData[ASD__COMMENT][0] = EOS
1812
1813 parse(szString, "", "", g_eAsData[ASD__NUMBER], MAX_AS_LEN - 1, g_eAsData[ASD__COMMENT], MAX_COMMENT_LEN - 1)
1814
1815 if(equal(g_eAsData[ASD__NUMBER], szBuffer)) {
1816 bFound = true
1817 continue
1818 }
1819
1820 fprintf(iNewFile, "^n%s %s", LIST_NAME[iCfgType], g_eAsData[ASD__NUMBER])
1821
1822 if(g_eAsData[ASD__COMMENT][0]) {
1823 fprintf(iNewFile, " ^"%s^"", g_eAsData[ASD__COMMENT])
1824 }
1825
1826 g_eAsTrieData[AST__STATE] = (iCfgType == LIST_TYPE__BLACKLIST) ? STATE__BAN : STATE__WHITELIST;
1827 g_eAsTrieData[AST__ARRAY_POS] = g_iAsCount[iCfgType]
1828 TrieSetArray(g_tAsNumbers, g_eAsData[ASD__NUMBER], g_eAsTrieData, sizeof(g_eAsTrieData))
1829
1830 ArrayPushArray(g_aAsArray[iCfgType], g_eAsData)
1831 g_iAsCount[iCfgType]++
1832 }
1833
1834 fclose(iOldFile)
1835 fclose(iNewFile)
1836
1837 if(!delete_file(szOldFileName)) {
1838 console_print(pPlayer, "* Warning! Can't delete '%s'", szOldFileName)
1839 }
1840
1841 if(bFound) {
1842 console_print(pPlayer, "* AS number '%s' successfully removed from %s!", szBuffer, LIST_NAME[iType])
1843 }
1844 else {
1845 console_print(pPlayer, "* AS number '%s' removed from memory, but not found in '%s'!", szBuffer, AS_FILE_NAME)
1846 }
1847
1848 log_to_file(g_eLogFile[LOG__CMD], "AS number '%s' removed from %s by %N", szBuffer, LIST_NAME[iType], pPlayer)
1849
1850 return PLUGIN_HANDLED
1851}
1852
1853/* -------------------- */
1854
1855public concmd_ShowAsList(pPlayer, iAccess) {
1856 if(!UserHasAccess(pPlayer, iAccess)) {
1857 return PLUGIN_HANDLED
1858 }
1859
1860 new szBuffer[64], iType
1861
1862 read_argv(0, szBuffer, chx(szBuffer))
1863
1864 if(equali(szBuffer, "bg_as_blacklist_show")) {
1865 iType = LIST_TYPE__BLACKLIST
1866 }
1867 else {
1868 iType = LIST_TYPE__WHITELIST
1869 }
1870
1871 new iTotalPages = floatround(float(g_iAsCount[iType]) / 10.0, floatround_ceil)
1872
1873 if(read_argc() == 1) {
1874 console_print(pPlayer, "* Usage: %s <page> (total pages: %i)",
1875 iType == LIST_TYPE__BLACKLIST ? "bg_as_blacklist_show" : "bg_as_whitelist_show", iTotalPages );
1876
1877 return PLUGIN_HANDLED
1878 }
1879
1880 new iPage = read_argv_int(1)
1881
1882 if(iPage < 1) {
1883 iPage = 1
1884 }
1885
1886 if(iPage > iTotalPages) {
1887 iPage = iTotalPages
1888 }
1889
1890 if(!iPage) {
1891 console_print(pPlayer, "* AS %s is empty!", LIST_NAME[iType])
1892 return PLUGIN_HANDLED
1893 }
1894
1895 console_print(pPlayer, "Displaying page %i/%i:", iPage, iTotalPages)
1896 console_print(pPlayer, "# <-> AS number <-> Comment")
1897
1898 new iCount, iStartPos, i
1899 iStartPos = i = 10 * (iPage - 1)
1900
1901 while(i < g_iAsCount[iType]) {
1902 ArrayGetArray(g_aAsArray[iType], i, g_eAsData)
1903
1904 if(!g_eAsData[ASD__COMMENT][0]) {
1905 g_eAsData[ASD__COMMENT] = _NA_
1906 }
1907
1908 console_print(pPlayer, "%i <-> %s <-> %s", i + 1, g_eAsData[ASD__NUMBER], g_eAsData[ASD__COMMENT])
1909
1910 if(++iCount == 10) {
1911 break
1912 }
1913
1914 i++
1915 }
1916
1917 console_print(pPlayer, "Displayed records: %i/%i", iStartPos + iCount, g_iAsCount[iType])
1918
1919 return PLUGIN_HANDLED
1920}
1921
1922/* -------------------- */
1923
1924public concmd_AddToIpList(pPlayer, iAccess) {
1925 if(!UserHasAccess(pPlayer, iAccess)) {
1926 return PLUGIN_HANDLED
1927 }
1928
1929 new szBuffer[64], iType
1930
1931 read_argv(0, szBuffer, chx(szBuffer))
1932
1933 if(equali(szBuffer, "bg_ip_blacklist_add")) {
1934 iType = LIST_TYPE__BLACKLIST
1935 }
1936 else {
1937 iType = LIST_TYPE__WHITELIST
1938 }
1939
1940 new iArgCount = read_argc()
1941
1942 if(iArgCount < 3) {
1943 console_print( pPlayer, "* Usage: %s <start_ip> <end_ip> <^"comment^">[optional]",
1944 iType == LIST_TYPE__BLACKLIST ? "bg_ip_blacklist_add" : "bg_ip_whitelist_add" );
1945
1946 return PLUGIN_HANDLED
1947 }
1948
1949 new szStart[MAX_IP_LENGTH], szEnd[MAX_IP_LENGTH]
1950
1951 read_argv(1, szStart, chx(szStart))
1952 read_argv(2, szEnd, chx(szEnd))
1953
1954 if(!IsIpValid(szStart) || !IsIpValid(szEnd)) {
1955 console_print(pPlayer, "* Wrong value, check your input!")
1956 return PLUGIN_HANDLED
1957 }
1958
1959 g_eRangeData[RDS__START_IP] = func_ParseIP(szStart)
1960 g_eRangeData[RDS__END_IP] = func_ParseIP(szEnd)
1961
1962 if((g_eRangeData[RDS__END_IP] - g_eRangeData[RDS__START_IP] + 1) < 1) {
1963 console_print(pPlayer, "* Start IP is lower than End IP!")
1964 return PLUGIN_HANDLED
1965 }
1966
1967 if(iArgCount > 3) {
1968 read_argv(3, szBuffer, chx(szBuffer))
1969 trim(szBuffer)
1970
1971 if(strlen(szBuffer) > MAX_COMMENT_LEN) {
1972 console_print(pPlayer, "* Comment is too long, max %i chars!", MAX_COMMENT_LEN)
1973 return PLUGIN_HANDLED
1974 }
1975 }
1976 else {
1977 szBuffer[0] = EOS
1978 }
1979
1980 copy(g_eRangeData[RDS__COMMENT], MAX_COMMENT_LEN - 1, szBuffer)
1981
1982 new iFile = fopen(g_szIpListFile, "a")
1983
1984 if(!iFile) {
1985 console_print(pPlayer, "* Error, can't open subnet list file!")
1986 return PLUGIN_HANDLED
1987 }
1988
1989 fprintf(iFile, "^n%s %s %s", LIST_NAME[iType], szStart, szEnd)
1990
1991 if(szBuffer[0]) {
1992 fprintf(iFile, " ^"%s^"", szBuffer)
1993 }
1994
1995 fclose(iFile)
1996
1997 ArrayPushArray(g_eIpArray[iType], g_eRangeData)
1998 g_iIpCount[iType]++
1999
2000 console_print(pPlayer, "* Range '%s - %s' successfully added to %s!", szStart, szEnd, LIST_NAME[iType])
2001
2002 log_to_file(g_eLogFile[LOG__CMD], "Range '%s - %s' added to %s by %N", szStart, szEnd, LIST_NAME[iType], pPlayer)
2003
2004 return PLUGIN_HANDLED
2005}
2006
2007/* -------------------- */
2008
2009public concmd_DelFromIpList(pPlayer, iAccess) {
2010 if(!UserHasAccess(pPlayer, iAccess)) {
2011 return PLUGIN_HANDLED
2012 }
2013
2014 new szCmd[64], iType
2015
2016 read_argv(0, szCmd, chx(szCmd))
2017
2018 if(equali(szCmd, "bg_ip_blacklist_del")) {
2019 iType = LIST_TYPE__BLACKLIST
2020 }
2021 else {
2022 iType = LIST_TYPE__WHITELIST
2023 }
2024
2025 if(read_argc() < 3) {
2026 console_print( pPlayer, "* Usage: %s <start_ip> <end_ip>",
2027 iType == LIST_TYPE__BLACKLIST ? "bg_ip_blacklist_del" : "bg_ip_whitelist_del" );
2028
2029 return PLUGIN_HANDLED
2030 }
2031
2032 new szStart[MAX_IP_LENGTH], szEnd[MAX_IP_LENGTH]
2033
2034 read_argv(1, szStart, chx(szStart))
2035 read_argv(2, szEnd, chx(szEnd))
2036
2037 if(!IsIpValid(szStart) || !IsIpValid(szEnd)) {
2038 console_print(pPlayer, "* Wrong value, check your input!")
2039 return PLUGIN_HANDLED
2040 }
2041
2042 new iStartIP = func_ParseIP(szStart)
2043 new iEndIP = func_ParseIP(szEnd)
2044
2045 new bool:bFound
2046
2047 for(new i; i < g_iIpCount[iType]; i++) {
2048 ArrayGetArray(g_eIpArray[iType], i, g_eRangeData)
2049
2050 if(g_eRangeData[RDS__START_IP] == iStartIP && g_eRangeData[RDS__END_IP] == iEndIP) {
2051 ArrayDeleteItem(g_eIpArray[iType], i)
2052 g_iIpCount[iType]--
2053 bFound = true
2054 break
2055 }
2056 }
2057
2058 if(!bFound) {
2059 console_print(pPlayer, "* Record with provided values not found!")
2060 return PLUGIN_HANDLED
2061 }
2062
2063 new szOldFileName[PLATFORM_MAX_PATH]
2064 formatex(szOldFileName, chx(szOldFileName), "%s_old", g_szIpListFile)
2065
2066 if(!rename_file(g_szIpListFile, szOldFileName, .relative = 1)) {
2067 console_print(pPlayer, "* Error, can't rename subnet list file!")
2068 return PLUGIN_HANDLED
2069 }
2070
2071 new iOldFile = fopen(szOldFileName, "r")
2072
2073 if(!iOldFile) {
2074 console_print(pPlayer, "* Error, can't open renamed subnet list file!")
2075 return PLUGIN_HANDLED
2076 }
2077
2078 new iNewFile = fopen(g_szIpListFile, "w")
2079
2080 if(!iNewFile) {
2081 fclose(iOldFile)
2082 console_print(pPlayer, "* Error, can't open new subnet list file!")
2083 return PLUGIN_HANDLED
2084 }
2085
2086 func_AddDefSting_List(iNewFile)
2087
2088 new iCfgType, szBuffer[128], szOldStart[MAX_IP_LENGTH], szOldEnd[MAX_IP_LENGTH], szComment[MAX_COMMENT_LEN]
2089
2090 bFound = false
2091
2092 while(!feof(iOldFile)) {
2093 fgets(iOldFile, szBuffer, chx(szBuffer))
2094 trim(szBuffer)
2095
2096 switch(szBuffer[0]) {
2097 case 'b': iCfgType = LIST_TYPE__BLACKLIST
2098 case 'w': iCfgType = LIST_TYPE__WHITELIST
2099 default: continue // ';', '/', EOS, etc.
2100 }
2101
2102 szComment[0] = EOS
2103
2104 parse(szBuffer, "", "", szOldStart, chx(szOldStart), szOldEnd, chx(szOldEnd), szComment, MAX_COMMENT_LEN - 1)
2105
2106 if(equal(szStart, szOldStart) && equal(szEnd, szOldEnd) && iType == iCfgType) {
2107 bFound = true
2108 continue
2109 }
2110
2111 fprintf(iNewFile, "^n%s %s %s", LIST_NAME[iCfgType], szOldStart, szOldEnd)
2112
2113 if(szComment[0]) {
2114 fprintf(iNewFile, " ^"%s^"", szComment)
2115 }
2116 }
2117
2118 fclose(iOldFile)
2119 fclose(iNewFile)
2120
2121 if(!delete_file(szOldFileName)) {
2122 console_print(pPlayer, "* Warning! Can't delete '%s'", szOldFileName)
2123 }
2124
2125 if(bFound) {
2126 console_print(pPlayer, "* Range '%s - %s' successfully removed from %s!", szStart, szEnd, LIST_NAME[iType])
2127 }
2128 else {
2129 console_print(pPlayer, "* Range '%s - %s' removed from %s, but not found in subnet list file!", szStart, szEnd, LIST_NAME[iType])
2130 }
2131
2132 log_to_file(g_eLogFile[LOG__CMD], "Range '%s - %s' removed from %s by %N", szStart, szEnd, LIST_NAME[iType], pPlayer)
2133
2134 return PLUGIN_HANDLED
2135}
2136
2137/* -------------------- */
2138
2139public concmd_ShowIpList(pPlayer, iAccess) {
2140 if(!UserHasAccess(pPlayer, iAccess)) {
2141 return PLUGIN_HANDLED
2142 }
2143
2144 new szBuffer[64], iType
2145
2146 read_argv(0, szBuffer, chx(szBuffer))
2147
2148 if(equali(szBuffer, "bg_ip_blacklist_show")) {
2149 iType = LIST_TYPE__BLACKLIST
2150 }
2151 else {
2152 iType = LIST_TYPE__WHITELIST
2153 }
2154
2155 new iTotalPages = floatround(float(g_iIpCount[iType]) / 10.0, floatround_ceil)
2156
2157 if(read_argc() == 1) {
2158 console_print( pPlayer, "* Usage: %s <page> (total pages: %i)",
2159 iType == LIST_TYPE__BLACKLIST ? "bg_ip_blacklist_show" : "bg_ip_whitelist_show", iTotalPages );
2160
2161 return PLUGIN_HANDLED
2162 }
2163
2164 new iPage = read_argv_int(1)
2165
2166 if(iPage < 1) {
2167 iPage = 1
2168 }
2169
2170 if(iPage > iTotalPages) {
2171 iPage = iTotalPages
2172 }
2173
2174 if(!iPage) {
2175 console_print(pPlayer, "* IP %s is empty!", LIST_NAME[iType])
2176 return PLUGIN_HANDLED
2177 }
2178
2179 console_print(pPlayer, "Displaying page %i/%i:", iPage, iTotalPages)
2180 console_print(pPlayer, "# <-> StartIP <-> EndIP <-> Comment")
2181
2182 new iCount, iStartPos, i
2183 iStartPos = i = 10 * (iPage - 1)
2184
2185 new szStartIP[MAX_IP_LENGTH], szEndIP[MAX_IP_LENGTH]
2186
2187 while(i < g_iIpCount[iType]) {
2188 ArrayGetArray(g_eIpArray[iType], i, g_eRangeData)
2189
2190 func_ReverseIP(g_eRangeData[RDS__START_IP], szStartIP, chx(szEndIP))
2191 func_ReverseIP(g_eRangeData[RDS__END_IP], szEndIP, chx(szEndIP))
2192
2193 if(!g_eRangeData[RDS__COMMENT][0]) {
2194 g_eRangeData[RDS__COMMENT] = _NA_
2195 }
2196
2197 console_print(pPlayer, "%i <-> %s <-> %s <-> %s", i + 1, szStartIP, szEndIP, g_eRangeData[RDS__COMMENT])
2198
2199 if(++iCount == 10) {
2200 break
2201 }
2202
2203 i++
2204 }
2205
2206 console_print(pPlayer, "Displayed records: %i/%i", iStartPos + iCount, g_iIpCount[iType])
2207
2208 return PLUGIN_HANDLED
2209}
2210
2211/* -------------------- */
2212
2213public concmd_FindIP(pPlayer, iAccess) {
2214 if(!UserHasAccess(pPlayer, iAccess)) {
2215 return PLUGIN_HANDLED
2216 }
2217
2218 if(read_argc() == 1) {
2219 console_print(pPlayer, "* Usage: bg_find_ip <ip> - Check IP presence in blacklist and whitelist")
2220 return PLUGIN_HANDLED
2221 }
2222
2223 new szBuffer[64]
2224
2225 read_args(szBuffer, chx(szBuffer))
2226 remove_quotes(szBuffer)
2227 trim(szBuffer)
2228
2229 if(!IsIpValid(szBuffer)) {
2230 console_print(pPlayer, "* Wrong IP, check your input!")
2231 return PLUGIN_HANDLED
2232 }
2233
2234 new iIP = func_ParseIP(szBuffer)
2235 new szStartIP[MAX_IP_LENGTH], szEndIP[MAX_IP_LENGTH]
2236
2237 for(new iType = LIST_TYPE__BLACKLIST; iType <= LIST_TYPE__WHITELIST; iType++) {
2238 if(IsIpInList(iIP, iType)) {
2239 console_print(pPlayer, "* IP '%s' FOUND in %s", szBuffer, LIST_NAME[iType])
2240
2241 if(!g_eRangeData[RDS__COMMENT][0]) {
2242 g_eRangeData[RDS__COMMENT] = _NA_
2243 }
2244
2245 func_ReverseIP(g_eRangeData[RDS__START_IP], szStartIP, chx(szStartIP))
2246 func_ReverseIP(g_eRangeData[RDS__END_IP], szEndIP, chx(szEndIP))
2247
2248 console_print(pPlayer, "* Range '%s <-> %s', comment: '%s'", szStartIP, szEndIP, g_eRangeData[RDS__COMMENT])
2249 continue
2250 }
2251
2252 console_print(pPlayer, "* IP '%s' NOT found in %s", szBuffer, LIST_NAME[iType])
2253 }
2254
2255 return PLUGIN_HANDLED
2256}
2257
2258/* -------------------- */
2259
2260public concmd_GetAsByIP(pPlayer, iAccess) {
2261 if(!UserHasAccess(pPlayer, iAccess)) {
2262 return PLUGIN_HANDLED
2263 }
2264
2265 if(read_argc() == 1) {
2266 console_print(pPlayer, "* Usage: bg_get_as_by_ip <ip> - Get AS number for provided IP")
2267 return PLUGIN_HANDLED
2268 }
2269
2270 new szBuffer[64]
2271
2272 read_args(szBuffer, chx(szBuffer))
2273 remove_quotes(szBuffer)
2274 trim(szBuffer)
2275
2276 if(!IsIpValid(szBuffer)) {
2277 console_print(pPlayer, "* Wrong IP, check your input!")
2278 return PLUGIN_HANDLED
2279 }
2280
2281 if(TrieGetArray(g_tIpInfo, szBuffer, g_eIpInfoData, sizeof(g_eIpInfoData))) {
2282 if(!g_eIpInfoData[IP_INFO__DESC][0]) {
2283 copy(g_eIpInfoData[IP_INFO__DESC], MAX_DESC_LEN - 1, _NA_)
2284 }
2285 }
2286 else {
2287 if(!equal(g_szGetWhoisIP, szBuffer)) {
2288 g_iManualWhoisState = 0
2289 copy(g_szGetWhoisIP, chx(g_szGetWhoisIP), szBuffer)
2290 new eExtData[WHOIS_DATA_STRUCT]; eExtData[WHOIS_DATA__QUERY_TYPE] = WHOIS_QUERY_TYPE__MANUAL
2291 WhoisIpQuery(szBuffer, "OnWhoisIpQueryComplete", eExtData, sizeof(eExtData))
2292 console_print(pPlayer, "* Query for IP '%s' was sent, use cmd again!", szBuffer)
2293 log_to_file(g_eLogFile[LOG__CMD], "GetAsByIp request for '%s' by %N", szBuffer, pPlayer)
2294 return PLUGIN_HANDLED
2295 }
2296
2297 g_szGetWhoisIP[0] = EOS
2298
2299 if(g_iManualWhoisState == INVALID_HANDLE) {
2300 console_print(pPlayer, "* Query error occured, try again!")
2301 return PLUGIN_HANDLED
2302 }
2303
2304 if(!g_iManualWhoisState) {
2305 console_print(pPlayer, "* No data has been recieved! Try again!")
2306 return PLUGIN_HANDLED
2307 }
2308 }
2309
2310 func_GetCountryCode(0, szBuffer, g_szCode[0], chx(g_szCode[]))
2311 func_GetCountryName(0, szBuffer, g_szCountry[0], chx(g_szCountry[]))
2312
2313 console_print( pPlayer, "* AS number for IP '%s' (%s, %s) is '%s' (%s)", szBuffer, g_szCode[0],
2314 g_szCountry[0], g_eIpInfoData[IP_INFO__AS], g_eIpInfoData[IP_INFO__DESC] );
2315
2316 return PLUGIN_HANDLED
2317}
2318
2319/* -------------------- */
2320
2321public concmd_FindAS(pPlayer, iAccess) {
2322 if(!UserHasAccess(pPlayer, iAccess)) {
2323 return PLUGIN_HANDLED
2324 }
2325
2326 if(read_argc() == 1) {
2327 console_print(pPlayer, "* Usage: bg_find_as <as_number> - Check AS number presence in blacklist and whitelist")
2328 return PLUGIN_HANDLED
2329 }
2330
2331 new szBuffer[64]
2332
2333 read_args(szBuffer, chx(szBuffer))
2334 remove_quotes(szBuffer)
2335 trim(szBuffer)
2336
2337 if(!IsValidAsNumber(szBuffer)) {
2338 console_print(pPlayer, "* Wrong AS number, check your input!")
2339 return PLUGIN_HANDLED
2340 }
2341
2342 if(!TrieKeyExists(g_tAsNumbers, szBuffer)) {
2343 console_print(pPlayer, "* AS number '%s' NOT found in both lists!", szBuffer)
2344 return PLUGIN_HANDLED
2345 }
2346
2347 for(new iType = LIST_TYPE__BLACKLIST; iType <= LIST_TYPE__WHITELIST; iType++) {
2348 for(new i; i < g_iAsCount[iType]; i++) {
2349 ArrayGetArray(g_aAsArray[iType], i, g_eAsData)
2350
2351 if(equal(g_eAsData[ASD__NUMBER], szBuffer)) {
2352 if(!g_eAsData[ASD__COMMENT][0]) {
2353 g_eAsData[ASD__COMMENT] = _NA_
2354 }
2355
2356 console_print(pPlayer, "* AS number '%s' FOUND in %s, comment: '%s'", szBuffer, LIST_NAME[iType], g_eAsData[ASD__COMMENT])
2357 return PLUGIN_HANDLED
2358 }
2359 }
2360 }
2361
2362 return PLUGIN_HANDLED
2363}
2364
2365/* -------------------- */
2366
2367public concmd_CheckIP(pPlayer, iAccess) {
2368 if(!UserHasAccess(pPlayer, iAccess)) {
2369 return PLUGIN_HANDLED
2370 }
2371
2372 if(read_argc() == 1) {
2373 console_print(pPlayer, "* Usage: bg_check_ip <ip> - Check IP address for Proxy/VPN involvement (bypasing all cache)")
2374 return PLUGIN_HANDLED
2375 }
2376
2377 if(!g_bSystemAvailable) {
2378 console_print(pPlayer, "* System not initialized yet!")
2379 return PLUGIN_HANDLED
2380 }
2381
2382 new szBuffer[64]
2383
2384 read_args(szBuffer, chx(szBuffer))
2385 remove_quotes(szBuffer)
2386 trim(szBuffer)
2387
2388 if(!IsIpValid(szBuffer)) {
2389 console_print(pPlayer, "* Wrong IP, check your input!")
2390 return PLUGIN_HANDLED
2391 }
2392
2393 new iIpStatus
2394
2395 if(!equal(g_szGetProxyIP, szBuffer)) {
2396 copy(g_szGetProxyIP, chx(g_szGetProxyIP), szBuffer)
2397 TrieDeleteKey(g_tIpProxyStatus, szBuffer)
2398 func_CheckForProxy(INVALID_HANDLE, szBuffer, "")
2399 console_print(pPlayer, "* Query for IP '%s' was sent, use cmd again!", szBuffer)
2400 log_to_file(g_eLogFile[LOG__CMD], "CheckIp request for '%s' by %N", szBuffer, pPlayer)
2401 return PLUGIN_HANDLED
2402 }
2403
2404 g_szGetProxyIP[0] = EOS
2405
2406 if(!TrieGetCell(g_tIpProxyStatus, szBuffer, iIpStatus)) {
2407 console_print(pPlayer, "* No data has been recieved (error?), try again!")
2408 return PLUGIN_HANDLED
2409 }
2410
2411 func_GetCountryCode(0, szBuffer, g_szCode[0], chx(g_szCode[]))
2412 func_GetCountryName(0, szBuffer, g_szCountry[0], chx(g_szCountry[]))
2413
2414 console_print( pPlayer, "* '%s' (%s, %s) status is '%s'", szBuffer, g_szCode[0],
2415 g_szCountry[0], IP_STATES[iIpStatus] );
2416
2417 return PLUGIN_HANDLED
2418}
2419
2420/* -------------------- */
2421
2422public concmd_FlushData(pPlayer, iAccess) {
2423 if(!UserHasAccess(pPlayer, iAccess)) {
2424 return PLUGIN_HANDLED
2425 }
2426
2427 if(read_argc() != 1) {
2428 log_to_file(g_eLogFile[LOG__CMD], "Flush cmd with param '%i' by %N", read_argv_int(1), pPlayer)
2429
2430 switch(read_argv_int(1)) {
2431 case 1: {
2432 if(!g_bSystemAvailable) {
2433 console_print(pPlayer, "* System not initialized yet!")
2434 return PLUGIN_HANDLED
2435 }
2436
2437 // TrieClear(g_tIpProxyStatus) -> In SQL_Handler(), see 'QUERY__FLUSH_IPS'
2438 formatex(g_szQuery, chx(g_szQuery), "DELETE FROM `%s`", g_szTableName)
2439 func_MakeQuery(QUERY__FLUSH_IPS)
2440 console_print(pPlayer, "* Proxy/VPN IP check data flushed!")
2441 return PLUGIN_HANDLED
2442 }
2443 case 2: {
2444 nvault_prune(g_hImmunity, 0, get_systime())
2445 console_print(pPlayer, "* SteamID immunity data flushed!")
2446 return PLUGIN_HANDLED
2447 }
2448 case 3: {
2449 TrieClear(g_tAsCache)
2450 console_print(pPlayer, "* AS check status cache flushed!")
2451 return PLUGIN_HANDLED
2452 }
2453 case 4: {
2454 g_iIpCount[LIST_TYPE__BLACKLIST] = 0
2455 g_iIpCount[LIST_TYPE__WHITELIST] = 0
2456 ArrayClear(Array:g_eIpArray[LIST_TYPE__BLACKLIST])
2457 ArrayClear(Array:g_eIpArray[LIST_TYPE__WHITELIST])
2458
2459 new iFile = fopen(g_szIpListFile, "w")
2460
2461 if(iFile) {
2462 func_AddDefSting_List(iFile)
2463 console_print(pPlayer, "* IP range configuration flushed!")
2464 fclose(iFile)
2465 }
2466 else {
2467 console_print(pPlayer, "* Can't open '%s'!", IP_FILE_NAME)
2468 }
2469
2470 return PLUGIN_HANDLED
2471 }
2472 case 5: {
2473 g_iAsCount[LIST_TYPE__BLACKLIST] = 0
2474 g_iAsCount[LIST_TYPE__WHITELIST] = 0
2475 TrieClear(g_tAsCache)
2476 TrieClear(g_tAsNumbers)
2477 ArrayClear(Array:g_aAsArray[LIST_TYPE__BLACKLIST])
2478 ArrayClear(Array:g_aAsArray[LIST_TYPE__WHITELIST])
2479
2480 new iFile = fopen(g_szAsListFile, "w")
2481
2482 if(iFile) {
2483 func_AddDefSting_AS(iFile)
2484 console_print(pPlayer, "* AS configuraion and check status cache flushed!")
2485 fclose(iFile)
2486 }
2487 else {
2488 console_print(pPlayer, "* Can't truncate '%s'!", AS_FILE_NAME)
2489 }
2490
2491 return PLUGIN_HANDLED
2492 }
2493 }
2494 }
2495
2496 console_print(pPlayer, "* Usage: bg_flush_data <1-5>")
2497 console_print(pPlayer, "* 1 - Proxy/VPN IP check status (sql + trie)")
2498 console_print(pPlayer, "* 2 - SteamID immunity list (nvault)")
2499 console_print(pPlayer, "* 3 - AS check status cache (trie)")
2500 console_print(pPlayer, "* 4 - IP range configuration (memory + file)")
2501 console_print(pPlayer, "* 5 - AS configuration (memory + file)")
2502 return PLUGIN_HANDLED
2503}
2504
2505/* -------------------- */
2506
2507public concmd_Status(pPlayer, iAccess) {
2508 if(!UserHasAccess(pPlayer, iAccess)) {
2509 return PLUGIN_HANDLED
2510 }
2511
2512 if(!g_eCvar[CVAR__PLUGIN_ENABLED]) {
2513 console_print(pPlayer, "* Plugin disabled, command unavailable!")
2514 return PLUGIN_HANDLED
2515 }
2516
2517 console_print(pPlayer, "^nPlayers status:^n# <-> Access <-> Nick <-> IP <-> SteamID <-> AS <-> Desc. <-> Code <-> Country")
2518
2519 new iCount, szIP[MAX_IP_LENGTH], szAuthID[MAX_AUTHID_LENGTH]
2520
2521 for(new pUser = 1; pUser <= MaxClients; pUser++) {
2522 if(!is_user_connected(pUser)) {
2523 continue
2524 }
2525
2526 get_user_ip(pUser, szIP, chx(szIP), .without_port = 1)
2527 get_user_authid(pUser, szAuthID, chx(szAuthID))
2528
2529 console_print( pPlayer, "%i <-> %s <-> %n <-> %s <-> %s <-> %s <-> %s <-> %s <-> %s", ++iCount, g_szAccess[pUser],
2530 pUser, szIP, szAuthID, g_szAsNumber[pUser], g_szDesc[pUser], g_szCode[pUser], g_szCountry[pUser] );
2531 }
2532
2533 console_print(pPlayer, "Total: %i^n", iCount)
2534
2535 return PLUGIN_HANDLED
2536}
2537
2538/* -------------------- */
2539
2540stock func_LoadRanges() {
2541 new iFile = fopen(g_szIpListFile, "r")
2542
2543 if(!iFile) {
2544 if(file_exists(g_szIpListFile)) {
2545 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open existing '%s'", IP_FILE_NAME)
2546 return
2547 }
2548
2549 iFile = fopen(g_szIpListFile, "w")
2550
2551 if(!iFile) {
2552 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't create default '%s'", IP_FILE_NAME)
2553 return
2554 }
2555
2556 func_AddDefSting_List(iFile)
2557 fclose(iFile)
2558 return
2559 }
2560
2561 new iType, szStartIP[MAX_IP_LENGTH], szEndIP[MAX_IP_LENGTH], szBuffer[128]
2562
2563 while(!feof(iFile)) {
2564 fgets(iFile, szBuffer, chx(szBuffer))
2565 trim(szBuffer)
2566
2567 switch(szBuffer[0]) {
2568 case 'b': iType = LIST_TYPE__BLACKLIST
2569 case 'w': iType = LIST_TYPE__WHITELIST
2570 default: continue // ';', '/', EOS, etc.
2571 }
2572
2573 g_eRangeData[RDS__COMMENT][0] = EOS
2574
2575 parse(szBuffer, "", "", szStartIP, chx(szStartIP), szEndIP, chx(szEndIP), g_eRangeData[RDS__COMMENT], MAX_COMMENT_LEN - 1)
2576
2577 g_eRangeData[RDS__START_IP] = func_ParseIP(szStartIP)
2578 g_eRangeData[RDS__END_IP] = func_ParseIP(szEndIP)
2579
2580 ArrayPushArray(g_eIpArray[iType], g_eRangeData)
2581 g_iIpCount[iType]++
2582 }
2583
2584 fclose(iFile)
2585}
2586
2587/* -------------------- */
2588
2589public func_LoadAsNumbers() {
2590 new iFile = fopen(g_szAsListFile, "r")
2591
2592 if(!iFile) {
2593 if(file_exists(g_szAsListFile)) {
2594 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open existing '%s'", AS_FILE_NAME)
2595 return
2596 }
2597
2598 iFile = fopen(g_szAsListFile, "w")
2599
2600 if(!iFile) {
2601 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't create default '%s'", AS_FILE_NAME)
2602 return
2603 }
2604
2605 func_AddDefSting_AS(iFile)
2606 fclose(iFile)
2607 return
2608 }
2609
2610 new iType, szBuffer[128]
2611
2612 while(!feof(iFile)) {
2613 fgets(iFile, szBuffer, chx(szBuffer))
2614 trim(szBuffer)
2615
2616 switch(szBuffer[0]) {
2617 case 'b': iType = LIST_TYPE__BLACKLIST
2618 case 'w': iType = LIST_TYPE__WHITELIST
2619 default: continue // ';', '/', EOS, etc.
2620 }
2621
2622 g_eAsData[ASD__COMMENT][0] = EOS
2623
2624 parse(szBuffer, "", "", g_eAsData[ASD__NUMBER], MAX_AS_LEN - 1, g_eAsData[ASD__COMMENT], MAX_COMMENT_LEN - 1)
2625
2626 g_eAsTrieData[AST__STATE] = (iType == LIST_TYPE__BLACKLIST) ? STATE__BAN : STATE__WHITELIST;
2627 g_eAsTrieData[AST__ARRAY_POS] = g_iAsCount[iType]
2628 TrieSetArray(g_tAsNumbers, g_eAsData[ASD__NUMBER], g_eAsTrieData, sizeof(g_eAsTrieData))
2629
2630 ArrayPushArray(g_aAsArray[iType], g_eAsData)
2631 g_iAsCount[iType]++
2632 }
2633
2634 fclose(iFile)
2635}
2636
2637/* -------------------- */
2638
2639stock func_LoadAllowedCodes(szCfgDir[]) {
2640 new iFile = fopen(szCfgDir, "r")
2641
2642 if(!iFile) {
2643 if(file_exists(szCfgDir)) {
2644 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open existing '%s'", COUNTRY_FILE_NAME)
2645 return
2646 }
2647
2648 iFile = fopen(szCfgDir, "w")
2649
2650 if(!iFile) {
2651 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't create default '%s'", COUNTRY_FILE_NAME)
2652 return
2653 }
2654
2655 fputs( iFile,
2656 "; Список стран, которые могут входить на сервер^n\
2657 ; Country list that can enter to the server^n\
2658 ^"RU^" // Россия^n\
2659 ^"UA^" // Украина^n\
2660 ^"BY^" // Беларусь^n\
2661 ^"KZ^" // Казахстан^n\
2662 ^"KG^" // Киргизия^n\
2663 ^"MD^" // Молдавия^n\
2664 ^"UZ^" // Узбекистан^n\
2665 ^"LV^" // Латвия^n\
2666 ^"LT^" // Литва^n\
2667 ^"EE^" // Эстония^n\
2668 ^"GE^" // Грузия^n\
2669 ^"RO^" // Румыния^n\
2670 ^"AM^" // Армения^n\
2671 ^"BG^" // Болгария^n\
2672 ^"AZ^" // Азербайджан^n\
2673 ^"TM^" // Туркменистан^n\
2674 ^"TJ^" // Таджикистан"
2675 );
2676
2677 fclose(iFile)
2678
2679 iFile = fopen(szCfgDir, "r")
2680
2681 if(!iFile) {
2682 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open existing '%s'", COUNTRY_FILE_NAME)
2683 return
2684 }
2685 }
2686
2687 new szBuffer[32]
2688
2689 while(!feof(iFile)) {
2690 if(g_iCodesCount == MAX_CODES) {
2691 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Codes limit reached! Increase 'MAX_CODES' in .sma !")
2692 break
2693 }
2694
2695 fgets(iFile, szBuffer, chx(szBuffer))
2696
2697 if(szBuffer[0] != '"') {
2698 continue
2699 }
2700
2701 parse(szBuffer, g_szAllowedCodes[g_iCodesCount++], MAX_CODE_LEN - 1)
2702 }
2703
2704 fclose(iFile)
2705}
2706
2707/* -------------------- */
2708
2709stock func_LoadApiKeys(szKeyFile[]) {
2710 new iFile = fopen(szKeyFile, "r")
2711
2712 if(!iFile) {
2713 if(file_exists(szKeyFile)) {
2714 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't open existing '%s'", KEY_FILE_NAME)
2715 return
2716 }
2717
2718 iFile = fopen(szKeyFile, "w")
2719
2720 if(!iFile) {
2721 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Can't create default '%s'", KEY_FILE_NAME)
2722 return
2723 }
2724
2725 fputs( iFile,
2726 "; Ключи к API iphub'а. Зарегистрируйтесь на http://iphub.info/ , получите бесплатный ключ (1000 проверок/сутки), и введите его сюда^n\
2727 ; API-keys for iphub. Register at http://iphub.info/ , get free key (1000 checks/day), and set it here^n\
2728 X-Key: ########################################"
2729 );
2730
2731 fclose(iFile)
2732 return
2733 }
2734
2735 new szBuffer[MAX_API_KEY_LEN + 8]
2736
2737 while(!feof(iFile)) {
2738 if(g_iLoadedKeys == MAX_KEYS) {
2739 log_to_file(g_eLogFile[LOG__ERROR], "[Error] Keys limit reached! Increase 'MAX_KEYS' in .sma !")
2740 break
2741 }
2742
2743 fgets(iFile, szBuffer, chx(szBuffer))
2744 trim(szBuffer)
2745
2746 if(szBuffer[0] != 'X') {
2747 continue
2748 }
2749
2750 copy(g_szApiKey[g_iLoadedKeys++], MAX_API_KEY_LEN - 1, szBuffer)
2751 }
2752
2753 fclose(iFile)
2754}
2755
2756/* -------------------- */
2757
2758stock func_ParseIP(szIP[]) {
2759 new szRight[MAX_IP_LENGTH], szPart[4], iIP, iOctet
2760
2761 strtok(szIP, szPart, chx(szPart), szRight, chx(szRight), '.')
2762
2763 for(new i; i < 4; i++) {
2764 iOctet = str_to_num(szPart)
2765
2766 if(iOctet < 0) {
2767 iOctet = 0
2768 }
2769 else if(iOctet > 255) {
2770 iOctet = 255
2771 }
2772
2773 iIP += iOctet
2774
2775 if(i == 3) {
2776 break
2777 }
2778
2779 strtok(szRight, szPart, chx(szPart), szRight, chx(szRight), '.')
2780 iIP = iIP << 8
2781 }
2782
2783 return iIP
2784}
2785
2786/* -------------------- */
2787
2788stock func_ReverseIP(iIP, szIP[MAX_IP_LENGTH], iLen) {
2789 new iOctet[4], bool:bHigh
2790
2791 if(iIP < 0) {
2792 bHigh = true;
2793 iIP = iIP & (~(1 << 31));
2794 }
2795
2796 for(new i = 0; i < 4; i++) {
2797 iOctet[i] = iIP & 255;
2798 iIP = iIP >> 8;
2799 }
2800 if(bHigh) {
2801 iOctet[3] += 128
2802 }
2803
2804 formatex(szIP, iLen, "%i.%i.%i.%i", iOctet[3], iOctet[2], iOctet[1], iOctet[0])
2805}
2806
2807/* -------------------- */
2808
2809stock bool:IsIpInList(iIP, iType) {
2810
2811 for(new i; i < g_iIpCount[iType]; i++) {
2812 ArrayGetArray(g_eIpArray[iType], i, g_eRangeData)
2813
2814 if(CompareUnsigned(g_eRangeData[RDS__START_IP], iIP) <= 0 && CompareUnsigned(g_eRangeData[RDS__END_IP], iIP) >= 0) {
2815 return true
2816 }
2817 }
2818
2819 return false
2820}
2821
2822/* -------------------- */
2823
2824/**
2825 * Compares two integers as unsigned values.
2826 *
2827 * @param first First value to compare.
2828 * @param second Second value to compare.
2829 * @return -1 if first is smaller than second.
2830 * 0 if first is equal to second.
2831 * 1 if first is greater than second.
2832 */
2833stock CompareUnsigned(first, second) {
2834 if(first == second) {
2835 return 0
2836 }
2837
2838 new bool:highFirst, bool:highSecond
2839
2840 if(first < 0) {
2841 highFirst = true
2842 first = first & (~(1 << 31))
2843 }
2844
2845 if(second < 0) {
2846 highSecond = true
2847 second = second & (~(1 << 31))
2848 }
2849
2850 if(highFirst && !highSecond) {
2851 return 1
2852 }
2853
2854 if(!highFirst && highSecond) {
2855 return -1
2856 }
2857
2858 if(first > second) {
2859 return 1
2860 }
2861
2862 return -1
2863}
2864
2865/* -------------------- */
2866
2867public hook_CvarChange(pCvar, szOldVal[], szNewVal[]) {
2868 if(pCvar == g_pCvar[PCVAR__AMX_DEFAULT_ACCESS]) {
2869 g_iDefAccFlags = read_flags(szNewVal)
2870 }
2871 else { // PCVAR__IMMUNITY_FLAGS
2872 g_iImmunityFlags = read_flags(szNewVal)
2873 }
2874}
2875
2876/* -------------------- */
2877
2878stock bool:IsIpValid(szIP[]) {
2879 new i, szRight[MAX_IP_LENGTH], szPart[4], iCount
2880
2881 strtok(szIP, szPart, chx(szPart), szRight, chx(szRight), '.')
2882
2883 while(szPart[0] >= '0' && szPart[0] <= '9') {
2884 i = str_to_num(szPart)
2885
2886 if(i < 0 || i > 255) {
2887 return false
2888 }
2889
2890 iCount++
2891 strtok(szRight, szPart, chx(szPart), szRight, chx(szRight), '.')
2892 }
2893
2894 return (iCount == 4)
2895}
2896
2897/* -------------------- */
2898
2899stock bool:IsValidSteamID(szAuthID[]) {
2900 // STEAM_ or VALVE_
2901 // 0:(0|1):\d+
2902 // 0-5 = STEAM_
2903 // 6 = integer
2904 // 7 = :
2905 // 8 = integer
2906 // 9 = :
2907 // 10+ = integer
2908 return (
2909 ( equal(szAuthID, "STEAM_", 6) || equal(szAuthID, "VALVE_", 6) )
2910 &&
2911 isdigit(szAuthID[6])
2912 /* &&
2913 szAuthID[7] == ':'
2914 &&
2915 isdigit(szAuthID[8])
2916 &&
2917 szAuthID[9] == ':'
2918 &&
2919 is_str_num(szAuthID[10])*/
2920 );
2921}
2922
2923/* -------------------- */
2924
2925stock bool:IsValidAsNumber(szAsNumber[]) {
2926 //As we use trie, we need case-sensitive comparement
2927 //if((szAsNumber[0] != 'A' && szAsNumber[0] != 'a') || (szAsNumber[1] != 'S' && szAsNumber[1] != 's') || !szAsNumber[2]) {
2928 if(szAsNumber[0] != 'A' || szAsNumber[1] != 'S' || !szAsNumber[2]) {
2929 return false
2930 }
2931
2932 for(new i = 2; i < MAX_AS_LEN; i++) {
2933 if(!szAsNumber[i]) {
2934 return true
2935 }
2936
2937 if(szAsNumber[i] < '0' || szAsNumber[i] > '9') {
2938 return false
2939 }
2940 }
2941
2942 return false
2943}
2944
2945/* -------------------- */
2946
2947stock func_AddDefSting_List(iFile) {
2948 fputs(iFile, ";Line format: blacklist/whitelist start_ip end_ip ^"comment^"[optional]^n")
2949}
2950
2951/* -------------------- */
2952
2953stock func_AddDefSting_AS(iFile) {
2954 fputs(iFile, ";Line format: blacklist/whitelist AS_number ^"comment^"[optional]^n")
2955}
2956
2957/* -------------------- */
2958
2959stock func_GetCountryCode(pPlayer, szIP[], szBuffer[], iMaxLen) {
2960 new szCode[MAX_CODE_LEN]
2961
2962 if(sxgeo_code(szIP, szCode)) {
2963 copy(szBuffer, iMaxLen, szCode)
2964 return
2965 }
2966
2967 g_szCode[pPlayer] = _NA_
2968}
2969
2970/* -------------------- */
2971
2972stock func_GetCountryName(pPlayer, szIP[], szBuffer[], iMaxLen) {
2973 if(!sxgeo_country(szIP, szBuffer, iMaxLen)) {
2974 g_szCountry[pPlayer] = _NA_
2975 }
2976}
2977
2978/* -------------------- */
2979
2980stock func_LogAllow(pPlayer, szIP[], szAuthID[]) {
2981 log_to_file( g_eLogFile[LOG__ALLOW], "[%s] %n | %s | %s | %s | %s | %s | %s",
2982 g_szAccess[pPlayer], pPlayer, szIP, szAuthID, g_szAsNumber[pPlayer], g_szDesc[pPlayer], g_szCode[pPlayer], g_szCountry[pPlayer] );
2983}
2984
2985/* -------------------- */
2986
2987stock bool:UserHasAccess(pPlayer, iAccess) {
2988 if(get_user_flags(pPlayer) & iAccess) {
2989 return true
2990 }
2991
2992 console_print(pPlayer, "* You have no access to this command!")
2993 return false
2994}
2995
2996/* -------------------- */
2997
2998stock func_MakeQuery(iQueryType, eSqlData[SQL_DATA_STRUCT] = "") {
2999 eSqlData[SQL_DATA__QUERY_TYPE] = iQueryType
3000 SQL_ThreadQuery(g_hSqlTuple, "SQL_Handler", g_szQuery, eSqlData, SQL_DATA_STRUCT)
3001}
3002
3003/* -------------------- */
3004
3005public plugin_end() {
3006 g_bPluginEnded = true
3007
3008 if(g_hSqlTuple) {
3009 SQL_FreeHandle(g_hSqlTuple)
3010 }
3011
3012 if(g_hImmunity != INVALID_HANDLE) {
3013 nvault_close(g_hImmunity)
3014 ArrayDestroy(Array:g_eIpArray[LIST_TYPE__BLACKLIST])
3015 ArrayDestroy(Array:g_eIpArray[LIST_TYPE__WHITELIST])
3016 TrieDestroy(g_tAsCache)
3017 TrieDestroy(g_tAsNumbers)
3018 ArrayDestroy(Array:g_aAsArray[LIST_TYPE__BLACKLIST])
3019 ArrayDestroy(Array:g_aAsArray[LIST_TYPE__WHITELIST])
3020 TrieDestroy(g_tCurl)
3021 TrieDestroy(g_tIpInfo)
3022 TrieDestroy(g_tIpProxyStatus)
3023 TrieDestroy(g_tIpMatch)
3024 }
3025}