msi: Change an ERR to a WARN.
[wine.git] / dlls / netapi32 / netbios.c
blob71b710510cc2975797bb54785794c3f9ddc0d5f2
1 /* Copyright (c) 2003 Juan Lang
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.
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.
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
17 #include "config.h"
18 #include "wine/debug.h"
19 #include "nbcmdqueue.h"
20 #include "netbios.h"
22 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
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.
29 #define ADAPTERS_INCR 8
30 #define DEFAULT_NUM_SESSIONS 16
32 typedef struct _NetBIOSTransportTableEntry
34 ULONG id;
35 NetBIOSTransport transport;
36 } NetBIOSTransportTableEntry;
38 typedef struct _NetBIOSSession
40 BOOL inUse;
41 UCHAR state;
42 UCHAR local_name[NCBNAMSZ];
43 UCHAR remote_name[NCBNAMSZ];
44 void *data;
45 } NetBIOSSession;
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 reenabling
53 * adapters using NetBIOSEnableAdapter.
55 typedef struct _NetBIOSAdapter
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;
69 typedef struct _NetBIOSAdapterTable {
70 CRITICAL_SECTION cs;
71 BOOL enumerated;
72 BOOL enumerating;
73 UCHAR tableSize;
74 NetBIOSAdapter *table;
75 } NetBIOSAdapterTable;
77 /* Just enough space for NBT right now */
78 static NetBIOSTransportTableEntry gTransports[1];
79 static UCHAR gNumTransports = 0;
80 static NetBIOSAdapterTable gNBTable;
82 static UCHAR nbResizeAdapterTable(UCHAR newSize)
84 UCHAR ret;
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)
95 gNBTable.tableSize = newSize;
96 ret = NRC_GOODRET;
98 else
99 ret = NRC_OSRESNOTAV;
100 return ret;
103 void NetBIOSInit(void)
105 memset(&gNBTable, 0, sizeof(gNBTable));
106 InitializeCriticalSection(&gNBTable.cs);
109 void NetBIOSShutdown(void)
111 UCHAR i;
113 EnterCriticalSection(&gNBTable.cs);
114 for (i = 0; i < gNBTable.tableSize; i++)
116 if (gNBTable.table[i].transport &&
117 gNBTable.table[i].transport->cleanupAdapter)
118 gNBTable.table[i].transport->cleanupAdapter(
119 gNBTable.table[i].impl.data);
121 for (i = 0; i < gNumTransports; i++)
122 if (gTransports[i].transport.cleanup)
123 gTransports[i].transport.cleanup();
124 LeaveCriticalSection(&gNBTable.cs);
125 DeleteCriticalSection(&gNBTable.cs);
126 HeapFree(GetProcessHeap(), 0, gNBTable.table);
129 BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
131 BOOL ret;
133 TRACE(": transport 0x%08lx, p %p\n", id, transport);
134 if (!transport)
135 ret = FALSE;
136 else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
138 FIXME("Too many transports %d\n", gNumTransports + 1);
139 ret = FALSE;
141 else
143 UCHAR i;
145 ret = FALSE;
146 for (i = 0; !ret && i < gNumTransports; i++)
148 if (gTransports[i].id == id)
150 WARN("Replacing NetBIOS transport ID %ld\n", id);
151 memcpy(&gTransports[i].transport, transport,
152 sizeof(NetBIOSTransport));
153 ret = TRUE;
156 if (!ret)
158 gTransports[gNumTransports].id = id;
159 memcpy(&gTransports[gNumTransports].transport, transport,
160 sizeof(NetBIOSTransport));
161 gNumTransports++;
162 ret = TRUE;
165 TRACE("returning %d\n", ret);
166 return ret;
169 /* In this, I acquire the table lock to make sure no one else is modifying it.
170 * This is _probably_ overkill since it should only be called during the
171 * context of a NetBIOSEnum call, but just to be safe..
173 BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
175 BOOL ret;
176 UCHAR i;
178 TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
179 data);
180 for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
182 if (gTransports[i].id == transport)
184 NetBIOSTransport *transportPtr = &gTransports[i].transport;
186 TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
188 EnterCriticalSection(&gNBTable.cs);
189 ret = FALSE;
190 for (i = 0; i < gNBTable.tableSize &&
191 gNBTable.table[i].transport != 0; i++)
193 if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
195 UCHAR newSize;
197 if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
198 newSize = gNBTable.tableSize + ADAPTERS_INCR;
199 else
200 newSize = MAX_LANA + 1;
201 nbResizeAdapterTable(newSize);
203 if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
205 TRACE(": registering as LANA %d\n", i);
206 gNBTable.table[i].transport_id = transport;
207 gNBTable.table[i].transport = transportPtr;
208 gNBTable.table[i].impl.lana = i;
209 gNBTable.table[i].impl.ifIndex = ifIndex;
210 gNBTable.table[i].impl.data = data;
211 gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
212 InitializeCriticalSection(&gNBTable.table[i].cs);
213 gNBTable.table[i].enabled = TRUE;
214 ret = TRUE;
216 LeaveCriticalSection(&gNBTable.cs);
218 else
219 ret = FALSE;
220 TRACE("returning %d\n", ret);
221 return ret;
224 /* In this, I acquire the table lock to make sure no one else is modifying it.
225 * This is _probably_ overkill since it should only be called during the
226 * context of a NetBIOSEnum call, but just to be safe..
228 void NetBIOSEnableAdapter(UCHAR lana)
230 TRACE(": %d\n", lana);
231 if (lana < gNBTable.tableSize)
233 EnterCriticalSection(&gNBTable.cs);
234 if (gNBTable.table[lana].transport != 0)
235 gNBTable.table[lana].enabled = TRUE;
236 LeaveCriticalSection(&gNBTable.cs);
240 static void nbShutdownAdapter(NetBIOSAdapter *adapter)
242 if (adapter)
244 adapter->shuttingDown = TRUE;
245 NBCmdQueueCancelAll(adapter->cmdQueue);
246 if (adapter->transport->cleanupAdapter)
247 adapter->transport->cleanupAdapter(adapter->impl.data);
248 NBCmdQueueDestroy(adapter->cmdQueue);
249 DeleteCriticalSection(&adapter->cs);
250 memset(adapter, 0, sizeof(NetBIOSAdapter));
254 static void nbInternalEnum(void)
256 UCHAR i;
258 EnterCriticalSection(&gNBTable.cs);
259 TRACE("before mark\n");
260 /* mark: */
261 for (i = 0; i < gNBTable.tableSize; i++)
262 if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
263 gNBTable.table[i].enabled = FALSE;
265 TRACE("marked, before store, %d transports\n", gNumTransports);
266 /* store adapters: */
267 for (i = 0; i < gNumTransports; i++)
268 if (gTransports[i].transport.enumerate)
269 gTransports[i].transport.enumerate();
271 TRACE("before sweep\n");
272 /* sweep: */
273 for (i = 0; i < gNBTable.tableSize; i++)
274 if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
275 nbShutdownAdapter(&gNBTable.table[i]);
276 gNBTable.enumerated = TRUE;
277 LeaveCriticalSection(&gNBTable.cs);
280 UCHAR NetBIOSNumAdapters(void)
282 UCHAR ret, i;
284 if (!gNBTable.enumerated)
285 nbInternalEnum();
286 for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
287 if (gNBTable.table[i].transport != 0)
288 ret++;
289 return ret;
292 void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
293 void *closure)
295 TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
296 closure);
297 if (cb)
299 BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
300 UCHAR i, numLANAs = 0;
302 EnterCriticalSection(&gNBTable.cs);
303 if (!gNBTable.enumerating)
305 gNBTable.enumerating = TRUE;
306 nbInternalEnum();
307 gNBTable.enumerating = FALSE;
309 for (i = 0; i < gNBTable.tableSize; i++)
310 if (enumAll || gNBTable.table[i].transport_id == transport)
311 numLANAs++;
312 if (numLANAs > 0)
314 UCHAR lanaIndex = 0;
316 for (i = 0; i < gNBTable.tableSize; i++)
317 if (gNBTable.table[i].transport_id != 0 &&
318 (enumAll || gNBTable.table[i].transport_id == transport))
319 cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
320 &gNBTable.table[i].impl, closure);
322 LeaveCriticalSection(&gNBTable.cs);
326 static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
328 NetBIOSAdapter *ret = NULL;
330 TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
331 if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
332 && gNBTable.table[lana].transport)
333 ret = &gNBTable.table[lana];
334 TRACE("returning %p\n", ret);
335 return ret;
338 static UCHAR nbEnum(PNCB ncb)
340 PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
341 UCHAR i, ret;
343 TRACE(": ncb %p\n", ncb);
345 if (!lanas)
346 ret = NRC_BUFLEN;
347 else if (ncb->ncb_length < sizeof(LANA_ENUM))
348 ret = NRC_BUFLEN;
349 else
351 nbInternalEnum();
352 lanas->length = 0;
353 for (i = 0; i < gNBTable.tableSize; i++)
354 if (gNBTable.table[i].transport)
356 lanas->length++;
357 lanas->lana[i] = i;
359 ret = NRC_GOODRET;
361 TRACE("returning 0x%02x\n", ret);
362 return ret;
365 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
367 static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
369 UCHAR ret;
371 TRACE(": adapter %p, ncb %p\n", adapter, ncb);
373 if (!adapter) return NRC_BRIDGE;
374 if (!ncb) return NRC_INVADDRESS;
376 switch (ncb->ncb_command & 0x7f)
378 case NCBCANCEL:
379 case NCBADDNAME:
380 case NCBADDGRNAME:
381 case NCBDELNAME:
382 case NCBRESET:
383 case NCBSSTAT:
384 ret = NRC_CANCEL;
385 break;
387 /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
388 * session if cancelled */
389 case NCBCALL:
390 case NCBSEND:
391 case NCBCHAINSEND:
392 case NCBSENDNA:
393 case NCBCHAINSENDNA:
394 case NCBHANGUP:
396 if (ncb->ncb_lsn >= adapter->sessionsLen)
397 ret = NRC_SNUMOUT;
398 else if (!adapter->sessions[ncb->ncb_lsn].inUse)
399 ret = NRC_SNUMOUT;
400 else
402 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
403 if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
404 nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
406 break;
409 default:
410 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
412 TRACE("returning 0x%02x\n", ret);
413 return ret;
416 /* Resizes adapter to contain space for at least sessionsLen sessions.
417 * If allocating more space for sessions, sets the adapter's sessionsLen to
418 * sessionsLen. If the adapter's sessionsLen was already at least sessionsLen,
419 * does nothing. Does not modify existing sessions. Assumes the adapter is
420 * locked.
421 * Returns NRC_GOODRET on success, and something else on failure.
423 static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
425 UCHAR ret = NRC_GOODRET;
427 if (adapter && adapter->sessionsLen < sessionsLen)
429 NetBIOSSession *newSessions;
431 if (adapter->sessions)
432 newSessions = HeapReAlloc(GetProcessHeap(),
433 HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
434 sizeof(NetBIOSSession));
435 else
436 newSessions = HeapAlloc(GetProcessHeap(),
437 HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
438 if (newSessions)
440 adapter->sessions = newSessions;
441 adapter->sessionsLen = sessionsLen;
443 else
444 ret = NRC_OSRESNOTAV;
446 return ret;
449 static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
451 UCHAR ret;
453 TRACE(": adapter %p, ncb %p\n", adapter, ncb);
455 if (!adapter) return NRC_BRIDGE;
456 if (!ncb) return NRC_INVADDRESS;
458 if (InterlockedIncrement(&adapter->resetting) == 1)
460 UCHAR i, resizeTo;
462 NBCmdQueueCancelAll(adapter->cmdQueue);
464 EnterCriticalSection(&adapter->cs);
465 for (i = 0; i < adapter->sessionsLen; i++)
466 if (adapter->sessions[i].inUse)
467 nbInternalHangup(adapter, &adapter->sessions[i]);
468 if (!ncb->ncb_lsn)
469 resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
470 ncb->ncb_callname[0];
471 else if (adapter->sessionsLen == 0)
472 resizeTo = DEFAULT_NUM_SESSIONS;
473 else
474 resizeTo = 0;
475 if (resizeTo > 0)
476 ret = nbResizeAdapter(adapter, resizeTo);
477 else
478 ret = NRC_GOODRET;
479 LeaveCriticalSection(&adapter->cs);
481 else
482 ret = NRC_TOOMANY;
483 InterlockedDecrement(&adapter->resetting);
484 TRACE("returning 0x%02x\n", ret);
485 return ret;
488 static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
490 UCHAR ret, i, spaceFor;
491 PSESSION_HEADER sstat;
493 TRACE(": adapter %p, NCB %p\n", adapter, ncb);
495 if (!adapter) return NRC_BADDR;
496 if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
497 if (!ncb) return NRC_INVADDRESS;
498 if (!ncb->ncb_buffer) return NRC_BADDR;
499 if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
501 sstat = (PSESSION_HEADER)ncb->ncb_buffer;
502 ret = NRC_GOODRET;
503 memset(sstat, 0, sizeof(SESSION_HEADER));
504 spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
505 sizeof(SESSION_BUFFER);
506 EnterCriticalSection(&adapter->cs);
507 for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
509 if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
510 !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
512 if (sstat->num_sess < spaceFor)
514 PSESSION_BUFFER buf;
516 buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
517 + sstat->num_sess * sizeof(SESSION_BUFFER));
518 buf->lsn = i;
519 buf->state = adapter->sessions[i].state;
520 memcpy(buf->local_name, adapter->sessions[i].local_name,
521 NCBNAMSZ);
522 memcpy(buf->remote_name, adapter->sessions[i].remote_name,
523 NCBNAMSZ);
524 buf->rcvs_outstanding = buf->sends_outstanding = 0;
525 sstat->num_sess++;
527 else
528 ret = NRC_BUFLEN;
531 LeaveCriticalSection(&adapter->cs);
533 TRACE("returning 0x%02x\n", ret);
534 return ret;
537 static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
539 UCHAR ret, i;
541 TRACE(": adapter %p, NCB %p\n", adapter, ncb);
543 if (!adapter) return NRC_BRIDGE;
544 if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
545 if (!adapter->transport->call) return NRC_ILLCMD;
546 if (!ncb) return NRC_INVADDRESS;
548 EnterCriticalSection(&adapter->cs);
549 for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
551 if (i < adapter->sessionsLen)
553 adapter->sessions[i].inUse = TRUE;
554 adapter->sessions[i].state = CALL_PENDING;
555 memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
556 memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
557 ret = NRC_GOODRET;
559 else
560 ret = NRC_LOCTFUL;
561 LeaveCriticalSection(&adapter->cs);
563 if (ret == NRC_GOODRET)
565 ret = adapter->transport->call(adapter->impl.data, ncb,
566 &adapter->sessions[i].data);
567 if (ret == NRC_GOODRET)
569 ncb->ncb_lsn = i;
570 adapter->sessions[i].state = SESSION_ESTABLISHED;
572 else
574 adapter->sessions[i].inUse = FALSE;
575 adapter->sessions[i].state = 0;
578 TRACE("returning 0x%02x\n", ret);
579 return ret;
582 static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
584 UCHAR ret;
585 NetBIOSSession *session;
587 if (!adapter) return NRC_BRIDGE;
588 if (!adapter->transport->send) return NRC_ILLCMD;
589 if (!ncb) return NRC_INVADDRESS;
590 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
591 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
592 if (!ncb->ncb_buffer) return NRC_BADDR;
594 session = &adapter->sessions[ncb->ncb_lsn];
595 if (session->state != SESSION_ESTABLISHED)
596 ret = NRC_SNUMOUT;
597 else
598 ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
599 return ret;
602 static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
604 UCHAR ret;
605 NetBIOSSession *session;
607 if (!adapter) return NRC_BRIDGE;
608 if (!adapter->transport->recv) return NRC_ILLCMD;
609 if (!ncb) return NRC_INVADDRESS;
610 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
611 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
612 if (!ncb->ncb_buffer) return NRC_BADDR;
614 session = &adapter->sessions[ncb->ncb_lsn];
615 if (session->state != SESSION_ESTABLISHED)
616 ret = NRC_SNUMOUT;
617 else
618 ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
619 return ret;
622 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
624 UCHAR ret;
626 if (!adapter) return NRC_BRIDGE;
627 if (!session) return NRC_SNUMOUT;
629 if (adapter->transport->hangup)
630 ret = adapter->transport->hangup(adapter->impl.data, session->data);
631 else
632 ret = NRC_ILLCMD;
633 EnterCriticalSection(&adapter->cs);
634 memset(session, 0, sizeof(NetBIOSSession));
635 LeaveCriticalSection(&adapter->cs);
636 return NRC_GOODRET;
639 static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
641 UCHAR ret;
642 NetBIOSSession *session;
644 if (!adapter) return NRC_BRIDGE;
645 if (!ncb) return NRC_INVADDRESS;
646 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
647 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
649 session = &adapter->sessions[ncb->ncb_lsn];
650 if (session->state != SESSION_ESTABLISHED)
651 ret = NRC_SNUMOUT;
652 else
654 session->state = HANGUP_PENDING;
655 ret = nbInternalHangup(adapter, session);
657 return ret;
660 void NetBIOSHangupSession(PNCB ncb)
662 NetBIOSAdapter *adapter;
664 if (!ncb) return;
666 adapter = nbGetAdapter(ncb->ncb_lana_num);
667 if (adapter)
669 if (ncb->ncb_lsn < adapter->sessionsLen &&
670 adapter->sessions[ncb->ncb_lsn].inUse)
671 nbHangup(adapter, ncb);
675 static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
677 UCHAR ret;
679 if (!adapter) return NRC_BRIDGE;
680 if (!adapter->transport->astat) return NRC_ILLCMD;
681 if (!ncb) return NRC_INVADDRESS;
682 if (!ncb->ncb_buffer) return NRC_BADDR;
683 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
685 ret = adapter->transport->astat(adapter->impl.data, ncb);
686 if (ncb->ncb_callname[0] == '*')
688 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
690 astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
692 return ret;
695 static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
697 UCHAR ret, cmd;
699 TRACE(": adapter %p, ncb %p\n", adapter, ncb);
701 if (!adapter) return NRC_BRIDGE;
702 if (!ncb) return NRC_INVADDRESS;
704 cmd = ncb->ncb_command & 0x7f;
705 if (cmd == NCBRESET)
706 ret = nbReset(adapter, ncb);
707 else
709 ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
710 if (ret == NRC_GOODRET)
712 switch (cmd)
714 case NCBCALL:
715 ret = nbCall(adapter, ncb);
716 break;
718 /* WinNT doesn't chain sends, it always sends immediately.
719 * Doubt there's any real significance to the NA variants.
721 case NCBSEND:
722 case NCBSENDNA:
723 case NCBCHAINSEND:
724 case NCBCHAINSENDNA:
725 ret = nbSend(adapter, ncb);
726 break;
728 case NCBRECV:
729 ret = nbRecv(adapter, ncb);
730 break;
732 case NCBHANGUP:
733 ret = nbHangup(adapter, ncb);
734 break;
736 case NCBASTAT:
737 ret = nbAStat(adapter, ncb);
738 break;
740 case NCBFINDNAME:
741 if (adapter->transport->findName)
742 ret = adapter->transport->findName(adapter->impl.data,
743 ncb);
744 else
745 ret = NRC_ILLCMD;
746 break;
748 default:
749 FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
750 ret = NRC_ILLCMD;
752 NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
755 TRACE("returning 0x%02x\n", ret);
756 return ret;
759 static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
761 PNCB ncb = (PNCB)lpVoid;
763 if (ncb)
765 UCHAR ret;
766 NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
768 if (adapter)
769 ret = nbDispatch(adapter, ncb);
770 else
771 ret = NRC_BRIDGE;
772 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
773 if (ncb->ncb_post)
774 ncb->ncb_post(ncb);
775 else if (ncb->ncb_event)
776 SetEvent(ncb->ncb_event);
778 return 0;
781 UCHAR WINAPI Netbios(PNCB ncb)
783 UCHAR ret, cmd;
785 TRACE("ncb = %p\n", ncb);
787 if (!ncb) return NRC_INVADDRESS;
789 TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
790 ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
791 cmd = ncb->ncb_command & 0x7f;
793 if (cmd == NCBENUM)
794 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
795 else if (cmd == NCBADDNAME)
797 FIXME("NCBADDNAME: stub, returning success");
798 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
800 else
802 NetBIOSAdapter *adapter;
804 /* Apps not specifically written for WinNT won't do an NCBENUM first,
805 * so make sure the table has been enumerated at least once
807 if (!gNBTable.enumerated)
808 nbInternalEnum();
809 adapter = nbGetAdapter(ncb->ncb_lana_num);
810 if (!adapter)
811 ret = NRC_BRIDGE;
812 else
814 if (adapter->shuttingDown)
815 ret = NRC_IFBUSY;
816 else if (adapter->resetting)
817 ret = NRC_TOOMANY;
818 else
820 /* non-asynch commands first */
821 if (cmd == NCBCANCEL)
822 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
823 nbCancel(adapter, ncb);
824 else if (cmd == NCBSSTAT)
825 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
826 nbSStat(adapter, ncb);
827 else
829 if (ncb->ncb_command & ASYNCH)
831 HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
832 CREATE_SUSPENDED, NULL);
834 if (thread != NULL)
836 ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
837 if (ncb->ncb_event)
838 ResetEvent(ncb->ncb_event);
839 ResumeThread(thread);
840 CloseHandle(thread);
841 ret = NRC_GOODRET;
843 else
844 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
845 NRC_OSRESNOTAV;
847 else
848 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
849 nbDispatch(adapter, ncb);
854 TRACE("returning 0x%02x\n", ret);
855 return ret;