Commit c6530476 authored by nanahira's avatar nanahira

support EXTERNAL_ADDRESS

parent 2dc01d77
......@@ -24,6 +24,7 @@
"20": "SURRENDER",
"21": "TIME_CONFIRM",
"22": "CHAT",
"23": "EXTERNAL_ADDRESS",
"32": "HS_TODUELIST",
"33": "HS_TOOBSERVER",
"34": "HS_READY",
......
......@@ -29,6 +29,10 @@
"replay_delay": true,
"hide_name": false,
"display_watchers": false,
"trusted_proxies": [
"127.0.0.1/8",
"::1/128"
],
"i18n": {
"auto_pick": false,
"default": "zh-cn",
......@@ -201,10 +205,6 @@
"neos": {
"enabled": false,
"port": 7977,
"trusted_proxies": [
"127.0.0.1/8",
"::1/128"
],
"ip_header": "x-forwarded-for"
},
"test_mode": {
......
......@@ -8,7 +8,8 @@
"HS_KICK":"CTOS_Kick",
"UPDATE_DECK": "deck",
"CHANGE_SIDE":"",
"CHAT": "chat"
"CHAT": "chat",
"EXTERNAL_ADDRESS": "CTOS_ExternalAddress"
},
"STOC":{
"JOIN_GAME":"STOC_JoinGame",
......
......@@ -42,6 +42,10 @@
{"name": "gameid", "type": "unsigned int"},
{"name": "pass", "type": "unsigned short", "length": 20, "encoding": "UTF-16LE"}
],
"CTOS_ExternalAddress": [
{"name": "real_ip", "type": "unsigned int"},
{"name": "hostname", "type": "unsigned short", "length":"256", "encoding": "UTF-16LE"}
],
"CTOS_Kick": [
{"name": "pos", "type": "unsigned char"}
],
......
......@@ -317,6 +317,10 @@ init = () ->
if settings.modules.hide_name == true
settings.modules.hide_name = "start"
imported = true
if settings.modules.neos.trusted_proxies
settings.modules.trusted_proxies = settings.modules.neos.trusted_proxies
delete settings.modules.neos.trusted_proxies
imported = true
#finish
keysFromEnv = Object.keys(process.env).filter((key) => key.startsWith('SRVPRO_'))
if keysFromEnv.length > 0
......@@ -1186,6 +1190,56 @@ CLIENT_send_replays_and_kick = global.CLIENT_send_replays_and_kick = (client, ro
CLIENT_kick(client)
return
toIpv4 = global.toIpv4 = (ip) ->
if ip.startsWith('::ffff:')
return ip.slice(7)
return ip
toIpv6 = global.toIpv6 = (ip) ->
if /^(\d{1,3}\.){3}\d{1,3}$/.test(ip)
return '::ffff:' + ip
return ip
isTrustedProxy = global.isTrustedProxy = (ip) ->
return settings.modules.trusted_proxies.some((trusted) ->
cidr = if trusted.includes('/') then ip6addr.createCIDR(trusted) else ip6addr.createAddrRange(trusted, trusted)
return cidr.contains(ip)
)
getRealIp = global.getRealIp = (physical_ip, xff_ip) ->
if not xff_ip or xff_ip == physical_ip
return toIpv6(physical_ip)
if isTrustedProxy(physical_ip)
return toIpv6(xff_ip.split(',')[0].trim())
log.warn("Untrusted proxy detected: #{physical_ip} -> #{xff_ip}")
return toIpv6(physical_ip)
CLIENT_set_ip = global.CLIENT_set_ip = (client, xff_ip) ->
client_prev_ip = client.ip
client.ip = getRealIp(client.physical_ip, xff_ip)
if client_prev_ip == client.ip
return false
if client_prev_ip and ROOM_connected_ip[client_prev_ip] and ROOM_connected_ip[client_prev_ip] > 0
ROOM_connected_ip[client_prev_ip]--
if ROOM_connected_ip[client_prev_ip] <= 0
delete ROOM_connected_ip[client_prev_ip]
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
connect_count = ROOM_connected_ip[client.ip] or 0
if !settings.modules.test_mode.no_connect_count_limit and !client.is_local and !isTrustedProxy(client.ip)
connect_count++
ROOM_connected_ip[client.ip] = connect_count
# log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
client.destroy()
return true
return false
SOCKET_flush_data = global.SOCKET_flush_data = (sk, datas) ->
if !sk or sk.isClosed
return false
......@@ -1830,19 +1884,9 @@ class Room
# 网络连接
netRequestHandler = (client) ->
if !client.isWs
client.ip = client.remoteAddress or ''
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
connect_count = ROOM_connected_ip[client.ip] or 0
if !settings.modules.test_mode.no_connect_count_limit and !client.is_local
connect_count++
ROOM_connected_ip[client.ip] = connect_count
#log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
client.destroy()
return
client.physical_ip = client.remoteAddress or ""
if CLIENT_set_ip(client)
return
# server stand for the connection to ygopro server process
server = new net.Socket()
......@@ -1859,9 +1903,12 @@ netRequestHandler = (client) ->
return
room=ROOM_all[client.rid]
connect_count = ROOM_connected_ip[client.ip]
if connect_count > 0
if connect_count and connect_count > 0
connect_count--
ROOM_connected_ip[client.ip] = connect_count
if connect_count == 0
delete ROOM_connected_ip[client.ip]
else
ROOM_connected_ip[client.ip] = connect_count
client.isClosed = true
if settings.modules.heartbeat_detection.enabled
CLIENT_heartbeat_unregister(client)
......@@ -2021,6 +2068,19 @@ deck_name_match = global.deck_name_match = (deck_name, player_name) ->
# 功能模块
# return true to cancel a synchronous message
ygopro.ctos_follow 'EXTERNAL_ADDRESS', true, (buffer, info, client, server, datas)->
ip_uint = info.real_ip
if ip_uint == 0
return false
ip_parts = [
(ip_uint >>> 24) & 0xFF,
(ip_uint >>> 16) & 0xFF,
(ip_uint >>> 8) & 0xFF,
ip_uint & 0xFF
]
xff_ip = ip_parts.join('.')
return CLIENT_set_ip(client, xff_ip)
ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)->
# second PLAYER_INFO = attack
if client.name
......@@ -3029,7 +3089,7 @@ ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
deck_arena = deck_arena + 'custom'
#log.info "DECK LOG START", client.name, room.arena
if settings.modules.deck_log.local
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + client.ip.slice(7) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_')
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + toIpv4(client.ip) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_')
fs.writeFile settings.modules.deck_log.local + deck_name + '.ydk', deck_text, 'utf-8', (err) ->
if err
log.warn 'DECK SAVE ERROR', err
......@@ -3695,7 +3755,7 @@ if true
users: _.sortBy((for player in room.players when player.pos?
id: (-1).toString(),
name: player.name,
ip: if settings.modules.http.show_ip and pass_validated and !player.is_local then player.ip.slice(7) else null,
ip: if settings.modules.http.show_ip and pass_validated and !player.is_local then toIpv4(player.ip) else null,
status: if settings.modules.http.show_info and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and player.pos != 7 then (
score: room.scores[player.name_vpass],
lp: if player.lp? then player.lp else room.hostinfo.start_lp,
......@@ -3960,19 +4020,13 @@ if true
ip6addr = require('ip6addr')
neosRequestListener = (client, req) ->
physicalAddress = req.socket.remoteAddress
if settings.modules.neos.trusted_proxies.some((trusted) ->
cidr = if trusted.includes('/') then ip6addr.createCIDR(trusted) else ip6addr.createAddrRange(trusted, trusted)
return cidr.contains(physicalAddress)
)
ipHeader = req.headers[settings.modules.neos.trusted_proxy_header]
if ipHeader
client.ip = ipHeader.split(',')[0].trim()
if !client.ip
client.ip = physicalAddress
client.setTimeout = () -> true
client.destroy = () -> client.close()
client.isWs = true
client.physical_ip = req.socket.remoteAddress or ""
xff_ip = req.headers[settings.modules.neos.trusted_proxy_header]
if CLIENT_set_ip(client, xff_ip)
return
netRequestHandler(client)
......
// Generated by CoffeeScript 2.6.1
(function() {
// 标准库
var CLIENT_get_absolute_pos, CLIENT_get_authorize_key, CLIENT_get_kick_reconnect_target, CLIENT_get_partner, CLIENT_heartbeat_register, CLIENT_heartbeat_unregister, CLIENT_import_data, CLIENT_is_able_to_kick_reconnect, CLIENT_is_able_to_reconnect, CLIENT_is_banned_by_mc, CLIENT_is_player, CLIENT_kick, CLIENT_kick_reconnect, CLIENT_pre_reconnect, CLIENT_reconnect, CLIENT_reconnect_register, CLIENT_reconnect_unregister, CLIENT_send_pre_reconnect_info, CLIENT_send_reconnect_info, CLIENT_send_replays, CLIENT_send_replays_and_kick, Q, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_oppentlist, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, athleticChecker, auth, axios, badwordR, badwords, ban_user, bunyan, challonge, checkFileExists, createDirectoryIfNotExists, crypto, dataManager, deck_name_match, dialogues, disconnect_list, exec, execFile, extra_mode_list, fs, geoip, getDuelLogQueryFromQs, getSeedTimet, get_memory_usage, http, httpRequestListener, importOldConfig, import_datas, init, ip6addr, lflists, loadJSON, loadJSONAsync, loadLFList, loadRemoteData, load_dialogues, load_tips, log, long_resolve_cards, memory_usage, merge, moment, moment_long_ago_string, moment_now, moment_now_string, neosRequestListener, net, netRequestHandler, os, osu, path, qs, real_windbot_server_ip, release_disconnect, report_to_big_brother, request, roomlist, rooms_count, setting_change, setting_get, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, url, users_cache, util, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib;
var CLIENT_get_absolute_pos, CLIENT_get_authorize_key, CLIENT_get_kick_reconnect_target, CLIENT_get_partner, CLIENT_heartbeat_register, CLIENT_heartbeat_unregister, CLIENT_import_data, CLIENT_is_able_to_kick_reconnect, CLIENT_is_able_to_reconnect, CLIENT_is_banned_by_mc, CLIENT_is_player, CLIENT_kick, CLIENT_kick_reconnect, CLIENT_pre_reconnect, CLIENT_reconnect, CLIENT_reconnect_register, CLIENT_reconnect_unregister, CLIENT_send_pre_reconnect_info, CLIENT_send_reconnect_info, CLIENT_send_replays, CLIENT_send_replays_and_kick, CLIENT_set_ip, Q, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_oppentlist, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, athleticChecker, auth, axios, badwordR, badwords, ban_user, bunyan, challonge, checkFileExists, createDirectoryIfNotExists, crypto, dataManager, deck_name_match, dialogues, disconnect_list, exec, execFile, extra_mode_list, fs, geoip, getDuelLogQueryFromQs, getRealIp, getSeedTimet, get_memory_usage, http, httpRequestListener, importOldConfig, import_datas, init, ip6addr, isTrustedProxy, lflists, loadJSON, loadJSONAsync, loadLFList, loadRemoteData, load_dialogues, load_tips, log, long_resolve_cards, memory_usage, merge, moment, moment_long_ago_string, moment_now, moment_now_string, neosRequestListener, net, netRequestHandler, os, osu, path, qs, real_windbot_server_ip, release_disconnect, report_to_big_brother, request, roomlist, rooms_count, setting_change, setting_get, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, toIpv4, toIpv6, url, users_cache, util, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib;
net = require('net');
......@@ -419,6 +419,11 @@
settings.modules.hide_name = "start";
imported = true;
}
if (settings.modules.neos.trusted_proxies) {
settings.modules.trusted_proxies = settings.modules.neos.trusted_proxies;
delete settings.modules.neos.trusted_proxies;
imported = true;
}
//finish
keysFromEnv = Object.keys(process.env).filter((key) => {
return key.startsWith('SRVPRO_');
......@@ -1568,6 +1573,67 @@
CLIENT_kick(client);
};
toIpv4 = global.toIpv4 = function(ip) {
if (ip.startsWith('::ffff:')) {
return ip.slice(7);
}
return ip;
};
toIpv6 = global.toIpv6 = function(ip) {
if (/^(\d{1,3}\.){3}\d{1,3}$/.test(ip)) {
return '::ffff:' + ip;
}
return ip;
};
isTrustedProxy = global.isTrustedProxy = function(ip) {
return settings.modules.trusted_proxies.some(function(trusted) {
var cidr;
cidr = trusted.includes('/') ? ip6addr.createCIDR(trusted) : ip6addr.createAddrRange(trusted, trusted);
return cidr.contains(ip);
});
};
getRealIp = global.getRealIp = function(physical_ip, xff_ip) {
if (!xff_ip || xff_ip === physical_ip) {
return toIpv6(physical_ip);
}
if (isTrustedProxy(physical_ip)) {
return toIpv6(xff_ip.split(',')[0].trim());
}
log.warn(`Untrusted proxy detected: ${physical_ip} -> ${xff_ip}`);
return toIpv6(physical_ip);
};
CLIENT_set_ip = global.CLIENT_set_ip = function(client, xff_ip) {
var client_prev_ip, connect_count;
client_prev_ip = client.ip;
client.ip = getRealIp(client.physical_ip, xff_ip);
if (client_prev_ip === client.ip) {
return false;
}
if (client_prev_ip && ROOM_connected_ip[client_prev_ip] && ROOM_connected_ip[client_prev_ip] > 0) {
ROOM_connected_ip[client_prev_ip]--;
if (ROOM_connected_ip[client_prev_ip] <= 0) {
delete ROOM_connected_ip[client_prev_ip];
}
}
client.is_local = client.ip && (client.ip.includes('127.0.0.1') || client.ip.includes(real_windbot_server_ip));
connect_count = ROOM_connected_ip[client.ip] || 0;
if (!settings.modules.test_mode.no_connect_count_limit && !client.is_local && !isTrustedProxy(client.ip)) {
connect_count++;
}
ROOM_connected_ip[client.ip] = connect_count;
// log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
client.destroy();
return true;
}
return false;
};
SOCKET_flush_data = global.SOCKET_flush_data = async function(sk, datas) {
var buffer;
if (!sk || sk.isClosed) {
......@@ -2476,21 +2542,12 @@
// 网络连接
netRequestHandler = function(client) {
var closeHandler, connect_count, dataHandler, server;
var closeHandler, dataHandler, server;
if (!client.isWs) {
client.ip = client.remoteAddress || '';
}
client.is_local = client.ip && (client.ip.includes('127.0.0.1') || client.ip.includes(real_windbot_server_ip));
connect_count = ROOM_connected_ip[client.ip] || 0;
if (!settings.modules.test_mode.no_connect_count_limit && !client.is_local) {
connect_count++;
}
ROOM_connected_ip[client.ip] = connect_count;
//log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
client.destroy();
return;
client.physical_ip = client.remoteAddress || "";
if (CLIENT_set_ip(client)) {
return;
}
}
// server stand for the connection to ygopro server process
server = new net.Socket();
......@@ -2500,7 +2557,7 @@
// 释放处理
closeHandler = function(error) {
var room;
var connect_count, room;
//log.info "client closed", client.name, error, client.isClosed
//log.info "disconnect", client.ip, ROOM_connected_ip[client.ip]
if (client.isClosed) {
......@@ -2508,10 +2565,14 @@
}
room = ROOM_all[client.rid];
connect_count = ROOM_connected_ip[client.ip];
if (connect_count > 0) {
if (connect_count && connect_count > 0) {
connect_count--;
if (connect_count === 0) {
delete ROOM_connected_ip[client.ip];
} else {
ROOM_connected_ip[client.ip] = connect_count;
}
}
ROOM_connected_ip[client.ip] = connect_count;
client.isClosed = true;
if (settings.modules.heartbeat_detection.enabled) {
CLIENT_heartbeat_unregister(client);
......@@ -2717,6 +2778,17 @@
// 功能模块
// return true to cancel a synchronous message
ygopro.ctos_follow('EXTERNAL_ADDRESS', true, function(buffer, info, client, server, datas) {
var ip_parts, ip_uint, xff_ip;
ip_uint = info.real_ip;
if (ip_uint === 0) {
return false;
}
ip_parts = [(ip_uint >>> 24) & 0xFF, (ip_uint >>> 16) & 0xFF, (ip_uint >>> 8) & 0xFF, ip_uint & 0xFF];
xff_ip = ip_parts.join('.');
return CLIENT_set_ip(client, xff_ip);
});
ygopro.ctos_follow('PLAYER_INFO', true, async function(buffer, info, client, server, datas) {
var geo, lang, name, name_full, struct, vpass;
// second PLAYER_INFO = attack
......@@ -4047,7 +4119,7 @@
}
//log.info "DECK LOG START", client.name, room.arena
if (settings.modules.deck_log.local) {
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + client.ip.slice(7) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_');
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + toIpv4(client.ip) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_');
fs.writeFile(settings.modules.deck_log.local + deck_name + '.ydk', deck_text, 'utf-8', function(err) {
if (err) {
return log.warn('DECK SAVE ERROR', err);
......@@ -4982,7 +5054,7 @@
results.push({
id: (-1).toString(),
name: player.name,
ip: settings.modules.http.show_ip && pass_validated && !player.is_local ? player.ip.slice(7) : null,
ip: settings.modules.http.show_ip && pass_validated && !player.is_local ? toIpv4(player.ip) : null,
status: settings.modules.http.show_info && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && player.pos !== 7 ? {
score: room.scores[player.name_vpass],
lp: player.lp != null ? player.lp : room.hostinfo.start_lp,
......@@ -5287,21 +5359,7 @@
ip6addr = require('ip6addr');
neosRequestListener = function(client, req) {
var ipHeader, physicalAddress;
physicalAddress = req.socket.remoteAddress;
if (settings.modules.neos.trusted_proxies.some(function(trusted) {
var cidr;
cidr = trusted.includes('/') ? ip6addr.createCIDR(trusted) : ip6addr.createAddrRange(trusted, trusted);
return cidr.contains(physicalAddress);
})) {
ipHeader = req.headers[settings.modules.neos.trusted_proxy_header];
if (ipHeader) {
client.ip = ipHeader.split(',')[0].trim();
}
}
if (!client.ip) {
client.ip = physicalAddress;
}
var xff_ip;
client.setTimeout = function() {
return true;
};
......@@ -5309,6 +5367,11 @@
return client.close();
};
client.isWs = true;
client.physical_ip = req.socket.remoteAddress || "";
xff_ip = req.headers[settings.modules.neos.trusted_proxy_header];
if (CLIENT_set_ip(client, xff_ip)) {
return;
}
return netRequestHandler(client);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment