/* Version: V1.0 */ /* Name: sx.c */ /* Location: sys$examples: */ /**************************************************************************/ /* */ /* XTI OSI Transport Class 4 Server Example */ /* */ /* This example server is intended to be used with sample client cx.c. */ /* The server waits for connect requests, and when one arrives it forks a */ /* copy of itself to deal with the client that made the request. It then */ /* waits for messages to arrive, and simply echoes them back to the */ /* client. */ /* */ /*Notes: */ /* */ /* This example suite was ported from OSF. The VMS compile does NOT */ /* support forking. The addressing datastructes and options specific to */ /* VMS can be found in the file vms_osi.h. Example addressing routines */ /* similar to OSF are in example file xtiutil.c. */ /* */ /**************************************************************************/ #include #include #include #include #ifdef vms #include "vms_osi.h" #else #include #include #include #include #endif extern int xti_osimakeaddr(); /**************************************************************************/ /* Figure out where to get a connection oriented service. */ /**************************************************************************/ #ifdef ultrix #define COTS "cots" #elif __osf__ #define COTS "/dev/streams/xtiso/cots" #endif /* *** The macro PNM (PTY_OSI) is found in vms_osi.h, it is use in t_open *** to access a specific provider. */ #ifdef vms #define COTS PNM( PTY_OSI ) /* from vms_osi.h */ #endif /**************************************************************************/ /* Local #defines */ /**************************************************************************/ #define RCVTSAP "recvtsap" /* *** The VMS version of OSIADDRLEN macro is found in vms_osi.h */ #ifndef vms #define OSIADDRLEN(a) ((a)->osi_length + sizeof(struct sockaddr_osi)) #endif /**************************************************************************/ /* Forward function declarations. */ /**************************************************************************/ struct t_call *sx_create_call_struct(); struct sockaddr_osi *subx_alloc_sosi(); /**************************************************************************/ /* Global variables. */ /**************************************************************************/ char sxid[32]; /* String to identify process */ /**************************************************************************/ /* Main */ /**************************************************************************/ main(argc,argv) int argc; char *argv[]; { int status; int xfd; int xfd0; int conn_count; int conn_copy_size; struct t_info t_open_info; struct t_call *call; struct t_call *conn_queue[SOMAXCONN]; strcpy(sxid,"Server"); /* Initialize the pending connect queue */ conn_count = 0; conn_copy_size = sizeof(conn_queue) - sizeof(sizeof(conn_queue[0])); memset (conn_queue,0,sizeof(conn_queue)); /* Create the servers listening endpoint */ xfd0 = sx_create_listen_fd(&t_open_info); /* Listen for & handle connect requests forever ... */ while (1) { /* Listen for an incoming connect */ call = sx_create_call_struct(&t_open_info); status = t_listen(xfd0,call); if ((status < 0) && (t_errno == TLOOK)) { /* An unaccepted client call has already been disconnected. We */ /* just need to do a t_rcvdis to get the listening socket back */ /* into the right state. */ sx_free_call (call); subx_tlook (xfd0,sxid,T_DISCONNECT,-1); t_rcvdis (xfd0,NULL); continue; } else if (status < 0) { /* Fatal error! */ sx_error("t_listen",NULL); exit(1); } else { /* Success */ sx_print_call(call); } /* Put the call on the list of pending connects. We have to */ /* maintain a list of pending calls because the attempt to do */ /* a t_accept on the call may fail with an error of TLOOK, */ /* requiring us to go back to do another t_listen before we */ /* can retry the t_accept. */ conn_queue[conn_count++] = call; /* Now process as many pending connects as we can before we */ /* have to go back and do another t_listen. */ while (conn_count > 0) { /* Try the first call in the queue */ call = conn_queue[conn_count-1]; xfd = sx_accept_call(xfd0,call); if (xfd == -1) { /* t_accept failed - try again later */ break; } /* If here we have processed the call (either succesfully */ /* or unsuccesfully, but either way, it's done). Remove */ /* the call from the queue */ memcpy(conn_queue,&conn_queue[SOMAXCONN-1],conn_copy_size); conn_queue[SOMAXCONN-1] = NULL; conn_count--; /* If the call could not be successfully accepted, we're */ /* done with it. */ if (xfd == -2) continue; /* If here, we really accepted a connection. Fork a copy */ /* of ourselves to handle it. */ /* *** UNIX forking is not supported under VMS XTI */ #ifdef vms /* fall into child logic */ status = 0; #else status = fork(); #endif if (status < 0) { /* Fork call failed */ fprintf (stderr,"%s: fork call failed, errno=%d\n",sxid,errno); exit(1); } else if (status == 0) { /* CHILD */ sprintf(sxid," (%d)",getpid()); fprintf (stderr,"%s: Created\n",sxid); sx_rcvdata(xfd); } else if (status > 0) { /* PARENT */ fprintf (stderr,"%s: Forked server process %d\n",sxid,status); } } } } /**************************************************************************/ /* */ /* Create the listening transport endpoint. */ /* */ /* NOTE: Addressing is XTI implementation dependent. As such, */ /* our XTI address is represented by sockaddr_osi structure. */ /* Note that this structure is variable length, with TSAP */ /* and NSAP dynamically constructed at the end of the */ /* structure. */ /* NOTE 1: */ /* For VMS addressing please see xti.h and */ /* sys$examples: vms_osi.h,xtiutil.c */ /* */ /**************************************************************************/ int sx_create_listen_fd (t_open_info) struct t_info *t_open_info; { struct t_bind req; struct t_bind ret; int rfd0; int status; struct sockaddr_osi *rcvsap; fprintf (stderr,"%s: Creating listening transport endpoint\n",sxid); /* Create a transport endpoint */ rfd0 = t_open(COTS, O_RDWR, t_open_info); if (rfd0 < 0) { sx_error("t_open",NULL); exit(1); } /* Initialize Server's sap */ rcvsap = subx_alloc_sosi(t_open_info->addr); (void) xti_osimakeaddr(rcvsap, OSIPROTO_COTS, /* Connection oriented transport */ strlen(RCVTSAP), /* Our service access point */ (unsigned char *) RCVTSAP, /* ditto */ 0, /* We don't care about network */ NULL, /* ditto */ NULL); /* ditto */ /* Bind the TSAP to a transport endpoint. */ req.addr.len = OSIADDRLEN(rcvsap); req.addr.buf = (char *)rcvsap; req.qlen = 5; /* Up to 5 pending connects allowed */ ret.addr.maxlen = t_open_info->addr; ret.addr.buf = (char *)rcvsap; status = t_bind(rfd0, &req, &ret); if (status < 0) { sx_error("t_bind",NULL); exit(1); } /* Set listener's options to Transport Provider. */ sx_negotiate_xtiopts(rfd0); fprintf(stderr,"%s: Listening transport endpoint ready\n",sxid); return(rfd0); } /**************************************************************************/ /* Create & set up a call structure, which will be used in the t_listen */ /* and t_accept calls to acquire incomming connects. */ /**************************************************************************/ struct t_call * sx_create_call_struct (ip) struct t_info *ip; { struct t_call *call; call = (struct t_call *)malloc(sizeof(struct t_call)); memset(call, 0, sizeof(struct t_call)); call->addr.maxlen = ip->addr; call->addr.buf = (char *)malloc(ip->addr); call->opt.maxlen = ip->options; call->opt.buf = (char *)malloc(ip->options); call->udata.maxlen = ip->connect; call->udata.buf = (char *)malloc(ip->connect); return(call); } /**************************************************************************/ /* Accept an incoming connect request. */ /**************************************************************************/ sx_accept_call (rfd0,call) int rfd0; struct t_call *call; { int rfd; int status; struct t_bind req; struct t_bind ret; struct t_info t_open_info; struct sockaddr_osi *rcvsap; fprintf(stderr,"%s: Attempting to accept call (sequence # = %d)\n", sxid,call->sequence); /* Get a new, bound transport endpoint to accept connection. */ rfd = t_open(COTS, O_RDWR, &t_open_info); if (rfd < 0) { sx_error ("t_open",NULL); sx_free_call (call); return (-2); } /* Set up an address to bind to the new transport endpoint */ rcvsap = subx_alloc_sosi(t_open_info.addr); (void) xti_osimakeaddr(rcvsap, OSIPROTO_COTS, /* Connection oriented transport */ strlen(RCVTSAP), /* Our service access point */ (unsigned char *) RCVTSAP, /* ditto */ 0, /* We don't care about network */ NULL, /* ditto */ NULL); /* ditto */ req.addr.len = OSIADDRLEN(rcvsap); req.addr.buf = (char *)rcvsap; req.qlen = 0; ret.addr.maxlen = t_open_info.addr; ret.addr.buf = (char *)rcvsap; status = t_bind(rfd, &req, &ret); if (status < 0) { sx_error ("t_bind",NULL); sx_free_call (call); return (-2); } /* As we are accepting the call on a different endpoint than */ /* the listening one, establish options for the new endpoint */ /* with the transport provider. */ sx_negotiate_xtiopts(rfd); /* If Client has sent optional connect data, echo it back */ if (call->udata.len > 0) { /* Don't need to do anything - udata.len & udata.buf already */ /* have the proper length & data. */ } /* No protocol specific options to worry about */ call->opt.len = 0; call->opt.buf = 0; /* Accept the connection. Note that if we get a TLOOK indication we */ /* have to do the t_look on the listening fd, not the newly created */ /* accepting fd. */ status = t_accept (rfd0, rfd, call); if ((status < 0) && (status == TLOOK)) { subx_tlook (rfd0,sxid,T_DISCONNECT,T_LISTEN); } else if (status < 0) { sx_error("t_accept",NULL); return(-2); } fprintf(stderr,"%s: Call %d accepted\n",sxid,call->sequence); return(rfd); } /**************************************************************************/ /* Loop, listening for messages and echoing them back to client. */ /**************************************************************************/ sx_rcvdata (rfd) int rfd; { int cc; int status; int emax; int emore; int etotal; int t_rcv_flags = 0; char *ebuf; char *eptr; int nmax; int nmore; int ntotal; char *nbuf; char *nptr; char rcvbuf[2048]; struct t_info info; /* Get information about this transport endpoint */ status = t_getinfo(rfd,&info); if (status == -1) { sx_error("t_getinfo"); exit(1); } /* Get receive buffers for normal & expedited data */ if (info.tsdu == -1) { nmax = 2048; nbuf = (char *)malloc(nmax); } else if (info.tsdu == -2) { nmax = 0; nbuf = NULL; } else { nmax = info.tsdu; nbuf = (char *)malloc(nmax); } if (info.etsdu == -1) { emax = 2048; ebuf = (char *)malloc(emax); } else if (info.etsdu == -2) { emax = 0; ebuf = NULL; } else { emax = info.etsdu; ebuf = (char *)malloc(emax); } /* We loop here for messages from the client until the client */ /* disconnects from us. */ emore = 0; etotal = 0; eptr = ebuf; nmore = 0; ntotal = 0; nptr = nbuf; while (1) { /* Wait for some data to arrive */ cc = t_rcv(rfd, rcvbuf, sizeof(rcvbuf), &t_rcv_flags); /* If an error occured, we print a message and exit. If we */ /* have an event pending (TLOOK) we handle it and then we */ /* exit (since the only event that can occur is a disconnect, */ /* there's no point in hanging around). */ if ((cc <= 0) && (t_errno == TLOOK)) { fprintf (stderr,"%s: TLOOK received during t_rcv()\n",sxid); subx_tlook(rfd,sxid,T_DISCONNECT,1); sx_handle_disconnect(rfd); exit(1); } else if (cc < 0) { sx_error("t_rcv"); exit(1); } /* Put the data we've read into the correct buffer & output a */ /* message saying what we've received. Note that an expedited */ /* data pdu can be receieved in the middle of reading a normal */ /* pdu that has been split into several segments. */ if (t_rcv_flags & T_EXPEDITED) { etotal += cc; emore = (t_rcv_flags & T_MORE); memcpy(eptr,rcvbuf,cc); eptr += cc; sx_rmsg(stderr,cc,etotal,t_rcv_flags); } else { ntotal += cc; nmore = (t_rcv_flags & T_MORE); memcpy(nptr,rcvbuf,cc); nptr += cc; sx_rmsg(stderr,cc,ntotal,t_rcv_flags); } /* If we've completed receiving an expedited tpdu, echo */ /* it back to the client & reset variables. */ if (etotal && !emore) { ebuf[etotal] = '\0'; fprintf(stderr,"%s: expedited data = \"%s\"\n",sxid,ebuf); sx_tsnd(rfd,ebuf,etotal,T_EXPEDITED); etotal = 0; emore = 0; eptr = ebuf; } /* If we've completed receiving a normal tpdu, echo */ /* it back to the client & reset variables. */ if (ntotal && !nmore) { nbuf[ntotal] = '\0'; fprintf(stderr,"%s: normal data = \"%s\"\n",sxid,nbuf); sx_tsnd(rfd,nbuf,ntotal,0); ntotal = 0; nmore = 0; nptr = nbuf; } } } /**************************************************************************/ /* Print a message about what's being received. */ /**************************************************************************/ sx_rmsg (fp,cc,total,flags) FILE *fp; int cc; int total; int flags; { char *type; if (flags & T_EXPEDITED) { type = "Expedited"; } else { type = "Normal"; } if ((flags & T_MORE) || (cc != total)) { fprintf (fp,"%s: %s data segment received (%d bytes)\n",sxid,type,cc); } else { fprintf (fp,"%s: %s data packet received (%d bytes)\n",sxid,type,cc); } if (!(flags & T_MORE) && (cc != total)) { fprintf (fp,"%s: %s data packet complete (%d bytes total)\n",sxid,type,cc); } } /**************************************************************************/ /* Try to echo the recieved data back to the client program. */ /**************************************************************************/ sx_tsnd (fd,ptr,total,flags) int fd; char *ptr; int total; int flags; { int status; char *type; if (flags & T_EXPEDITED) { type = "expedited"; } else { type = "normal"; } fprintf (stderr,"%s: Trying to send %d bytes of %s data\n",sxid,total,type); status = t_snd(fd,ptr,total,flags); if ((status < 0 ) && (t_errno == TLOOK)) { fprintf (stderr,"TLOOK received\n"); subx_tlook(fd,sxid,T_DISCONNECT,-1); sx_handle_disconnect(fd); exit(1); } else if (status < 0) { sx_error("t_snd",NULL); exit(1); } else if (status == total) { fprintf (stderr,"%s: Write complete\n",sxid); } else { fprintf (stderr,"%s: Short write! (%d)\n",sxid,status); } } /**************************************************************************/ /* Process a disconnect from the client. */ /**************************************************************************/ sx_handle_disconnect (rfd) int rfd; { int status; struct t_discon discon; memset(&discon, 0, sizeof(discon)); discon.udata.maxlen = 100; discon.udata.buf = (char *)malloc(100); status = t_rcvdis(rfd, &discon); if ((status < 0) && (t_errno == TNODIS)) { fprintf (stderr,"%s: No disconnect data received\n",sxid); } else if (status < 0) { sx_error("t_rcvdis",NULL); exit(1); } else { fprintf (stderr,"%s: Disconnect data = \"%s\"\n",sxid,discon.udata.buf); } } /**************************************************************************/ /* Negotiate options with transport provider. */ /**************************************************************************/ sx_negotiate_xtiopts (fd) int fd; { int status; struct t_optmgmt t_optm_req; struct t_optmgmt t_optm_ret; struct isoco_options options; /* Get default options */ t_optm_req.opt.len = 0; t_optm_req.flags = T_DEFAULT; t_optm_ret.opt.maxlen = sizeof(struct isoco_options); t_optm_ret.opt.buf = (char *)(&options); status = t_optmgmt(fd, &t_optm_req, &t_optm_ret); if (status < 0) { sx_error("t_optmgmt","T_DEFAULT"); exit(1); } /* Now that we've got the default options, change those parameters */ /* we care about to have the values we want. */ #ifndef vms options.mngmt.dflt = T_NO; /* Don't ignore following params */ options.mngmt.class = T_CLASS4; /* Preferred class is class 4 */ options.mngmt.checksum = T_YES; /* We want checksums used */ options.mngmt.ltpdu = 2048; /* Max TPDU length */ options.expd = T_YES; /* We want expedited data support */ #else /* The routine vms_set_options is used to map the implementaion */ /* specific options functions. Under VMS we use the parameters as */ /* the values to supported options. The options that can be negotiated */ /* under VMS are class, exppedited data and checksum. The options */ /* extended format and flow control are read only. */ /* */ /* 1) class 2) expedited data 3) checksum 4) extended 5) flow ctrl */ /* */ vms_set_options( &options, T_CLASS4, T_YES, T_YES, 0, 0 ); #endif t_optm_ret.opt.maxlen = sizeof(options); t_optm_ret.opt.buf = (char *)(&options); t_optm_req.opt.len = sizeof(options); t_optm_req.opt.buf = (char *)(&options); t_optm_req.flags = T_NEGOTIATE; status = t_optmgmt(fd, &t_optm_req, &t_optm_ret); if (status < 0) { sx_error("t_optmgmt","T_NEGOTIATE"); exit(1); } } /**************************************************************************/ /* Print info about a call we've received. */ /**************************************************************************/ sx_print_call (call) struct t_call *call; { fprintf(stderr,"%s: CALL RECEIVED\n",sxid); fprintf(stderr,"%s: sequence # = %d\n",sxid,call->sequence); fprintf(stderr,"%s: address length = %d\n",sxid,call->addr.len); fprintf(stderr,"%s: option data length = %d\n",sxid,call->opt.len); fprintf(stderr,"%s: user data length = %d\n",sxid,call->udata.len); fprintf(stderr,"%s: user data = ",sxid); if (call->udata.len) { fprintf(stderr,"\"%s\"\n",call->udata.buf); } else { fprintf(stderr,"\"\"\n"); } } /**************************************************************************/ /**************************************************************************/ sx_free_call (call) struct t_call *call; { if (call->addr.buf) free (call->addr.buf); if (call->opt.buf) free (call->opt.buf); if (call->udata.buf) free (call->udata.buf); free (call); } /**************************************************************************/ /* Call t_error, with appropriate pre & post processing. */ /**************************************************************************/ sx_error (s1,s2) char *s1; char *s2; { char buff[128]; if (s2 == NULL) { sprintf(buff,"\n%s: ERROR: %s",sxid,s1); } else { sprintf(buff,"\n%s: ERROR: %s (%s)",sxid,s1,s2); } t_error(buff); }