1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 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
30void 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
40ServerMediaSession* GenericMediaServer
41::lookupServerMediaSession(char const* streamName, Boolean /*isFirstLookupInSession*/) {
42 // Default implementation:
43 return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
44}
45
46void 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
57void GenericMediaServer::removeServerMediaSession(char const* streamName) {
58 removeServerMediaSession(GenericMediaServer::lookupServerMediaSession(streamName));
59}
60
61void 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
75void GenericMediaServer::closeAllClientSessionsForServerMediaSession(char const* streamName) {
76 closeAllClientSessionsForServerMediaSession(lookupServerMediaSession(streamName));
77}
78
79void GenericMediaServer::deleteServerMediaSession(ServerMediaSession* serverMediaSession) {
80 if (serverMediaSession == NULL) return;
81
82 closeAllClientSessionsForServerMediaSession(serverMediaSession);
83 removeServerMediaSession(serverMediaSession);
84}
85
86void GenericMediaServer::deleteServerMediaSession(char const* streamName) {
87 deleteServerMediaSession(lookupServerMediaSession(streamName));
88}
89
90GenericMediaServer
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
106GenericMediaServer::~GenericMediaServer() {
107 // Turn off background read handling:
108 envir().taskScheduler().turnOffBackgroundReadHandling(fServerSocket);
109 ::closeSocket(fServerSocket);
110}
111
112void 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
143int 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
178void GenericMediaServer::incomingConnectionHandler(void* instance, int /*mask*/) {
179 GenericMediaServer* server = (GenericMediaServer*)instance;
180 server->incomingConnectionHandler();
181}
182void GenericMediaServer::incomingConnectionHandler() {
183 incomingConnectionHandlerOnSocket(fServerSocket);
184}
185
186void 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
212GenericMediaServer::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
224GenericMediaServer::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
231void 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
239void GenericMediaServer::ClientConnection::incomingRequestHandler(void* instance, int /*mask*/) {
240 ClientConnection* connection = (ClientConnection*)instance;
241 connection->incomingRequestHandler();
242}
243
244void 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
251void GenericMediaServer::ClientConnection::resetRequestBuffer() {
252 fRequestBytesAlreadySeen = 0;
253 fRequestBufferBytesLeft = sizeof fRequestBuffer;
254}
255
256
257////////// GenericMediaServer::ClientSession implementation //////////
258
259GenericMediaServer::ClientSession
260::ClientSession(GenericMediaServer& ourServer, u_int32_t sessionId)
261 : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL),
262 fLivenessCheckTask(NULL) {
263 noteLiveness();
264}
265
266GenericMediaServer::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
285void 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
301void GenericMediaServer::ClientSession::noteClientLiveness(ClientSession* clientSession) {
302 clientSession->noteLiveness();
303}
304
305void 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
316GenericMediaServer::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
337GenericMediaServer::ClientSession*
338GenericMediaServer::lookupClientSession(u_int32_t sessionId) {
339 char sessionIdStr[8+1];
340 snprintf(sessionIdStr, sizeof sessionIdStr, "%08X", sessionId);
341 return lookupClientSession(sessionIdStr);
342}
343
344GenericMediaServer::ClientSession*
345GenericMediaServer::lookupClientSession(char const* sessionIdStr) {
346 return (GenericMediaServer::ClientSession*)fClientSessions->Lookup(sessionIdStr);
347}
348
349
350////////// ServerMediaSessionIterator implementation //////////
351
352GenericMediaServer::ServerMediaSessionIterator
353::ServerMediaSessionIterator(GenericMediaServer& server)
354 : fOurIterator((server.fServerMediaSessions == NULL)
355 ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) {
356}
357
358GenericMediaServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() {
359 delete fOurIterator;
360}
361
362ServerMediaSession* 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
372UserAuthenticationDatabase::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
379UserAuthenticationDatabase::~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
390void 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
396void UserAuthenticationDatabase::removeUserRecord(char const* username) {
397 char* password = (char*)(fTable->Lookup(username));
398 fTable->Remove(username);
399 delete[] password;
400}
401
402char const* UserAuthenticationDatabase::lookupPassword(char const* username) {
403 return (char const*)(fTable->Lookup(username));
404}
405