DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
Developing applications over NetBIOS using XTI

Example NetBIOS server code

1   /*
2    * Example NetBIOS server
3    */

4 #include <stdio.h> 5 #include <signal.h> 6 #include <stropts.h> 7 #include <xti.h> 8 #include <fcntl.h> 9 #include <sys/nb/nbtpi.h>

10 #define SRV_NAME "NB-XTI-DEMO-SERV" 11 #define RFCNBPATH "/dev/nbcots" 12 #define NBEPATH "/dev/netbeui" 13 #define DISCONNECT -1

14 extern int t_errno; 15 void run_server(int); 16 int conn_fd; 17 char *netpath = RFCNBPATH;

18 main(int argc, char *argv[]) 19 { 20 int listen_fd; 21 struct t_bind *bind; 22 struct t_call *call; 23 struct nbaddr srv_addr; 24 struct nbaddr client_addr;

25 /* ensure child processes do not become zombies */

26 signal(SIGCHLD, SIG_IGN);

27 if ((listen_fd = 28 t_open(netpath, O_RDWR, (struct t_info *) NULL)) < 0) { 29 t_error("t_open failed for listen_fd"); 30 exit(1); 31 }

32 if ((bind = (struct t_bind *)t_alloc(listen_fd, T_BIND, T_ADDR)) 33 == NULL) { 34 t_error("t_alloc of t_bind structure failed"); 35 exit(2); 36 }

37 bind->qlen = 5; 38 bind->addr.len = sizeof(srv_addr); 39 bind->addr.buf = (char *)&srv_addr;

40 srv_addr.nb_type = NB_UNIQUE; 41 memcpy(srv_addr.nb_name, SRV_NAME, NB_NAMELEN);

42 if (t_bind(listen_fd, bind, bind) < 0) { 43 t_error("t_bind failed for listen_fd"); 44 exit(3); 45 }

46 if (memcmp(srv_addr.nb_name, SRV_NAME, NB_NAMELEN) != 0) { 47 fprintf(stderr, "t_bind bound wrong address\n"); 48 exit(4); 49 }

50 if ((call = (struct t_call *)t_alloc(listen_fd, T_CALL, T_ADDR)) 51 == NULL) { 52 t_error("t_alloc failed"); 53 exit(5); 54 } 55 call->addr.len = sizeof(srv_addr); 56 call->addr.buf = (char *)&client_addr;

57 while (1) { 58 if (t_listen(listen_fd, call) < 0) { 59 t_error("t_listen failed for listen_fd"); 60 exit(6); 61 } 62 if ((conn_fd = accept_call(listen_fd, call)) != DISCONNECT) 63 run_server(listen_fd); 64 } 65 }

66 int 67 accept_call(int listen_fd, struct t_call *call) 68 { 69 int resfd;

70 if ((resfd = t_open(netpath, O_RDWR, (struct t_info *) NULL)) 71 < 0) { 72 t_error("t_open for responding fd failed"); 73 exit(7); 74 }

75 if (t_bind(resfd, (struct t_bind *) NULL, 76 (struct t_bind *) NULL) < 0) { 77 t_error("t_bind for responding fd failed"); 78 exit(8); 79 }

80 if (t_accept(listen_fd, resfd, call) < 0) { 81 if (t_errno == TLOOK) { 82 if (t_rcvdis(listen_fd, (struct t_discon *) NULL) < 0) { 83 t_error("t_rcvdis failed for listen_fd"); 84 exit(9); 85 } 86 if (t_close(resfd) < 0) { 87 t_error("t_close failed for responding fd"); 88 exit(10); 89 } 90 return(DISCONNECT); 91 } 92 t_error("t_accept failed"); 93 exit(11); 94 }

95 return(resfd); 96 }

97 connrelease() 98 { 99 exit(0); 100 }

101 void 102 run_server(int listen_fd) 103 { 104 int nbytes; 105 FILE *logfp;

106 struct { 107 unsigned short length; 108 char buf[1024]; 109 } msg;

110 switch (fork()) {

111 case -1: 112 perror("fork failed"); 113 exit(12);

114 default: /* parent */ 115 /* close conn_fd and go up to listen again */ 116 if (t_close(conn_fd) < 0) { 117 t_error("t_close failed for conn_fd"); 118 exit(13); 119 } 120 return;

121 case 0: /* child */ 122 /* close listen_fd and do service */ 123 if (t_close(listen_fd) < 0) { 124 t_error("t_close failed for listen_fd"); 125 exit(14); 126 } 127 if ((logfp = fopen("logfile", "r")) == NULL) { 128 perror("cannot open logfile"); 129 exit(15); 130 }

131 signal(SIGPOLL, connrelease);

132 if (ioctl(conn_fd, I_SETSIG, S_INPUT) < 0) { 133 perror("ioctl I_SETSIG failed"); 134 exit(16); 135 }

136 if (t_look(conn_fd) != 0) { 137 fprintf(stderr, "t_look returned unexpected event"); 138 exit(17); 139 }

140 while ((nbytes = fread(msg.buf, 1, 1024, logfp)) > 0) { 141 msg.length = nbytes; 142 if (t_snd(conn_fd, &msg, nbytes + 143 sizeof(msg.length), 0) < 0) { 144 t_error("t_snd failed"); 145 exit(18); 146 } 147 }

148 msg.length = 0;

149 if (t_snd(conn_fd, &msg, 2, 0) < 0) { 150 t_error("can't send 0 bytes"); 151 exit(19); 152 }

153 pause(); /* until disconnect indication arrives */ 154 } 155 }


line 16
declares the variable that will identify the transport endpoint to be used by the child process (to be spawned later) when it communicates with the client. The variable happens to be global in this example to avoid passing it around as a parameter. It is referenced both by main and run_server.

line 17
assigns the device name of the transport to be used. Use the value NBEPATH instead of RFCNBPATH to access NetBEUI.

line 20
declares the variable to be used by the parent process to identify the transport endpoint that the parent process will use to listen for incoming connection requests.

lines 27-28
open a connection-oriented full-duplex transport endpoint in non-blocking mode. The third argument is set to NULL in this example, indicating that the server does not care to examine any of the transport provider's attributes.

lines 32-33
allocate the struct t_bind data structure to be used in the call to t_bind.

line 37
sets the depth of the queue for incoming connection requests to five.

line 39
points addr.buf to the NetBIOS name structure.

lines 40-41
assign the server's well-known NetBIOS name. The name is termed ``well-known'' because all clients on the network are expected to know this name. The server expects to be the only one on the network with this name, so it assigns the name type a value of NB_UNIQUE.

line 42
binds the server's name and the length of the connection request queue to the transport endpoint. These are specified in the second argument (first occurrence of bind). The third argument points to the same bind data structure specified in the second argument. The transport provider will return in bind->qlen the actual value of the connection request queue length it can support, if that is less than the requested value.

lines 50-51
allocate the struct t_call data structure to be used in the call to t_listen.

lines 55-56
set up the call data structure. call->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 58.

lines 57-65
loop forever listening for connection requests on one transport endpoint, accepting them on different transport endpoint, then spawning a child process to communicate with the client.

lines 62-63
accept the connection with the client on the new transport endpoint, conn_fd. The server parent process will continue to listen for incoming connection requests on listen_fd, while the child process spawned in run_server will respond to the client. The functions accept_call and run_server are application-level functions, not XTI functions.

lines 66-96
open a new transport endpoint, let the transport bind any valid name to it, then accept the incoming connection request on that endpoint.

line 70
opens a new transport endpoint.

line 75
lets the transport select a NetBIOS name to bind to the endpoint.

line 80
accepts on resfd the connection request received earlier on listen_fd. By accepting the connection request on a file descriptor different from the one on which the connection request was received, the server can fork a child process to handle the client request, while the (parent) server continues to listen for new clients trying to connect on the original file descriptor.

lines 81-91
respond to an asynchronous event. The server assumes the client is now trying to disconnect. The server responds by consuming the disconnect event (line 82) and shutting down the transport endpoint on which it intended to accept the connection (line 86).

An alternative is to call t_look after line 81 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.


line 90
returns DISCONNECT to indicate that the connection request was aborted by the client.

line 95
returns the value of the transport endpoint on which the connection request has been accepted.

lines 97-100
declare the signal handler that is set in line 131. If an unexpected input message arrives at the transport endpoint, this handler will be executed. The handler simply exits, tearing down the transport endpoint the server is listening on. The exit also kills all of the child processes that are communicating with clients, thus tearing down their transport endpoints. Clients will receive the T_DISCONNECT asynchronous event at their local transport endpoint.

lines 106-109
declare the variable to hold the next chunk of data to be read from the log file and transmitted to a client.

line 110
spawns a child process to communicate with the client.

lines 114-120
close conn_fd. The parent process executes these lines because it will not be using this transport endpoint. The transport endpoint associated with conn_fd is not destroyed, however, because the child process will leave it open to communicate with the client. The parent returns to the calling function to continue to listen for connections on listen_fd.

lines 121-125
close listen_fd. The child process executes these lines because it will not be using this transport endpoint. The transport endpoint associated with listen_fd is not destroyed, however, because the parent process will leave it open to listen for the next incoming connection request from a client.

lines 127-130
open the log file for reading. The child process executes these lines.

lines 131-155
list the remaining instructions to be executed by the child process.

line 131
sets connrelease as the SIGPOLL signal handler.

line 132
establishes that a SIGPOLL signal will be generated if an unexpected message arrives at the transport endpoint denoted by conn_fd.

lines 140-147
read from the log file in 1K chunks and send both the data and the number of bytes read to the client.

lines 148-152
signal the end of the file by sending a message with the length field set to zero.

line 153
waits until the SIGPOLL signal is generated. This will occur when the client receives the message with the length field set to zero, because the client will send a disconnect indication to the server by calling t_rcvdis. The signal handler connrelease will then be called, which calls exit to terminate the process.

© 2004 The SCO Group, Inc. All rights reserved.
UnixWare 7 Release 7.1.4 - 27 April 2004