home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume07 / tinytcp < prev    next >
Encoding:
Text File  |  1988-09-11  |  46.8 KB  |  1,573 lines

  1. Subject:  v07i001:  A tiny set of TCP routines (tinytcp)
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4.  
  5. Submitted by: pyramid!decwrl!imagen!geof (Geof Cooper)
  6. Mod.sources: Volume 7, Issue 1
  7. Archive-name: tinytcp
  8.  
  9. [  I have not tried to use this code.  --r$  ]
  10.  
  11. #!/bin/sh
  12. : "This is a shell archive, meaning:                              "
  13. : "1. Remove everything above the #! /bin/sh line.                "
  14. : "2. Save the resulting test in a file.                          "
  15. : "3. Execute the file with /bin/sh (not csh) to create the files:"
  16. : "    README"
  17. : "    arp.c"
  18. : "    sed.c"
  19. : "    sed.h"
  20. : "    tinyftp.c"
  21. : "    tinytcp.c"
  22. : "    tinytcp.h"
  23. : "This archive created:  Tue Jul 22 09:59:35 PDT 1986 "
  24. echo file: README
  25. sed 's/^X//' >README << 'END-of-README'
  26. X
  27. X    TinyTcp Public Domain Release
  28. X
  29. XThe files in this release contain a simple implementation of TCP & FTP,
  30. Xsuitable for burning into ROM.  It is, in effect, a big hack put together
  31. Xin two or three days.  It works for us, though, and you might like it,
  32. Xtoo.  We use it to boot our image processor by retrieving a load file
  33. Xusing the standard FTP server.
  34. X
  35. XThe code is intended to use the buffers from the Ethernet interface,
  36. Xalthough shadow buffers could be hidden in the driver with no problem.
  37. XOn one home-brew board here and didn't support byte-mode access to it.
  38. XHence, the code takes pains to fool our local compiler into never
  39. Xgenerating byte- or bit-mode instructions.
  40. X
  41. XSince we have already burned the TCP into ROM, it is unlikely that
  42. Xany further development will take place at IMAGEN.  I would be willing
  43. Xto act as a clearinghouse for future improvements to the code.
  44. X
  45. XWarning: the code was intended for a 68000, and doesn't have
  46. Xany byte swapping support in it.  Shouldn't be too hard to add
  47. Xif you want to run it on a little-endian machine.
  48. X
  49. XPlease note (and honor) the copyright on each file:
  50. X
  51. X  Copyright (C) 1986, IMAGEN Corporation
  52. X  "This code may be duplicated in whole or in part provided that [1] there
  53. X   is no commercial gain involved in the duplication, and [2] that this
  54. X   copyright notice is preserved on all copies.  Any other duplication
  55. X   requires written notice of the author (Geoffrey H. Cooper)."
  56. X
  57. X...in other words, do what you want with the code, but don't sell it and
  58. Xgive IMAGEN and me credit on whatever you produce.  If you develop a product
  59. Xbased on this code and want to sell it, give me a call and I'll be reasonable.
  60. X
  61. XThis code is distributed "as is." Neither the author nor IMAGEN Corporation
  62. Xguarantees that it works or meets any particular specifications.  The act
  63. Xof distributing this software does not represent a commitment by either the
  64. Xauthor nor IMAGEN Corporation to provide maintenance or consulting services
  65. Xrelated to this software.
  66. X
  67. XBut feel free to ask me any questions that you can't answer for yourself.
  68. X
  69. X    - Geof Cooper
  70. X      Imagen Corporation
  71. X      (408)986 9400
  72. X      [imagen!geof@decwrl.dec.com, {decwrl,saber}!imagen!geof]
  73. X      April 16, 1986
  74. X
  75. XThe package requires some system support:
  76. X
  77. X    clock_ValueRough() - should be a procedure that returns the current
  78. X        value of a millisecond clock.  The procedure is called frequently,
  79. X        so that interrupts are not needed to service the clock.  Our
  80. X        implementation polls the real time timer and assumes that it is
  81. X        called frequently enough so that it doesn't miss clock ticks (Since
  82. X        the timer is only used for network timeouts, it doesn't really matter
  83. X        if it does miss clock ticks, of course).  Systems without a clock
  84. X        could probably get by with a procedure that increments a static
  85. X        variable and returns it, by adjusting the timeout constants in the
  86. X        program.
  87. X
  88. X    Network driver - some network interface  driver is needed.  A driver for a
  89. X        3Com multibus (ethernet) board is included; this board isn't made
  90. X        anymore (at least not by 3Com, there are still compatible boards
  91. X        being made, but I don't know of any being popularly distributed),
  92. X        so you'll probably need to write a driver for the board in your system.
  93. X
  94. X
  95. XGuide to source files:
  96. X
  97. X    sed.c - Simple Ethernet Driver - Driver for 3Com multibus card.  If you
  98. X            have another type of Ethernet board, you can use this driver as
  99. X            a template.
  100. X
  101. X    sed.h - header file for the above.
  102. X
  103. X    arp.c - Implementation of Address Resolution Protocol.  Note that there
  104. X            is no arp "mapping" per se.  The higher level code (tcp, in this
  105. X            case) is required to keep track of internet and ethernet addresses.
  106. X
  107. X    tinytcp.c - Implementation of TCP.
  108. X
  109. X    tinytcp.h - Header file for above, and for everything else.
  110. X
  111. X    tinyftp.c - Implementation of FTP, only allows files to be retrieved,
  112. X                not sent.
  113. END-of-README
  114. echo file: arp.c
  115. sed 's/^X//' >arp.c << 'END-of-arp.c'
  116. X/* 
  117. X * SAR: Simple Address Resolution Protocol Implementation
  118. X * Written by Geoffrey Cooper, September 27, 1983
  119. X * 
  120. X * This package implements a very simple version of the Plummer Address
  121. X * Resolution Protocol (RFC 826).  It allows clients to resolve Internet
  122. X * addresses into Ethernet addresses, and knows how to respond to an
  123. X * address resolution request (when the transmit buffer is free).
  124. X * 
  125. X * Routines:
  126. X * 
  127. X *  sar_CheckPacket( pb ) => 1, if ARP packet and processed, 0 otherwise
  128. X *  sar_MapIn2Eth( ina, ethap ) => 1 if did it, 0 if couldn't.
  129. X *
  130. X * Copyright (C) 1983, 1986 IMAGEN Corporation
  131. X *  "This code may be duplicated in whole or in part provided that [1] there
  132. X *   is no commercial gain involved in the duplication, and [2] that this
  133. X *   copyright notice is preserved on all copies.  Any other duplication
  134. X *   requires written notice of the author (Geoffrey H. Cooper)."
  135. X * 
  136. X */
  137. X#include "tinytcp.h"
  138. X
  139. Xsar_CheckPacket(ap)
  140. X    register arp_Header *ap;
  141. X{
  142. X    register arp_Header *op;
  143. X
  144. X    if ( ap->hwType != arp_TypeEther || /* have ethernet hardware, */
  145. X         ap->protType != 0x800 ||       /* and internet software, */
  146. X         ap->opcode != ARP_REQUEST ||   /* and be a resolution req. */
  147. X         ap->dstIPAddr != sin_lclINAddr /* for my addr. */
  148. X       ) return ( 0 );                  /* .... or we ignore it. */
  149. X
  150. X    /* format response. */
  151. X    op = (arp_Header *)sed_FormatPacket(ap->srcEthAddr, 0x806);
  152. X    op->hwType = arp_TypeEther;
  153. X    op->protType = 0x800;
  154. X    op->hwProtAddrLen = (sizeof(eth_HwAddress) << 8) + sizeof(in_HwAddress);
  155. X    op->opcode = ARP_REPLY;
  156. X    op->srcIPAddr = sin_lclINAddr;
  157. X    MoveW(sed_lclEthAddr, op->srcEthAddr, sizeof(eth_HwAddress));
  158. X    ap->dstIPAddr = op->srcIPAddr;
  159. X    MoveW(ap->srcEthAddr, op->dstEthAddr, sizeof(eth_HwAddress));
  160. X
  161. X    sed_Send(sizeof(arp_Header));
  162. X    
  163. X    return ( 1 );
  164. X}
  165. X
  166. X/* 
  167. X * Do an address resolution bit.
  168. X */
  169. Xsar_MapIn2Eth(ina, ethap)
  170. X    longword ina;
  171. X    eth_HwAddress *ethap;
  172. X{
  173. X    register arp_Header *op;
  174. X    extern in_HwAddress sin_lclINAddr;
  175. X    register i;
  176. X    longword endTime;
  177. X    longword rxMitTime;
  178. X
  179. X    sed_Receive( 0 );
  180. X    endTime = clock_ValueRough() + 2000;
  181. X    while ( endTime > clock_ValueRough() ) {
  182. X        op = (arp_Header *)sed_FormatPacket(&sed_ethBcastAddr[0], 0x806);
  183. X        op->hwType = arp_TypeEther;
  184. X        op->protType = 0x800;
  185. X        op->hwProtAddrLen = (sizeof(eth_HwAddress) << 8) + sizeof(in_HwAddress);
  186. X        op->opcode = ARP_REQUEST;
  187. X        op->srcIPAddr = sin_lclINAddr;
  188. X        MoveW(sed_lclEthAddr, op->srcEthAddr, sizeof(eth_HwAddress));
  189. X        op->dstIPAddr = ina;
  190. X
  191. X        /* ...and send the packet */
  192. X        sed_Send( sizeof(arp_Header) );
  193. X
  194. X        rxMitTime = clock_ValueRough() + 250;
  195. X        while ( rxMitTime > clock_ValueRough() ) {
  196. X            op = (arp_Header *)sed_IsPacket();
  197. X            if ( op ) {
  198. X                if ( sed_CheckPacket(op, 0x806) == 1 &&
  199. X                    op->protType == 0x800 &&
  200. X                    op->srcIPAddr == ina &&
  201. X                    op->opcode == ARP_REPLY ) {
  202. X                    MoveW(op->srcEthAddr, ethap, sizeof(eth_HwAddress));
  203. X                    return ( 1 );
  204. X                }
  205. X                sed_Receive(op);
  206. X            }
  207. X        }
  208. X    }
  209. X    return ( 0 );
  210. X}
  211. END-of-arp.c
  212. echo file: sed.c
  213. sed 's/^X//' >sed.c << 'END-of-sed.c'
  214. X/* 
  215. X * Ethernet Driver.
  216. X * A Very Simple set of ethernet driver primitives.  The ethernet (3com Mbus)
  217. X * interface is controlled by busy-waiting, the application is handed the
  218. X * location of on-board packet buffers, and allowed to fill in the
  219. X * transmit buffer directly.  The interface is entirely blocking.
  220. X * 
  221. X * Written March, 1986 by Geoffrey Cooper
  222. X *
  223. X * Copyright (C) 1986, IMAGEN Corporation
  224. X *  "This code may be duplicated in whole or in part provided that [1] there
  225. X *   is no commercial gain involved in the duplication, and [2] that this
  226. X *   copyright notice is preserved on all copies.  Any other duplication
  227. X *   requires written notice of the author (Geoffrey H. Cooper)."
  228. X * 
  229. X * Primitives:
  230. X *  sed_Init()  -- Initialize the package
  231. X *  sed_FormatPacket( destEAddr ) => location of transmit buffer
  232. X *  sed_Send( pkLength ) -- send the packet that is in the transmit buffer
  233. X *  sed_Receive( recBufLocation ) -- enable receiving packets.
  234. X *  sed_IsPacket() => location of packet in receive buffer
  235. X *  sed_CheckPacket( recBufLocation, expectedType )
  236. X *
  237. X * Global Variables:
  238. X *  sed_lclEthAddr -- Ethernet address of this host.
  239. X *  sed_ethBcastAddr -- Ethernet broadcast address.
  240. X */
  241. X
  242. X#include "tinytcp.h"
  243. X#include "sed.h"
  244. X
  245. X#define en10pages        ((en10size) >> pageshift)
  246. X
  247. Xoctet *sed_va;                          /* virtual address of ethernet card */
  248. Xeth_HwAddress sed_lclEthAddr;           /* local ethernet address */
  249. Xeth_HwAddress sed_ethBcastAddr;         /* Ethernet broadcast address */
  250. XBOOL sed_respondARPreq;                 /* controls responses to ARP req's */
  251. Xchar bufAinUse, bufBinUse;              /* tell whether bufs are in use */
  252. X
  253. X/* 
  254. X *  Initialize the Ethernet Interface, and this package.  Enable input on
  255. X * both buffers.
  256. X */
  257. Xsed_Init()
  258. X{
  259. X    int recState;
  260. X    register i, j;
  261. X
  262. X    recState = 7;                       /* == mine + broad - errors */
  263. X
  264. X    /* Map the Ethernet Interface in, and initialize sed_va */
  265. X    sed_va = (octet *)SED3CVA;        /* our virtual addr */
  266. X
  267. X    /* Map memory for 3Com board (must be 8k boundary) */
  268. X    /* INSERT CODE HERE */
  269. X    map_ethernet_board();
  270. X
  271. X    /* Reset the Ethernet controller */
  272. X    MECSR(sed_va) = RESET;
  273. X    for (i=0; i<10; i++);           /* delay a bit... */
  274. X
  275. X    /* just copy on-board ROM to on-board RAM, to use std. address */
  276. X    Move(MEAROM(sed_va), sed_lclEthAddr, 6);
  277. X    Move(sed_lclEthAddr, MEARAM(sed_va), 6);
  278. X    
  279. X    MECSR(sed_va) |= AMSW;        /* and tell board we did it */
  280. X
  281. X    /*
  282. X     * and initialize the exported variable which gives the Eth broadcast
  283. X     * address, for everyone else to use. 
  284. X     */
  285. X    for (i=0; i<3; i++) sed_ethBcastAddr[i] = 0xFFFF;
  286. X    
  287. X    /* accept packets addressed for us and broadcast packets */
  288. X
  289. X    MECSR(sed_va) = (MECSR(sed_va)&~PA) | recState;
  290. X
  291. X    /* declare both buffers in use... */
  292. X    bufAinUse = bufBinUse = TRUE;
  293. X
  294. X}
  295. X
  296. X/* 
  297. X * Format an ethernet header in the transmit buffer, and say where it is.
  298. X * Note that because of the way the 3Com interface works, we need to know
  299. X * how long the packet is before we know where to put it.  The solution is
  300. X * that we format the packet at the BEGINNING of the transmit buffer, and
  301. X * later copy it (carefully) to where it belongs.  Another hack would be
  302. X * to be inefficient about the size of the packet to be sent (always send
  303. X * a larger ethernet packet than you need to, but copying should be ok for
  304. X * now.
  305. X */
  306. Xoctet *
  307. Xsed_FormatPacket( destEAddr, ethType )
  308. X        register octet *destEAddr;
  309. X{
  310. X    register octet *xMitBuf;
  311. X    
  312. X    xMitBuf = &((octet *)MEXBUF(sed_va))[-0x800];
  313. X    Move( destEAddr, xMitBuf, 6 );
  314. X    Move( sed_lclEthAddr, xMitBuf + 6, 6 );
  315. X    *((short *)(xMitBuf+12)) = ethType;
  316. X    return ( xMitBuf+14 );
  317. X}
  318. X
  319. X/*
  320. X *  Send a packet out over the ethernet.  The packet is sitting at the
  321. X * beginning of the transmit buffer.  The routine returns when the
  322. X * packet has been successfully sent.
  323. X */
  324. Xsed_Send( pkLengthInOctets )
  325. X    register int pkLengthInOctets;
  326. X{
  327. X    register octet *fromO, *toO;
  328. X    register pkLength;
  329. X    register csr;
  330. X
  331. X    pkLengthInOctets += 14;             /* account for Ethernet header */
  332. X    pkLengthInOctets = (pkLengthInOctets + 1) & (~1);
  333. X
  334. X    if (pkLengthInOctets < E10P_MIN) 
  335. X        pkLengthInOctets = E10P_MIN; /* and min. ethernet len */
  336. X
  337. X    /*  and copy the packet where it belongs */
  338. X    pkLength = pkLengthInOctets;
  339. X    fromO = &((octet *)MEXBUF(sed_va))[-0x800] + pkLength;
  340. X    toO = ((octet *)MEXBUF(sed_va));
  341. X
  342. X    while ( pkLength-- ) *--toO = *--fromO;
  343. X    
  344. X    /* send the packet */
  345. X    
  346. X    MEXHDR(sed_va) = 2048 - pkLengthInOctets;
  347. X    MECSR(sed_va) |= TBSW;
  348. X
  349. X    /* and wait until it has really been sent. */
  350. X
  351. X    for (pkLength=0; pkLength < 15; pkLength++) {
  352. X        while ( (! ((csr = MECSR(sed_va)) & JAM)) && (csr & TBSW) )
  353. X            ;
  354. X        if (csr & JAM ) {
  355. X            /* Ethernet Collision detected... */
  356. X#ifdef DEBUG
  357. X            printf("sed: JAM: MECSR=%x\n", csr);
  358. X#endif
  359. X            MEBACK(sed_va) = clock_ValueRough();
  360. X            MECSR(sed_va) |= JAM;
  361. X        } else break;
  362. X    }
  363. X    if ( pkLength == 15 ) SysBug("Go and Buy a New Ethernet Interface.");
  364. X
  365. X    /*  else we sent the packet ok. */
  366. X}
  367. X
  368. X/* 
  369. X *  Enable the Ethernet interface to receive packets from the network.  If
  370. X * the argument is zero, enable both buffers.  If the argument is nonzero,
  371. X * take it as the address of the buffer to be enabled.
  372. X */
  373. Xsed_Receive( recBufLocation )
  374. X    octet *recBufLocation;
  375. X{
  376. X    word enables = 0;
  377. X
  378. X    if (recBufLocation == 0) {
  379. X        bufAinUse = FALSE;
  380. X        bufBinUse = FALSE;
  381. X        enables = (ABSW | BBSW);
  382. X    }
  383. X    recBufLocation -= 16;
  384. X    if (recBufLocation == ((octet *)MEAHDR(sed_va))) {
  385. X        bufAinUse = FALSE;
  386. X        enables = ABSW;
  387. X        }
  388. X    if (recBufLocation == ((octet *)MEBHDR(sed_va))) {
  389. X        bufBinUse = FALSE;
  390. X        enables = BBSW;
  391. X    }
  392. X
  393. X    MECSR (sed_va) |= enables;
  394. X}
  395. X
  396. X/* 
  397. X * Test for the arrival of a packet on the Ethernet interface.  The packet may
  398. X * arrive in either buffer A or buffer B; the location of the packet is
  399. X * returned.  If no packet is returned withing 'timeout' milliseconds,
  400. X * then the routine returns zero.
  401. X * 
  402. X * Note: ignores ethernet errors.  may occasionally return something
  403. X * which was received in error.
  404. X */
  405. X
  406. Xoctet *
  407. Xsed_IsPacket()
  408. X{
  409. X    register oldStatus;
  410. X    register octet *pb;
  411. X    
  412. X    pb = 0;
  413. X    if ( ! bufAinUse && (MECSR(sed_va)&ABSW) == 0 ) 
  414. X        pb = (octet *)MEAHDR(sed_va);
  415. X    if ( ! pb && ! bufBinUse && (MECSR(sed_va)&BBSW) == 0 )
  416. X        pb = (octet *)MEBHDR(sed_va);
  417. X
  418. X    if ( pb ) {
  419. X        if ( ((octet *)pb) == ((octet *)MEAHDR(sed_va)) ) bufAinUse = 1;
  420. X        else bufBinUse = 1;
  421. X        pb += 16;                       /* get past the ethernet header */
  422. X    }
  423. X
  424. X    return ( pb );
  425. X}
  426. X
  427. X/* 
  428. X *  Check to make sure that the packet that you received was the one that
  429. X * you expected to get.
  430. X */
  431. Xsed_CheckPacket( recBufLocation, expectedType )
  432. X    word *recBufLocation;
  433. X    word expectedType;
  434. X{
  435. X    register recHeader = recBufLocation[-8];
  436. X    if ( (recHeader&R_ERROR) != 0 ||
  437. X         (recHeader&R_OFFSET) < E10P_MIN ) {
  438. X         return ( -1 );
  439. X    }
  440. X    if ( recBufLocation[-1] != expectedType ) {
  441. X        return ( 0 );
  442. X    }
  443. X    return (1);
  444. X}
  445. END-of-sed.c
  446. echo file: sed.h
  447. sed 's/^X//' >sed.h << 'END-of-sed.h'
  448. X/* 
  449. X *  Header file for very simple ethernet driver, based on 3Com Multibus
  450. X *  board.
  451. X *
  452. X * Copyright (C) 1986, IMAGEN Corporation
  453. X *  "This code may be duplicated in whole or in part provided that [1] there
  454. X *   is no commercial gain involved in the duplication, and [2] that this
  455. X *   copyright notice is preserved on all copies.  Any other duplication
  456. X *   requires written notice of the author (Geoffrey H. Cooper)."
  457. X */
  458. X
  459. X#define    en10size        (8*1024)    /* size of interface memory */
  460. X#define    en10pages    ((en10size) >> pageshift)
  461. X#define E10P_MIN    60              /* Minimum Ethernet packet size */
  462. X
  463. X/* 
  464. X * The position of the 3Com interface in virtual memory.  If we're
  465. X * Running the bootloader function, then it must be in the last 8k
  466. X * of virtual addresses.
  467. X */
  468. X#ifdef BOOTLOADER
  469. X#define SED3CVA vm_3ComAdr /* hack, only need pb68.h if bootloader */
  470. X#endif
  471. X#ifndef SED3CVA
  472. X#define SED3CVA 0x1c000
  473. X#endif
  474. X
  475. X/* 10Mb Ethernet interface addresses */
  476. X
  477. X#define    MECSR(eth_va)    *(word*)(((octet *) eth_va) + 0x0)
  478. X#define    MEBACK(eth_va)    *(word*)(((octet *) eth_va) + 0x2)
  479. X#define    MEAROM(eth_va)    (word*)(((octet *) eth_va) + 0x400)
  480. X#define    MEARAM(eth_va)    (word*)(((octet *) eth_va) + 0x600)
  481. X#define    MEXHDR(eth_va)    *(word*)(((octet *) eth_va) + 0x800)
  482. X#define    MEXBUF(eth_va)    (word*)(((octet *) eth_va) + 0x1000)
  483. X#define    MEAHDR(eth_va)    (word*)(((octet *) eth_va) + 0x1000)
  484. X#define    MEBHDR(eth_va)    (word*)(((octet *) eth_va) + 0x1800)
  485. X
  486. X/* control/status register fields */
  487. X
  488. X#define    BBSW        0x8000    /* Buffer B belongs to Network */
  489. X#define    ABSW        0x4000    /* Buffer A belongs to Network */
  490. X#define    TBSW        0x2000    /* Transmit buffer belongs to Network */
  491. X#define    JAM        0x1000    /* Set when transmit collision */
  492. X#define    AMSW        0x0800    /* 
  493. X#define    RBBA        0x0400    /* Oldest received packet is in B */
  494. X/*#define    UNUSED        0x0200 */
  495. X#define    RESET        0x0100    /* Reset the controller */
  496. X#define    BINT        0x0080    /* Interrupt when BBSW=>0 (packet in B) */
  497. X#define    AINT        0x0040    /* Interrupt when ABSW=>0 (packet in A) */
  498. X#define    TINT        0x0020    /* Interrupt when TBSW=>0 (transmit done) */
  499. X#define    JINT        0x0010    /* Enable interrupts when JAM=>1 */
  500. X#define    PA        0x000F    /* Which packets should be received? */
  501. X#define INTENABLS    0x00F0
  502. X
  503. X/*
  504. X * Receiver Header Fields: 
  505. X * The receiver header is the first (short) word of the receive buffer.  It
  506. X * includes such information as how big the packet is, whether it was a
  507. X * broadcast, whether there was an error in receiving it, etc.
  508. X */
  509. X
  510. X#define    R_FCS        0x8000    /* fcs error */
  511. X#define    R_BCAST        0x4000    /* packet was NOT a broadcast */
  512. X#define    R_RANGE        0x2000    /* range error (size of pkt?) */
  513. X#define    R_MATCH        0x1000    /* packet is multicast (i.e., address
  514. X                   received is not that of the interface) */
  515. X#define    R_FRAME        0x0800    /* framing error */
  516. X#define    R_ERROR        0x8800    /* was there any error */
  517. X#define    R_OFFSET    0x07FF    /* packet length + 1 word */
  518. X
  519. Xextern octet *sed_FormatPacket(), *sed_WaitPacket();
  520. X
  521. X#ifdef BOOTLOADER
  522. X#define ConsPrintf printf
  523. X#endif
  524. END-of-sed.h
  525. echo file: tinyftp.c
  526. sed 's/^X//' >tinyftp.c << 'END-of-tinyftp.c'
  527. X/*
  528. X * tinyftp.c - user ftp built on tinytcp.c
  529. X *
  530. X * Written March 31, 1986 by Geoffrey Cooper
  531. X *
  532. X * Copyright (C) 1986, IMAGEN Corporation
  533. X *  "This code may be duplicated in whole or in part provided that [1] there
  534. X *   is no commercial gain involved in the duplication, and [2] that this
  535. X *   copyright notice is preserved on all copies.  Any other duplication
  536. X *   requires written notice of the author (Geoffrey H. Cooper)."
  537. X */
  538. X#include "tinytcp.h"
  539. X
  540. Xtcp_Socket ftp_ctl, ftp_data, ftp_data2;
  541. Xbyte ftp_cmdbuf[120];
  542. Xint ftp_cmdbufi;
  543. X
  544. Xbyte ftp_outbuf[80];
  545. Xint ftp_outbufix, ftp_outbuflen;
  546. X    
  547. Xshort ftp_rcvState;
  548. X#define ftp_StateGETCMD     0       /* get a command from the user */
  549. X#define ftp_StateIDLE       1       /* idle connection */
  550. X#define ftp_StateINCOMMAND  2       /* command sent, awaiting response */
  551. X#define ftp_StateRCVRESP    3       /* received response, need more data */
  552. X
  553. Xchar *ftp_script[7];
  554. Xint ftp_scriptline;
  555. Xchar ftp_retrfile[80];
  556. XBOOL ftp_echoMode;
  557. X
  558. Xftp_ctlHandler(s, dp, len)
  559. X    tcp_Socket *s;
  560. X    byte *dp;
  561. X    int len;
  562. X{
  563. X    byte c, *bp, data[80];
  564. X    int i;
  565. X
  566. X    if ( dp == 0 ) {
  567. X        tcp_Abort(&ftp_data);
  568. X        return;
  569. X    }
  570. X
  571. X    do {
  572. X        i = len;
  573. X        if ( i > sizeof data ) i = sizeof data;
  574. X        MoveW(dp, data, i);
  575. X        len -= i;
  576. X        bp = data;
  577. X        while ( i-- > 0 ) {
  578. X            c = *bp++;
  579. X            if ( c != '\r' ) {
  580. X                if ( c == '\n' ) {
  581. X                    ftp_cmdbuf[ftp_cmdbufi] = 0;
  582. X                    ftp_commandLine();
  583. X                    ftp_cmdbufi = 0;
  584. X                } else if ( ftp_cmdbufi < (sizeof ftp_cmdbuf)-1 ) {
  585. X                    ftp_cmdbuf[ftp_cmdbufi++] = c;
  586. X                }
  587. X            }
  588. X        }
  589. X    } while ( len > 0 );
  590. X}
  591. X
  592. Xftp_commandLine()
  593. X{
  594. X    printf("> %s\n", ftp_cmdbuf);
  595. X    switch(ftp_rcvState) {
  596. X     case ftp_StateIDLE:
  597. X        if ( ftp_cmdbuf[3] == '-' )
  598. X            ftp_rcvState = ftp_StateRCVRESP;
  599. X        break;
  600. X
  601. X     case ftp_StateINCOMMAND:
  602. X        if ( ftp_cmdbuf[3] == '-' )
  603. X            ftp_rcvState = ftp_StateRCVRESP;
  604. X     case ftp_StateRCVRESP:
  605. X        if ( ftp_cmdbuf[3] == ' ' )
  606. X            ftp_rcvState = ftp_StateIDLE;
  607. X        break;
  608. X    }
  609. X}
  610. X
  611. Xftp_Abort()
  612. X{
  613. X    tcp_Abort(&ftp_ctl);
  614. X    tcp_Abort(&ftp_data);
  615. X}
  616. X
  617. X
  618. Xftp_application()
  619. X{
  620. X    char *s;
  621. X    char *dp;
  622. X    int i;
  623. X
  624. X    i = -1;
  625. X    if ( isina() ) {
  626. X        i = busyina() & 0177;
  627. X#ifdef DEBUG
  628. X        if ( i == ('D' & 037) ) SysBug("Pause to DDT");
  629. X#endif
  630. X        if ( i == ('C' & 037) ) {
  631. X            printf("Closing...\n");
  632. X            tcp_Close(&ftp_ctl);
  633. X        }
  634. X    }
  635. X
  636. X    switch (ftp_rcvState) {
  637. X      case ftp_StateGETCMD:
  638. X getcmd:if ( i != -1 ) {
  639. X            ftp_outbuf[ftp_outbuflen] = 0;
  640. X            switch (i) {
  641. X                case 'H' & 037:
  642. X                case 0177:
  643. X                    if ( ftp_outbuflen > 0 ) {
  644. X                        ftp_outbuflen--;
  645. X                        printf("\010 \010");
  646. X                    }
  647. X                    break;
  648. X
  649. X                case 'R' & 037:
  650. X                    if ( ftp_echoMode )
  651. X                        printf("\nFtpCmd> %s", ftp_outbuf);
  652. X                    break;
  653. X
  654. X                case 033:
  655. X                    ftp_echoMode = ! ftp_echoMode;
  656. X                    break;
  657. X
  658. X                case '\r':
  659. X                case '\n':
  660. X                    busyouta('\n');
  661. X                    dp = &ftp_outbuf[ftp_outbuflen];
  662. X                    goto docmd;
  663. X
  664. X                default:
  665. X                    if ( i >= ' ' && ftp_outbuflen < sizeof ftp_outbuf ) {
  666. X                        ftp_outbuf[ftp_outbuflen++] = i;
  667. X                        if ( ftp_echoMode ) busyouta(i);
  668. X                    }
  669. X            }
  670. X        }
  671. X        break;
  672. X
  673. X      case ftp_StateIDLE:
  674. X        if ( ftp_scriptline < 0 ) {
  675. X            ftp_rcvState = ftp_StateGETCMD;
  676. X            ftp_echoMode = true;
  677. X            ftp_outbuflen = 0;
  678. X            printf("FtpCmd> ");
  679. X            goto getcmd;
  680. X        }
  681. X        s = ftp_script[ftp_scriptline];
  682. X        if ( s == NIL )
  683. X            break;
  684. X        ftp_scriptline++;
  685. X        printf("%s\n", s);
  686. X        dp = ftp_outbuf;
  687. X        while ( *dp++ = *s++ ) ;
  688. X        dp--;
  689. X docmd: *dp++ = '\r';
  690. X        *dp++ = '\n';
  691. X        ftp_outbuflen = dp - ftp_outbuf;
  692. X        ftp_outbufix = 0;
  693. X        ftp_rcvState = ftp_StateINCOMMAND;
  694. X        /* fall through */
  695. X    case ftp_StateINCOMMAND:
  696. X        i = ftp_outbuflen - ftp_outbufix;
  697. X        if ( i > 0 ) {
  698. X            i = tcp_Write(&ftp_ctl, &ftp_outbuf[ftp_outbufix], i);
  699. X            ftp_outbufix += i;
  700. X            tcp_Flush(&ftp_ctl);
  701. X        }
  702. X        /* fall through */
  703. X    case ftp_StateRCVRESP:
  704. X        break;
  705. X    }
  706. X
  707. X}
  708. X
  709. Xftp(host, fn, dataHandler)
  710. X    in_HwAddress host;
  711. X    char *fn;
  712. X    procref dataHandler;
  713. X{
  714. X    word port;
  715. X    char filecmd[80];
  716. X
  717. X    port = (sed_lclEthAddr[2] + clock_ValueRough()) | 0x8000;
  718. X
  719. X    if ( fn ) {
  720. X        /* set up the script for this session */
  721. X        ftp_script[0] = "user foo";
  722. X        ftp_script[1] = "pass foo";
  723. X        ftp_script[2] = "type i";
  724. X        sprintf(filecmd, "retr %s", fn);
  725. X        ftp_script[3] = filecmd;
  726. X        ftp_script[4] = "quit";
  727. X        ftp_script[5] = 0;
  728. X        ftp_scriptline = 0;
  729. X    } else {
  730. X        ftp_scriptline = -1;        /* interactive mode */
  731. X        ftp_echoMode = true;
  732. X    }
  733. X
  734. X    /* set up state variables */
  735. X    ftp_rcvState = ftp_StateRCVRESP;
  736. X    ftp_cmdbufi = 0;
  737. X    tcp_Listen(&ftp_data, port, dataHandler, 0);
  738. X    tcp_Open(&ftp_ctl, port, host, 21, ftp_ctlHandler);
  739. X    tcp(ftp_application);
  740. X}
  741. END-of-tinyftp.c
  742. echo file: tinytcp.c
  743. sed 's/^X//' >tinytcp.c << 'END-of-tinytcp.c'
  744. X/*
  745. X * tinytcp.c - Tiny Implementation of the Transmission Control Protocol
  746. X *
  747. X * Written March 28, 1986 by Geoffrey Cooper, IMAGEN Corporation.
  748. X *
  749. X * This code is a small implementation of the TCP and IP protocols, suitable
  750. X * for burning into ROM.  The implementation is bare-bones and represents
  751. X * two days' coding efforts.  A timer and an ethernet board are assumed.  The
  752. X * implementation is based on busy-waiting, but the tcp_handler procedure
  753. X * could easily be integrated into an interrupt driven scheme.
  754. X *
  755. X * IP routing is accomplished on active opens by broadcasting the tcp SYN
  756. X * packet when ARP mapping fails.  If anyone answers, the ethernet address
  757. X * used is saved for future use.  This also allows IP routing on incoming
  758. X * connections.
  759. X * 
  760. X * The TCP does not implement urgent pointers (easy to add), and discards
  761. X * segments that are received out of order.  It ignores the received window
  762. X * and always offers a fixed window size on input (i.e., it is not flow
  763. X * controlled).
  764. X *
  765. X * Special care is taken to access the ethernet buffers only in word
  766. X * mode.  This is to support boards that only allow word accesses.
  767. X *
  768. X * Copyright (C) 1986, IMAGEN Corporation
  769. X *  "This code may be duplicated in whole or in part provided that [1] there
  770. X *   is no commercial gain involved in the duplication, and [2] that this
  771. X *   copyright notice is preserved on all copies.  Any other duplication
  772. X *   requires written notice of the author (Geoffrey H. Cooper)."
  773. X */
  774. X
  775. X#include "tinytcp.h"
  776. X
  777. X/*
  778. X * Local IP address
  779. X */
  780. Xin_HwAddress sin_lclINAddr;
  781. X
  782. X/*
  783. X * IP identification numbers
  784. X */
  785. Xint tcp_id;
  786. X
  787. Xtcp_Socket *tcp_allsocs;
  788. X
  789. X/* Timer definitions */
  790. X#define tcp_RETRANSMITTIME 1000     /* interval at which retransmitter is called */
  791. X#define tcp_LONGTIMEOUT 31000       /* timeout for opens */
  792. X#define tcp_TIMEOUT 10000           /* timeout during a connection */
  793. X
  794. X#ifdef DEBUG
  795. X/* 
  796. X * Primitive logging facility
  797. X */
  798. X#define tcp_LOGPACKETS 1        /* log packet headers */
  799. Xword tcp_logState;
  800. X#endif
  801. X
  802. X/*
  803. X * Initialize the tcp implementation
  804. X */
  805. Xtcp_Init()
  806. X{
  807. X    extern eth_HwAddress sed_lclEthAddr;
  808. X
  809. X    /* initialize ethernet interface */
  810. X    sed_Init();
  811. X
  812. X    tcp_allsocs = NIL;
  813. X#ifdef DEBUG
  814. X    tcp_logState = 0;
  815. X#endif
  816. X    tcp_id = 0;
  817. X
  818. X    /* hack - assume the network number */
  819. X    sin_lclINAddr = 0x7d000000 + (*((longword *)&sed_lclEthAddr[1]) & 0xFFFFFF);
  820. X}
  821. X
  822. X/*
  823. X * Actively open a TCP connection to a particular destination.
  824. X */
  825. Xtcp_Open(s, lport, ina, port, datahandler)
  826. X    tcp_Socket *s;
  827. X    in_HwAddress ina;
  828. X    word lport, port;
  829. X    procref datahandler;
  830. X{
  831. X    extern eth_HwAddress sed_ethBcastAddr;
  832. X
  833. X    s->state = tcp_StateSYNSENT;
  834. X    s->timeout = tcp_LONGTIMEOUT;
  835. X    if ( lport == 0 ) lport = clock_ValueRough();
  836. X    s->myport = lport;
  837. X    if ( ! sar_MapIn2Eth(ina, &s->hisethaddr[0]) ) {
  838. X        printf("tcp_Open of 0x%x: defaulting ethernet address to broadcast\n", ina);
  839. X        Move(&sed_ethBcastAddr[0], &s->hisethaddr[0], sizeof(eth_HwAddress));
  840. X    }
  841. X    s->hisaddr = ina;
  842. X    s->hisport = port;
  843. X    s->seqnum = 0;
  844. X    s->dataSize = 0;
  845. X    s->flags = tcp_FlagSYN;
  846. X    s->unhappy = true;
  847. X    s->dataHandler = datahandler;
  848. X    s->next = tcp_allsocs;
  849. X    tcp_allsocs = s;
  850. X    tcp_Send(s);
  851. X}
  852. X
  853. X/*
  854. X * Passive open: listen for a connection on a particular port
  855. X */
  856. Xtcp_Listen(s, port, datahandler, timeout)
  857. X    tcp_Socket *s;
  858. X    word port;
  859. X    procref datahandler;
  860. X{
  861. X    s->state = tcp_StateLISTEN;
  862. X    if ( timeout == 0 ) s->timeout = 0x7ffffff; /* forever... */
  863. X    else s->timeout = timeout;
  864. X    s->myport = port;
  865. X    s->hisport = 0;
  866. X    s->seqnum = 0;
  867. X    s->dataSize = 0;
  868. X    s->flags = 0;
  869. X    s->unhappy = 0;
  870. X    s->dataHandler = datahandler;
  871. X    s->next = tcp_allsocs;
  872. X    tcp_allsocs = s;
  873. X}
  874. X
  875. X/*
  876. X * Send a FIN on a particular port -- only works if it is open
  877. X */
  878. Xtcp_Close(s)
  879. X    tcp_Socket *s;
  880. X{
  881. X    if ( s->state == tcp_StateESTAB || s->state == tcp_StateSYNREC ) {
  882. X        s->flags = tcp_FlagACK | tcp_FlagFIN;
  883. X        s->state = tcp_StateFINWT1;
  884. X        s->unhappy = true;
  885. X    }
  886. X}
  887. X
  888. X/*
  889. X * Abort a tcp connection
  890. X */
  891. Xtcp_Abort(s)
  892. X    tcp_Socket *s;
  893. X{
  894. X    if ( s->state != tcp_StateLISTEN && s->state != tcp_StateCLOSED ) {
  895. X        s->flags = tcp_FlagRST | tcp_FlagACK;
  896. X        tcp_Send(s);
  897. X    }
  898. X    s->unhappy = 0;
  899. X    s->dataSize = 0;
  900. X    s->state = tcp_StateCLOSED;
  901. X    s->dataHandler(s, 0, -1);
  902. X    tcp_Unthread(s);
  903. X}
  904. X
  905. X/*
  906. X * Retransmitter - called periodically to perform tcp retransmissions
  907. X */
  908. Xtcp_Retransmitter()
  909. X{
  910. X    tcp_Socket *s;
  911. X    BOOL x;
  912. X
  913. X    for ( s = tcp_allsocs; s; s = s->next ) {
  914. X        x = false;
  915. X        if ( s->dataSize > 0 || s->unhappy ) {
  916. X            tcp_Send(s);
  917. X            x = true;
  918. X        }
  919. X        if ( x || s->state != tcp_StateESTAB )
  920. X            s->timeout -= tcp_RETRANSMITTIME;
  921. X        if ( s->timeout <= 0 ) {
  922. X            if ( s->state == tcp_StateTIMEWT ) {
  923. X                printf("Closed.    \n");
  924. X                s->state = tcp_StateCLOSED;
  925. X                s->dataHandler(s, 0, 0);
  926. X                tcp_Unthread(s);
  927. X            } else {
  928. X                printf("Timeout, aborting\n");
  929. X                tcp_Abort(s);
  930. X            }
  931. X        }
  932. X    }
  933. X}
  934. X
  935. X/*
  936. X * Unthread a socket from the socket list, if it's there 
  937. X */
  938. Xtcp_Unthread(ds)
  939. X    tcp_Socket *ds;
  940. X{
  941. X    tcp_Socket *s, **sp;
  942. X
  943. X    sp = &tcp_allsocs;
  944. X    for (;;) {
  945. X        s = *sp;
  946. X        if ( s == ds ) {
  947. X            *sp = s->next;
  948. X            break;
  949. X        }
  950. X        if ( s == NIL ) break;
  951. X        sp = &s->next;
  952. X    }
  953. X}
  954. X
  955. X/*
  956. X * busy-wait loop for tcp.  Also calls an "application proc"
  957. X */
  958. Xtcp(application)
  959. X    procref application;
  960. X{
  961. X    in_Header *ip;
  962. X    longword timeout, start;
  963. X    int x;
  964. X
  965. X    sed_Receive(0);
  966. X
  967. X    timeout = 0;
  968. X    while ( tcp_allsocs ) {
  969. X        start = clock_ValueRough();
  970. X        ip = sed_IsPacket();
  971. X        if ( ip == NIL ) {
  972. X            if ( clock_ValueRough() > timeout ) {
  973. X                tcp_Retransmitter();
  974. X                timeout = clock_ValueRough() + tcp_RETRANSMITTIME;
  975. X            }
  976. X
  977. X            application();
  978. X
  979. X            continue;
  980. X        }
  981. X
  982. X        if ( sed_CheckPacket(ip, 0x806) == 1 ) {
  983. X            /* do arp */
  984. X            sar_CheckPacket(ip);
  985. X
  986. X        } else if ( sed_CheckPacket(ip, 0x800) == 1 ) {
  987. X            /* do IP */
  988. X            if ( ip->destination == sin_lclINAddr &&
  989. X                 in_GetProtocol(ip) == 6 &&
  990. X                 checksum(ip, in_GetHdrlenBytes(ip)) == 0xFFFF ) {
  991. X                tcp_Handler(ip);
  992. X            }
  993. X        }
  994. X        /* recycle buffer */
  995. X        sed_Receive(ip);
  996. X
  997. X        x = clock_ValueRough() - start;
  998. X        timeout -= x;
  999. X    }
  1000. X
  1001. X    return ( 1 );
  1002. X}
  1003. X
  1004. X/*
  1005. X * Write data to a connection.
  1006. X * Returns number of bytes written, == 0 when connection is not in
  1007. X * established state.
  1008. X */
  1009. Xtcp_Write(s, dp, len)
  1010. X    tcp_Socket *s;
  1011. X    byte *dp;
  1012. X    int len;
  1013. X{
  1014. X    int x;
  1015. X
  1016. X    if ( s->state != tcp_StateESTAB ) len = 0;
  1017. X    if ( len > (x = tcp_MaxData - s->dataSize) ) len = x;
  1018. X    if ( len > 0 ) {
  1019. X        Move(dp, &s->data[s->dataSize], len);
  1020. X        s->dataSize += len;
  1021. X        tcp_Flush(s);
  1022. X    }
  1023. X
  1024. X    return ( len );
  1025. X}
  1026. X
  1027. X/*
  1028. X * Send pending data
  1029. X */
  1030. Xtcp_Flush(s)
  1031. X    tcp_Socket *s;
  1032. X{
  1033. X    if ( s->dataSize > 0 ) {
  1034. X        s->flags |= tcp_FlagPUSH;
  1035. X        tcp_Send(s);
  1036. X    }
  1037. X}
  1038. X
  1039. X/*
  1040. X * Handler for incoming packets.
  1041. X */
  1042. Xtcp_Handler(ip)
  1043. X    in_Header *ip;
  1044. X{
  1045. X    tcp_Header *tp;
  1046. X    tcp_PseudoHeader ph;
  1047. X    int len;
  1048. X    byte *dp;
  1049. X    int x, diff;
  1050. X    tcp_Socket *s;
  1051. X    word flags;
  1052. X
  1053. X    len = in_GetHdrlenBytes(ip);
  1054. X    tp = (tcp_Header *)((byte *)ip + len);
  1055. X    len = ip->length - len;
  1056. X
  1057. X    /* demux to active sockets */
  1058. X    for ( s = tcp_allsocs; s; s = s->next )
  1059. X        if ( s->hisport != 0 &&
  1060. X             tp->dstPort == s->myport &&
  1061. X             tp->srcPort == s->hisport &&
  1062. X             ip->source == s->hisaddr ) break;
  1063. X    if ( s == NIL ) {
  1064. X        /* demux to passive sockets */
  1065. X        for ( s = tcp_allsocs; s; s = s->next )
  1066. X            if ( s->hisport == 0 && tp->dstPort == s->myport ) break;
  1067. X    }
  1068. X    if ( s == NIL ) {
  1069. X#ifdef DEBUG
  1070. X        if ( tcp_logState & tcp_LOGPACKETS ) tcp_DumpHeader(ip, tp, "Discarding");
  1071. X#endif
  1072. X        return;
  1073. X    }
  1074. X
  1075. X#ifdef DEBUG
  1076. X    if ( tcp_logState & tcp_LOGPACKETS )
  1077. X        tcp_DumpHeader(ip, tp, "Received");
  1078. X#endif
  1079. X
  1080. X    /* save his ethernet address */
  1081. X    MoveW(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0], sizeof(eth_HwAddress));
  1082. X
  1083. X    ph.src = ip->source;
  1084. X    ph.dst = ip->destination;
  1085. X    ph.mbz = 0;
  1086. X    ph.protocol = 6;
  1087. X    ph.length = len;
  1088. X    ph.checksum = checksum(tp, len);
  1089. X    if ( checksum(&ph, sizeof ph) != 0xffff )
  1090. X         printf("bad tcp checksum, received anyway\n");
  1091. X
  1092. X    flags = tp->flags;
  1093. X    if ( flags & tcp_FlagRST ) {
  1094. X        printf("connection reset\n");
  1095. X        s->state = tcp_StateCLOSED;
  1096. X        s->dataHandler(s, 0, -1);
  1097. X        tcp_Unthread(s);
  1098. X        return;
  1099. X    }
  1100. X
  1101. X    switch ( s->state ) {
  1102. X
  1103. X    case tcp_StateLISTEN:
  1104. X        if ( flags & tcp_FlagSYN ) {
  1105. X            s->acknum = tp->seqnum + 1;
  1106. X            s->hisport = tp->srcPort;
  1107. X            s->hisaddr = ip->source;
  1108. X            s->flags = tcp_FlagSYN | tcp_FlagACK;
  1109. X            tcp_Send(s);
  1110. X            s->state = tcp_StateSYNREC;
  1111. X            s->unhappy = true;
  1112. X            s->timeout = tcp_TIMEOUT;
  1113. X            printf("Syn from 0x%x#%d (seq 0x%x)\n", s->hisaddr, s->hisport, tp->seqnum);
  1114. X        }
  1115. X        break;
  1116. X
  1117. X    case tcp_StateSYNSENT:
  1118. X        if ( flags & tcp_FlagSYN ) {
  1119. X            s->acknum++;
  1120. X            s->flags = tcp_FlagACK;
  1121. X            s->timeout = tcp_TIMEOUT;
  1122. X            if ( (flags & tcp_FlagACK) && tp->acknum == (s->seqnum + 1) ) {
  1123. X                printf("Open\n");
  1124. X                s->state = tcp_StateESTAB;
  1125. X                s->seqnum++;
  1126. X                s->acknum = tp->seqnum + 1;
  1127. X                s->unhappy = false;
  1128. X            } else {
  1129. X                s->state = tcp_StateSYNREC;
  1130. X            }
  1131. X        }
  1132. X        break;
  1133. X
  1134. X    case tcp_StateSYNREC:
  1135. X        if ( flags & tcp_FlagSYN ) {
  1136. X            s->flags = tcp_FlagSYN | tcp_FlagACK;
  1137. X            tcp_Send(s);
  1138. X            s->timeout = tcp_TIMEOUT;
  1139. X            printf(" retransmit of original syn\n");
  1140. X        }
  1141. X        if ( (flags & tcp_FlagACK) && tp->acknum == (s->seqnum + 1) ) {
  1142. X            s->flags = tcp_FlagACK;
  1143. X            tcp_Send(s);
  1144. X            s->seqnum++;
  1145. X            s->unhappy = false;
  1146. X            s->state = tcp_StateESTAB;
  1147. X            s->timeout = tcp_TIMEOUT;
  1148. X            printf("Synack received - connection established\n");
  1149. X        }
  1150. X        break;
  1151. X
  1152. X    case tcp_StateESTAB:
  1153. X        if ( (flags & tcp_FlagACK) == 0 ) return;
  1154. X        /* process ack value in packet */
  1155. X        diff = tp->acknum - s->seqnum;
  1156. X        if ( diff > 0 ) {
  1157. X            Move(&s->data[diff], &s->data[0], diff);
  1158. X            s->dataSize -= diff;
  1159. X            s->seqnum += diff;
  1160. X        }
  1161. X        s->flags = tcp_FlagACK;
  1162. X        tcp_ProcessData(s, tp, len);
  1163. X        break;
  1164. X
  1165. X    case tcp_StateFINWT1:
  1166. X        if ( (flags & tcp_FlagACK) == 0 ) return;
  1167. X        diff = tp->acknum - s->seqnum - 1;
  1168. X        s->flags = tcp_FlagACK | tcp_FlagFIN;
  1169. X        if ( diff == 0 ) {
  1170. X            s->state = tcp_StateFINWT2;
  1171. X            s->flags = tcp_FlagACK;
  1172. X            printf("finack received.\n");
  1173. X        }
  1174. X        tcp_ProcessData(s, tp, len);
  1175. X        break;
  1176. X
  1177. X    case tcp_StateFINWT2:
  1178. X        s->flags = tcp_FlagACK;
  1179. X        tcp_ProcessData(s, tp, len);
  1180. X        break;
  1181. X
  1182. X    case tcp_StateCLOSING:
  1183. X        if ( tp->acknum == (s->seqnum + 1) ) {
  1184. X            s->state = tcp_StateTIMEWT;
  1185. X            s->timeout = tcp_TIMEOUT;
  1186. X        }
  1187. X        break;
  1188. X
  1189. X    case tcp_StateLASTACK:
  1190. X        if ( tp->acknum == (s->seqnum + 1) ) {
  1191. X            s->state = tcp_StateCLOSED;
  1192. X            s->unhappy = false;
  1193. X            s->dataSize = 0;
  1194. X            s->dataHandler(s, 0, 0);
  1195. X            tcp_Unthread(s);
  1196. X            printf("Closed.    \n");
  1197. X        } else {
  1198. X            s->flags = tcp_FlagACK | tcp_FlagFIN;
  1199. X            tcp_Send(s);
  1200. X            s->timeout = tcp_TIMEOUT;
  1201. X            printf("retransmitting FIN\n");
  1202. X        }
  1203. X        break;
  1204. X
  1205. X    case tcp_StateTIMEWT:
  1206. X        s->flags = tcp_FlagACK;
  1207. X        tcp_Send(s);
  1208. X    }
  1209. X}
  1210. X
  1211. X/*
  1212. X * Process the data in an incoming packet.
  1213. X * Called from all states where incoming data can be received: established,
  1214. X * fin-wait-1, fin-wait-2
  1215. X */
  1216. Xtcp_ProcessData(s, tp, len)
  1217. X    tcp_Socket *s;
  1218. X    tcp_Header *tp;
  1219. X    int len;
  1220. X{
  1221. X    int diff, x;
  1222. X    word flags;
  1223. X    byte *dp;
  1224. X
  1225. X    flags = tp->flags;
  1226. X    diff = s->acknum - tp->seqnum;
  1227. X    if ( flags & tcp_FlagSYN ) diff--;
  1228. X    x = tcp_GetDataOffset(tp) << 2;
  1229. X    dp = (byte *)tp + x;
  1230. X    len -= x;
  1231. X    if ( diff >= 0 ) {
  1232. X        dp += diff;
  1233. X        len -= diff;
  1234. X        s->acknum += len;
  1235. X        s->dataHandler(s, dp, len);
  1236. X        if ( flags & tcp_FlagFIN ) {
  1237. X            s->acknum++;
  1238. X#ifdef DEBUG
  1239. X            printf("consumed fin.\n");
  1240. X#endif
  1241. X            switch(s->state) {
  1242. X              case tcp_StateESTAB:
  1243. X                /* note: skip state CLOSEWT by automatically closing conn */
  1244. X                x = tcp_StateLASTACK;
  1245. X                s->flags |= tcp_FlagFIN;
  1246. X                s->unhappy = true;
  1247. X#ifdef DEBUG
  1248. X                printf("sending fin.\n");
  1249. X#endif
  1250. X                break;
  1251. X              case tcp_StateFINWT1:
  1252. X                x = tcp_StateCLOSING;
  1253. X                break;
  1254. X              case tcp_StateFINWT2:
  1255. X                x = tcp_StateTIMEWT;
  1256. X                break;
  1257. X            }
  1258. X            s->state = x;
  1259. X        }
  1260. X    }
  1261. X    s->timeout = tcp_TIMEOUT;
  1262. X    tcp_Send(s);
  1263. X}
  1264. X
  1265. X/*
  1266. X * Format and send an outgoing segment
  1267. X */
  1268. Xtcp_Send(s)
  1269. X    tcp_Socket *s;
  1270. X{
  1271. X    tcp_PseudoHeader ph;
  1272. X    struct _pkt {
  1273. X        in_Header in;
  1274. X        tcp_Header tcp;
  1275. X        longword maxsegopt;
  1276. X    } *pkt;
  1277. X    byte *dp;
  1278. X
  1279. X    pkt = (struct _pkt *)sed_FormatPacket(&s->hisethaddr[0], 0x800);
  1280. X    dp = &pkt->maxsegopt;
  1281. X
  1282. X    pkt->in.length = sizeof(in_Header) + sizeof(tcp_Header) + s->dataSize;
  1283. X
  1284. X    /* tcp header */
  1285. X    pkt->tcp.srcPort = s->myport;
  1286. X    pkt->tcp.dstPort = s->hisport;
  1287. X    pkt->tcp.seqnum = s->seqnum;
  1288. X    pkt->tcp.acknum = s->acknum;
  1289. X    pkt->tcp.window = 1024;
  1290. X    pkt->tcp.flags = s->flags | 0x5000;
  1291. X    pkt->tcp.checksum = 0;
  1292. X    pkt->tcp.urgentPointer = 0;
  1293. X    if ( s->flags & tcp_FlagSYN ) {
  1294. X        pkt->tcp.flags += 0x1000;
  1295. X        pkt->in.length += 4;
  1296. X        pkt->maxsegopt = 0x02040578; /* 1400 bytes */
  1297. X        dp += 4;
  1298. X    }
  1299. X    MoveW(s->data, dp, s->dataSize);
  1300. X
  1301. X    /* internet header */
  1302. X    pkt->in.vht = 0x4500;   /* version 4, hdrlen 5, tos 0 */
  1303. X    pkt->in.identification = tcp_id++;
  1304. X    pkt->in.frag = 0;
  1305. X    pkt->in.ttlProtocol = (250<<8) + 6;
  1306. X    pkt->in.checksum = 0;
  1307. X    pkt->in.source = sin_lclINAddr;
  1308. X    pkt->in.destination = s->hisaddr;
  1309. X    pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
  1310. X
  1311. X    /* compute tcp checksum */
  1312. X    ph.src = pkt->in.source;
  1313. X    ph.dst = pkt->in.destination;
  1314. X    ph.mbz = 0;
  1315. X    ph.protocol = 6;
  1316. X    ph.length = pkt->in.length - sizeof(in_Header);
  1317. X    ph.checksum = checksum(&pkt->tcp, ph.length);
  1318. X    pkt->tcp.checksum = ~checksum(&ph, sizeof ph);
  1319. X
  1320. X#ifdef DEBUG
  1321. X    if ( tcp_logState & tcp_LOGPACKETS )
  1322. X        tcp_DumpHeader(&pkt->in, &pkt->tcp, "Sending");
  1323. X#endif
  1324. X
  1325. X    sed_Send(pkt->in.length);
  1326. X}
  1327. X
  1328. X/*
  1329. X * Do a one's complement checksum
  1330. X */
  1331. Xchecksum(dp, length)
  1332. X    word *dp;
  1333. X    int length;
  1334. X{
  1335. X    int len;
  1336. X    longword sum;
  1337. X
  1338. X    len = length >> 1;
  1339. X    sum = 0;
  1340. X    while ( len-- > 0 ) sum += *dp++;
  1341. X    if ( length & 1 ) sum += (*dp & 0xFF00);
  1342. X    sum = (sum & 0xFFFF) + ((sum >> 16) & 0xFFFF);
  1343. X    sum = (sum & 0xFFFF) + ((sum >> 16) & 0xFFFF);
  1344. X
  1345. X    return ( sum );
  1346. X}
  1347. X
  1348. X/*
  1349. X * Dump the tcp protocol header of a packet
  1350. X */
  1351. Xtcp_DumpHeader( ip, tp, mesg )
  1352. X    in_Header *ip;
  1353. X    char *mesg;
  1354. X{
  1355. X    register tcp_Header *tp = (tcp_Header *)((byte *)ip + in_GetHdrlenBytes(ip));
  1356. X    static char *flags[] = { "FIN", "SYN", "RST", "PUSH", "ACK", "URG" };
  1357. X    int len;
  1358. X    word f;
  1359. X
  1360. X    len =  ip->length - ((tcp_GetDataOffset(tp) + in_GetHdrlen(ip)) << 2);
  1361. X    printf("TCP: %s packet:\nS: %x; D: %x; SN=%x ACK=%x W=%d DLen=%d\n",
  1362. X           mesg, tp->srcPort, tp->dstPort, tp->seqnum, tp->acknum,
  1363. X           tp->window, len);
  1364. X    printf("DO=%d, C=%x U=%d",
  1365. X           tcp_GetDataOffset(tp), tp->checksum, tp->urgentPointer);
  1366. X    /* output flags */
  1367. X    f = tp->flags;
  1368. X    for ( len = 0; len < 6; len++ )
  1369. X        if ( f & (1 << len) ) printf(" %s", flags[len]);
  1370. X    printf("\n");
  1371. X}
  1372. X
  1373. X/*
  1374. X * Move bytes from hither to yon
  1375. X */
  1376. XMove( src, dest, numbytes )
  1377. X    register byte *src, *dest;
  1378. X    register numbytes;
  1379. X{
  1380. X    if ( numbytes <= 0 ) return;
  1381. X    if ( src < dest ) {
  1382. X        src += numbytes;
  1383. X        dest += numbytes;
  1384. X        do {
  1385. X            *--dest = *--src;
  1386. X        } while ( --numbytes > 0 );
  1387. X    } else
  1388. X        do {
  1389. X             *dest++ = *src++;
  1390. X        } while ( --numbytes > 0 );
  1391. X}
  1392. END-of-tinytcp.c
  1393. echo file: tinytcp.h
  1394. sed 's/^X//' >tinytcp.h << 'END-of-tinytcp.h'
  1395. X/*
  1396. X * tinytcp.h - header file for tinytcp.c
  1397. X *
  1398. X * Copyright (C) 1986, IMAGEN Corporation
  1399. X *  "This code may be duplicated in whole or in part provided that [1] there
  1400. X *   is no commercial gain involved in the duplication, and [2] that this
  1401. X *   copyright notice is preserved on all copies.  Any other duplication
  1402. X *   requires written notice of the author (Geoffrey H. Cooper)."
  1403. X *
  1404. X * Note: the structures herein must guarantee that the
  1405. X *       code only performs word fetches, since the
  1406. X *       imagenether card doesn't accept byte accesses.
  1407. X */
  1408. X
  1409. X#define TRUE        1
  1410. X#define true        1
  1411. X#define FALSE       0
  1412. X#define false       0
  1413. X#define NULL        0               /* An empty value */
  1414. X#define NIL         0               /* The distinguished empty pointer */
  1415. X
  1416. X/* Useful type definitions */
  1417. Xtypedef int (*procref)();
  1418. Xtypedef short BOOL;                  /* boolean type */
  1419. X
  1420. X/* Canonically-sized data */
  1421. Xtypedef unsigned long longword;     /* 32 bits */
  1422. Xtypedef unsigned short word;        /* 16 bits */
  1423. Xtypedef unsigned char byte;         /*  8 bits */
  1424. Xtypedef byte octet;                 /*  8 bits, for TCP */
  1425. X
  1426. X#ifdef DDT
  1427. Xextern longword MsecClock();
  1428. X#define clock_ValueRough() MsecClock()
  1429. X#else
  1430. Xextern longword clock_MS;
  1431. X#define clock_ValueRough() clock_MS
  1432. X#endif
  1433. X
  1434. X/* protocol address definitions */
  1435. Xtypedef longword in_HwAddress;
  1436. Xtypedef word eth_HwAddress[3];
  1437. X
  1438. X/* The Ethernet header */
  1439. Xtypedef struct {
  1440. X    eth_HwAddress   destination;
  1441. X    eth_HwAddress   source;
  1442. X    word            type;
  1443. X} eth_Header;
  1444. X
  1445. X/* The Internet Header: */
  1446. Xtypedef struct {
  1447. X    word            vht;    /* version, hdrlen, tos */
  1448. X    word            length;
  1449. X    word            identification;
  1450. X    word            frag;
  1451. X    word            ttlProtocol;
  1452. X    word            checksum;
  1453. X    in_HwAddress    source;
  1454. X    in_HwAddress    destination;
  1455. X} in_Header;
  1456. X#define in_GetVersion(ip) (((ip)->vht >> 12) & 0xf)
  1457. X#define in_GetHdrlen(ip)  (((ip)->vht >> 8) & 0xf)
  1458. X#define in_GetHdrlenBytes(ip)  (((ip)->vht >> 6) & 0x3c)
  1459. X#define in_GetTos(ip)      ((ip)->vht & 0xff)
  1460. X
  1461. X#define in_GetTTL(ip)      ((ip)->ttlProtocol >> 8)
  1462. X#define in_GetProtocol(ip) ((ip)->ttlProtocol & 0xff)
  1463. X
  1464. X
  1465. Xtypedef struct {
  1466. X    word            srcPort;
  1467. X    word            dstPort;
  1468. X    longword        seqnum;
  1469. X    longword        acknum;
  1470. X    word            flags;
  1471. X    word            window;
  1472. X    word            checksum;
  1473. X    word            urgentPointer;
  1474. X} tcp_Header;
  1475. X
  1476. X
  1477. X#define tcp_FlagFIN     0x0001
  1478. X#define tcp_FlagSYN     0x0002
  1479. X#define tcp_FlagRST     0x0004
  1480. X#define tcp_FlagPUSH    0x0008
  1481. X#define tcp_FlagACK     0x0010
  1482. X#define tcp_FlagURG     0x0020
  1483. X#define tcp_FlagDO      0xF000
  1484. X#define tcp_GetDataOffset(tp) ((tp)->flags >> 12)
  1485. X
  1486. X/* The TCP/UDP Pseudo Header */
  1487. Xtypedef struct {
  1488. X    in_HwAddress    src;
  1489. X    in_HwAddress    dst;
  1490. X    octet           mbz;
  1491. X    octet           protocol;
  1492. X    word            length;
  1493. X    word            checksum;
  1494. X} tcp_PseudoHeader;
  1495. X
  1496. X/*
  1497. X * TCP states, from tcp manual.
  1498. X * Note: close-wait state is bypassed by automatically closing a connection
  1499. X *       when a FIN is received.  This is easy to undo.
  1500. X */
  1501. X#define tcp_StateLISTEN  0      /* listening for connection */
  1502. X#define tcp_StateSYNSENT 1      /* syn sent, active open */
  1503. X#define tcp_StateSYNREC  2      /* syn received, synack+syn sent. */
  1504. X#define tcp_StateESTAB   3      /* established */
  1505. X#define tcp_StateFINWT1  4      /* sent FIN */
  1506. X#define tcp_StateFINWT2  5      /* sent FIN, received FINACK */
  1507. X/*#define tcp_StateCLOSEWT 6    /* received FIN waiting for close */
  1508. X#define tcp_StateCLOSING 6      /* sent FIN, received FIN (waiting for FINACK) */
  1509. X#define tcp_StateLASTACK 7      /* fin received, finack+fin sent */
  1510. X#define tcp_StateTIMEWT  8      /* dally after sending final FINACK */
  1511. X#define tcp_StateCLOSED  9      /* finack received */
  1512. X
  1513. X/*
  1514. X * TCP Socket definition
  1515. X */
  1516. X#define tcp_MaxData 32              /* maximum bytes to buffer on output */
  1517. X
  1518. Xtypedef struct _tcp_socket {
  1519. X    struct _tcp_socket *next;
  1520. X    short           state;          /* connection state */
  1521. X    procref         dataHandler;    /* called with incoming data */
  1522. X    eth_HwAddress   hisethaddr;     /* ethernet address of peer */
  1523. X    in_HwAddress    hisaddr;        /* internet address of peer */
  1524. X    word            myport, hisport;/* tcp ports for this connection */
  1525. X    longword        acknum, seqnum; /* data ack'd and sequence num */
  1526. X    int             timeout;        /* timeout, in milliseconds */
  1527. X    BOOL            unhappy;        /* flag, indicates retransmitting segt's */
  1528. X    word            flags;          /* tcp flags word for last packet sent */
  1529. X    short           dataSize;       /* number of bytes of data to send */
  1530. X    byte            data[tcp_MaxData]; /* data to send */
  1531. X} tcp_Socket;
  1532. X
  1533. Xextern eth_HwAddress sed_lclEthAddr;
  1534. Xextern eth_HwAddress sed_ethBcastAddr;
  1535. Xextern in_HwAddress  sin_lclINAddr;
  1536. X
  1537. X/*
  1538. X * ARP definitions
  1539. X */
  1540. X#define arp_TypeEther  1        /* ARP type of Ethernet address *
  1541. X
  1542. X/* harp op codes */
  1543. X#define ARP_REQUEST 1
  1544. X#define ARP_REPLY   2
  1545. X
  1546. X/*
  1547. X * Arp header
  1548. X */
  1549. Xtypedef struct {
  1550. X    word            hwType;
  1551. X    word            protType;
  1552. X    word            hwProtAddrLen;  /* hw and prot addr len */
  1553. X    word            opcode;
  1554. X    eth_HwAddress   srcEthAddr;
  1555. X    in_HwAddress    srcIPAddr;
  1556. X    eth_HwAddress   dstEthAddr;
  1557. X    in_HwAddress    dstIPAddr;
  1558. X} arp_Header;
  1559. X
  1560. X/*
  1561. X * Ethernet interface:
  1562. X *   sed_WaitPacket(0) => ptr to packet (beyond eth header)
  1563. X *                          or NIL if no packet ready.
  1564. X *   sed_Receive(ptr) - reenables receive on input buffer
  1565. X *   sed_FormatPacket(ðdest, ethtype) => ptr to packet buffer
  1566. X *   sed_Send(packet_length) - send the buffer last formatted.
  1567. X */
  1568. Xbyte *sed_IsPacket(), *sed_FormatPacket();
  1569. END-of-tinytcp.h
  1570. exit
  1571.  
  1572.  
  1573.