/[resiprocate]/main/reTurn/ReTurnConfig.cxx
ViewVC logotype

Contents of /main/reTurn/ReTurnConfig.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11221 - (show annotations) (download)
Sat Aug 30 14:15:44 2014 UTC (5 years, 2 months ago) by dpocock
File MIME type: text/plain
File size: 15738 byte(s)
reTurn: Logging: configuring Syslog facility
1 #include <fstream>
2 #include <iostream>
3 #include <string>
4 #include <sstream>
5
6 #ifndef WIN32
7 #include <csignal>
8 #endif
9
10 #include <sys/stat.h>
11
12 #include <boost/bind.hpp>
13
14 #include "ReTurnConfig.hxx"
15
16 #include "ReTurnSubsystem.hxx"
17 #include "rutil/ParseBuffer.hxx"
18 #include <rutil/Logger.hxx>
19
20 #define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
21
22 #ifdef PACKAGE_VERSION
23 #define SOFTWARE_STRING "reTURNServer " PACKAGE_VERSION " (RFC5389)"
24 #else
25 #define SOFTWARE_STRING "reTURNServer (RFC5389)"
26 #endif
27
28 using namespace std;
29 using namespace resip;
30
31 namespace reTurn {
32
33 ReTurnConfig::ReTurnConfig() :
34 mSoftwareName(SOFTWARE_STRING),
35 mPadSoftwareName(true),
36 mTurnPort(3478),
37 mTlsTurnPort(5349),
38 mAltStunPort(0), // Note: The default is to disable RFC3489 binding support
39 mTurnAddress(asio::ip::address::from_string("0.0.0.0")),
40 mTurnV6Address(asio::ip::address::from_string("::0")),
41 mAltStunAddress(asio::ip::address::from_string("0.0.0.0")),
42 mAuthenticationRealm("reTurn"),
43 mUserDatabaseCheckInterval(60),
44 mNonceLifetime(3600), // 1 hour - at least 1 hours is recommended by the RFC
45 mAllocationPortRangeMin(49152), // must be even - This default range is the Dynamic and/or Private Port range - recommended by RFC
46 mAllocationPortRangeMax(65535), // must be odd
47 mDefaultAllocationLifetime(600), // 10 minutes
48 mMaxAllocationLifetime(3600), // 1 hour
49 mMaxAllocationsPerUser(0), // 0 - no max
50 mTlsServerCertificateFilename("server.pem"),
51 mTlsServerPrivateKeyFilename(""),
52 mTlsTempDhFilename("dh512.pem"),
53 mTlsPrivateKeyPassword(""),
54 mUsersDatabaseFilename(""),
55 mUserDatabaseHashedPasswords(false),
56 mLoggingType("cout"),
57 mSyslogFacility("LOG_DAEMON"),
58 mLoggingLevel("INFO"),
59 mLoggingFilename("reTurnServer.log"),
60 mLoggingFileMaxLineCount(50000), // 50000 about 5M size
61 mDaemonize(false),
62 mPidFile(""),
63 mRunAsUser(""),
64 mRunAsGroup("")
65 {
66 }
67
68 void ReTurnConfig::parseConfig(int argc, char** argv, const resip::Data& defaultConfigFilename)
69 {
70 resip::ConfigParse::parseConfig(argc, argv, defaultConfigFilename);
71
72 mSoftwareName = getConfigData("SoftwareName", SOFTWARE_STRING);
73 mPadSoftwareName = getConfigBool("PadSoftwareName", true);
74 mTurnPort = getConfigUnsignedShort("TurnPort", mTurnPort);
75 mTlsTurnPort = getConfigUnsignedShort("TlsTurnPort", mTlsTurnPort);
76 mAltStunPort = getConfigUnsignedShort("AltStunPort", mAltStunPort);
77 mTurnAddress = asio::ip::address::from_string(getConfigData("TurnAddress", "0.0.0.0").c_str());
78 mTurnV6Address = asio::ip::address::from_string(getConfigData("TurnV6Address", "::0").c_str());
79 mAltStunAddress = asio::ip::address::from_string(getConfigData("AltStunAddress", "0.0.0.0").c_str());
80 mAuthenticationRealm = getConfigData("AuthenticationRealm", mAuthenticationRealm);
81 mUserDatabaseCheckInterval = getConfigUnsignedShort("UserDatabaseCheckInterval", 60);
82 mNonceLifetime = getConfigUnsignedLong("NonceLifetime", mNonceLifetime);
83 mAllocationPortRangeMin = getConfigUnsignedShort("AllocationPortRangeMin", mAllocationPortRangeMin);
84 mAllocationPortRangeMax = getConfigUnsignedShort("AllocationPortRangeMax", mAllocationPortRangeMax);
85 mDefaultAllocationLifetime = getConfigUnsignedLong("DefaultAllocationLifetime", mDefaultAllocationLifetime);
86 mMaxAllocationLifetime = getConfigUnsignedLong("MaxAllocationLifetime", mMaxAllocationLifetime);
87 mMaxAllocationsPerUser = getConfigUnsignedLong("MaxAllocationsPerUser", mMaxAllocationsPerUser);
88 mTlsServerCertificateFilename = getConfigData("TlsServerCertificateFilename", mTlsServerCertificateFilename);
89 mTlsServerPrivateKeyFilename = getConfigData("TlsServerPrivateKeyFilename", mTlsServerPrivateKeyFilename);
90 mTlsTempDhFilename = getConfigData("TlsTempDhFilename", mTlsTempDhFilename);
91 mTlsPrivateKeyPassword = getConfigData("TlsPrivateKeyPassword", mTlsPrivateKeyPassword);
92 mUserDatabaseHashedPasswords = getConfigBool("UserDatabaseHashedPasswords", mUserDatabaseHashedPasswords);
93 mLoggingType = getConfigData("LoggingType", mLoggingType);
94 mSyslogFacility = getConfigData("SyslogFacility", "LOG_DAEMON");
95 mLoggingLevel = getConfigData("LoggingLevel", mLoggingLevel);
96 mLoggingFilename = getConfigData("LogFilename", mLoggingFilename);
97 mLoggingFileMaxLineCount = getConfigUnsignedLong("LogFileMaxLines", mLoggingFileMaxLineCount);
98 mDaemonize = getConfigBool("Daemonize", mDaemonize);
99 mPidFile = getConfigData("PidFile", mPidFile);
100 mRunAsUser = getConfigData("RunAsUser", mRunAsUser);
101 mRunAsGroup = getConfigData("RunAsGroup", mRunAsGroup);
102
103 if(mPadSoftwareName)
104 {
105 // padding size to a multiple of 4, to help compatibility with older clients
106 while(mSoftwareName.size() % 4)
107 {
108 mSoftwareName += ' ';
109 }
110 }
111
112 // fork is not possible on Windows
113 #ifdef WIN32
114 if(mDaemonize)
115 {
116 throw ConfigParse::Exception("Unable to fork/daemonize on Windows, please check the config", __FILE__, __LINE__);
117 }
118 #endif
119
120 // TODO: For ShortTermCredentials use mAuthenticationCredentials[username] = password;
121
122
123 // LongTermCredentials
124 mUsersDatabaseFilename = getConfigData("UserDatabaseFile", "");
125 if(mUsersDatabaseFilename.size() == 0)
126 {
127 throw ConfigParse::Exception("Missing user database option! Expected \"UserDatabaseFile = file location\".", __FILE__, __LINE__);
128 }
129
130 AddBasePathIfRequired(mLoggingFilename);
131 AddBasePathIfRequired(mTlsServerCertificateFilename);
132 AddBasePathIfRequired(mTlsServerPrivateKeyFilename);
133 AddBasePathIfRequired(mTlsTempDhFilename);
134 AddBasePathIfRequired(mUsersDatabaseFilename);
135
136 authParse(mUsersDatabaseFilename);
137 }
138
139 ReTurnConfig::~ReTurnConfig()
140 {
141 }
142
143 void
144 ReTurnConfig::addUser(const resip::Data& username, const resip::Data& password, const resip::Data& realm)
145 {
146 UserAuthData newUser(
147 mUserDatabaseHashedPasswords ?
148 UserAuthData::createFromHex(username, realm, password)
149 : UserAuthData::createFromPassword(username, realm, password)
150 );
151 mRealmUsersAuthenticaionCredentials[std::make_pair(username, realm)] = newUser.getHa1();
152 RealmUsers& realmUsers(mUsers[realm]);
153 realmUsers.insert(pair<resip::Data,UserAuthData>(username, newUser));
154 }
155
156 void
157 ReTurnConfig::authParse(const resip::Data& accountDatabaseFilename)
158 {
159 std::ifstream accountDatabaseFile(accountDatabaseFilename.c_str());
160 std::string sline;
161 int lineNbr = 0, userCount = 0;
162 if(!accountDatabaseFile)
163 {
164 throw ReTurnConfig::Exception("Error opening/reading user database file!", __FILE__, __LINE__);
165 }
166
167 mUsers.clear();
168 mRealmUsersAuthenticaionCredentials.clear();
169
170 while(std::getline(accountDatabaseFile, sline))
171 {
172 AccountState accountState;
173 Data username;
174 Data password;
175 Data realm;
176 Data state;
177 Data line(sline);
178 ParseBuffer pb(line);
179
180 lineNbr++;
181
182 // Jump over empty lines.
183 if(line.size() == 0)
184 {
185 continue;
186 }
187
188 pb.skipWhitespace();
189 if(!pb.eof() && *pb.position() == '#')
190 {
191 // Line is commented out, skip it
192 continue;
193 }
194
195 const char * anchor = pb.position();
196
197 pb.skipToOneOf(" :");
198
199 if (pb.eof())
200 {
201 ErrLog(<< "Missing or invalid credentials at line " << lineNbr);
202 continue;
203 }
204
205 pb.data(username, anchor);
206
207 pb.skipToChar(':');
208 if (!pb.eof())
209 {
210 pb.skipChar(':');
211 pb.skipWhitespace();
212 }
213
214 anchor = pb.position();
215 pb.skipToOneOf(" :");
216
217 if (pb.eof())
218 {
219 ErrLog(<< "Missing or invalid credentials at line " << lineNbr);
220 continue;
221 }
222
223 pb.data(password, anchor);
224
225 pb.skipToChar(':');
226 if (!pb.eof())
227 {
228 pb.skipChar(':');
229 pb.skipWhitespace();
230 }
231
232 anchor = pb.position();
233 pb.skipToOneOf(" :");
234
235 if (pb.eof())
236 {
237 ErrLog(<< "Missing or invalid credentials at line " << lineNbr);
238 continue;
239 }
240
241 pb.data(realm, anchor);
242
243 pb.skipToChar(':');
244 if (!pb.eof())
245 {
246 pb.skipChar(':');
247 pb.skipWhitespace();
248 }
249
250 anchor = pb.position();
251 pb.skipToOneOf(" \t\n");
252
253 pb.data(state, anchor);
254 state.lowercase();
255
256 if (state.size() != 0)
257 {
258 if(state == "authorized")
259 {
260 accountState = AUTHORIZED;
261 }
262 else if(state == "restricted")
263 {
264 accountState = RESTRICTED;
265 }
266 else if(state == "refused")
267 {
268 accountState = REFUSED;
269 }
270 else
271 {
272 ErrLog(<< "Invalid state value at line " << lineNbr << ", state= " << state);
273 continue;
274 }
275 }
276 else
277 {
278 ErrLog(<< "Missing state value at line " << lineNbr);
279 continue;
280 }
281
282 if(accountState != REFUSED)
283 {
284 try {
285 addUser(username, password, realm);
286 } catch (ConfigParse::Exception& ex) {
287 ErrLog(<< "Exception adding user: " << username << ", cause: " << ex << ", skipping record");
288 }
289 }
290 userCount++;
291 }
292
293 InfoLog(<< "Processed " << userCount << " user(s) from " << lineNbr << " line(s) in " << accountDatabaseFilename);
294 accountDatabaseFile.close();
295
296 if(mUsers.find(mAuthenticationRealm) == mUsers.end())
297 {
298 WarningLog(<<"AuthenticationRealm = " << mAuthenticationRealm << " but no users defined for this realm in " << accountDatabaseFilename);
299 }
300 }
301
302 void
303 ReTurnConfig::printHelpText(int argc, char **argv)
304 {
305 std::cerr << "Command line format is:" << std::endl;
306 std::cerr << " " << removePath(argv[0]) << " [<ConfigFilename>] [--<ConfigValueName>=<ConfigValue>] [--<ConfigValueName>=<ConfigValue>] ..." << std::endl;
307 std::cerr << "Sample Command lines:" << std::endl;
308 std::cerr << " " << removePath(argv[0]) << " reTurnServer.config --LogLevel=INFO" << std::endl;
309 }
310
311 // LongTermAuthentication
312 bool
313 ReTurnConfig::isUserNameValid(const resip::Data& username, const resip::Data& realm) const
314 {
315 return getUser(username, realm).get() != NULL;
316 }
317
318 Data
319 ReTurnConfig::getHa1ForUsername(const Data& username, const resip::Data& realm) const
320 {
321 ReadLock lock(mUserDataMutex);
322 std::map<RealmUserPair, resip::Data>::const_iterator it = mRealmUsersAuthenticaionCredentials.find(std::make_pair(username, realm));
323 if(it != mRealmUsersAuthenticaionCredentials.end())
324 {
325 return it->second;
326 }
327 else
328 {
329 return Data::Empty;
330 }
331 }
332
333 std::auto_ptr<UserAuthData>
334 ReTurnConfig::getUser(const resip::Data& userName, const resip::Data& realm) const
335 {
336 ReadLock lock(mUserDataMutex);
337 std::auto_ptr<UserAuthData> ret(0);
338 std::map<resip::Data,RealmUsers>::const_iterator it = mUsers.find(realm);
339 if(it == mUsers.end())
340 return ret;
341
342 RealmUsers realmUsers = it->second;
343 RealmUsers::const_iterator it2 = realmUsers.find(userName);
344 if(it2 == realmUsers.end())
345 return ret;
346
347 return std::auto_ptr<UserAuthData>(new UserAuthData(it2->second));
348 }
349
350 bool ReTurnUserFileScanner::mHup = false;
351
352 ReTurnUserFileScanner::ReTurnUserFileScanner(asio::io_service& ioService, ReTurnConfig& reTurnConfig)
353 : mLoadedTime(time(0)),
354 mReTurnConfig(reTurnConfig),
355 mLoopInterval(3),
356 mNextFileCheck(mLoadedTime + reTurnConfig.mUserDatabaseCheckInterval),
357 mTimer(ioService, boost::posix_time::seconds(mLoopInterval))
358 {
359 mHup = false;
360 #ifndef WIN32
361 // After all parsing is done, we can set up the signal handler
362 // For the moment, HUP will re-read the users file.
363 // It won't re-read the whole config
364 signal(SIGHUP, onSignal);
365 #endif
366 }
367
368 void
369 ReTurnUserFileScanner::start()
370 {
371 int timerInterval = mLoopInterval;
372
373 if(timerInterval > 0)
374 {
375 mTimer.expires_from_now(boost::posix_time::seconds(timerInterval));
376 mTimer.async_wait(boost::bind(&ReTurnUserFileScanner::timeout, this, asio::placeholders::error));
377 }
378 }
379
380 bool
381 ReTurnUserFileScanner::hasUserFileChanged()
382 {
383 StackLog(<<"checking user database freshness");
384
385 time_t latestFileTS = 0;
386 #ifdef WIN32
387 StackLog(<<"not yet implemented on Windows");
388 #else
389 struct stat user_stat;
390 int ret = stat(mReTurnConfig.mUsersDatabaseFilename.c_str(), &user_stat);
391 if(ret == 0)
392 {
393 latestFileTS = user_stat.st_mtime;
394 }
395 else
396 {
397 ErrLog(<<"Call to stat failed, not checking " << mReTurnConfig.mUsersDatabaseFilename << " freshness, errno = " << errno);
398 }
399 #endif
400
401 return latestFileTS > mLoadedTime;
402 }
403
404 void
405 ReTurnUserFileScanner::timeout(const asio::error_code& e)
406 {
407 bool mustReload = mHup;
408
409 if(!mustReload && mReTurnConfig.mUserDatabaseCheckInterval > 0)
410 {
411 time_t now = time(0);
412 if(now >= mNextFileCheck)
413 {
414 mustReload = hasUserFileChanged();
415 mNextFileCheck = now + mReTurnConfig.mUserDatabaseCheckInterval;
416 }
417 }
418
419 if(mustReload)
420 {
421 InfoLog(<<"change in user database detected, reloading...");
422 WriteLock lock(mReTurnConfig.mUserDataMutex);
423 try
424 {
425 mReTurnConfig.authParse(mReTurnConfig.mUsersDatabaseFilename);
426 InfoLog(<<"user database reload completed");
427 mLoadedTime = time(0);
428 mNextFileCheck = mLoadedTime + mReTurnConfig.mUserDatabaseCheckInterval;
429 }
430 catch (ReTurnConfig::Exception& ex)
431 {
432 WarningLog(<<"user reload failed, will check again in " << mReTurnConfig.mUserDatabaseCheckInterval << " second(s).");
433 mNextFileCheck = time(0) + mReTurnConfig.mUserDatabaseCheckInterval;
434 }
435 }
436
437 // clear any signal
438 mHup = false;
439
440 // set the timer again:
441 start();
442 }
443
444 void
445 ReTurnUserFileScanner::onSignal(int signum)
446 {
447 #ifndef WIN32
448 if(signum == SIGHUP)
449 {
450 InfoLog(<<"HUP signal received, scheduling a users.txt reload");
451 mHup = true;
452 // also rotate the log file
453 Log::reset();
454 }
455 else
456 {
457 WarningLog(<<"received unexpected signal number " << signum);
458 }
459 #endif
460 }
461
462 } // namespace
463
464
465 /* ====================================================================
466
467 Copyright (c) 2007-2008, Plantronics, Inc.
468 All rights reserved.
469
470 Redistribution and use in source and binary forms, with or without
471 modification, are permitted provided that the following conditions are
472 met:
473
474 1. Redistributions of source code must retain the above copyright
475 notice, this list of conditions and the following disclaimer.
476
477 2. Redistributions in binary form must reproduce the above copyright
478 notice, this list of conditions and the following disclaimer in the
479 documentation and/or other materials provided with the distribution.
480
481 3. Neither the name of Plantronics nor the names of its contributors
482 may be used to endorse or promote products derived from this
483 software without specific prior written permission.
484
485 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
486 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
487 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
488 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
489 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
490 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
491 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
492 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
493 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
494 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
495 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
496
497 ==================================================================== */

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27