monitor.cc 6.11 KB
Newer Older
1
/*
2 3
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.
François Grisez's avatar
François Grisez committed
4

5 6 7 8
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.
François Grisez's avatar
François Grisez committed
9

10 11 12 13
	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 Affero General Public License for more details.
François Grisez's avatar
François Grisez committed
14

15 16
	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 18 19 20
*/

#include "monitor.hh"
#include "configmanager.hh"
21 22
#include "authdb.hh"
#include <sofia-sip/su_md5.h>
23
#include <ortp/rtpsession.h>
24 25 26 27

using namespace std;

Monitor::Init Monitor::sInit;
28
const string Monitor::SCRIPT_PATH = "./flexisip_monitor.py";
29 30
const string Monitor::CALLER_PREFIX = "monitor-caller";
const string Monitor::CALLEE_PREFIX = "monitor-callee";
31
const int Monitor::PASSWORD_CACHE_EXPIRE = INT_MAX / 2;
32 33 34

Monitor::Init::Init() {
	ConfigItemDescriptor items[] = {
35 36 37 38 39 40
		{Boolean, "enabled", "Enable or disable the Flexisip monitor daemon", "false"},
		{Integer, "test-interval", "Time between two consecutive tests", "30"},
		{String, "logfile", "Path to the log file", "/etc/flexisip/flexisip_monitor.log"},
		{Integer, "switch-port", "Port to open/close folowing the test succeed or not", "12345"},
		{String, "password-salt", "Salt used to generate the passwords of each test account", ""},
		config_item_end};
François Grisez's avatar
François Grisez committed
41

42 43 44 45 46
	GenericStruct *s = new GenericStruct("monitor", "Flexisip monitor parameters", 0);
	GenericManager::get()->getRoot()->addChild(s);
	s->addChildrenValues(items);
}

47
void Monitor::exec(int socket) {
48 49 50 51
	// Create a temporary agent to load all modules
	su_root_t *root = NULL;
	shared_ptr<Agent> a = make_shared<Agent>(root);
	GenericManager::get()->loadStrict();
François Grisez's avatar
François Grisez committed
52

53
	GenericStruct *monitorParams = GenericManager::get()->getRoot()->get<GenericStruct>("monitor");
54
	GenericStruct *cluster = GenericManager::get()->getRoot()->get<GenericStruct>("cluster");
55 56 57
	string interval = monitorParams->get<ConfigValue>("test-interval")->get();
	string logfile = monitorParams->get<ConfigString>("logfile")->read();
	string port = monitorParams->get<ConfigValue>("switch-port")->get();
58
	string salt = monitorParams->get<ConfigString>("password-salt")->read();
59
	list<string> nodes = cluster->get<ConfigStringList>("nodes")->read();
François Grisez's avatar
François Grisez committed
60

61 62 63
	string domain;
	try {
		domain = findDomain();
64
	} catch (const FlexisipException &e) {
65
		LOGF("Monitor: cannot find domain. %s", e.str().c_str());
66
		exit(EXIT_FAILURE);
67
	}
François Grisez's avatar
François Grisez committed
68

69
	if (salt.empty()) {
70
		LOGF("Monitor: no salt set");
71
		exit(EXIT_FAILURE);
72
	}
François Grisez's avatar
François Grisez committed
73

74
	if (nodes.empty()) {
75
		LOGF("Monitor: no nodes declared in the cluster section");
76
		exit(EXIT_FAILURE);
77 78
	}

79 80 81 82 83 84 85 86 87 88 89
	char **args = new char *[9 + nodes.size() + 1];
	args[0] = strdup(SCRIPT_PATH.c_str());
	args[1] = strdup("--interval");
	args[2] = strdup(interval.c_str());
	args[3] = strdup("--log");
	args[4] = strdup(logfile.c_str());
	args[5] = strdup("--port");
	args[6] = strdup(port.c_str());
	args[7] = strdup(domain.c_str());
	args[8] = strdup(salt.c_str());
	int i = 9;
90
	for (list<string>::const_iterator it = nodes.cbegin(); it != nodes.cend(); it++) {
91
		args[i] = strdup((*it).c_str());
92 93 94
		i++;
	}
	args[i] = NULL;
95

96
	if (write(socket, "ok", 3) == -1) {
97 98 99
		exit(-1);
	}
	close(socket);
100

101 102
	execvp(args[0], args);
}
103

104 105
string Monitor::findLocalAddress(const list<string> &nodes) {
	RtpSession *session = rtp_session_new(RTP_SESSION_RECVONLY);
106 107
	for (list<string>::const_iterator it = nodes.cbegin(); it != nodes.cend(); it++) {
		if (rtp_session_set_local_addr(session, (*it).c_str(), 0, 0) != -1) {
108
			rtp_session_destroy(session);
109
			return *it;
110 111 112 113 114
		}
	}
	return "";
}

115
void Monitor::createAccounts() {
116 117
	url_t url;
	memset(&url, 0, sizeof(url_t));
118
	AuthDbBackend *authDb = AuthDbBackend::get();
119
	GenericStruct *cluster = GenericManager::get()->getRoot()->get<GenericStruct>("cluster");
120 121
	GenericStruct *monitorConf = GenericManager::get()->getRoot()->get<GenericStruct>("monitor");
	string salt = monitorConf->get<ConfigString>("password-salt")->read();
122 123
	list<string> nodes = cluster->get<ConfigStringList>("nodes")->read();

François Grisez's avatar
François Grisez committed
124 125 126
	string domaine = findDomain();
	url.url_host = domaine.c_str();

127
	string localIP = findLocalAddress(nodes);
128
	if (localIP == "") {
129
		LOGA("Monitor::createAccounts(): Could not find local IP address");
130 131
		exit(-1);
	}
François Grisez's avatar
François Grisez committed
132 133 134 135

	string password = generatePassword(localIP, salt).c_str();
	string username = generateUsername(CALLER_PREFIX, localIP);
	url.url_user = username.c_str();
136
	authDb->createAccount(&url, url.url_user, password.c_str(), PASSWORD_CACHE_EXPIRE);
François Grisez's avatar
François Grisez committed
137 138 139

	username = generateUsername(CALLEE_PREFIX, localIP).c_str();
	url.url_user = username.c_str();
140
	authDb->createAccount(&url, url.url_user, password.c_str(), PASSWORD_CACHE_EXPIRE);
141 142
}

143
bool Monitor::isLocalhost(const string &host) {
144
	return host == "localhost" || host == "127.0.0.1" || host == "::1" || host == "localhost.localdomain";
145 146
}

147
bool Monitor::notLocalhost(const string &host) {
148 149 150
	return !isLocalhost(host);
}

151
string Monitor::md5sum(const string &s) {
François Grisez's avatar
François Grisez committed
152
	char digest[2 * SU_MD5_DIGEST_SIZE + 1];
153 154 155 156 157 158 159
	su_md5_t ctx;
	su_md5_init(&ctx);
	su_md5_strupdate(&ctx, s.c_str());
	su_md5_hexdigest(&ctx, digest);
	return digest;
}

160 161
string Monitor::generateUsername(const string &prefix, const string &host) {
	return prefix + "-" + md5sum(host);
162 163
}

164
string Monitor::generatePassword(const string &host, const string &salt) {
165 166 167 168 169 170
	return md5sum(host + salt);
}

string Monitor::findDomain() {
	GenericStruct *registrarConf = GenericManager::get()->getRoot()->get<GenericStruct>("module::Registrar");
	list<string> domains = registrarConf->get<ConfigStringList>("reg-domains")->read();
171
	if (domains.size() == 0) {
172 173 174
		throw FlexisipException("No domain declared in the registar module parameters");
	}
	list<string>::const_iterator it = find_if(domains.cbegin(), domains.cend(), notLocalhost);
175
	if (it == domains.cend()) {
176 177 178 179
		throw FlexisipException("Only localhost is declared as registrar domain");
	}
	return *it;
}