Pull to refresh

Еще один «велосипед» по сбору данных о пользователях ПК в домене

Reading time 18 min
Views 13K
Доброго дня всем.
Решил опубликовать свой вариант решения по сбору данных о пользователях ПК в домене. Просто чтобы не пропало зря и возможно кому-нибудь пригодилось.

Данное решение позволяет собирать данные о ПК в домене по мере их подключения к сети, входах пользователей на эти ПК и установленного ПО в момент включения. Началось все с желания поэкспериментировать со средами программирования (Java, Qt), а закончилось довольно полезной утилитой для повседневной работы системного администратора. Серверная часть работает под Linux, но я думаю также будет работать и под любой системой где можно запустить JVM. Клиент для отображения данных изначально собирался под Linux. При компиляции под Windows единственная сложность — не было в комплекте Qt драйвера для обращения к серверу MySQL, который пришлось собирать отдельно.

Из чего состоит это решение:
1. Небольшая агентская программа на чистом С. Запускается дважды через доменные политики. Первый раз при включении ПК с правами системы — собирает данные об оборудовании, установленных программах и сразу отправляет эти данные на сервер. Второй раз — с правами пользователя при его входе в систему для сбора данных о логине пользователя. Данные на сервер отправляет простым XML. Среда разработки Visual C++ 2010 Express.
2. Сервер на языке Java без каких либо фреймворков. Постоянно ожидает сообщения на порту 8889 и складывает полученные данные в базу данных MySQL.
3. Клиент для отображение данных разработан в среде Qt4/5 (изначально Qt4, затем собирался в Qt5). Обращается напрямую к серверу БД MySQL.

Далее приведен исходный код для каждой из указанных частей.


Агент

Среда разработки — Visual C++ 2010 Express.
Никаких зависимостей. Все вызовы через WinAPI. Работает с Win XP/7/2003/2008/Vista/8
Адрес сервера для сбора данных зашит в программу (сейчас там указан произвольный «192.168.0.123»).
Формат вызова в командной строке:
1. без параметров — сбор данных о ПК (нужны права локальной системы)
2. с параметром "-u" — сбор данных о пользователе (достаточно прав пользователя)

Код агента на языке С
#include "stdafx.h"
#include <winsock2.h>
#include <windows.h>
#include <iostream>

#define SECURITY_WIN32
#include <Security.h>

using namespace std;
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Secur32.lib")

#define BUF_SIZE 32768
#define DST_PORT 8889
#define DST_IPADDR "192.168.0.123"

int	enum_installed_applications(BOOL IsUpdates, BOOL IsUserKey);
int enum_user_info();
int wide_to_utf8(TCHAR *src, char *dst, int dst_size);
int escape_xml_string(TCHAR *src, int src_size, TCHAR *dst, int dst_size);

int _tmain(int argc, _TCHAR* argv[])
{
	if((argc > 1)&&(wcscmp(argv[1],TEXT("-u"))==0)) {
		cout << "will scan users" << endl;
		enum_user_info();
	} else {
		enum_installed_applications(false, false);	
	}

	return 0;
}

int enum_user_info()
{
	SOCKET s;
	sockaddr_in addr;
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	char buf[BUF_SIZE];
	const char xml_header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	const char xml_user_start[] = "<user";
	const char xml_user_login[] = " Login=\"";
	const char xml_user_computer_name[] = " NBName=\"";
	const char xml_user_computer_domain[] = " Domain=\"";
	const char xml_user_end[] = "</user>";
	

	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		printf("WSAStartup failed with error: %d\n", err);
		return -1;
	}

	s = socket(AF_INET, SOCK_STREAM, 0);
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(DST_PORT);
	addr.sin_addr.S_un.S_addr = inet_addr(DST_IPADDR);

	if(connect(s, (SOCKADDR*) &addr, sizeof(addr)) == SOCKET_ERROR) {
		int error = GetLastError();
		printf("ERROR CONNECT: %s (%x)\n", strerror(error), error);
		//system("pause");
		return -1;
	}

	TCHAR computer_name[BUF_SIZE];
	DWORD computer_name_count = BUF_SIZE;
	int computer_name_utf8_count;
	GetComputerNameEx(ComputerNamePhysicalNetBIOS, computer_name, &computer_name_count);

	TCHAR user_computer_domain[BUF_SIZE];
	DWORD user_computer_domain_count = BUF_SIZE;
	int user_computer_domain_utf8_count;
	GetComputerNameEx(ComputerNamePhysicalDnsDomain, user_computer_domain, &user_computer_domain_count);

	TCHAR login[1024];	
	DWORD login_size = 1024;
	int login_utf8_count;
	if(GetUserNameEx(NameUserPrincipal, (TCHAR*)login, &login_size) == 0) { 
	//if(GetUserName((TCHAR*)login, &login_size) == 0) {
		int error = GetLastError();
		printf("ERROR GetUSerName: %s (%x)\n", strerror(error), error);
		//system("pause");	
		return -1;
	}
	cout << login << endl;

	send(s, xml_header, strlen(xml_header), 0);
	send(s, xml_user_start, strlen(xml_user_start), 0);	
	send(s, xml_user_computer_name, strlen(xml_user_computer_name), 0);
	computer_name_utf8_count = wide_to_utf8(computer_name, buf, sizeof(buf));
	send(s, buf, computer_name_utf8_count, 0);
	send(s, "\"", 1, 0);
	send(s, xml_user_login, strlen(xml_user_login), 0);
	login_utf8_count = wide_to_utf8(login, buf, sizeof(buf));
	send(s, buf, login_utf8_count, 0);

	cout << buf << endl;

	send(s, "\"", 1, 0);
	send(s, xml_user_computer_domain, strlen(xml_user_computer_domain), 0);
	user_computer_domain_utf8_count = wide_to_utf8(user_computer_domain, buf, sizeof(buf));
	send(s, buf, user_computer_domain_utf8_count, 0);
	send(s, "\">", 2, 0);
	send(s, xml_user_end, strlen(xml_user_end), 0);
	
	closesocket(s);
	WSACleanup();

	return 0;
}

int enum_installed_applications(BOOL IsUpdates, BOOL IsUserKey)
{
	DWORD dwSize = MAX_PATH, dwType, dwValue;
	bool bIsSystemComponent, bIsUpdate;
	TCHAR szParentKeyName[MAX_PATH];
	TCHAR szDisplayName[BUF_SIZE];
	TCHAR szDisplayNameEsc[BUF_SIZE];
	TCHAR szKeyName[MAX_PATH];	
	char buf[BUF_SIZE];
	HKEY hKey, hRootKey, hAppKey;
	LONG ItemIndex = 0;
	const char xml_header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	const char xml_computer_start[] = "<computer";
	const char xml_computer_name[] = " NBName=\"";
	const char xml_computer_osversion[] = " OSVersion=\"";
	const char xml_computer_domain[] = " Domain=\"";
	const char xml_computer_end[] = "</computer>";
	const char xml_product_start[] = "<product";
	const char xml_product_name[] = " name=\"";

	hRootKey = IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;

	SOCKET s;
	sockaddr_in addr;
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		printf("WSAStartup failed with error: %d\n", err);
		return -1;
	}

	s = socket(AF_INET, SOCK_STREAM, 0);
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(DST_PORT);
	addr.sin_addr.S_un.S_addr = inet_addr(DST_IPADDR);

	if(connect(s, (SOCKADDR*) &addr, sizeof(addr)) == SOCKET_ERROR) {
		int error = GetLastError();
		printf("ERROR CONNECT: %s (%x)\n", strerror(error), error);
		//system("pause");
		return -1;
	}

	TCHAR computer_name[BUF_SIZE];
	DWORD computer_name_count = BUF_SIZE;
	int computer_name_utf8_count;

	TCHAR computer_domain[BUF_SIZE];
	DWORD computer_domain_count = BUF_SIZE;
	int computer_domain_utf8_count;

	GetComputerNameEx(ComputerNamePhysicalNetBIOS, computer_name, &computer_name_count);
	GetComputerNameEx(ComputerNamePhysicalDnsDomain, computer_domain, &computer_domain_count);

	send(s, xml_header, strlen(xml_header), 0);
	send(s, xml_computer_start, strlen(xml_computer_start), 0);	
	send(s, xml_computer_name, strlen(xml_computer_name), 0);
	computer_name_utf8_count = wide_to_utf8(computer_name, buf, sizeof(buf));
	send(s, buf, computer_name_utf8_count, 0);
	send(s, "\"", 1, 0);
	send(s, xml_computer_domain, strlen(xml_computer_domain), 0);
	computer_domain_utf8_count = wide_to_utf8(computer_domain, buf, sizeof(buf));
	send(s, buf, computer_domain_utf8_count, 0);
	send(s, "\">", 2, 0);

	if (RegOpenKey(hRootKey, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"), &hKey) != ERROR_SUCCESS)
	{
		return -1;
	}

	while (RegEnumKeyEx(hKey, ItemIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
	{		
		if (RegOpenKey(hKey, szKeyName, &hAppKey) == ERROR_SUCCESS)
		{
			dwType = REG_DWORD;
			dwSize = sizeof(DWORD);

			if (RegQueryValueEx(hAppKey, _T("SystemComponent"), NULL, &dwType, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS)
			{
				bIsSystemComponent = (dwValue == 0x1);
			}
			else
			{
				bIsSystemComponent = FALSE;
			}

			dwType = REG_SZ;
			dwSize = MAX_PATH;
			bIsUpdate = (RegQueryValueEx(hAppKey, _T("ParentKeyName"), NULL, &dwType, (LPBYTE)szParentKeyName, &dwSize) == ERROR_SUCCESS);

			dwSize = MAX_PATH;
			if (RegQueryValueEx(hAppKey, _T("DisplayName"), NULL, &dwType, (LPBYTE)szDisplayName, &dwSize) == ERROR_SUCCESS)
			{
				if (!bIsSystemComponent)
				{
					if ((IsUpdates == FALSE && !bIsUpdate) || /* Applications only */
						(IsUpdates == TRUE && bIsUpdate)) /* Updates only */
					{
						send(s, xml_product_start, strlen(xml_product_start), 0);
						send(s, xml_product_name, strlen(xml_product_name), 0);
						escape_xml_string(szDisplayName, wcslen(szDisplayName), szDisplayNameEsc, BUF_SIZE);
						int name_len = wide_to_utf8(szDisplayNameEsc, buf, sizeof(buf));
						send(s, buf, name_len, 0);						
						send(s, "\"/>", 3, 0);
					}
				}
			}
			RegCloseKey(hAppKey);
		}

		dwSize = MAX_PATH;
		++ItemIndex;
	}

	send(s, xml_computer_end, strlen(xml_computer_end), 0);

	RegCloseKey(hKey);
	closesocket(s);
	WSACleanup();

	return 0;
}

int wide_to_utf8(TCHAR *src, char *dst, int dst_size) {
	int len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)src, -1, NULL, 0, NULL, NULL);
	WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)src, -1, dst, dst_size, NULL, NULL);
	return len-1;
}

int escape_xml_string(TCHAR *src, int src_size, TCHAR *dst, int dst_size) {

	if((src_size == 0)||(dst_size==0)) return 0;
	if((src == NULL)||(dst == NULL)) return 0;
	int i,j;	
	for(i=0, j=0; (i<src_size)&&(j<(dst_size-1)); i++, j++){
		if(src[i] == _T('\"')) {
			dst[j] = _T('&');
			dst[++j] = _T('q');
			dst[++j] = _T('u');
			dst[++j] = _T('o');
			dst[++j] = _T('t');
			dst[++j] = _T(';');
		} else if(src[i] == _T('<')) {
			dst[j] = _T('&');
			dst[++j] = _T('l');
			dst[++j] = _T('t');								
			dst[++j] = _T(';');
		} else if(src[i] == _T('>')) {
			dst[j] = _T('&');
			dst[++j] = _T('g');
			dst[++j] = _T('t');
			dst[++j] = _T(';');
		} else if(src[i] == _T('&')) {
			dst[j] = _T('&');
			dst[++j] = _T('a');
			dst[++j] = _T('m');
			dst[++j] = _T('p');
			dst[++j] = _T(';');
		} else {
			dst[j] = src[i];
		}
	}
	dst[j] = _T('\0');
	return j;
}



Сервер

Написан на языке Java. Использует только базовые возможности JVM без применения дополнительных библиотек и фреймворков (если не считать библиотеку JDBC для доступа к серверу БД MySQL).

Структура базы данных сервера MySQL
--
-- База данных: `audit`
--

-- --------------------------------------------------------

--
-- Структура таблицы `computers`
--

CREATE TABLE IF NOT EXISTS `computers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ipaddress` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `domain` varchar(255) NOT NULL,
  `osversion` varchar(255) DEFAULT NULL,
  `processor` varchar(255) DEFAULT NULL,
  `ram_kb` int(11) DEFAULT NULL,
  `updated` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--
-- Структура таблицы `netadapters`
--

CREATE TABLE IF NOT EXISTS `netadapters` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` int(11) DEFAULT NULL,
  `computer_id` int(11) NOT NULL,
  `mac` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `isdhcp` int(11) NOT NULL,
  `ipaddr` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `gateway` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- --------------------------------------------------------

--
-- Структура таблицы `software`
--

CREATE TABLE IF NOT EXISTS `software` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `computer_id` int(11) NOT NULL,
  `name` varchar(512) NOT NULL,
  `version` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--
-- Структура таблицы `users`
--

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `login` varchar(255) NOT NULL,
  `domain` varchar(255) NOT NULL,
  `loggedin` datetime NOT NULL,
  `computername` varchar(255) NOT NULL,
  `ipaddress` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;



Класс Computer для ранения данных о ПК
package auditServer;

import java.util.Date;
import java.util.LinkedList;

public class Computer {
    private int id;
    private String name;
    private String domain;
    private String OSVersion;
    private String processor;
    private String ipAddress;
    private int ram_kb;
    private Date updated;
    private LinkedList<Product> products;
    private LinkedList<NetAdapter> netAdapters;

    public Computer(String ipAddress, Date updated) {
        this.updated = updated;
        this.ipAddress = ipAddress;
    }

    public Computer(String ipAddress, String name, String domain, String OSVersion, String processor, Date updated) {
        this.ipAddress = ipAddress;
        this.name = name;
        this.domain = domain;
        this.OSVersion = OSVersion;
        this.processor = processor;
        this.updated = updated;
    }

    public int getRam_kb() {
        return ram_kb;
    }

    public void setRam_kb(int ram_kb) {
        this.ram_kb = ram_kb;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }

    public LinkedList<Product> getProducts() {
        return products;
    }

    public void setProducts(LinkedList<Product> products) {
        this.products = products;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDomain() {
        return domain;
    }

    public void setDomain(String domain) {
        this.domain = domain;
    }

    public String getOSVersion() {
        return OSVersion;
    }

    public void setOSVersion(String OSVersion) {
        this.OSVersion = OSVersion;
    }

    public String getProcessor() {
        return processor;
    }

    public void setProcessor(String processor) {
        this.processor = processor;
    }

    public LinkedList<NetAdapter> getNetAdapters() {
        return netAdapters;
    }

    public void setNetAdapters(LinkedList<NetAdapter> netAdapters) {
        this.netAdapters = netAdapters;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "name='" + name + '\'' +
                ", domain='" + domain + '\'' +
                ", ipAddress='" + ipAddress + '\'' +
                '}';
    }
}



Класс NetAdapter для хранения данных о сетевых адаптерах ПК
package auditServer;

public class NetAdapter {
    private int id;
    private int type;
    private int isdhcp;
    private String mac;
    private String ipaddr;
    private String gateway;

    public NetAdapter() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public int getIsdhcp() {
        return isdhcp;
    }

    public void setIsdhcp(int isdhcp) {
        this.isdhcp = isdhcp;
    }

    public String getMac() {
        return mac;
    }

    public void setMac(String mac) {
        this.mac = mac;
    }

    public String getIpaddr() {
        return ipaddr;
    }

    public void setIpaddr(String ipaddr) {
        this.ipaddr = ipaddr;
    }

    public String getGateway() {
        return gateway;
    }

    public void setGateway(String gateway) {
        this.gateway = gateway;
    }

    @Override
    public String toString() {
        return "NetAdapter{" +
                "id=" + id +
                ", type=" + type +
                ", mac='" + mac + '\'' +
                ", isdhcp='" + isdhcp + '\'' +
                ", ipaddr='" + ipaddr + '\'' +
                ", gateway='" + gateway + '\'' +
                '}';
    }
}



Класс User для хранения данных о пользователе
package auditServer;

import java.util.Date;

public class User {
    private int id;
    private String login;
    private String domain;
    private Date loggedIn;
    private String computerName;
    private String ipAddress;

    public User() {

    }

    public User(String login, String domain, Date loggedIn, String computerName, String ipAddress) {
        this.login = login;
        this.domain = domain;
        this.loggedIn = loggedIn;
        this.computerName = computerName;
        this.ipAddress = ipAddress;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getDomain() {
        return domain;
    }

    public void setDomain(String domain) {
        this.domain = domain;
    }

    public Date getLoggedIn() {
        return loggedIn;
    }

    public void setLoggedIn(Date loggedIn) {
        this.loggedIn = loggedIn;
    }

    public String getComputerName() {
        return computerName;
    }

    public void setComputerName(String computerName) {
        this.computerName = computerName;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }

    @Override
    public String toString() {
        return "User{" +
                "login='" + login + '\'' +
                ", domain='" + domain + '\'' +
                ", computerName='" + computerName + '\'' +
                '}';
    }
}



Класс Product для хранения данных об установленном ПО
package auditServer;

public class Product {
    private String name;
    private String version;

    public Product(String name, String version) {
            this.name = name;
            this.version = version;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        if((version == null)||(version.isEmpty())) return name;
        else return (name + " (" + version + ")");
    }
}



Код потока сервера для сохранения данных о ПК
package auditServer;

import java.net.ConnectException;
import java.sql.*;
import java.util.concurrent.SynchronousQueue;

public class ComputerDBWriter implements Runnable {
    private SynchronousQueue<Computer> computersQueue;
    static final String driverClassName = "com.mysql.jdbc.Driver";
    static final String url = "jdbc:mysql://localhost:3306/audit?characterSetResults=UTF-8&characterEncoding=UTF-8&useUnicode=yes";
    static final String user = "user";
    static final String password = "password";

    public ComputerDBWriter(SynchronousQueue<Computer> computersQueue) {
        this.computersQueue = computersQueue;
    }


    private Connection getConnection() throws SQLException {
        Connection conn = null;
        try {
            Class.forName(driverClassName);
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return conn;
    }

    private void updateComputer(Computer computer) {
        String sql1 = "INSERT INTO computers(ipaddress,name,domain,osversion,processor,ram_kb,updated) VALUES(?,?,?,?,?,?,?)";
        String sql2 = "INSERT INTO software(computer_id,name,version) VALUES(?,?,?)";
        String sql3 = "INSERT INTO netadapters(computer_id,type,mac,ipaddr,gateway,isdhcp) VALUES(?,?,?,?,?,?)";
        Connection conn = null;
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;
        PreparedStatement ps3 = null;
        try {
            conn = getConnection();
            ps1 = conn.prepareStatement(sql1, Statement.RETURN_GENERATED_KEYS);
            ps1.setString(1, computer.getIpAddress());
            ps1.setString(2, computer.getName());
            ps1.setString(3, computer.getDomain());
            ps1.setString(4, computer.getOSVersion());
            ps1.setString(5, computer.getProcessor());
            ps1.setInt(6, computer.getRam_kb());
            ps1.setTimestamp(7, new Timestamp(computer.getUpdated().getTime()));
            ps1.executeUpdate();
            ResultSet rs1 = ps1.getGeneratedKeys();
            if(rs1.next()) {
                computer.setId(rs1.getInt(1));

                for(Product p : computer.getProducts()) {
                    ps2 = conn.prepareStatement(sql2);
                    ps2.setInt(1, computer.getId());
                    ps2.setString(2, p.getName());
                    ps2.setString(3, p.getVersion());
                    ps2.executeUpdate();
                }

                for(NetAdapter na : computer.getNetAdapters()) {
                    ps3 = conn.prepareStatement(sql3);
                    ps3.setInt(1, computer.getId());
                    ps3.setInt(2, na.getType());
                    ps3.setString(3, na.getMac());
                    ps3.setString(4, na.getIpaddr());
                    ps3.setString(5, na.getGateway());
                    ps3.setInt(6, na.getIsdhcp());
                    ps3.executeUpdate();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                ps1.close();
                ps2.close();
                ps3.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public void run() {
        while(true) {
            try {
                Computer computer = computersQueue.take();
                System.out.println("!!! got new computer: " + computer);
                //for(Product p : computer.getProducts()) {
                //    System.out.println(p);
                //}
                updateComputer(computer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



Код потока сервера для сохранения данных о пользователях
package auditServer;

import java.sql.*;
import java.util.concurrent.SynchronousQueue;

public class UserDBWriter implements Runnable {
    private SynchronousQueue<User> usersQueue;
    static final String driverClassName = "com.mysql.jdbc.Driver";
    static final String url = "jdbc:mysql://localhost:3306/audit?characterSetResults=UTF-8&characterEncoding=UTF-8&useUnicode=yes";
    static final String user = "user";
    static final String password = "password";


    public UserDBWriter(SynchronousQueue<User> usersQueue) {
        this.usersQueue = usersQueue;
    }

    private Connection getConnection() throws SQLException {
        Connection conn = null;
        try {
            Class.forName(driverClassName);
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return conn;
    }

    private void updateUser(User user) {
        String sql1 = "INSERT INTO users(computername,domain,ipaddress,loggedin,login) VALUES(?,?,?,?,?)";
        Connection conn = null;
        PreparedStatement ps1 = null;
        try {
            conn = getConnection();
            ps1 = conn.prepareStatement(sql1, Statement.RETURN_GENERATED_KEYS);
            ps1.setString(1, user.getComputerName());
            ps1.setString(2, user.getDomain());
            ps1.setString(3, user.getIpAddress());
            ps1.setTimestamp(4, new Timestamp(user.getLoggedIn().getTime()));
            ps1.setString(5, user.getLogin());
            ps1.executeUpdate();
            ResultSet rs1 = ps1.getGeneratedKeys();
            if(rs1.next()) {
                user.setId(rs1.getInt(1));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                ps1.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void run() {
        while(true) {
            try {
                User user = usersQueue.take();
                System.out.println("!!! got new user: " + user);
                updateUser(user);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



Код потока сервера для приема данных от агентов
package auditServer;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.concurrent.SynchronousQueue;

public class ServerHandler implements Runnable {
    private Socket incoming;
    private SynchronousQueue<Computer> computersQueue;
    private SynchronousQueue<User> usersQueue;

    public ServerHandler(Socket incoming, SynchronousQueue<Computer> computersQueue, SynchronousQueue<User> usersQueue) {
        this.incoming = incoming;
        this.computersQueue = computersQueue;
        this.usersQueue = usersQueue;
    }

    @Override
    public void run() {
        try {

            LinkedList<Product> products = new LinkedList<Product>();
            LinkedList<NetAdapter> netAdapters = new LinkedList<NetAdapter>();
            Computer c = null;
            User u = null;
            boolean eok = false;

//            Scanner in = new Scanner(incoming.getInputStream());
//            while(in.hasNextLine()) {
//                System.out.println(in.nextLine());
//            }

            System.out.println("Got connection from " + incoming.getInetAddress().getHostAddress());

            XMLInputFactory factory = XMLInputFactory.newInstance();
            try {
                XMLStreamReader parser = factory.createXMLStreamReader(incoming.getInputStream());
                String eName;

                while(parser.hasNext()) {
                    int event = parser.next();
                    if(event == XMLStreamConstants.START_ELEMENT) {
                        eName = parser.getLocalName();
                        if(eName.equals("computer")) {
                            c = new Computer(incoming.getInetAddress().getHostAddress(), new java.util.Date());
                            int attrCount = parser.getAttributeCount();
                            for(int i=0; i<attrCount; i++) {
                                String attrName = parser.getAttributeLocalName(i);
                                if(attrName.equals("NBName")) c.setName(parser.getAttributeValue(i));
                                else if(attrName.equals("Domain")) c.setDomain(parser.getAttributeValue(i));
                                else if(attrName.equals("OSVersion")) c.setOSVersion(parser.getAttributeValue(i));
                                else if(attrName.equals("Processor")) c.setProcessor(parser.getAttributeValue(i));
                                else if(attrName.equals("RAM")) c.setRam_kb(Integer.parseInt(parser.getAttributeValue(i)));
                            }
                            System.out.println(c);

                        } else if(eName.equals("product")) {
                            Product p = new Product(parser.getAttributeValue(0),
                                    parser.getAttributeValue(1));
                            products.add(p);
                            //System.out.println(p);
                        } else if(eName.equals("user")) {
                            u = new User();
                            u.setIpAddress(incoming.getInetAddress().getHostAddress());
                            u.setLoggedIn(new java.util.Date());
                            int attrCount = parser.getAttributeCount();
                            for(int i=0; i<attrCount; i++) {
                                String attrName = parser.getAttributeLocalName(i);
                                if(attrName.equals("NBName")) u.setComputerName(parser.getAttributeValue(i));
                                else if(attrName.equals("Domain")) u.setDomain(parser.getAttributeValue(i));
                                else if(attrName.equals("Login")) u.setLogin(parser.getAttributeValue(i));
                            }
                        } else if(eName.equals("netAdapter")) {
                            NetAdapter na = new NetAdapter();
                            int attrCount = parser.getAttributeCount();
                            for(int i=0; i<attrCount; i++) {
                                String attrName = parser.getAttributeLocalName(i);
                                if(attrName.equals("type")) na.setType(Integer.parseInt(parser.getAttributeValue(i)));
                                else if(attrName.equals("isdhcp")) na.setIsdhcp(Integer.parseInt(parser.getAttributeValue(i)));
                                else if(attrName.equals("mac")) na.setMac(parser.getAttributeValue(i));
                                else if(attrName.equals("ip")) na.setIpaddr(parser.getAttributeValue(i));
                                else if(attrName.equals("gateway")) na.setGateway(parser.getAttributeValue(i));
                            }
                            netAdapters.add(na);
                        }


                    } else if(event == XMLStreamConstants.END_DOCUMENT) {
                        eok = true;
                        //System.out.println("END_DOCUMENT");
                    }
                }
            } catch (XMLStreamException e) {
                e.printStackTrace();
            }
            incoming.close();

            if((eok)&&(c != null)&&(products.size() > 0)) {
                c.setProducts(products);
                c.setNetAdapters(netAdapters);
                System.out.println(netAdapters);
                try {
                    computersQueue.put(c);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if((eok)&&(u != null)) {
                try {
                    usersQueue.put(u);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}



Код основного потока сервера
package auditServer;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.SynchronousQueue;

public class Server {
    public static void main(String[] args) {
        int port = 8889;
        SynchronousQueue<Computer> computersQueue = new SynchronousQueue<Computer>(true);
        SynchronousQueue<User> usersQueue = new SynchronousQueue<User>(true);

        Thread tComputerDBWriter = new Thread(new ComputerDBWriter(computersQueue));
        tComputerDBWriter.start();
        Thread tUserDBWriter = new Thread(new UserDBWriter(usersQueue));
        tUserDBWriter.start();

        try {
            ServerSocket s = new ServerSocket(port);
            while(true) {
                Socket incoming = s.accept();
                Runnable r = new ServerHandler(incoming, computersQueue, usersQueue);
                Thread t = new Thread(r);
                t.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}



Клиент

Разрабатывался в среде Qt4, затем дорабатывался в среде Qt5.
Обращается напрямую к серверу БД через драйвер MySQL.
Отображает только последние данные о ПК и пользователях. При желании можно добавить историю изменения состояния ПК и подключения пользователей к этим ПК.
При двойном клике по ID ПК открывается информация об установленном ПО на нем, собранная при последнем подключении.
При двойном клике на IP адресе (откуда было получено сообщение агента) открывается информация обо всех сетевых адаптерах ПК, их MAC и IPv4 адреса, используется ли DHCP.
При двойном клике по логину пользователя открывается rdesktop для подключения к указанному ПК с учетными данными этого пользователя.

Код клиента в архиве

UPDATE
Проект опубликован на GitHub
Tags:
Hubs:
+13
Comments 5
Comments Comments 5

Articles