1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // A generic media server class, used to implement a RTSP server, and any other server that uses |
19 | // "ServerMediaSession" objects to describe media to be served. |
20 | // Implementation |
21 | |
22 | #include "GenericMediaServer.hh" |
23 | #include <GroupsockHelper.hh> |
24 | #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) |
25 | #define snprintf _snprintf |
26 | #endif |
27 | |
28 | ////////// GenericMediaServer implementation ////////// |
29 | |
30 | void GenericMediaServer::addServerMediaSession(ServerMediaSession* serverMediaSession) { |
31 | if (serverMediaSession == NULL) return; |
32 | |
33 | char const* sessionName = serverMediaSession->streamName(); |
34 | if (sessionName == NULL) sessionName = "" ; |
35 | removeServerMediaSession(sessionName); // in case an existing "ServerMediaSession" with this name already exists |
36 | |
37 | fServerMediaSessions->Add(sessionName, (void*)serverMediaSession); |
38 | } |
39 | |
40 | ServerMediaSession* GenericMediaServer |
41 | ::lookupServerMediaSession(char const* streamName, Boolean /*isFirstLookupInSession*/) { |
42 | // Default implementation: |
43 | return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)); |
44 | } |
45 | |
46 | void GenericMediaServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) { |
47 | if (serverMediaSession == NULL) return; |
48 | |
49 | fServerMediaSessions->Remove(serverMediaSession->streamName()); |
50 | if (serverMediaSession->referenceCount() == 0) { |
51 | Medium::close(serverMediaSession); |
52 | } else { |
53 | serverMediaSession->deleteWhenUnreferenced() = True; |
54 | } |
55 | } |
56 | |
57 | void GenericMediaServer::removeServerMediaSession(char const* streamName) { |
58 | removeServerMediaSession(GenericMediaServer::lookupServerMediaSession(streamName)); |
59 | } |
60 | |
61 | void GenericMediaServer::closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession) { |
62 | if (serverMediaSession == NULL) return; |
63 | |
64 | HashTable::Iterator* iter = HashTable::Iterator::create(*fClientSessions); |
65 | GenericMediaServer::ClientSession* clientSession; |
66 | char const* key; // dummy |
67 | while ((clientSession = (GenericMediaServer::ClientSession*)(iter->next(key))) != NULL) { |
68 | if (clientSession->fOurServerMediaSession == serverMediaSession) { |
69 | delete clientSession; |
70 | } |
71 | } |
72 | delete iter; |
73 | } |
74 | |
75 | void GenericMediaServer::closeAllClientSessionsForServerMediaSession(char const* streamName) { |
76 | closeAllClientSessionsForServerMediaSession(lookupServerMediaSession(streamName)); |
77 | } |
78 | |
79 | void GenericMediaServer::deleteServerMediaSession(ServerMediaSession* serverMediaSession) { |
80 | if (serverMediaSession == NULL) return; |
81 | |
82 | closeAllClientSessionsForServerMediaSession(serverMediaSession); |
83 | removeServerMediaSession(serverMediaSession); |
84 | } |
85 | |
86 | void GenericMediaServer::deleteServerMediaSession(char const* streamName) { |
87 | deleteServerMediaSession(lookupServerMediaSession(streamName)); |
88 | } |
89 | |
90 | GenericMediaServer |
91 | ::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort, |
92 | unsigned reclamationSeconds) |
93 | : Medium(env), |
94 | fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds), |
95 | fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)), |
96 | fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)), |
97 | fClientSessions(HashTable::create(STRING_HASH_KEYS)), |
98 | fPreviousClientSessionId(0) |
99 | { |
100 | ignoreSigPipeOnSocket(fServerSocket); // so that clients on the same host that are killed don't also kill us |
101 | |
102 | // Arrange to handle connections from others: |
103 | env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this); |
104 | } |
105 | |
106 | GenericMediaServer::~GenericMediaServer() { |
107 | // Turn off background read handling: |
108 | envir().taskScheduler().turnOffBackgroundReadHandling(fServerSocket); |
109 | ::closeSocket(fServerSocket); |
110 | } |
111 | |
112 | void GenericMediaServer::cleanup() { |
113 | // This member function must be called in the destructor of any subclass of |
114 | // "GenericMediaServer". (We don't call this in the destructor of "GenericMediaServer" itself, |
115 | // because by that time, the subclass destructor will already have been called, and this may |
116 | // affect (break) the destruction of the "ClientSession" and "ClientConnection" objects, which |
117 | // themselves will have been subclassed.) |
118 | |
119 | // Close all client session objects: |
120 | GenericMediaServer::ClientSession* clientSession; |
121 | while ((clientSession = (GenericMediaServer::ClientSession*)fClientSessions->getFirst()) != NULL) { |
122 | delete clientSession; |
123 | } |
124 | delete fClientSessions; |
125 | |
126 | // Close all client connection objects: |
127 | GenericMediaServer::ClientConnection* connection; |
128 | while ((connection = (GenericMediaServer::ClientConnection*)fClientConnections->getFirst()) != NULL) { |
129 | delete connection; |
130 | } |
131 | delete fClientConnections; |
132 | |
133 | // Delete all server media sessions |
134 | ServerMediaSession* serverMediaSession; |
135 | while ((serverMediaSession = (ServerMediaSession*)fServerMediaSessions->getFirst()) != NULL) { |
136 | removeServerMediaSession(serverMediaSession); // will delete it, because it no longer has any 'client session' objects using it |
137 | } |
138 | delete fServerMediaSessions; |
139 | } |
140 | |
141 | #define LISTEN_BACKLOG_SIZE 20 |
142 | |
143 | int GenericMediaServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) { |
144 | int ourSocket = -1; |
145 | |
146 | do { |
147 | // The following statement is enabled by default. |
148 | // Don't disable it (by defining ALLOW_SERVER_PORT_REUSE) unless you know what you're doing. |
149 | #if !defined(ALLOW_SERVER_PORT_REUSE) && !defined(ALLOW_RTSP_SERVER_PORT_REUSE) |
150 | // ALLOW_RTSP_SERVER_PORT_REUSE is for backwards-compatibility ##### |
151 | NoReuse dummy(env); // Don't use this socket if there's already a local server using it |
152 | #endif |
153 | |
154 | ourSocket = setupStreamSocket(env, ourPort, True, True); |
155 | if (ourSocket < 0) break; |
156 | |
157 | // Make sure we have a big send buffer: |
158 | if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break; |
159 | |
160 | // Allow multiple simultaneous connections: |
161 | if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) { |
162 | env.setResultErrMsg("listen() failed: " ); |
163 | break; |
164 | } |
165 | |
166 | if (ourPort.num() == 0) { |
167 | // bind() will have chosen a port for us; return it also: |
168 | if (!getSourcePort(env, ourSocket, ourPort)) break; |
169 | } |
170 | |
171 | return ourSocket; |
172 | } while (0); |
173 | |
174 | if (ourSocket != -1) ::closeSocket(ourSocket); |
175 | return -1; |
176 | } |
177 | |
178 | void GenericMediaServer::incomingConnectionHandler(void* instance, int /*mask*/) { |
179 | GenericMediaServer* server = (GenericMediaServer*)instance; |
180 | server->incomingConnectionHandler(); |
181 | } |
182 | void GenericMediaServer::incomingConnectionHandler() { |
183 | incomingConnectionHandlerOnSocket(fServerSocket); |
184 | } |
185 | |
186 | void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) { |
187 | struct sockaddr_in clientAddr; |
188 | SOCKLEN_T clientAddrLen = sizeof clientAddr; |
189 | int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); |
190 | if (clientSocket < 0) { |
191 | int err = envir().getErrno(); |
192 | if (err != EWOULDBLOCK) { |
193 | envir().setResultErrMsg("accept() failed: " ); |
194 | } |
195 | return; |
196 | } |
197 | ignoreSigPipeOnSocket(clientSocket); // so that clients on the same host that are killed don't also kill us |
198 | makeSocketNonBlocking(clientSocket); |
199 | increaseSendBufferTo(envir(), clientSocket, 50*1024); |
200 | |
201 | #ifdef DEBUG |
202 | envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n" ; |
203 | #endif |
204 | |
205 | // Create a new object for handling this connection: |
206 | (void)createNewClientConnection(clientSocket, clientAddr); |
207 | } |
208 | |
209 | |
210 | ////////// GenericMediaServer::ClientConnection implementation ////////// |
211 | |
212 | GenericMediaServer::ClientConnection |
213 | ::ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr) |
214 | : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) { |
215 | // Add ourself to our 'client connections' table: |
216 | fOurServer.fClientConnections->Add((char const*)this, this); |
217 | |
218 | // Arrange to handle incoming requests: |
219 | resetRequestBuffer(); |
220 | envir().taskScheduler() |
221 | .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this); |
222 | } |
223 | |
224 | GenericMediaServer::ClientConnection::~ClientConnection() { |
225 | // Remove ourself from the server's 'client connections' hash table before we go: |
226 | fOurServer.fClientConnections->Remove((char const*)this); |
227 | |
228 | closeSockets(); |
229 | } |
230 | |
231 | void GenericMediaServer::ClientConnection::closeSockets() { |
232 | // Turn off background handling on our socket: |
233 | envir().taskScheduler().disableBackgroundHandling(fOurSocket); |
234 | if (fOurSocket>= 0) ::closeSocket(fOurSocket); |
235 | |
236 | fOurSocket = -1; |
237 | } |
238 | |
239 | void GenericMediaServer::ClientConnection::incomingRequestHandler(void* instance, int /*mask*/) { |
240 | ClientConnection* connection = (ClientConnection*)instance; |
241 | connection->incomingRequestHandler(); |
242 | } |
243 | |
244 | void GenericMediaServer::ClientConnection::incomingRequestHandler() { |
245 | struct sockaddr_in dummy; // 'from' address, meaningless in this case |
246 | |
247 | int bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy); |
248 | handleRequestBytes(bytesRead); |
249 | } |
250 | |
251 | void GenericMediaServer::ClientConnection::resetRequestBuffer() { |
252 | fRequestBytesAlreadySeen = 0; |
253 | fRequestBufferBytesLeft = sizeof fRequestBuffer; |
254 | } |
255 | |
256 | |
257 | ////////// GenericMediaServer::ClientSession implementation ////////// |
258 | |
259 | GenericMediaServer::ClientSession |
260 | ::ClientSession(GenericMediaServer& ourServer, u_int32_t sessionId) |
261 | : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL), |
262 | fLivenessCheckTask(NULL) { |
263 | noteLiveness(); |
264 | } |
265 | |
266 | GenericMediaServer::ClientSession::~ClientSession() { |
267 | // Turn off any liveness checking: |
268 | envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask); |
269 | |
270 | // Remove ourself from the server's 'client sessions' hash table before we go: |
271 | char sessionIdStr[8+1]; |
272 | sprintf(sessionIdStr, "%08X" , fOurSessionId); |
273 | fOurServer.fClientSessions->Remove(sessionIdStr); |
274 | |
275 | if (fOurServerMediaSession != NULL) { |
276 | fOurServerMediaSession->decrementReferenceCount(); |
277 | if (fOurServerMediaSession->referenceCount() == 0 |
278 | && fOurServerMediaSession->deleteWhenUnreferenced()) { |
279 | fOurServer.removeServerMediaSession(fOurServerMediaSession); |
280 | fOurServerMediaSession = NULL; |
281 | } |
282 | } |
283 | } |
284 | |
285 | void GenericMediaServer::ClientSession::noteLiveness() { |
286 | #ifdef DEBUG |
287 | char const* streamName |
288 | = (fOurServerMediaSession == NULL) ? "???" : fOurServerMediaSession->streamName(); |
289 | fprintf(stderr, "Client session (id \"%08X\", stream name \"%s\"): Liveness indication\n" , |
290 | fOurSessionId, streamName); |
291 | #endif |
292 | if (fOurServerMediaSession != NULL) fOurServerMediaSession->noteLiveness(); |
293 | |
294 | if (fOurServer.fReclamationSeconds > 0) { |
295 | envir().taskScheduler().rescheduleDelayedTask(fLivenessCheckTask, |
296 | fOurServer.fReclamationSeconds*1000000, |
297 | (TaskFunc*)livenessTimeoutTask, this); |
298 | } |
299 | } |
300 | |
301 | void GenericMediaServer::ClientSession::noteClientLiveness(ClientSession* clientSession) { |
302 | clientSession->noteLiveness(); |
303 | } |
304 | |
305 | void GenericMediaServer::ClientSession::livenessTimeoutTask(ClientSession* clientSession) { |
306 | // If this gets called, the client session is assumed to have timed out, so delete it: |
307 | #ifdef DEBUG |
308 | char const* streamName |
309 | = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName(); |
310 | fprintf(stderr, "Client session (id \"%08X\", stream name \"%s\") has timed out (due to inactivity)\n" , |
311 | clientSession->fOurSessionId, streamName); |
312 | #endif |
313 | delete clientSession; |
314 | } |
315 | |
316 | GenericMediaServer::ClientSession* GenericMediaServer::createNewClientSessionWithId() { |
317 | u_int32_t sessionId; |
318 | char sessionIdStr[8+1]; |
319 | |
320 | // Choose a random (unused) 32-bit integer for the session id |
321 | // (it will be encoded as a 8-digit hex number). (We avoid choosing session id 0, |
322 | // because that has a special use by some servers. Similarly, we avoid choosing the same |
323 | // session id twice in a row.) |
324 | do { |
325 | sessionId = (u_int32_t)our_random32(); |
326 | snprintf(sessionIdStr, sizeof sessionIdStr, "%08X" , sessionId); |
327 | } while (sessionId == 0 || sessionId == fPreviousClientSessionId |
328 | || lookupClientSession(sessionIdStr) != NULL); |
329 | fPreviousClientSessionId = sessionId; |
330 | |
331 | ClientSession* clientSession = createNewClientSession(sessionId); |
332 | if (clientSession != NULL) fClientSessions->Add(sessionIdStr, clientSession); |
333 | |
334 | return clientSession; |
335 | } |
336 | |
337 | GenericMediaServer::ClientSession* |
338 | GenericMediaServer::lookupClientSession(u_int32_t sessionId) { |
339 | char sessionIdStr[8+1]; |
340 | snprintf(sessionIdStr, sizeof sessionIdStr, "%08X" , sessionId); |
341 | return lookupClientSession(sessionIdStr); |
342 | } |
343 | |
344 | GenericMediaServer::ClientSession* |
345 | GenericMediaServer::lookupClientSession(char const* sessionIdStr) { |
346 | return (GenericMediaServer::ClientSession*)fClientSessions->Lookup(sessionIdStr); |
347 | } |
348 | |
349 | |
350 | ////////// ServerMediaSessionIterator implementation ////////// |
351 | |
352 | GenericMediaServer::ServerMediaSessionIterator |
353 | ::ServerMediaSessionIterator(GenericMediaServer& server) |
354 | : fOurIterator((server.fServerMediaSessions == NULL) |
355 | ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) { |
356 | } |
357 | |
358 | GenericMediaServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() { |
359 | delete fOurIterator; |
360 | } |
361 | |
362 | ServerMediaSession* GenericMediaServer::ServerMediaSessionIterator::next() { |
363 | if (fOurIterator == NULL) return NULL; |
364 | |
365 | char const* key; // dummy |
366 | return (ServerMediaSession*)(fOurIterator->next(key)); |
367 | } |
368 | |
369 | |
370 | ////////// UserAuthenticationDatabase implementation ////////// |
371 | |
372 | UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm, |
373 | Boolean passwordsAreMD5) |
374 | : fTable(HashTable::create(STRING_HASH_KEYS)), |
375 | fRealm(strDup(realm == NULL ? "LIVE555 Streaming Media" : realm)), |
376 | fPasswordsAreMD5(passwordsAreMD5) { |
377 | } |
378 | |
379 | UserAuthenticationDatabase::~UserAuthenticationDatabase() { |
380 | delete[] fRealm; |
381 | |
382 | // Delete the allocated 'password' strings that we stored in the table, and then the table itself: |
383 | char* password; |
384 | while ((password = (char*)fTable->RemoveNext()) != NULL) { |
385 | delete[] password; |
386 | } |
387 | delete fTable; |
388 | } |
389 | |
390 | void UserAuthenticationDatabase::addUserRecord(char const* username, |
391 | char const* password) { |
392 | char* oldPassword = (char*)fTable->Add(username, (void*)(strDup(password))); |
393 | delete[] oldPassword; // if any |
394 | } |
395 | |
396 | void UserAuthenticationDatabase::removeUserRecord(char const* username) { |
397 | char* password = (char*)(fTable->Lookup(username)); |
398 | fTable->Remove(username); |
399 | delete[] password; |
400 | } |
401 | |
402 | char const* UserAuthenticationDatabase::lookupPassword(char const* username) { |
403 | return (char const*)(fTable->Lookup(username)); |
404 | } |
405 | |