Mercurial > hg > nodescore
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 |