aboutsummaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/uucico/protg.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/uucico/protg.c')
-rw-r--r--gnu/libexec/uucp/uucico/protg.c1933
1 files changed, 1933 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/uucico/protg.c b/gnu/libexec/uucp/uucico/protg.c
new file mode 100644
index 000000000000..39a79212cf77
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/protg.c
@@ -0,0 +1,1933 @@
+/* protg.c
+ The 'g' protocol.
+
+ Copyright (C) 1991, 1992 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char protg_rcsid[] = "$Id: protg.c,v 1.1 1993/08/05 18:27:11 conklin Exp $";
+#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"
+
+/* Each 'g' protocol packet begins with six bytes. They are:
+
+ <DLE><k><c0><c1><C><x>
+
+ <DLE> is the ASCII DLE character (^P or '\020').
+ if 1 <= <k> <= 8, the packet is followed by 2 ** (k + 4) bytes of data;
+ if <k> == 9, these six bytes are a complete control packet;
+ other value of <k> are illegal.
+ <c0> is the low byte of a checksum.
+ <c1> is the high byte of a checksum.
+ <C> is a control byte (see below).
+ <x> is <k> ^ <c0> ^ <c1> ^ <C>.
+
+ The control byte <C> is divided into three bitfields:
+
+ t t x x x y y y
+
+ The two bit field tt is the packet type.
+ The three bit field xxx is the control type for a control packet, or
+ the sequence number for a data packet.
+ The three bit field yyy is a value for a control packet, or the
+ sequence number of the last packet received for a data packet.
+
+ For all successfully recieved packets, the control byte is stored
+ into iGpacket_control. */
+
+/* Names for the bytes in the frame header. */
+#define IFRAME_DLE (0)
+#define IFRAME_K (1)
+#define IFRAME_CHECKLOW (2)
+#define IFRAME_CHECKHIGH (3)
+#define IFRAME_CONTROL (4)
+#define IFRAME_XOR (5)
+
+/* Length of the frame header. */
+#define CFRAMELEN (6)
+
+/* Macros to break apart the control bytes. */
+#define CONTROL_TT(b) ((int)(((b) >> 6) & 03))
+#define CONTROL_XXX(b) ((int)(((b) >> 3) & 07))
+#define CONTROL_YYY(b) ((int)((b) & 07))
+
+/* DLE value. */
+#define DLE ('\020')
+
+/* Get the length of a packet given a pointer to the header. */
+#define CPACKLEN(z) ((size_t) (1 << ((z)[IFRAME_K] + 4)))
+
+/* <k> field value for a control message. */
+#define KCONTROL (9)
+
+/* Get the next sequence number given a sequence number. */
+#define INEXTSEQ(i) ((i + 1) & 07)
+
+/* Compute i1 - i2 modulo 8. */
+#define CSEQDIFF(i1, i2) (((i1) + 8 - (i2)) & 07)
+
+/* Packet types. These are from the tt field.
+ CONTROL -- control packet
+ ALTCHAN -- alternate channel; not used by UUCP
+ DATA -- full data segment
+ SHORTDATA -- less than full data segment (all the bytes specified by
+ the packet length <k> are always transferred). Let <u> be the number
+ of bytes in the data segment not to be used. If <u> <= 0x7f, the first
+ byte of the data segment is <u> and the data follows. If <u> > 0x7f,
+ the first byte of the data segment is 0x80 | (<u> & 0x7f), the second
+ byte of the data segment is <u> >> 7, and the data follows. The
+ maximum possible data segment size is 2**12, so this handles all
+ possible cases. */
+#define CONTROL (0)
+#define ALTCHAN (1)
+#define DATA (2)
+#define SHORTDATA (3)
+
+/* Control types. These are from the xxx field if the type (tt field)
+ is CONTROL.
+
+ CLOSE -- close the connection
+ RJ -- reject; packet yyy last to be received correctly
+ SRJ -- selective reject; reject only packet yyy (not used by UUCP)
+ RR -- receiver ready; packet yyy received correctly
+ INITC -- third step of initialization; yyy holds window size
+ INITB -- second step of initialization; yyy holds maximum <k> value - 1
+ INITA -- first step of initialization; yyy holds window size.
+
+ The yyy value for RR is the same as the yyy value for an ordinary
+ data packet. */
+#define CLOSE (1)
+#define RJ (2)
+#define SRJ (3)
+#define RR (4)
+#define INITC (5)
+#define INITB (6)
+#define INITA (7)
+
+/* Maximum amount of data in a single packet. This is set by the <k>
+ field in the header; the amount of data in a packet is
+ 2 ** (<k> + 4). <k> ranges from 1 to 8. */
+
+#define CMAXDATAINDEX (8)
+
+#define CMAXDATA (1 << (CMAXDATAINDEX + 4))
+
+/* Maximum window size. */
+#define CMAXWINDOW (7)
+
+/* Defaults for the protocol parameters. These may all be changed by
+ using the ``protocol-parameter g'' command, so there is no
+ particular reason to change the values given here. */
+
+/* The desired window size. This is what we tell the other system to
+ use. It must be between 1 and 7, and there's no reason to use less
+ than 7. Protocol parameter ``window''. */
+#define IWINDOW (7)
+
+/* The desired packet size. Many implementations only support 64 byte
+ packets. Protocol parameter ``packet-size''. */
+#define IPACKSIZE (64)
+
+/* The number of times to retry the exchange of INIT packets when
+ starting the protocol. Protocol parameter ``startup-retries''. */
+#define CSTARTUP_RETRIES (8)
+
+/* The timeout to use when waiting for an INIT packet when starting up
+ the protocol. Protocol parameter ``init-timeout''. */
+#define CEXCHANGE_INIT_TIMEOUT (10)
+
+/* The number of times to retry sending and waiting for a single INIT
+ packet when starting the protocol. This controls a single INIT
+ packet, while CSTARTUP_RETRIES controls how many times to try the
+ entire INIT sequence. Protocol parameter ``init-retries''. */
+#define CEXCHANGE_INIT_RETRIES (4)
+
+/* The timeout to use when waiting for a packet. Protocol parameter
+ ``timeout''. */
+#define CTIMEOUT (10)
+
+/* The number of times to retry waiting for a packet. Each time the
+ timeout fails we send a copy of our last data packet or a reject
+ message for the packet we expect from the other side, depending on
+ whether we are waiting for an acknowledgement or a data packet.
+ This is the number of times we try doing that and then waiting
+ again. Protocol parameter ``retries''. */
+#define CRETRIES (6)
+
+/* If we see more than this much unrecognized data, we drop the
+ connection. This must be larger than a single packet size, which
+ means it must be larger than 4096 (the largest possible packet
+ size). Protocol parameter ``garbage''. */
+#define CGARBAGE (10000)
+
+/* If we see more than this many protocol errors, we drop the
+ connection. Protocol parameter ``errors''. */
+#define CERRORS (100)
+
+/* Default decay rate. Each time we send or receive this many packets
+ succesfully, we decrement the error level by one (protocol
+ parameter ``error-decay''). */
+#define CERROR_DECAY (10)
+
+/* If this value is non-zero, it will be used as the remote window
+ size regardless of what the other side requested. This can be
+ useful for dealing with some particularly flawed packages. This
+ default value should always be 0, and protocol parameter
+ ``remote-window'' should be used for the affected systems. */
+#define IREMOTE_WINDOW (0)
+
+/* If this value is non-zero, it will be used as the packet size to
+ send to the remote system regardless of what it requested. It's
+ difficult to imagine any circumstances where you would want to set
+ this. Protocol parameter ``remote-packet-size''. */
+#define IREMOTE_PACKSIZE (0)
+
+/* Local variables. */
+
+/* Next sequence number to send. */
+static int iGsendseq;
+
+/* Last sequence number that has been acked. */
+static int iGremote_ack;
+
+/* Last sequence number to be retransmitted. */
+static int iGretransmit_seq;
+
+/* Last sequence number we have received. */
+static int iGrecseq;
+
+/* Last sequence number we have acked. */
+static int iGlocal_ack;
+
+/* Window size to request (protocol parameter ``window''). */
+static int iGrequest_winsize = IWINDOW;
+
+/* Packet size to request (protocol parameter ``packet-size''). */
+static int iGrequest_packsize = IPACKSIZE;
+
+/* Remote window size (set during handshake). */
+static int iGremote_winsize;
+
+/* Forced remote window size (protocol parameter ``remote-window''). */
+static int iGforced_remote_winsize = IREMOTE_WINDOW;
+
+/* Remote segment size (set during handshake). This is one less than
+ the value in a packet header. */
+static int iGremote_segsize;
+
+/* Remote packet size (set based on iGremote_segsize). */
+static size_t iGremote_packsize;
+
+/* Forced remote packet size (protocol parameter
+ ``remote-packet-size''). */
+static int iGforced_remote_packsize = IREMOTE_PACKSIZE;
+
+/* Recieved control byte. */
+static int iGpacket_control;
+
+/* Number of times to retry the initial handshake. Protocol parameter
+ ``startup-retries''. */
+static int cGstartup_retries = CSTARTUP_RETRIES;
+
+/* Number of times to retry sending an initial control packet.
+ Protocol parameter ``init-retries''. */
+static int cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
+
+/* Timeout (seconds) for receiving an initial control packet.
+ Protocol parameter ``init-timeout''. */
+static int cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
+
+/* Timeout (seconds) for receiving a data packet. Protocol parameter
+ ``timeout''. */
+static int cGtimeout = CTIMEOUT;
+
+/* Maximum number of timeouts when receiving a data packet or
+ acknowledgement. Protocol parameter ``retries''. */
+static int cGretries = CRETRIES;
+
+/* Amount of garbage data we are prepared to see before giving up.
+ Protocol parameter ``garbage''. */
+static int cGgarbage_data = CGARBAGE;
+
+/* Maximum number of errors we are prepared to see before giving up.
+ Protocol parameter ``errors''. */
+static int cGmax_errors = CERRORS;
+
+/* Each time we receive this many packets succesfully, we decrement
+ the error level by one (protocol parameter ``error-decay''). */
+static int cGerror_decay = CERROR_DECAY;
+
+/* Whether to use shorter packets when possible. Protocol parameter
+ ``short-packets''. */
+static boolean fGshort_packets = TRUE;
+
+/* Protocol parameter commands. */
+struct uuconf_cmdtab asGproto_params[] =
+{
+ { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_winsize, NULL },
+ { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_packsize,
+ NULL },
+ { "startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGstartup_retries,
+ NULL },
+ { "init-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_timeout,
+ NULL },
+ { "init-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_retries,
+ NULL },
+ { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGtimeout, NULL },
+ { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGretries, NULL },
+ { "garbage", UUCONF_CMDTABTYPE_INT, (pointer) &cGgarbage_data, NULL },
+ { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cGmax_errors, NULL },
+ { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cGerror_decay, NULL },
+ { "remote-window", UUCONF_CMDTABTYPE_INT,
+ (pointer) &iGforced_remote_winsize, NULL },
+ { "remote-packet-size", UUCONF_CMDTABTYPE_INT,
+ (pointer) &iGforced_remote_packsize, NULL },
+ { "short-packets", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fGshort_packets,
+ NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+/* Statistics. */
+
+/* Number of packets we have sent. */
+static long cGsent_packets;
+
+/* Number of packets we have resent (these are not included in
+ cGsent_packets). */
+static long cGresent_packets;
+
+/* Number of packets we have delayed sending (these should not be
+ counted in cGresent_packets). */
+static long cGdelayed_packets;
+
+/* Number of packets we have received. */
+static long cGrec_packets;
+
+/* Number of packets rejected because the header was bad. */
+static long cGbad_hdr;
+
+/* Number of packets rejected because the checksum was bad. */
+static long cGbad_checksum;
+
+/* Number of packets received out of order. */
+static long cGbad_order;
+
+/* Number of packets rejected by receiver (number of RJ packets
+ received). */
+static long cGremote_rejects;
+
+/* The error level. This is the total number of errors as adjusted by
+ cGerror_decay. */
+static long cGerror_level;
+
+/* Each time we send an RJ, we can expect several out of order of
+ packets, because the other side will probably have sent a full
+ window by the time it sees the RJ. This variable keeps track of
+ the number of out of order packets we expect to see. We don't
+ count expected out of order packets against the error level. This
+ is reset to 0 when an in order packet is received. */
+static int cGexpect_bad_order;
+
+#if DEBUG > 1
+/* Control packet names used for debugging. */
+static const char * const azGcontrol[] =
+{"?0?", "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};
+#endif
+
+/* Local functions. */
+static boolean fgexchange_init P((struct sdaemon *qdaemon, int ictl,
+ int ival, int *piset));
+static boolean fgsend_control P((struct sdaemon *qdaemon, int ictl,
+ int ival));
+static char *zgadjust_ack P((int iseq));
+static boolean fgwait_for_packet P((struct sdaemon *qdaemon,
+ boolean freturncontrol, int ctimeout,
+ int cretries));
+static boolean fgsend_acks P((struct sdaemon *qdaemon));
+static boolean fggot_ack P((struct sdaemon *qdaemon, int iack));
+static boolean fgprocess_data P((struct sdaemon *qdaemon, boolean fdoacks,
+ boolean freturncontrol,
+ boolean *pfexit, size_t *pcneed,
+ boolean *pffound));
+static boolean fginit_sendbuffers P((boolean fallocate));
+static boolean fgcheck_errors P((struct sdaemon *qdaemon));
+static int igchecksum P((const char *zdata, size_t clen));
+static int igchecksum2 P((const char *zfirst, size_t cfirst,
+ const char *zsecond, size_t csecond));
+
+/* Start the protocol. This requires a three way handshake. Both sides
+ must send and receive an INITA packet, an INITB packet, and an INITC
+ packet. The INITA and INITC packets contain the window size, and the
+ INITB packet contains the packet size. */
+
+boolean
+fgstart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ int iseg;
+ int i;
+ boolean fgota, fgotb;
+
+ *pzlog = NULL;
+
+ /* The 'g' protocol requires a full eight bit interface. */
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
+ return FALSE;
+
+ iGsendseq = 1;
+ iGremote_ack = 0;
+ iGretransmit_seq = -1;
+ iGrecseq = 0;
+ iGlocal_ack = 0;
+ cGsent_packets = 0;
+ cGresent_packets = 0;
+ cGdelayed_packets = 0;
+ cGrec_packets = 0;
+ cGbad_hdr = 0;
+ cGbad_checksum = 0;
+ cGbad_order = 0;
+ cGremote_rejects = 0;
+ cGerror_level = 0;
+ cGexpect_bad_order = 0;
+
+ /* We must determine the segment size based on the packet size
+ which may have been modified by a protocol parameter command.
+ A segment size of 2^n is passed as n - 5. */
+ i = iGrequest_packsize;
+ iseg = -1;
+ while (i > 0)
+ {
+ ++iseg;
+ i >>= 1;
+ }
+ iseg -= 5;
+ if (iseg < 0 || iseg > 7)
+ {
+ ulog (LOG_ERROR, "Illegal packet size %d for '%c' protocol",
+ iGrequest_packsize, qdaemon->qproto->bname);
+ iseg = 1;
+ }
+
+ fgota = FALSE;
+ fgotb = FALSE;
+ for (i = 0; i < cGstartup_retries; i++)
+ {
+ if (fgota)
+ {
+ if (! fgsend_control (qdaemon, INITA, iGrequest_winsize))
+ return FALSE;
+ }
+ else
+ {
+ if (! fgexchange_init (qdaemon, INITA, iGrequest_winsize,
+ &iGremote_winsize))
+ continue;
+ }
+ fgota = TRUE;
+
+ if (fgotb)
+ {
+ if (! fgsend_control (qdaemon, INITB, iseg))
+ return FALSE;
+ }
+ else
+ {
+ if (! fgexchange_init (qdaemon, INITB, iseg, &iGremote_segsize))
+ continue;
+ }
+ fgotb = TRUE;
+
+ if (! fgexchange_init (qdaemon, INITC, iGrequest_winsize,
+ &iGremote_winsize))
+ continue;
+
+ /* We have succesfully connected. Determine the remote packet
+ size. */
+ iGremote_packsize = 1 << (iGremote_segsize + 5);
+
+ /* If the user requested us to force specific remote window and
+ packet sizes, do so now. */
+ if (iGforced_remote_winsize > 0
+ && iGforced_remote_winsize <= CMAXWINDOW)
+ iGremote_winsize = iGforced_remote_winsize;
+
+ if (iGforced_remote_packsize >= 32
+ && iGforced_remote_packsize <= 4096)
+ {
+ /* Force the value to a power of two. */
+ i = iGforced_remote_packsize;
+ iseg = -1;
+ while (i > 0)
+ {
+ ++iseg;
+ i >>= 1;
+ }
+ iGremote_packsize = 1 << iseg;
+ iGremote_segsize = iseg - 5;
+ }
+
+ /* Set up packet buffers to use. We don't do this until we know
+ the maximum packet size we are going to send. */
+ if (! fginit_sendbuffers (TRUE))
+ return FALSE;
+
+ *pzlog = zbufalc (sizeof "protocol '' packet size window " + 50);
+ sprintf (*pzlog, "protocol '%c' packet size %d window %d",
+ qdaemon->qproto->bname, (int) iGremote_packsize,
+ (int) iGremote_winsize);
+
+ return TRUE;
+ }
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgstart: Protocol startup failed");
+
+ return FALSE;
+}
+
+/* The 'G' protocol is identical to the 'g' protocol, except that
+ short packets are never supported. */
+
+boolean
+fbiggstart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ fGshort_packets = FALSE;
+ return fgstart (qdaemon, pzlog);
+}
+
+/* Exchange initialization messages with the other system.
+
+ A problem:
+
+ We send INITA; it gets received
+ We receive INITA
+ We send INITB; it gets garbled
+ We receive INITB
+
+ We have seen and sent INITB, so we start to send INITC. The other
+ side as sent INITB but not seen it, so it times out and resends
+ INITB. We will continue sending INITC and the other side will
+ continue sending INITB until both sides give up and start again
+ with INITA.
+
+ It might seem as though if we are sending INITC and receive INITB,
+ we should resend our INITB, but this could cause infinite echoing
+ of INITB on a long-latency line. Rather than risk that, I have
+ implemented a fast drop-back procedure. If we are sending INITB and
+ receive INITC, the other side has gotten ahead of us. We immediately
+ fail and begin again with INITA. For the other side, if we are
+ sending INITC and see INITA, we also immediately fail back to INITA.
+
+ Unfortunately, this doesn't work for the other case, in which we
+ are sending INITB but the other side has not yet seen INITA. As
+ far as I can see, if this happens we just have to wait until we
+ time out and resend INITA. */
+
+static boolean
+fgexchange_init (qdaemon, ictl, ival, piset)
+ struct sdaemon *qdaemon;
+ int ictl;
+ int ival;
+ int *piset;
+{
+ int i;
+
+ /* The three-way handshake should be independent of who initializes
+ it, but it seems that some versions of uucico assume that the
+ caller sends first and the callee responds. This only matters if
+ we are the callee and the first packet is garbled. If we send a
+ packet, the other side will assume that we must have seen the
+ packet they sent and will never time out and send it again.
+ Therefore, if we are the callee we don't send a packet the first
+ time through the loop. This can still fail, but should usually
+ work, and, after all, if the initialization packets are received
+ correctly there will be no problem no matter what we do. */
+ for (i = 0; i < cGexchange_init_retries; i++)
+ {
+ long itime;
+ int ctimeout;
+
+ if (qdaemon->fcaller || i > 0)
+ {
+ if (! fgsend_control (qdaemon, ictl, ival))
+ return FALSE;
+ }
+
+ itime = ixsysdep_time ((long *) NULL);
+ ctimeout = cGexchange_init_timeout;
+
+ do
+ {
+ long inewtime;
+
+ /* We pass 0 as the retry count to fgwait_for_packet because
+ we want to handle retries here and because if it retried
+ it would send a packet, which would be bad. */
+ if (! fgwait_for_packet (qdaemon, TRUE, ctimeout, 0))
+ break;
+
+ if (CONTROL_TT (iGpacket_control) == CONTROL)
+ {
+ if (CONTROL_XXX (iGpacket_control) == ictl)
+ {
+ *piset = CONTROL_YYY (iGpacket_control);
+
+ /* If we didn't already send our initialization
+ packet, send it now. */
+ if (! qdaemon->fcaller && i == 0)
+ {
+ if (! fgsend_control (qdaemon, ictl, ival))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ /* If the other side is farther along than we are,
+ we have lost a packet. Fail immediately back to
+ INITA (but don't fail if we are already doing INITA,
+ since that would count against cStart_retries more
+ than it should). */
+ if (CONTROL_XXX (iGpacket_control) < ictl && ictl != INITA)
+ return FALSE;
+
+ /* If we are sending INITC and we receive an INITA, the other
+ side has failed back (we know this because we have
+ seen an INITB from them). Fail back ourselves to
+ start the whole handshake over again. */
+ if (CONTROL_XXX (iGpacket_control) == INITA && ictl == INITC)
+ return FALSE;
+
+ /* As a special hack, if we are sending INITC and we
+ receive INITB, we update the segment size from the
+ packet. This permits a second INITB to override the
+ first one. It would be nice to do this in a cleaner
+ way. */
+ if (CONTROL_XXX (iGpacket_control) == INITB && ictl == INITC)
+ iGremote_segsize = CONTROL_YYY (iGpacket_control);
+ }
+
+ inewtime = ixsysdep_time ((long *) NULL);
+ ctimeout -= inewtime - itime;
+ }
+ while (ctimeout > 0);
+ }
+
+ return FALSE;
+}
+
+/* Shut down the protocol. */
+
+boolean
+fgshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ (void) fgsend_control (qdaemon, CLOSE, 0);
+ (void) fgsend_control (qdaemon, CLOSE, 0);
+ (void) fginit_sendbuffers (FALSE);
+
+ /* The count of sent packets may not be accurate, because some of
+ them may have not been sent yet if the connection failed in the
+ middle (the ones that counted for cGdelayed_packets). I don't
+ think it's worth being precise. */
+ ulog (LOG_NORMAL,
+ "Protocol '%c' packets: sent %ld, resent %ld, received %ld",
+ qdaemon->qproto->bname, cGsent_packets,
+ cGresent_packets - cGdelayed_packets, cGrec_packets);
+ if (cGbad_hdr != 0
+ || cGbad_checksum != 0
+ || cGbad_order != 0
+ || cGremote_rejects != 0)
+ ulog (LOG_NORMAL,
+ "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld",
+ cGbad_hdr, cGbad_checksum, cGbad_order, cGremote_rejects);
+
+ /* Reset all the parameters to their default values, so that the
+ protocol parameters used for this connection do not affect the
+ next one. */
+ iGrequest_winsize = IWINDOW;
+ iGrequest_packsize = IPACKSIZE;
+ cGstartup_retries = CSTARTUP_RETRIES;
+ cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
+ cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
+ cGtimeout = CTIMEOUT;
+ cGretries = CRETRIES;
+ cGgarbage_data = CGARBAGE;
+ cGmax_errors = CERRORS;
+ cGerror_decay = CERROR_DECAY;
+ iGforced_remote_winsize = IREMOTE_WINDOW;
+ iGforced_remote_packsize = IREMOTE_PACKSIZE;
+ fGshort_packets = TRUE;
+
+ return TRUE;
+}
+
+/* Send a command string. We send packets containing the string until
+ the entire string has been sent. Each packet is full. */
+
+/*ARGSUSED*/
+boolean
+fgsendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ size_t clen;
+ boolean fagain;
+
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgsendcmd: Sending command \"%s\"", z);
+
+ clen = strlen (z);
+
+ do
+ {
+ char *zpacket;
+ size_t cdummy;
+
+ zpacket = zggetspace (qdaemon, &cdummy);
+
+ if (clen < iGremote_packsize)
+ {
+ size_t csize;
+
+ /* If the remote packet size is larger than 64 (the default,
+ which may indicate an older UUCP package), try to fit
+ this command into a smaller packet. We still always send
+ a complete packet, though. */
+ if (iGremote_packsize <= 64 || ! fGshort_packets)
+ csize = iGremote_packsize;
+ else
+ {
+ csize = 32;
+ while (csize <= clen)
+ csize <<= 1;
+ }
+
+ memcpy (zpacket, z, clen);
+ bzero (zpacket + clen, csize - clen);
+ fagain = FALSE;
+
+ if (! fgsenddata (qdaemon, zpacket, csize, 0, 0, (long) 0))
+ return FALSE;
+ }
+ else
+ {
+ memcpy (zpacket, z, iGremote_packsize);
+ z += iGremote_packsize;
+ clen -= iGremote_packsize;
+ fagain = TRUE;
+
+ if (! fgsenddata (qdaemon, zpacket, iGremote_packsize,
+ 0, 0, (long) 0))
+ return FALSE;
+ }
+ }
+ while (fagain);
+
+ return TRUE;
+}
+
+/* We keep an array of buffers to retransmit as necessary. Rather
+ than waste static space on large buffer sizes, we allocate the
+ buffers once we know how large the other system expects them to be.
+ The sequence numbers used in the 'g' protocol are only three bits
+ long, so we allocate eight buffers and maintain a correspondence
+ between buffer index and sequence number. This always wastes some
+ buffer space, but it's easy to implement.
+
+ We leave room at the front of the buffer for the frame header and
+ two additional bytes. The two extra bytes are used for short
+ packets, which essentially use a longer header and shorter data.
+ We do this to avoid moving the data. We zero out any unused bytes
+ before the frame, so we can locate the real header given a buffer
+ by finding the first non-zero byte (which will be one of the first
+ three bytes in the buffer). */
+
+#define CSENDBUFFERS (CMAXWINDOW + 1)
+
+static char *azGsendbuffers[CSENDBUFFERS];
+
+static boolean
+fginit_sendbuffers (fallocate)
+ boolean fallocate;
+{
+ int i;
+
+ /* Free up any remaining old buffers. */
+ for (i = 0; i < CSENDBUFFERS; i++)
+ {
+ xfree ((pointer) azGsendbuffers[i]);
+ if (fallocate)
+ {
+ azGsendbuffers[i] = (char *) malloc (CFRAMELEN + 2
+ + iGremote_packsize);
+ if (azGsendbuffers[i] == NULL)
+ return FALSE;
+
+ /* This bzero might not seem necessary, since before we send
+ out each packet we zero out any non-data bytes. However,
+ if we receive an SRJ at the start of the conversation, we
+ will send out the packet before it has been set to
+ anything, thus sending the contents of our heap. We
+ avoid this by using bzero. */
+ bzero (azGsendbuffers[i], CFRAMELEN + 2 + iGremote_packsize);
+ }
+ else
+ azGsendbuffers[i] = NULL;
+ }
+ return TRUE;
+}
+
+/* Allocate a packet to send out. The return value of this function
+ must be filled in and passed to fgsenddata, or discarded. This
+ will ensure that the buffers and iGsendseq stay in synch. Set
+ *pclen to the amount of data to place in the buffer. */
+
+/*ARGSUSED*/
+char *
+zggetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = iGremote_packsize;
+ return azGsendbuffers[iGsendseq] + CFRAMELEN + 2;
+}
+
+/* Send out a data packet. This computes the checksum, sets up the
+ header, and sends the packet out. The argument zdata should point
+ to the return value of zggetspace. */
+
+/*ARGSIGNORED*/
+boolean
+fgsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+ char *z;
+ int itt, iseg;
+ size_t csize;
+ int iclr1, iclr2;
+ unsigned short icheck;
+
+ /* Set the initial length bytes. See the description at the definition
+ of SHORTDATA, above. */
+ itt = DATA;
+ csize = iGremote_packsize;
+ iseg = iGremote_segsize + 1;
+
+#if DEBUG > 0
+ if (cdata > csize)
+ ulog (LOG_FATAL, "fgsend_packet: Packet size too large");
+#endif
+
+ iclr1 = -1;
+ iclr2 = -2;
+ if (cdata < csize)
+ {
+ /* If the remote packet size is larger than 64, the default, we
+ can assume they can handle a smaller packet as well, which
+ will be more efficient to send. */
+ if (iGremote_packsize > 64 && fGshort_packets)
+ {
+ /* The packet size is 1 << (iseg + 4). */
+ iseg = 1;
+ csize = 32;
+ while (csize < cdata)
+ {
+ csize <<= 1;
+ ++iseg;
+ }
+ }
+
+ if (csize != cdata)
+ {
+ size_t cshort;
+
+ /* We have to add bytes which indicate how short the packet
+ is. We do this by pushing the header backward, which we
+ can do because we allocated two extra bytes for this
+ purpose. */
+ iclr2 = 0;
+ itt = SHORTDATA;
+ cshort = csize - cdata;
+ if (cshort <= 127)
+ {
+ --zdata;
+ zdata[0] = (char) cshort;
+ zdata[-1] = '\0';
+ bzero (zdata + cdata + 1, cshort - 1);
+ }
+ else
+ {
+ zdata -= 2;
+ zdata[0] = (char) (0x80 | (cshort & 0x7f));
+ zdata[1] = (char) (cshort >> 7);
+ bzero (zdata + cdata + 2, cshort - 2);
+ iclr1 = 0;
+ }
+ }
+ }
+
+ z = zdata - CFRAMELEN;
+
+ /* Zero out the preceding bytes, in case the last time this buffer
+ was used those bytes were used. We need to zero out the initial
+ bytes so that we can find the true start of the packet in
+ zgadjust_ack. */
+ z[iclr1] = '\0';
+ z[iclr2] = '\0';
+
+ z[IFRAME_DLE] = DLE;
+ z[IFRAME_K] = (char) iseg;
+
+ icheck = (unsigned short) igchecksum (zdata, csize);
+
+ /* We're just about ready to go. Wait until there is room in the
+ receiver's window for us to send the packet. We do this now so
+ that we send the correct value for the last packet received.
+ Note that if iGsendseq == iGremote_ack, this means that the
+ sequence numbers are actually 8 apart, since the packet could not
+ have been acknowledged before it was sent; this can happen when
+ the window size is 7. */
+ while (iGsendseq == iGremote_ack
+ || CSEQDIFF (iGsendseq, iGremote_ack) > iGremote_winsize)
+ {
+ if (! fgwait_for_packet (qdaemon, TRUE, cGtimeout, cGretries))
+ return FALSE;
+ }
+
+ /* Ack all packets up to the next one, since the UUCP protocol
+ requires that all packets be acked in order. */
+ while (CSEQDIFF (iGrecseq, iGlocal_ack) > 1)
+ {
+ iGlocal_ack = INEXTSEQ (iGlocal_ack);
+ if (! fgsend_control (qdaemon, RR, iGlocal_ack))
+ return FALSE;
+ }
+ iGlocal_ack = iGrecseq;
+
+ z[IFRAME_CONTROL] = (char) ((itt << 6) | (iGsendseq << 3) | iGrecseq);
+
+ iGsendseq = INEXTSEQ (iGsendseq);
+
+ icheck = ((unsigned short)
+ ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
+ z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
+ z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
+
+ z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
+ ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
+
+ /* If we're waiting for acks of retransmitted packets, then don't
+ send this packet yet. The other side may not be ready for it
+ yet. Instead, code in fggot_ack will send the outstanding
+ packets when an ack is received. */
+ ++cGsent_packets;
+
+ if (iGretransmit_seq != -1)
+ {
+ ++cGdelayed_packets;
+ return TRUE;
+ }
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fgsenddata: Sending packet %d (%d bytes)",
+ CONTROL_XXX (z[IFRAME_CONTROL]), cdata);
+
+ return fsend_data (qdaemon->qconn, z, CFRAMELEN + csize, TRUE);
+}
+
+/* Recompute the control byte and checksum of a packet so that it
+ includes the correct packet acknowledgement. This is called when a
+ packet is retransmitted to make sure the retransmission does not
+ confuse the other side. It returns a pointer to the start of the
+ packet, skipping the bytes that may be unused at the start of
+ azGsendbuffers[iseq]. */
+
+static char *
+zgadjust_ack (iseq)
+ int iseq;
+{
+ register char *z;
+ unsigned short icheck;
+
+ z = azGsendbuffers[iseq];
+ if (*z == '\0')
+ ++z;
+ if (*z == '\0')
+ ++z;
+
+ /* If the received packet number is the same, there is nothing
+ to do. */
+ if (CONTROL_YYY (z[IFRAME_CONTROL]) == iGrecseq)
+ return z;
+
+ /* Get the old checksum. */
+ icheck = (unsigned short) (((z[IFRAME_CHECKHIGH] & 0xff) << 8)
+ | (z[IFRAME_CHECKLOW] & 0xff));
+ icheck = ((unsigned short)
+ (((0xaaaa - icheck) ^ (z[IFRAME_CONTROL] & 0xff)) & 0xffff));
+
+ /* Update the control byte. */
+ z[IFRAME_CONTROL] = (char) ((z[IFRAME_CONTROL] &~ 07) | iGrecseq);
+
+ /* Create the new checksum. */
+ icheck = ((unsigned short)
+ ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
+ z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
+ z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
+
+ /* Update the XOR byte. */
+ z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
+ ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
+
+ return z;
+}
+
+/* Send a control packet. These are fairly simple to construct. It
+ seems reasonable to me that we should be able to send a control
+ packet at any time, even if the receive window is closed. In
+ particular, we don't want to delay when sending a CLOSE control
+ message. If I'm wrong, it can be changed easily enough. */
+
+static boolean
+fgsend_control (qdaemon, ixxx, iyyy)
+ struct sdaemon *qdaemon;
+ int ixxx;
+ int iyyy;
+{
+ char ab[CFRAMELEN];
+ int ictl;
+ unsigned short icheck;
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_PROTO) ||
+ (FDEBUGGING (DEBUG_ABNORMAL) && ixxx != RR))
+ ulog (LOG_DEBUG, "fgsend_control: Sending control %s %d",
+ azGcontrol[ixxx], iyyy);
+#endif
+
+ ab[IFRAME_DLE] = DLE;
+ ab[IFRAME_K] = KCONTROL;
+
+ ictl = (CONTROL << 6) | (ixxx << 3) | iyyy;
+ icheck = (unsigned short) (0xaaaa - ictl);
+ ab[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
+ ab[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
+
+ ab[IFRAME_CONTROL] = (char) ictl;
+
+ ab[IFRAME_XOR] = (char) (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
+ ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]);
+
+ return fsend_data (qdaemon->qconn, ab, (size_t) CFRAMELEN, TRUE);
+}
+
+/* Wait for data to come in. This continues processing until a
+ complete file or command has been received. */
+
+boolean
+fgwait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ return fgwait_for_packet (qdaemon, FALSE, cGtimeout, cGretries);
+}
+
+/* Get a packet. This is called when we have nothing to send, but
+ want to wait for a packet to come in. If freturncontrol is TRUE,
+ this will return after getting any control packet. Otherwise, it
+ will continue to receive packets until a complete file or a
+ complete command has been received. The timeout and the number of
+ retries are specified as arguments. The function returns FALSE if
+ an error occurs or if cretries timeouts of ctimeout seconds were
+ exceeded. */
+
+static boolean
+fgwait_for_packet (qdaemon, freturncontrol, ctimeout, cretries)
+ struct sdaemon *qdaemon;
+ boolean freturncontrol;
+ int ctimeout;
+ int cretries;
+{
+ int ctimeouts;
+ int cgarbage;
+ int cshort;
+
+ ctimeouts = 0;
+ cgarbage = 0;
+ cshort = 0;
+
+ while (TRUE)
+ {
+ boolean fexit;
+ size_t cneed;
+ boolean ffound;
+ size_t crec;
+
+ if (! fgprocess_data (qdaemon, TRUE, freturncontrol, &fexit,
+ &cneed, &ffound))
+ return FALSE;
+
+ if (fexit)
+ return TRUE;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fgwait_for_packet: Need %lu bytes",
+ (unsigned long) cneed);
+
+ if (ffound)
+ {
+ ctimeouts = 0;
+ cgarbage = 0;
+ }
+ else
+ {
+ if (cgarbage > cGgarbage_data)
+ {
+ ulog (LOG_ERROR, "Too much unrecognized data");
+ return FALSE;
+ }
+ }
+
+ if (! freceive_data (qdaemon->qconn, cneed, &crec, ctimeout, TRUE))
+ return FALSE;
+
+ cgarbage += crec;
+
+ if (crec != 0)
+ {
+ /* If we don't get enough data twice in a row, we may have
+ dropped some data and still be looking for the end of a
+ large packet. Incrementing iPrecstart will force
+ fgprocess_data to skip that packet and look through the
+ rest of the data. In some situations, this will be a
+ mistake. */
+ if (crec >= cneed)
+ cshort = 0;
+ else
+ {
+ ++cshort;
+ if (cshort > 1)
+ {
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ cshort = 0;
+ }
+ }
+ }
+ else
+ {
+ /* The read timed out. If we have an unacknowledged packet,
+ send it again. Otherwise, send an RJ with the last
+ packet we received correctly. */
+ ++ctimeouts;
+ if (ctimeouts > cretries)
+ {
+ if (cretries > 0)
+ ulog (LOG_ERROR, "Timed out waiting for packet");
+ return FALSE;
+ }
+
+ if (INEXTSEQ (iGremote_ack) != iGsendseq)
+ {
+ int inext;
+ char *zsend;
+
+ inext = INEXTSEQ (iGremote_ack);
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgwait_for_packet: Resending packet %d",
+ inext);
+
+ ++cGresent_packets;
+ zsend = zgadjust_ack (inext);
+ if (! fsend_data (qdaemon->qconn, zsend,
+ CFRAMELEN + CPACKLEN (zsend), TRUE))
+ return FALSE;
+ iGretransmit_seq = inext;
+ }
+ else
+ {
+ /* Send all pending acks first, to avoid confusing
+ the other side. */
+ if (iGlocal_ack != iGrecseq)
+ {
+ if (! fgsend_acks (qdaemon))
+ return FALSE;
+ }
+ if (! fgsend_control (qdaemon, RJ, iGrecseq))
+ return FALSE;
+ }
+ }
+ }
+}
+
+/* Send acks for all packets we haven't acked yet. */
+
+static boolean
+fgsend_acks (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ while (iGlocal_ack != iGrecseq)
+ {
+ iGlocal_ack = INEXTSEQ (iGlocal_ack);
+ if (! fgsend_control (qdaemon, RR, iGlocal_ack))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Handle an ack of a packet. According to Hanrahan's paper, this
+ acknowledges all previous packets. If this is an ack for a
+ retransmitted packet, continue by resending up to two more packets
+ following the retransmitted one. This should recover quickly from
+ a line glitch, while avoiding the problem of continual
+ retransmission. */
+
+static boolean
+fggot_ack (qdaemon, iack)
+ struct sdaemon *qdaemon;
+ int iack;
+{
+ int inext;
+ char *zsend;
+
+ /* We only decrement the error level if we are not retransmitting
+ packets. We want to catch a sudden downgrade in line quality as
+ fast as possible. */
+ if (cGerror_level > 0
+ && iGretransmit_seq == -1
+ && cGsent_packets % cGerror_decay == 0)
+ --cGerror_level;
+ cGexpect_bad_order = 0;
+
+ /* Each time packet 0 is acknowledged, we call uwindow_acked since a
+ new window has been acked. */
+ if (iack < iGremote_ack)
+ uwindow_acked (qdaemon, FALSE);
+
+ iGremote_ack = iack;
+
+ if (iGretransmit_seq == -1)
+ return TRUE;
+
+ inext = INEXTSEQ (iGretransmit_seq);
+ if (inext == iGsendseq)
+ iGretransmit_seq = -1;
+ else
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fggot_ack: Sending packet %d", inext);
+
+ ++cGresent_packets;
+ zsend = zgadjust_ack (inext);
+ if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend),
+ TRUE))
+ return FALSE;
+ inext = INEXTSEQ (inext);
+ if (inext == iGsendseq)
+ iGretransmit_seq = -1;
+ else
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fggot_ack: Sending packet %d", inext);
+
+ ++cGresent_packets;
+ zsend = zgadjust_ack (inext);
+ if (! fsend_data (qdaemon->qconn, zsend,
+ CFRAMELEN + CPACKLEN (zsend), TRUE))
+ return FALSE;
+ iGretransmit_seq = inext;
+ }
+ }
+
+ return TRUE;
+}
+
+/* See if we've received more than the permitted number of errors. If
+ we receive a bad packet, we can expect a window full (less one) of
+ out of order packets to follow, so we discount cGbad_order
+ accordingly. */
+
+static boolean
+fgcheck_errors (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ if (cGerror_level > cGmax_errors && cGmax_errors >= 0)
+ {
+ ulog (LOG_ERROR, "Too many '%c' protocol errors",
+ qdaemon->qproto->bname);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Process the receive buffer into a data packet, if possible. All
+ control packets are handled here. When a data packet is received,
+ fgprocess_data calls fgot_data with the data; if that sets its
+ pfexit argument to TRUE fgprocess_data will set *pfexit to TRUE and
+ return TRUE. Also, if the freturncontrol argument is TRUE
+ fgprocess_data will set *pfexit to TRUE and return TRUE. Otherwise
+ fgprocess_data will continue trying to process data. If some error
+ occurs, fgprocess_data will return FALSE. If there is not enough
+ data to form a complete packet, then *pfexit will be set to FALSE,
+ *pcneed will be set to the number of bytes needed to form a
+ complete packet (unless pcneed is NULL) and fgprocess_data will
+ return TRUE. If this function found a data packet, and pffound is
+ not NULL, it will set *pffound to TRUE; this can be used to tell
+ valid data from an endless stream of garbage and control packets.
+ If fdoacks is TRUE, received packets will be acknowledged;
+ otherwise they must be acknowledged later. */
+
+static boolean
+fgprocess_data (qdaemon, fdoacks, freturncontrol, pfexit, pcneed, pffound)
+ struct sdaemon *qdaemon;
+ boolean fdoacks;
+ boolean freturncontrol;
+ boolean *pfexit;
+ size_t *pcneed;
+ boolean *pffound;
+{
+ *pfexit = FALSE;
+ if (pffound != NULL)
+ *pffound = FALSE;
+
+ while (iPrecstart != iPrecend)
+ {
+ char ab[CFRAMELEN];
+ int i, iget, cwant;
+ unsigned short ihdrcheck, idatcheck;
+ const char *zfirst, *zsecond;
+ int cfirst, csecond;
+ boolean fduprr;
+
+ /* Look for the DLE which must start a packet. */
+ if (abPrecbuf[iPrecstart] != DLE)
+ {
+ char *zdle;
+
+ cfirst = iPrecend - iPrecstart;
+ if (cfirst < 0)
+ cfirst = CRECBUFLEN - iPrecstart;
+
+ zdle = memchr (abPrecbuf + iPrecstart, DLE, (size_t) cfirst);
+
+ if (zdle == NULL)
+ {
+ iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN;
+ continue;
+ }
+
+ /* We don't need % CRECBUFLEN here because zdle - (abPrecbuf
+ + iPrecstart) < cfirst <= CRECBUFLEN - iPrecstart. */
+ iPrecstart += zdle - (abPrecbuf + iPrecstart);
+ }
+
+ /* Get the first six bytes into ab. */
+ for (i = 0, iget = iPrecstart;
+ i < CFRAMELEN && iget != iPrecend;
+ i++, iget = (iget + 1) % CRECBUFLEN)
+ ab[i] = abPrecbuf[iget];
+
+ /* If there aren't six bytes, there is no packet. */
+ if (i < CFRAMELEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = CFRAMELEN - i;
+ return TRUE;
+ }
+
+ /* Make sure these six bytes start a packet. The check on
+ IFRAME_DLE is basically a debugging check, since the above
+ code should have ensured that it will never fail. If this is
+ not the start of a packet, bump iPrecstart and loop around to
+ look for another DLE. */
+ if (ab[IFRAME_DLE] != DLE
+ || ab[IFRAME_K] < 1
+ || ab[IFRAME_K] > 9
+ || ab[IFRAME_XOR] != (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
+ ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL])
+ || CONTROL_TT (ab[IFRAME_CONTROL]) == ALTCHAN)
+ {
+ ++cGbad_hdr;
+ ++cGerror_level;
+
+ DEBUG_MESSAGE4 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad header: K %d TT %d XOR byte %d calc %d",
+ ab[IFRAME_K] & 0xff,
+ CONTROL_TT (ab[IFRAME_CONTROL]),
+ ab[IFRAME_XOR] & 0xff,
+ (ab[IFRAME_K]
+ ^ ab[IFRAME_CHECKLOW]
+ ^ ab[IFRAME_CHECKHIGH]
+ ^ ab[IFRAME_CONTROL]) & 0xff);
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ /* The zfirst and cfirst pair point to the first set of data for
+ this packet; the zsecond and csecond point to the second set,
+ in case the packet wraps around the end of the buffer. */
+ zfirst = abPrecbuf + iPrecstart + CFRAMELEN;
+ cfirst = 0;
+ zsecond = NULL;
+ csecond = 0;
+
+ if (ab[IFRAME_K] == KCONTROL)
+ {
+ /* This is a control packet. It should not have any data. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
+ {
+ ++cGbad_hdr;
+ ++cGerror_level;
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad header: control packet with data");
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ idatcheck = (unsigned short) (0xaaaa - ab[IFRAME_CONTROL]);
+ cwant = 0;
+ }
+ else
+ {
+ int cinbuf;
+ unsigned short icheck;
+
+ /* This is a data packet. It should not be type CONTROL. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL)
+ {
+ ++cGbad_hdr;
+ ++cGerror_level;
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad header: data packet is type CONTROL");
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+ cinbuf -= CFRAMELEN;
+
+ /* Make sure we have enough data. If we don't, wait for
+ more. */
+
+ cwant = (int) CPACKLEN (ab);
+ if (cinbuf < cwant)
+ {
+ if (pcneed != NULL)
+ *pcneed = cwant - cinbuf;
+ return TRUE;
+ }
+
+ /* Set up the data pointers and compute the checksum. */
+ if (iPrecend >= iPrecstart)
+ cfirst = cwant;
+ else
+ {
+ cfirst = CRECBUFLEN - (iPrecstart + CFRAMELEN);
+ if (cfirst >= cwant)
+ cfirst = cwant;
+ else if (cfirst > 0)
+ {
+ zsecond = abPrecbuf;
+ csecond = cwant - cfirst;
+ }
+ else
+ {
+ /* Here cfirst is non-positive, so subtracting from
+ abPrecbuf will actually skip the appropriate number
+ of bytes at the start of abPrecbuf. */
+ zfirst = abPrecbuf - cfirst;
+ cfirst = cwant;
+ }
+ }
+
+ if (csecond == 0)
+ icheck = (unsigned short) igchecksum (zfirst, (size_t) cfirst);
+ else
+ icheck = (unsigned short) igchecksum2 (zfirst, (size_t) cfirst,
+ zsecond,
+ (size_t) csecond);
+
+ idatcheck = ((unsigned short)
+ (((0xaaaa - (icheck ^ (ab[IFRAME_CONTROL] & 0xff)))
+ & 0xffff)));
+ }
+
+ ihdrcheck = (unsigned short) (((ab[IFRAME_CHECKHIGH] & 0xff) << 8)
+ | (ab[IFRAME_CHECKLOW] & 0xff));
+
+ if (ihdrcheck != idatcheck)
+ {
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad checksum: header 0x%x, data 0x%x",
+ ihdrcheck, idatcheck);
+
+ ++cGbad_checksum;
+ ++cGerror_level;
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ /* If the checksum failed for a data packet, then if it was
+ the one we were expecting send an RJ, otherwise ignore
+ it. Previously if this code got the wrong packet number
+ it would send an RR, but that may confuse some Telebit
+ modems and it doesn't help in any case since the receiver
+ will probably just ignore the RR as a duplicate (that's
+ basically what this code does). If we totally missed the
+ packet we will time out and send an RJ in the function
+ fgwait_for_packet above. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
+ {
+ /* Make sure we've acked everything up to this point. */
+ if (iGrecseq != iGlocal_ack)
+ {
+ if (! fgsend_acks (qdaemon))
+ return FALSE;
+ }
+
+ /* If this is the packet we wanted, tell the sender that
+ it failed. */
+ if (CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq))
+ {
+ if (! fgsend_control (qdaemon, RJ, iGrecseq))
+ return FALSE;
+ cGexpect_bad_order += iGrequest_winsize - 1;
+ }
+ }
+
+ /* We can't skip the packet data after this, because if we
+ have lost incoming bytes the next DLE will be somewhere
+ in what we thought was the packet data. */
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ /* We have a packet; remove the processed bytes from the receive
+ buffer. */
+ iPrecstart = (iPrecstart + cwant + CFRAMELEN) % CRECBUFLEN;
+
+ /* Store the control byte for the handshake routines. */
+ iGpacket_control = ab[IFRAME_CONTROL] & 0xff;
+
+ /* Annoyingly, some UUCP packages appear to send an RR packet
+ rather than an RJ packet when they want a packet to be
+ resent. If we get a duplicate RR, we treat it as an RJ. */
+ fduprr = FALSE;
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL
+ && CONTROL_XXX (ab[IFRAME_CONTROL]) == RR
+ && iGremote_ack == CONTROL_YYY (ab[IFRAME_CONTROL])
+ && INEXTSEQ (iGremote_ack) != iGsendseq)
+ {
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Treating duplicate RR as RJ");
+ fduprr = TRUE;
+ }
+
+ /* Update the received sequence number from the yyy field of a
+ data packet or an RR control packet. If we've been delaying
+ sending packets until we received an ack, this may send out
+ some packets. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL
+ || CONTROL_XXX (ab[IFRAME_CONTROL]) == RR)
+ {
+ if (! fggot_ack (qdaemon, CONTROL_YYY (ab[IFRAME_CONTROL])))
+ return FALSE;
+ }
+
+ /* If this isn't a control message, make sure we have received
+ the expected packet sequence number, acknowledge the packet
+ if it's the right one, and process the data. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
+ {
+ if (CONTROL_XXX (ab[IFRAME_CONTROL]) != INEXTSEQ (iGrecseq))
+ {
+ /* We got the wrong packet number. */
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Got packet %d; expected %d",
+ CONTROL_XXX (ab[IFRAME_CONTROL]),
+ INEXTSEQ (iGrecseq));
+
+ if (cGexpect_bad_order > 0)
+ --cGexpect_bad_order;
+ else
+ {
+ ++cGbad_order;
+ ++cGerror_level;
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+ }
+
+ /* This code used to send an RR to encourage the other
+ side to get back in synch, but that may confuse some
+ Telebit modems and does little good in any case,
+ since the other side will probably just ignore it
+ anyhow (that's what this code does). */
+ continue;
+ }
+
+ /* We got the packet we expected. */
+ ++cGrec_packets;
+ if (cGerror_level > 0
+ && cGrec_packets % cGerror_decay == 0)
+ --cGerror_level;
+ cGexpect_bad_order = 0;
+
+ iGrecseq = INEXTSEQ (iGrecseq);
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fgprocess_data: Got packet %d", iGrecseq);
+
+ /* Tell the caller that we found something. */
+ if (pffound != NULL)
+ *pffound = TRUE;
+
+ /* If we are supposed to do acknowledgements here, send back
+ an RR packet. */
+ if (fdoacks)
+ {
+ if (! fgsend_acks (qdaemon))
+ return FALSE;
+ }
+
+ /* If this is a short data packet, adjust the data pointers
+ and lengths. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) == SHORTDATA)
+ {
+ int cshort, cmove;
+
+ if ((zfirst[0] & 0x80) == 0)
+ {
+ cshort = zfirst[0] & 0xff;
+ cmove = 1;
+ }
+ else
+ {
+ int cbyte2;
+
+ if (cfirst > 1)
+ cbyte2 = zfirst[1] & 0xff;
+ else
+ cbyte2 = zsecond[0] & 0xff;
+ cshort = (zfirst[0] & 0x7f) + (cbyte2 << 7);
+ cmove = 2;
+ }
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fgprocess_data: Packet short by %d",
+ cshort);
+
+ /* Adjust the start of the buffer for the bytes used
+ by the count. */
+ if (cfirst > cmove)
+ {
+ zfirst += cmove;
+ cfirst -= cmove;
+ }
+ else
+ {
+ zfirst = zsecond + (cmove - cfirst);
+ cfirst = csecond - (cmove - cfirst);
+ csecond = 0;
+ }
+
+ /* Adjust the length of the buffer for the bytes we are
+ not supposed to consider. */
+ cshort -= cmove;
+ if (csecond >= cshort)
+ csecond -= cshort;
+ else
+ {
+ cfirst -= cshort - csecond;
+ csecond = 0;
+ }
+
+#if DEBUG > 0
+ /* This should not happen, but just in case. */
+ if (cfirst < 0)
+ cfirst = 0;
+#endif
+ }
+
+ if (! fgot_data (qdaemon, zfirst, (size_t) cfirst,
+ zsecond, (size_t) csecond,
+ -1, -1, (long) -1,
+ INEXTSEQ (iGremote_ack) == iGsendseq,
+ pfexit))
+ return FALSE;
+
+ /* If fgot_data told us that we were finished, get out. */
+ if (*pfexit)
+ return TRUE;
+
+ /* If we've been asked to return control packets, get out
+ now. */
+ if (freturncontrol)
+ {
+ *pfexit = TRUE;
+ return TRUE;
+ }
+
+ continue;
+ }
+
+ /* Handle control messages here. */
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_PROTO)
+ || (FDEBUGGING (DEBUG_ABNORMAL)
+ && CONTROL_XXX (ab[IFRAME_CONTROL]) != RR))
+ ulog (LOG_DEBUG, "fgprocess_data: Got control %s %d",
+ azGcontrol[CONTROL_XXX (ab[IFRAME_CONTROL])],
+ CONTROL_YYY (ab[IFRAME_CONTROL]));
+#endif
+
+ switch (CONTROL_XXX (ab[IFRAME_CONTROL]))
+ {
+ case CLOSE:
+ /* The other side has closed the connection. */
+ if (fLog_sighup)
+ {
+ ulog (LOG_ERROR, "Received unexpected CLOSE packet");
+ (void) fgsend_control (qdaemon, CLOSE, 0);
+ }
+ return FALSE;
+ case RR:
+ /* Acknowledge receipt of a packet. This was already handled
+ above, unless we are treating it as RJ. */
+ if (! fduprr)
+ break;
+ /* Fall through. */
+ case RJ:
+ /* The other side dropped a packet. Begin retransmission with
+ the packet following the one acknowledged. We don't
+ retransmit the packets immediately, but instead wait
+ for the first one to be acked. This prevents us from
+ sending an entire window several times if we get several
+ RJ packets. */
+ iGremote_ack = CONTROL_YYY (ab[IFRAME_CONTROL]);
+ iGretransmit_seq = INEXTSEQ (iGremote_ack);
+ if (iGretransmit_seq == iGsendseq)
+ iGretransmit_seq = -1;
+ else
+ {
+ char *zpack;
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Remote reject: next %d resending %d",
+ iGsendseq, iGretransmit_seq);
+
+ ++cGresent_packets;
+ ++cGremote_rejects;
+ ++cGerror_level;
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+ zpack = zgadjust_ack (iGretransmit_seq);
+ if (! fsend_data (qdaemon->qconn, zpack,
+ CFRAMELEN + CPACKLEN (zpack),
+ TRUE))
+ return FALSE;
+ }
+ break;
+ case SRJ:
+ /* Selectively reject a particular packet. This is not used
+ by UUCP, but it's easy to support. */
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Selective reject of %d",
+ CONTROL_YYY (ab[IFRAME_CONTROL]));
+ {
+ char *zpack;
+
+ ++cGresent_packets;
+ ++cGremote_rejects;
+ ++cGerror_level;
+ zpack = zgadjust_ack (CONTROL_YYY (ab[IFRAME_CONTROL]));
+ if (! fsend_data (qdaemon->qconn, zpack,
+ CFRAMELEN + CPACKLEN (zpack),
+ TRUE))
+ return FALSE;
+ }
+ break;
+ case INITC:
+ case INITB:
+ case INITA:
+ /* Ignore attempts to reinitialize. */
+ break;
+ }
+
+ /* If we've been asked to return control packets, get out. */
+ if (freturncontrol)
+ {
+ *pfexit = TRUE;
+ return TRUE;
+ }
+
+ /* Loop around to look for the next packet, if any. */
+ }
+
+ /* There is no data left in the receive buffer. */
+ if (pcneed != NULL)
+ *pcneed = CFRAMELEN;
+ return TRUE;
+}
+
+/* Compute the 'g' protocol checksum. This is unfortunately rather
+ awkward. This is the most time consuming code in the entire
+ program. It's also not a great checksum, since it can be fooled
+ by some single bit errors. */
+
+/* Sorry about this knavery, but it speeds up the VAX code
+ significantly. It would be better to rewrite the whole routine in
+ assembler. */
+#ifdef __GNUC__
+#ifdef __vax__
+#define VAX_ASM 1
+#endif
+#endif
+
+#if VAX_ASM
+#define ROTATE(i) \
+ asm ("cvtwl %1,%0\n\trotl $1,%0,%0" : "=g" (i) : "g" (i))
+#else
+#define ROTATE(i) i += i + ((i & 0x8000) >> 15)
+#endif
+
+#define ITERATION \
+ /* Rotate ichk1 left. */ \
+ ROTATE (ichk1); \
+ \
+ /* The guts of the checksum. */ \
+ b = BUCHAR (*z++); \
+ if (b != 0) \
+ { \
+ ichk1 &= 0xffff; \
+ ichk1 += b; \
+ ichk2 += ichk1 ^ c; \
+ if ((ichk1 >> 16) != 0) \
+ ichk1 ^= ichk2; \
+ } \
+ else \
+ { \
+ ichk2 += ichk1 ^ c; \
+ ichk1 ^= ichk2; \
+ } \
+ \
+ --c
+
+static int
+igchecksum (z, c)
+ register const char *z;
+ register size_t c;
+{
+ register unsigned long ichk1, ichk2;
+
+ ichk1 = 0xffff;
+ ichk2 = 0;
+
+ do
+ {
+ register unsigned int b;
+
+ ITERATION;
+ ITERATION;
+ ITERATION;
+ ITERATION;
+ }
+ while (c > 0);
+
+ return ichk1 & 0xffff;
+}
+
+/* We use a separate function compute the checksum if the block is
+ split around the end of the receive buffer since it occurs much
+ less frequently and the checksum is already high up in the
+ profiles. These functions are almost identical, and this one
+ actually only has a few more instructions in the inner loop. */
+
+static int
+igchecksum2 (zfirst, cfirst, zsecond, csecond)
+ const char *zfirst;
+ size_t cfirst;
+ const char *zsecond;
+ size_t csecond;
+{
+ register unsigned long ichk1, ichk2;
+ register const char *z;
+ register size_t c;
+
+ z = zfirst;
+ c = cfirst + csecond;
+
+ ichk1 = 0xffff;
+ ichk2 = 0;
+
+ do
+ {
+ register unsigned int b;
+
+ ITERATION;
+
+ /* If the first buffer has been finished, switch to the second. */
+ --cfirst;
+ if (cfirst == 0)
+ z = zsecond;
+ }
+ while (c > 0);
+
+ return ichk1 & 0xffff;
+}