add --cat-client behavior
This commit is contained in:
+306
-220
@@ -10,6 +10,7 @@
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "CatSession.hh"
|
||||
#include "DNSServer.hh"
|
||||
#include "IPStackSimulator.hh"
|
||||
#include "Loggers.hh"
|
||||
@@ -207,6 +208,59 @@ void drop_privileges(const string& username) {
|
||||
|
||||
|
||||
|
||||
void print_usage() {
|
||||
fputs("\
|
||||
newserv - a Phantasy Star Online Swiss Army knife\n\
|
||||
\n\
|
||||
Usage:\n\
|
||||
newserv [options]\n\
|
||||
\n\
|
||||
With no options, newserv runs in server mode. PSO clients can connect normally,\n\
|
||||
join lobbies, play games, and use the proxy server. See README.md and\n\
|
||||
system/config.json for more information.\n\
|
||||
\n\
|
||||
When options are given, newserv will do things other than running the server.\n\
|
||||
Specifically:\n\
|
||||
--decrypt-data\n\
|
||||
--encrypt-data\n\
|
||||
If either of these options is given, newserv will read from stdin and\n\
|
||||
write the encrypted or decrypted result to stdout. By default, PSO GC\n\
|
||||
encryption is used, but this can be overridden with --pc or --bb. The\n\
|
||||
--seed option specifies the ecryption seed (4 hex bytes for PC or GC, or\n\
|
||||
48 hex bytes for BB). For BB, the --key option is requires as well, and\n\
|
||||
should refer to a .nsk file in system/blueburst/keys (without the\n\
|
||||
directory or .nsk extension).\n\
|
||||
--decode-sjis\n\
|
||||
If this option is given, newserv applies its text decoding algorithm to\n\
|
||||
the data on stding, producing little-endian UTF-16 data on stdout.\n\
|
||||
--decode-gci=FILENAME\n\
|
||||
--decode-dlq=FILENAME\n\
|
||||
--decode-qst=FILENAME\n\
|
||||
If any of these options are given, newserv will decode the given quest\n\
|
||||
file into a compressed, unencrypted .bin or .dat file (or in the case of)\n\
|
||||
--decode-qst, both a .bin and a .dat file).\n\
|
||||
--cat-client=ADDR:PORT\n\
|
||||
If this option is given, newserv will behave as if it's a PSO client, and\n\
|
||||
will connect to the given server. It will then print all the received\n\
|
||||
commands to stdout, and forward any commands typed into stdin to the\n\
|
||||
remote server. It is assumed that the input and output are terminals, so\n\
|
||||
all commands are hex-encoded. The --patch, --dc, --pc, --gc, and --bb\n\
|
||||
options can be used to select the command format end encryption.\n\
|
||||
--replay-log=FILENAME\n\
|
||||
This option makes newserv replay terminal log as if it were a client\n\
|
||||
session. This is used for regression testing, to make sure client\n\
|
||||
sessions are repeatable and code changes don\'t affect existing (working)\n\
|
||||
functionality.\n\
|
||||
\n\
|
||||
A few options apply to multiple modes described above:\n\
|
||||
--parse-data\n\
|
||||
For modes that take input on stdin, parse the input as a hex string\n\
|
||||
before encrypting/decoding/etc.\n\
|
||||
--config=FILENAME\n\
|
||||
Use this file instead of system/config.json.\n\
|
||||
", stderr);
|
||||
}
|
||||
|
||||
enum class Behavior {
|
||||
RUN_SERVER = 0,
|
||||
DECRYPT_DATA,
|
||||
@@ -214,12 +268,7 @@ enum class Behavior {
|
||||
DECODE_QUEST_FILE,
|
||||
DECODE_SJIS,
|
||||
REPLAY_LOG,
|
||||
};
|
||||
|
||||
enum class EncryptionType {
|
||||
PC = 0,
|
||||
GC,
|
||||
BB,
|
||||
CAT_CLIENT,
|
||||
};
|
||||
|
||||
enum class QuestFileFormat {
|
||||
@@ -230,7 +279,7 @@ enum class QuestFileFormat {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Behavior behavior = Behavior::RUN_SERVER;
|
||||
EncryptionType crypt_type = EncryptionType::PC;
|
||||
GameVersion cli_version = GameVersion::GC;
|
||||
QuestFileFormat quest_file_type = QuestFileFormat::GCI;
|
||||
string quest_filename;
|
||||
string seed;
|
||||
@@ -238,8 +287,12 @@ int main(int argc, char** argv) {
|
||||
const char* config_filename = "system/config.json";
|
||||
bool parse_data = false;
|
||||
const char* replay_log_filename = nullptr;
|
||||
struct sockaddr_storage cat_client_remote;
|
||||
for (int x = 1; x < argc; x++) {
|
||||
if (!strcmp(argv[x], "--decrypt-data")) {
|
||||
if (!strcmp(argv[x], "--help")) {
|
||||
print_usage();
|
||||
return 0;
|
||||
} else if (!strcmp(argv[x], "--decrypt-data")) {
|
||||
behavior = Behavior::DECRYPT_DATA;
|
||||
} else if (!strcmp(argv[x], "--encrypt-data")) {
|
||||
behavior = Behavior::ENCRYPT_DATA;
|
||||
@@ -257,12 +310,19 @@ int main(int argc, char** argv) {
|
||||
behavior = Behavior::DECODE_QUEST_FILE;
|
||||
quest_file_type = QuestFileFormat::QST;
|
||||
quest_filename = &argv[x][13];
|
||||
} else if (!strncmp(argv[x], "--cat-client=", 13)) {
|
||||
behavior = Behavior::CAT_CLIENT;
|
||||
cat_client_remote = make_sockaddr_storage(parse_netloc(&argv[x][13])).first;
|
||||
} else if (!strcmp(argv[x], "--patch")) {
|
||||
cli_version = GameVersion::PATCH;
|
||||
} else if (!strcmp(argv[x], "--dc")) {
|
||||
cli_version = GameVersion::DC;
|
||||
} else if (!strcmp(argv[x], "--pc")) {
|
||||
crypt_type = EncryptionType::PC;
|
||||
cli_version = GameVersion::PC;
|
||||
} else if (!strcmp(argv[x], "--gc")) {
|
||||
crypt_type = EncryptionType::GC;
|
||||
cli_version = GameVersion::GC;
|
||||
} else if (!strcmp(argv[x], "--bb")) {
|
||||
crypt_type = EncryptionType::BB;
|
||||
cli_version = GameVersion::BB;
|
||||
} else if (!strncmp(argv[x], "--seed=", 7)) {
|
||||
seed = &argv[x][7];
|
||||
} else if (!strncmp(argv[x], "--key=", 6)) {
|
||||
@@ -279,224 +339,250 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (behavior == Behavior::DECRYPT_DATA || behavior == Behavior::ENCRYPT_DATA) {
|
||||
shared_ptr<PSOEncryption> crypt;
|
||||
if (crypt_type == EncryptionType::PC) {
|
||||
crypt.reset(new PSOPCEncryption(stoul(seed, nullptr, 16)));
|
||||
} else if (crypt_type == EncryptionType::GC) {
|
||||
crypt.reset(new PSOGCEncryption(stoul(seed, nullptr, 16)));
|
||||
} else if (crypt_type == EncryptionType::BB) {
|
||||
seed = parse_data_string(seed);
|
||||
auto key = load_object_file<PSOBBEncryption::KeyFile>(
|
||||
"system/blueburst/keys/" + key_file_name + ".nsk");
|
||||
crypt.reset(new PSOBBEncryption(key, seed.data(), seed.size()));
|
||||
} else {
|
||||
throw logic_error("invalid encryption type");
|
||||
}
|
||||
|
||||
string data = read_all(stdin);
|
||||
if (parse_data) {
|
||||
data = parse_data_string(data);
|
||||
}
|
||||
|
||||
if (behavior == Behavior::DECRYPT_DATA) {
|
||||
crypt->decrypt(data.data(), data.size());
|
||||
} else if (behavior == Behavior::ENCRYPT_DATA) {
|
||||
crypt->encrypt(data.data(), data.size());
|
||||
} else {
|
||||
throw logic_error("invalid behavior");
|
||||
}
|
||||
|
||||
if (isatty(fileno(stdout))) {
|
||||
print_data(stdout, data);
|
||||
} else {
|
||||
fwritex(stdout, data);
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
return 0;
|
||||
|
||||
} else if (behavior == Behavior::DECODE_QUEST_FILE) {
|
||||
if (quest_file_type == QuestFileFormat::GCI) {
|
||||
save_file(quest_filename + ".dec", Quest::decode_gci(quest_filename));
|
||||
} else if (quest_file_type == QuestFileFormat::DLQ) {
|
||||
save_file(quest_filename + ".dec", Quest::decode_dlq(quest_filename));
|
||||
} else if (quest_file_type == QuestFileFormat::QST) {
|
||||
auto data = Quest::decode_qst(quest_filename);
|
||||
save_file(quest_filename + ".bin", data.first);
|
||||
save_file(quest_filename + ".dat", data.second);
|
||||
} else {
|
||||
throw logic_error("invalid quest file format");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
} else if (behavior == Behavior::DECODE_SJIS) {
|
||||
string data = read_all(stdin);
|
||||
if (parse_data) {
|
||||
data = parse_data_string(data);
|
||||
}
|
||||
auto decoded = decode_sjis(data);
|
||||
print_data(stderr, decoded.data(), decoded.size() * sizeof(decoded[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (isatty(fileno(stderr))) {
|
||||
use_terminal_colors = true;
|
||||
}
|
||||
|
||||
shared_ptr<ServerState> state(new ServerState());
|
||||
|
||||
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
|
||||
|
||||
config_log.info("Reading network addresses");
|
||||
state->all_addresses = get_local_addresses();
|
||||
for (const auto& it : state->all_addresses) {
|
||||
string addr_str = string_for_address(it.second);
|
||||
config_log.info("Found interface: %s = %s", it.first.c_str(), addr_str.c_str());
|
||||
}
|
||||
|
||||
config_log.info("Loading configuration");
|
||||
auto config_json = JSONObject::parse(load_file(config_filename));
|
||||
populate_state_from_config(state, config_json);
|
||||
|
||||
config_log.info("Loading license list");
|
||||
state->license_manager.reset(new LicenseManager("system/licenses.nsi"));
|
||||
|
||||
config_log.info("Loading battle parameters");
|
||||
state->battle_params.reset(new BattleParamTable("system/blueburst/BattleParamEntry"));
|
||||
|
||||
config_log.info("Loading level table");
|
||||
state->level_table.reset(new LevelTable("system/blueburst/PlyLevelTbl.prs", true));
|
||||
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
state->ep3_data_index.reset(new Ep3DataIndex("system/ep3"));
|
||||
|
||||
config_log.info("Collecting quest metadata");
|
||||
state->quest_index.reset(new QuestIndex("system/quests"));
|
||||
|
||||
if (!replay_log_filename) {
|
||||
config_log.info("Compiling client functions");
|
||||
state->function_code_index.reset(new FunctionCodeIndex("system/ppc"));
|
||||
config_log.info("Loading DOL files");
|
||||
state->dol_file_index.reset(new DOLFileIndex("system/dol"));
|
||||
} else {
|
||||
state->function_code_index.reset(new FunctionCodeIndex());
|
||||
state->dol_file_index.reset(new DOLFileIndex());
|
||||
}
|
||||
|
||||
config_log.info("Creating menus");
|
||||
state->create_menus(config_json);
|
||||
|
||||
if (replay_log_filename) {
|
||||
state->allow_saving = false;
|
||||
state->license_manager->set_autosave(false);
|
||||
config_log.info("Saving disabled because this is a replay session");
|
||||
}
|
||||
|
||||
shared_ptr<DNSServer> dns_server;
|
||||
if (state->dns_server_port && !replay_log_filename) {
|
||||
config_log.info("Starting DNS server");
|
||||
dns_server.reset(new DNSServer(base, state->local_address,
|
||||
state->external_address));
|
||||
dns_server->listen("", state->dns_server_port);
|
||||
} else {
|
||||
config_log.info("DNS server is disabled");
|
||||
}
|
||||
|
||||
shared_ptr<Shell> shell;
|
||||
shared_ptr<ReplaySession> replay_session;
|
||||
shared_ptr<IPStackSimulator> ip_stack_simulator;
|
||||
if (behavior == Behavior::REPLAY_LOG) {
|
||||
config_log.info("Starting proxy server");
|
||||
state->proxy_server.reset(new ProxyServer(base, state));
|
||||
config_log.info("Starting game server");
|
||||
state->game_server.reset(new Server(base, state));
|
||||
|
||||
auto f = fopen_unique(replay_log_filename, "rt");
|
||||
replay_session.reset(new ReplaySession(base, f.get(), state));
|
||||
replay_session->start();
|
||||
|
||||
} else if (behavior == Behavior::RUN_SERVER) {
|
||||
config_log.info("Opening sockets");
|
||||
for (const auto& it : state->name_to_port_config) {
|
||||
const auto& pc = it.second;
|
||||
if (pc->behavior == ServerBehavior::PROXY_SERVER) {
|
||||
if (!state->proxy_server.get()) {
|
||||
config_log.info("Starting proxy server");
|
||||
state->proxy_server.reset(new ProxyServer(base, state));
|
||||
switch (behavior) {
|
||||
case Behavior::DECRYPT_DATA:
|
||||
case Behavior::ENCRYPT_DATA: {
|
||||
shared_ptr<PSOEncryption> crypt;
|
||||
switch (cli_version) {
|
||||
case GameVersion::PATCH:
|
||||
case GameVersion::DC:
|
||||
case GameVersion::PC:
|
||||
crypt.reset(new PSOPCEncryption(stoul(seed, nullptr, 16)));
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
crypt.reset(new PSOGCEncryption(stoul(seed, nullptr, 16)));
|
||||
break;
|
||||
case GameVersion::BB: {
|
||||
seed = parse_data_string(seed);
|
||||
auto key = load_object_file<PSOBBEncryption::KeyFile>(
|
||||
"system/blueburst/keys/" + key_file_name + ".nsk");
|
||||
crypt.reset(new PSOBBEncryption(key, seed.data(), seed.size()));
|
||||
break;
|
||||
}
|
||||
if (state->proxy_server.get()) {
|
||||
// For PC and GC, proxy sessions are dynamically created when a client
|
||||
// picks a destination from the menu. For patch and BB clients, there's
|
||||
// no way to ask the client which destination they want, so only one
|
||||
// destination is supported, and we have to manually specify the
|
||||
// destination netloc here.
|
||||
if (pc->version == GameVersion::PATCH) {
|
||||
struct sockaddr_storage ss = make_sockaddr_storage(
|
||||
state->proxy_destination_patch.first,
|
||||
state->proxy_destination_patch.second).first;
|
||||
state->proxy_server->listen(pc->port, pc->version, &ss);
|
||||
} else if (pc->version == GameVersion::BB) {
|
||||
struct sockaddr_storage ss = make_sockaddr_storage(
|
||||
state->proxy_destination_bb.first,
|
||||
state->proxy_destination_bb.second).first;
|
||||
state->proxy_server->listen(pc->port, pc->version, &ss);
|
||||
default:
|
||||
throw logic_error("invalid game version");
|
||||
}
|
||||
|
||||
string data = read_all(stdin);
|
||||
if (parse_data) {
|
||||
data = parse_data_string(data);
|
||||
}
|
||||
|
||||
if (behavior == Behavior::DECRYPT_DATA) {
|
||||
crypt->decrypt(data.data(), data.size());
|
||||
} else if (behavior == Behavior::ENCRYPT_DATA) {
|
||||
crypt->encrypt(data.data(), data.size());
|
||||
} else {
|
||||
throw logic_error("invalid behavior");
|
||||
}
|
||||
|
||||
if (isatty(fileno(stdout))) {
|
||||
print_data(stdout, data);
|
||||
} else {
|
||||
fwritex(stdout, data);
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::DECODE_QUEST_FILE:
|
||||
if (quest_file_type == QuestFileFormat::GCI) {
|
||||
save_file(quest_filename + ".dec", Quest::decode_gci(quest_filename));
|
||||
} else if (quest_file_type == QuestFileFormat::DLQ) {
|
||||
save_file(quest_filename + ".dec", Quest::decode_dlq(quest_filename));
|
||||
} else if (quest_file_type == QuestFileFormat::QST) {
|
||||
auto data = Quest::decode_qst(quest_filename);
|
||||
save_file(quest_filename + ".bin", data.first);
|
||||
save_file(quest_filename + ".dat", data.second);
|
||||
} else {
|
||||
throw logic_error("invalid quest file format");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Behavior::DECODE_SJIS: {
|
||||
string data = read_all(stdin);
|
||||
if (parse_data) {
|
||||
data = parse_data_string(data);
|
||||
}
|
||||
auto decoded = decode_sjis(data);
|
||||
print_data(stderr, decoded.data(), decoded.size() * sizeof(decoded[0]));
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::CAT_CLIENT: {
|
||||
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
|
||||
CatSession session(base, cat_client_remote, cli_version);
|
||||
event_base_dispatch(base.get());
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::REPLAY_LOG:
|
||||
case Behavior::RUN_SERVER: {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (isatty(fileno(stderr))) {
|
||||
use_terminal_colors = true;
|
||||
}
|
||||
|
||||
shared_ptr<ServerState> state(new ServerState());
|
||||
|
||||
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
|
||||
|
||||
config_log.info("Reading network addresses");
|
||||
state->all_addresses = get_local_addresses();
|
||||
for (const auto& it : state->all_addresses) {
|
||||
string addr_str = string_for_address(it.second);
|
||||
config_log.info("Found interface: %s = %s", it.first.c_str(), addr_str.c_str());
|
||||
}
|
||||
|
||||
config_log.info("Loading configuration");
|
||||
auto config_json = JSONObject::parse(load_file(config_filename));
|
||||
populate_state_from_config(state, config_json);
|
||||
|
||||
config_log.info("Loading license list");
|
||||
state->license_manager.reset(new LicenseManager("system/licenses.nsi"));
|
||||
|
||||
config_log.info("Loading battle parameters");
|
||||
state->battle_params.reset(new BattleParamTable("system/blueburst/BattleParamEntry"));
|
||||
|
||||
config_log.info("Loading level table");
|
||||
state->level_table.reset(new LevelTable("system/blueburst/PlyLevelTbl.prs", true));
|
||||
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
state->ep3_data_index.reset(new Ep3DataIndex("system/ep3"));
|
||||
|
||||
config_log.info("Collecting quest metadata");
|
||||
state->quest_index.reset(new QuestIndex("system/quests"));
|
||||
|
||||
if (!replay_log_filename) {
|
||||
config_log.info("Compiling client functions");
|
||||
state->function_code_index.reset(new FunctionCodeIndex("system/ppc"));
|
||||
config_log.info("Loading DOL files");
|
||||
state->dol_file_index.reset(new DOLFileIndex("system/dol"));
|
||||
} else {
|
||||
state->function_code_index.reset(new FunctionCodeIndex());
|
||||
state->dol_file_index.reset(new DOLFileIndex());
|
||||
}
|
||||
|
||||
config_log.info("Creating menus");
|
||||
state->create_menus(config_json);
|
||||
|
||||
if (replay_log_filename) {
|
||||
state->allow_saving = false;
|
||||
state->license_manager->set_autosave(false);
|
||||
config_log.info("Saving disabled because this is a replay session");
|
||||
}
|
||||
|
||||
shared_ptr<DNSServer> dns_server;
|
||||
if (state->dns_server_port && !replay_log_filename) {
|
||||
config_log.info("Starting DNS server");
|
||||
dns_server.reset(new DNSServer(base, state->local_address,
|
||||
state->external_address));
|
||||
dns_server->listen("", state->dns_server_port);
|
||||
} else {
|
||||
config_log.info("DNS server is disabled");
|
||||
}
|
||||
|
||||
shared_ptr<Shell> shell;
|
||||
shared_ptr<ReplaySession> replay_session;
|
||||
shared_ptr<IPStackSimulator> ip_stack_simulator;
|
||||
if (behavior == Behavior::REPLAY_LOG) {
|
||||
config_log.info("Starting proxy server");
|
||||
state->proxy_server.reset(new ProxyServer(base, state));
|
||||
config_log.info("Starting game server");
|
||||
state->game_server.reset(new Server(base, state));
|
||||
|
||||
auto f = fopen_unique(replay_log_filename, "rt");
|
||||
replay_session.reset(new ReplaySession(base, f.get(), state));
|
||||
replay_session->start();
|
||||
|
||||
} else if (behavior == Behavior::RUN_SERVER) {
|
||||
config_log.info("Opening sockets");
|
||||
for (const auto& it : state->name_to_port_config) {
|
||||
const auto& pc = it.second;
|
||||
if (pc->behavior == ServerBehavior::PROXY_SERVER) {
|
||||
if (!state->proxy_server.get()) {
|
||||
config_log.info("Starting proxy server");
|
||||
state->proxy_server.reset(new ProxyServer(base, state));
|
||||
}
|
||||
if (state->proxy_server.get()) {
|
||||
// For PC and GC, proxy sessions are dynamically created when a client
|
||||
// picks a destination from the menu. For patch and BB clients, there's
|
||||
// no way to ask the client which destination they want, so only one
|
||||
// destination is supported, and we have to manually specify the
|
||||
// destination netloc here.
|
||||
if (pc->version == GameVersion::PATCH) {
|
||||
struct sockaddr_storage ss = make_sockaddr_storage(
|
||||
state->proxy_destination_patch.first,
|
||||
state->proxy_destination_patch.second).first;
|
||||
state->proxy_server->listen(pc->port, pc->version, &ss);
|
||||
} else if (pc->version == GameVersion::BB) {
|
||||
struct sockaddr_storage ss = make_sockaddr_storage(
|
||||
state->proxy_destination_bb.first,
|
||||
state->proxy_destination_bb.second).first;
|
||||
state->proxy_server->listen(pc->port, pc->version, &ss);
|
||||
} else {
|
||||
state->proxy_server->listen(pc->port, pc->version);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state->proxy_server->listen(pc->port, pc->version);
|
||||
if (!state->game_server.get()) {
|
||||
config_log.info("Starting game server");
|
||||
state->game_server.reset(new Server(base, state));
|
||||
}
|
||||
string spec = string_printf("T-%hu-%s-%s-%s",
|
||||
pc->port, name_for_version(pc->version), pc->name.c_str(),
|
||||
name_for_server_behavior(pc->behavior));
|
||||
state->game_server->listen(spec, "", pc->port, pc->version, pc->behavior);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!state->game_server.get()) {
|
||||
config_log.info("Starting game server");
|
||||
state->game_server.reset(new Server(base, state));
|
||||
|
||||
if (!state->ip_stack_addresses.empty()) {
|
||||
config_log.info("Starting IP stack simulator");
|
||||
ip_stack_simulator.reset(new IPStackSimulator(base, state));
|
||||
for (const auto& it : state->ip_stack_addresses) {
|
||||
auto netloc = parse_netloc(it);
|
||||
ip_stack_simulator->listen(netloc.first, netloc.second);
|
||||
}
|
||||
}
|
||||
string spec = string_printf("T-%hu-%s-%s-%s",
|
||||
pc->port, name_for_version(pc->version), pc->name.c_str(),
|
||||
name_for_server_behavior(pc->behavior));
|
||||
state->game_server->listen(spec, "", pc->port, pc->version, pc->behavior);
|
||||
|
||||
} else {
|
||||
throw logic_error("invalid behavior");
|
||||
}
|
||||
|
||||
if (!state->username.empty()) {
|
||||
config_log.info("Switching to user %s", state->username.c_str());
|
||||
drop_privileges(state->username);
|
||||
}
|
||||
|
||||
bool should_run_shell;
|
||||
if (state->run_shell_behavior == ServerState::RunShellBehavior::DEFAULT) {
|
||||
should_run_shell = isatty(fileno(stdin));
|
||||
} else if (state->run_shell_behavior == ServerState::RunShellBehavior::ALWAYS) {
|
||||
should_run_shell = true;
|
||||
} else {
|
||||
should_run_shell = false;
|
||||
}
|
||||
if (should_run_shell) {
|
||||
should_run_shell = !replay_session.get();
|
||||
}
|
||||
if (should_run_shell) {
|
||||
shell.reset(new ServerShell(base, state));
|
||||
}
|
||||
|
||||
config_log.info("Ready");
|
||||
event_base_dispatch(base.get());
|
||||
|
||||
config_log.info("Normal shutdown");
|
||||
state->proxy_server.reset(); // Break reference cycle
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state->ip_stack_addresses.empty()) {
|
||||
config_log.info("Starting IP stack simulator");
|
||||
ip_stack_simulator.reset(new IPStackSimulator(base, state));
|
||||
for (const auto& it : state->ip_stack_addresses) {
|
||||
auto netloc = parse_netloc(it);
|
||||
ip_stack_simulator->listen(netloc.first, netloc.second);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
throw logic_error("invalid behavior");
|
||||
default:
|
||||
throw logic_error("invalid behavior");
|
||||
}
|
||||
|
||||
if (!state->username.empty()) {
|
||||
config_log.info("Switching to user %s", state->username.c_str());
|
||||
drop_privileges(state->username);
|
||||
}
|
||||
|
||||
bool should_run_shell;
|
||||
if (state->run_shell_behavior == ServerState::RunShellBehavior::DEFAULT) {
|
||||
should_run_shell = isatty(fileno(stdin));
|
||||
} else if (state->run_shell_behavior == ServerState::RunShellBehavior::ALWAYS) {
|
||||
should_run_shell = true;
|
||||
} else {
|
||||
should_run_shell = false;
|
||||
}
|
||||
if (should_run_shell) {
|
||||
should_run_shell = !replay_session.get();
|
||||
}
|
||||
if (should_run_shell) {
|
||||
shell.reset(new ServerShell(base, state));
|
||||
}
|
||||
|
||||
config_log.info("Ready");
|
||||
event_base_dispatch(base.get());
|
||||
|
||||
config_log.info("Normal shutdown");
|
||||
state->proxy_server.reset(); // Break reference cycle
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user