1 /* Copyright (c) 2003 Juan Lang 2 * 3 * This library is free software; you can redistribute it and/or 4 * modify it under the terms of the GNU Lesser General Public 5 * License as published by the Free Software Foundation; either 6 * version 2.1 of the License, or (at your option) any later version. 7 * 8 * This library is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public 14 * License along with this library; if not, write to the Free Software 15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 16 */ 17 18 #include "netapi32.h" 19 20 #include "nbcmdqueue.h" 21 22 WINE_DEFAULT_DEBUG_CHANNEL(netbios); 23 24 /* This file provides a NetBIOS emulator that implements the NetBIOS interface, 25 * including thread safety and asynchronous call support. The protocol 26 * implementation is separate, with blocking (synchronous) functions. 27 */ 28 29 #define ADAPTERS_INCR 8 30 #define DEFAULT_NUM_SESSIONS 16 31 32 typedef struct _NetBIOSTransportTableEntry 33 { 34 ULONG id; 35 NetBIOSTransport transport; 36 } NetBIOSTransportTableEntry; 37 38 typedef struct _NetBIOSSession 39 { 40 BOOL inUse; 41 UCHAR state; 42 UCHAR local_name[NCBNAMSZ]; 43 UCHAR remote_name[NCBNAMSZ]; 44 void *data; 45 } NetBIOSSession; 46 47 /* This struct needs a little explanation, unfortunately. enabled is only 48 * used by nbInternalEnum (see). If transport_id is not 0 and transport 49 * is not NULL, the adapter is considered valid. (transport is a pointer to 50 * an entry in a NetBIOSTransportTableEntry.) data has data for the callers of 51 * NetBIOSEnumAdapters to be able to see. The lana is repeated there, even 52 * though I don't use it internally--it's for transports to use re-enabling 53 * adapters using NetBIOSEnableAdapter. 54 */ 55 typedef struct _NetBIOSAdapter 56 { 57 BOOL enabled; 58 BOOL shuttingDown; 59 LONG resetting; 60 ULONG transport_id; 61 NetBIOSTransport *transport; 62 NetBIOSAdapterImpl impl; 63 struct NBCmdQueue *cmdQueue; 64 CRITICAL_SECTION cs; 65 DWORD sessionsLen; 66 NetBIOSSession *sessions; 67 } NetBIOSAdapter; 68 69 typedef struct _NetBIOSAdapterTable { 70 CRITICAL_SECTION cs; 71 BOOL enumerated; 72 BOOL enumerating; 73 UCHAR tableSize; 74 NetBIOSAdapter *table; 75 } NetBIOSAdapterTable; 76 77 /* Just enough space for NBT right now */ 78 static NetBIOSTransportTableEntry gTransports[1]; 79 static UCHAR gNumTransports = 0; 80 static NetBIOSAdapterTable gNBTable; 81 82 static UCHAR nbResizeAdapterTable(UCHAR newSize) 83 { 84 UCHAR ret; 85 86 if (gNBTable.table) 87 gNBTable.table = HeapReAlloc(GetProcessHeap(), 88 HEAP_ZERO_MEMORY, gNBTable.table, 89 newSize * sizeof(NetBIOSAdapter)); 90 else 91 gNBTable.table = HeapAlloc(GetProcessHeap(), 92 HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter)); 93 if (gNBTable.table) 94 { 95 gNBTable.tableSize = newSize; 96 ret = NRC_GOODRET; 97 } 98 else 99 ret = NRC_OSRESNOTAV; 100 return ret; 101 } 102 103 void NetBIOSInit(void) 104 { 105 memset(&gNBTable, 0, sizeof(gNBTable)); 106 InitializeCriticalSection(&gNBTable.cs); 107 gNBTable.cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBIOSAdapterTable.cs"); 108 } 109 110 void NetBIOSShutdown(void) 111 { 112 UCHAR i; 113 114 EnterCriticalSection(&gNBTable.cs); 115 for (i = 0; i < gNBTable.tableSize; i++) 116 { 117 if (gNBTable.table[i].transport && 118 gNBTable.table[i].transport->cleanupAdapter) 119 gNBTable.table[i].transport->cleanupAdapter( 120 gNBTable.table[i].impl.data); 121 } 122 for (i = 0; i < gNumTransports; i++) 123 if (gTransports[i].transport.cleanup) 124 gTransports[i].transport.cleanup(); 125 LeaveCriticalSection(&gNBTable.cs); 126 gNBTable.cs.DebugInfo->Spare[0] = 0; 127 DeleteCriticalSection(&gNBTable.cs); 128 HeapFree(GetProcessHeap(), 0, gNBTable.table); 129 } 130 131 BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport) 132 { 133 BOOL ret; 134 135 TRACE(": transport 0x%08x, p %p\n", id, transport); 136 if (!transport) 137 ret = FALSE; 138 else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0])) 139 { 140 FIXME("Too many transports %d\n", gNumTransports + 1); 141 ret = FALSE; 142 } 143 else 144 { 145 UCHAR i; 146 147 ret = FALSE; 148 for (i = 0; !ret && i < gNumTransports; i++) 149 { 150 if (gTransports[i].id == id) 151 { 152 WARN("Replacing NetBIOS transport ID %d\n", id); 153 memcpy(&gTransports[i].transport, transport, 154 sizeof(NetBIOSTransport)); 155 ret = TRUE; 156 } 157 } 158 if (!ret) 159 { 160 gTransports[gNumTransports].id = id; 161 memcpy(&gTransports[gNumTransports].transport, transport, 162 sizeof(NetBIOSTransport)); 163 gNumTransports++; 164 ret = TRUE; 165 } 166 } 167 TRACE("returning %d\n", ret); 168 return ret; 169 } 170 171 /* In this, I acquire the table lock to make sure no one else is modifying it. 172 * This is _probably_ overkill since it should only be called during the 173 * context of a NetBIOSEnum call, but just to be safe.. 174 */ 175 BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data) 176 { 177 BOOL ret; 178 UCHAR i; 179 180 TRACE(": transport 0x%08x, ifIndex 0x%08x, data %p\n", transport, ifIndex, 181 data); 182 for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++) 183 ; 184 if ((i < gNumTransports) && gTransports[i].id == transport) 185 { 186 NetBIOSTransport *transportPtr = &gTransports[i].transport; 187 188 TRACE(": found transport %p for id 0x%08x\n", transportPtr, transport); 189 190 EnterCriticalSection(&gNBTable.cs); 191 ret = FALSE; 192 for (i = 0; i < gNBTable.tableSize && 193 gNBTable.table[i].transport != 0; i++) 194 ; 195 if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1) 196 { 197 UCHAR newSize; 198 199 if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR) 200 newSize = gNBTable.tableSize + ADAPTERS_INCR; 201 else 202 newSize = MAX_LANA + 1; 203 nbResizeAdapterTable(newSize); 204 } 205 if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0) 206 { 207 TRACE(": registering as LANA %d\n", i); 208 gNBTable.table[i].transport_id = transport; 209 gNBTable.table[i].transport = transportPtr; 210 gNBTable.table[i].impl.lana = i; 211 gNBTable.table[i].impl.ifIndex = ifIndex; 212 gNBTable.table[i].impl.data = data; 213 gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap()); 214 InitializeCriticalSection(&gNBTable.table[i].cs); 215 gNBTable.table[i].cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBIOSAdapterTable.NetBIOSAdapter.cs"); 216 gNBTable.table[i].enabled = TRUE; 217 ret = TRUE; 218 } 219 LeaveCriticalSection(&gNBTable.cs); 220 } 221 else 222 ret = FALSE; 223 TRACE("returning %d\n", ret); 224 return ret; 225 } 226 227 /* In this, I acquire the table lock to make sure no one else is modifying it. 228 * This is _probably_ overkill since it should only be called during the 229 * context of a NetBIOSEnum call, but just to be safe.. 230 */ 231 void NetBIOSEnableAdapter(UCHAR lana) 232 { 233 TRACE(": %d\n", lana); 234 if (lana < gNBTable.tableSize) 235 { 236 EnterCriticalSection(&gNBTable.cs); 237 if (gNBTable.table[lana].transport != 0) 238 gNBTable.table[lana].enabled = TRUE; 239 LeaveCriticalSection(&gNBTable.cs); 240 } 241 } 242 243 static void nbShutdownAdapter(NetBIOSAdapter *adapter) 244 { 245 if (adapter) 246 { 247 adapter->shuttingDown = TRUE; 248 NBCmdQueueCancelAll(adapter->cmdQueue); 249 if (adapter->transport->cleanupAdapter) 250 adapter->transport->cleanupAdapter(adapter->impl.data); 251 NBCmdQueueDestroy(adapter->cmdQueue); 252 adapter->cs.DebugInfo->Spare[0] = 0; 253 DeleteCriticalSection(&adapter->cs); 254 memset(adapter, 0, sizeof(NetBIOSAdapter)); 255 } 256 } 257 258 static void nbInternalEnum(void) 259 { 260 UCHAR i; 261 262 EnterCriticalSection(&gNBTable.cs); 263 TRACE("before mark\n"); 264 /* mark: */ 265 for (i = 0; i < gNBTable.tableSize; i++) 266 if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0) 267 gNBTable.table[i].enabled = FALSE; 268 269 TRACE("marked, before store, %d transports\n", gNumTransports); 270 /* store adapters: */ 271 for (i = 0; i < gNumTransports; i++) 272 if (gTransports[i].transport.enumerate) 273 gTransports[i].transport.enumerate(); 274 275 TRACE("before sweep\n"); 276 /* sweep: */ 277 for (i = 0; i < gNBTable.tableSize; i++) 278 if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0) 279 nbShutdownAdapter(&gNBTable.table[i]); 280 gNBTable.enumerated = TRUE; 281 LeaveCriticalSection(&gNBTable.cs); 282 } 283 284 UCHAR NetBIOSNumAdapters(void) 285 { 286 UCHAR ret, i; 287 288 if (!gNBTable.enumerated) 289 nbInternalEnum(); 290 for (i = 0, ret = 0; i < gNBTable.tableSize; i++) 291 if (gNBTable.table[i].transport != 0) 292 ret++; 293 return ret; 294 } 295 296 void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb, 297 void *closure) 298 { 299 TRACE("transport 0x%08x, callback %p, closure %p\n", transport, cb, 300 closure); 301 if (cb) 302 { 303 BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0; 304 UCHAR i, numLANAs = 0; 305 306 EnterCriticalSection(&gNBTable.cs); 307 if (!gNBTable.enumerating) 308 { 309 gNBTable.enumerating = TRUE; 310 nbInternalEnum(); 311 gNBTable.enumerating = FALSE; 312 } 313 for (i = 0; i < gNBTable.tableSize; i++) 314 if (enumAll || gNBTable.table[i].transport_id == transport) 315 numLANAs++; 316 if (numLANAs > 0) 317 { 318 UCHAR lanaIndex = 0; 319 320 for (i = 0; i < gNBTable.tableSize; i++) 321 if (gNBTable.table[i].transport_id != 0 && 322 (enumAll || gNBTable.table[i].transport_id == transport)) 323 cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id, 324 &gNBTable.table[i].impl, closure); 325 } 326 LeaveCriticalSection(&gNBTable.cs); 327 } 328 } 329 330 static NetBIOSAdapter *nbGetAdapter(UCHAR lana) 331 { 332 NetBIOSAdapter *ret = NULL; 333 334 TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize); 335 if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0 336 && gNBTable.table[lana].transport) 337 ret = &gNBTable.table[lana]; 338 TRACE("returning %p\n", ret); 339 return ret; 340 } 341 342 static UCHAR nbEnum(PNCB ncb) 343 { 344 PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer; 345 UCHAR i, ret; 346 347 TRACE(": ncb %p\n", ncb); 348 349 if (!lanas) 350 ret = NRC_BUFLEN; 351 else if (ncb->ncb_length < sizeof(LANA_ENUM)) 352 ret = NRC_BUFLEN; 353 else 354 { 355 nbInternalEnum(); 356 lanas->length = 0; 357 for (i = 0; i < gNBTable.tableSize; i++) 358 if (gNBTable.table[i].transport) 359 { 360 lanas->length++; 361 lanas->lana[i] = i; 362 } 363 ret = NRC_GOODRET; 364 } 365 TRACE("returning 0x%02x\n", ret); 366 return ret; 367 } 368 369 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session); 370 371 static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb) 372 { 373 UCHAR ret; 374 375 TRACE(": adapter %p, ncb %p\n", adapter, ncb); 376 377 if (!adapter) return NRC_BRIDGE; 378 if (!ncb) return NRC_INVADDRESS; 379 380 switch (ncb->ncb_command & 0x7f) 381 { 382 case NCBCANCEL: 383 case NCBADDNAME: 384 case NCBADDGRNAME: 385 case NCBDELNAME: 386 case NCBRESET: 387 case NCBSSTAT: 388 ret = NRC_CANCEL; 389 break; 390 391 /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated 392 * session if cancelled */ 393 case NCBCALL: 394 case NCBSEND: 395 case NCBCHAINSEND: 396 case NCBSENDNA: 397 case NCBCHAINSENDNA: 398 case NCBHANGUP: 399 { 400 if (ncb->ncb_lsn >= adapter->sessionsLen) 401 ret = NRC_SNUMOUT; 402 else if (!adapter->sessions[ncb->ncb_lsn].inUse) 403 ret = NRC_SNUMOUT; 404 else 405 { 406 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb); 407 if (ret == NRC_CMDCAN || ret == NRC_CANOCCR) 408 nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]); 409 } 410 break; 411 } 412 413 default: 414 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb); 415 } 416 TRACE("returning 0x%02x\n", ret); 417 return ret; 418 } 419 420 /* Resizes adapter to contain space for at least sessionsLen sessions. 421 * If allocating more space for sessions, sets the adapter's sessionsLen to 422 * sessionsLen. If the adapter's sessionsLen was already at least sessionsLen, 423 * does nothing. Does not modify existing sessions. Assumes the adapter is 424 * locked. 425 * Returns NRC_GOODRET on success, and something else on failure. 426 */ 427 static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen) 428 { 429 UCHAR ret = NRC_GOODRET; 430 431 if (adapter && adapter->sessionsLen < sessionsLen) 432 { 433 NetBIOSSession *newSessions; 434 435 if (adapter->sessions) 436 newSessions = HeapReAlloc(GetProcessHeap(), 437 HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen * 438 sizeof(NetBIOSSession)); 439 else 440 newSessions = HeapAlloc(GetProcessHeap(), 441 HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession)); 442 if (newSessions) 443 { 444 adapter->sessions = newSessions; 445 adapter->sessionsLen = sessionsLen; 446 } 447 else 448 ret = NRC_OSRESNOTAV; 449 } 450 return ret; 451 } 452 453 static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb) 454 { 455 UCHAR ret; 456 457 TRACE(": adapter %p, ncb %p\n", adapter, ncb); 458 459 if (!adapter) return NRC_BRIDGE; 460 if (!ncb) return NRC_INVADDRESS; 461 462 if (InterlockedIncrement(&adapter->resetting) == 1) 463 { 464 UCHAR i, resizeTo; 465 466 NBCmdQueueCancelAll(adapter->cmdQueue); 467 468 EnterCriticalSection(&adapter->cs); 469 for (i = 0; i < adapter->sessionsLen; i++) 470 if (adapter->sessions[i].inUse) 471 nbInternalHangup(adapter, &adapter->sessions[i]); 472 if (!ncb->ncb_lsn) 473 resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS : 474 ncb->ncb_callname[0]; 475 else if (adapter->sessionsLen == 0) 476 resizeTo = DEFAULT_NUM_SESSIONS; 477 else 478 resizeTo = 0; 479 if (resizeTo > 0) 480 ret = nbResizeAdapter(adapter, resizeTo); 481 else 482 ret = NRC_GOODRET; 483 LeaveCriticalSection(&adapter->cs); 484 } 485 else 486 ret = NRC_TOOMANY; 487 InterlockedDecrement(&adapter->resetting); 488 TRACE("returning 0x%02x\n", ret); 489 return ret; 490 } 491 492 static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb) 493 { 494 UCHAR ret, i, spaceFor; 495 PSESSION_HEADER sstat; 496 497 TRACE(": adapter %p, NCB %p\n", adapter, ncb); 498 499 if (!adapter) return NRC_BADDR; 500 if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF; 501 if (!ncb) return NRC_INVADDRESS; 502 if (!ncb->ncb_buffer) return NRC_BADDR; 503 if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN; 504 505 sstat = (PSESSION_HEADER)ncb->ncb_buffer; 506 ret = NRC_GOODRET; 507 memset(sstat, 0, sizeof(SESSION_HEADER)); 508 spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) / 509 sizeof(SESSION_BUFFER); 510 EnterCriticalSection(&adapter->cs); 511 for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++) 512 { 513 if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' || 514 !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ))) 515 { 516 if (sstat->num_sess < spaceFor) 517 { 518 PSESSION_BUFFER buf; 519 520 buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER) 521 + sstat->num_sess * sizeof(SESSION_BUFFER)); 522 buf->lsn = i; 523 buf->state = adapter->sessions[i].state; 524 memcpy(buf->local_name, adapter->sessions[i].local_name, 525 NCBNAMSZ); 526 memcpy(buf->remote_name, adapter->sessions[i].remote_name, 527 NCBNAMSZ); 528 buf->rcvs_outstanding = buf->sends_outstanding = 0; 529 sstat->num_sess++; 530 } 531 else 532 ret = NRC_BUFLEN; 533 } 534 } 535 LeaveCriticalSection(&adapter->cs); 536 537 TRACE("returning 0x%02x\n", ret); 538 return ret; 539 } 540 541 static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb) 542 { 543 UCHAR ret, i; 544 545 TRACE(": adapter %p, NCB %p\n", adapter, ncb); 546 547 if (!adapter) return NRC_BRIDGE; 548 if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF; 549 if (!adapter->transport->call) return NRC_ILLCMD; 550 if (!ncb) return NRC_INVADDRESS; 551 552 EnterCriticalSection(&adapter->cs); 553 for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++) 554 ; 555 if (i < adapter->sessionsLen) 556 { 557 adapter->sessions[i].inUse = TRUE; 558 adapter->sessions[i].state = CALL_PENDING; 559 memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ); 560 memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ); 561 ret = NRC_GOODRET; 562 } 563 else 564 ret = NRC_LOCTFUL; 565 LeaveCriticalSection(&adapter->cs); 566 567 if (ret == NRC_GOODRET) 568 { 569 ret = adapter->transport->call(adapter->impl.data, ncb, 570 &adapter->sessions[i].data); 571 if (ret == NRC_GOODRET) 572 { 573 ncb->ncb_lsn = i; 574 adapter->sessions[i].state = SESSION_ESTABLISHED; 575 } 576 else 577 { 578 adapter->sessions[i].inUse = FALSE; 579 adapter->sessions[i].state = 0; 580 } 581 } 582 TRACE("returning 0x%02x\n", ret); 583 return ret; 584 } 585 586 static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb) 587 { 588 UCHAR ret; 589 NetBIOSSession *session; 590 591 if (!adapter) return NRC_BRIDGE; 592 if (!adapter->transport->send) return NRC_ILLCMD; 593 if (!ncb) return NRC_INVADDRESS; 594 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; 595 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; 596 if (!ncb->ncb_buffer) return NRC_BADDR; 597 598 session = &adapter->sessions[ncb->ncb_lsn]; 599 if (session->state != SESSION_ESTABLISHED) 600 ret = NRC_SNUMOUT; 601 else 602 ret = adapter->transport->send(adapter->impl.data, session->data, ncb); 603 return ret; 604 } 605 606 static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb) 607 { 608 UCHAR ret; 609 NetBIOSSession *session; 610 611 if (!adapter) return NRC_BRIDGE; 612 if (!adapter->transport->recv) return NRC_ILLCMD; 613 if (!ncb) return NRC_INVADDRESS; 614 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; 615 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; 616 if (!ncb->ncb_buffer) return NRC_BADDR; 617 618 session = &adapter->sessions[ncb->ncb_lsn]; 619 if (session->state != SESSION_ESTABLISHED) 620 ret = NRC_SNUMOUT; 621 else 622 ret = adapter->transport->recv(adapter->impl.data, session->data, ncb); 623 return ret; 624 } 625 626 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session) 627 { 628 UCHAR ret; 629 630 if (!adapter) return NRC_BRIDGE; 631 if (!session) return NRC_SNUMOUT; 632 633 if (adapter->transport->hangup) 634 ret = adapter->transport->hangup(adapter->impl.data, session->data); 635 else 636 ret = NRC_ILLCMD; 637 EnterCriticalSection(&adapter->cs); 638 memset(session, 0, sizeof(NetBIOSSession)); 639 LeaveCriticalSection(&adapter->cs); 640 return ret; 641 } 642 643 static UCHAR nbHangup(NetBIOSAdapter *adapter, const NCB *ncb) 644 { 645 UCHAR ret; 646 NetBIOSSession *session; 647 648 if (!adapter) return NRC_BRIDGE; 649 if (!ncb) return NRC_INVADDRESS; 650 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; 651 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; 652 653 session = &adapter->sessions[ncb->ncb_lsn]; 654 if (session->state != SESSION_ESTABLISHED) 655 ret = NRC_SNUMOUT; 656 else 657 { 658 session->state = HANGUP_PENDING; 659 ret = nbInternalHangup(adapter, session); 660 } 661 return ret; 662 } 663 664 void NetBIOSHangupSession(const NCB *ncb) 665 { 666 NetBIOSAdapter *adapter; 667 668 if (!ncb) return; 669 670 adapter = nbGetAdapter(ncb->ncb_lana_num); 671 if (adapter) 672 { 673 if (ncb->ncb_lsn < adapter->sessionsLen && 674 adapter->sessions[ncb->ncb_lsn].inUse) 675 nbHangup(adapter, ncb); 676 } 677 } 678 679 static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb) 680 { 681 UCHAR ret; 682 683 if (!adapter) return NRC_BRIDGE; 684 if (!adapter->transport->astat) return NRC_ILLCMD; 685 if (!ncb) return NRC_INVADDRESS; 686 if (!ncb->ncb_buffer) return NRC_BADDR; 687 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN; 688 689 ret = adapter->transport->astat(adapter->impl.data, ncb); 690 if (ncb->ncb_callname[0] == '*') 691 { 692 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; 693 694 astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen; 695 } 696 return ret; 697 } 698 699 static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb) 700 { 701 UCHAR ret, cmd; 702 703 TRACE(": adapter %p, ncb %p\n", adapter, ncb); 704 705 if (!adapter) return NRC_BRIDGE; 706 if (!ncb) return NRC_INVADDRESS; 707 708 cmd = ncb->ncb_command & 0x7f; 709 if (cmd == NCBRESET) 710 ret = nbReset(adapter, ncb); 711 else 712 { 713 ret = NBCmdQueueAdd(adapter->cmdQueue, ncb); 714 if (ret == NRC_GOODRET) 715 { 716 switch (cmd) 717 { 718 case NCBCALL: 719 ret = nbCall(adapter, ncb); 720 break; 721 722 /* WinNT doesn't chain sends, it always sends immediately. 723 * Doubt there's any real significance to the NA variants. 724 */ 725 case NCBSEND: 726 case NCBSENDNA: 727 case NCBCHAINSEND: 728 case NCBCHAINSENDNA: 729 ret = nbSend(adapter, ncb); 730 break; 731 732 case NCBRECV: 733 ret = nbRecv(adapter, ncb); 734 break; 735 736 case NCBHANGUP: 737 ret = nbHangup(adapter, ncb); 738 break; 739 740 case NCBASTAT: 741 ret = nbAStat(adapter, ncb); 742 break; 743 744 case NCBFINDNAME: 745 if (adapter->transport->findName) 746 ret = adapter->transport->findName(adapter->impl.data, 747 ncb); 748 else 749 ret = NRC_ILLCMD; 750 break; 751 752 default: 753 FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command); 754 ret = NRC_ILLCMD; 755 } 756 NBCmdQueueComplete(adapter->cmdQueue, ncb, ret); 757 } 758 } 759 TRACE("returning 0x%02x\n", ret); 760 return ret; 761 } 762 763 static DWORD WINAPI nbCmdThread(LPVOID lpVoid) 764 { 765 PNCB ncb = lpVoid; 766 767 if (ncb) 768 { 769 UCHAR ret; 770 NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num); 771 772 if (adapter) 773 ret = nbDispatch(adapter, ncb); 774 else 775 ret = NRC_BRIDGE; 776 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret; 777 if (ncb->ncb_post) 778 ncb->ncb_post(ncb); 779 else if (ncb->ncb_event) 780 SetEvent(ncb->ncb_event); 781 } 782 return 0; 783 } 784 785 UCHAR WINAPI Netbios(PNCB ncb) 786 { 787 UCHAR ret, cmd; 788 789 TRACE("ncb = %p\n", ncb); 790 791 if (!ncb) return NRC_INVADDRESS; 792 793 TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n", 794 ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length); 795 cmd = ncb->ncb_command & 0x7f; 796 797 if (cmd == NCBENUM) 798 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb); 799 else if (cmd == NCBADDNAME) 800 { 801 FIXME("NCBADDNAME: stub, returning success\n"); 802 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET; 803 } 804 else 805 { 806 NetBIOSAdapter *adapter; 807 808 /* Apps not specifically written for WinNT won't do an NCBENUM first, 809 * so make sure the table has been enumerated at least once 810 */ 811 if (!gNBTable.enumerated) 812 nbInternalEnum(); 813 adapter = nbGetAdapter(ncb->ncb_lana_num); 814 if (!adapter) 815 ret = NRC_BRIDGE; 816 else 817 { 818 if (adapter->shuttingDown) 819 ret = NRC_IFBUSY; 820 else if (adapter->resetting) 821 ret = NRC_TOOMANY; 822 else 823 { 824 /* non-asynch commands first */ 825 if (cmd == NCBCANCEL) 826 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = 827 nbCancel(adapter, ncb); 828 else if (cmd == NCBSSTAT) 829 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = 830 nbSStat(adapter, ncb); 831 else 832 { 833 if (ncb->ncb_command & ASYNCH) 834 { 835 HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb, 836 CREATE_SUSPENDED, NULL); 837 838 if (thread != NULL) 839 { 840 ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING; 841 if (ncb->ncb_event) 842 ResetEvent(ncb->ncb_event); 843 ResumeThread(thread); 844 CloseHandle(thread); 845 ret = NRC_GOODRET; 846 } 847 else 848 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = 849 NRC_OSRESNOTAV; 850 } 851 else 852 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = 853 nbDispatch(adapter, ncb); 854 } 855 } 856 } 857 } 858 TRACE("returning 0x%02x\n", ret); 859 return ret; 860 } 861 862 DWORD WINAPI NetpNetBiosStatusToApiStatus(DWORD nrc) 863 { 864 DWORD ret; 865 866 switch (nrc) 867 { 868 case NRC_GOODRET: 869 ret = NO_ERROR; 870 break; 871 case NRC_NORES: 872 ret = NERR_NoNetworkResource; 873 break; 874 case NRC_DUPNAME: 875 ret = NERR_AlreadyExists; 876 break; 877 case NRC_NAMTFUL: 878 ret = NERR_TooManyNames; 879 break; 880 case NRC_ACTSES: 881 ret = NERR_DeleteLater; 882 break; 883 case NRC_REMTFUL: 884 ret = ERROR_REM_NOT_LIST; 885 break; 886 case NRC_NOCALL: 887 ret = NERR_NameNotFound; 888 break; 889 case NRC_NOWILD: 890 ret = ERROR_INVALID_PARAMETER; 891 break; 892 case NRC_INUSE: 893 ret = NERR_DuplicateName; 894 break; 895 case NRC_NAMERR: 896 ret = ERROR_INVALID_PARAMETER; 897 break; 898 case NRC_NAMCONF: 899 ret = NERR_DuplicateName; 900 break; 901 default: 902 ret = NERR_NetworkError; 903 } 904 return ret; 905 } 906