--- /dev/null
+/*
+ * This file contains routines useful to the applications developer who
+ * must read or write BDT data.
+ */
+
+/*
+ $Log: bdt.c,v $
+ * Revision 3.0 87/01/14 14:39:02 ed
+ * release containing Xerox (Webster Research Center) modifications
+ *
+ * Revision 2.1 86/09/07 09:36:29 jqj
+ * return correct number of bytes written by BDTwrite, or -1 on error.
+ *
+ * Revision 2.0 85/11/21 07:22:02 jqj
+ * 4.3BSD standard release
+ *
+ * Revision 1.4 85/03/11 16:36:38 jqj
+ * *** empty log message ***
+ *
+ * Revision 1.4 85/03/11 16:36:38 jqj
+ * Public alpha-test version, released 11 March 1985
+ *
+ * Revision 1.3 85/03/11 16:34:19 jqj
+ * Public alpha-test version, released 11 March 1985
+ *
+ * Revision 1.2 85/01/27 07:37:06 jqj
+ * finished but undebugged version
+ *
+ */
+
+#ifndef lint
+static char rcsid[] = "$Header: bdt.c,v 3.0 87/01/14 14:39:02 ed Exp $";
+#endif
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h> /* for socket.h and xn.h */
+#include <sys/socket.h>
+#include <sys/uio.h> /* for scatter/gather io */
+#include <netns/ns.h> /* for XNS addresses and courierconnection.h */
+#include <netns/idp.h>
+#include <netns/sp.h> /* for spphdr */
+#include "courier.h"
+#include "realcourierconnection.h"
+
+#define MAKEVEC(idx, addr, len) our_iovec[idx].iov_base = (caddr_t)addr;\
+ our_iovec[idx].iov_len = len;
+
+\f
+
+int
+BDTwrite(f,buffer,nbytes)
+/* Call with CourierConnection*, not *(CourierConnection*) */
+/* Semantics are much like write(), except that it returns -1
+ * if a BDT abort message arrives from receiver.
+ * Returns # of bytes actually written, or -1 if an error occurs (some
+ * data may have been transferred!).
+ */
+ register CourierConnection *f;
+ char *buffer;
+ int nbytes;
+{
+ register int n, w;
+ struct iovec our_iovec[2];
+
+ MAKEVEC(0, &(f->sphdrOpts), sizeof(f->sphdrOpts));
+ MAKEVEC(1, buffer, SPPMAXDATA);
+
+ if (f->bdtstate == wantdata) {
+ /* stream=BDT, EOM=FALSE, Attn=FALSE */
+ f->sphdrOpts.sp_dt = SPPSST_BDT;
+ f->sphdrOpts.sp_cc &= ~SP_EM;
+ f->bdtstate = established;
+ }
+ if (BDTabortSeen(f)) {
+ BDTabort(f); /* send end (abort) */
+ f->abortseen = FALSE; /* clear abort */
+ f->bdtstate = bdteomseen;
+ return(-1); /* truncate the stream */
+ }
+ /* ### if nbytes > SPPMAXDATA, do something intelligent? */
+ for(n = nbytes; n > SPPMAXDATA; n -= SPPMAXDATA) {
+ w = writev(f->fd, our_iovec, 2) - sizeof(f->sphdrOpts);
+ if (w < 0) return(-1);
+ if(w < SPPMAXDATA)
+ return( w + nbytes - n);
+ our_iovec[1].iov_base += SPPMAXDATA;
+ }
+ our_iovec[1].iov_len = n;
+ w = writev(f->fd, our_iovec, 2) - sizeof(f->sphdrOpts);
+ if (w < 0) return(-1);
+ return( w + nbytes - n);
+}
+
+
+int
+BDTclosewrite(f)
+/* call with CourierConnection*, not *(CourierConnection*) */
+/* End a BDT connection. Returns 0 on success, -1 on failure.
+ */
+ register CourierConnection *f;
+{
+
+ f->bdtstate = bdteomseen;
+ if (BDTabortSeen(f)) {
+ BDTabort(f);
+ f->abortseen = FALSE;
+ return(-1);
+ }
+ /* stream=BDT, EOM=TRUE, Attn=FALSE */
+ f->sphdrOpts.sp_dt = SPPSST_BDT;
+ f->sphdrOpts.sp_cc |= SP_EM;
+ /* finally, send normal end in a packet of its own */
+ write(f->fd,(char*)&f->sphdrOpts,sizeof(struct sphdr));
+ return(0);
+}
+
+
+int
+BDTread(f, buffer, nbytes)
+/* Call with CourierConnection*, not *(CourierConnection*) */
+/* Semantics are much like read(), except that it returns -1 on
+ * more conditions. Returns number of characters actually read,
+ * or 0 on end of message.
+ */
+ register CourierConnection *f;
+ char *buffer;
+ int nbytes;
+{
+ register int count;
+ struct {
+ struct sphdr hdr;
+ char data[SPPMAXDATA];
+ } packbuf;
+ struct iovec our_iovec[2];
+
+ switch (f->state) {
+ case closed:
+ case calldone:
+ fprintf(stderr,"BDTread() called while connection state = %s\n",
+ (f->state == closed) ? "closed" : "calldone");
+ exit(1);
+ /* NOTREACHED */
+ case wantversion:
+ count = recv(f->fd, (char*) &packbuf, sizeof(packbuf), MSG_PEEK)
+ - sizeof(struct sphdr);
+ while (count == 0
+ && packbuf.hdr.sp_dt == SPPSST_RPC) {
+ read(f->fd, (char*) &packbuf, sizeof(packbuf));
+ count = recv(f->fd, (char*) &packbuf, sizeof(packbuf),
+ MSG_PEEK)
+ - sizeof(struct sphdr);
+ }
+ if (count == 0)
+ /* streamtype != SPPSST_RPC, so we can't */
+ /* have a version number */
+ break;
+ /* fall out of switch, still wantversion */
+ /* ### N.B. we don't handle count==2 */
+ else if (count != (2*sizeof(Cardinal)))
+ /* must be a REJECT or ABORT message */
+ /* let someone else handle it! */
+ return(-1);
+ else {
+ /* must be a Courier version number */
+ /* read it and throw it away */
+ read(f->fd, (char*) &packbuf, sizeof(packbuf));
+ f->state = inprogress;
+ /* fall into case inprogress */
+ }
+ case inprogress:
+ switch (f->bdtstate) {
+ case wantdata:
+ count = recv(f->fd, (char*) &packbuf, sizeof(packbuf),
+ MSG_PEEK)
+ - sizeof(struct sphdr);
+ if (packbuf.hdr.sp_dt == SPPSST_RPC)
+ return(-1);
+ f->bdtstate = established;
+ /* fall through to case established */
+ case established:
+ break;
+ /* fall out of inner (and outer!) switch */
+ case bdteomseen:
+ return(0);
+ }
+ break;
+ }
+ MAKEVEC(0,&packbuf.hdr,sizeof(struct sphdr));
+ MAKEVEC(1,buffer,nbytes);
+ count = readv(f->fd,our_iovec,2) - sizeof(struct sphdr);
+ /* at this point, we've read a packet that isn't SPPSST_RPC */
+ while (TRUE) {
+ if (packbuf.hdr.sp_dt == SPPSST_END) {
+ (void) sppclosereply(f->fd);
+ f->state = closed;
+ fprintf(stderr,"SPP END received during BDT\n");
+ exit(1);
+ }
+ if (packbuf.hdr.sp_dt != SPPSST_BDT) {
+ fprintf(stderr,
+ "wrong stream type, %d, seen during BDT\n",
+ packbuf.hdr.sp_dt);
+ exit(1);
+ /* NOTREACHED */
+ }
+ if (f->abortseen || (packbuf.hdr.sp_cc & SP_OB)) {
+ f->abortseen = TRUE;
+ return(-1);
+ }
+ if (packbuf.hdr.sp_cc & SP_EM) {
+ f->bdtstate = bdteomseen;
+ /* next read will return 0 */
+ return(count);
+ }
+ if (count > 0)
+ return(count);
+ count = readv(f->fd,our_iovec,2) - sizeof(struct sphdr);
+ }
+}
+
+BDTabort(f)
+ register CourierConnection *f;
+{
+ static struct handy {
+ struct sphdr hdr;
+ char value;
+ } data;
+ /* stream=BDT, EOM=FALSE, Attn=TRUE */
+ data.hdr.sp_dt = SPPSST_BDT;
+ data.hdr.sp_cc = SP_EM;
+ data.value = 1; /* BDT abort data value */
+ send(f->fd, &data, sizeof(data), MSG_OOB);
+ f->bdtstate = bdteomseen;
+ f->abortseen = TRUE;
+}
+
+
+BDTabortSeen(f)
+ register CourierConnection *f;
+{
+ struct {
+ struct sphdr hdr;
+ Unspecified data[MAXWORDS];
+ } packbuf;
+ int fdmask;
+ register int count;
+ static struct timeval timeout = {0,0};
+
+ fdmask = 1<<(f->fd);
+ /* ### code here for OOB signalling! */
+ while (select(f->fd+1,&fdmask,(int*)NULL,(int*)NULL,&timeout) > 0
+ && (count = recv(f->fd,(char*)&packbuf, sizeof(packbuf),
+ MSG_PEEK) - sizeof(struct sphdr)) > 0) {
+ if (packbuf.hdr.sp_dt == SPPSST_BDT
+ && (packbuf.hdr.sp_dt & SP_OB)
+ && count == 1) {
+ read(f->fd, (char*)&packbuf, sizeof(packbuf));
+ f->abortseen = TRUE;
+ return(TRUE);
+ }
+ else if (count == 0)
+ read(f->fd, (char*)&packbuf, sizeof(packbuf));
+ else return(FALSE);
+ }
+ return(FALSE);
+}