annotate oscgroups/OscGroupClient.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
rob@76 21 #include "OscGroupClient.h"
rob@76 22
rob@76 23 #include <cstring>
rob@76 24 #include <iostream>
rob@76 25 #include <vector>
rob@76 26 #include <ctime>
rob@76 27 #include <string>
rob@76 28 #include <cassert>
rob@76 29 #include <cstdlib>
rob@76 30
rob@76 31 #include "osc/OscReceivedElements.h"
rob@76 32 #include "osc/OscOutboundPacketStream.h"
rob@76 33 #include "osc/OscPacketListener.h"
rob@76 34
rob@76 35 #include "ip/UdpSocket.h"
rob@76 36 #include "ip/IpEndpointName.h"
rob@76 37 #include "ip/PacketListener.h"
rob@76 38 #include "ip/TimerListener.h"
rob@76 39
rob@76 40 #include "md5.h"
rob@76 41
rob@76 42 #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug
rob@76 43 namespace std {
rob@76 44 using ::__strcmp__; // avoid error: E2316 '__strcmp__' is not a member of 'std'.
rob@76 45 }
rob@76 46 #endif
rob@76 47
rob@76 48 /*
rob@76 49 There are three sockets:
rob@76 50 externalSocket is used to send and receive data from the network
rob@76 51 including the server and other peers
rob@76 52
rob@76 53 localRxSocket is used to forward data to the local (client)
rob@76 54 application from other peers
rob@76 55
rob@76 56 localTxSocket is used to recieve data from the local (client)
rob@76 57 application to be forwarded to other peers
rob@76 58
rob@76 59
rob@76 60 Some behavioral rules:
rob@76 61
rob@76 62 the choice of which peer endpoint to forward traffic to is made based
rob@76 63 on which endpoints we have received pings from. if pings have been
rob@76 64 received from multiple endpoints preference is given to the private
rob@76 65 endpoint. as soon as a ping has been received we start forwarding data
rob@76 66
rob@76 67 during the establishment phase we ping all endpoints repeatedly
rob@76 68 even if we have already received pings from an endpoint.
rob@76 69
rob@76 70 during the establishment phase we initially send pings faster, and then
rob@76 71 gradually return to a slower rate.
rob@76 72
rob@76 73 the establishment phase ends when:
rob@76 74 - we receive a ping from the private endpoint
rob@76 75
rob@76 76 - we receive a ping from any endpoint, and
rob@76 77 ESTABLISHMENT_PING_PERIOD_COUNT ping periods have elapsed
rob@76 78
rob@76 79 if the server indicates that any of the port or address information of
rob@76 80 the peer has changed, we restart the establishment process.
rob@76 81
rob@76 82 if a ping is received from an endpoint which we haven't received from
rob@76 83 before we restart the establishment phase.
rob@76 84
rob@76 85 pings are sent less frequently if the channel is being kept open
rob@76 86 by forwarded traffic. We ensure that some traffic is sent accross
rob@76 87 the link every IDLE_PING_PERIOD_SECONDS and that a ping is sent
rob@76 88 accross the link at least every ACTIVE_PING_PERIOD_SECONDS
rob@76 89
rob@76 90
rob@76 91 if the last time at which the server has heard from a peer
rob@76 92 AND the last time from which we received a ping from the peer
rob@76 93 exceeds PURGE_PEER_TIMEOUT_SECONDS then the peer is removed from
rob@76 94 the peers list.
rob@76 95
rob@76 96
rob@76 97 if we havn't received a ping from a peer in
rob@76 98 PEER_ESTABLISHMENT_RETRY_TIME_SECONDS then we attempt to re-establish
rob@76 99 the connection.
rob@76 100
rob@76 101
rob@76 102
rob@76 103 TODO:
rob@76 104
rob@76 105 o- report ping times (requires higher resolution timer i think)
rob@76 106
rob@76 107
rob@76 108 -----------
rob@76 109
rob@76 110
rob@76 111
rob@76 112 */
rob@76 113
rob@76 114
rob@76 115
rob@76 116
rob@76 117 #define PURGE_PEER_TIMEOUT_SECONDS 180 // time before removing peer from active list
rob@76 118
rob@76 119 #define PEER_ESTABLISHMENT_RETRY_TIME_SECONDS 60
rob@76 120
rob@76 121 #define IDLE_PING_PERIOD_SECONDS 6 // seconds between pings when the link is idle
rob@76 122
rob@76 123 #define ACTIVE_PING_PERIOD_SECONDS 30 // seconds between pings when the link is active
rob@76 124
rob@76 125 #define ESTABLISHMENT_PING_PERIOD_COUNT 5 // 5 pings are always sent to all peer endpoints
rob@76 126
rob@76 127
rob@76 128
rob@76 129 struct PeerEndpoint{
rob@76 130 PeerEndpoint()
rob@76 131 : pingReceived( false )
rob@76 132 , sentPingsCount( 0 )
rob@76 133 , forwardedPacketsCount( 0 ) {}
rob@76 134
rob@76 135 IpEndpointName endpointName;
rob@76 136 bool pingReceived;
rob@76 137 std::time_t lastPingReceiveTime;
rob@76 138
rob@76 139 int sentPingsCount;
rob@76 140 std::time_t lastPingSendTime;
rob@76 141
rob@76 142 int forwardedPacketsCount;
rob@76 143 std::time_t lastPacketForwardTime;
rob@76 144 };
rob@76 145
rob@76 146
rob@76 147 struct Peer{
rob@76 148 Peer( const char *userName )
rob@76 149 : name( userName )
rob@76 150 , pingPeriodCount( 0 ) {}
rob@76 151
rob@76 152 std::string name;
rob@76 153
rob@76 154 std::time_t lastUserInfoReceiveTime;
rob@76 155 int secondsSinceLastAliveReceivedByServer;
rob@76 156
rob@76 157 /*
rob@76 158 we maintain three addresses for each peer. the private address is the
rob@76 159 address the peer thinks it is, the public is the address that the
rob@76 160 peer appears as to the server, and the ping address is the address
rob@76 161 that the client appears as to us when we receive a ping from it. this
rob@76 162 last address is necessary for half cone (symmetric) NATs which assign
rob@76 163 the peer a different port to talk to us than the one they assigned to
rob@76 164 talk to the server.
rob@76 165 */
rob@76 166
rob@76 167 PeerEndpoint privateEndpoint;
rob@76 168 PeerEndpoint publicEndpoint;
rob@76 169 PeerEndpoint pingEndpoint;
rob@76 170
rob@76 171 int pingPeriodCount;
rob@76 172 std::time_t lastPingPeriodTime;
rob@76 173
rob@76 174 std::time_t MostRecentActivityTime() const
rob@76 175 {
rob@76 176 // the most recent activity time is the most recent time either the
rob@76 177 // server heard from the peer, or we received a ping from the peer
rob@76 178
rob@76 179 std::time_t lastUserInfoReceivedByTheServerTime =
rob@76 180 lastUserInfoReceiveTime - secondsSinceLastAliveReceivedByServer; // FIXME: assumes time_t is in seconds
rob@76 181
rob@76 182 std::time_t result = lastUserInfoReceivedByTheServerTime;
rob@76 183 if( privateEndpoint.pingReceived )
rob@76 184 result = std::max( result, privateEndpoint.lastPingReceiveTime );
rob@76 185 if( publicEndpoint.pingReceived )
rob@76 186 result = std::max( result, publicEndpoint.lastPingReceiveTime );
rob@76 187 if( pingEndpoint.pingReceived )
rob@76 188 result = std::max( result, pingEndpoint.lastPingReceiveTime );
rob@76 189
rob@76 190 return result;
rob@76 191 }
rob@76 192
rob@76 193 };
rob@76 194
rob@76 195
rob@76 196 static std::vector<Peer> peers_;
rob@76 197
rob@76 198
rob@76 199 class ExternalCommunicationsSender : public TimerListener {
rob@76 200 #define IP_MTU_SIZE 1536
rob@76 201 char aliveBuffer_[IP_MTU_SIZE];
rob@76 202 std::size_t aliveSize_;
rob@76 203 std::time_t lastAliveSentTime_;
rob@76 204
rob@76 205 char pingBuffer_[IP_MTU_SIZE];
rob@76 206 std::size_t pingSize_;
rob@76 207
rob@76 208 UdpSocket& externalSocket_;
rob@76 209 IpEndpointName remoteServerEndpoint_;
rob@76 210 IpEndpointName localToServerEndpoint_;
rob@76 211
rob@76 212 std::string userName_;
rob@76 213 std::string userPassword_;
rob@76 214 std::string groupName_;
rob@76 215 std::string groupPassword_;
rob@76 216
rob@76 217 void PrepareAliveBuffer()
rob@76 218 {
rob@76 219 osc::OutboundPacketStream p( aliveBuffer_, IP_MTU_SIZE );
rob@76 220
rob@76 221 p << osc::BeginBundle();
rob@76 222
rob@76 223 p << osc::BeginMessage( "/groupserver/user_alive" )
rob@76 224 << userName_.c_str()
rob@76 225 << userPassword_.c_str()
rob@76 226 << ~((osc::int32)localToServerEndpoint_.address)
rob@76 227 << (osc::int32)localToServerEndpoint_.port
rob@76 228 << groupName_.c_str()
rob@76 229 << groupPassword_.c_str()
rob@76 230 << osc::EndMessage;
rob@76 231
rob@76 232 p << osc::BeginMessage( "/groupserver/get_group_users_info" )
rob@76 233 << groupName_.c_str()
rob@76 234 << groupPassword_.c_str()
rob@76 235 << osc::EndMessage;
rob@76 236
rob@76 237 p << osc::EndBundle;
rob@76 238
rob@76 239 aliveSize_ = p.Size();
rob@76 240 }
rob@76 241
rob@76 242 void SendAlive( std::time_t currentTime )
rob@76 243 {
rob@76 244 int secondsSinceLastAliveSent = (int)std::difftime(currentTime, lastAliveSentTime_);
rob@76 245 if( secondsSinceLastAliveSent >= IDLE_PING_PERIOD_SECONDS ){
rob@76 246
rob@76 247 externalSocket_.SendTo( remoteServerEndpoint_, aliveBuffer_, aliveSize_ );
rob@76 248
rob@76 249 lastAliveSentTime_ = currentTime;
rob@76 250 }
rob@76 251 }
rob@76 252
rob@76 253 void PreparePingBuffer()
rob@76 254 {
rob@76 255 osc::OutboundPacketStream p( pingBuffer_, IP_MTU_SIZE );
rob@76 256
rob@76 257 p << osc::BeginBundle();
rob@76 258
rob@76 259 p << osc::BeginMessage( "/groupclient/ping" )
rob@76 260 << userName_.c_str()
rob@76 261 << osc::EndMessage;
rob@76 262
rob@76 263 p << osc::EndBundle;
rob@76 264
rob@76 265 pingSize_ = p.Size();
rob@76 266 }
rob@76 267
rob@76 268 void SendPing( PeerEndpoint& to, std::time_t currentTime )
rob@76 269 {
rob@76 270 char addressString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
rob@76 271 to.endpointName.AddressAndPortAsString( addressString );
rob@76 272
rob@76 273 std::cout << "sending ping to " << addressString << "\n";
rob@76 274
rob@76 275 externalSocket_.SendTo( to.endpointName, pingBuffer_, pingSize_ );
rob@76 276 ++to.sentPingsCount;
rob@76 277 to.lastPingSendTime = currentTime;
rob@76 278 }
rob@76 279
rob@76 280
rob@76 281 PeerEndpoint* SelectEndpoint( Peer& peer )
rob@76 282 {
rob@76 283 if( peer.privateEndpoint.pingReceived ){
rob@76 284
rob@76 285 return &peer.privateEndpoint;
rob@76 286
rob@76 287 }else if( peer.publicEndpoint.pingReceived ){
rob@76 288
rob@76 289 return &peer.publicEndpoint;
rob@76 290
rob@76 291 }else if( peer.pingEndpoint.pingReceived ){
rob@76 292
rob@76 293 return &peer.pingEndpoint;
rob@76 294 }
rob@76 295
rob@76 296 return 0;
rob@76 297 }
rob@76 298
rob@76 299
rob@76 300 void PollPeerPingTimer( Peer& peer, std::time_t currentTime, bool executeNowIgnoringTimeouts=false )
rob@76 301 {
rob@76 302 bool noPingsReceivedYet =
rob@76 303 !peer.privateEndpoint.pingReceived
rob@76 304 && !peer.publicEndpoint.pingReceived
rob@76 305 && !peer.pingEndpoint.pingReceived;
rob@76 306
rob@76 307 if( !noPingsReceivedYet ){
rob@76 308 // check whether we should attempt to re-establish the link
rob@76 309 // due to no traffic arriving for PEER_ESTABLISHMENT_RETRY_TIME_SECONDS
rob@76 310
rob@76 311 std::time_t mostRecentPingTime = 0;
rob@76 312 if( peer.privateEndpoint.pingReceived )
rob@76 313 mostRecentPingTime = std::max( mostRecentPingTime, peer.privateEndpoint.lastPingReceiveTime );
rob@76 314 if( peer.publicEndpoint.pingReceived )
rob@76 315 mostRecentPingTime = std::max( mostRecentPingTime, peer.publicEndpoint.lastPingReceiveTime );
rob@76 316 if( peer.pingEndpoint.pingReceived )
rob@76 317 mostRecentPingTime = std::max( mostRecentPingTime, peer.pingEndpoint.lastPingReceiveTime );
rob@76 318
rob@76 319 if( (int)std::difftime(currentTime, mostRecentPingTime) > PEER_ESTABLISHMENT_RETRY_TIME_SECONDS ){
rob@76 320
rob@76 321 peer.pingPeriodCount = 0;
rob@76 322 executeNowIgnoringTimeouts = true;
rob@76 323 }
rob@76 324 }
rob@76 325
rob@76 326
rob@76 327 bool inEstablishmentPhase =
rob@76 328 ( ( peer.pingPeriodCount < ESTABLISHMENT_PING_PERIOD_COUNT )
rob@76 329 && ( !peer.privateEndpoint.pingReceived ) )
rob@76 330 || noPingsReceivedYet;
rob@76 331
rob@76 332 if( inEstablishmentPhase ){
rob@76 333 int pingPeriod;
rob@76 334 if( peer.pingPeriodCount < ESTABLISHMENT_PING_PERIOD_COUNT ){
rob@76 335
rob@76 336 pingPeriod = (int) (IDLE_PING_PERIOD_SECONDS *
rob@76 337 ((double)(peer.pingPeriodCount + 1) / (double) ESTABLISHMENT_PING_PERIOD_COUNT));
rob@76 338
rob@76 339 }else{
rob@76 340 pingPeriod = IDLE_PING_PERIOD_SECONDS;
rob@76 341 }
rob@76 342
rob@76 343 if( currentTime >= (peer.lastPingPeriodTime + pingPeriod)
rob@76 344 || executeNowIgnoringTimeouts ){
rob@76 345 SendPing( peer.privateEndpoint, currentTime );
rob@76 346 SendPing( peer.publicEndpoint, currentTime );
rob@76 347 if( peer.pingEndpoint.pingReceived )
rob@76 348 SendPing( peer.pingEndpoint, currentTime );
rob@76 349
rob@76 350 peer.lastPingPeriodTime = currentTime;
rob@76 351 ++peer.pingPeriodCount;
rob@76 352 }
rob@76 353
rob@76 354 }else{
rob@76 355
rob@76 356 PeerEndpoint *peerEndpointToUse = SelectEndpoint( peer );
rob@76 357 assert( peerEndpointToUse != 0 );
rob@76 358
rob@76 359 bool sendPing = false;
rob@76 360
rob@76 361 if( executeNowIgnoringTimeouts ){
rob@76 362
rob@76 363 sendPing = true;
rob@76 364
rob@76 365 }else{
rob@76 366
rob@76 367 if( peerEndpointToUse->sentPingsCount == 0 ){
rob@76 368
rob@76 369 sendPing = true;
rob@76 370
rob@76 371 }else{
rob@76 372
rob@76 373 int secondsSinceLastPing = (int)std::difftime(currentTime, peerEndpointToUse->lastPingSendTime);
rob@76 374
rob@76 375 if( peerEndpointToUse->forwardedPacketsCount == 0 ){
rob@76 376
rob@76 377 if( secondsSinceLastPing >= IDLE_PING_PERIOD_SECONDS ){
rob@76 378
rob@76 379 sendPing = true;
rob@76 380 }
rob@76 381
rob@76 382 }else{
rob@76 383
rob@76 384 int secondsSinceLastForwardedTraffic =
rob@76 385 (int)std::difftime(currentTime, peerEndpointToUse->lastPacketForwardTime);
rob@76 386
rob@76 387 if( secondsSinceLastForwardedTraffic >= IDLE_PING_PERIOD_SECONDS ){
rob@76 388
rob@76 389 if( secondsSinceLastPing >= IDLE_PING_PERIOD_SECONDS ){
rob@76 390 sendPing = true;
rob@76 391 }
rob@76 392
rob@76 393 }else if( secondsSinceLastPing >= ACTIVE_PING_PERIOD_SECONDS ){
rob@76 394 sendPing = true;
rob@76 395 }
rob@76 396 }
rob@76 397 }
rob@76 398 }
rob@76 399
rob@76 400 if( sendPing ){
rob@76 401 SendPing( *peerEndpointToUse, currentTime );
rob@76 402 peer.lastPingPeriodTime = currentTime;
rob@76 403 ++peer.pingPeriodCount;
rob@76 404 }
rob@76 405 }
rob@76 406 }
rob@76 407
rob@76 408 ExternalCommunicationsSender(); // no default ctor
rob@76 409 ExternalCommunicationsSender( const ExternalCommunicationsSender& ); // no copy ctor
rob@76 410 ExternalCommunicationsSender& operator=( const ExternalCommunicationsSender& ); // no assignment operator
rob@76 411
rob@76 412 public:
rob@76 413 ExternalCommunicationsSender( UdpSocket& externalSocket,
rob@76 414 IpEndpointName remoteServerEndpoint,
rob@76 415 int localToRemotePort,
rob@76 416 const char *userName, const char *userPassword,
rob@76 417 const char *groupName, const char *groupPassword )
rob@76 418 : lastAliveSentTime_( 0 )
rob@76 419 , externalSocket_( externalSocket )
rob@76 420 , remoteServerEndpoint_( remoteServerEndpoint )
rob@76 421 , localToServerEndpoint_(
rob@76 422 externalSocket.LocalEndpointFor( remoteServerEndpoint ).address,
rob@76 423 localToRemotePort )
rob@76 424 , userName_( userName )
rob@76 425 , userPassword_( userPassword )
rob@76 426 , groupName_( groupName )
rob@76 427 , groupPassword_( groupPassword )
rob@76 428 {
rob@76 429 PrepareAliveBuffer();
rob@76 430 PreparePingBuffer();
rob@76 431 }
rob@76 432
rob@76 433
rob@76 434 void RestartPeerCommunicationEstablishment( Peer& peer, std::time_t currentTime )
rob@76 435 {
rob@76 436 peer.pingPeriodCount = 0;
rob@76 437 PollPeerPingTimer( peer, currentTime, true );
rob@76 438 }
rob@76 439
rob@76 440
rob@76 441 void ForwardPacketToAllPeers( const char *data, int size )
rob@76 442 {
rob@76 443 std::time_t currentTime = std::time(0);
rob@76 444
rob@76 445 for( std::vector<Peer>::iterator i = peers_.begin(); i != peers_.end(); ++i ){
rob@76 446
rob@76 447 PeerEndpoint *peerEndpointToUse = SelectEndpoint( *i );
rob@76 448 if( peerEndpointToUse ){
rob@76 449 externalSocket_.SendTo( peerEndpointToUse->endpointName, data, size );
rob@76 450 ++peerEndpointToUse->forwardedPacketsCount;
rob@76 451 peerEndpointToUse->lastPacketForwardTime = currentTime;
rob@76 452 }
rob@76 453 }
rob@76 454 }
rob@76 455
rob@76 456
rob@76 457 virtual void TimerExpired()
rob@76 458 {
rob@76 459 std::time_t currentTime = std::time(0);
rob@76 460
rob@76 461 SendAlive( currentTime );
rob@76 462
rob@76 463 // check for peers to purge,
rob@76 464 std::vector<Peer>::iterator i = peers_.begin();
rob@76 465 while( i != peers_.end() ){
rob@76 466
rob@76 467 if( std::difftime(currentTime,i->MostRecentActivityTime()) > PURGE_PEER_TIMEOUT_SECONDS ){
rob@76 468
rob@76 469 i = peers_.erase( i );
rob@76 470
rob@76 471 }else{
rob@76 472 PollPeerPingTimer( *i, currentTime );
rob@76 473 ++i;
rob@76 474 }
rob@76 475 }
rob@76 476 }
rob@76 477 };
rob@76 478
rob@76 479
rob@76 480 class ExternalSocketListener : public osc::OscPacketListener {
rob@76 481
rob@76 482 void user_alive_status( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint )
rob@76 483 {
rob@76 484 // only accept user_alive_status from the server
rob@76 485 if( remoteEndpoint != remoteServerEndpoint_ )
rob@76 486 return;
rob@76 487
rob@76 488 // /groupclient/user_alive_status userName userPassword status
rob@76 489
rob@76 490 osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
rob@76 491
rob@76 492 const char *userName, *userPassword, *status;
rob@76 493
rob@76 494 args >> userName >> userPassword >> status;
rob@76 495
rob@76 496 if( std::strcmp( userName, userName_ ) == 0
rob@76 497 && std::strcmp( userPassword, userPassword_ ) == 0 ){
rob@76 498 // message really is for us
rob@76 499
rob@76 500 if( std::strcmp( status, "ok" ) == 0 ){
rob@76 501
rob@76 502 std::cout << "ok: user '" << userName << "' is registered with server\n";
rob@76 503
rob@76 504 }else{
rob@76 505 std::cout << "user registration error: server returned status of '" << status
rob@76 506 << "' for user '" << userName << "'\n";
rob@76 507 }
rob@76 508 }
rob@76 509 }
rob@76 510
rob@76 511 void user_group_status( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint )
rob@76 512 {
rob@76 513 // only accept user_alive_status from the server
rob@76 514 if( remoteEndpoint != remoteServerEndpoint_ )
rob@76 515 return;
rob@76 516
rob@76 517 // /groupclient/user_group_status userName userPassword groupName groupPassword status
rob@76 518
rob@76 519 osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
rob@76 520
rob@76 521 const char *userName, *userPassword, *groupName, *groupPassword, *status;
rob@76 522
rob@76 523 args >> userName >> userPassword >> groupName >> groupPassword >> status;
rob@76 524
rob@76 525 if( std::strcmp( userName, userName_ ) == 0
rob@76 526 && std::strcmp( userPassword, userPassword_ ) == 0
rob@76 527 && std::strcmp( groupName, groupName_ ) == 0
rob@76 528 && std::strcmp( groupPassword, groupPassword_ ) == 0 ){
rob@76 529 // message really is for us
rob@76 530
rob@76 531 if( std::strcmp( status, "ok" ) == 0 ){
rob@76 532
rob@76 533 std::cout << "ok: user '" << userName << "' is a member of group '" << groupName << "'\n";
rob@76 534
rob@76 535 }else{
rob@76 536 std::cout << "group membership error: server returned status of '" << status
rob@76 537 << "' for user '" << userName
rob@76 538 << "' membership of group '" << groupName << "'\n";
rob@76 539 }
rob@76 540 }
rob@76 541 }
rob@76 542
rob@76 543 void user_info( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint )
rob@76 544 {
rob@76 545 // only accept user_info from the server
rob@76 546 if( remoteEndpoint != remoteServerEndpoint_ )
rob@76 547 return;
rob@76 548
rob@76 549 // /groupclient/user_info userName privateIpAddress privatePort
rob@76 550 // publicIpAddress publicPort secondsSinceLastAlive group0 group1 ...
rob@76 551
rob@76 552 osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
rob@76 553
rob@76 554 const char *userName;
rob@76 555 osc::int32 privateAddress;
rob@76 556 osc::int32 privatePort;
rob@76 557 osc::int32 publicAddress;
rob@76 558 osc::int32 publicPort;
rob@76 559 osc::int32 secondsSinceLastAlive;
rob@76 560
rob@76 561 args >> userName >> privateAddress >> privatePort >>
rob@76 562 publicAddress >> publicPort >> secondsSinceLastAlive;
rob@76 563
rob@76 564 // addresses are transmitted as ones complement (bit inverse)
rob@76 565 // to avoid problems with buggy NATs trying to re-write addresses
rob@76 566 privateAddress = ~privateAddress;
rob@76 567 publicAddress = ~publicAddress;
rob@76 568
rob@76 569 IpEndpointName privateEndpoint( privateAddress, privatePort );
rob@76 570 IpEndpointName publicEndpoint( publicAddress, publicPort );
rob@76 571
rob@76 572 char privateAddressString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
rob@76 573 privateEndpoint.AddressAndPortAsString( privateAddressString );
rob@76 574 char publicAddressString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
rob@76 575 publicEndpoint.AddressAndPortAsString( publicAddressString );
rob@76 576
rob@76 577 std::cout << "user info received for '" << userName << "', "
rob@76 578 << "private: " << privateAddressString
rob@76 579 << " public: " << publicAddressString
rob@76 580 << "\n";
rob@76 581
rob@76 582 if( std::strcmp( userName, userName_ ) == 0 )
rob@76 583 return; // discard info referring to ourselves
rob@76 584
rob@76 585
rob@76 586 bool userIsInGroup = false;
rob@76 587 while( !args.Eos() ){
rob@76 588 const char *groupName;
rob@76 589 args >> groupName;
rob@76 590 if( std::strcmp( groupName, groupName_ ) == 0 ){
rob@76 591 userIsInGroup = true;
rob@76 592 break;
rob@76 593 }
rob@76 594 }
rob@76 595
rob@76 596
rob@76 597 if( userIsInGroup ){
rob@76 598 bool restartPeerCommunicationEstablishment = false;
rob@76 599
rob@76 600 bool found = false;
rob@76 601 std::vector<Peer>::iterator peer;
rob@76 602 for( std::vector<Peer>::iterator i = peers_.begin(); i != peers_.end(); ++i ){
rob@76 603
rob@76 604 if( i->name.compare( userName ) == 0 ){
rob@76 605 peer = i;
rob@76 606 found = true;
rob@76 607 break;
rob@76 608 }
rob@76 609 }
rob@76 610
rob@76 611 if( !found ){
rob@76 612 peers_.push_back( Peer( userName ) );
rob@76 613 peer = peers_.end() - 1;
rob@76 614 restartPeerCommunicationEstablishment = true;
rob@76 615 }
rob@76 616
rob@76 617 if( peer->privateEndpoint.endpointName != privateEndpoint ){
rob@76 618 peer->privateEndpoint.endpointName = privateEndpoint;
rob@76 619 peer->privateEndpoint.pingReceived = false;
rob@76 620 peer->privateEndpoint.forwardedPacketsCount = 0;
rob@76 621 peer->pingEndpoint.pingReceived = false;
rob@76 622 peer->pingEndpoint.forwardedPacketsCount = 0;
rob@76 623 restartPeerCommunicationEstablishment = true;
rob@76 624 }
rob@76 625
rob@76 626 if( peer->publicEndpoint.endpointName != publicEndpoint ){
rob@76 627 peer->publicEndpoint.endpointName = publicEndpoint;
rob@76 628 peer->publicEndpoint.pingReceived = false;
rob@76 629 peer->publicEndpoint.forwardedPacketsCount = 0;
rob@76 630 peer->pingEndpoint.pingReceived = false;
rob@76 631 peer->pingEndpoint.forwardedPacketsCount = 0;
rob@76 632 restartPeerCommunicationEstablishment = true;
rob@76 633 }
rob@76 634
rob@76 635
rob@76 636 peer->secondsSinceLastAliveReceivedByServer = secondsSinceLastAlive;
rob@76 637
rob@76 638 std::time_t currentTime = std::time(0);
rob@76 639 peer->lastUserInfoReceiveTime = currentTime;
rob@76 640
rob@76 641 if( restartPeerCommunicationEstablishment )
rob@76 642 externalCommunicationsSender_.RestartPeerCommunicationEstablishment( *peer, currentTime );
rob@76 643
rob@76 644 }else{
rob@76 645 // fixme should remove user from peer list if it is present
rob@76 646 }
rob@76 647 }
rob@76 648
rob@76 649 void ping( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint )
rob@76 650 {
rob@76 651 osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
rob@76 652
rob@76 653 const char *userName;
rob@76 654 // osc::TimeTag timeSent;
rob@76 655
rob@76 656 // TODO:
rob@76 657 // support 3 variants of the ping message:
rob@76 658 // /ping userName (basic version, only one needed for compatibility)
rob@76 659 // /ping userName timeSent
rob@76 660 // response -> /ping userName timeSent inResponseToUserName inResponseToTimeSent
rob@76 661
rob@76 662 args >> userName >> osc::EndMessage;
rob@76 663
rob@76 664 char sourceAddressString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
rob@76 665 remoteEndpoint.AddressAndPortAsString( sourceAddressString );
rob@76 666
rob@76 667 std::cout << "ping recieved from '" << userName << "' at "
rob@76 668 << sourceAddressString << "\n";
rob@76 669
rob@76 670 for( std::vector<Peer>::iterator i = peers_.begin(); i != peers_.end(); ++i ){
rob@76 671
rob@76 672 if( i->name.compare( userName ) == 0 ){
rob@76 673 bool restartPeerCommunicationEstablishment = false;
rob@76 674
rob@76 675 std::time_t currentTime = std::time(0);
rob@76 676
rob@76 677 if( remoteEndpoint == i->privateEndpoint.endpointName ){
rob@76 678
rob@76 679 restartPeerCommunicationEstablishment = !i->privateEndpoint.pingReceived;
rob@76 680 i->privateEndpoint.pingReceived = true;
rob@76 681 i->privateEndpoint.lastPingReceiveTime = currentTime;
rob@76 682
rob@76 683 }else if( remoteEndpoint == i->publicEndpoint.endpointName ){
rob@76 684
rob@76 685 restartPeerCommunicationEstablishment = !i->publicEndpoint.pingReceived;
rob@76 686 i->publicEndpoint.pingReceived = true;
rob@76 687 i->publicEndpoint.lastPingReceiveTime = currentTime;
rob@76 688
rob@76 689 }else{
rob@76 690 // otherwise assume the messages is coming from the ping endpoint
rob@76 691
rob@76 692 restartPeerCommunicationEstablishment = ( !i->pingEndpoint.pingReceived
rob@76 693 || i->pingEndpoint.endpointName != remoteEndpoint );
rob@76 694
rob@76 695 i->pingEndpoint.endpointName = remoteEndpoint;
rob@76 696 i->pingEndpoint.pingReceived = true;
rob@76 697 i->pingEndpoint.lastPingReceiveTime = currentTime;
rob@76 698 }
rob@76 699
rob@76 700 if( restartPeerCommunicationEstablishment )
rob@76 701 externalCommunicationsSender_.RestartPeerCommunicationEstablishment( *i, currentTime );
rob@76 702
rob@76 703 break;
rob@76 704 }
rob@76 705 }
rob@76 706 }
rob@76 707
rob@76 708 protected:
rob@76 709
rob@76 710 virtual void ProcessMessage( const osc::ReceivedMessage& m,
rob@76 711 const IpEndpointName& remoteEndpoint )
rob@76 712 {
rob@76 713 try{
rob@76 714
rob@76 715 if( std::strcmp( m.AddressPattern(), "/groupclient/user_info" ) == 0 ){
rob@76 716 user_info( m, remoteEndpoint );
rob@76 717 }else if( std::strcmp( m.AddressPattern(), "/groupclient/ping" ) == 0 ){
rob@76 718 ping( m, remoteEndpoint );
rob@76 719 }else if( std::strcmp( m.AddressPattern(), "/groupclient/user_alive_status" ) == 0 ){
rob@76 720 user_alive_status( m, remoteEndpoint );
rob@76 721 }else if( std::strcmp( m.AddressPattern(), "/groupclient/user_group_status" ) == 0 ){
rob@76 722 user_group_status( m, remoteEndpoint );
rob@76 723 }
rob@76 724
rob@76 725 }catch( osc::Exception& e ){
rob@76 726 std::cout << "error while parsing message: " << e.what() << "\n";
rob@76 727 }
rob@76 728 }
rob@76 729
rob@76 730 IpEndpointName remoteServerEndpoint_;
rob@76 731
rob@76 732 const char *userName_;
rob@76 733 const char *userPassword_;
rob@76 734 const char *groupName_;
rob@76 735 const char *groupPassword_;
rob@76 736
rob@76 737 UdpTransmitSocket localRxSocket_;
rob@76 738
rob@76 739 ExternalCommunicationsSender& externalCommunicationsSender_;
rob@76 740
rob@76 741 ExternalSocketListener(); // no default ctor
rob@76 742 ExternalSocketListener( const ExternalSocketListener& ); // no copy ctor
rob@76 743 ExternalSocketListener& operator=( const ExternalSocketListener& ); // no assignment operator
rob@76 744
rob@76 745 public:
rob@76 746 ExternalSocketListener( const IpEndpointName& remoteServerEndpoint,
rob@76 747 int localRxPort, const char *userName, const char *userPassword,
rob@76 748 const char *groupName, const char *groupPassword,
rob@76 749 ExternalCommunicationsSender& externalCommunicationsSender )
rob@76 750 : remoteServerEndpoint_( remoteServerEndpoint )
rob@76 751 , userName_( userName )
rob@76 752 , userPassword_( userPassword )
rob@76 753 , groupName_( groupName )
rob@76 754 , groupPassword_( groupPassword )
rob@76 755 , localRxSocket_( IpEndpointName( "localhost", localRxPort ) )
rob@76 756 , externalCommunicationsSender_( externalCommunicationsSender )
rob@76 757 {
rob@76 758 }
rob@76 759
rob@76 760 virtual void ProcessPacket( const char *data, int size,
rob@76 761 const IpEndpointName& remoteEndpoint )
rob@76 762 {
rob@76 763 // for now we parse _all_ packets, and pass all those on to clients
rob@76 764 // except those which come from the server. ideally we should avoid
rob@76 765 // parsing most packets except the ones containing pings, or perhaps
rob@76 766 // only process non-bundled pings.
rob@76 767
rob@76 768 // in the future it could be useful to register which peer a packet
rob@76 769 // is coming from so that we can keep track of channel activity
rob@76 770 // not just by receiving pings but also by recieving other traffic
rob@76 771 // this would also allow us to reject packets from unknown sources
rob@76 772
rob@76 773
rob@76 774 osc::OscPacketListener::ProcessPacket( data, size, remoteEndpoint );
rob@76 775
rob@76 776 if( remoteEndpoint != remoteServerEndpoint_ ){
rob@76 777
rob@76 778 // forward packet to local receive socket
rob@76 779
rob@76 780 localRxSocket_.Send( data, size );
rob@76 781 }
rob@76 782 }
rob@76 783 };
rob@76 784
rob@76 785
rob@76 786 class LocalTxSocketListener : public PacketListener {
rob@76 787
rob@76 788 ExternalCommunicationsSender& externalCommunicationsSender_;
rob@76 789
rob@76 790 LocalTxSocketListener(); // no default ctor
rob@76 791 LocalTxSocketListener( const LocalTxSocketListener& ); // no copy ctor
rob@76 792 LocalTxSocketListener& operator=( const LocalTxSocketListener& ); // no assignment operator
rob@76 793
rob@76 794 public:
rob@76 795 LocalTxSocketListener( ExternalCommunicationsSender& externalCommunicationsSender )
rob@76 796 : externalCommunicationsSender_( externalCommunicationsSender )
rob@76 797 {
rob@76 798 }
rob@76 799
rob@76 800 virtual void ProcessPacket( const char *data, int size,
rob@76 801 const IpEndpointName& remoteEndpoint )
rob@76 802 {
rob@76 803 (void) remoteEndpoint; // suppress unused parameter warning
rob@76 804 externalCommunicationsSender_.ForwardPacketToAllPeers( data, size );
rob@76 805 }
rob@76 806 };
rob@76 807
rob@76 808
rob@76 809 char IntToHexDigit( int n )
rob@76 810 {
rob@76 811 if( n < 10 )
rob@76 812 return (char)('0' + n);
rob@76 813 else
rob@76 814 return (char)('a' + (n-10));
rob@76 815 }
rob@76 816
rob@76 817 void MakeHashString( char *dest, const char *src )
rob@76 818 {
rob@76 819 MD5_CTX md5Context;
rob@76 820 MD5Init( &md5Context );
rob@76 821 MD5Update( &md5Context, (unsigned char*)src, (unsigned int)std::strlen(src) );
rob@76 822 unsigned char numericHash[16];
rob@76 823 MD5Final( numericHash, &md5Context );
rob@76 824 char *p = dest;
rob@76 825 for( int i=0; i < 16; ++i ){
rob@76 826
rob@76 827 *p++ = IntToHexDigit(((unsigned char)numericHash[i] >> 4) & 0x0F);
rob@76 828 *p++ = IntToHexDigit((unsigned char)numericHash[i] & 0x0F);
rob@76 829 }
rob@76 830 *p = '\0';
rob@76 831
rob@76 832 //printf( "src: %s dest: %s\n", src, dest );
rob@76 833 }
rob@76 834
rob@76 835 void SanityCheckMd5()
rob@76 836 {
rob@76 837 // if anything in this function fails there's a problem with your build configuration
rob@76 838
rob@76 839 // check that the size of types declared in md5.h are correct
rob@76 840 assert( sizeof(UINT2) == 2 );
rob@76 841 assert( sizeof(UINT4) == 4 );
rob@76 842
rob@76 843 // sanity check that the hash is working by comparing to a known good hash:
rob@76 844 char testHash[33];
rob@76 845 MakeHashString( testHash, "0123456789" );
rob@76 846 assert( std::strcmp( testHash, "781e5e245d69b566979b86e28d23f2c7" ) == 0 );
rob@76 847 }
rob@76 848
rob@76 849 void RunOscGroupClientUntilSigInt(
rob@76 850 const IpEndpointName& serverRemoteEndpoint,
rob@76 851 int localToRemotePort, int localTxPort, int localRxPort,
rob@76 852 const char *userName, const char *userPassword,
rob@76 853 const char *groupName, const char *groupPassword )
rob@76 854 {
rob@76 855 // used hashed passwords instead of the user supplied ones
rob@76 856
rob@76 857 char userPasswordHash[33];
rob@76 858 MakeHashString( userPasswordHash, userPassword );
rob@76 859
rob@76 860 char groupPasswordHash[33];
rob@76 861 MakeHashString( groupPasswordHash, groupPassword );
rob@76 862
rob@76 863 UdpReceiveSocket externalSocket(
rob@76 864 IpEndpointName( IpEndpointName::ANY_ADDRESS, localToRemotePort ) );
rob@76 865
rob@76 866 UdpReceiveSocket localTxSocket( localTxPort );
rob@76 867
rob@76 868 ExternalCommunicationsSender externalCommunicationsSender( externalSocket,
rob@76 869 serverRemoteEndpoint, localToRemotePort,
rob@76 870 userName, userPasswordHash, groupName, groupPasswordHash );
rob@76 871
rob@76 872 ExternalSocketListener externalSocketListener(
rob@76 873 serverRemoteEndpoint, localRxPort,
rob@76 874 userName, userPasswordHash, groupName, groupPasswordHash,
rob@76 875 externalCommunicationsSender );
rob@76 876
rob@76 877 LocalTxSocketListener localTxSocketListener( externalCommunicationsSender );
rob@76 878
rob@76 879 SocketReceiveMultiplexer mux;
rob@76 880 mux.AttachPeriodicTimerListener( 0, (IDLE_PING_PERIOD_SECONDS * 1000) / 10, &externalCommunicationsSender );
rob@76 881 mux.AttachSocketListener( &externalSocket, &externalSocketListener );
rob@76 882 mux.AttachSocketListener( &localTxSocket, &localTxSocketListener );
rob@76 883
rob@76 884 std::cout << "running...\n";
rob@76 885 std::cout << "press ctrl-c to end\n";
rob@76 886
rob@76 887 mux.RunUntilSigInt();
rob@76 888
rob@76 889 std::cout << "finishing.\n";
rob@76 890 }
rob@76 891
rob@76 892
rob@76 893 int oscgroupclient_main(int argc, char* argv[])
rob@76 894 {
rob@76 895 SanityCheckMd5();
rob@76 896
rob@76 897 try{
rob@76 898 if( argc != 10 ){
rob@76 899 std::cout << "usage: oscgroupclient serveraddress serverport localtoremoteport localtxport localrxport username password groupname grouppassword\n";
rob@76 900 std::cout << "users should send data to localhost:localtxport and listen on localhost:localrxport\n";
rob@76 901 return 0;
rob@76 902 }
rob@76 903
rob@76 904 IpEndpointName serverRemoteEndpoint( argv[1], atoi( argv[2] ) );
rob@76 905 int localToRemotePort = std::atoi( argv[3] );
rob@76 906 int localTxPort = std::atoi( argv[4] );
rob@76 907 int localRxPort = std::atoi( argv[5] );
rob@76 908 const char *userName = argv[6];
rob@76 909 const char *userPassword = argv[7];
rob@76 910 const char *groupName = argv[8];
rob@76 911 const char *groupPassword = argv[9];
rob@76 912
rob@76 913 char serverAddressString[ IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH ];
rob@76 914 serverRemoteEndpoint.AddressAndPortAsString( serverAddressString );
rob@76 915
rob@76 916 std::cout << "oscgroupclient\n";
rob@76 917 std::cout << "connecting to group '" << groupName << "' as user '" << userName << "'.\n";
rob@76 918 std::cout << "using server at " << serverAddressString
rob@76 919 << " with external traffic on local port " << localToRemotePort << "\n";
rob@76 920 std::cout << "--> send outbound traffic to localhost port " << localTxPort << "\n";
rob@76 921 std::cout << "<-- listen for inbound traffic on localhost port " << localRxPort << "\n";
rob@76 922
rob@76 923 RunOscGroupClientUntilSigInt( serverRemoteEndpoint, localToRemotePort,
rob@76 924 localTxPort, localRxPort, userName, userPassword, groupName, groupPassword );
rob@76 925
rob@76 926 }catch( std::exception& e ){
rob@76 927 std::cout << e.what() << std::endl;
rob@76 928 }
rob@76 929
rob@76 930 return 0;
rob@76 931 }
rob@76 932
rob@76 933
rob@76 934 #ifndef NO_MAIN
rob@76 935
rob@76 936 int main(int argc, char* argv[])
rob@76 937 {
rob@76 938 return oscgroupclient_main( argc, argv );
rob@76 939 }
rob@76 940
rob@76 941 #endif /* NO_MAIN */
rob@76 942