307 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <sol/call.hpp>
 | 
						|
#include <sol/types.hpp>
 | 
						|
#include <sstream>
 | 
						|
 | 
						|
#include "i_network_bridge.h"
 | 
						|
#include "lua_api.h"
 | 
						|
#include "session.h"
 | 
						|
#include "machine.h"
 | 
						|
#include "vfs.h"
 | 
						|
#include "util.h"
 | 
						|
 | 
						|
namespace api {
 | 
						|
 | 
						|
vfs_node* get_current_dir(Session& context) {
 | 
						|
  return context.get_current_dir();
 | 
						|
}
 | 
						|
 | 
						|
std::string rm(Session& context, const std::string& filename) {
 | 
						|
  vfs_node* current_dir = context.get_current_dir();
 | 
						|
  auto it = current_dir->children.find(filename);
 | 
						|
 | 
						|
  if(it == current_dir->children.end()) {
 | 
						|
    return "rm: cannot remove '" + filename + "': No such file or directory.";
 | 
						|
  }
 | 
						|
  if(it->second->type == DIR_NODE) {
 | 
						|
    return "rm: cannot remove '" + filename + "': Is a directory.";
 | 
						|
  }
 | 
						|
 | 
						|
  delete it->second; /* Free the memory for the node. */
 | 
						|
  current_dir->children.erase(it); /* Remove from map. */
 | 
						|
  return "";
 | 
						|
}
 | 
						|
 | 
						|
std::string write_file(Session& context, const std::string& path,
 | 
						|
                       const std::string& content) {
 | 
						|
  vfs_node* parent_dir = nullptr;
 | 
						|
  std::string filename;
 | 
						|
 | 
						|
  if(path[0] == '/') {
 | 
						|
    Machine* session_machine = context.get_session_machine();
 | 
						|
    vfs_node* root = session_machine->vfs_root;
 | 
						|
 | 
						|
    size_t last_slash = path.find_last_of('/');
 | 
						|
    if(last_slash == 0) {
 | 
						|
      /* File in root. */
 | 
						|
      parent_dir = root;
 | 
						|
      filename = path.substr(1);
 | 
						|
    } else {
 | 
						|
      std::string parent_path = path.substr(0, last_slash);
 | 
						|
      parent_dir = find_node_by_path(root, parent_path);
 | 
						|
      filename = path.substr(last_slash+1);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    /* Relative path. */
 | 
						|
    parent_dir = context.get_current_dir();
 | 
						|
    filename = path;
 | 
						|
  }
 | 
						|
 | 
						|
  if(!parent_dir) {
 | 
						|
    return "write: cannot create file '" + path + "': No such file or directory";
 | 
						|
  }
 | 
						|
 | 
						|
  if(parent_dir->type != DIR_NODE) {
 | 
						|
    return "write: cannot create file in '" + get_full_path(parent_dir) + "': Not a directory";
 | 
						|
  }
 | 
						|
 | 
						|
  auto it = parent_dir->children.find(filename);
 | 
						|
  if(it != parent_dir->children.end()) {
 | 
						|
    if(it->second->type == DIR_NODE) {
 | 
						|
      return "write: " + path + ": Is a directory";
 | 
						|
    }
 | 
						|
    it->second->content = content;
 | 
						|
  } else {
 | 
						|
    /* File does not exist, create it. */
 | 
						|
    vfs_node* new_file = new_node(filename, FILE_NODE, parent_dir);
 | 
						|
    new_file->content = content;
 | 
						|
    parent_dir->children[filename] = new_file;
 | 
						|
  }
 | 
						|
  return "";
 | 
						|
}
 | 
						|
 | 
						|
std::string create_executable(Session& context, const std::string& path,
 | 
						|
                              const std::string& content) {
 | 
						|
  vfs_node* parent_dir = nullptr;
 | 
						|
  std::string filename;
 | 
						|
 | 
						|
  if(path[0] == '/') {
 | 
						|
    Machine* session_machine = context.get_session_machine();
 | 
						|
    vfs_node* root = session_machine->vfs_root;
 | 
						|
 | 
						|
    size_t last_slash = path.find_last_of('/');
 | 
						|
    if(last_slash == 0) {
 | 
						|
      parent_dir = root;
 | 
						|
      filename = path.substr(1);
 | 
						|
    } else {
 | 
						|
      std::string parent_path = path.substr(0, last_slash);
 | 
						|
      parent_dir = find_node_by_path(root, parent_path);
 | 
						|
      filename = path.substr(last_slash+1);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    parent_dir = context.get_current_dir();
 | 
						|
    filename = path;
 | 
						|
  }
 | 
						|
 | 
						|
  if(!parent_dir) {
 | 
						|
    return "create_executable: cannot create file '" + path + "': No such file or directory";
 | 
						|
  }
 | 
						|
 | 
						|
  if(parent_dir->type != DIR_NODE) {
 | 
						|
    return "create_executable: cannot create file in '" + get_full_path(parent_dir)
 | 
						|
        + "': Not a directory";
 | 
						|
  }
 | 
						|
 | 
						|
  /* Overwrite if exists. */
 | 
						|
  auto it = parent_dir->children.find(filename);
 | 
						|
  if(it != parent_dir->children.end()) {
 | 
						|
    delete it->second;
 | 
						|
    parent_dir->children.erase(it);
 | 
						|
  }
 | 
						|
  vfs_node* new_exec = new_node(filename, EXEC_NODE, parent_dir);
 | 
						|
  new_exec->content = util::xor_string(content);
 | 
						|
  parent_dir->children[filename] = new_exec;
 | 
						|
  return "";
 | 
						|
}
 | 
						|
 | 
						|
std::string cd(Session& context, const std::string& path) {
 | 
						|
  vfs_node* current_dir = context.get_current_dir();
 | 
						|
 | 
						|
  if(path == "..") {
 | 
						|
    if(current_dir->parent) {
 | 
						|
      context.set_current_dir(current_dir->parent);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    auto it = current_dir->children.find(path);
 | 
						|
    if(it != current_dir->children.end() && it->second->type == DIR_NODE) {
 | 
						|
      context.set_current_dir(it->second);
 | 
						|
    } else {
 | 
						|
      return "cd: no such file or directory: " + path;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return "";
 | 
						|
}
 | 
						|
 | 
						|
std::string ls(Session& context) {
 | 
						|
  vfs_node* dir = context.get_current_dir();
 | 
						|
  if(dir->type != DIR_NODE) {
 | 
						|
    return "ls: not a directory";
 | 
						|
  }
 | 
						|
 | 
						|
  std::stringstream ss;
 | 
						|
  for(auto const& [name, node] : dir->children) {
 | 
						|
    ss << name;
 | 
						|
    if(node->type == DIR_NODE) {
 | 
						|
      ss << "/";
 | 
						|
    }
 | 
						|
    ss << "   ";
 | 
						|
  }
 | 
						|
  return ss.str();
 | 
						|
}
 | 
						|
 | 
						|
std::string ssh(Session& context, const std::string& ip) {
 | 
						|
  INetworkBridge* bridge = context.get_network_bridge();
 | 
						|
  Machine* target_machine = bridge->get_machine_by_ip(ip);
 | 
						|
 | 
						|
  if(target_machine) {
 | 
						|
    context.set_session_machine(target_machine);
 | 
						|
    return "Connected to " + ip;
 | 
						|
  } else {
 | 
						|
    return "ssh: Could not resolve hostname " + ip + ": Name or service not found";
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::string nmap(Session& context, const std::string& ip) {
 | 
						|
  INetworkBridge* bridge = context.get_network_bridge();
 | 
						|
  Machine* target_machine = bridge->get_machine_by_ip(ip);
 | 
						|
 | 
						|
  if(!target_machine) {
 | 
						|
    return "nmap: Could not resolve host: " + ip;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Read services from the cached machine object, not the DB. */
 | 
						|
  auto services = target_machine->services;
 | 
						|
 | 
						|
  /* Release the machine from the cache if it's a remote machine. */
 | 
						|
  if(target_machine != context.get_session_machine()) {
 | 
						|
    bridge->release_machine(target_machine->id);
 | 
						|
  }
 | 
						|
 | 
						|
  if(services.empty()) {
 | 
						|
    return "No open ports for " + ip;
 | 
						|
  }
 | 
						|
 | 
						|
  std::stringstream ss;
 | 
						|
  ss << "Host: " << ip << "\n";
 | 
						|
  ss << "PORT\tSTATE\tSERVICE\n";
 | 
						|
  for(auto const& [port, service_name] : services) {
 | 
						|
    ss << port << "/tcp\t" << "open\t" << service_name << "\n";
 | 
						|
  }
 | 
						|
  return ss.str();
 | 
						|
}
 | 
						|
 | 
						|
std::string disconnect(Session& context) {
 | 
						|
  Machine* current_machine = context.get_session_machine();
 | 
						|
  if(current_machine != context.get_home_machine()) {
 | 
						|
    INetworkBridge* bridge = context.get_network_bridge();
 | 
						|
    bridge->release_machine(current_machine->id);
 | 
						|
  }
 | 
						|
  context.set_session_machine(context.get_home_machine());
 | 
						|
  return "Connection closed.";
 | 
						|
}
 | 
						|
 | 
						|
std::string close_terminal(Session& context) {
 | 
						|
  return "__CLOSE_CONNECTION__";
 | 
						|
}
 | 
						|
 | 
						|
struct ScpPath {
 | 
						|
  std::string host;
 | 
						|
  std::string path;
 | 
						|
};
 | 
						|
 | 
						|
ScpPath parse_scp_path(const std::string& arg) {
 | 
						|
  size_t colon_pos = arg.find(':');
 | 
						|
  if(colon_pos != std::string::npos) {
 | 
						|
    return {arg.substr(0, colon_pos), arg.substr(colon_pos+1)};
 | 
						|
  } else {
 | 
						|
    return {"", arg};
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::string scp(Session& context, const std::string& source_arg,
 | 
						|
                const std::string& dest_arg) {
 | 
						|
  ScpPath source_path = parse_scp_path(source_arg);
 | 
						|
  ScpPath dest_path = parse_scp_path(dest_arg);
 | 
						|
  Machine* session_machine = context.get_session_machine();
 | 
						|
  INetworkBridge* bridge = context.get_network_bridge();
 | 
						|
 | 
						|
  Machine* source_machine = source_path.host.empty()
 | 
						|
    ? session_machine : bridge->get_machine_by_ip(source_path.host);
 | 
						|
 | 
						|
  Machine* dest_machine = dest_path.host.empty()
 | 
						|
    ? session_machine : bridge->get_machine_by_ip(dest_path.host);
 | 
						|
 | 
						|
  if(!source_machine) {
 | 
						|
    return "scp: " + source_path.host + ": Name or service not known";
 | 
						|
  }
 | 
						|
  if(!dest_machine) {
 | 
						|
    if(source_machine != session_machine) bridge->release_machine(source_machine->id);
 | 
						|
    return "scp: " + dest_path.host + ": Name or service not known";
 | 
						|
  }
 | 
						|
 | 
						|
  /* Simplified to use only absolute paths for now. */
 | 
						|
  vfs_node* source_node = find_node_by_path(source_machine->vfs_root, source_path.path);
 | 
						|
 | 
						|
  if(!source_node || source_node->type != FILE_NODE) {
 | 
						|
    if(source_machine != session_machine) bridge->release_machine(source_machine->id);
 | 
						|
    if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
 | 
						|
    return "scp: " + source_path.path + ": No such file or directory";
 | 
						|
  }
 | 
						|
 | 
						|
  vfs_node* dest_node = find_node_by_path(dest_machine->vfs_root, dest_path.path);
 | 
						|
  vfs_node* dest_dir = nullptr;
 | 
						|
  std::string dest_filename;
 | 
						|
 | 
						|
  if(dest_node && dest_node->type == DIR_NODE) {
 | 
						|
    dest_dir = dest_node;
 | 
						|
    dest_filename = source_node->name;
 | 
						|
  } else {
 | 
						|
    size_t last_slash = dest_path.path.find_last_of('/');
 | 
						|
    if(last_slash != std::string::npos) {
 | 
						|
      std::string parent_path = dest_path.path.substr(0, last_slash);
 | 
						|
      if(parent_path.empty()) parent_path = "/";
 | 
						|
      dest_dir = find_node_by_path(dest_machine->vfs_root, parent_path);
 | 
						|
      dest_filename = dest_path.path.substr(last_slash+1);
 | 
						|
    } else {
 | 
						|
      dest_dir = dest_machine->vfs_root;
 | 
						|
      dest_filename = dest_path.path;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if(!dest_dir) {
 | 
						|
    if(source_machine != session_machine) bridge->release_machine(source_machine->id);
 | 
						|
    if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
 | 
						|
    return "scp: " + dest_path.path + ": No such file or directory";
 | 
						|
  }
 | 
						|
 | 
						|
  if(dest_dir->children.count(dest_filename)) {
 | 
						|
    vfs_node* existing_node = dest_dir->children[dest_filename];
 | 
						|
    if(existing_node->type == DIR_NODE) {
 | 
						|
      if(source_machine != session_machine) bridge->release_machine(source_machine->id);
 | 
						|
      if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
 | 
						|
      return "scp: " + dest_path.path + ": Is a directory";
 | 
						|
    }
 | 
						|
    existing_node->content = source_node->content;
 | 
						|
  } else {
 | 
						|
    vfs_node* new_file = new_node(dest_filename, FILE_NODE, dest_dir);
 | 
						|
    new_file->content = source_node->content;
 | 
						|
    dest_dir->children[dest_filename] = new_file;
 | 
						|
  }
 | 
						|
 | 
						|
  if(source_machine != session_machine) bridge->release_machine(source_machine->id);
 | 
						|
  if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
 | 
						|
 | 
						|
  return "";
 | 
						|
}
 | 
						|
 | 
						|
} /* namespace api */
 |