comparison oscgroups/OscGroupServer.cpp @ 76:0ae87af84e2f

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