/* $NetBSD: scmio.c,v 1.5 1997/06/17 18:56:36 christos Exp $ */ /* * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * SUP Communication Module for 4.3 BSD * * SUP COMMUNICATION MODULE SPECIFICATIONS: * * IN THIS MODULE: * * OUTPUT TO NETWORK * * MESSAGE START/END * writemsg (msg) start message * int msg; message type * writemend () end message and flush data to network * * MESSAGE DATA * writeint (i) write int * int i; integer to write * writestring (p) write string * char *p; string pointer * writefile (f) write open file * int f; open file descriptor * * COMPLETE MESSAGE (start, one data block, end) * writemnull (msg) write message with no data * int msg; message type * writemint (msg,i) write int message * int msg; message type * int i; integer to write * writemstr (msg,p) write string message * int msg; message type * char *p; string pointer * * INPUT FROM NETWORK * MESSAGE START/END * readflush () flush any unread data (close) * readmsg (msg) read specified message type * int msg; message type * readmend () read message end * * MESSAGE DATA * readskip () skip over one input data block * readint (i) read int * int *i; pointer to integer * readstring (p) read string * char **p; pointer to string pointer * readfile (f) read into open file * int f; open file descriptor * * COMPLETE MESSAGE (start, one data block, end) * readmnull (msg) read message with no data * int msg; message type * readmint (msg,i) read int message * int msg; message type * int *i; pointer to integer * readmstr (msg,p) read string message * int msg; message type * char **p; pointer to string pointer * * RETURN CODES * All routines normally return SCMOK. SCMERR may be returned * by any routine on abnormal (usually fatal) errors. An * unexpected MSGGOAWAY will result in SCMEOF being returned. * * COMMUNICATION PROTOCOL * Messages always alternate, with the first message after * connecting being sent by the client process. * * At the end of the conversation, the client process will * send a message to the server telling it to go away. Then, * both processes will close the network connection. * * Any time a process receives a message it does not expect, * the "readmsg" routine will send a MSGGOAWAY message and * return SCMEOF. * * Each message has this format: * ---------- ------------ ------------ ---------- * |msg type| |count|data| |count|data| ... |ENDCOUNT| * ---------- ------------ ------------ ---------- * size: int int var. int var. int * * All ints are assumed to be 32-bit quantities. A message * with no data simply has a message type followed by ENDCOUNT. * ********************************************************************** * HISTORY * Revision 1.7 92/09/09 22:04:41 mrt * Removed the data encryption routines from here to netcrypt.c * [92/09/09 mrt] * * Revision 1.6 92/08/11 12:05:57 mrt * Brad's changes: Delinted,Added forward declarations of * static functions. Added copyright. * [92/07/24 mrt] * * 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added crosspatch support. * * 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University * Found error in debuging code for readint(). * * 01-Apr-87 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added code to readdata to "push" data back into the data buffer. * Added prereadcount() to return the message count size after * reading it and then pushing it back into the buffer. Clear * any encryption when a GOAWAY message is detected before reading * the reason string. [V5.19] * * 02-Oct-86 Rudy Nedved (ern) at Carnegie-Mellon University * Put a timeout on reading from the network. * * 25-May-86 Jonathan J. Chew (jjc) at Carnegie-Mellon University * Renamed "howmany" parameter to routines "encode" and "decode" from * to "count" to avoid conflict with 4.3BSD macro. * * 15-Feb-86 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added readflush() to flush any unread data from the input * buffer. Called by requestend() in scm.c module. * * 19-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added register variables to decode() for speedup. Added I/O * buffering to reduce the number or read/write system calls. * Removed readmfil/writemfil routines which were not used and were * not compatable with the other similarly defined routines anyway. * * 19-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Created from scm.c I/O and crypt routines. * ********************************************************************** */ #include #include #include #include #include #include #include "supcdefs.h" #include "supextern.h" #include "supmsg.h" extern int errno; /************************* *** M A C R O S *** *************************/ /* end of message */ #define ENDCOUNT (-1) /* end of message marker */ #define NULLCOUNT (-2) /* used for sending NULL pointer */ #define RETRIES 15 /* # of times to retry io */ #define FILEXFER 2048 /* block transfer size */ #define XFERSIZE(count) ((count > FILEXFER) ? FILEXFER : count) /********************************************* *** G L O B A L V A R I A B L E S *** *********************************************/ extern int netfile; /* network file descriptor */ int scmdebug; /* scm debug flag */ int cryptflag; /* whether to encrypt/decrypt data */ char *cryptbuf; /* buffer for data encryption/decryption */ extern char *goawayreason; /* reason for goaway message */ struct buf { char b_data[FILEXFER]; /* buffered data */ char *b_ptr; /* pointer to end of buffer */ int b_cnt; /* number of bytes in buffer */ } buffers[2]; struct buf *bufptr; /* buffer pointer */ static int writedata __P((int, char *)); static int writeblock __P((int, char *)); static int readdata __P((int, char *)); static int readcount __P((int *)); /*********************************************** *** O U T P U T T O N E T W O R K *** ***********************************************/ static int writedata (count,data) /* write raw data to network */ int count; char *data; { register int x,tries; register struct buf *bp; if (bufptr) { if (bufptr->b_cnt + count <= FILEXFER) { bcopy (data,bufptr->b_ptr,count); bufptr->b_cnt += count; bufptr->b_ptr += count; return (SCMOK); } bp = (bufptr == buffers) ? &buffers[1] : buffers; bcopy (data,bp->b_data,count); bp->b_cnt = count; bp->b_ptr = bp->b_data + count; data = bufptr->b_data; count = bufptr->b_cnt; bufptr->b_cnt = 0; bufptr->b_ptr = bufptr->b_data; bufptr = bp; } tries = 0; for (;;) { errno = 0; x = write (netfile,data,count); if (x > 0) break; if (errno) break; if (++tries > RETRIES) break; if (scmdebug > 0) logerr ("SCM Retrying failed network write"); } if (x <= 0) { if (errno == EPIPE) return (scmerr (-1,"Network write timed out")); if (errno) return (scmerr (errno,"Write error on network")); return (scmerr (-1,"Write retries failed")); } if (x != count) return (scmerr (-1,"Write error on network returned %d on write of %d",x,count)); return (SCMOK); } static int writeblock (count,data) /* write data block */ int count; char *data; { register int x; int y = byteswap(count); x = writedata (sizeof(int),(char *)&y); if (x == SCMOK) x = writedata (count,data); return (x); } int writemsg (msg) /* write start of message */ int msg; { int x; if (scmdebug > 1) loginfo ("SCM Writing message %d",msg); if (bufptr) return (scmerr (-1,"Buffering already enabled")); bufptr = buffers; bufptr->b_ptr = bufptr->b_data; bufptr->b_cnt = 0; x = byteswap (msg); return (writedata(sizeof(int),(char *)&x)); } int writemend () /* write end of message */ { register int count; register char *data; int x; x = byteswap (ENDCOUNT); x = writedata (sizeof(int),(char *)&x); if (x != SCMOK) return (x); if (bufptr == NULL) return (scmerr (-1,"Buffering already disabled")); if (bufptr->b_cnt == 0) { bufptr = NULL; return (SCMOK); } data = bufptr->b_data; count = bufptr->b_cnt; bufptr = NULL; return (writedata (count, data)); } int writeint (i) /* write int as data block */ int i; { int x; if (scmdebug > 2) loginfo ("SCM Writing integer %d",i); x = byteswap(i); return (writeblock(sizeof(int),(char *)&x)); } int writestring (p) /* write string as data block */ char *p; { register int len,x; if (p == NULL) { int y = byteswap(NULLCOUNT); if (scmdebug > 2) loginfo ("SCM Writing string NULL"); return (writedata (sizeof(int),(char *)&y)); } if (scmdebug > 2) loginfo ("SCM Writing string %s",p); len = strlen (p); if (cryptflag) { x = getcryptbuf (len+1); if (x != SCMOK) return (x); encode (p,cryptbuf,len); p = cryptbuf; } return (writeblock(len,p)); } int writefile (f) /* write open file as a data block */ int f; { char buf[FILEXFER]; register int number = 0,sum = 0,filesize,x; int y; struct stat statbuf; if (fstat(f,&statbuf) < 0) return (scmerr (errno,"Can't access open file for message")); filesize = statbuf.st_size; y = byteswap(filesize); x = writedata (sizeof(int),(char *)&y); if (cryptflag) x = getcryptbuf (FILEXFER); if (x == SCMOK) { sum = 0; do { number = read (f,buf,FILEXFER); if (number > 0) { if (cryptflag) { encode (buf,cryptbuf,number); x = writedata (number,cryptbuf); } else { x = writedata (number,buf); } sum += number; } } while (x == SCMOK && number > 0); } if (sum != filesize) return (scmerr (-1,"File size error on output message")); if (number < 0) return (scmerr (errno,"Read error on file output message")); return (x); } int writemnull (msg) /* write message with no data */ int msg; { register int x; x = writemsg (msg); if (x == SCMOK) x = writemend (); return (x); } int writemint (msg,i) /* write message of one int */ int msg,i; { register int x; x = writemsg (msg); if (x == SCMOK) x = writeint (i); if (x == SCMOK) x = writemend (); return (x); } int writemstr (msg,p) /* write message of one string */ int msg; char *p; { register int x; x = writemsg (msg); if (x == SCMOK) x = writestring (p); if (x == SCMOK) x = writemend (); return (x); } /************************************************* *** I N P U T F R O M N E T W O R K *** *************************************************/ static int readdata (count,data) /* read raw data from network */ int count; char *data; { register char *p; register int n,m,x; int tries; static int bufcnt = 0; static char *bufptr; static char buffer[FILEXFER]; static int imask; static struct timeval timout; if (count < 0) { if (bufptr + count < buffer) return (scmerr (-1,"No space in buffer %d",count)); bufptr += count; bufcnt -= count; bcopy (data,bufptr,-count); return (SCMOK); } if (count == 0 && data == NULL) { bufcnt = 0; return (SCMOK); } if (count <= bufcnt) { bcopy (bufptr,data,count); bufptr += count; bufcnt -= count; return (SCMOK); } if (bufcnt > 0) { bcopy (bufptr,data,bufcnt); data += bufcnt; count -= bufcnt; } bufptr = buffer; bufcnt = 0; timout.tv_usec = 0; timout.tv_sec = 2*60*60; p = buffer; n = FILEXFER; m = count; while (m > 0) { tries = 0; for (;;) { imask = 1 << netfile; if (select(32,(fd_set *)&imask,(fd_set *)0,(fd_set *)0,&timout) < 0) imask = 1; errno = 0; if (imask) x = read (netfile,p,n); else return (scmerr (-1,"Timeout on network input")); if (x > 0) break; if (x == 0) return (scmerr (-1,"Premature EOF on network input")); if (errno) break; if (++tries > RETRIES) break; if (scmdebug > 0) loginfo ("SCM Retrying failed network read"); } if (x < 0) { if (errno) return (scmerr (errno,"Read error on network")); return (scmerr (-1,"Read retries failed")); } p += x; n -= x; m -= x; bufcnt += x; } bcopy (bufptr,data,count); bufptr += count; bufcnt -= count; return (SCMOK); } static int readcount (count) /* read count of data block */ int *count; { register int x; int y; x = readdata (sizeof(int),(char *)&y); if (x != SCMOK) return (x); *count = byteswap(y); return (SCMOK); } int prereadcount (count) /* preread count of data block */ int *count; { register int x; int y; x = readdata (sizeof(int),(char *)&y); if (x != SCMOK) return (x); x = readdata (- ((int)(sizeof(int))),(char *)&y); if (x != SCMOK) return (x); *count = byteswap(y); return (SCMOK); } int readflush () { return (readdata (0, (char *)NULL)); } int readmsg (msg) /* read header for expected message */ int msg; /* if message is unexpected, send back SCMHUH */ { register int x; int m; if (scmdebug > 1) loginfo ("SCM Reading message %d",msg); x = readdata (sizeof(int),(char *)&m); /* msg type */ if (x != SCMOK) return (x); m = byteswap(m); if (m == msg) return (x); /* check for MSGGOAWAY in case he noticed problems first */ if (m != MSGGOAWAY) return (scmerr (-1,"Received unexpected message %d",m)); (void) netcrypt ((char *)NULL); (void) readstring (&goawayreason); (void) readmend (); if (goawayreason == NULL) return (SCMEOF); logerr ("SCM GOAWAY %s",goawayreason); return (SCMEOF); } int readmend () { register int x; int y; x = readdata (sizeof(int),(char *)&y); y = byteswap(y); if (x == SCMOK && y != ENDCOUNT) return (scmerr (-1,"Error reading end of message")); return (x); } int readskip () /* skip over one input block */ { register int x; int n; char buf[FILEXFER]; x = readcount (&n); if (x != SCMOK) return (x); if (n < 0) return (scmerr (-1,"Invalid message count %d",n)); while (x == SCMOK && n > 0) { x = readdata (XFERSIZE(n),buf); n -= XFERSIZE(n); } return (x); } int readint (buf) /* read int data block */ int *buf; { register int x; int y; x = readcount (&y); if (x != SCMOK) return (x); if (y < 0) return (scmerr (-1,"Invalid message count %d",y)); if (y != sizeof(int)) return (scmerr (-1,"Size error for int message is %d",y)); x = readdata (sizeof(int),(char *)&y); (*buf) = byteswap(y); if (scmdebug > 2) loginfo ("SCM Reading integer %d",*buf); return (x); } int readstring (buf) /* read string data block */ register char **buf; { register int x; int count; register char *p; x = readcount (&count); if (x != SCMOK) return (x); if (count == NULLCOUNT) { if (scmdebug > 2) loginfo ("SCM Reading string NULL"); *buf = NULL; return (SCMOK); } if (count < 0) return (scmerr (-1,"Invalid message count %d",count)); if (scmdebug > 3) loginfo ("SCM Reading string count %d",count); if ((p = (char *)malloc ((unsigned)count+1)) == NULL) return (scmerr (-1,"Can't malloc %d bytes for string",count)); if (cryptflag) { x = getcryptbuf (count+1); if (x == SCMOK) x = readdata (count,cryptbuf); if (x != SCMOK) return (x); if (scmdebug > 3) printf ("SCM Reading encrypted string %s\n",cryptbuf); decode (cryptbuf,p,count); } else { x = readdata (count,p); if (x != SCMOK) return (x); } p[count] = 0; /* NULL at end of string */ *buf = p; if (scmdebug > 2) loginfo ("SCM Reading string %s",*buf); return (SCMOK); } int readfile (f) /* read data block into open file */ int f; { register int x; int count; char buf[FILEXFER]; if (cryptflag) { x = getcryptbuf (FILEXFER); if (x != SCMOK) return (x); } x = readcount (&count); if (x != SCMOK) return (x); if (count < 0) return (scmerr (-1,"Invalid message count %d",count)); while (x == SCMOK && count > 0) { if (cryptflag) { x = readdata (XFERSIZE(count),cryptbuf); if (x == SCMOK) decode (cryptbuf,buf,XFERSIZE(count)); } else x = readdata (XFERSIZE(count),buf); if (x == SCMOK) { (void) write (f,buf,XFERSIZE(count)); count -= XFERSIZE(count); } } return (x); } int readmnull (msg) /* read null message */ int msg; { register int x; x = readmsg (msg); if (x == SCMOK) x = readmend (); return (x); } int readmint (msg,buf) /* read int message */ int msg,*buf; { register int x; x = readmsg (msg); if (x == SCMOK) x = readint (buf); if (x == SCMOK) x = readmend (); return (x); } int readmstr (msg,buf) /* read string message */ int msg; char **buf; { register int x; x = readmsg (msg); if (x == SCMOK) x = readstring (buf); if (x == SCMOK) x = readmend (); return (x); } /********************************** *** C R O S S P A T C H *** **********************************/ void crosspatch () { fd_set ibits, obits, xbits; register int c; char buf[STRINGLENGTH]; for (;;) { FD_ZERO (&ibits); FD_ZERO (&obits); FD_ZERO (&xbits); FD_SET (0,&ibits); FD_SET (netfile,&ibits); if ((c = select(16, &ibits, &obits, &xbits, (struct timeval *)NULL)) < 1) { if (c == -1) { if (errno == EINTR) { continue; } } sleep (5); continue; } if (FD_ISSET (netfile,&ibits)) { c = read (netfile,buf,sizeof (buf)); if (c < 0 && errno == EWOULDBLOCK) c = 0; else { if (c <= 0) { break; } (void) write (1,buf,c); } } if (FD_ISSET(0, &ibits)) { c = read (0,buf,sizeof (buf)); if (c < 0 && errno == EWOULDBLOCK) c = 0; else { if (c <= 0) break; (void) write (netfile,buf,c); } } } }