home *** CD-ROM | disk | FTP | other *** search
- /* protj.c
- The 'j' protocol.
-
- Copyright (C) 1992, 1994 Ian Lance Taylor
-
- This file is part of the Taylor UUCP package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The author of the program may be contacted at ian@airs.com or
- c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
- */
-
- #include "uucp.h"
-
- #if USE_RCS_ID
- const char protj_rcsid[] = "$Id: protj.c,v 1.6 1995/06/21 19:15:32 ian Rel $";
- #endif
-
- #include <ctype.h>
- #include <errno.h>
-
- #include "uudefs.h"
- #include "uuconf.h"
- #include "conn.h"
- #include "trans.h"
- #include "system.h"
- #include "prot.h"
-
- /* The 'j' protocol.
-
- The 'j' protocol is a wrapper around the 'i' protocol, which avoids
- the use of certain characters, such as XON and XOFF.
-
- Each 'j' protocol packet begins with a '^' character, followed by a
- two byte encoded size giving the total number of bytes in the
- packet. The first byte is HIGH, the second byte is LOW, and the
- number of bytes is (HIGH - 32) * 64 + (LOW - 32), where 32 <= HIGH
- < 127 and 32 <= LOW < 96 (i.e., HIGH and LOW are printable ASCII
- characters). This is followed by a '=' character. The next two
- bytes are the number of data bytes in the packet, using the same
- encoding. This is followed by a '@' character, and then that
- number of data bytes. The remaining bytes in the packet are
- indices of bytes which must be transformed, followed by a trailing
- '~' character. The indices are encoded in the following overly
- complex format.
-
- Each byte index is two bytes long. The first byte in the index is
- INDEX-HIGH and the second is INDEX-LOW. If 32 <= INDEX-HIGH < 126,
- the byte index refers to the byte at position (INDEX-HIGH - 32) *
- 32 + INDEX-LOW % 32 in the actual data, where 32 <= INDEX-LOW <
- 127. If 32 <= INDEX-LOW < 64, then 128 must be added to the
- indexed byte. If 64 <= INDEX-LOW < 96, then the indexed byte must
- be exclusive or'red with 32. If 96 <= INDEX-LOW < 127, both
- operations must be performed. If INDEX-HIGH == 126, then the byte
- index refers to the byte at position (INDEX-LOW - 32) * 32 + 31,
- where 32 <= INDEX-LOW < 126. 128 must be added to the byte, and it
- must be exclusive or'red with 32. This unfortunately requires a
- special test (when encoding INDEX-LOW must be checked for 127; when
- decoding INDEX-HIGH must be checked for 126). It does, however,
- permit the byte indices field to consist exclusively of printable
- ASCII characters.
-
- The maximum value for a byte index is (125 - 32) * 32 + 31 == 3007,
- so the is the maximum number of data bytes permitted. Since it is
- convenient to have each 'j' protocol packet correspond to each 'i'
- protocol packet, we restrict the 'i' protocol accordingly.
-
- Note that this encoding method assumes that we can send all
- printable ASCII characters. */
-
- /* The first byte of each packet. I just picked these values
- randomly, trying to get characters that were perhaps slightly less
- likely to appear in normal text. */
- #define FIRST '\136'
-
- /* The fourth byte of each packet. */
- #define FOURTH '\075'
-
- /* The seventh byte of each packet. */
- #define SEVENTH '\100'
-
- /* The trailing byte of each packet. */
- #define TRAILER '\176'
-
- /* The length of the header. */
- #define CHDRLEN (7)
-
- /* Get a number of bytes encoded in a two byte length at the start of
- a packet. */
- #define CGETLENGTH(b1, b2) (((b1) - 32) * 64 + ((b2) - 32))
-
- /* Set the high and low bytes of a two byte length at the start of a
- packet. */
- #define ISETLENGTH_FIRST(i) ((i) / 64 + 32)
- #define ISETLENGTH_SECOND(i) ((i) % 64 + 32)
-
- /* The maximum packet size we support, as determined by the byte
- indices. */
- #define IMAXPACKSIZE ((125 - 32) * 32 + 31)
-
- /* Amount to offset the bytes in the byte index by. */
- #define INDEX_OFFSET (32)
-
- /* Maximum value of INDEX-LOW, before offsetting. */
- #define INDEX_MAX_LOW (32)
-
- /* Maximum value of INDEX-HIGH, before offsetting. */
- #define INDEX_MAX_HIGH (94)
-
- /* The set of characters to avoid. */
- static char *zJavoid;
-
- /* The number of characters to avoid. */
- static size_t cJavoid;
-
- /* A buffer used when sending data. */
- static char *zJbuf;
-
- /* The end of the undecoded data in abPrecbuf. */
- static int iJrecend;
-
- /* Local functions. */
- static boolean fjsend_data P((struct sconnection *qconn, const char *zsend,
- size_t csend, boolean fdoread));
- static boolean fjreceive_data P((struct sconnection *qconn, size_t cneed,
- size_t *pcrec, int ctimeout,
- boolean freport));
- static boolean fjprocess_data P((size_t *pcneed));
-
- /* Start the protocol. We first send over the list of characters to
- avoid as an escape sequence, starting with FIRST and ending with
- TRAILER. There is no error checking done on this string. */
-
- boolean
- fjstart (qdaemon, pzlog)
- struct sdaemon *qdaemon;
- char **pzlog;
- {
- size_t clen;
- char *zsend;
- int b;
- size_t cbuf, cgot;
- char *zbuf;
- int i;
-
- /* Send the characters we want to avoid to the other side. */
- clen = strlen (zJavoid_parameter);
- zsend = zbufalc (clen + 3);
- zsend[0] = FIRST;
- memcpy (zsend + 1, zJavoid_parameter, clen);
- zsend[clen + 1] = TRAILER;
- zsend[clen + 2] = '\0';
- if (! fsend_data (qdaemon->qconn, zsend, clen + 2, TRUE))
- {
- ubuffree (zsend);
- return FALSE;
- }
- ubuffree (zsend);
-
- /* Read the characters the other side wants to avoid. */
- while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE))
- != FIRST)
- {
- if (b < 0)
- {
- if (b == -1)
- ulog (LOG_ERROR, "Timed out in 'j' protocol startup");
- return FALSE;
- }
- }
-
- cbuf = 20;
- zbuf = zbufalc (cbuf);
- cgot = 0;
- while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE))
- != TRAILER)
- {
- if (b < 0)
- {
- ubuffree (zbuf);
- if (b == -1)
- ulog (LOG_ERROR, "Timed out in 'j' protocol startup");
- return FALSE;
- }
- if (cgot + 1 >= cbuf)
- {
- char *znew;
-
- cbuf += 20;
- znew = zbufalc (cbuf);
- memcpy (znew, zbuf, cgot);
- ubuffree (zbuf);
- zbuf = znew;
- }
- zbuf[cgot] = b;
- ++cgot;
- }
- zbuf[cgot] = '\0';
-
- /* Merge the local and remote avoid bytes into one list, translated
- into bytes. */
- cgot = cescape (zbuf);
-
- clen = strlen (zJavoid_parameter);
- zJavoid = zbufalc (clen + cgot + 1);
- memcpy (zJavoid, zJavoid_parameter, clen + 1);
- cJavoid = cescape (zJavoid);
-
- for (i = 0; i < cgot; i++)
- {
- if (memchr (zJavoid, zbuf[i], cJavoid) == NULL)
- {
- zJavoid[cJavoid] = zbuf[i];
- ++cJavoid;
- }
- }
-
- ubuffree (zbuf);
-
- /* We can't avoid ASCII printable characters, since the encoding
- method assumes that they can always be sent. If it ever turns
- out to be important, a different encoding method could be used,
- perhaps keyed by a different FIRST character. */
- if (cJavoid == 0)
- {
- ulog (LOG_ERROR, "No characters to avoid in 'j' protocol");
- return FALSE;
- }
- for (i = 0; i < cJavoid; i++)
- {
- if (zJavoid[i] >= 32 && zJavoid[i] <= 126)
- {
- ulog (LOG_ERROR, "'j' protocol can't avoid character '\\%03o'",
- zJavoid[i]);
- return FALSE;
- }
- }
-
- /* If we are avoiding XON and XOFF, use XON/XOFF handshaking. */
- if (memchr (zJavoid, '\021', cJavoid) != NULL
- && memchr (zJavoid, '\023', cJavoid) != NULL)
- {
- if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
- STRIPSETTING_EIGHTBITS, XONXOFF_ON))
- return FALSE;
- }
-
- /* Let the port settle. */
- usysdep_sleep (2);
-
- /* Allocate a buffer we use when sending data. We will probably
- never actually need one this big; if this code is ported to a
- computer with small amounts of memory, this should be changed to
- increase the buffer size as needed. */
- zJbuf = zbufalc (CHDRLEN + IMAXPACKSIZE * 3 + 1);
- zJbuf[0] = FIRST;
- zJbuf[3] = FOURTH;
- zJbuf[6] = SEVENTH;
-
- /* iJrecend is the end of the undecoded data, and iPrecend is the
- end of the decoded data. At this point there is no decoded data,
- and we must initialize the variables accordingly. */
- iJrecend = iPrecend;
- iPrecend = iPrecstart;
-
- /* Now do the 'i' protocol startup. */
- return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fjsend_data,
- fjreceive_data);
- }
-
- /* Shut down the protocol. */
-
- boolean
- fjshutdown (qdaemon)
- struct sdaemon *qdaemon;
- {
- boolean fret;
-
- fret = fishutdown (qdaemon);
- ubuffree (zJavoid);
- ubuffree (zJbuf);
- return fret;
- }
-
- /* Encode a packet of data and send it. This copies the data, which
- is a waste of time, but calling fsend_data three times (for the
- header, the body, and the trailer) would waste even more time. */
-
- static boolean
- fjsend_data (qconn, zsend, csend, fdoread)
- struct sconnection *qconn;
- const char *zsend;
- size_t csend;
- boolean fdoread;
- {
- char *zput, *zindex;
- const char *zfrom, *zend;
- char bfirst, bsecond;
- int iprecendhold;
- boolean fret;
-
- zput = zJbuf + CHDRLEN;
- zindex = zput + csend;
- zfrom = zsend;
- zend = zsend + csend;
-
- /* Optimize for the common case of avoiding two characters. */
- bfirst = zJavoid[0];
- if (cJavoid <= 1)
- bsecond = bfirst;
- else
- bsecond = zJavoid[1];
- while (zfrom < zend)
- {
- char b;
- boolean f128, f32;
- int i, ihigh, ilow;
-
- b = *zfrom++;
- if (b != bfirst && b != bsecond)
- {
- int ca;
- char *za;
-
- if (cJavoid <= 2)
- {
- *zput++ = b;
- continue;
- }
-
- ca = cJavoid - 2;
- za = zJavoid + 2;
- while (ca-- != 0)
- if (*za++ == b)
- break;
-
- if (ca < 0)
- {
- *zput++ = b;
- continue;
- }
- }
-
- if ((b & 0x80) == 0)
- f128 = FALSE;
- else
- {
- b &=~ 0x80;
- f128 = TRUE;
- }
- if (b >= 32 && b != 127)
- f32 = FALSE;
- else
- {
- b ^= 0x20;
- f32 = TRUE;
- }
-
- /* We must now put the byte index into the buffer. The byte
- index is encoded similarly to the length of the actual data,
- but the byte index also encodes the operations that must be
- performed on the byte. The first byte in the index is the
- most significant bits. If we only had to subtract 128 from
- the byte, we use the second byte directly. If we had to xor
- the byte with 32, we add 32 to the second byte index. If we
- had to perform both operations, we add 64 to the second byte
- index. However, if we had to perform both operations, and
- the second byte index was 31, then after adding 64 and
- offsetting by 32 we would come up with 127, which we are not
- permitted to use. Therefore, in this special case we set the
- first byte of the index to 126 and put the original first
- byte into the second byte position instead. This is why we
- could not permit the high byte of the length of the actual
- data to be 126. We can get away with the switch because both
- the value of the second byte index (31) and the operations to
- perform (both) are known. */
- i = zput - (zJbuf + CHDRLEN);
- ihigh = i / INDEX_MAX_LOW;
- ilow = i % INDEX_MAX_LOW;
-
- if (f128 && ! f32)
- ;
- else if (f32 && ! f128)
- ilow += INDEX_MAX_LOW;
- else
- {
- /* Both operations had to be performed. */
- if (ilow != INDEX_MAX_LOW - 1)
- ilow += 2 * INDEX_MAX_LOW;
- else
- {
- ilow = ihigh;
- ihigh = INDEX_MAX_HIGH;
- }
- }
-
- *zindex++ = ihigh + INDEX_OFFSET;
- *zindex++ = ilow + INDEX_OFFSET;
- *zput++ = b;
- }
-
- *zindex++ = TRAILER;
-
- /* Set the lengths into the buffer. zJbuf[0,3,6] were set when
- zJbuf was allocated, and are never changed thereafter. */
- zJbuf[1] = ISETLENGTH_FIRST (zindex - zJbuf);
- zJbuf[2] = ISETLENGTH_SECOND (zindex - zJbuf);
- zJbuf[4] = ISETLENGTH_FIRST (csend);
- zJbuf[5] = ISETLENGTH_SECOND (csend);
-
- /* Send the data over the line. We must preserve iPrecend as
- discussed in fjreceive_data. */
- iprecendhold = iPrecend;
- iPrecend = iJrecend;
- fret = fsend_data (qconn, zJbuf, (size_t) (zindex - zJbuf), fdoread);
- iJrecend = iPrecend;
- iPrecend = iprecendhold;
-
- /* Process any bytes that may have been placed in abPrecbuf. */
- if (fret && iPrecend != iJrecend)
- {
- if (! fjprocess_data ((size_t *) NULL))
- return FALSE;
- }
-
- return fret;
- }
-
- /* Receive and decode data. This is called by fiwait_for_packet. We
- need to be able to return decoded data between iPrecstart and
- iPrecend, while not losing any undecoded partial packets we may
- have read. We use iJrecend as a pointer to the end of the
- undecoded data, and set iPrecend for the decoded data. iPrecend
- points to the start of the undecoded data. */
-
- static boolean
- fjreceive_data (qconn, cineed, pcrec, ctimeout, freport)
- struct sconnection *qconn;
- size_t cineed;
- size_t *pcrec;
- int ctimeout;
- boolean freport;
- {
- int iprecendstart;
- size_t cjneed;
- size_t crec;
- int cnew;
-
- iprecendstart = iPrecend;
-
- /* Figure out how many bytes we need to decode the next packet. */
- if (! fjprocess_data (&cjneed))
- return FALSE;
-
- /* As we long as we read some data but don't have enough to decode a
- packet, we try to read some more. We decrease the timeout each
- time so that we will not wait forever if the connection starts
- dribbling data. */
- do
- {
- int iprecendhold;
- size_t cneed;
-
- if (cjneed > cineed)
- cneed = cjneed;
- else
- cneed = cineed;
-
- /* We are setting iPrecend to the end of the decoded data for
- the 'i' protocol. When we do the actual read, we have to set
- it to the end of the undecoded data so that any undecoded
- data we have received is not overwritten. */
- iprecendhold = iPrecend;
- iPrecend = iJrecend;
- if (! freceive_data (qconn, cneed, &crec, ctimeout, freport))
- return FALSE;
- iJrecend = iPrecend;
- iPrecend = iprecendhold;
-
- /* Process any data we have received. This will set iPrecend to
- the end of the new decoded data. */
- if (! fjprocess_data (&cjneed))
- return FALSE;
-
- cnew = iPrecend - iprecendstart;
- if (cnew < 0)
- cnew += CRECBUFLEN;
-
- if (cnew > cineed)
- cineed = 0;
- else
- cineed -= cnew;
-
- --ctimeout;
- }
- while (cnew == 0 && crec > 0 && ctimeout > 0);
-
- DEBUG_MESSAGE1 (DEBUG_PROTO, "fjreceive_data: Got %d decoded bytes",
- cnew);
-
- *pcrec = cnew;
- return TRUE;
- }
-
- /* Decode the data in the buffer, optionally returning the number of
- bytes needed to complete the next packet. */
-
- static boolean
- fjprocess_data (pcneed)
- size_t *pcneed;
- {
- int istart;
-
- istart = iPrecend;
- while (istart != iJrecend)
- {
- int i, iget;
- char ab[CHDRLEN];
- int cpacket, cdata, chave;
- int iindex, iendindex;
-
- /* Find the next occurrence of FIRST. If we have to skip some
- garbage bytes to get to it, zero them out (so they don't
- confuse the 'i' protocol) and advance iPrecend. This will
- save us from looking at them again. */
- if (abPrecbuf[istart] != FIRST)
- {
- int cintro;
- char *zintro;
- size_t cskipped;
-
- cintro = iJrecend - istart;
- if (cintro < 0)
- cintro = CRECBUFLEN - istart;
-
- zintro = memchr (abPrecbuf + istart, FIRST, (size_t) cintro);
- if (zintro == NULL)
- {
- bzero (abPrecbuf + istart, (size_t) cintro);
- istart = (istart + cintro) % CRECBUFLEN;
- iPrecend = istart;
- continue;
- }
-
- cskipped = zintro - (abPrecbuf + istart);
- bzero (abPrecbuf + istart, cskipped);
- istart += cskipped;
- iPrecend = istart;
- }
-
- for (i = 0, iget = istart;
- i < CHDRLEN && iget != iJrecend;
- ++i, iget = (iget + 1) % CRECBUFLEN)
- ab[i] = abPrecbuf[iget];
-
- if (i < CHDRLEN)
- {
- if (pcneed != NULL)
- *pcneed = CHDRLEN - i;
- return TRUE;
- }
-
- cpacket = CGETLENGTH (ab[1], ab[2]);
- cdata = CGETLENGTH (ab[4], ab[5]);
-
- /* Make sure the header has the right magic characters, that the
- data is not larger than the packet, and that we have an even
- number of byte index characters. */
- if (ab[3] != FOURTH
- || ab[6] != SEVENTH
- || cdata > cpacket - CHDRLEN - 1
- || (cpacket - cdata - CHDRLEN - 1) % 2 == 1)
- {
- istart = (istart + 1) % CRECBUFLEN;
- continue;
- }
-
- chave = iJrecend - istart;
- if (chave < 0)
- chave += CRECBUFLEN;
-
- if (chave < cpacket)
- {
- if (pcneed != NULL)
- *pcneed = cpacket - chave;
- return TRUE;
- }
-
- /* Figure out where the byte indices start and end. */
- iindex = (istart + CHDRLEN + cdata) % CRECBUFLEN;
- iendindex = (istart + cpacket - 1) % CRECBUFLEN;
-
- /* Make sure the magic trailer character is there. */
- if (abPrecbuf[iendindex] != TRAILER)
- {
- istart = (istart + 1) % CRECBUFLEN;
- continue;
- }
-
- /* We have a packet to decode. The decoding process is simpler
- than the encoding process, since all we have to do is examine
- the byte indices. We zero out the byte indices as we go, so
- that they will not confuse the 'i' protocol. */
- while (iindex != iendindex)
- {
- int ihigh, ilow;
- boolean f32, f128;
- int iset;
-
- ihigh = abPrecbuf[iindex] - INDEX_OFFSET;
- abPrecbuf[iindex] = 0;
- iindex = (iindex + 1) % CRECBUFLEN;
- ilow = abPrecbuf[iindex] - INDEX_OFFSET;
- abPrecbuf[iindex] = 0;
- iindex = (iindex + 1) % CRECBUFLEN;
-
- /* Now we must undo the encoding, by adding 128 and xoring
- with 32 as appropriate. Which to do is encoded in the
- low byte, except that if the high byte is the special
- value 126, then the low byte is actually the high byte
- and both operations are performed. */
- f128 = TRUE;
- f32 = TRUE;
- if (ihigh == INDEX_MAX_HIGH)
- iset = ilow * INDEX_MAX_LOW + INDEX_MAX_LOW - 1;
- else
- {
- iset = ihigh * INDEX_MAX_LOW + ilow % INDEX_MAX_LOW;
- if (ilow < INDEX_MAX_LOW)
- f32 = FALSE;
- else if (ilow < 2 * INDEX_MAX_LOW)
- f128 = FALSE;
- }
-
- /* Now iset is the index from the start of the data to the
- byte to modify; adjust it to an index in abPrecbuf. */
- iset = (istart + CHDRLEN + iset) % CRECBUFLEN;
-
- if (f128)
- abPrecbuf[iset] |= 0x80;
- if (f32)
- abPrecbuf[iset] ^= 0x20;
- }
-
- /* Zero out the header and trailer to avoid confusing the 'i'
- protocol, and update iPrecend to the end of decoded data. */
- for (i = 0, iget = istart;
- i < CHDRLEN && iget != iJrecend;
- ++i, iget = (iget + 1) % CRECBUFLEN)
- abPrecbuf[iget] = 0;
- abPrecbuf[iendindex] = 0;
- iPrecend = (iendindex + 1) % CRECBUFLEN;
- istart = iPrecend;
- }
-
- if (pcneed != NULL)
- *pcneed = CHDRLEN + 1;
- return TRUE;
- }
-