1 /* DPLAYX.DLL name server implementation 2 * 3 * Copyright 2000-2001 - Peter Hunnisett 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library 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 GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 /* NOTE: Methods with the NS_ prefix are name server methods */ 21 22 #include <stdarg.h> 23 #include <string.h> 24 25 #define NONAMELESSUNION 26 27 #include "windef.h" 28 #include "winbase.h" 29 #include "winnls.h" 30 #include "wine/unicode.h" 31 #include "wine/debug.h" 32 #include "mmsystem.h" 33 34 #include "dplayx_global.h" 35 #include "name_server.h" 36 #include "wine/dplaysp.h" 37 #include "dplayx_messages.h" 38 #include "dplayx_queue.h" 39 40 /* FIXME: Need to create a crit section, store and use it */ 41 42 WINE_DEFAULT_DEBUG_CHANNEL(dplay); 43 44 /* NS specific structures */ 45 struct NSCacheData 46 { 47 DPQ_ENTRY(NSCacheData) next; 48 49 DWORD dwTime; /* Time at which data was last known valid */ 50 LPDPSESSIONDESC2 data; 51 52 LPVOID lpNSAddrHdr; 53 54 }; 55 typedef struct NSCacheData NSCacheData, *lpNSCacheData; 56 57 struct NSCache 58 { 59 lpNSCacheData present; /* keep track of what is to be looked at when walking */ 60 61 DPQ_HEAD(NSCacheData) first; 62 63 BOOL bNsIsLocal; 64 LPVOID lpLocalAddrHdr; /* FIXME: Not yet used */ 65 LPVOID lpRemoteAddrHdr; /* FIXME: Not yet used */ 66 }; 67 typedef struct NSCache NSCache, *lpNSCache; 68 69 /* Function prototypes */ 70 static DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData ); 71 72 /* Name Server functions 73 * --------------------- 74 */ 75 void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd, LPVOID lpNSInfo ) 76 { 77 lpNSCache lpCache = (lpNSCache)lpNSInfo; 78 79 lpCache->bNsIsLocal = TRUE; 80 } 81 82 static DPQ_DECL_COMPARECB( cbUglyPig, GUID ) 83 { 84 return IsEqualGUID( elem1, elem2 ); 85 } 86 87 /* Store the given NS remote address for future reference */ 88 void NS_AddRemoteComputerAsNameServer( LPCVOID lpcNSAddrHdr, 89 DWORD dwHdrSize, 90 LPCDPMSG_ENUMSESSIONSREPLY lpcMsg, 91 LPVOID lpNSInfo ) 92 { 93 DWORD len; 94 lpNSCache lpCache = (lpNSCache)lpNSInfo; 95 lpNSCacheData lpCacheNode; 96 97 TRACE( "%p, %p, %p\n", lpcNSAddrHdr, lpcMsg, lpNSInfo ); 98 99 /* See if we can find this session. If we can, remove it as it's a dup */ 100 DPQ_REMOVE_ENTRY_CB( lpCache->first, next, data->guidInstance, cbUglyPig, 101 lpcMsg->sd.guidInstance, lpCacheNode ); 102 103 if( lpCacheNode != NULL ) 104 { 105 TRACE( "Duplicate session entry for %s removed - updated version kept\n", 106 debugstr_guid( &lpCacheNode->data->guidInstance ) ); 107 cbDeleteNSNodeFromHeap( lpCacheNode ); 108 } 109 110 /* Add this to the list */ 111 lpCacheNode = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpCacheNode ) ); 112 113 if( lpCacheNode == NULL ) 114 { 115 ERR( "no memory for NS node\n" ); 116 return; 117 } 118 119 lpCacheNode->lpNSAddrHdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 120 dwHdrSize ); 121 CopyMemory( lpCacheNode->lpNSAddrHdr, lpcNSAddrHdr, dwHdrSize ); 122 123 lpCacheNode->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *(lpCacheNode->data) ) ); 124 125 if( lpCacheNode->data == NULL ) 126 { 127 ERR( "no memory for SESSIONDESC2\n" ); 128 HeapFree( GetProcessHeap(), 0, lpCacheNode ); 129 return; 130 } 131 132 *lpCacheNode->data = lpcMsg->sd; 133 len = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)(lpcMsg+1), -1, NULL, 0, NULL, NULL ); 134 if ((lpCacheNode->data->u1.lpszSessionNameA = HeapAlloc( GetProcessHeap(), 0, len ))) 135 { 136 WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)(lpcMsg+1), -1, 137 lpCacheNode->data->u1.lpszSessionNameA, len, NULL, NULL ); 138 } 139 140 lpCacheNode->dwTime = timeGetTime(); 141 142 DPQ_INSERT(lpCache->first, lpCacheNode, next ); 143 144 lpCache->present = lpCacheNode; 145 146 /* Use this message as an opportunity to weed out any old sessions so 147 * that we don't enum them again 148 */ 149 NS_PruneSessionCache( lpNSInfo ); 150 } 151 152 LPVOID NS_GetNSAddr( LPVOID lpNSInfo ) 153 { 154 lpNSCache lpCache = (lpNSCache)lpNSInfo; 155 156 FIXME( ":quick stub\n" ); 157 158 /* Ok. Cheat and don't search for the correct stuff just take the first. 159 * FIXME: In the future how are we to know what is _THE_ enum we used? 160 * This is going to have to go into dplay somehow. Perhaps it 161 * comes back with app server id for the join command! Oh... that 162 * must be it. That would make this method obsolete once that's 163 * in place. 164 */ 165 #if 1 166 if ( lpCache->first.lpQHFirst ) 167 return lpCache->first.lpQHFirst->lpNSAddrHdr; 168 169 return NULL; 170 #else 171 /* FIXME: Should convert over to this */ 172 return lpCache->bNsIsLocal ? lpCache->lpLocalAddrHdr 173 : lpCache->lpRemoteAddrHdr; 174 #endif 175 } 176 177 /* Get the magic number associated with the Name Server */ 178 DWORD NS_GetNsMagic( LPVOID lpNSInfo ) 179 { 180 LPDWORD lpHdrInfo = NS_GetNSAddr( lpNSInfo ); 181 182 return lpHdrInfo[1]; 183 } 184 185 void NS_SetLocalAddr( LPVOID lpNSInfo, LPCVOID lpHdr, DWORD dwHdrSize ) 186 { 187 lpNSCache lpCache = (lpNSCache)lpNSInfo; 188 189 lpCache->lpLocalAddrHdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwHdrSize ); 190 191 CopyMemory( lpCache->lpLocalAddrHdr, lpHdr, dwHdrSize ); 192 } 193 194 /* This function is responsible for sending a request for all other known 195 nameservers to send us what sessions they have registered locally 196 */ 197 HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid, 198 DWORD dwFlags, 199 const SPINITDATA *lpSpData ) 200 201 { 202 DPSP_ENUMSESSIONSDATA data; 203 LPDPMSG_ENUMSESSIONSREQUEST lpMsg; 204 205 TRACE( "enumerating for guid %s\n", debugstr_guid( lpcGuid ) ); 206 207 /* Get the SP to deal with sending the EnumSessions request */ 208 FIXME( ": not all data fields are correct\n" ); 209 210 data.dwMessageSize = lpSpData->dwSPHeaderSize + sizeof( *lpMsg ); /*FIXME!*/ 211 data.lpMessage = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 212 data.dwMessageSize ); 213 data.lpISP = lpSpData->lpISP; 214 data.bReturnStatus = (dwFlags & DPENUMSESSIONS_RETURNSTATUS) != 0; 215 216 217 lpMsg = (LPDPMSG_ENUMSESSIONSREQUEST)(((BYTE*)data.lpMessage)+lpSpData->dwSPHeaderSize); 218 219 /* Setup EnumSession request message */ 220 lpMsg->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG; 221 lpMsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREQUEST; 222 lpMsg->envelope.wVersion = DPMSGVER_DP6; 223 224 lpMsg->dwPasswordSize = 0; /* FIXME: If enumerating passwords..? */ 225 lpMsg->dwFlags = dwFlags; 226 227 lpMsg->guidApplication = *lpcGuid; 228 229 return (lpSpData->lpCB->EnumSessions)( &data ); 230 } 231 232 /* Delete a name server node which has been allocated on the heap */ 233 static DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData ) 234 { 235 /* NOTE: This proc doesn't deal with the walking pointer */ 236 237 /* FIXME: Memory leak on data (contained ptrs) */ 238 HeapFree( GetProcessHeap(), 0, elem->data ); 239 HeapFree( GetProcessHeap(), 0, elem->lpNSAddrHdr ); 240 HeapFree( GetProcessHeap(), 0, elem ); 241 } 242 243 /* Render all data in a session cache invalid */ 244 void NS_InvalidateSessionCache( LPVOID lpNSInfo ) 245 { 246 lpNSCache lpCache = (lpNSCache)lpNSInfo; 247 248 if( lpCache == NULL ) 249 { 250 ERR( ": invalidate nonexistent cache\n" ); 251 return; 252 } 253 254 DPQ_DELETEQ( lpCache->first, next, lpNSCacheData, cbDeleteNSNodeFromHeap ); 255 256 /* NULL out the walking pointer */ 257 lpCache->present = NULL; 258 259 lpCache->bNsIsLocal = FALSE; 260 261 } 262 263 /* Create and initialize a session cache */ 264 BOOL NS_InitializeSessionCache( LPVOID* lplpNSInfo ) 265 { 266 lpNSCache lpCache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *lpCache ) ); 267 268 *lplpNSInfo = lpCache; 269 270 if( lpCache == NULL ) 271 { 272 return FALSE; 273 } 274 275 DPQ_INIT(lpCache->first); 276 lpCache->present = NULL; 277 278 lpCache->bNsIsLocal = FALSE; 279 280 return TRUE; 281 } 282 283 /* Delete a session cache */ 284 void NS_DeleteSessionCache( LPVOID lpNSInfo ) 285 { 286 NS_InvalidateSessionCache( (lpNSCache)lpNSInfo ); 287 } 288 289 /* Reinitialize the present pointer for this cache */ 290 void NS_ResetSessionEnumeration( LPVOID lpNSInfo ) 291 { 292 ((lpNSCache)lpNSInfo)->present = ((lpNSCache)lpNSInfo)->first.lpQHFirst; 293 } 294 295 LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo ) 296 { 297 LPDPSESSIONDESC2 lpSessionDesc; 298 lpNSCache lpCache = (lpNSCache)lpNSInfo; 299 300 /* FIXME: The pointers could disappear when walking if a prune happens */ 301 302 /* Test for end of the list */ 303 if( lpCache->present == NULL ) 304 { 305 return NULL; 306 } 307 308 lpSessionDesc = lpCache->present->data; 309 310 /* Advance tracking pointer */ 311 lpCache->present = lpCache->present->next.lpQNext; 312 313 return lpSessionDesc; 314 } 315 316 /* This method should check to see if there are any sessions which are 317 * older than the criteria. If so, just delete that information. 318 */ 319 /* FIXME: This needs to be called by some periodic timer */ 320 void NS_PruneSessionCache( LPVOID lpNSInfo ) 321 { 322 lpNSCache lpCache = lpNSInfo; 323 324 const DWORD dwPresentTime = timeGetTime(); 325 const DWORD dwPrunePeriod = DPMSG_WAIT_60_SECS; /* is 60 secs enough? */ 326 327 /* This silly little algorithm is based on the fact we keep entries in 328 * the queue in a time based order. It also assumes that it is not possible 329 * to wrap around over yourself (which is not unreasonable). 330 * The if statements verify if the first entry in the queue is less 331 * than dwPrunePeriod old depending on the "clock" roll over. 332 */ 333 for( ;; ) 334 { 335 lpNSCacheData lpFirstData; 336 337 if( DPQ_IS_EMPTY(lpCache->first) ) 338 { 339 /* Nothing to prune */ 340 break; 341 } 342 343 /* Deal with time in a wrap around safe manner - unsigned arithmetic. 344 * Check the difference in time */ 345 if( (dwPresentTime - (DPQ_FIRST(lpCache->first)->dwTime)) < dwPrunePeriod ) 346 { 347 /* First entry has not expired yet; don't prune */ 348 break; 349 } 350 351 lpFirstData = DPQ_FIRST(lpCache->first); 352 DPQ_REMOVE( lpCache->first, DPQ_FIRST(lpCache->first), next ); 353 cbDeleteNSNodeFromHeap( lpFirstData ); 354 } 355 356 } 357 358 /* NAME SERVER Message stuff */ 359 void NS_ReplyToEnumSessionsRequest( const void *lpcMsg, void **lplpReplyData, DWORD *lpdwReplySize, 360 IDirectPlayImpl *lpDP ) 361 { 362 LPDPMSG_ENUMSESSIONSREPLY rmsg; 363 DWORD dwVariableSize; 364 DWORD dwVariableLen; 365 /* LPCDPMSG_ENUMSESSIONSREQUEST msg = (LPDPMSG_ENUMSESSIONSREQUEST)lpcMsg; */ 366 367 /* FIXME: Should handle ANSI or WIDECHAR input. Currently just ANSI input */ 368 FIXME( ": few fixed + need to check request for response, might need UNICODE input ability.\n" ); 369 370 dwVariableLen = MultiByteToWideChar( CP_ACP, 0, 371 lpDP->dp2->lpSessionDesc->u1.lpszSessionNameA, 372 -1, NULL, 0 ); 373 dwVariableSize = dwVariableLen * sizeof( WCHAR ); 374 375 *lpdwReplySize = lpDP->dp2->spData.dwSPHeaderSize + 376 sizeof( *rmsg ) + dwVariableSize; 377 *lplpReplyData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 378 *lpdwReplySize ); 379 380 rmsg = (LPDPMSG_ENUMSESSIONSREPLY)( (BYTE*)*lplpReplyData + 381 lpDP->dp2->spData.dwSPHeaderSize); 382 383 rmsg->envelope.dwMagic = DPMSGMAGIC_DPLAYMSG; 384 rmsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREPLY; 385 rmsg->envelope.wVersion = DPMSGVER_DP6; 386 387 CopyMemory( &rmsg->sd, lpDP->dp2->lpSessionDesc, 388 lpDP->dp2->lpSessionDesc->dwSize ); 389 rmsg->dwUnknown = 0x0000005c; 390 MultiByteToWideChar( CP_ACP, 0, lpDP->dp2->lpSessionDesc->u1.lpszSessionNameA, -1, 391 (LPWSTR)(rmsg+1), dwVariableLen ); 392 } 393