winspool: Start support for XcvMonitor and XcvPort in OpenPrinter.
[wine] / dlls / netapi32 / netbios.c
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 #include "config.h"
18 #include "wine/debug.h"
19 #include "nbcmdqueue.h"
20 #include "netbios.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 reenabling
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 }
108
109 void NetBIOSShutdown(void)
110 {
111     UCHAR i;
112
113     EnterCriticalSection(&gNBTable.cs);
114     for (i = 0; i < gNBTable.tableSize; i++)
115     {
116         if (gNBTable.table[i].transport &&
117          gNBTable.table[i].transport->cleanupAdapter)
118             gNBTable.table[i].transport->cleanupAdapter(
119              gNBTable.table[i].impl.data);
120     }
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);
127 }
128
129 BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
130 {
131     BOOL ret;
132
133     TRACE(": transport 0x%08x, p %p\n", id, transport);
134     if (!transport)
135         ret = FALSE;
136     else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
137     {
138         FIXME("Too many transports %d\n", gNumTransports + 1);
139         ret = FALSE;
140     }
141     else
142     {
143         UCHAR i;
144
145         ret = FALSE;
146         for (i = 0; !ret && i < gNumTransports; i++)
147         {
148             if (gTransports[i].id == id)
149             {
150                 WARN("Replacing NetBIOS transport ID %d\n", id);
151                 memcpy(&gTransports[i].transport, transport,
152                  sizeof(NetBIOSTransport));
153                 ret = TRUE;
154             }
155         }
156         if (!ret)
157         {
158             gTransports[gNumTransports].id = id;
159             memcpy(&gTransports[gNumTransports].transport, transport,
160              sizeof(NetBIOSTransport));
161             gNumTransports++;
162             ret = TRUE;
163         }
164     }
165     TRACE("returning %d\n", ret);
166     return ret;
167 }
168
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..
172  */
173 BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
174 {
175     BOOL ret;
176     UCHAR i;
177
178     TRACE(": transport 0x%08x, ifIndex 0x%08x, data %p\n", transport, ifIndex,
179      data);
180     for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
181         ;
182     if (gTransports[i].id == transport)
183     {
184         NetBIOSTransport *transportPtr = &gTransports[i].transport;
185
186         TRACE(": found transport %p for id 0x%08x\n", transportPtr, transport);
187
188         EnterCriticalSection(&gNBTable.cs);
189         ret = FALSE;
190         for (i = 0; i < gNBTable.tableSize &&
191          gNBTable.table[i].transport != 0; i++)
192             ;
193         if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
194         {
195             UCHAR newSize;
196
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);
202         }
203         if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
204         {
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;
215         }
216         LeaveCriticalSection(&gNBTable.cs);
217     }
218     else
219         ret = FALSE;
220     TRACE("returning %d\n", ret);
221     return ret;
222 }
223
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..
227  */
228 void NetBIOSEnableAdapter(UCHAR lana)
229 {
230     TRACE(": %d\n", lana);
231     if (lana < gNBTable.tableSize)
232     {
233         EnterCriticalSection(&gNBTable.cs);
234         if (gNBTable.table[lana].transport != 0)
235             gNBTable.table[lana].enabled = TRUE;
236         LeaveCriticalSection(&gNBTable.cs);
237     }
238 }
239
240 static void nbShutdownAdapter(NetBIOSAdapter *adapter)
241 {
242     if (adapter)
243     {
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));
251     }
252 }
253
254 static void nbInternalEnum(void)
255 {
256     UCHAR i;
257
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;
264
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();
270
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);
278 }
279
280 UCHAR NetBIOSNumAdapters(void)
281 {
282     UCHAR ret, i;
283
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;
290 }
291
292 void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
293  void *closure)
294 {
295     TRACE("transport 0x%08x, callback %p, closure %p\n", transport, cb,
296      closure);
297     if (cb)
298     {
299         BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
300         UCHAR i, numLANAs = 0;
301
302         EnterCriticalSection(&gNBTable.cs);
303         if (!gNBTable.enumerating)
304         {
305             gNBTable.enumerating = TRUE;
306             nbInternalEnum();
307             gNBTable.enumerating = FALSE;
308         }
309         for (i = 0; i < gNBTable.tableSize; i++)
310             if (enumAll || gNBTable.table[i].transport_id == transport)
311                 numLANAs++;
312         if (numLANAs > 0)
313         {
314             UCHAR lanaIndex = 0;
315
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);
321         }
322         LeaveCriticalSection(&gNBTable.cs);
323     }
324 }
325
326 static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
327 {
328     NetBIOSAdapter *ret = NULL;
329
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;
336 }
337
338 static UCHAR nbEnum(PNCB ncb)
339 {
340     PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
341     UCHAR i, ret;
342
343     TRACE(": ncb %p\n", ncb);
344
345     if (!lanas)
346         ret = NRC_BUFLEN;
347     else if (ncb->ncb_length < sizeof(LANA_ENUM))
348         ret = NRC_BUFLEN;
349     else
350     {
351         nbInternalEnum();
352         lanas->length = 0;
353         for (i = 0; i < gNBTable.tableSize; i++)
354             if (gNBTable.table[i].transport)
355             {
356                 lanas->length++;
357                 lanas->lana[i] = i;
358             }
359         ret = NRC_GOODRET;
360     }
361     TRACE("returning 0x%02x\n", ret);
362     return ret;
363 }
364
365 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
366
367 static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
368 {
369     UCHAR ret;
370
371     TRACE(": adapter %p, ncb %p\n", adapter, ncb);
372
373     if (!adapter) return NRC_BRIDGE;
374     if (!ncb) return NRC_INVADDRESS;
375
376     switch (ncb->ncb_command & 0x7f)
377     {
378         case NCBCANCEL:
379         case NCBADDNAME:
380         case NCBADDGRNAME:
381         case NCBDELNAME:
382         case NCBRESET:
383         case NCBSSTAT:
384             ret = NRC_CANCEL;
385             break;
386
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:
395         {
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
401             {
402                 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
403                 if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
404                     nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
405             }
406             break;
407         }
408
409         default:
410             ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
411     }
412     TRACE("returning 0x%02x\n", ret);
413     return ret;
414 }
415
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.
422  */
423 static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
424 {
425     UCHAR ret = NRC_GOODRET;
426
427     if (adapter && adapter->sessionsLen < sessionsLen)
428     {
429         NetBIOSSession *newSessions;
430
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)
439         {
440             adapter->sessions = newSessions;
441             adapter->sessionsLen = sessionsLen;
442         }
443         else
444             ret = NRC_OSRESNOTAV;
445     }
446     return ret;
447 }
448
449 static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
450 {
451     UCHAR ret;
452
453     TRACE(": adapter %p, ncb %p\n", adapter, ncb);
454
455     if (!adapter) return NRC_BRIDGE;
456     if (!ncb) return NRC_INVADDRESS;
457
458     if (InterlockedIncrement(&adapter->resetting) == 1)
459     {
460         UCHAR i, resizeTo;
461
462         NBCmdQueueCancelAll(adapter->cmdQueue);
463
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);
480     }
481     else
482         ret = NRC_TOOMANY;
483     InterlockedDecrement(&adapter->resetting);
484     TRACE("returning 0x%02x\n", ret);
485     return ret;
486 }
487
488 static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
489 {
490     UCHAR ret, i, spaceFor;
491     PSESSION_HEADER sstat;
492
493     TRACE(": adapter %p, NCB %p\n", adapter, ncb);
494
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;
500
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++)
508     {
509         if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
510          !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
511         {
512             if (sstat->num_sess < spaceFor)
513             {
514                 PSESSION_BUFFER buf;
515                
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++;
526             }
527             else
528                 ret = NRC_BUFLEN;
529         }
530     }
531     LeaveCriticalSection(&adapter->cs);
532
533     TRACE("returning 0x%02x\n", ret);
534     return ret;
535 }
536
537 static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
538 {
539     UCHAR ret, i;
540
541     TRACE(": adapter %p, NCB %p\n", adapter, ncb);
542
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;
547
548     EnterCriticalSection(&adapter->cs);
549     for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
550         ;
551     if (i < adapter->sessionsLen)
552     {
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;
558     }
559     else
560         ret = NRC_LOCTFUL;
561     LeaveCriticalSection(&adapter->cs);
562
563     if (ret == NRC_GOODRET)
564     {
565         ret = adapter->transport->call(adapter->impl.data, ncb,
566          &adapter->sessions[i].data);
567         if (ret == NRC_GOODRET)
568         {
569             ncb->ncb_lsn = i;
570             adapter->sessions[i].state = SESSION_ESTABLISHED;
571         }
572         else
573         {
574             adapter->sessions[i].inUse = FALSE;
575             adapter->sessions[i].state = 0;
576         }
577     }
578     TRACE("returning 0x%02x\n", ret);
579     return ret;
580 }
581
582 static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
583 {
584     UCHAR ret;
585     NetBIOSSession *session;
586
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;
593
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;
600 }
601
602 static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
603 {
604     UCHAR ret;
605     NetBIOSSession *session;
606
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;
613
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;
620 }
621
622 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
623 {
624     UCHAR ret;
625
626     if (!adapter) return NRC_BRIDGE;
627     if (!session) return NRC_SNUMOUT;
628
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;
637 }
638
639 static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
640 {
641     UCHAR ret;
642     NetBIOSSession *session;
643
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;
648
649     session = &adapter->sessions[ncb->ncb_lsn];
650     if (session->state != SESSION_ESTABLISHED)
651         ret = NRC_SNUMOUT;
652     else
653     {
654         session->state = HANGUP_PENDING;
655         ret = nbInternalHangup(adapter, session);
656     }
657     return ret;
658 }
659
660 void NetBIOSHangupSession(PNCB ncb)
661 {
662     NetBIOSAdapter *adapter;
663
664     if (!ncb) return;
665
666     adapter = nbGetAdapter(ncb->ncb_lana_num);
667     if (adapter)
668     {
669         if (ncb->ncb_lsn < adapter->sessionsLen &&
670          adapter->sessions[ncb->ncb_lsn].inUse)
671             nbHangup(adapter, ncb);
672     }
673 }
674
675 static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
676 {
677     UCHAR ret;
678
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;
684
685     ret = adapter->transport->astat(adapter->impl.data, ncb);
686     if (ncb->ncb_callname[0] == '*')
687     {
688         PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
689
690         astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
691     }
692     return ret;
693 }
694
695 static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
696 {
697     UCHAR ret, cmd;
698
699     TRACE(": adapter %p, ncb %p\n", adapter, ncb);
700
701     if (!adapter) return NRC_BRIDGE;
702     if (!ncb) return NRC_INVADDRESS;
703
704     cmd = ncb->ncb_command & 0x7f;
705     if (cmd == NCBRESET)
706         ret = nbReset(adapter, ncb);
707     else
708     {
709         ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
710         if (ret == NRC_GOODRET)
711         {
712             switch (cmd)
713             {
714                 case NCBCALL:
715                     ret = nbCall(adapter, ncb);
716                     break;
717
718                 /* WinNT doesn't chain sends, it always sends immediately.
719                  * Doubt there's any real significance to the NA variants.
720                  */
721                 case NCBSEND:
722                 case NCBSENDNA:
723                 case NCBCHAINSEND:
724                 case NCBCHAINSENDNA:
725                     ret = nbSend(adapter, ncb);
726                     break;
727
728                 case NCBRECV:
729                     ret = nbRecv(adapter, ncb);
730                     break;
731
732                 case NCBHANGUP:
733                     ret = nbHangup(adapter, ncb);
734                     break;
735
736                 case NCBASTAT:
737                     ret = nbAStat(adapter, ncb);
738                     break;
739
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;
747
748                 default:
749                     FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
750                     ret = NRC_ILLCMD;
751             }
752             NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
753         }
754     }
755     TRACE("returning 0x%02x\n", ret);
756     return ret;
757 }
758
759 static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
760 {
761     PNCB ncb = (PNCB)lpVoid;
762
763     if (ncb)
764     {
765         UCHAR ret;
766         NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
767
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);
777     }
778     return 0;
779 }
780
781 UCHAR WINAPI Netbios(PNCB ncb)
782 {
783     UCHAR ret, cmd;
784
785     TRACE("ncb = %p\n", ncb);
786
787     if (!ncb) return NRC_INVADDRESS;
788
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;
792
793     if (cmd == NCBENUM)
794         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
795     else if (cmd == NCBADDNAME)
796     {
797         FIXME("NCBADDNAME: stub, returning success\n");
798         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
799     }
800     else
801     {
802         NetBIOSAdapter *adapter;
803
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
806          */
807         if (!gNBTable.enumerated)
808             nbInternalEnum();
809         adapter = nbGetAdapter(ncb->ncb_lana_num);
810         if (!adapter)
811             ret = NRC_BRIDGE;
812         else
813         {
814             if (adapter->shuttingDown)
815                 ret = NRC_IFBUSY;
816             else if (adapter->resetting)
817                 ret = NRC_TOOMANY;
818             else
819             {
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
828                 {
829                     if (ncb->ncb_command & ASYNCH)
830                     {
831                         HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
832                          CREATE_SUSPENDED, NULL);
833
834                         if (thread != NULL)
835                         {
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;
842                         }
843                         else
844                         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
845                          NRC_OSRESNOTAV;
846                     }
847                     else
848                         ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
849                          nbDispatch(adapter, ncb);
850                 }
851             }
852         }
853     }
854     TRACE("returning 0x%02x\n", ret);
855     return ret;
856 }