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