/* * ZeroTier One - Global Peer to Peer Ethernet * Copyright (C) 2011-2014 ZeroTier Networks LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * -- * * ZeroTier may be used and distributed under the terms of the GPLv3, which * are available at: http://www.gnu.org/licenses/gpl-3.0.html * * If you would like to embed ZeroTier into a commercial application or * redistribute it in a modified binary form, please contact ZeroTier Networks * LLC. Start here: http://www.zerotier.com/ */ #include #include #include #include "../node/Constants.hpp" #include "NodeControlService.hpp" #include "NodeControlClient.hpp" #include "../node/Node.hpp" #include "../node/Utils.hpp" namespace ZeroTier { NodeControlService::NodeControlService(Node *node,const char *authToken) : _node(node), _listener((IpcListener *)0), _authToken(authToken), _running(true), _thread(Thread::start(this)) { } NodeControlService::~NodeControlService() { _running = false; Thread::join(_thread); { Mutex::Lock _l(_connections_m); for(std::map< IpcConnection *,bool >::iterator c(_connections.begin());c!=_connections.end();++c) delete c->first; _connections.clear(); } delete _listener; } void NodeControlService::threadMain() throw() { char tmp[1024]; try { while (_running) { if (!_node->running()) { if (_node->started()) break; } else if ((_node->initialized())&&(_node->address())) { Utils::snprintf(tmp,sizeof(tmp),"%s%.10llx",ZT_IPC_ENDPOINT_BASE,(unsigned long long)_node->address()); _listener = new IpcListener(tmp,&_CBcommandHandler,this); break; } Thread::sleep(100); // wait for Node to start } } catch ( ... ) { delete _listener; _listener = (IpcListener *)0; } } void NodeControlService::_CBcommandHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *commandLine) { if (!((NodeControlService *)arg)->_running) return; if ((!commandLine)||(!commandLine[0])) return; switch(event) { case IpcConnection::IPC_EVENT_COMMAND: { ((NodeControlService *)arg)->_doCommand(ipcc,commandLine); } break; case IpcConnection::IPC_EVENT_NEW_CONNECTION: { Mutex::Lock _l(((NodeControlService *)arg)->_connections_m); ((NodeControlService *)arg)->_connections[ipcc] = false; // not yet authenticated } break; case IpcConnection::IPC_EVENT_CONNECTION_CLOSED: { Mutex::Lock _l(((NodeControlService *)arg)->_connections_m); ((NodeControlService *)arg)->_connections.erase(ipcc); delete ipcc; } break; } } void NodeControlService::_doCommand(IpcConnection *ipcc,const char *commandLine) { std::vector r; std::vector cmd(Utils::split(commandLine,"\r\n \t","\\","'")); if ((cmd.empty())||(cmd[0] == "help")) { ipcc->printf("200 help help"ZT_EOL_S); ipcc->printf("200 help auth "ZT_EOL_S); ipcc->printf("200 help info"ZT_EOL_S); ipcc->printf("200 help listpeers"ZT_EOL_S); ipcc->printf("200 help listnetworks"ZT_EOL_S); ipcc->printf("200 help join "ZT_EOL_S); ipcc->printf("200 help leave "ZT_EOL_S); ipcc->printf("200 help terminate []"ZT_EOL_S); ipcc->printf("200 help updatecheck"ZT_EOL_S); } else if (cmd[0] == "auth") { if ((cmd.size() > 1)&&(_authToken.length() > 0)&&(_authToken == cmd[1])) { Mutex::Lock _l(_connections_m); _connections[ipcc] = true; ipcc->printf("200 auth OK"ZT_EOL_S); } else ipcc->printf("403 auth failed"ZT_EOL_S); } else { { Mutex::Lock _l(_connections_m); if (!_connections[ipcc]) { ipcc->printf("403 %s unauthorized"ZT_EOL_S"."ZT_EOL_S,cmd[0].c_str()); return; } } if (cmd[0] == "info") { ipcc->printf("200 info %.10llx %s %s"ZT_EOL_S,_node->address(),(_node->online() ? "ONLINE" : "OFFLINE"),Node::versionString()); } else if (cmd[0] == "listpeers") { ipcc->printf("200 listpeers "ZT_EOL_S); ZT1_Node_PeerList *pl = _node->listPeers(); if (pl) { for(unsigned int i=0;inumPeers;++i) { ipcc->printf("200 listpeers %.10llx ",(unsigned long long)pl->peers[i].rawAddress); if (pl->peers[i].numPaths == 0) ipcc->printf("-"); else { for(unsigned int j=0;jpeers[i].numPaths;++j) { if (j > 0) ipcc->printf(","); switch(pl->peers[i].paths[j].type) { default: ipcc->printf("unknown;"); break; case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_UDP: ipcc->printf("udp;"); break; case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_TCP_OUT: ipcc->printf("tcp_out;"); break; case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_TCP_IN: ipcc->printf("tcp_in;"); break; case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_ETHERNET: ipcc->printf("eth;"); break; } ipcc->printf("%s/%d;%ld;%ld;%ld;%s", pl->peers[i].paths[j].address.ascii, (int)pl->peers[i].paths[j].address.port, pl->peers[i].paths[j].lastSend, pl->peers[i].paths[j].lastReceive, pl->peers[i].paths[j].lastPing, (pl->peers[i].paths[j].fixed ? "fixed" : (pl->peers[i].paths[j].active ? "active" : "inactive"))); } } ipcc->printf(" %u %s"ZT_EOL_S,pl->peers[i].latency,(pl->peers[i].remoteVersion[0]) ? pl->peers[i].remoteVersion : "-"); } _node->freeQueryResult(pl); } } else if (cmd[0] == "listnetworks") { ipcc->printf("200 listnetworks "ZT_EOL_S); ZT1_Node_NetworkList *nl = _node->listNetworks(); if (nl) { for(unsigned int i=0;inumNetworks;++i) { ipcc->printf("200 listnetworks %s %s %s %s %ld %s %s ", nl->networks[i].nwidHex, nl->networks[i].name, nl->networks[i].macStr, nl->networks[i].statusStr, nl->networks[i].configAge, (nl->networks[i].isPrivate ? "private" : "public"), nl->networks[i].device); if (nl->networks[i].numIps > 0) { for(unsigned int j=0;jnetworks[i].numIps;++j) { if (j > 0) ipcc->printf(","); ipcc->printf("%s/%d",nl->networks[i].ips[j].ascii,(int)nl->networks[i].ips[j].port); } } else ipcc->printf("-"); ipcc->printf(ZT_EOL_S); } _node->freeQueryResult(nl); } } else if (cmd[0] == "join") { if (cmd.size() > 1) { uint64_t nwid = Utils::hexStrToU64(cmd[1].c_str()); _node->join(nwid); ipcc->printf("200 join %.16llx OK"ZT_EOL_S,(unsigned long long)nwid); } else { ipcc->printf("400 join requires a network ID (>0) in hexadecimal format"ZT_EOL_S); } } else if (cmd[0] == "leave") { if (cmd.size() > 1) { uint64_t nwid = Utils::hexStrToU64(cmd[1].c_str()); _node->leave(nwid); ipcc->printf("200 leave %.16llx OK"ZT_EOL_S,(unsigned long long)nwid); } else { ipcc->printf("400 leave requires a network ID (>0) in hexadecimal format"ZT_EOL_S); } } else if (cmd[0] == "terminate") { if (cmd.size() > 1) _node->terminate(Node::NODE_NORMAL_TERMINATION,cmd[1].c_str()); else _node->terminate(Node::NODE_NORMAL_TERMINATION,"terminate via IPC command"); } else if (cmd[0] == "updatecheck") { if (_node->updateCheck()) { ipcc->printf("500 software updates are not enabled"ZT_EOL_S); } else { ipcc->printf("200 OK"ZT_EOL_S); } } else { ipcc->printf("404 %s No such command. Use 'help' for help."ZT_EOL_S,cmd[0].c_str()); } } ipcc->printf("."ZT_EOL_S); // blank line ends response } } // namespace ZeroTier