[NETAPI32]
[reactos.git] / reactos / dll / win32 / 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
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 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 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 }