00001
00002
00003
00004
00005
00006
00007
00008
00010
00011 #include "upnp.h"
00012
00013 #include "upnptasksearch.h"
00014 #include "upnptaskcache.h"
00015
00016 #include "multicast.h"
00017 #include "broadcast.h"
00018
00019 #include <qregexp.h>
00020
00021 #include <unistd.h>
00022 #include <stdlib.h>
00023 #include <qstringlist.h>
00024 #include <sys/time.h>
00025
00028
00029
00030
00033
00035
00037
00038 SSDP::SSDP( int nServicePort ) : m_bTermRequested( false )
00039 {
00040
00041 m_nServicePort = nServicePort;
00042 m_nPort = UPnp::g_pConfig->GetValue( "UPnP/SSDP/Port" , SSDP_PORT );
00043 m_nSearchPort = UPnp::g_pConfig->GetValue( "UPnP/SSDP/SearchPort", SSDP_SEARCHPORT );
00044
00045 m_Sockets[ SocketIdx_Search ] = new QSocketDevice( QSocketDevice::Datagram );
00046 m_Sockets[ SocketIdx_Multicast ] = new QMulticastSocket( SSDP_GROUP, m_nPort );
00047 m_Sockets[ SocketIdx_Broadcast ] = new QBroadcastSocket( "255.255.255.255", m_nPort );
00048
00049
00050 m_Sockets[ SocketIdx_Search ]->setBlocking( FALSE );
00051 m_Sockets[ SocketIdx_Multicast ]->setBlocking( FALSE );
00052 m_Sockets[ SocketIdx_Broadcast ]->setBlocking( FALSE );
00053
00054
00055 QHostAddress ip4addr( (Q_UINT32) INADDR_ANY );
00056 m_Sockets[ SocketIdx_Search ]->bind( ip4addr, m_nSearchPort );
00057
00058 m_pNotifyTask = NULL;
00059
00060 }
00061
00063
00065
00066 SSDP::~SSDP()
00067 {
00068 DisableNotifications();
00069
00070 m_bTermRequested = true;
00071 wait();
00072
00073 if (m_pNotifyTask != NULL)
00074 m_pNotifyTask->Release();
00075
00076 for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ )
00077 {
00078 if (m_Sockets[ nIdx ] != NULL )
00079 {
00080 delete m_Sockets[ nIdx ];
00081 }
00082 }
00083 }
00084
00086
00088
00089 void SSDP::EnableNotifications()
00090 {
00091 if ( m_pNotifyTask == NULL )
00092 {
00093 VERBOSE(VB_UPNP, "SSDP::EnableNotifications() - creating new task");
00094 m_pNotifyTask = new UPnpNotifyTask( m_nServicePort );
00095
00096
00097
00098
00099
00100 m_pNotifyTask->AddRef();
00101
00102
00103
00104
00105
00106 VERBOSE(VB_UPNP, "SSDP::EnableNotifications() - sending NTS_byebye");
00107 m_pNotifyTask->SetNTS( NTS_byebye );
00108 m_pNotifyTask->Execute( NULL );
00109 }
00110
00111
00112
00113
00114
00115 VERBOSE(VB_UPNP, "SSDP::EnableNotifications() - sending NTS_alive");
00116 m_pNotifyTask->SetNTS( NTS_alive );
00117 UPnp::g_pTaskQueue->AddTask( m_pNotifyTask );
00118 VERBOSE(VB_UPNP, "SSDP::EnableNotifications() - Task added to UPnP queue");
00119 }
00120
00122
00124
00125 void SSDP::DisableNotifications()
00126 {
00127 if (m_pNotifyTask != NULL)
00128 {
00129
00130
00131 m_pNotifyTask->SetNTS( NTS_byebye );
00132 m_pNotifyTask->Execute( NULL );
00133 }
00134 }
00135
00137
00139
00140 void SSDP::PerformSearch( const QString &sST )
00141 {
00142 QCString sRequest = QString( "M-SEARCH * HTTP/1.1\r\n"
00143 "HOST: 239.255.255.250:1900\r\n"
00144 "MAN: \"ssdp:discover\"\r\n"
00145 "MX: 2\r\n"
00146 "ST: %1\r\n"
00147 "\r\n" )
00148 .arg( sST ).utf8();
00149
00150 QSocketDevice *pSocket = m_Sockets[ SocketIdx_Search ];
00151
00152 QHostAddress address;
00153 address.setAddress( SSDP_GROUP );
00154
00155 int nSize = sRequest.size();
00156
00157 if ( pSocket->writeBlock( sRequest.data(), sRequest.size(), address, SSDP_PORT ) != nSize)
00158 cerr << "SSDP::PerformSearch - did not write entire buffer." << endl;
00159
00160 usleep( rand() % 250000 );
00161
00162 if ( pSocket->writeBlock( sRequest.data(), sRequest.size(), address, SSDP_PORT ) != nSize)
00163 cerr << "SSDP::PerformSearch - did not write entire buffer." << endl;
00164
00165 }
00166
00168
00170
00171 void SSDP::run()
00172 {
00173 fd_set read_set;
00174 struct timeval timeout;
00175
00176
00177
00178
00179
00180 while ( ! m_bTermRequested )
00181 {
00182 int nMaxSocket = 0;
00183
00184 FD_ZERO( &read_set );
00185
00186 for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ )
00187 {
00188 if (m_Sockets[ nIdx ] != NULL)
00189 {
00190 FD_SET( m_Sockets[ nIdx ]->socket(), &read_set );
00191 nMaxSocket = max( m_Sockets[ nIdx ]->socket(), nMaxSocket );
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201 }
00202 }
00203
00204 timeout.tv_sec = 1;
00205 timeout.tv_usec = 0;
00206
00207 if ( select( nMaxSocket + 1, &read_set, NULL, NULL, &timeout ) != -1)
00208 {
00209 for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ )
00210 {
00211 if (m_Sockets[ nIdx ] != NULL)
00212 {
00213 if (FD_ISSET( m_Sockets[ nIdx ]->socket(), &read_set ))
00214 {
00215
00216
00217 ProcessData( m_Sockets[ nIdx ] );
00218 }
00219 }
00220 }
00221 }
00222 }
00223 }
00224
00226
00228
00229 void SSDP::ProcessData( QSocketDevice *pSocket )
00230 {
00231 long nBytes = 0;
00232 long nRead = 0;
00233
00234 while ((nBytes = pSocket->bytesAvailable()) > 0)
00235 {
00236 QCString buffer( nBytes + 1 );
00237
00238 nRead = pSocket->readBlock( buffer.data(), nBytes );
00239
00240 QHostAddress peerAddress = pSocket->peerAddress();
00241 Q_UINT16 peerPort = pSocket->peerPort ();
00242
00243
00244
00245 QStringList lines = QStringList::split( "\r\n", buffer );
00246 QString sRequestLine = lines[0];
00247
00248 lines.pop_front();
00249
00250
00251
00252
00253
00254 VERBOSE(VB_UPNP, QString( "SSDP::ProcessData - requestLine: %1" ).arg( sRequestLine ));
00255
00256 SSDPRequestType eType = ProcessRequestLine( sRequestLine );
00257
00258
00259
00260
00261
00262 QStringMap headers;
00263
00264 for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it )
00265 {
00266 QString sLine = *it;
00267 QString sName = sLine.section( ':', 0, 0 ).stripWhiteSpace();
00268 QString sValue = sLine.section( ':', 1 );
00269
00270 sValue.truncate( sValue.length() );
00271
00272 if ((sName.length() != 0) && (sValue.length() !=0))
00273 headers.insert( sName.lower(), sValue.stripWhiteSpace() );
00274 }
00275
00276
00277
00278
00279
00280
00281
00282 switch( eType )
00283 {
00284 case SSDP_MSearch : ProcessSearchRequest ( headers, peerAddress, peerPort ); break;
00285 case SSDP_MSearchResp: ProcessSearchResponse( headers ); break;
00286 case SSDP_Notify : ProcessNotify ( headers ); break;
00287 case SSDP_Unknown:
00288 default:
00289 VERBOSE(VB_UPNP, "SSPD::ProcessData - Unknown request Type.");
00290 break;
00291 }
00292 }
00293
00294 }
00295
00297
00299
00300 SSDPRequestType SSDP::ProcessRequestLine( const QString &sLine )
00301 {
00302 QStringList tokens = QStringList::split(QRegExp("[ \r\n][ \r\n]*"), sLine );
00303
00304
00305
00306
00307
00308
00309
00310
00311 if ( sLine.startsWith( "HTTP/" ))
00312 return SSDP_MSearchResp;
00313 else
00314 {
00315 if (tokens.count() > 0)
00316 {
00317 if (tokens[0] == "M-SEARCH" ) return SSDP_MSearch;
00318 if (tokens[0] == "NOTIFY" ) return SSDP_Notify;
00319 }
00320 }
00321
00322 return SSDP_Unknown;
00323 }
00324
00326
00328
00329 QString SSDP::GetHeaderValue( const QStringMap &headers, const QString &sKey, const QString &sDefault )
00330 {
00331 QStringMap::const_iterator it = headers.find( sKey.lower() );
00332
00333 if ( it == headers.end())
00334 return( sDefault );
00335
00336 return( it.data() );
00337 }
00338
00340
00342
00343 bool SSDP::ProcessSearchRequest( const QStringMap &sHeaders,
00344 QHostAddress peerAddress,
00345 Q_UINT16 peerPort )
00346 {
00347 QString sMAN = GetHeaderValue( sHeaders, "MAN", "" );
00348 QString sST = GetHeaderValue( sHeaders, "ST" , "" );
00349 QString sMX = GetHeaderValue( sHeaders, "MX" , "" );
00350 int nMX = 0;
00351
00352 VERBOSE( VB_UPNP, QString( "SSDP::ProcessSearchrequest : [%1] MX=%2" )
00353 .arg( sST )
00354 .arg( nMX ));
00355
00356
00357
00358
00359
00360 if ( UPnp::g_pTaskQueue == NULL ) return false;
00361
00362
00363
00364 if ( sMAN != "\"ssdp:discover\"" ) return false;
00365 if ( sST.length() == 0 ) return false;
00366 if ( sMX.length() == 0 ) return false;
00367 if ((nMX = sMX.toInt()) == 0 ) return false;
00368 if ( nMX < 0 ) return false;
00369
00370
00371
00372
00373
00374 nMX = (nMX > 120) ? 120 : nMX;
00375
00376 int nNewMX = (int)(0 + ((unsigned short)rand() % nMX)) * 1000;
00377
00378
00379
00380
00381
00382 if ((sST == "ssdp:all") || (sST == "upnp:rootdevice"))
00383 {
00384 UPnpSearchTask *pTask = new UPnpSearchTask( m_nServicePort,
00385 peerAddress,
00386 peerPort,
00387 sST,
00388 UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN());
00389
00390
00391
00392
00393
00394
00395 UPnp::g_pTaskQueue->AddTask( nNewMX, pTask );
00396
00397 return true;
00398 }
00399
00400
00401
00402
00403
00404 QString sUDN = UPnp::g_UPnpDeviceDesc.FindDeviceUDN( &(UPnp::g_UPnpDeviceDesc.m_rootDevice), sST );
00405
00406 if (sUDN.length() > 0)
00407 {
00408 UPnpSearchTask *pTask = new UPnpSearchTask( m_nServicePort,
00409 peerAddress,
00410 peerPort,
00411 sST,
00412 sUDN );
00413
00414
00415
00416
00417 pTask->Execute( NULL );
00418
00419 UPnp::g_pTaskQueue->AddTask( nNewMX, pTask );
00420
00421 return true;
00422 }
00423
00424 return false;
00425 }
00426
00428
00430
00431 bool SSDP::ProcessSearchResponse( const QStringMap &headers )
00432 {
00433 QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" );
00434 QString sST = GetHeaderValue( headers, "ST" , "" );
00435 QString sUSN = GetHeaderValue( headers, "USN" , "" );
00436 QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" );
00437
00438 VERBOSE( VB_UPNP, QString( "SSDP::ProcessSearchResponse \n"
00439 "DescURL=%1\n"
00440 "ST =%2\n"
00441 "USN =%3\n"
00442 "Cache =%4")
00443 .arg( sDescURL )
00444 .arg( sST )
00445 .arg( sUSN )
00446 .arg( sCache ));
00447
00448 int nPos = sCache.find( "max-age", 0, false );
00449
00450 if (nPos < 0)
00451 return false;
00452
00453 if ((nPos = sCache.find( "=", nPos, false )) < 0)
00454 return false;
00455
00456 int nSecs = sCache.mid( nPos+1 ).toInt();
00457
00458 UPnp::g_SSDPCache.Add( sST, sUSN, sDescURL, nSecs );
00459
00460 return true;
00461 }
00462
00464
00466
00467 bool SSDP::ProcessNotify( const QStringMap &headers )
00468 {
00469 QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" );
00470 QString sNTS = GetHeaderValue( headers, "NTS" , "" );
00471 QString sNT = GetHeaderValue( headers, "NT" , "" );
00472 QString sUSN = GetHeaderValue( headers, "USN" , "" );
00473 QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" );
00474
00475 VERBOSE( VB_UPNP, QString( "SSDP::ProcessNotify\n"
00476 "DescURL=%1\n"
00477 "NTS =%2\n"
00478 "NT =%3\n"
00479 "USN =%4\n"
00480 "Cache =%5" )
00481 .arg( sDescURL )
00482 .arg( sNTS )
00483 .arg( sNT )
00484 .arg( sUSN )
00485 .arg( sCache ));
00486
00487
00488 if (sNTS.contains( "ssdp:alive"))
00489 {
00490 int nPos = sCache.find( "max-age", 0, false );
00491
00492 if (nPos < 0)
00493 return false;
00494
00495 if ((nPos = sCache.find( "=", nPos, false )) < 0)
00496 return false;
00497
00498 int nSecs = sCache.mid( nPos+1 ).toInt();
00499
00500 UPnp::g_SSDPCache.Add( sNT, sUSN, sDescURL, nSecs );
00501
00502 return true;
00503 }
00504
00505
00506 if ( sNTS.contains( "ssdp:byebye" ) )
00507 {
00508 UPnp::g_SSDPCache.Remove( sNT, sUSN );
00509
00510 return true;
00511 }
00512
00513 return false;
00514 }
00515
00518
00519
00520
00523
00525
00527
00528 SSDPExtension::SSDPExtension( int nServicePort ) : HttpServerExtension( "SSDP" )
00529 {
00530 m_sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath", m_sSharePath );
00531 m_nServicePort = nServicePort;
00532 }
00533
00535
00537
00538 SSDPExtension::~SSDPExtension( )
00539 {
00540 }
00541
00543
00545
00546 SSDPMethod SSDPExtension::GetMethod( const QString &sURI )
00547 {
00548 if (sURI == "getDeviceDesc" ) return( SSDPM_GetDeviceDesc );
00549 if (sURI == "getDeviceList" ) return( SSDPM_GetDeviceList );
00550
00551 return( SSDPM_Unknown );
00552 }
00553
00555
00557
00558 bool SSDPExtension::ProcessRequest( HttpWorkerThread *, HTTPRequest *pRequest )
00559 {
00560 if (pRequest)
00561 {
00562 if ( pRequest->m_sBaseUrl != "/")
00563 return( false );
00564
00565 switch( GetMethod( pRequest->m_sMethod ))
00566 {
00567 case SSDPM_GetDeviceDesc : GetDeviceDesc( pRequest ); return( true );
00568 case SSDPM_GetDeviceList : GetDeviceList( pRequest ); return( true );
00569
00570 default: break;
00571 }
00572 }
00573
00574 return( false );
00575 }
00576
00578
00580
00581 void SSDPExtension::GetDeviceDesc( HTTPRequest *pRequest )
00582 {
00583 pRequest->m_eResponseType = ResponseTypeXML;
00584
00585 QString sUserAgent = pRequest->GetHeaderValue( "User-Agent", "" );
00586
00587 VERBOSE( VB_UPNP, QString( "SSDPExtension::GetDeviceDesc - Host=%1 Port=%2 UserAgent=%3" )
00588 .arg( pRequest->GetHostAddress() )
00589 .arg( m_nServicePort )
00590 .arg( sUserAgent ));
00591
00592
00593 UPnp::g_UPnpDeviceDesc.GetValidXML( pRequest->GetHostAddress(),
00594 m_nServicePort,
00595 pRequest->m_response,
00596 sUserAgent );
00597 }
00598
00600
00602
00603 void SSDPExtension::GetFile( HTTPRequest *pRequest, QString sFileName )
00604 {
00605 pRequest->m_eResponseType = ResponseTypeHTML;
00606 pRequest->m_nResponseStatus = 404;
00607
00608 pRequest->m_sFileName = m_sUPnpDescPath + sFileName;
00609
00610 if (QFile::exists( pRequest->m_sFileName ))
00611 {
00612 VERBOSE( VB_UPNP, QString( "SSDPExtension::GetFile( %1 ) - Exists" )
00613 .arg( pRequest->m_sFileName ));
00614
00615 pRequest->m_eResponseType = ResponseTypeFile;
00616 pRequest->m_nResponseStatus = 200;
00617 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000";
00618 }
00619 else
00620 {
00621 VERBOSE( VB_UPNP, QString( "SSDPExtension::GetFile( %1 ) - Not Found" )
00622 .arg( pRequest->m_sFileName ));
00623 }
00624
00625 }
00626
00628
00630
00631 void SSDPExtension::GetDeviceList( HTTPRequest *pRequest )
00632 {
00633 SSDPCache &cache = UPnp::g_SSDPCache;
00634 int nCount = 0;
00635 NameValueList list;
00636
00637 VERBOSE( VB_UPNP, "SSDPExtension::GetDeviceList" );
00638
00639 cache.Lock();
00640
00641 QString sXML;
00642 QTextStream os( sXML, IO_WriteOnly );
00643
00644 for (SSDPCacheEntriesMap::Iterator it = cache.Begin();
00645 it != cache.End();
00646 ++it )
00647 {
00648 SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data();
00649
00650 if (pEntries != NULL)
00651 {
00652 os << "<Device uri='" << it.key() << "'>" << endl;
00653
00654 pEntries->Lock();
00655
00656 EntryMap *pMap = pEntries->GetEntryMap();
00657
00658 for (EntryMap::Iterator itEntry = pMap->begin();
00659 itEntry != pMap->end();
00660 ++itEntry )
00661 {
00662
00663 DeviceLocation *pEntry = (DeviceLocation *)itEntry.data();
00664
00665 if (pEntry != NULL)
00666 {
00667 nCount++;
00668
00669 pEntry->AddRef();
00670
00671 os << "<Service usn='" << pEntry->m_sUSN
00672 << "' expiresInSecs='" << pEntry->ExpiresInSecs()
00673 << "' url='" << pEntry->m_sLocation << "' />" << endl;
00674
00675 pEntry->Release();
00676 }
00677 }
00678
00679 os << "</Device>" << endl;
00680
00681 pEntries->Unlock();
00682 }
00683 }
00684
00685 list.append( new NameValue( "DeviceCount" , QString::number( cache.Count() )));
00686 list.append( new NameValue( "DevicesAllocated" , QString::number( SSDPCacheEntries::g_nAllocated )));
00687
00688 list.append( new NameValue( "CacheEntriesFound" , QString::number( nCount )));
00689 list.append( new NameValue( "CacheEntriesAllocated", QString::number( DeviceLocation::g_nAllocated )));
00690
00691 list.append( new NameValue( "DeviceList" , sXML));
00692
00693 cache.Unlock();
00694
00695 pRequest->FormatActionResponse( &list );
00696
00697 pRequest->m_eResponseType = ResponseTypeXML;
00698 pRequest->m_nResponseStatus = 200;
00699
00700 }