annotate oscgroups/OscGroupServer.cpp @ 101:52e44ee1c791 tip master

enabled all scores in autostart script
author Rob Canning <rc@kiben.net>
date Tue, 21 Apr 2015 16:20:57 +0100
parents 0ae87af84e2f
children
rev   line source
rob@76 1 /*
rob@76 2 OSCgroups -- open sound control groupcasting infrastructure
rob@76 3 Copyright (C) 2005 Ross Bencina
rob@76 4
rob@76 5 This program is free software; you can redistribute it and/or
rob@76 6 modify it under the terms of the GNU General Public License
rob@76 7 as published by the Free Software Foundation; either version 2
rob@76 8 of the License, or (at your option) any later version.
rob@76 9
rob@76 10 This program is distributed in the hope that it will be useful,
rob@76 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
rob@76 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
rob@76 13 GNU General Public License for more details.
rob@76 14
rob@76 15 You should have received a copy of the GNU General Public License
rob@76 16 along with this program; if not, write to the Free Software
rob@76 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
rob@76 18 */
rob@76 19
rob@76 20 #include "OscGroupServer.h"
rob@76 21
rob@76 22 #include <cassert>
rob@76 23 #include <cstring>
rob@76 24 #include <ctime>
rob@76 25 #include <iostream>
rob@76 26 #include <fstream>
rob@76 27 #include <cstdlib>
rob@76 28
rob@76 29 #include "osc/OscReceivedElements.h"
rob@76 30 #include "osc/OscOutboundPacketStream.h"
rob@76 31
rob@76 32 #include "ip/UdpSocket.h"
rob@76 33 #include "osc/OscPacketListener.h"
rob@76 34 #include "ip/TimerListener.h"
rob@76 35
rob@76 36 #include "GroupServer.h"
rob@76 37
rob@76 38 #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug
rob@76 39 namespace std {
rob@76 40 using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'.
rob@76 41 using ::__strcmp__; // avoid error: E2316 '__strcmp__' is not a member of 'std'.
rob@76 42 }
rob@76 43 #endif
rob@76 44
rob@76 45
rob@76 46 static std::ostream& Log()
rob@76 47 {
rob@76 48 std::time_t t;
rob@76 49 std::time( &t );
rob@76 50
rob@76 51 // ctime() returns a constant width 26 char string including trailing \0
rob@76 52 // the fields are all constant width.
rob@76 53 char s[26];
rob@76 54 std::strcpy( s, std::ctime( &t ) );
rob@76 55 s[24] = '\0'; // remove trailing null
rob@76 56
rob@76 57 std::cout << s << ": ";
rob@76 58 return std::cout;
rob@76 59 }
rob@76 60
rob@76 61
rob@76 62 static const char *UserStatusToString( GroupServer::UserStatus userStatus )
rob@76 63 {
rob@76 64 switch( userStatus ){
rob@76 65 case GroupServer::USER_STATUS_OK:
rob@76 66 return "ok";
rob@76 67 case GroupServer::USER_STATUS_WRONG_PASSWORD:
rob@76 68 return "wrong password";
rob@76 69 case GroupServer::USER_STATUS_SERVER_LIMIT_REACHED:
rob@76 70 return "user limit reached";
rob@76 71 case GroupServer::USER_STATUS_UNKNOWN:
rob@76 72 /* FALLTHROUGH */ ;
rob@76 73 }
rob@76 74
rob@76 75 return "unknown";
rob@76 76 }
rob@76 77
rob@76 78
rob@76 79 class OscGroupServerListener
rob@76 80 : public osc::OscPacketListener
rob@76 81 , public TimerListener
rob@76 82 {
rob@76 83
rob@76 84 GroupServer groupServer_;
rob@76 85 UdpSocket& externalSocket_;
rob@76 86
rob@76 87 #define IP_MTU_SIZE 1536
rob@76 88 char buffer_[IP_MTU_SIZE];
rob@76 89 osc::OutboundPacketStream resultStream_;
rob@76 90 std::size_t emptyResultSize_;
rob@76 91
rob@76 92 void user_alive( const osc::ReceivedMessage& m,
rob@76 93 const IpEndpointName& remoteEndpoint )
rob@76 94 {
rob@76 95 // /groupserver/user_alive
rob@76 96 // userName password
rob@76 97 // privateIpAddress privatePort
rob@76 98 // groupName0 groupPassword0 groupName1 groupPassword1 ...
rob@76 99
rob@76 100 osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
rob@76 101 const char *userName, *userPassword;
rob@76 102 osc::int32 privateAddress, privatePort;
rob@76 103
rob@76 104 args >> userName >> userPassword >> privateAddress >> privatePort;
rob@76 105
rob@76 106 // addresses are transmitted as ones complement (bit inverse)
rob@76 107 // to avoid problems with buggy NATs trying to re-write addresses
rob@76 108 privateAddress = ~privateAddress;
rob@76 109
rob@76 110 IpEndpointName privateEndpoint( privateAddress, privatePort );
rob@76 111
rob@76 112 int groupCount = (m.ArgumentCount() - 4) / 2;
rob@76 113 const char **groupNamesAndPasswords = 0;
rob@76 114 GroupServer::UserStatus *userGroupsStatus = 0;
rob@76 115 if( groupCount > 0 ){
rob@76 116 groupNamesAndPasswords = new const char*[ groupCount * 2 ];
rob@76 117 int i = 0;
rob@76 118 while( !args.Eos() ){
rob@76 119 args >> groupNamesAndPasswords[i++]; // group name
rob@76 120 args >> groupNamesAndPasswords[i++]; // group password
rob@76 121 }
rob@76 122
rob@76 123 userGroupsStatus = new GroupServer::UserStatus[ groupCount ];
rob@76 124 for( int j=0; j < groupCount; ++j )
rob@76 125 userGroupsStatus[j] = GroupServer::USER_STATUS_UNKNOWN;
rob@76 126 }
rob@76 127
rob@76 128 GroupServer::UserStatus userStatus =
rob@76 129 groupServer_.UserAlive( userName, userPassword,
rob@76 130 privateEndpoint, remoteEndpoint,
rob@76 131 groupNamesAndPasswords, userGroupsStatus, groupCount );
rob@76 132
rob@76 133 resultStream_ << osc::BeginMessage( "/groupclient/user_alive_status" )
rob@76 134 << userName
rob@76 135 << userPassword
rob@76 136 << UserStatusToString( userStatus )
rob@76 137 << osc::EndMessage;
rob@76 138
rob@76 139 if( userStatus == GroupServer::USER_STATUS_OK ){
rob@76 140 for( int i=0; i < groupCount; ++i ){
rob@76 141 const char *groupName = groupNamesAndPasswords[i*2];
rob@76 142 const char *groupPassword = groupNamesAndPasswords[i*2 + 1];
rob@76 143
rob@76 144 resultStream_
rob@76 145 << osc::BeginMessage( "/groupclient/user_group_status" )
rob@76 146 << userName
rob@76 147 << userPassword
rob@76 148 << groupName
rob@76 149 << groupPassword
rob@76 150 << UserStatusToString( userGroupsStatus[i] )
rob@76 151 << osc::EndMessage;
rob@76 152 }
rob@76 153 }
rob@76 154
rob@76 155 delete [] groupNamesAndPasswords;
rob@76 156 delete [] userGroupsStatus;
rob@76 157 }
rob@76 158
rob@76 159 void MakeUserInfoMessage( osc::OutboundPacketStream& p,
rob@76 160 User *user, std::time_t currentTime )
rob@76 161 {
rob@76 162 // addresses are transmitted as ones complement (bit inverse)
rob@76 163 // to avoid problems with buggy NATs trying to re-write addresses
rob@76 164
rob@76 165 p << osc::BeginMessage( "/groupclient/user_info" )
rob@76 166 << user->name.c_str()
rob@76 167 << ~((osc::int32)user->privateEndpoint.address)
rob@76 168 << (osc::int32)user->privateEndpoint.port
rob@76 169 << ~((osc::int32)user->publicEndpoint.address)
rob@76 170 << (osc::int32)user->publicEndpoint.port
rob@76 171 << (osc::int32)user->SecondsSinceLastAliveReceived( currentTime );
rob@76 172
rob@76 173 for( User::const_group_iterator i = user->groups_begin();
rob@76 174 i != user->groups_end(); ++i )
rob@76 175 p << (*i)->name.c_str();
rob@76 176
rob@76 177 p << osc::EndMessage;
rob@76 178 }
rob@76 179
rob@76 180 void get_group_users_info( const osc::ReceivedMessage& m,
rob@76 181 const IpEndpointName& remoteEndpoint )
rob@76 182 {
rob@76 183 (void) remoteEndpoint; // suppress unused parameter warning
rob@76 184
rob@76 185 // /groupserver/get_group_users_info group-name group-password
rob@76 186
rob@76 187 osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
rob@76 188 const char *groupName, *groupPassword;
rob@76 189
rob@76 190 args >> groupName >> groupPassword >> osc::EndMessage;
rob@76 191
rob@76 192 Group *group = groupServer_.FindGroup( groupName );
rob@76 193 if( group && group->password.compare( groupPassword ) == 0 ){
rob@76 194 std::time_t currentTime = time(0);
rob@76 195
rob@76 196 for( Group::const_user_iterator i = group->users_begin();
rob@76 197 i != group->users_end(); ++i )
rob@76 198 MakeUserInfoMessage( resultStream_, *i, currentTime );
rob@76 199 }
rob@76 200
rob@76 201 // we don't return a result to the client in the case where the group
rob@76 202 // doesn't exist, or the password is wrong because legitimate clients
rob@76 203 // will have already successfully joined the group, or they will have
rob@76 204 // received an error message when they tried to join.
rob@76 205 }
rob@76 206
rob@76 207 protected:
rob@76 208
rob@76 209 virtual void ProcessMessage( const osc::ReceivedMessage& m,
rob@76 210 const IpEndpointName& remoteEndpoint )
rob@76 211 {
rob@76 212 try{
rob@76 213
rob@76 214 if( std::strcmp( m.AddressPattern(), "/groupserver/user_alive" ) == 0 ){
rob@76 215 user_alive( m, remoteEndpoint );
rob@76 216 }else if( std::strcmp( m.AddressPattern(), "/groupserver/get_group_users_info" ) == 0 ){
rob@76 217 get_group_users_info( m, remoteEndpoint );
rob@76 218 }
rob@76 219
rob@76 220 }catch( osc::Exception& e ){
rob@76 221 Log() << "error while parsing message: " << e.what() << std::endl;
rob@76 222 }
rob@76 223 }
rob@76 224
rob@76 225 public:
rob@76 226 OscGroupServerListener( int timeoutSeconds, int maxUsers, int maxGroups,
rob@76 227 UdpSocket& externalSocket )
rob@76 228 : groupServer_( timeoutSeconds, maxUsers, maxGroups )
rob@76 229 , externalSocket_( externalSocket )
rob@76 230 , resultStream_( buffer_, IP_MTU_SIZE )
rob@76 231 {
rob@76 232 resultStream_ << osc::BeginBundle();
rob@76 233 resultStream_ << osc::EndBundle;
rob@76 234 emptyResultSize_ = resultStream_.Size();
rob@76 235 }
rob@76 236
rob@76 237 virtual void ProcessPacket( const char *data, int size,
rob@76 238 const IpEndpointName& remoteEndpoint )
rob@76 239 {
rob@76 240 resultStream_.Clear();
rob@76 241 resultStream_ << osc::BeginBundle();
rob@76 242
rob@76 243 osc::OscPacketListener::ProcessPacket( data, size, remoteEndpoint );
rob@76 244
rob@76 245 resultStream_ << osc::EndBundle;
rob@76 246
rob@76 247 assert( resultStream_.IsReady() );
rob@76 248
rob@76 249 if( resultStream_.Size() != emptyResultSize_ ){
rob@76 250 char addressString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
rob@76 251 remoteEndpoint.AddressAndPortAsString( addressString );
rob@76 252 Log() << "responding to request from " << addressString << "." << std::endl;
rob@76 253
rob@76 254 externalSocket_.SendTo(
rob@76 255 remoteEndpoint, resultStream_.Data(), resultStream_.Size() );
rob@76 256 }
rob@76 257 }
rob@76 258
rob@76 259 virtual void TimerExpired()
rob@76 260 {
rob@76 261 groupServer_.PurgeStaleUsers();
rob@76 262 }
rob@76 263 };
rob@76 264
rob@76 265
rob@76 266 void RunOscGroupServerUntilSigInt(
rob@76 267 int port, int timeoutSeconds, int maxUsers, int maxGroups, const char *logFile )
rob@76 268 {
rob@76 269 std::ofstream *logStream = 0;
rob@76 270 std::streambuf *oldCoutRdbuf = std::cout.rdbuf();
rob@76 271
rob@76 272 try{
rob@76 273 if( logFile ){
rob@76 274 std::cout << "oscgroupserver redirecting output to " << logFile << std::endl;
rob@76 275 logStream = new std::ofstream( logFile, std::ios_base::app );
rob@76 276 std::cout.rdbuf( logStream->rdbuf() );
rob@76 277 }
rob@76 278
rob@76 279 UdpReceiveSocket externalSocket(
rob@76 280 IpEndpointName( IpEndpointName::ANY_ADDRESS, port ) );
rob@76 281 OscGroupServerListener externalSocketListener(
rob@76 282 timeoutSeconds, maxUsers, maxGroups, externalSocket );
rob@76 283
rob@76 284 SocketReceiveMultiplexer mux;
rob@76 285 mux.AttachSocketListener( &externalSocket, &externalSocketListener );
rob@76 286
rob@76 287 // timer is used for purging inactive users
rob@76 288 mux.AttachPeriodicTimerListener( (timeoutSeconds * 1000) / 10, &externalSocketListener );
rob@76 289
rob@76 290 Log() << "oscgroupserver listening on port " << port << "...\n"
rob@76 291 << "timeoutSeconds=" << timeoutSeconds << ", "
rob@76 292 << "maxUsers=" << maxUsers << ", "
rob@76 293 << "maxGroups=" << maxGroups << ", "
rob@76 294 << "logFile=" << ((logFile) ? logFile : "stdout") << "\n"
rob@76 295 << "press ctrl-c to end" << std::endl;
rob@76 296
rob@76 297 mux.RunUntilSigInt();
rob@76 298
rob@76 299 Log() << "finishing." << std::endl;
rob@76 300
rob@76 301 }catch( std::exception& e ){
rob@76 302 Log() << e.what() << std::endl;
rob@76 303 Log() << "finishing." << std::endl;
rob@76 304 }
rob@76 305
rob@76 306 std::cout.rdbuf( oldCoutRdbuf );
rob@76 307 delete logStream;
rob@76 308 }
rob@76 309
rob@76 310
rob@76 311 static void usage()
rob@76 312 {
rob@76 313 std::cerr << "usage: oscgroupserver [-p port] [-t timeoutSeconds] [-u maxUsers] [-g maxGroups] [-l logfile]\n";
rob@76 314 }
rob@76 315
rob@76 316
rob@76 317 int oscgroupserver_main(int argc, char* argv[])
rob@76 318 {
rob@76 319 if( argc % 2 != 1 ){
rob@76 320 usage();
rob@76 321 return 0;
rob@76 322 }
rob@76 323
rob@76 324 int port = 22242;
rob@76 325 int timeoutSeconds = 60;
rob@76 326 int maxUsers = 100;
rob@76 327 int maxGroups = 50;
rob@76 328 const char *logFile = 0;
rob@76 329
rob@76 330 int argIndex = 1;
rob@76 331 while( argIndex < argc ){
rob@76 332 const char *selector = argv[argIndex];
rob@76 333 const char *value = argv[argIndex + 1];
rob@76 334 if( selector[0] == '-' && selector[1] != '\0' && selector[2] == '\0' ){
rob@76 335 switch( selector[1] ){
rob@76 336 case 'p':
rob@76 337 port = std::atoi( value );
rob@76 338 break;
rob@76 339 case 't':
rob@76 340 timeoutSeconds = std::atoi( value );
rob@76 341 break;
rob@76 342 case 'u':
rob@76 343 maxUsers = std::atoi( value );
rob@76 344 break;
rob@76 345 case 'g':
rob@76 346 maxGroups = std::atoi( value );
rob@76 347 break;
rob@76 348 case 'l':
rob@76 349 logFile = value;
rob@76 350 break;
rob@76 351 default:
rob@76 352 usage();
rob@76 353 return 0;
rob@76 354 }
rob@76 355 }else{
rob@76 356 usage();
rob@76 357 return 0;
rob@76 358 }
rob@76 359 argIndex += 2;
rob@76 360 }
rob@76 361
rob@76 362 RunOscGroupServerUntilSigInt(
rob@76 363 port, timeoutSeconds, maxUsers, maxGroups, logFile );
rob@76 364
rob@76 365 return 0;
rob@76 366 }
rob@76 367
rob@76 368
rob@76 369 #ifndef NO_MAIN
rob@76 370
rob@76 371 int main(int argc, char* argv[])
rob@76 372 {
rob@76 373 return oscgroupserver_main( argc, argv );
rob@76 374 }
rob@76 375
rob@76 376 #endif /* NO_MAIN */
rob@76 377