reshuffling of dlls
[reactos.git] / reactos / dll / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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%08lx, p %p\n", id, transport);
134 if (!transport)
135 ret = FALSE;
136 else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
137 {
138 FIXME("You tried to add %d transports, but I only have space for %d\n",
139 gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0]));
140 ret = FALSE;
141 }
142 else
143 {
144 UCHAR i;
145
146 ret = FALSE;
147 for (i = 0; !ret && i < gNumTransports; i++)
148 {
149 if (gTransports[i].id == id)
150 {
151 WARN("Replacing NetBIOS transport ID %ld\n", id);
152 memcpy(&gTransports[i].transport, transport,
153 sizeof(NetBIOSTransport));
154 ret = TRUE;
155 }
156 }
157 if (!ret)
158 {
159 gTransports[gNumTransports].id = id;
160 memcpy(&gTransports[gNumTransports].transport, transport,
161 sizeof(NetBIOSTransport));
162 gNumTransports++;
163 ret = TRUE;
164 }
165 }
166 TRACE("returning %d\n", ret);
167 return ret;
168 }
169
170 /* In this, I acquire the table lock to make sure no one else is modifying it.
171 * This is _probably_ overkill since it should only be called during the
172 * context of a NetBIOSEnum call, but just to be safe..
173 */
174 BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
175 {
176 BOOL ret;
177 UCHAR i;
178
179 TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
180 data);
181 for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
182 ;
183 if (gTransports[i].id == transport)
184 {
185 NetBIOSTransport *transportPtr = &gTransports[i].transport;
186
187 TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
188
189 EnterCriticalSection(&gNBTable.cs);
190 ret = FALSE;
191 for (i = 0; i < gNBTable.tableSize &&
192 gNBTable.table[i].transport != 0; i++)
193 ;
194 if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
195 {
196 UCHAR newSize;
197
198 if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
199 newSize = gNBTable.tableSize + ADAPTERS_INCR;
200 else
201 newSize = MAX_LANA + 1;
202 nbResizeAdapterTable(newSize);
203 }
204 if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
205 {
206 TRACE(": registering as LANA %d\n", i);
207 gNBTable.table[i].transport_id = transport;
208 gNBTable.table[i].transport = transportPtr;
209 gNBTable.table[i].impl.lana = i;
210 gNBTable.table[i].impl.ifIndex = ifIndex;
211 gNBTable.table[i].impl.data = data;
212 gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
213 InitializeCriticalSection(&gNBTable.table[i].cs);
214 gNBTable.table[i].enabled = TRUE;
215 ret = TRUE;
216 }
217 LeaveCriticalSection(&gNBTable.cs);
218 }
219 else
220 ret = FALSE;
221 TRACE("returning %d\n", ret);
222 return ret;
223 }
224
225 /* In this, I acquire the table lock to make sure no one else is modifying it.
226 * This is _probably_ overkill since it should only be called during the
227 * context of a NetBIOSEnum call, but just to be safe..
228 */
229 void NetBIOSEnableAdapter(UCHAR lana)
230 {
231 TRACE(": %d\n", lana);
232 if (lana < gNBTable.tableSize)
233 {
234 EnterCriticalSection(&gNBTable.cs);
235 if (gNBTable.table[lana].transport != 0)
236 gNBTable.table[lana].enabled = TRUE;
237 LeaveCriticalSection(&gNBTable.cs);
238 }
239 }
240
241 static void nbShutdownAdapter(NetBIOSAdapter *adapter)
242 {
243 if (adapter)
244 {
245 adapter->shuttingDown = TRUE;
246 NBCmdQueueCancelAll(adapter->cmdQueue);
247 if (adapter->transport->cleanupAdapter)
248 adapter->transport->cleanupAdapter(adapter->impl.data);
249 NBCmdQueueDestroy(adapter->cmdQueue);
250 DeleteCriticalSection(&adapter->cs);
251 memset(adapter, 0, sizeof(NetBIOSAdapter));
252 }
253 }
254
255 static void nbInternalEnum(void)
256 {
257 UCHAR i;
258
259 EnterCriticalSection(&gNBTable.cs);
260 TRACE("before mark\n");
261 /* mark: */
262 for (i = 0; i < gNBTable.tableSize; i++)
263 if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
264 gNBTable.table[i].enabled = FALSE;
265
266 TRACE("marked, before store, %d transports\n", gNumTransports);
267 /* store adapters: */
268 for (i = 0; i < gNumTransports; i++)
269 if (gTransports[i].transport.enumerate)
270 gTransports[i].transport.enumerate();
271
272 TRACE("before sweep\n");
273 /* sweep: */
274 for (i = 0; i < gNBTable.tableSize; i++)
275 if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
276 nbShutdownAdapter(&gNBTable.table[i]);
277 gNBTable.enumerated = TRUE;
278 LeaveCriticalSection(&gNBTable.cs);
279 }
280
281 UCHAR NetBIOSNumAdapters(void)
282 {
283 UCHAR ret, i;
284
285 if (!gNBTable.enumerated)
286 nbInternalEnum();
287 for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
288 if (gNBTable.table[i].transport != 0)
289 ret++;
290 return ret;
291 }
292
293 void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
294 void *closure)
295 {
296 TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
297 closure);
298 if (cb)
299 {
300 BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
301 UCHAR i, numLANAs = 0;
302
303 EnterCriticalSection(&gNBTable.cs);
304 if (!gNBTable.enumerating)
305 {
306 gNBTable.enumerating = TRUE;
307 nbInternalEnum();
308 gNBTable.enumerating = FALSE;
309 }
310 for (i = 0; i < gNBTable.tableSize; i++)
311 if (enumAll || gNBTable.table[i].transport_id == transport)
312 numLANAs++;
313 if (numLANAs > 0)
314 {
315 UCHAR lanaIndex = 0;
316
317 for (i = 0; i < gNBTable.tableSize; i++)
318 if (gNBTable.table[i].transport_id != 0 &&
319 (enumAll || gNBTable.table[i].transport_id == transport))
320 cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
321 &gNBTable.table[i].impl, closure);
322 }
323 LeaveCriticalSection(&gNBTable.cs);
324 }
325 }
326
327 static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
328 {
329 NetBIOSAdapter *ret = NULL;
330
331 TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
332 if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
333 && gNBTable.table[lana].transport)
334 ret = &gNBTable.table[lana];
335 TRACE("returning %p\n", ret);
336 return ret;
337 }
338
339 static UCHAR nbEnum(PNCB ncb)
340 {
341 PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
342 UCHAR i, ret;
343
344 TRACE(": ncb %p\n", ncb);
345
346 if (!lanas)
347 ret = NRC_BUFLEN;
348 else if (ncb->ncb_length < sizeof(LANA_ENUM))
349 ret = NRC_BUFLEN;
350 else
351 {
352 nbInternalEnum();
353 lanas->length = 0;
354 for (i = 0; i < gNBTable.tableSize; i++)
355 if (gNBTable.table[i].transport)
356 {
357 lanas->length++;
358 lanas->lana[i] = i;
359 }
360 ret = NRC_GOODRET;
361 }
362 TRACE("returning 0x%02x\n", ret);
363 return ret;
364 }
365
366 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
367
368 static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
369 {
370 UCHAR ret;
371
372 TRACE(": adapter %p, ncb %p\n", adapter, ncb);
373
374 if (!adapter) return NRC_BRIDGE;
375 if (!ncb) return NRC_INVADDRESS;
376
377 switch (ncb->ncb_command & 0x7f)
378 {
379 case NCBCANCEL:
380 case NCBADDNAME:
381 case NCBADDGRNAME:
382 case NCBDELNAME:
383 case NCBRESET:
384 case NCBSSTAT:
385 ret = NRC_CANCEL;
386 break;
387
388 /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
389 * session if cancelled */
390 case NCBCALL:
391 case NCBSEND:
392 case NCBCHAINSEND:
393 case NCBSENDNA:
394 case NCBCHAINSENDNA:
395 case NCBHANGUP:
396 {
397 if (ncb->ncb_lsn >= adapter->sessionsLen)
398 ret = NRC_SNUMOUT;
399 else if (!adapter->sessions[ncb->ncb_lsn].inUse)
400 ret = NRC_SNUMOUT;
401 else
402 {
403 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
404 if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
405 nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
406 }
407 break;
408 }
409
410 default:
411 ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
412 }
413 TRACE("returning 0x%02x\n", ret);
414 return ret;
415 }
416
417 /* Resizes adapter to contain space for at least sessionsLen sessions.
418 * If allocating more space for sessions, sets the adapter's sessionsLen to
419 * sessionsLen. If the adapter's sessionsLen was already at least sessionsLen,
420 * does nothing. Does not modify existing sessions. Assumes the adapter is
421 * locked.
422 * Returns NRC_GOODRET on success, and something else on failure.
423 */
424 static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
425 {
426 UCHAR ret = NRC_GOODRET;
427
428 if (adapter && adapter->sessionsLen < sessionsLen)
429 {
430 NetBIOSSession *newSessions;
431
432 if (adapter->sessions)
433 newSessions = HeapReAlloc(GetProcessHeap(),
434 HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
435 sizeof(NetBIOSSession));
436 else
437 newSessions = HeapAlloc(GetProcessHeap(),
438 HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
439 if (newSessions)
440 {
441 adapter->sessions = newSessions;
442 adapter->sessionsLen = sessionsLen;
443 }
444 else
445 ret = NRC_OSRESNOTAV;
446 }
447 return ret;
448 }
449
450 static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
451 {
452 UCHAR ret;
453
454 TRACE(": adapter %p, ncb %p\n", adapter, ncb);
455
456 if (!adapter) return NRC_BRIDGE;
457 if (!ncb) return NRC_INVADDRESS;
458
459 if (InterlockedIncrement(&adapter->resetting) == 1)
460 {
461 UCHAR i, resizeTo;
462
463 NBCmdQueueCancelAll(adapter->cmdQueue);
464
465 EnterCriticalSection(&adapter->cs);
466 for (i = 0; i < adapter->sessionsLen; i++)
467 if (adapter->sessions[i].inUse)
468 nbInternalHangup(adapter, &adapter->sessions[i]);
469 if (!ncb->ncb_lsn)
470 resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
471 ncb->ncb_callname[0];
472 else if (adapter->sessionsLen == 0)
473 resizeTo = DEFAULT_NUM_SESSIONS;
474 else
475 resizeTo = 0;
476 if (resizeTo > 0)
477 ret = nbResizeAdapter(adapter, resizeTo);
478 else
479 ret = NRC_GOODRET;
480 LeaveCriticalSection(&adapter->cs);
481 }
482 else
483 ret = NRC_TOOMANY;
484 InterlockedDecrement(&adapter->resetting);
485 TRACE("returning 0x%02x\n", ret);
486 return ret;
487 }
488
489 static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
490 {
491 UCHAR ret, i, spaceFor;
492 PSESSION_HEADER sstat;
493
494 TRACE(": adapter %p, NCB %p\n", adapter, ncb);
495
496 if (!adapter) return NRC_BADDR;
497 if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
498 if (!ncb) return NRC_INVADDRESS;
499 if (!ncb->ncb_buffer) return NRC_BADDR;
500 if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
501
502 sstat = (PSESSION_HEADER)ncb->ncb_buffer;
503 ret = NRC_GOODRET;
504 memset(sstat, 0, sizeof(SESSION_HEADER));
505 spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
506 sizeof(SESSION_BUFFER);
507 EnterCriticalSection(&adapter->cs);
508 for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
509 {
510 if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
511 !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
512 {
513 if (sstat->num_sess < spaceFor)
514 {
515 PSESSION_BUFFER buf;
516
517 buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
518 + sstat->num_sess * sizeof(SESSION_BUFFER));
519 buf->lsn = i;
520 buf->state = adapter->sessions[i].state;
521 memcpy(buf->local_name, adapter->sessions[i].local_name,
522 NCBNAMSZ);
523 memcpy(buf->remote_name, adapter->sessions[i].remote_name,
524 NCBNAMSZ);
525 buf->rcvs_outstanding = buf->sends_outstanding = 0;
526 sstat->num_sess++;
527 }
528 else
529 ret = NRC_BUFLEN;
530 }
531 }
532 LeaveCriticalSection(&adapter->cs);
533
534 TRACE("returning 0x%02x\n", ret);
535 return ret;
536 }
537
538 static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
539 {
540 UCHAR ret, i;
541
542 TRACE(": adapter %p, NCB %p\n", adapter, ncb);
543
544 if (!adapter) return NRC_BRIDGE;
545 if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
546 if (!adapter->transport->call) return NRC_ILLCMD;
547 if (!ncb) return NRC_INVADDRESS;
548
549 EnterCriticalSection(&adapter->cs);
550 for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
551 ;
552 if (i < adapter->sessionsLen)
553 {
554 adapter->sessions[i].inUse = TRUE;
555 adapter->sessions[i].state = CALL_PENDING;
556 memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
557 memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
558 ret = NRC_GOODRET;
559 }
560 else
561 ret = NRC_LOCTFUL;
562 LeaveCriticalSection(&adapter->cs);
563
564 if (ret == NRC_GOODRET)
565 {
566 ret = adapter->transport->call(adapter->impl.data, ncb,
567 &adapter->sessions[i].data);
568 if (ret == NRC_GOODRET)
569 {
570 ncb->ncb_lsn = i;
571 adapter->sessions[i].state = SESSION_ESTABLISHED;
572 }
573 else
574 {
575 adapter->sessions[i].inUse = FALSE;
576 adapter->sessions[i].state = 0;
577 }
578 }
579 TRACE("returning 0x%02x\n", ret);
580 return ret;
581 }
582
583 static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
584 {
585 UCHAR ret;
586 NetBIOSSession *session;
587
588 if (!adapter) return NRC_BRIDGE;
589 if (!adapter->transport->send) return NRC_ILLCMD;
590 if (!ncb) return NRC_INVADDRESS;
591 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
592 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
593 if (!ncb->ncb_buffer) return NRC_BADDR;
594
595 session = &adapter->sessions[ncb->ncb_lsn];
596 if (session->state != SESSION_ESTABLISHED)
597 ret = NRC_SNUMOUT;
598 else
599 ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
600 return ret;
601 }
602
603 static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
604 {
605 UCHAR ret;
606 NetBIOSSession *session;
607
608 if (!adapter) return NRC_BRIDGE;
609 if (!adapter->transport->recv) return NRC_ILLCMD;
610 if (!ncb) return NRC_INVADDRESS;
611 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
612 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
613 if (!ncb->ncb_buffer) return NRC_BADDR;
614
615 session = &adapter->sessions[ncb->ncb_lsn];
616 if (session->state != SESSION_ESTABLISHED)
617 ret = NRC_SNUMOUT;
618 else
619 ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
620 return ret;
621 }
622
623 static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
624 {
625 UCHAR ret;
626
627 if (!adapter) return NRC_BRIDGE;
628 if (!session) return NRC_SNUMOUT;
629
630 if (adapter->transport->hangup)
631 ret = adapter->transport->hangup(adapter->impl.data, session->data);
632 else
633 ret = NRC_ILLCMD;
634 EnterCriticalSection(&adapter->cs);
635 memset(session, 0, sizeof(NetBIOSSession));
636 LeaveCriticalSection(&adapter->cs);
637 return NRC_GOODRET;
638 }
639
640 static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
641 {
642 UCHAR ret;
643 NetBIOSSession *session;
644
645 if (!adapter) return NRC_BRIDGE;
646 if (!ncb) return NRC_INVADDRESS;
647 if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
648 if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
649
650 session = &adapter->sessions[ncb->ncb_lsn];
651 if (session->state != SESSION_ESTABLISHED)
652 ret = NRC_SNUMOUT;
653 else
654 {
655 session->state = HANGUP_PENDING;
656 ret = nbInternalHangup(adapter, session);
657 }
658 return ret;
659 }
660
661 void NetBIOSHangupSession(PNCB ncb)
662 {
663 NetBIOSAdapter *adapter;
664
665 if (!ncb) return;
666
667 adapter = nbGetAdapter(ncb->ncb_lana_num);
668 if (adapter)
669 {
670 if (ncb->ncb_lsn < adapter->sessionsLen &&
671 adapter->sessions[ncb->ncb_lsn].inUse)
672 nbHangup(adapter, ncb);
673 }
674 }
675
676 static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
677 {
678 UCHAR ret;
679
680 if (!adapter) return NRC_BRIDGE;
681 if (!adapter->transport->astat) return NRC_ILLCMD;
682 if (!ncb) return NRC_INVADDRESS;
683 if (!ncb->ncb_buffer) return NRC_BADDR;
684 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
685
686 ret = adapter->transport->astat(adapter->impl.data, ncb);
687 if (ncb->ncb_callname[0] == '*')
688 {
689 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
690
691 astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
692 }
693 return ret;
694 }
695
696 static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
697 {
698 UCHAR ret, cmd;
699
700 TRACE(": adapter %p, ncb %p\n", adapter, ncb);
701
702 if (!adapter) return NRC_BRIDGE;
703 if (!ncb) return NRC_INVADDRESS;
704
705 cmd = ncb->ncb_command & 0x7f;
706 if (cmd == NCBRESET)
707 ret = nbReset(adapter, ncb);
708 else
709 {
710 ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
711 if (ret == NRC_GOODRET)
712 {
713 switch (cmd)
714 {
715 case NCBCALL:
716 ret = nbCall(adapter, ncb);
717 break;
718
719 /* WinNT doesn't chain sends, it always sends immediately.
720 * Doubt there's any real significance to the NA variants.
721 */
722 case NCBSEND:
723 case NCBSENDNA:
724 case NCBCHAINSEND:
725 case NCBCHAINSENDNA:
726 ret = nbSend(adapter, ncb);
727 break;
728
729 case NCBRECV:
730 ret = nbRecv(adapter, ncb);
731 break;
732
733 case NCBHANGUP:
734 ret = nbHangup(adapter, ncb);
735 break;
736
737 case NCBASTAT:
738 ret = nbAStat(adapter, ncb);
739 break;
740
741 case NCBFINDNAME:
742 if (adapter->transport->findName)
743 ret = adapter->transport->findName(adapter->impl.data,
744 ncb);
745 else
746 ret = NRC_ILLCMD;
747 break;
748
749 default:
750 FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
751 ret = NRC_ILLCMD;
752 }
753 NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
754 }
755 }
756 TRACE("returning 0x%02x\n", ret);
757 return ret;
758 }
759
760 static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
761 {
762 PNCB ncb = (PNCB)lpVoid;
763
764 if (ncb)
765 {
766 UCHAR ret;
767 NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
768
769 if (adapter)
770 ret = nbDispatch(adapter, ncb);
771 else
772 ret = NRC_BRIDGE;
773 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
774 if (ncb->ncb_post)
775 ncb->ncb_post(ncb);
776 else if (ncb->ncb_event)
777 SetEvent(ncb->ncb_event);
778 }
779 return 0;
780 }
781
782 UCHAR WINAPI Netbios(PNCB ncb)
783 {
784 UCHAR ret, cmd;
785
786 TRACE("ncb = %p\n", ncb);
787
788 if (!ncb) return NRC_INVADDRESS;
789
790 TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
791 ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
792 cmd = ncb->ncb_command & 0x7f;
793
794 if (cmd == NCBENUM)
795 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
796 else if (cmd == NCBADDNAME)
797 {
798 FIXME("NCBADDNAME: stub, returning success");
799 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
800 }
801 else
802 {
803 NetBIOSAdapter *adapter;
804
805 /* Apps not specifically written for WinNT won't do an NCBENUM first,
806 * so make sure the table has been enumerated at least once
807 */
808 if (!gNBTable.enumerated)
809 nbInternalEnum();
810 adapter = nbGetAdapter(ncb->ncb_lana_num);
811 if (!adapter)
812 ret = NRC_BRIDGE;
813 else
814 {
815 if (adapter->shuttingDown)
816 ret = NRC_IFBUSY;
817 else if (adapter->resetting)
818 ret = NRC_TOOMANY;
819 else
820 {
821 /* non-asynch commands first */
822 if (cmd == NCBCANCEL)
823 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
824 nbCancel(adapter, ncb);
825 else if (cmd == NCBSSTAT)
826 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
827 nbSStat(adapter, ncb);
828 else
829 {
830 if (ncb->ncb_command & ASYNCH)
831 {
832 HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
833 CREATE_SUSPENDED, NULL);
834
835 if (thread != NULL)
836 {
837 ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
838 if (ncb->ncb_event)
839 ResetEvent(ncb->ncb_event);
840 ResumeThread(thread);
841 CloseHandle(thread);
842 ret = NRC_GOODRET;
843 }
844 else
845 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
846 NRC_OSRESNOTAV;
847 }
848 else
849 ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
850 nbDispatch(adapter, ncb);
851 }
852 }
853 }
854 }
855 TRACE("returning 0x%02x\n", ret);
856 return ret;
857 }