1
0
Fork 0

Telent protocol Nasal command

This commit is contained in:
James Turner 2018-02-03 13:44:26 +00:00
parent bd66bfb696
commit a05cdce793
6 changed files with 130 additions and 35 deletions

View file

@ -386,6 +386,26 @@ void fgResetIdleState()
fgRegisterIdleHandler( &fgIdleFunction );
}
void fgInitSecureMode()
{
bool secureMode = true;
if (Options::sharedInstance()->isOptionSet("allow-nasal-from-sockets")) {
SG_LOG(SG_GENERAL, SG_ALERT, "\n!! Network connections allowed to use Nasal !!\n"
"Network connections will be allowed full access to the simulator \n"
"including running arbitrary scripts. Ensure you have adequate security\n"
"(such as a firewall which blocks external connections).\n");
secureMode = false;
}
// it's by design that we overwrite any existing property tree value
// here - this prevents an aircraft or add-on setting the property
// value underneath us, eg in their -set.xml
SGPropertyNode_ptr secureFlag = fgGetNode("/sim/secure-flag", true);
secureFlag->setBoolValue(secureMode);
secureFlag->setAttributes(SGPropertyNode::READ |
SGPropertyNode::PRESERVE |
SGPropertyNode::PROTECTED);
}
static void upper_case_property(const char *name)
{
@ -470,8 +490,7 @@ int fgMainInit( int argc, char **argv )
}
std::string version(FLIGHTGEAR_VERSION);
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version "
<< version );
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version " << version );
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Build Type " << FG_BUILD_TYPE );
SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR);
SG_LOG( SG_GENERAL, SG_INFO, "Jenkins number/ID " << HUDSON_BUILD_NUMBER << ":"
@ -529,9 +548,11 @@ int fgMainInit( int argc, char **argv )
}
#else
if (showLauncher) {
SG_LOG(SG_GENERAL, SG_ALERT, "\n!Launcher requested, but FlightGear was compiled without Qt support!");
SG_LOG(SG_GENERAL, SG_ALERT, "\n!Launcher requested, but FlightGear was compiled without Qt support!\n");
}
#endif
fgInitSecureMode();
fgInitAircraftPaths(false);
configResult = fgInitAircraft(false);

View file

@ -1621,6 +1621,7 @@ struct OptionDesc {
{"language", true, OPTION_IGNORE, "", false, "", 0 },
{"console", false, OPTION_FUNC, "", false, "", fgOptConsole },
{"launcher", false, OPTION_IGNORE, "", false, "", 0 },
{"allow-nasal-from-sockets", false, OPTION_IGNORE, "", false, "", 0 },
{"disable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", false, "", 0 },
{"enable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", true, "", 0 },
{"renderer", true, OPTION_STRING, "/sim/rendering/rembrandt/renderer", false, "", 0 },

View file

@ -40,9 +40,11 @@
#include <iostream>
#include <errno.h>
#include <algorithm>
#include <functional>
#include <Main/globals.hxx>
#include <Viewer/viewmgr.hxx>
#include <Scripting/NasalSys.hxx>
#include <simgear/io/sg_netChat.hxx>
@ -73,13 +75,13 @@ class FGProps::PropsChannel : public simgear::NetChat, public SGPropertyChangeLi
/**
* Current property node name.
*/
string path;
string path= "/";
enum Mode {
PROMPT,
DATA
};
Mode mode;
Mode mode = PROMPT;
public:
/**
@ -136,22 +138,23 @@ private:
// callback implementations:
void subscribe(const ParameterList &p);
void unsubscribe(const ParameterList &p);
void beginNasal(const ParameterList &p);
FGProps* _owner = nullptr;
bool _colletingNasal = false;
};
/**
*
*/
FGProps::PropsChannel::PropsChannel(FGProps* owner)
: buffer(8192),
path("/"),
mode(PROMPT),
_owner(owner)
: buffer(8192)
, _owner(owner)
{
setTerminator( "\r\n" );
callback_map["subscribe"] = &PropsChannel::subscribe;
callback_map["unsubscribe"] = &PropsChannel::unsubscribe;
callback_map["nasal"] = &PropsChannel::beginNasal;
}
FGProps::PropsChannel::~PropsChannel()
@ -204,6 +207,18 @@ void FGProps::PropsChannel::unsubscribe(const ParameterList &param) {
}
}
void FGProps::PropsChannel::beginNasal(const ParameterList &param)
{
std::string eofMarker = "##EOF##";
if (param.size() > 1) {
if ((param.at(1) == "eof") && (param.size() >= 3)) {
eofMarker = param.at(2);
}
} // of optional argument parsing
_colletingNasal = true;
setTerminator(eofMarker);
}
//TODO: provide support for different types of subscriptions MODES ? (child added/removed, thesholds, min/max)
void FGProps::PropsChannel::valueChanged(SGPropertyNode* ptr)
@ -276,8 +291,35 @@ getValueTypeString( const SGPropertyNode *node )
void
FGProps::PropsChannel::foundTerminator()
{
if (_colletingNasal) {
std::string nasalSource = buffer.getData(); // make a copy
_colletingNasal = false;
setTerminator("\r\n");
buffer.remove(); // safe since we copied the source above
if (globals->get_props()->getBoolValue("sim/secure-flag", true) == true) {
SG_LOG(SG_IO, SG_ALERT, "Telnet connection trying to run Nasal, blocked it.\n"
"Run the simulator with --allow-nasal-from-sockets to allow this.");
error("Simulator running in secure mode, Nasal execution blocked.");
} else {
auto nasal = globals->get_subsystem<FGNasalSys>();
if (nasal) {
std::string errors, output;
bool ok = nasal->parseAndRunWithOutput(nasalSource, output, errors);
if (!ok) {
error("Nasal error" + errors);
} else if (!output.empty()) {
// success and we have output: push it
push(output.c_str());
}
}
}
return;
}
const char* cmd = buffer.getData();
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
SG_LOG( SG_IO, SG_DEBUG, "processing command = \"" << cmd << "\"" );
ParameterList tokens = simgear::strutils::split( cmd );
@ -506,12 +548,12 @@ FGProps::PropsChannel::foundTerminator()
} else if ( command == "prompt" ) {
mode = PROMPT;
} else if (callback_map.find(command) != callback_map.end() ) {
TelnetCallback t = callback_map[ command ];
if (t)
(this->*t) (tokens);
else
error("No matching callback found for command:"+command);
}
TelnetCallback t = callback_map[ command ];
if (t)
(this->*t) (tokens);
else
error("No matching callback found for command:"+command);
}
else if ( command == "seti" ) {
string value, tmp;
if (tokens.size() == 3) {
@ -604,7 +646,9 @@ setf <var> <val> alias for setd\r\n\
seti <var> <val> set Int <var> to a new <val>\r\n\
del <var> <nod> delete <nod> in <var>\r\n\
subscribe <var> subscribe to property changes \r\n\
unscubscribe <var> unscubscribe from property changes (var must be the property name/path used by subscribe)\r\n";
unsubscribe <var> unscubscribe from property changes (var must be the property name/path used by subscribe)\r\n\
nasal [EOF <marker>] execute arbitrary Nasal code (simulator must be running with Nasal allowed from sockets)\r\n\
";
push( msg );
}
}
@ -615,7 +659,7 @@ unscubscribe <var> unscubscribe from property changes (var must be the property
push( getTerminator() );
}
if ( mode == PROMPT ) {
if ( (mode == PROMPT) && !_colletingNasal) {
string prompt = node->getPath();
if (prompt.empty()) {
prompt = "/";

View file

@ -49,7 +49,7 @@ private:
/**
* Server port to listen on.
*/
int port;
int port = 5501;
simgear::NetChannelPoller poller;
std::vector<PropsChannel*> _activeChannels;
@ -69,22 +69,22 @@ public:
/**
* Start the telnet server.
*/
bool open();
bool open() override;
/**
* Process network activity.
*/
bool process();
bool process() override;
/**
*
*/
bool close();
bool close() override;
/**
* Accept a new client connection.
*/
void handleAccept();
void handleAccept() override;
void removeChannel(PropsChannel* channel);
};

View file

@ -298,20 +298,37 @@ FGNasalSys::~FGNasalSys()
nasalSys = 0;
}
bool FGNasalSys::parseAndRun(const char* sourceCode)
bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& output, std::string& errors)
{
naContext ctx = naNewContext();
naRef code = parse(ctx, "FGNasalSys::parseAndRun()", sourceCode,
strlen(sourceCode));
naRef code = parse(ctx, "FGNasalSys::parseAndRun()", source.c_str(),
source.size(), errors);
if(naIsNil(code)) {
naFreeContext(ctx);
return false;
}
callWithContext(ctx, code, 0, 0, naNil());
naRef result = callWithContext(ctx, code, 0, 0, naNil());
// if there was a result value, try to convert it to a string
// value.
if (!naIsNil(result)) {
naRef s = naStringValue(ctx, result);
if (!naIsNil(s)) {
output = naStr_data(s);
}
}
naFreeContext(ctx);
return true;
}
bool FGNasalSys::parseAndRun(const std::string& source)
{
std::string errors;
std::string output;
return parseAndRunWithOutput(source, output, errors);
}
#if 0
FGNasalScript* FGNasalSys::parseScript(const char* src, const char* name)
{
@ -1208,7 +1225,8 @@ bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
int argc, naRef* args)
{
naContext ctx = naNewContext();
naRef code = parse(ctx, fileName, src, len);
std::string errors;
naRef code = parse(ctx, fileName, src, len, errors);
if(naIsNil(code)) {
naFreeContext(ctx);
return false;
@ -1257,16 +1275,21 @@ naRef FGNasalSys::getModule(const char* moduleName)
return mod;
}
naRef FGNasalSys::parse(naContext ctx, const char* filename, const char* buf, int len)
naRef FGNasalSys::parse(naContext ctx, const char* filename,
const char* buf, int len,
std::string& errors)
{
int errLine = -1;
naRef srcfile = naNewString(ctx);
naStr_fromdata(srcfile, (char*)filename, strlen(filename));
naRef code = naParseCode(ctx, srcfile, 1, (char*)buf, len, &errLine);
if(naIsNil(code)) {
SG_LOG(SG_NASAL, SG_ALERT,
"Nasal parse error: " << naGetError(ctx) <<
" in "<< filename <<", line " << errLine);
std::ostringstream errorMessageStream;
errorMessageStream << "Nasal parse error: " << naGetError(ctx) <<
" in "<< filename <<", line " << errLine;
errors = errorMessageStream.str();
SG_LOG(SG_NASAL, SG_ALERT, errors);
return naNil();
}
@ -1281,7 +1304,8 @@ bool FGNasalSys::handleCommand( const char* moduleName,
SGPropertyNode* root)
{
naContext ctx = naNewContext();
naRef code = parse(ctx, fileName, src, strlen(src));
std::string errorMessage;
naRef code = parse(ctx, fileName, src, strlen(src), errorMessage);
if(naIsNil(code)) {
naFreeContext(ctx);
return false;

View file

@ -48,8 +48,12 @@ public:
// indicate successful execution. Does *not* return any Nasal
// values, because handling garbage-collected objects from C space
// is deep voodoo and violates the "simple hook" idea.
bool parseAndRun(const char* sourceCode);
bool parseAndRun(const std::string& source);
bool parseAndRunWithOutput(const std::string& source,
std::string& output,
std::string& errors);
// Slightly more complicated hook to get a handle to a precompiled
// Nasal script that can be invoked via a call() method. The
// caller is expected to delete the FGNasalScript returned from
@ -191,7 +195,8 @@ private:
void loadScriptDirectory(simgear::Dir nasalDir);
void addModule(std::string moduleName, simgear::PathList scripts);
static void logError(naContext);
naRef parse(naContext ctx, const char* filename, const char* buf, int len);
naRef parse(naContext ctx, const char* filename, const char* buf, int len,
std::string& errors);
naRef genPropsModule();
bool _inited;