Source

Bluebat / D / bluebatClient.d

import bluez;
import tango.sys.Process;
import tango.sys.Environment;
import tango.io.stream.Lines;
import tango.io.Stdout;
import tango.io.Console;
import tango.io.Path;
import tango.util.log.Log;
import tango.util.log.AppendFile;
import tango.util.log.AppendConsole;
import tango.core.ThreadPool;
import tango.core.Thread;
import tango.text.xml.Document;
import tango.text.xml.DocPrinter;
import tango.io.device.File;
import Integer = tango.text.convert.Integer;
import tango.net.device.Socket;
import tango.net.InternetAddress;
import tango.io.FileScan;
import tango.io.encode.Base64;

/**
 * DataFile handles the data.xml file which is used to store collected data 
 * from the honeypot.
 * 
 */
class DataFile{
	private:
		File output;
	public:
		/**
		 * Create an instance of DataFile in the working directory
		 * Append other data if there is still data to send to BluebatServer
		 * from the previous execution
		 */
		this(){
			output = new File("data.xml", File.WriteAppending);
		}
		/**
		 * When object is destroyed, close the data.xml file
		 *
		 */
		~this(){
			output.close();
		}
		/**
		 * Add data to the data.xml file
		 * Params:
		 *     msg = a char[] with xml data to put in data.xml 
		 */
		synchronized void UpdateFile(char[] msg){
			output.write(msg~"\n");				
		}
		/**
		 * Create a new data.xml file since the content have been sent
		 * to BluebatServer
		 *
		 */
		synchronized void CleanData(){
			output.close();
			output = new File("data.xml", File.WriteCreate);
		}
}

DataFile datafile;

///this statement is executed before main and represent global variables
static this(){
	datafile = new DataFile();
}

/**
 * Uploads data to BluebatServer running at pluto.vincenzo-ampolo.net
 * Every 10 mins this function tries to upload data.xml file to the BluebatServer
 * It needs to be executed by a thread
 */
void upload_data(){
	///set up logging system
	auto logger = Log.lookup("bluebat.client.file_uploader");
	logger.level = logger.Info;
	logger.add(new AppendFile("log.txt"));
	logger.add(new AppendConsole());
	
	char[] server = "bluebat.elet.polimi.it";
	int port = 5168;
	char[] buf;
	auto output = new File("data.xml");
	Socket s;
	File file;
	logger.info("Starting file uploader");
	
	///enter the main loop
	while(1){
		
		//sleep for 10 mins = 600
		Thread.sleep(600);
		
		file = new File("data.xml");
		
		if(file.length == 0)
			continue;
		
		logger.info("data.xml is not empty, trying to send to server {}", server);
				
		//check if there is connection
		try{
			///try to connect to BluebatServer via tcp/ip
		s = new Socket();
		s.connect(new InternetAddress (server, port));
		}
		catch (tango.core.Exception.SocketException){
			continue;
		}
		
		logger.info("Connection Created. Sending data.xml");
		
		///allocate a buffer dynamic array at the same size of the file
		buf = new char [file.length];
		
		///read the whole file
		file.read(buf);
		file.close();
		
		///remove old data.xml file
		datafile.CleanData();
		
		///send the whole file
		s.write(buf);
		
		///close socket with BluebatServer
		s.shutdown();
	}
}

/**
 * This function takes care of all the data gathered with OBEX-PUSH
 * and stores inside the data.xml file
 * The main loop is executed every 5 seconds
 * It needs to be executed by a thread
 */
void merge_data_sobexsrv(){
	auto logger = Log.lookup("bluebat.client.sobexsrv");
	logger.level = logger.Info;
	logger.add(new AppendFile("log.txt"));
	logger.add(new AppendConsole());
	
	auto scan = new FileScan();
	File file;
	FilePath file_path;
	ubyte[] buf;
	char[] encoded;
	
	///entering the main loop
	while(1){
		//sleep for 5 sec
		Thread.sleep(5);
		
		scan("/tmp/sobexfiles");
		
		///recursive scanning of files and directories like python os.walk
		foreach (filename; scan.files){
		         logger.info("received {}", filename);
		         file = new File(filename.toString());
		         buf = new ubyte [file.length];
		         file.read(buf);
		         file.close();
		         
		         ///Encode to Base64 to be sure that binary data is preserved in xml
		         encoded = null;
		         encoded = encode(buf);
		         
		         auto doc = new Document!(char);
		         auto docprint = new DocPrinter!(char);
		     	
		         ///create a xml entry to be written in data.xml
		         doc.tree.element(null, "communication", "file")
		         .attribute(null, "address", "unknow")
		         .attribute(null, "protocol", "OBEX-PUSH")
		         .element(null, "file", encoded)
		         .attribute(null, "name", filename.toString()[16 .. filename.toString().length])
		         .attribute(null, "encoded", "base64");
		     	
		        ///write xml entry
		     	datafile.UpdateFile(docprint(doc));
		     	file_path = new FilePath(filename.toString());
		     	file_path.remove();
		     	
		     	logger.info("File included in data.xml");
		         
		}
	}
}

/**
 * Handles a single client
 */
int process_client(Communication client, char[] protocol, int port){
	auto doc = new Document!(char);
	auto docprint = new DocPrinter!(char);
	
	auto logger = Log.lookup("bluebat.client.communication");
	logger.level = logger.Info;
	logger.add(new AppendFile("log.txt"));
	logger.add(new AppendConsole());
	ubyte[] data;
	
	///get data from the Bluetooth interface
	data = client.Receive();
	
	///	Encode to Base64 to be sure that binary data is preserved in xml
    char[] encoded = encode(data);
    
    ///create xml entry
	doc.tree.element(null, "communication", "stream")
    .attribute(null, "address", client.GetAddr)
    .attribute(null, "protocol", protocol)
    .attribute(null, "port", Integer.toString(port))
    .element(null, "stream", encoded)
    .attribute(null, "encoded", "base64");
	
	logger.warn("Communication on protocol {} port {}, client address {}", protocol, port, client.GetAddr);
	
	///write data into data.xml file
	datafile.UpdateFile(docprint(doc));
	
	return 0;
}

/**
 * Create a RfcommServer instace at given channel
 */
void Rf_thread(int i){
 	auto logger = Log.lookup("bluebat.Rfthread");
	logger.level = logger.Info;
	logger.add(new AppendFile("log.txt"));
	logger.add(new AppendConsole());
	
	auto server = new RfcommServer(i,1);
	logger.info("Rfcomm server active in channel {}", i);
	
	while(1){
		auto client = server.Accept();
		process_client(client, "rfcomm", i);
	}
	
}

/**
 * Create a L2CapServer instance at give channel
 */
void L2_thread(int i){
	auto logger = Log.lookup("bluebat.L2thread");
	logger.level = logger.Info;
	logger.add(new AppendFile("log.txt"));
	logger.add(new AppendConsole());
	
	auto server = new L2capServer(i,1);
	logger.info("L2cap server active in channel {}", i);
	
	while(1){
		auto client = server.Accept();
		process_client(client, "l2cap", i);
	}
	
}

int main(){
	///set up the logging system
	auto logger = Log.lookup("bluebat.main");
	int[] rfcomm;	//store all the socket that should be opened
	int[] l2cap;	//this too
	
	logger.level = logger.Info;
	logger.add(new AppendFile("log.txt"));
	logger.add(new AppendConsole());
	
	logger.info("Starting");
	
	///calculate the RFCOMM and L2CAP channel and psm to listen to
	rfcomm.length = 30;
	for(int i = 1; i < 30; i++)
		rfcomm[i-1]= i;
	logger.info("Rfcomm sockets to monitor are {}", rfcomm.length);
	logger.info("Rfcomm {}", rfcomm);
	
	l2cap.length = 0x10FF - 0x1001;
	logger.info("L2cap sockets to monitor are {}", l2cap.length);
	for(int i = 0x1001; i < 0x10FF; i++)
		l2cap[i - 0x1001] = i;
	
	logger.info("Creating Threads");
	
	logger.info("Total Threads are {}", rfcomm.length + l2cap.length);
	
	///Create one thread for each listening server
	auto RfPool = new ThreadPool!(int)(rfcomm.length);
	void delegate(int) f = (int x) { Rf_thread(x); };
	auto L2Pool = new ThreadPool!(int)(l2cap.length);
	void delegate(int) g = (int x) { L2_thread(x); };
	
	///Run threads
	foreach(int i; rfcomm)
			 RfPool.append(f, i);
	foreach(int i; l2cap)
			L2Pool.append(g,i);
	
	///create two threads for merge_data_sobexsrv() and upload data
	auto SysPool = new ThreadPool!()(2);
	void delegate() h = () {
		merge_data_sobexsrv();
	};
	
	void delegate() l = (){
		upload_data();
	};
	
	SysPool.append(h);
	SysPool.append(l);
	
	Thread.sleep(2);
	
	///create /tmp/sobexfiles to store OBEX files
	auto path = new FilePath("/tmp/sobexfiles");
	try{
		path.createFolder();
	}
	catch (tango.core.Exception.IOException){
		;
	}
	
	///run Collin Mulliner's sobexsrv
	auto sobexsrv = new Process(Environment.cwd~"sobexsrv-1.0.1/src/"~"./sobexsrv -d -I -r /tmp/sobexfiles/", null); 
	
	sobexsrv.execute();
	
	///set device to discoverable mode
	auto dbus = new Process("bash -c 'dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/`pidof bluetoothd`/hci0 org.bluez.Adapter.SetProperty string:Discoverable variant:boolean:true'", null);
	
	dbus.execute();
	
	Thread.sleep(5);
	logger.warn("Press any key to close the honeypot");
	Cin.get();
	
	logger.warn("Exiting...");
	
	///close all the interfaces
	RfPool.finish();
	L2Pool.finish();
	SysPool.finish();
	sobexsrv.kill();
	
	return 0;
	
}