At first NetBIOS was confined to the world of MS-DOS® and compatible operating systems. Later, implementations of NetBIOS transport providers were created for other operating systems, including the UNIX system. Applications written for NetBIOS providers can run on LANs that have a mixture of UNIX, DOS, OS/2 and other machines.
X/Open has defined a way to use
XTI to write UNIX system clients and
servers that access the services of any NetBIOS provider.
The NetBIOS providers available from
SCO are TCP/NetBIOS and
NetBEUI.
References in this chapter to
SCO TCP/NetBIOS
refer to the NetBIOS in
Release 1.2.1 or later of
SCO TCP/IP (also available as part of
SCO Open Server and
SCO Open Desktop Release 3.0 or later).
References in this chapter to NetBEUI
refer to Release 2.2 or later (available as part of
Microsoft® LAN
Manager for SCO Systems Release 2.2 or later.
NetBIOS names and addresses
The definition (in /usr/include/xti.h)
of a NetBIOS transport address is:
struct nb_addr {
char nb_type;
char nb_name[NB_NAMELEN];
};
The value of nb_type is either NB_UNIQUE
or NB_GROUP,
and the value of NB_NAMELEN is 16.
The constants NB_UNIQUE,
NB_GROUP, and NB_NAMELEN
(NetBIOS name length)
are defined in xti.h.
A NetBIOS name consists of 16 alphanumeric bytes. This address identifies a client or server process to the LAN. When specifying NetBIOS names:
This function never returns any user data to the caller.
Consequently, the value of call->udata.len
is always zero.
If you call this function for
a connectionless transport provider, the
function returns -1 and t_errno is set to
TNOTSUPPORT.
There are no special considerations when calling this function
over a NetBIOS transport provider.
In addition to specifying the name you wish to bind
in the nb_name field of the struct nb_addr
structure, you must set the nb_type field
to either NB_UNIQUE or NB_GROUP.
If the transport provider cannot bind the NetBIOS
name to the transport endpoint, the t_bind
returns -1 and t_errno is set to:
nb_type field),
the error TADDRBUSY is returned to that other process.
Only one process on the LAN can bind a name of type
NB_UNIQUE.
However, multiple processes can bind the same name to their
transport endpoints, provided
that each one sets the nb_type field to
NB_GROUP when calling t_bind.
Group names are useful for logically segmenting the
LAN. You can implement multicasting
(the sending of datagrams to some,
but not all of the hosts on a LAN) by specifying a
group name when you send a datagram.
There are no special considerations when calling this function over a NetBIOS transport provider.
If this function succeeds in establishing a connection,
the NetBIOS name that the transport provider returns
in rcvcall->addr.buf
will always be the same as the name specified in
sndcall->addr.buf.
You cannot send user data during session establishment.
Therefore,
sndcall->udata.len must be zero.
This field will be set correctly if you call t_alloc to
allocate sndcall.
Do not call this function over a connectionless transport endpoint. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
With some NetBIOS providers, (but not SCO TCP/NetBIOS or NetBEUI) it is possible for the NetBIOS name you bound to the transport endpoint to be removed by the transport provider as a result of a name conflict resolution protocol. If the name is removed between the call to t_bind and the call to t_connect, t_connect returns -1 and t_errno is set to TBADF. See the documentation supplied with transport provider software for more information.
There are no special considerations when calling this function over a NetBIOS transport provider.
There are no special considerations when calling this function over a NetBIOS transport provider.
These fields are part of the the t_info structure returned by t_getinfo:
addrsizeof(struct nb_addr).
optionsetsduconnectdisconflagsThere are no special considerations when calling this function over a NetBIOS transport provider.
You cannot send user data during connection establishment over
a NetBIOS transport provider. Consequently,
call->udata.len equals zero when the
function returns.
Do not call this function over a connectionless transport endpoint. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
With some NetBIOS providers, (but not SCO TCP/NetBIOS or NetBEUI) it is possible for the NetBIOS name you bound to the transport endpoint to be removed by the transport provider as a result of a name conflict resolution protocol. If the name is removed between the call to t_bind and the call to t_connect, t_connect returns -1 and t_errno is set to TBADF. See the documentation supplied with transport provider software for more information.
Because you cannot send expedited data over NetBIOS transports, t_look never returns the events T_EXDATA and T_GOEXDATA.
These fields are part of the the t_info structure returned by t_open:
addrsizeof(struct nb_addr).
optionsetsduconnectdisconflagsThere are no special considerations when calling this function over a NetBIOS transport provider.
Do not call this function over a connectionless transport endpoint. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
Because you cannot send expedited data over NetBIOS transports, the T_EXPEDITED flag in the flags parameter is never set.
You cannot send user data during connection establishment over
a NetBIOS transport provider. Consequently,
call->udata.len equals zero when the
function returns.
Do not call this function over a
connectionless transport endpoint.
If you do, the function returns -1, and t_errno
is set to TNOTSUPPORT.
The disconnect reason codes listed below are supported by all valid NetBIOS transport providers that conform to the XPG4 specification of XTI. Following each reason code is the hexadecimal value of the equivalent reason code in PC NetBIOS.
->reason field of the
struct t_discon parameter.
Do not call this function over either a connectionless transport endpoint or connection-oriented transport endpoint without orderly release. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
When a transport user sends an orderly release request by calling t_sndrel, the transport user that receives the request sees the T_DISCONNECT event, not the T_ORDREL event. Therefore, the transport user receiving the request calls t_rcvdis to consume the event, not t_rcvrel.
To complete the orderly release handshake,
the transport provider that receives the orderly release request
transparently sends a release response to the originating
transport endpoint. The originating transport user sees the
T_ORDREL event, and
consumes it by calling t_rcvrel.
Do not call this function over a connection-oriented transport endpoint. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
If you want to receive broadcast datagrams, you must bind the name NB_BCAST_NAME to the transport endpoint.
With some NetBIOS providers, (but not SCO TCP/NetBIOS or NetBEUI) it is possible for the NetBIOS name you bound to the transport endpoint to be removed by the transport provider as a result of a name conflict resolution protocol. If the name is removed between the call to t_bind and the call to t_connect, t_connect returns -1 and t_errno is set to TBADF. See the documentation supplied with transport provider software for more information.
Because there are no unit data error codes defined for XTI over NetBIOS, do not call this function. If you do, the function returns -1, and t_errno is set to TNOUDERR.
Do not set the T_EXPEDITED flag, because NetBIOS does not support the sending of expedited data. If you do, and you are using SCO TCP/NetBIOS, the function returns -1, and t_errno is set to TNOTSUPPORT. However, if you are using NetBEUI, the function returns zero and the flag is ignored.
Do not call this function over a connectionless transport endpoint. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
The following information applies if you are using SCO TCP/NetBIOS or NetBEUI. If, when you call t_snd:
Because NetBIOS transport providers do not support the sending of data with a disconnect request, set the call parameter to t_snddis to NULL.
Do not call this function over a
connectionless transport endpoint.
If you do, the function returns -1,
and t_errno is set to TNOTSUPPORT.
Do not call this function over either a connectionless transport endpoint or a connection-oriented transport endpoint without orderly release. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
Do not call this function over a connection-oriented transport endpoint. If you do, the function returns -1, and t_errno is set to TNOTSUPPORT.
If you want to send a broadcast datagram with
t_sndudata, you must specify the
name NB_BCAST_NAME. Assign this name
to the nb_name field of
the struct nb_addr structure pointed to by
unitdata->addr.buf.
With some NetBIOS providers, (but not
SCO TCP/NetBIOS or
NetBEUI) it is possible for
the NetBIOS name you bound to the transport endpoint
to be removed by the transport provider
as a result of a name conflict resolution protocol.
If the name is removed between the call to t_bind
and the call to t_connect, t_connect
returns -1 and t_errno is set to TBADF.
See the documentation supplied with transport provider
software for more information.
There are no special considerations when calling this function
over a NetBIOS transport provider.
This function disassociates the NetBIOS name from the
specified transport endpoint.
The NetBIOS transport provider will also delete this name
from the NetBIOS name table if no other transport
endpoint is currently bound to that name.
How PC NetBIOS is different from XTI over NetBIOS
Unsupported PC NetBIOS services
XTI over NetBIOS does not provide
user-level applications with interfaces for accessing the
following services that are available with
PC NetBIOS:
Although you cannot set send and receive timeouts for an
individual session,
your NetBIOS transport provider may allow
you to configure send
and receive timeouts for all sessions over that provider.
For information, see the documentation supplied with your
NetBIOS transport.
Using XTI functions instead of NCBs
Both XTI and PC NetBIOS
provide an interface that
user-level applications can use to access the services of a
NetBIOS transport provider.
XTI uses a set of C language function calls,
whereas PC NetBIOS
uses Network Control Blocks (NCBs) and the
DOS interrupt mechanism.
Because the interfaces are
not tied to the protocols that implement them, you can write
some parts of your application using one interface and other
parts using the other. For example, the client
portion of an application can be written for a DOS
machine using NCBs, while the server
portion can be written for a UNIX system using XTI.
As long as the same underlying transport (for example,
SCO TCP/NetBIOS or
NetBEUI) is used on both the client and the server,
the client and server will be able to communicate with each other.
Shutting down a session with orderly release
The NetBIOS HANGUP command provides an
orderly shutdown of a session. The XTI function
t_sndrel provides a similar mechanism over transport
providers that support connection-oriented service with orderly release.
If the transport provider does not support orderly release, you must call t_snddis to terminate a session. In this case, pending send and receive data may be lost. Set the call parameter to NULL, because NetBIOS providers do not support the sending of user data as part of a disconnect request.
Obtaining session and adapter status
You can use the ioctl system call to obtain
NetBIOS session status and adapter status (both local
and remote) over SCO TCP/NetBIOS
and NetBEUI.
The header file /usr/include/sconetbios.h
defines the constants and data structures you need.
The following program provides an example of how to
obtain session and adapter status over an
existing transport endpoint. The transport endpoint can
be either connection-oriented or connectionless.
Example 5-1 Example for obtaining session and adapter status
1 #include <stropts.h> 2 #include <xti.h> 3 #include <sconetbios.h>4 #define ASTAT_BUFSIZE 4096 5 #define SSTAT_BUFSIZE 4096
6 extern print_astatus(struct strioctl *); 7 extern print_sstatus(struct strioctl *);
8 demo_ioctl_user_function(int fd, char *name) 9 { 10 struct strioctl cmdbuf; 11 union { 12 struct astat adapter_status; 13 char name[NB_NAMELEN]; 14 } astat_buf; 15 struct sstat session_status;
16 cmdbuf.ic_cmd = NBIOCTL_ASTAT; 17 cmdbuf.ic_timout = 0; 18 cmdbuf.ic_dp = (char *)&astat_buf; 19 cmdbuf.ic_len = ASTAT_BUFSIZE;
20 memcpy(&astat_buf.name[0], name, NB_NAMELEN);
21 if (ioctl(fd, I_STR, &cmdbuf) < 0) { 22 perror("Error getting Adapter Status"); 23 exit(1); 24 } 25 print_astatus(&cmdbuf);
26 cmdbuf.ic_cmd = NBIOCTL_SSTAT; 27 cmdbuf.ic_timout = 0; 28 cmdbuf.ic_dp = (char *)&session_status; 29 cmdbuf.ic_len = SSTAT_BUFSIZE;
30 if (ioctl(fd, I_STR, &cmdbuf) < 0) { 31 perror("Error getting session status"); 32 exit(2); 33 } 34 print_sstatus(&cmdbuf); 35 }
This code has at least two interesting features:
->qlen that
is greater than one (five, in this example) in the call to
t_bind, allows the server to respond to incoming
connection requests in parallel.
Example 5-2 Example NetBIOS client
1 #include <stdio.h> 2 #include <fcntl.h> 3 #include <xti.h>4 #define SRV_NAME "NB-XTI-DEMO-SERV" 5 #define RFCNBPATH "/dev/nbcots" 6 #define NBEPATH "/dev/netbeui"
7 extern int t_errno;
8 main (int argc, char *argv[]) 9 { 10 int flags = 0; 11 char *netpath = RFCNBPATH; 12 int fd; 13 int nbytes; 14 struct t_call *sndcall; 15 struct nb_addr srv_addr; 16 struct { 17 unsigned short length; 18 char buf[1024]; 19 } msg;
20 if ((fd = t_open(netpath, O_RDWR, (struct t_info *) NULL)) < 0) { 21 t_error("t_open failed"); 22 exit(1); 23 }
24 if (t_bind(fd, (struct t_bind *) NULL, 25 (struct t_bind *) NULL) < 0) { 26 t_error("t_bind error"); 27 exit(2); 28 }
29 if ((sndcall = 30 (struct t_call *)t_alloc(fd, T_CALL, T_ADDR)) == NULL) { 31 t_error("t_alloc failed"); 32 exit(3); 33 }
34 sndcall->addr.len = sizeof(srv_addr); 35 sndcall->addr.buf = (char *)&srv_addr;
t_open(netpath, O_NONBLOCK | O_RDWR, (struct t_info *)NULL)
36 srv_addr.nb_type = NB_UNIQUE; 37 memcpy(srv_addr.nb_name, SRV_NAME, NB_NAMELEN);38 if (t_connect(fd, sndcall, (struct t_call *) NULL) < 0) { 39 t_error("t_connect failed for fd"); 40 exit(4); 41 }
42 while ((nbytes = t_rcv(fd, &msg, sizeof(msg), &flags)) >= 43 sizeof(msg.length)) { 44 if (fwrite(msg.buf, 1, msg.length, stdout) < 0) { 45 fprintf(stderr, "fwrite failed"); 46 exit(5); 47 } 48 if (msg.length == 0) 49 break; 50 }
51 if (nbytes == sizeof(msg.length) && msg.length == 0) { 52 if (t_snddis(fd, NULL) < 0) { 53 t_error("t_snddis failed"); 54 exit(6); 55 } 56 exit(0); 57 }
58 if (nbytes < sizeof(msg.length)) { 59 fprintf(stderr, "received invalid message, hanging up"); 60 t_snddis(fd, (struct t_call *) NULL); 61 exit(7); 62 } 63 else { 64 if (t_errno == TLOOK) { 65 if (t_look(fd) == T_DISCONNECT) { 66 printf("Got T_DISCONNECT, doing t_rcvdis"); 67 if (t_rcvdis(fd, (struct t_discon *) NULL) < 0) { 68 t_error("t_rcvdis failed"); 69 exit(8); 70 } 71 exit(0); 72 } 73 } 74 else { 75 t_error("t_rcv failed"); 76 exit(9); 77 } 78 } 79 }
Example 5-3 Example NetBIOS server
1 #include <stdio.h> 2 #include <signal.h> 3 #include <stropts.h> 4 #include <xti.h> 5 #include <fcntl.h>6 #define SRV_NAME "NB-XTI-DEMO-SERV" 7 #define RFCNBPATH "/dev/nbcots" 8 #define NBEPATH "/dev/netbeui" 9 #define DISCONNECT -1
10 extern int t_errno; 11 void run_server(int); 12 int conn_fd; 13 char *netpath = RFCNBPATH;
14 main(int argc, char *argv[]) 15 { 16 int listen_fd; 17 struct t_bind *bind; 18 struct t_call *call; 19 struct nb_addr srv_addr; 20 struct nb_addr client_addr;
21 if ((listen_fd = 22 t_open(netpath, O_RDWR, (struct t_info *) NULL)) < 0) { 23 t_error("t_open failed for listen_fd"); 24 exit(1); 25 }
26 if ((bind = (struct t_bind *)t_alloc(listen_fd, T_BIND, T_ADDR)) 27 == NULL) { 28 t_error("t_alloc of t_bind structure failed"); 29 exit(2); 30 }
28 bind->qlen = 5; 29 bind->addr.len = sizeof(srv_addr); 30 bind->addr.buf = (char *)&srv_addr;31 srv_addr.nb_type = NB_UNIQUE; 32 memcpy(srv_addr.nb_name, SRV_NAME, NB_NAMELEN);
33 if (t_bind(listen_fd, bind, bind) < 0) { 34 t_error("t_bind failed for listen_fd"); 35 exit(3); 36 }
37 if (memcmp(srv_addr.nb_name, SRV_NAME, NB_NAMELEN) != 0) { 38 fprintf(stderr, "t_bind bound wrong address\n"); 39 exit(4); 40 }
41 if ((call = (struct t_call *)t_alloc(listen_fd, T_CALL, T_ADDR)) 42 == NULL) { 43 t_error("t_alloc failed"); 44 exit(5); 45 } 46 call->addr.len = sizeof(srv_addr); 47 call->addr.buf = (char *)&client_addr;
48 while (1) { 49 if (t_listen(listen_fd, call) < 0) { 50 t_error("t_listen failed for listen_fd"); 51 exit(6); 52 } 53 if ((conn_fd = accept_call(listen_fd, call)) != DISCONNECT) 54 run_server(listen_fd); 55 } 56 }
addr.buf to the
NetBIOS name structure.
->qlen the
actual value of the connection request queue length it can support,
if that is less than the requested value.
->add.buf will point to the name of the
client trying to connect to the server. This value will be
filled in by the call to t_listen on line 49.
57 int
58 accept_call(int listen_fd, struct t_call *call)
59 {
60 int resfd;
61 if ((resfd = t_open(netpath, O_RDWR, (struct t_info *) NULL))
62 < 0) {
63 t_error("t_open for responding fd failed");
64 exit(7);
65 }
66 if (t_bind(resfd, (struct t_bind *) NULL,
67 (struct t_bind *) NULL) < 0) {
68 t_error("t_bind for responding fd failed");
69 exit(8);
70 }
71 if (t_accept(listen_fd, resfd, call) < 0) {
72 if (t_errno == TLOOK) {
73 if (t_rcvdis(listen_fd, (struct t_discon *) NULL) < 0) {
74 t_error("t_rcvdis failed for listen_fd");
75 exit(9);
76 }
77 if (t_close(resfd) < 0) {
78 t_error("t_close failed for responding fd");
79 exit(10);
80 }
81 return(DISCONNECT);
82 }
83 t_error("t_accept failed");
84 exit(11);
85 }
86 return(resfd);
87 }
88 connrelease()
89 {
90 exit(0);
91 }
An alternative is to call t_look after line 72 to determine precisely what asynchronous event has occurred. The server can then respond appropriately. See the manual page for t_look for a description of the asynchronous events returned by this function.
92 void
93 run_server(int listen_fd)
94 {
95 int nbytes;
96 FILE *logfp;
97 struct {
98 unsigned short length;
99 char buf[1024];
100 } msg;
101 switch (fork()) {
102 case -1:
103 perror("fork failed");
104 exit(12);
105 default: /* parent */
106 /* close conn_fd and go up to listen again */
107 if (t_close(conn_fd) < 0) {
108 t_error("t_close failed for conn_fd");
109 exit(13);
110 }
111 return;
112 case 0: /* child */
113 /* close listen_fd and do service */
114 if (t_close(listen_fd) < 0) {
115 t_error("t_close failed for listen_fd");
116 exit(14);
117 }
118 if ((logfp = fopen("logfile", "r")) == NULL) {
119 perror("cannot open logfile");
120 exit(15);
121 }
122 signal(SIGPOLL, connrelease);123 if (ioctl(conn_fd, I_SETSIG, S_INPUT) < 0) { 124 perror("ioctl I_SETSIG failed"); 125 exit(16); 126 }
127 if (t_look(conn_fd) != 0) { 128 fprintf(stderr, "t_look returned unexpected event"); 129 exit(17); 130 }
131 while ((nbytes = fread(msg.buf, 1, 1024, logfp)) > 0) { 132 msg.length = nbytes; 133 if (t_snd(conn_fd, &msg, nbytes + 134 sizeof(msg.length), 0) < 0) { 135 t_error("t_snd failed"); 136 exit(18); 137 } 138 }
139 msg.length = 0;
140 if (t_snd(conn_fd, &msg, 2, 0) < 0) { 141 t_error("can't send 0 bytes"); 142 exit(19); 143 }
144 pause(); /* until disconnect indication arrives */ 145 } 146 }
To get more information on the official X/Open specification of the use of XTI over NetBIOS providers, see Appendix D of the X/Open CAE Specification: X/Open Transport Interface (XTI), January 1992, ISBN 1-872630-29-4.