'=========================================================================== ' Subject: PB NETBIOS Date: 09-01-96 (22:19) ' Author: Andrew Coggins Code: PB ' Origin: rjohnso6@ix.netcom.com Packet: NETWORK.ABC '=========================================================================== '==============================PBNETBIO.BAS================================ 'This is public domain stuff, use at your own risk, etc. To test the use 'of the NetBios commands used to receive and send broadcast messages over 'a network. This sends ONE 80 character message to the other machine each 'time you hit "S" You must have registered an new name (and know its 'number) in the local name table before you can use the broadcast stuff. 'This is true for NetBIOS over NetBEUI in a DOS VDM in OS2 PEER. You may 'can avoid having to create a name in true DOS/NOVELL stuff. I don't have 'that so I can't test it. You only have to add a name once for your process 'to use it many times. (You should probably delete it when you finish your 'process because the name table is finite , but OS2 deletes it for me 'when I close the VDM so I don't worry about it. (I can't say enough good 'about OS2's accurate simulation of DOS (unlike Windoze XX))) 'This does not check for the presence of a 'network. PB BBS has an example already that does that (PBLANT.ZIP). 'Another usefull document is Tom Thompson's NetBios Programmers Reference '(1988 is the latest version I have) also on the PB BBS. This is meant 'mostly as an example of how to set up and talk to the NetBIOS (and as a 'result, the other computers). It wasn't meant to do anything useful, but 'if you run it on 2 computers when you hit the "S" key on one you'll see 'a string pop up on the other. I recommend single stepping if you want to 'see what is going on. 'The basic operation of the program is: ' 1. Establish a name and get its number in the local name table ' 2. Go into Receive (no wait) mode (listen for broadcast from others) ' 3. Wait on local user (You) to hit "S" or ESC (to quit) ' 4. Upon S-key, cancel Receive, issue Send Broadcast, reissue Receive ' 5. Upon any receive, print what was received, and reissue receive. 'OBTW - my name is Andy Coggins - I can be reached at 'rjohnso6@netcom.com. I am in the discovery phase w/ this stuff myself. 'But I could find NOTHING on this on ANY board/net/ftp/www so I decided 'to send this up because it worked as intended and maybe it will help 'somebody out. If you have anything that expands on this please let me 'know or post to the PowerBASIC site (support@powerpasic.com). '====================================================================== %FLAGS = 0 'constants for register names %AX = 1 %BX = 2 %CX = 3 %DX = 4 %SI = 5 %DI = 6 %BP = 7 %DS = 8 %ES = 9 'establish the Network Control block Type (64 bytes) TYPE NCBType cmd AS BYTE ' 1-command code rcode AS BYTE ' 1-return code lsn AS BYTE ' 1-Local Session Number locnum AS BYTE ' 1-my name # in local name table (1) iobuff AS DWORD ' 4-FAR Pointer to I/O Buffer bufflen AS WORD ' 2-length of data in buffer callname AS STRING * 16 ' 16-remote system to call locname AS STRING * 16 ' 16-network name of local machine rectimout AS BYTE ' 1-Recieve Time Out in 1/2 Seconds sentimout AS BYTE ' 1-send Timeout in 1/2 second inthandl AS DWORD ' 4-FAR Pointer to Post routine handler (0:0) cardnum AS BYTE ' 1-Network Adapter number to execute command cmd_ccode AS BYTE ' 1-Command Completion code dum AS STRING * 14 ' 14-reserved for use by Network Card Only END TYPE 'Initialize the NCB to all 0's (I assume they mean nulls in strings) 'You are supposed to clear the NCB each time you issue a new command '(or use a different NCB) 'this should happen upon creation of the variable, so just copy 'what was created and that is my init value DIM ncb AS SHARED NCBType 'working variable DIM ncbinit AS SHARED NCBType 'initiazation value netname$ = "DELL 486-50" 'a unique name to add to the name table MAP iobuff$$ * 512 'use 512 buffer for broadcast messages DIM buffaddr AS SHARED BYTE PTR 'pointer for string data buffaddr = STRPTR32(iobuff$$) '32bit address of data itself CLS 'add unique name to name table and get the number returned by 'NetBIOS. ncb = ncbinit 'clear the Network Control Block PRINT "Adding Name will take approx 10 seconds (why, I don't know)" CALL NCBAddName (ncb, netname$, xcode%) IF xcode% THEN hexxcode$ = RIGHT$("00"+HEX$(xcode%),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" GOTO End.NTTest ELSE PRINT "ADD Name was sucessful" PRINT "The Name ADDED was - ";ncb.locname PRINT "The Name NUMBER for this name is - ";ncb.locnum namenum? = ncb.locnum 'this is # to use for broadcast cmds END IF ncb = ncbinit 'must do to enter a command rxcnt% = 0: txcnt% = 0 'just some status counters 'set up receive mode on each computer CALL NCBRcvBroadcast (ncb, buffaddr, namenum?, xcode%) 'CALL PrintNCB(ncb) 'just for testing IF xcode% THEN hexxcode$ = RIGHT$("00"+HEX$(xcode%),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" CALL PrintNCB(ncb) 'just to see what changed in the NCB GOTO End.NTTest END IF 'error code on exit PRINT "Hit (S)end to send a broadcast, ESC to stop the program" 'This is the main program loop choice$ = "" WHILE choice$ <> CHR$(27) choice$ = INKEY$ choice$ = UCASE$(choice$) ' choice$ = "S" 'for single step testing IF choice$ = "S" THEN 'cancel receive command CALL NCBCancel (ncb, xcode%) ' CALL PrintNCB (ncb) IF xcode% THEN hexxcode$ = RIGHT$("00"+HEX$(xcode%),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" CALL PrintNCB(ncb) 'just to see what changed in the NCB GOTO End.NTTest END IF 'xcode% 'initiate send command INCR txcnt% ncb = ncbinit 'reinitialize ncb 'use a dummy dynamic string (so you will know its length) 'change the string each time so you can see some progress iobuff$ = STRING$(80, LTRIM$(STR$(txcnt% MOD 10))) CALL NCBSendBroadcast (ncb, buffaddr, namenum?, iobuff$, xcode%) ' CALL PrintNCB (ncb) IF xcode% THEN hexxcode$ = RIGHT$("00"+HEX$(xcode%),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" CALL PrintNCB(ncb) 'just to see what changed in the NCB GOTO End.NTTest ELSE PRINT "Broadcast packet number - ";txcnt% PRINT iobuff$ 'print what you sent 'reissue receive command ncb = ncbinit CALL NCBRcvBroadcast (ncb, buffaddr, namenum?, xcode%) IF xcode% THEN hexxcode$ = RIGHT$("00"+HEX$(xcode%),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" CALL PrintNCB(ncb) 'just to see what changed in the NCB GOTO End.NTTest END IF 'error code on exit END IF ELSE 'choice$ <> "s" 'check for received message in no wait mode 'print received message IF ncb.cmd_ccode = 255 THEN '&HFF until completed LOCATE 24,1 PRINT "Waiting on Send Broadcast from other computer - ";waitcnt&; INCR waitcnt& ELSEIF ncb.cmd_ccode = 0 THEN 'receive was successful INCR rxcnt% LOCATE 10,1 PRINT "Receive packet number - ";USING$("#######",rxcnt%);" From ";ncb.callname msg$ = LEFT$(iobuff$$, ncb.bufflen) PRINT msg$ msg$ = "" 'reissue receive command ncb = ncbinit CALL NCBRcvBroadcast (ncb, buffaddr, namenum?, xcode%) IF xcode% THEN hexxcode$ = RIGHT$("00"+HEX$(xcode%),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" CALL PrintNCB(ncb) 'just to see what changed in the NCB GOTO End.NTTest END IF 'error code on exit ELSE 'we had some netbios error - never happened in my test hexxcode$ = RIGHT$("00"+HEX$(ncb.cmd_ccode),2) PRINT "We have a NetBIOS error - AL = ";hexxcode$;" (hex) ";xcode%;" decimal" CALL PrintNCB(ncb) 'just to see what changed in the NCB GOTO End.NTTest END IF 'ncb.cmd_ccode END IF 'choice$ = "s" or not WEND End.Nttest: PRINT "The program is finished - I hope you enjoyed this as much as I did" END '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'Below are the Network generic routines. 'A routine to dump the Network Control Block to the Screen SUB PrintNcb (ncb AS NCBType) LOCAL PRINT "This is the value in each NCB field" PRINT "cmd = "; ncb.cmd PRINT "rcode = "; ncb.rcode PRINT "lsn = "; ncb.lsn PRINT "locnum = "; ncb.locnum PRINT "iobuff = "; ncb.iobuff PRINT "bufflen = "; ncb.bufflen PRINT "callname = "; ncb.callname PRINT "locname = "; ncb.locname PRINT "rectimout = "; ncb.rectimout PRINT "sentimout = "; ncb.sentimout PRINT "inthandl = "; ncb.inthandl PRINT "cardnum = "; ncb.cardnum PRINT "cmd_ccode = "; ncb.cmd_ccode PRINT "dum = "; ncb.dum IF ncb.bufflen <> 0 THEN DIM x AS BYTE PTR 'handle string 1 byte at a time x = ncb.iobuff 'transfer 32bit address of message buffer FOR count% = 1 TO ncb.bufflen 'read msg buffer 1 byte at a time inchar$ = CHR$(@x) '@z points to an ASCII byte msg$ = msg$ + inchar$ 'build a string for viewing in IDE INCR x ' increment the pointer itself NEXT count% PRINT "|" + msg$ + "|" END IF 'anything in the io buffer? END SUB 'PrintNcb ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'display the NCB Status stuff. I got junk for the most of these, but that 'could be because of OS2's virtualization (or it could be me taking it 'apart wrong) I only wanted to look at the name table and I got that part 'right. If you want to get the rest of the status fields correct you 'need better documentation than I have. If you correct it, feel free to 'just replace this post w/ the correct stuff. 'This routine is not called in this program. SUB DispNcbStatus (ncb AS NCBType, a$$) LOCAL 'a$$ is the global message buffer iobuff$$ 'take apart the message buffer returned by the &H33 command to NetBios 'ie. show me satus of things DIM y AS BYTE PTR 'a local byte pointer to walk down the string with y = ncb.iobuff 'same as STRPTR32(a$$) namerows% = (ncb.bufflen - 60)\18 DIM Locname$(1:namerows%, 1:3) 'table for local names 'there is probably an easier way to do this, but I don't need it,so 'I didn't spend time thinking about it. nodename$ = GimmeHex$(a$$, 1, 6) jumpstat$ = GimmeHex$(a$$, 7, 1) post$ = GimmeHex$(a$$, 8, 1) version$ = GimmeHex$(a$$, 9, 2) minrun$ = GimmeHex$(a$$, 11,2) crcerr$ = GimmeHex$(a$$, 13,2) alignerr$ = GimmeHex$(a$$, 15,2) coliderr$ = GimmeHex$(a$$, 17,2) aborterr$ = GimmeHex$(a$$, 19,2) txpacket$ = GimmeHex$(a$$, 21,4) rcpacket$ = GimmeHex$(a$$, 25,4) txretry$ = GimmeHex$(a$$, 29,2) rxnobuf$ = GimmeHex$(a$$, 31,2) dum1$ = GimmeHex$(a$$, 33,8) freencb$ = GimmeHex$(a$$, 41,2) lastresetncb$ = GimmeHex$(a$$, 43,2) maxresetncb$ = GimmeHex$(a$$, 45,2) dum2$ = GimmeHex$(a$$, 47,4) numsess$ = GimmeHex$(a$$, 51,2) lastresetsess$ = GimmeHex$(a$$, 53,2) maxresetsess$ = GimmeHex$(a$$, 55,2) maxpacketsize$ = GimmeHex$(a$$, 57,2) numnames$ = GimmeHex$(a$$, 59,2) 'below are the local names FOR i% = 0 TO namerows%-1 Locname$(i%+1, 1) = MID$(a$$, 61+(18*i%),16) 'local name Locname$(i%+1, 2) = LTRIM$(STR$(ASCII(MID$(a$$, 77+(18*i%),1)))) 'name # Locname$(i%+1, 3) = BinFmt$(ASCII(MID$(a$$, 78+(18*i%),1)),8) 'status NEXT i% PRINT "nodename$ = "; nodename$; " "; PRINT "jumpstat$ = "; BimFmt$(VAL("&H"+jumpstat$),8); " "; PRINT "post$ = "; VAL("&H"+post$); " "; PRINT "version$ = "; version$; " " PRINT "minrun$ = "; VAL("&H"+minrun$); " "; PRINT "crcerr$ = "; VAL("&H"+crcerr$); " "; PRINT "alignerr$ = "; VAL("&H"+alignerr$); " "; PRINT "coliderr$ = "; VAL("&H"+coliderr$); " "; PRINT "aborterr$ = "; VAL("&H"+aborterr$); " " PRINT "txpacket$ = "; VAL("&H"+txpacket$); " "; PRINT "rcpacket$ = "; VAL("&H"+rcpacket$); " "; PRINT "txretry$ = "; VAL("&H"+txretry$); " "; PRINT "rxnobuf$ = "; VAL("&H"+rxnobuf$); " " 'PRINT "dum1$ = "; dum1$ PRINT "freencb$ = "; VAL("&H"+freencb$); " "; PRINT "lastresetncb$ = "; VAL("&H"+lastresetncb$); " "; PRINT "maxresetncb$ = "; VAL("&H"+maxresetncb$); " " 'PRINT "dum2$ = "; dum2$ PRINT "numsess$ = "; VAL("&H"+numsess$); " "; PRINT "lastresetsess$ = "; VAL("&H"+lastresetsess$); " "; PRINT "maxresetsess$ = "; VAL("&H"+maxresetsess$); " "; PRINT "maxpacketsize$ = "; VAL("&H"+maxpacketsize$); " "; PRINT "numnames$ = "; VAL("&H"+numnames$); " " PRINT PRINT " LOCAL Name Table follows" 'below are the local names FOR i% = 0 TO namerows%-1 PRINT "Name = ";Locname$(i%+1, 1); " "; PRINT "NameNum = ";Locname$(i%+1, 2); " "; PRINT "Status = ";Locname$(i%+1, 3); " " NEXT i% END SUB 'DispNcbStatus ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'to look at the binary representation of a value (from PB3 help) FUNCTION BinFmt$(value&,size%) ' returns a binary string of size% digits BinFmt$ = RIGHT$("000000000000000"+BIN$(value&),size%) END FUNCTION 'BinFmt$ ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'not used in this program (used by the DispNcbStatus routine) FUNCTION GimmeHex$(a$$, start%, length%) 'since y is pointing to 1st byte already, we want to hand it a start% = 1 '(like MID$) but we don't want to add 1 to it. If we want it to start at 'position 7 we want to add 6 to it. DIM y AS BYTE PTR y = STRPTR32(a$$) INCR y, start%-1 FOR i% = 1 TO length% a$ = a$ + RIGHT$("00"+HEX$(@y),2) INCR y NEXT i% FUNCTION = a$ END FUNCTION 'GimmeHex$ ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'just to consolidate code the call and the associated return value 'are passed to and from this routine SUB CallNcb (ncb AS NCBType, rcode%) LOCAL ncbseg?? = VARSEG(ncb) 'find our typed var in memory 'ncbseghex$ = HEX$(ncbseg??) ncbofs?? = VARPTR(ncb) 'ncbofshex$ = HEX$(ncbofs??) REG %ES, ncbseg?? REG %BX, ncbofs?? CALL INTERRUPT &H5C AL?? = REG(%AX) AND &H00FF rcode% = AL?? 'AL = 0 means no error END SUB 'Call Ncb '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'not used in this routine SUB CheckAddapterStatus (ncb AS NCBType, buffaddr AS DWORD, rcode%) LOCAL ncb.cmd = &h033 'send a broadcast in wait mode ncb.cardnum = 0 'send message from 1st network card in machine ncb.callname = "*" 'use my local call name ncb.iobuff = buffaddr '32 bit address of the msg buffer ncb.bufflen = 512 'length of the message buffer CALL CallNcb (ncb, rcode%) END SUB ' CheckAddapterStatus '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'add a name to the local name table SUB NCBAddName(ncb AS NCBType, addname$, rcode%) LOCAL ncb.cmd = &H030 'add name command - wait mode ncb.cardnum = 0 'use 1st network card in machine ncb.locname = addname$ 'name to add CALL CallNcb (ncb, rcode%) END SUB 'NCBAddName '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'delete a name from the local name table SUB NCBDeleteName(ncb AS NCBType, delname$, rcode%) LOCAL ncb.cmd = &H031 'delete name command - wait mode ncb.cardnum = 0 'use 1st network card in machine ncb.locname = delname$ CALL CallNcb (ncb, rcode%) END SUB 'NCBDeleteName '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'set up to receive, use no wait mode so you can do other things 'like get operator input. (you can keep checking for 'ncb.cmd_ccode <> &HFF to see if you received anything) SUB NCBRcvBroadcast(ncb AS NCBType, buffaddr AS DWORD, namenum?, rcode%) LOCAL 'ncb.cmd = &H023 'receive command - wait mode ncb.cmd = &H0A3 'receive command - nowait mode ncb.cardnum = 0 'use 1st network card in machine ncb.locnum = namenum? 'my number in local name table ncb.iobuff = buffaddr '32 bit address of the msg buffer ncb.bufflen = 512 'default (max) length of the message buffer CALL CallNcb (ncb, rcode%) END SUB 'Receive Broadcast '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'send a broadcast to EVERYBODY that has any kind of receive broadcast 'issued (I'm sure this could screw up some complex programs) SUB NCBSendBroadcast(ncb AS NCBType, buffaddr AS DWORD, namenum?, iobuff$, rcode%) LOCAL SHARED iobuff$$ ncb.cmd = &H022 'send command - wait mode ncb.cardnum = 0 'use 1st network card in machine ncb.locnum = namenum? 'my position in local name table ncb.iobuff = buffaddr '32 bit address of the msg buffer iobuff$$ = iobuff$ ncb.bufflen = LEN(iobuff$) 'length of the message to send CALL CallNcb (ncb, rcode%) END SUB 'Send Broadcast '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'must cancel an unfinished command issued by a ncb before using 'THAT ncb for another command (you wouldn't believe how things bombed 'when I didn't cancel receive before I tried to send) 'I used wait mode (it was instantaneous) SUB NCBCancel(ncb AS NCBType, rcode%) LOCAL ncb.cmd = &H035 'cancel command for this ncb - wait mode ncb.cardnum = 0 'use 1st network card in machine CALL CallNcb (ncb, rcode%) 'I think OS/2 returns a 24h (36) (command competed before could cancel) to 'cancel a receive broadcast command, so I will treat this as no error. IF rcode% = 36 THEN rcode% = 0 END SUB 'NCBCancel ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''