1/* proty.c
2   The 'y' protocol.
3
4   Copyright (C) 1994, 1995, 2002, 2003 Jorge Cwik and Ian Lance Taylor
5
6   This file is part of the Taylor UUCP package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21   */
22
23#include "uucp.h"
24
25#if USE_RCS_ID
26const char proty_id[] = "$Id: proty.c,v 1.9 2003/05/29 06:00:49 ian Rel $";
27#endif
28
29#include "uudefs.h"
30#include "uuconf.h"
31#include "conn.h"
32#include "trans.h"
33#include "system.h"
34#include "prot.h"
35
36/* The 'y' protocol, and this implementation, was written and designed
37   by Jorge Cwik <jorge@satlink.net>.  Some of the routines, and the
38   coding style in general, were taken verbatim or adapted from other
39   Taylor UUCP modules.  Mark Delany made the initial testings and
40   helped in portability issues.
41
42   This protocol does not perform any kind of error correction or flow
43   control.  It does do error checking.  It does not require an end to
44   end reliable link.  It is recommended for error-free (also called
45   semi-reliable) connections as provided by error correcting modems.
46   It needs an eight bit clean channel and some kind of flow control
47   at the lower layers, typically RTS/CTS hardware flow control.
48
49   The flow of the file transmission is completely unidirectional.
50   There are no ACKs or NAKs outside file boundaries. This makes it
51   very suitable for half duplex modulations (like PEP) and
52   connections with very long delays, like multihop satellite links.  */
53
54/* This protocol uses 16 bit little-endian ints in the packet header.  */
55#define	FROMLITTLE(p)  (((p)[0] & 0xff) + (((p)[1] & 0xff) << 8))
56#define	TOLITTLE(p, i) ((p)[0] = (i) & 0xff, (p)[1] = ((i) >> 8) & 0xff)
57
58/* The buffer and packet size we use.  */
59#define CYBUFSIZE (1024)
60#define IYPACKSIZE (1024)
61
62/* The offset in the buffer to the data.  */
63#define CYFRAMELEN (6)
64
65/* Offsets in a packet header.  */
66#define YFRAME_SEQ_OFF (0)
67#define YFRAME_LEN_OFF (2)
68#define YFRAME_CTL_OFF (2)
69#define YFRAME_CHK_OFF (4)
70
71/* Offsets in a packet header viewed as an array of shorts.  */
72#define YFRAME_SEQ (0)
73#define YFRAME_LEN (1)
74#define YFRAME_CTL (1)
75#define YFRAME_CHK (2)
76
77/* The default timeout.  */
78#define	CYTIMEOUT (60)
79
80/* Control packet types.  */
81#define YPKT_ACK (0xFFFE)
82#define YPKT_ERR (0xFFFD)
83#define YPKT_BAD (0xFFFC)
84
85/* The protocol version number.  */
86#define Y_VERSION (1)
87
88/* When the protocol starts up, it transmit the following information:
89     1 byte version
90     1 byte packet size
91     2 byte flags (none currently defined)
92   Future revision may expand the structure as long as these members
93   keep their current offset.  */
94#define Y_INIT_HDR_LEN (4)
95#define Y_INIT_HDR_VERSION_OFF (0)
96#define Y_INIT_HDR_PKTSIZE_OFF (1)
97#define Y_INIT_HDR_FLAGS_OFF (2)
98
99/* The initialization length of the lowest accepted version.  */
100#define MIN_Y_SYNC (4)
101
102/* Not strictly needed, but I would not want to accept a 32k sync pkt.  */
103#define MAX_Y_SYNC IYPACKSIZE
104
105/* Local and remote packet sizes (we actually use the same value for
106   both).  */
107static size_t iYlocal_packsize = IYPACKSIZE;
108static size_t iYremote_packsize = IYPACKSIZE;
109
110/* Local and remote packet sequence numbers.  */
111static unsigned short iYlocal_pktnum;
112static unsigned short iYremote_pktnum;
113
114/* The timeout.  */
115static int cYtimeout = CYTIMEOUT;
116
117/* Transmitter buffer.  */
118static char *zYbuf;
119
120/* Protocol parameters.  */
121
122struct uuconf_cmdtab asYproto_params[] =
123{
124  { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cYtimeout, NULL },
125  { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iYlocal_packsize, NULL },
126  { NULL, 0, NULL, NULL }
127};
128
129/* Local functions.  */
130
131static boolean fyxchg_syncs P((struct sdaemon *qdaemon));
132static boolean fywait_for_packet P((struct sdaemon *qdaemon,
133				    boolean *pfexit));
134static unsigned short iychecksum P((const char *z, size_t c));
135static unsigned short iychecksum2 P((const char *zfirst, size_t cfirst,
136				     const char *zsecond, size_t csecond));
137static boolean fywait_for_header P((struct sdaemon *qdaemon,
138				    unsigned short header[3], int timeout));
139static boolean fysend_pkt P((struct sdaemon *qdaemon,
140			     const void *zdata, size_t cdata));
141static boolean fysend_control P((struct sdaemon *qdaemon,
142				 int itype));
143static boolean fyread_data P((struct sdaemon *qdaemon, size_t clen,
144			      int timeout));
145
146/* Exchange sync packets at protocol startup.  */
147
148static boolean
149fyxchg_syncs (qdaemon)
150     struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
151{
152  char inithdr[Y_INIT_HDR_LEN];
153  unsigned short header[3];
154  unsigned short ichk;
155  size_t clen, cfirst;
156  int rpktsize;
157
158  /* Send our configuration.  We could use only one array (for local
159     and remote).  But this is safer in case the code changes and
160     depend on separate ones.  */
161
162  inithdr[Y_INIT_HDR_VERSION_OFF] = Y_VERSION;
163  inithdr[Y_INIT_HDR_PKTSIZE_OFF] = iYlocal_packsize >> 8;
164  TOLITTLE (inithdr + Y_INIT_HDR_FLAGS_OFF, 0);
165
166  if (! fysend_pkt (qdaemon, inithdr, Y_INIT_HDR_LEN))
167    return FALSE;
168
169  if (! fywait_for_header (qdaemon, header, cYtimeout))
170    return FALSE;
171
172  DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fyxchg_syncs: Got sync header");
173  clen = header[YFRAME_LEN];
174
175  if (clen < MIN_Y_SYNC || clen > MAX_Y_SYNC)
176    {
177      ulog (LOG_ERROR, "Bad 'y' protocol sync packet length");
178      return FALSE;
179    }
180
181  /* It may be better to integrate this code with fywait_for_packet.  */
182  if (! fyread_data (qdaemon, clen, cYtimeout))
183    return FALSE;
184
185  cfirst = CRECBUFLEN - iPrecstart;
186  ichk = iychecksum2 (abPrecbuf + iPrecstart, cfirst,
187		      abPrecbuf, clen - cfirst);
188
189  rpktsize = BUCHAR (abPrecbuf[(iPrecstart + 1) % CRECBUFLEN]);
190
191  /* Future versions of the protocol may need to check and react
192     according to the version number.  */
193
194  if (rpktsize == 0 || header[YFRAME_CHK] != ichk)
195    {
196      ulog (LOG_ERROR, "Bad 'y' protocol sync packet");
197      return FALSE;
198    }
199
200  iYremote_packsize = rpktsize << 8;
201
202  /* Some may want to do this different and in effect the protocol
203     support this.  But I like the idea that the packet size would be
204     the same in both directions.  This allows the caller to select
205     both packet sizes without changing the configuration at the
206     server.  */
207  if (iYremote_packsize > iYlocal_packsize)
208    iYremote_packsize = iYlocal_packsize;
209
210  iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
211  return TRUE;
212}
213
214/* Start the protocol.  */
215
216boolean
217fystart (qdaemon, pzlog)
218     struct sdaemon *qdaemon;
219     char **pzlog;
220{
221  *pzlog = NULL;
222
223  /* This should force, or at least enable if available, RTS/CTS
224     hardware flow control !! */
225
226  /* The 'y' protocol requires an eight bit clean link */
227  if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
228		   STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
229    return FALSE;
230
231  iYlocal_pktnum = iYremote_pktnum = 0;
232
233  /* Only multiple of 256 sizes are allowed */
234  iYlocal_packsize &= ~0xff;
235  if (iYlocal_packsize < 256 || iYlocal_packsize > (16*1024))
236    iYlocal_packsize = IYPACKSIZE;
237
238  /* Exhange SYNC packets */
239  if (! fyxchg_syncs (qdaemon))
240    {
241      /* Restore defaults */
242      cYtimeout = CYTIMEOUT;
243      iYlocal_packsize = IYPACKSIZE;
244      return FALSE;
245    }
246
247  zYbuf = (char *) xmalloc (CYBUFSIZE + CYFRAMELEN);
248  return TRUE;
249}
250
251/* Shutdown the protocol.  */
252
253boolean
254fyshutdown (qdaemon)
255     struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
256{
257  xfree ((pointer) zYbuf);
258  zYbuf = NULL;
259  cYtimeout = CYTIMEOUT;
260  iYlocal_packsize = IYPACKSIZE;
261  return TRUE;
262}
263
264/* Send a command string.  We send packets containing the string until
265   the entire string has been sent, including the zero terminator.  */
266
267/*ARGSUSED*/
268boolean
269fysendcmd (qdaemon, z, ilocal, iremote)
270     struct sdaemon *qdaemon;
271     const char *z;
272     int ilocal ATTRIBUTE_UNUSED;
273     int iremote ATTRIBUTE_UNUSED;
274{
275  size_t clen;
276
277  DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fysendcmd: Sending command \"%s\"", z);
278
279  clen = strlen (z) + 1;
280
281  while (clen > 0)
282    {
283      size_t csize;
284
285      csize = clen;
286      if (csize > iYremote_packsize)
287	csize = iYremote_packsize;
288
289      if (! fysend_pkt (qdaemon, z, csize))
290	return FALSE;
291
292      z += csize;
293      clen -= csize;
294    }
295
296  return TRUE;
297}
298
299/* Get space to be filled with data.  We always use zYbuf, which was
300   allocated from the heap.  */
301
302char *
303zygetspace (qdaemon, pclen)
304     struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
305     size_t *pclen;
306{
307  *pclen = iYremote_packsize;
308  return zYbuf + CYFRAMELEN;
309}
310
311/* Send out a data packet.  */
312
313boolean
314fysenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
315     struct sdaemon *qdaemon;
316     char *zdata;
317     size_t cdata;
318     int ilocal ATTRIBUTE_UNUSED;
319     int iremote ATTRIBUTE_UNUSED;
320     long ipos ATTRIBUTE_UNUSED;
321{
322#if DEBUG > 0
323  if (cdata > iYremote_packsize)
324    ulog (LOG_FATAL, "fysend_packet: Packet size too large");
325#endif
326
327  TOLITTLE (zYbuf + YFRAME_SEQ_OFF, iYlocal_pktnum);
328  ++iYlocal_pktnum;
329  TOLITTLE (zYbuf + YFRAME_LEN_OFF, cdata);
330  TOLITTLE (zYbuf + YFRAME_CHK_OFF, iychecksum (zdata, cdata));
331
332  /* We pass FALSE to fsend_data since we don't expect the other side
333     to be sending us anything just now.  */
334  return fsend_data (qdaemon->qconn, zYbuf, cdata + CYFRAMELEN, FALSE);
335}
336
337/* Wait for data to come in and process it until we've finished a
338   command or a file.  */
339
340boolean
341fywait (qdaemon)
342     struct sdaemon *qdaemon;
343{
344  boolean fexit = FALSE;
345
346  while (! fexit)
347    {
348      if (! fywait_for_packet (qdaemon, &fexit))
349	return FALSE;
350    }
351  return TRUE;
352}
353
354/* File level routines
355   We could handle this inside the other public routines,
356   but this is cleaner and better for future expansions */
357
358boolean
359fyfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
360     struct sdaemon *qdaemon;
361     struct stransfer *qtrans ATTRIBUTE_UNUSED;
362     boolean fstart;
363     boolean fsend;
364     long cbytes ATTRIBUTE_UNUSED;
365     boolean *pfhandled;
366{
367  unsigned short header[3];
368
369  *pfhandled = FALSE;
370
371  if (! fstart)
372    {
373      if (fsend)
374	{
375	  /* It is critical that the timeout here would be long
376	     enough.  We have just sent a full file without any kind
377	     of flow control at the protocol level.  The traffic may
378	     be buffered in many places of the link, and the remote
379	     may take a while until cathing up.  */
380	  if (! fywait_for_header (qdaemon, header, cYtimeout * 2))
381	    return FALSE;
382
383	  if (header[YFRAME_CTL] != (unsigned short) YPKT_ACK)
384	    {
385	      DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
386			      "fyfile: Error from remote: 0x%04X", header[1]);
387	      ulog (LOG_ERROR, "Received 'y' protocol error from remote");
388	      return FALSE;
389	    }
390	}
391      else
392	{
393	  /* This is technically not requireed.  But I've put this in
394	     the protocol to allow easier expansions.  */
395	  return fysend_control (qdaemon, YPKT_ACK);
396	}
397    }
398
399  return TRUE;
400}
401
402/* Send a control packet, not used during the normal file
403   transmission.  */
404
405static boolean
406fysend_control (qdaemon, itype)
407     struct sdaemon *qdaemon;
408     int itype;
409{
410  char header[CYFRAMELEN];
411
412  TOLITTLE (header + YFRAME_SEQ_OFF, iYlocal_pktnum);
413  iYlocal_pktnum++;
414  TOLITTLE (header + YFRAME_CTL_OFF, itype);
415  TOLITTLE (header + YFRAME_CHK_OFF, 0);
416
417  return fsend_data (qdaemon->qconn, header, CYFRAMELEN, FALSE);
418}
419
420/* Private function to send a packet.  This one doesn't need the data
421   to be in the buffer provided by zygetspace.  I've found it worth
422   for avoiding memory copies.  Somebody may want to do it otherwise */
423
424static boolean
425fysend_pkt (qdaemon, zdata, cdata)
426     struct sdaemon *qdaemon;
427     const void *zdata;
428     size_t cdata;
429{
430  char header[CYFRAMELEN];
431
432  TOLITTLE (header + YFRAME_SEQ_OFF, iYlocal_pktnum);
433  iYlocal_pktnum++;
434  TOLITTLE (header + YFRAME_LEN_OFF, cdata);
435  TOLITTLE (header + YFRAME_CHK_OFF, iychecksum (zdata, cdata));
436
437  if (! fsend_data (qdaemon->qconn, header, CYFRAMELEN, FALSE))
438    return FALSE;
439  return fsend_data (qdaemon->qconn, zdata, cdata, FALSE);
440}
441
442/* Wait until enough data arrived from the comm line.  This protocol
443   doesn't need to perform any kind of action while waiting.  */
444
445static boolean
446fyread_data (qdaemon, clen, timeout)
447     struct sdaemon *qdaemon;
448     size_t clen;
449     int timeout;
450{
451  int cinbuf;
452  size_t crec;
453
454  cinbuf = iPrecend - iPrecstart;
455  if (cinbuf < 0)
456    cinbuf += CRECBUFLEN;
457
458  if ((size_t) cinbuf < clen)
459    {
460      if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec,
461			   timeout, TRUE))
462	return FALSE;
463      cinbuf += crec;
464      if ((size_t) cinbuf < clen)
465	{
466	  if (! freceive_data (qdaemon->qconn, clen - cinbuf, &crec,
467			       timeout, TRUE))
468	    return FALSE;
469	}
470      cinbuf += crec;
471      if ((size_t) cinbuf < clen)
472	{
473	  ulog (LOG_ERROR, "Timed out waiting for data");
474	  return FALSE;
475	}
476    }
477
478  return TRUE;
479}
480
481/* Receive a remote packet header, check for correct sequence number.  */
482
483static boolean
484fywait_for_header (qdaemon, header, timeout)
485     struct sdaemon *qdaemon;
486     unsigned short header[3];
487     int timeout;
488{
489  if (! fyread_data (qdaemon, CYFRAMELEN, timeout))
490    return FALSE;
491
492  /* Somebody may want to optimize this in a portable way.  I'm not
493     sure it's worth, but the output by gcc for the portable construct
494     is so bad (even with optimization), that I couldn't resist.  */
495
496  if (iPrecstart <= (CRECBUFLEN - CYFRAMELEN))
497    {
498      header[0] = FROMLITTLE (abPrecbuf + iPrecstart);
499      header[1] = FROMLITTLE (abPrecbuf + iPrecstart + 2);
500      header[2] = FROMLITTLE (abPrecbuf + iPrecstart + 4);
501    }
502  else
503    {
504      register int i, j;
505
506      for (i = j = 0; j < CYFRAMELEN; i++, j += 2)
507	{
508	  header[i] =
509	    (((abPrecbuf[(iPrecstart + j + 1) % CRECBUFLEN] & 0xff) << 8)
510	     + (abPrecbuf[(iPrecstart + j) % CRECBUFLEN] & 0xff));
511	}
512    }
513
514  iPrecstart = (iPrecstart + CYFRAMELEN) % CRECBUFLEN;
515
516  DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO,
517		  "fywait_for_header: Got header: 0x%04X, 0x%04X, 0x%04X",
518		  header[0], header[1], header[2]);
519
520  if (header[YFRAME_SEQ] != iYremote_pktnum++)
521    {
522      ulog (LOG_ERROR, "Incorrect 'y' packet sequence");
523      fysend_control (qdaemon, YPKT_BAD);
524      return FALSE;
525    }
526
527  return TRUE;
528}
529
530/* Receive a remote data packet */
531
532static boolean
533fywait_for_packet (qdaemon, pfexit)
534     struct sdaemon *qdaemon;
535     boolean *pfexit;
536{
537  unsigned short header[3], ichk;
538  size_t clen, cfirst;
539
540  if (! fywait_for_header (qdaemon, header, cYtimeout))
541    return FALSE;
542
543  clen = header[YFRAME_LEN];
544  if (clen == 0 && pfexit != NULL)
545    {
546      /* I Suppose the pointers could be NULL ??? */
547      return fgot_data (qdaemon, abPrecbuf, 0, abPrecbuf, 0,
548			-1, -1, (long) -1, TRUE, pfexit);
549    }
550
551  if (clen & 0x8000)
552    {
553      DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
554		      "fywait_for_packet: Error from remote: 0x%04X",
555		      header[YFRAME_CTL]);
556      ulog (LOG_ERROR, "Remote error packet");
557      return FALSE;
558    }
559
560  /* This is really not neccessary.  But if this check is removed,
561     take in mind that the packet may be up to 32k long.  */
562  if (clen > iYlocal_packsize)
563    {
564      ulog (LOG_ERROR, "Packet too large");
565      return FALSE;
566    }
567
568  if (! fyread_data (qdaemon, clen, cYtimeout))
569    return FALSE;
570
571  cfirst = CRECBUFLEN - iPrecstart;
572  if (cfirst > clen)
573    cfirst = clen;
574
575  if (cfirst == clen)
576    ichk = iychecksum (abPrecbuf + iPrecstart, clen);
577  else
578    ichk = iychecksum2 (abPrecbuf + iPrecstart, cfirst,
579			abPrecbuf, clen - cfirst);
580  if (header[YFRAME_CHK] != ichk)
581    {
582      DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
583		      "fywait_for_packet: Bad checksum 0x%x != 0x%x",
584		      header[YFRAME_CHK], ichk);
585      fysend_control (qdaemon, YPKT_ERR);
586      ulog (LOG_ERROR, "Checksum error");
587      return FALSE;
588    }
589
590  if (pfexit != NULL
591      && ! fgot_data (qdaemon, abPrecbuf + iPrecstart, cfirst,
592		      abPrecbuf, clen - cfirst,
593		      -1, -1, (long) -1, TRUE, pfexit))
594    return FALSE;
595
596  iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
597
598  return TRUE;
599}
600
601/* Compute 16 bit checksum */
602
603#ifdef __GNUC__
604#ifdef __i386__
605#define I386_ASM
606#endif
607#endif
608
609#ifdef I386_ASM
610#define ROTATE(i) \
611	asm ("rolw $1,%0" : "=g" (i) : "g" (i))
612#else
613#define ROTATE(i) i += i + ((i & 0x8000) >> 15)
614#endif
615
616static unsigned short
617iychecksum (z, c)
618     register const char *z;
619     register size_t c;
620{
621  register unsigned short ichk;
622
623  ichk = 0xffff;
624
625  while (c-- > 0)
626    {
627      ROTATE (ichk);
628      ichk += BUCHAR (*z++);
629    }
630
631  return ichk;
632}
633
634static unsigned short
635iychecksum2 (zfirst, cfirst, zsecond, csecond)
636	const char *zfirst;
637	size_t cfirst;
638	const char *zsecond;
639	size_t csecond;
640{
641  register unsigned short ichk;
642  register const char *z;
643  register size_t c;
644
645  z = zfirst;
646  c = cfirst + csecond;
647
648  ichk = 0xffff;
649
650  while (c-- > 0)
651    {
652      ROTATE (ichk);
653      ichk += BUCHAR (*z++);
654
655      /* If the first buffer has been finished, switch to the second.  */
656      if (--cfirst == 0)
657	z = zsecond;
658    }
659
660  return ichk;
661}
662