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