home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
msvp98b1.lzh
/
MSNTCP.C
< prev
next >
Wrap
Text File
|
1993-05-14
|
51KB
|
2,000 lines
/* File MSNTCP.C
* Main TCP protocol code
*
* Copyright (C) 1991, University of Waterloo.
* Copyright (C) 1985, 1992, Trustees of Columbia University in the
* City of New York. Permission is granted to any individual or institution
* to use this software as long as it is not sold for profit. This copyright
* notice must be retained. This software may not be included in commercial
* products without written permission of Columbia University.
*
* Original version created by Erick Engelke of the University of
* Waterloo, Waterloo, Ontario, Canada.
* Adapted and modified for MS-DOS Kermit by Joe R. Doupnik,
* Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet.
*
* Last edit
* 12 May 1992
*
* PCTCP - the true worker of Waterloo TCP
* - contains all opens, closes, major read/write routines and
* basic IP handler for incomming packets
* - NOTE: much of the TCP/UDP/IP layering is done at data structure
* level, not in separate routines or tasks
*
*/
#include "msntcp.h"
#include "msnlib.h"
/*
#define OLD 1
*/
static void (*system_yield)() = NULL;
static tcp_Retransmitter();
static tcp_unthread(void * s);
static tcp_handler(void * s);
static udp_handler(void * s);
extern int arp_handler(in_Header *);
extern int icmp_handler(void * s);
extern void icmp_noport(void * s);
extern void server_hello(tcp_Socket *s);
int tcp_rst(in_Header *his_ip, tcp_Header *oldtcpp);
static tcp_ProcessData(void *s, void * tp, int len);
static initialized = 0;
longword ipbcast = 0xffffffffL; /* default IP broadcast address */
word waittime = 2; /* tcp_sendsoon, normal ticks to wait, ~0.1 sec */
#ifdef DEBUG
void (*dbugxmit)() = NULL;
void (*dbugrecv)() = NULL;
#endif
/*
* Local IP address
*/
longword my_ip_addr = 0L; /* for external references */
longword sin_mask = 0xfffffe00L;
longword sin_gate = 0L;
/*
* IP identification numbers
*/
static int ip_id = 0; /* packet number */
static int next_tcp_port = 1024; /* auto incremented */
static int next_udp_port = 1024;
static tcp_Socket *tcp_allsocs = NULL; /* TCP socket linked list head */
static udp_Socket *udp_allsocs = NULL; /* UDP socket linked list head */
/* Timer definitions */
#define RETRAN_STRAT_TIME 1 /* ticks, how often to check retransmitter*/
#define tcp_RETRANSMITTIME 3 /* interval to call retransmitter */
#define tcp_LONGTIMEOUT 31 /* timeout for opens */
#define tcp_TIMEOUT 13 /* timeout during a connection */
word debug_on = 0;
byte *hostname = "012345678901234567890123456789012345678901234567890";
word mss = ETH_MSS;
word sourcequench = 0; /* non-zero if received ICMP Source Quench */
/* TCP Header (and size in bits)
Source Port (16)
Destination Port (16)
Sequence Number (32) First octet in this segment. If SYN then
this is the initial seq num and first data
octet is one larger.
Acknowledgment Number (32) Next octet expected to be received
Data Offset (4) number of 32 bit words in this header so we
know when options are present (and how large)
Reserved (6)
URG (1) Urgent
ACK (1) Acknowledgment
PSH (1) Push function (expedite)
RST (1) Reset the connection
SYN (1) Synchronize sequence numbers
FIN (1) Finish, no more data from sender
Window (16) Octets willing to accept after Ack value
Checksum (16) One's complement of header and Pseudo header
Urgent Pointer (16) Points to octet following urgent data <sic>
Options & Padding (32) Only Option is max segment size
Data (the rest)
*/
/*
* tcp_Init - Initialize the tcp implementation
* - may be called more than once without hurting
*/
int
tcp_init()
{
extern int arp_last_gateway;
extern int last_nameserver;
void icmp_init();
if (initialized != 0) return (1); /* success, inited already */
/* initialize ethernet interface */
arp_init(); /* clear ARP tables */
icmp_init(); /* clear ICMP material */
last_nameserver = 0; /* reset the nameserver table */
*hostname = 0; /* reset the host's name */
tcp_allsocs = NULL; /* zero socket pointers */
udp_allsocs = NULL;
ip_id = 0; /* packet number, convenient */
initialized = eth_init(); /* init the hardware, can fail */
if (my_ip_addr == 0L) /* my_ap_addr is longword*/
bcopy(ð_addr[2], &my_ip_addr, 4); /* a pseudo long */
/* next port numbers to choose */
next_udp_port = next_tcp_port = 1024 + (realclock() & 0x1ff);
eth_free (NULL); /* clear all pkt rcv bufs */
return (initialized); /* success (1) or failure (0) */
}
/*
* Shut down the card and all services
*/
void
tcp_shutdown()
{
while (tcp_allsocs != NULL)
tcp_abort(tcp_allsocs);
eth_release();
initialized = 0;
}
int
udp_open(s, lport, ina, port, datahandler)
udp_Socket *s;
longword ina;
word lport, port;
procref datahandler;
{
if (s == NULL) return (0);
memset(s, 0, sizeof(udp_Socket));
s->ip_type = UDP_PROTO;
if (lport == 0) lport = ++next_udp_port; /* get a nonzero port val */
s->myport = lport;
/* check for broadcast */
if (ina == 0xffffffffL || ina == 0L || ina == ipbcast)
memset(s->hisethaddr, 0xff, sizeof(eth_address));
else
if (arp_resolve(ina, &s->hisethaddr[0]) == 0)
return (0);
s->hisaddr = ina;
s->hisport = port;
s->dataHandler = datahandler;
s->usr_yield = system_yield;
s->next = udp_allsocs;
udp_allsocs = s;
return (1);
}
/*
* Actively open a TCP connection to a particular destination.
* - 0 on error
*/
int
tcp_open(s, lport, ina, port, datahandler)
tcp_Socket *s;
longword ina;
word lport, port;
procref datahandler;
{
if (s == NULL) return (0);
memset(s, 0, sizeof(tcp_Socket)); /* zero everything */
s->ip_type = TCP_PROTO;
s->mss = mss;
s->state = tcp_StateSYNSENT;
s->timeout = set_timeout(tcp_LONGTIMEOUT);
s->rmaxdatalen = tcp_MaxBufSize;
s->cwindow = 1; /* slow start VJ algorithm */
s->vj_sa = 4; /* about 250 ms (4/18.2 tics/sec) */
if (lport == 0)
lport = ++next_tcp_port; /* get a nonzero port value */
s->myport = lport;
if (! arp_resolve(ina, &s->hisethaddr[0]))
return (0); /* failed to get host Ethernet addr */
s->hisaddr = ina;
s->hisport = port;
s->seqnum = ntohl(set_timeout(0)) & 0xffff0000;
s->datalen = 0;
s->flags = tcp_FlagSYN;
s->unhappy = TRUE;
s->dataHandler = datahandler;
s->usr_yield = system_yield;
s->next = tcp_allsocs;
s->rtt_delay = s->rtt_smooth = 18; /* one second startup */
tcp_allsocs = s;
return (tcp_send(s)); /* fail if send fails */
}
/*
* Passive open: listen for a connection on a particular port
*/
tcp_listen(s, lport, ina, port, datahandler, timeout)
tcp_Socket *s;
word port, lport, timeout;
longword ina;
procref datahandler;
{
if (s == NULL) return (0);
memset(s, 0, sizeof(tcp_Socket));
s->ip_type = TCP_PROTO;
s->mss = mss;
s->rmaxdatalen = tcp_MaxBufSize;
s->cwindow = 1; /* slow start VJ algorithm */
s->vj_sa = 4; /* about 250 ms */
s->state = tcp_StateLISTEN;
if (timeout == 0)
s->timeout = 0; /* no timeout */
else
s->timeout = set_timeout(timeout);
s->myport = lport;
s->hisport = port;
s->hisaddr = ina;
s->seqnum = ntohl(set_timeout(0)) & 0xffff0000;
s->datalen = 0;
s->flags = 0;
s->unhappy = FALSE;
s->dataHandler = datahandler;
s->usr_yield = system_yield;
s->next = tcp_allsocs;
tcp_allsocs = s;
return (1);
}
static int
udp_close(ds)
udp_Socket *ds;
{
register udp_Socket *sp;
sp = udp_allsocs; /* ptr to first socket */
if (sp == NULL) return (0); /* failure */
if (sp == ds) udp_allsocs = ds->next; /* if we are first, unlink */
while (sp != NULL)
{
if (sp->next == ds) /* if current points to us */
{
sp->next = ds->next; /* point it to our successor */
break;
}
sp = sp->next; /* look at next socket */
}
return (1); /* success */
}
/*
* Send a FIN on a particular port -- only works if it is open
* Must still allow receives
* Returns 0 for failure
*/
static
tcp_close(s)
tcp_Socket *s;
{
if (s == NULL) return (0);
if (s->ip_type != TCP_PROTO)
return (0); /* failure */
if (s->state == tcp_StateESTAB || s->state == tcp_StateSYNREC)
{
if (s->err_msg == NULL)
s->err_msg = "TCP_Closed called";
s->flags = tcp_FlagACK | tcp_FlagFIN;
if (s->datalen)
s->flags |= tcp_FlagPUSH;
s->state = tcp_StateFINWT1;
s->timeout = set_timeout(4);
/* should be a pretty lengthy time */
s->unhappy = TRUE;
tcp_send(s);
return (1); /* success */
}
return (0);
}
/*
* Abort a tcp connection
* Returns 0 if failure
*/
static int
tcp_abort(s)
tcp_Socket *s;
{
if (s == NULL) return (0); /* failure */
if (s->err_msg == NULL) s->err_msg = "TCP_ABORT";
if ((s->state != tcp_StateLISTEN) && (s->state != tcp_StateCLOSED))
{
s->flags = tcp_FlagRST | tcp_FlagACK;
s->unhappy = TRUE;
tcp_send(s);
}
s->unhappy = FALSE;
s->datalen = 0;
s->state = tcp_StateCLOSED;
if (s->dataHandler) s->dataHandler(s, 0, -1);
tcp_unthread(s);
return (1); /* success */
}
void
sock_abort(s)
tcp_Socket *s;
{
if (s == NULL) return; /* do nothing */
if (s->ip_type == TCP_PROTO)
tcp_abort(s);
else
udp_close(s);
}
/*
* tcp_sendsoon - schedule a transmission pretty soon
* - this one has an imperfection at midnight, but it
* is not significant to the connection performance
* Return 0 if failure.
*/
tcp_sendsoon(s)
tcp_Socket *s;
{
longword temp;
if (s == NULL) return (0);
if (s->ip_type != TCP_PROTO) return (0);
s->karn_count = 1;
if (sourcequench != 0) /* ICMP shutup message */
{
waittime += waittime; /* double the timeout */
sourcequench = 0; /* forget the reminder */
}
temp = set_ttimeout(waittime); /* Bios ticks into the future */
if ((s->rtt_time < temp)) /* if now is too soon */
return (1); /* then wait some more */
if (waittime > 2) /* if backing off already */
waittime = (waittime * 15) / 16; /* recover slowly */
s->rtt_time = temp;
return (1);
}
/*
* Retransmitter - called periodically to perform tcp retransmissions
* Returns 0 if failure
*/
static
tcp_Retransmitter()
{
register tcp_Socket *s;
static longword retran_strat = 0L; /* timeout retran strategy */
extern word pktdevclass;
/* do this only once per RETRAN_STRAT_TIME clock ticks */
if (!chk_timeout(retran_strat))
return (0L);
retran_strat = set_ttimeout( (pktdevclass == PD_SLIP)?
4 * RETRAN_STRAT_TIME: RETRAN_STRAT_TIME);
for (s = tcp_allsocs; s != NULL; s = s->next)
{
if ((s->datalen > 0) || (s->unhappy == TRUE) ||
(s->karn_count == 1))
/* retransmission strategy */
if (chk_timeout(s->rtt_time)) /* if timeout */
{
if (s->karn_count == 0)
s->karn_count = 2;
/* if really did timeout */
tcp_send(s);
}
if (s->timeout && chk_timeout(s->timeout))
{
if (s->state == tcp_StateTIMEWT)
{
s->flags = tcp_FlagACK;
s->state = tcp_StateCLOSED;
}
else if (s->state != tcp_StateESTAB)
{
s->err_msg = "Timeout, aborting";
tcp_abort(s);
}
}
}
return (1); /* success */
}
/*
* Unthread a socket from the socket list, if it's there. Return 0 on failure
*/
static int
tcp_unthread(ds)
tcp_Socket *ds;
{
register tcp_Socket *sp;
if (ds == NULL) return (0); /* failure */
if ((ds->rdatalen == 0) || (ds->state > tcp_StateESTAB))
ds->ip_type = 0; /* fail io */
ds->state = tcp_StateCLOSED; /* tcp_tick needs this */
if (tcp_allsocs == NULL) return (0);
sp = tcp_allsocs; /* ptr to first socket */
if (sp == ds) tcp_allsocs = ds->next; /* if we are first, unlink */
while (sp != NULL)
{
if (sp->next == ds) /* if current points to us */
{
sp->next = ds->next; /* point it to our successor */
break;
}
sp = sp->next; /* look at next socket */
}
return (1); /* success */
}
/*
* tcp_tick - called periodically by user application
* - returns 0 when our socket closes
* - called with socket parameter or NULL
*/
tcp_tick(s)
sock_type *s;
{
register in_Header *ip;
int packettype;
/* finish off dead sockets */
if (s != NULL)
if (s->tcp.ip_type == TCP_PROTO &&
s->tcp.state == tcp_StateCLOSED)
s->tcp.ip_type = 0;
/* read a packet */
while ((ip = (in_Header *)eth_arrived(&packettype)) != NULL)
{
switch (ntohs(packettype)) /* big to little endian form */
{
case 0x0800: /* do IP */
if (checksum(ip, in_GetHdrlenBytes(ip)) != 0xffff)
{
if (debug_on)
outs("IP Received BAD Checksum \n\r");
break;
}
if ((my_ip_addr == 0L) ||
(htonl(ip->destination) == my_ip_addr))
{
switch (ip->proto)
{
case TCP_PROTO:
tcp_handler(ip);
break;
case UDP_PROTO:
udp_handler(ip);
break;
case ICMP_PROTO:
icmp_handler(ip);
break;
default:
break;
} /* end of switch (ip->proto) */
}
break;
case 0x0806: /* do ARP */
arp_handler(ip);
break;
case 0x8035: /* do RARP */
rarp_handler(ip);
break;
default: /* unknown type */
break;
} /* end of switch */
eth_free(ip); /* free the processed packet */
} /* end of while */
tcp_Retransmitter(); /* check on pending sends */
return ((s != NULL)? s->tcp.ip_type: 0); /* 0 means closed socket */
}
void
tcp_set_debug_state(x)
word x;
{
debug_on = x;
}
/* returns 1 if connection is established, else 0 */
int
tcp_established(s)
tcp_Socket *s;
{
if (s == NULL) return (0); /* failure */
return (s->state == tcp_StateESTAB);
}
static int
udp_Write(s, datap, len)
udp_Socket *s;
byte *datap;
int len;
{
tcp_PseudoHeader ph;
struct pkt
{
in_Header in;
udp_Header udp;
int data;
/* longword maxsegopt; */
} register *pkt;
if (s == NULL || datap == NULL)
return (0); /* failure */
pkt = (struct pkt *)eth_formatpacket(&s->hisethaddr[0], 0x0008);
pkt->in.length = htons(sizeof(in_Header) + UDP_LENGTH + len);
/* UDP header */
pkt->udp.srcPort = htons(s->myport);
pkt->udp.dstPort = htons(s->hisport);
pkt->udp.checksum = 0;
pkt->udp.length = htons(UDP_LENGTH + len);
bcopy(datap, &pkt->data, len);
/* Internet header */
pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
pkt->in.tos = 0;
pkt->in.identification = htons(++ip_id); /* was post inc */
pkt->in.frag = 0;
#ifdef KERMIT
pkt->in.ttl = 60;
#else
pkt->in.ttl = 254;
#endif
pkt->in.proto = UDP_PROTO; /* UDP */
pkt->in.checksum = 0;
pkt->in.source = htonl(my_ip_addr);
pkt->in.destination = htonl(s->hisaddr);
pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
/* compute udp checksum if desired */
if (s->soc_mode & UDP_MODE_NOCHK)
pkt->udp.checksum = 0;
else
{
ph.src = pkt->in.source; /* big endian now */
ph.dst = pkt->in.destination;
ph.mbz = 0;
ph.protocol = UDP_PROTO; /* UDP */
ph.length = pkt->udp.length; /* big endian now */
ph.checksum = checksum(&pkt->udp, htons(ph.length));
pkt->udp.checksum = ~checksum(&ph, sizeof(ph));
}
#ifdef DEBUG
if (dbugxmit) (*dbugxmit)(s,inp,udpp);
#endif
if (eth_send(htons(pkt->in.length)) != 0) /* send pkt */
return (len);
else return (0); /* failed */
}
/*
* udp_read - read data from buffer, does large buffering.
* Return 0 on failure.
*/
static int
udp_read(s, datap, maxlen)
udp_Socket *s;
byte *datap;
int maxlen;
{
register int x;
if (s == NULL || datap == NULL) return (0); /* failure */
if ((x = s->rdatalen) > 0)
{
if (x > maxlen) x = maxlen;
if (x > 0)
{
bcopy(s->rdata, datap, x);
if (s->rdatalen -= x)
bcopy(&s->rdata[x], s->rdata, s->rdatalen);
}
}
return (x);
}
udp_cancel(in_Header *ip)
{
int len;
register udp_Header *up;
register udp_Socket *s;
/* match to a udp socket */
len = in_GetHdrlenBytes(ip);
up = (udp_Header *)((byte *)ip + len); /* udp frame pointer */
/* demux to active sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
ntohs(up->dstPort) == s->myport &&
ntohs(up->srcPort) == s->hisport &&
ntohl(ip->source) == s->hisaddr)
break;
if (s == NULL) /* demux to passive sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisport == 0 &&
ntohs(up->dstPort) == s->myport)
break;
if (s != NULL)
{
s->rdatalen = 0;
s->ip_type = 0;
}
return (1); /* success */
}
int
tcp_cancel(in_Header *ip)
{
int len;
register tcp_Socket *s;
register tcp_Header *tp;
if (ip == NULL) return (0); /* failure */
len = in_GetHdrlenBytes(ip); /* check work */
tp = (tcp_Header *)((byte *)ip + len); /* TCP frame pointer */
/* demux to active sockets */
for (s = tcp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
ntohs(tp->dstPort) == s->myport &&
ntohs(tp->srcPort) == s->hisport &&
ntohl(ip->source) == s->hisaddr)
break;
if (s == NULL) /* demux to passive sockets */
for (s = tcp_allsocs; s != NULL; s = s->next)
if (s->hisport == 0 &&
ntohs(tp->dstPort) == s->myport)
break;
if (s != NULL)
{
s->rdatalen = 0;
s->state = tcp_StateCLOSED;
s->ip_type = 0;
if (s->dataHandler) s->dataHandler(s, 0, -1);
tcp_unthread(s);
}
return (1); /* success */
}
static int
tcp_read(s, datap, maxlen)
tcp_Socket *s;
byte *datap;
int maxlen;
{
register int x;
if (s == NULL || datap == NULL || maxlen == 0)
return (0); /* failure or read nothing */
if ((x = s->rdatalen) > 0)
{
if (x > maxlen) x = maxlen;
bcopy(s->rdata, datap, x); /* copy out desired data */
if ((s->rdatalen -= x) < 0)
s->rdatalen = 0; /* readjust socket buf */
bcopy(&s->rdata[x], s->rdata, s->rdatalen);
tcp_sendsoon(s); /* update the window */
}
return (x); /* return bytes read */
}
/*
* Write data to a connection.
* Returns number of bytes written, == 0 when connection is not in
* established state.
*/
static int
tcp_Write(s, dp, len)
tcp_Socket *s;
byte *dp;
int len;
{
register int x;
if (s == NULL || dp == NULL) return (0); /* failure */
if (s->state != tcp_StateESTAB) len = 0;
if (len > (x = tcp_MaxBufSize - s->datalen)) len = x;
if (len > 0)
{
bcopy(dp, &s->data[s->datalen], len);
s->datalen += len;
s->unhappy = TRUE;/* redundant because of outstanding data */
if (s->soc_mode & TCP_MODE_NONAGLE)
tcp_send(s);
else /* transmit if first data or reached MTU */
/* not true MTU, but better than nothing */
/* Nagle's algorithm, RFC 896 */
if ((s->datalen == len) || (s->datalen > (s->mss)))
tcp_send(s);
}
return (len);
}
/*
* Send pending data
*/
static int
tcp_Flush(s)
tcp_Socket *s;
{
if (s == NULL) return (0); /* failure */
if (s->datalen > 0)
{
s->flags |= tcp_FlagPUSH;
tcp_send(s);
}
return (1); /* success */
}
/*
* Handler for incoming UDP packets. If no socket tell the host via ICMP.
*/
static int
udp_handler(ip)
in_Header *ip;
{
register udp_Header *up;
register udp_Socket *s;
tcp_PseudoHeader ph;
byte *dp;
word len;
len = in_GetHdrlenBytes(ip);
up = (udp_Header *)((byte *)ip + len); /* UDP segment pointer */
len = ntohs(up->length);
/* demux to active sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
ntohs(up->dstPort) == s->myport &&
ntohs(up->srcPort) == s->hisport &&
ntohl(ip->source) == s->hisaddr)
break;
#ifdef DEBUG
if (dbugrecv) (*dbugrecv)(s,ip,up);
#endif
if (s == NULL) /* demux to passive sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisaddr == 0 &&
ntohs(up->dstPort) == s->myport)
{
if (arp_resolve(htonl(ip->source),
&s->hisethaddr[0]))
{
s->hisaddr = ntohl(ip->source);
s->hisport = ntohs(up->srcPort);
}
break;
}
if (s == NULL) /* demux to broadcast sockets */
for (s = udp_allsocs; s != NULL; s = s->next)
if (s->hisaddr == ipbcast &&
ntohs(up->dstPort) == s->myport)
break;
if (s == NULL)
{
icmp_noport(ip); /* tell host port is unreachable */
if (debug_on) outs("discarding...");
return (0); /* say no socket */
}
if (up->checksum)
{
ph.src = ip->source; /* already bigend'd */
ph.dst = ip->destination;
ph.mbz = 0;
ph.protocol = UDP_PROTO;
ph.length = up->length;
ph.checksum = checksum(up, len);
if (checksum(&ph, sizeof(tcp_PseudoHeader)) != 0xffff)
return (0); /* failure */
}
/* process user data */
if ((len -= UDP_LENGTH) > 0)
{
dp = (byte *)(up);
if (s->dataHandler != NULL)
s->dataHandler(s, &dp[UDP_LENGTH], len, &ph);
else
{
if (len > udp_MaxBufSize) len = udp_MaxBufSize;
bcopy(&dp[UDP_LENGTH], s->rdata, len);
s->rdatalen = len;
}
}
return (1); /* success */
}
/* Handle TCP packets. If no socket send an RST pkt. */
static
tcp_handler(ip)
in_Header *ip;
{
register tcp_Header *tp;
tcp_PseudoHeader ph;
int len;
int diff; /* signed, please */
register tcp_Socket *s;
word flags;
long diffticks, ldiff; /* must be signed */
len = in_GetHdrlenBytes(ip);
tp = (tcp_Header *)((byte *)ip + len); /* tcp frame pointer */
len = ntohs(ip->length) - len; /* len of tcp data */
flags = ntohs(tp->flags); /* flags from pkt */
/* demux to active sockets */
for (s = tcp_allsocs; s != NULL; s = s->next)
if (s->hisport != 0 &&
ntohs(tp->dstPort) == s->myport &&
ntohs(tp->srcPort) == s->hisport &&
ntohl(ip->source) == s->hisaddr)
break;
if (s == NULL) /* demux to passive sockets, must be a new session */
for (s = tcp_allsocs; s != NULL; s = s->next)
if ((s->hisport == 0) &&
(ntohs(tp->dstPort) == s->myport))
break;
#ifdef DEBUG
if (dbugrecv) (*dbugrecv)(s, ip, tp);
#endif
if (s == NULL)
{
/* no session seems to exist, nor is there desire to start
one, so we must send a reset */
tcp_rst(ip, tp);
return (0); /* 0 to say socket is closed */
}
/* save his ethernet address */
bcopy(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0],
sizeof(eth_address));
ph.src = ip->source; /* network order now */
ph.dst = ip->destination;
ph.mbz = 0;
ph.protocol = TCP_PROTO;
ph.length = htons(len);
ph.checksum = checksum(tp, len);
if (checksum(&ph, sizeof(ph)) != 0xffff)
{
if (debug_on) outs("bad tcp checksum \n\r");
return (1);
}
/* reset code */
if (flags & tcp_FlagRST)
{
if (debug_on) outs("\7\7connection reset\n");
s->rdatalen = 0;
s->state = tcp_StateCLOSED;
if (s->dataHandler) s->dataHandler(s, 0, -1);
tcp_unthread(s);
return (0); /* say socket is closed */
}
/* update our retransmission stuff */
if (s->karn_count != 2)
if ((diffticks = set_ttimeout(0) - s->vj_last) >= 0)
{ /* we ignore the overnight case */
diffticks -= (s->vj_sa >> 3);
s->vj_sa += (word)diffticks;
if (diffticks < 0)
diffticks = -diffticks;
diffticks -= (s->vj_sd >> 2);
s->vj_sd += diffticks;
s->rto = ((s->vj_sa >> 2) + s->vj_sd) >> 1;
s->karn_count = 0;
} /* else (== 2) use the backed off rto */
switch (s->state) {
case tcp_StateLISTEN: /* accepting SYNs */
if (flags & tcp_FlagSYN)
{
s->acknum = ntohl(tp->seqnum) + 1;
s->hisport = ntohs(tp->srcPort);
s->hisaddr = ntohl(ip->source);
s->flags = tcp_FlagSYN | tcp_FlagACK;
server_hello(s); /* put greeting msg in socket */
tcp_send(s); /* we must respond immediately */
s->state = tcp_StateSYNREC;
s->unhappy = FALSE;
s->timeout = set_timeout(tcp_TIMEOUT);
}
else
tcp_rst(ip, tp); /* send a reset */
break;
case tcp_StateSYNSENT: /* added ACK Section */
if (flags & tcp_FlagSYN)
{
s->flags = tcp_FlagACK;
s->timeout = set_timeout(tcp_TIMEOUT);
/* FlagACK means connection established, else SYNREC */
if (flags & tcp_FlagACK)
{ /* but is it for the correct session? */
if (tp->acknum == htonl(s->seqnum + 1))
{
s->state = tcp_StateESTAB;
s->seqnum++; /* good increment */
s->acknum = ntohl(tp->seqnum) + 1; /*32 bits*/
s->unhappy = TRUE;
tcp_ProcessData(s, tp, len);
}
else
{ /* wrong ack, force a RST and resend SYN*/
s->flags = tcp_FlagRST;
s->unhappy = TRUE;
tcp_send(s);
s->unhappy = FALSE; /* no need to retransmit */
s->flags = tcp_FlagSYN;
tcp_send(s);
}
}
else
{
s->acknum++;
s->state = tcp_StateSYNREC;
/* maybe check sequence number stuff too */
}
}
break;
case tcp_StateSYNREC: /* recSYNSENT, sentACK, waiting EST */
if (flags & tcp_FlagSYN)
{
s->flags = tcp_FlagSYN | tcp_FlagACK;
tcp_send(s);
s->timeout = set_timeout(tcp_TIMEOUT);
}
if ((flags & tcp_FlagACK) && (ntohl(tp->acknum) == (s->seqnum + 1)))
{
s->flags = tcp_FlagACK;
tcp_send(s);
s->seqnum++;
s->unhappy = FALSE;
s->state = tcp_StateESTAB;
s->timeout = 0; /* never timeout */
tcp_ProcessData(s, tp, len);
}
break;
case tcp_StateESTAB:
if ((flags & tcp_FlagACK) == 0) return (1); /* they must ack somthing*/
s->timeout = 0L; /* we do not timeout at this point */
/* process their ack value in packet */
/* If their ack preceeds the window this is an old pkt. */
/* If ack exceeds the window then they are lying, don't believe. */
/* However, in all cases grab any useful data for us. */
ldiff = ntohl(tp->acknum) - s->seqnum; /* current - prev ack */
if (ldiff >= 0 && ldiff <= s->datalen)
{ /* their ack is in our window*/
s->seqnum += ldiff; /* update ACK'd file pointer */
diff = ldiff; /* 16 bits, bigger than our window */
s->datalen -= diff; /* deduct amount ACK'd */
/* move down residual in send buf */
bcopy(&s->data[diff], s->data, s->datalen);
}
s->flags = tcp_FlagACK; /* tell them thanks */
tcp_ProcessData(s, tp, len); /* process our data in the packet */
if (ldiff != 0 || len != 0)
tcp_sendsoon(s);
break;
case tcp_StateFINWT1:
/* process ack value in packet */
ldiff = ntohl(tp->acknum) - s->seqnum; /* current - prev ack */
if (ldiff >= 0 && ldiff <= s->datalen) /* their ack is in our window*/
{
s->seqnum += ldiff; /* update ACK'd file counter */
diff = ldiff; /* 16 bits, more than our window */
s->datalen -= diff; /* deduct amount ACK'd */
bcopy(&s->data[diff], s->data, s->datalen); /* move residual */
}
tcp_ProcessData(s, tp, len); /* process our data in the packet */
if (flags & tcp_FlagFIN) /* trying to do simultaneous close */
{
if ((ntohl(tp->acknum) >= s->seqnum + 1) &&
(ntohl(tp->seqnum) == s->acknum))
{ /* other guy wishes to close too */
s->seqnum++;
s->acknum++;
s->flags = tcp_FlagACK;
s->unhappy = TRUE;
s->timeout = set_timeout(3);
s->state = tcp_StateCLOSING;
}
}
else if (flags & tcp_FlagACK)
/* other side is legitimately acking our fin */
if ((ntohl(tp->acknum) == s->seqnum + 1) &&
(ntohl(tp->seqnum) == s->acknum) &&
(s->datalen == 0))
{
s->seqnum++;
s->datalen = 0;
s->state = tcp_StateFINWT2;
s->timeout = set_timeout(3);
s->unhappy = FALSE;
}
break;
case tcp_StateFINWT2: /* we can timeout of this state */
if ((flags & (tcp_FlagACK | tcp_FlagFIN)) ==
(tcp_FlagACK | tcp_FlagFIN))
if ((ntohl(tp->acknum) == s->seqnum) &&
(ntohl(tp->seqnum) == s->acknum))
{
s->acknum++;
s->flags = tcp_FlagACK;
tcp_send(s);
s->unhappy = FALSE;
s->timeout = set_timeout(2); /* max seg life 2 sec */
s->state = tcp_StateTIMEWT;
}
break;
case tcp_StateCLOSING:
if ((flags & (tcp_FlagACK | tcp_FlagFIN)) == tcp_FlagACK)
if ((tp->acknum == htonl(s->seqnum)) &&
(tp->seqnum == htonl(s->acknum))) /* all agree */
{
s->state = tcp_StateTIMEWT;
s->timeout = set_timeout(tcp_TIMEOUT);
s->unhappy = FALSE; /* no more retransmissions */
}
break;
case tcp_StateLASTACK:
if (flags & tcp_FlagFIN) /* they lost our two packets, back up */
{
s->flags = tcp_FlagACK;
tcp_send(s);
s->flags = tcp_FlagACK | tcp_FlagFIN;
tcp_send(s);
s->unhappy = FALSE;
}
else
{
if ((ntohl(tp->acknum) == (s->seqnum + 1)) &&
(ntohl(tp->seqnum) == s->acknum))
s->state = tcp_StateCLOSED; /* no 2 msl */
tcp_unthread(s);
return (1);
}
break;
case tcp_StateTIMEWT:
s->flags = tcp_FlagACK;
s->acknum = ntohl(tp->seqnum) + 1;
tcp_send(s);
break;
} /* end switch */
if (s->unhappy == TRUE) tcp_sendsoon(s);
return (1); /* success */
}
/*
* Process the data in an incoming packet.
* Called from all states where incoming data can be received: established,
* fin-wait-1, fin-wait-2
*/
static int
tcp_ProcessData(s, tp, len)
tcp_Socket *s;
tcp_Header *tp;
int len;
{
register int diff, x;
long ldiff; /* signed */
byte *dp;
word flags, *options, numoptions, opt_temp;
if (s == NULL || tp == NULL) return (0); /* failure */
s->window = ntohs(tp->window);
flags = ntohs(tp->flags);
ldiff = s->acknum - ntohl(tp->seqnum); /* signed long */
if (flags & tcp_FlagSYN) ldiff--; /* SYN counts as one unit */
diff = ldiff; /* 16 bit version */
/* find the data portion */
x = tcp_GetDataOffset(tp) << 2; /* quadword to byte format */
dp = (byte *)tp + x;
/* process those options */
if (numoptions = x - sizeof(tcp_Header))
{
options = (word *)(tp + sizeof(tcp_Header));
while (numoptions-- > 0)
switch (*options++)
{
case 0: numoptions = 0; /* end of options */
case 1: break; /* nop */
/* we are very liberal on MSS stuff */
case 2: if (*options == 2)
{
opt_temp = ntohs(*(word*)(&options[1]));
if (opt_temp < s->mss)
s->mss = opt_temp;
}
numoptions -= 2 + *options;
options += *options;
break;
} /* end of switch and while */
} /* end of if */
/* done option processing */
len -= x; /* remove the header length */
if (diff >= 0) /* skip already received bytes */
{
dp += diff;
len -= diff;
if (s->dataHandler != NULL)
s->acknum += s->dataHandler(s, dp, len);
else
/* no handler, just dump to buffer, should be indexed, handles goofs */
/* limit receive size to our window */
if (s->rdatalen >= 0)
{
if (len > (x = tcp_MaxBufSize - s->rdatalen))
len = x;
if (len > 0)
{ /* new ack begins at end of data */
s->acknum += len;
bcopy(dp, &s->rdata[s->rdatalen], len);
s->rdatalen += len;
}
} /* end of if (s->datalen.. else */
} /* end of if (diff > 0) */
s->unhappy = (s->datalen != 0)? TRUE: FALSE;
if (flags & tcp_FlagFIN)
{
s->acknum++;
switch (s->state)
{
case tcp_StateESTAB:
s->err_msg = "Connection closed.";
/* tcp_StateCLOSWT ... here we go */
x = tcp_StateLASTACK;
s->flags |= tcp_FlagFIN;
s->unhappy = TRUE; /* to send packet */
break;
case tcp_StateFINWT1: /* on ack should go to finwt2 */
s->flags |= tcp_FlagFIN;
/* not certain of this addition */
x = tcp_StateCLOSING;
break;
case tcp_StateFINWT2:
x = tcp_StateTIMEWT;
break;
} /* end of switch (s->state) */
s->state = x;
} /* end of if (flags & tcp_FlagFIN) */
s->timeout = set_timeout(tcp_TIMEOUT);
return (1); /* success */
}
/*
* Format and send an outgoing segment
*/
static int
tcp_send(s)
tcp_Socket *s;
{
tcp_PseudoHeader ph;
struct pkt
{
in_Header in;
tcp_Header tcp;
word maxsegopt[2];
} register *pkt;
byte *dp;
int senddatalen, sendtotlen, sendpktlen;
register int ippkt; /* 1..s->cwindow */
if (s == NULL) return (0); /* failure */
pkt = (struct pkt *)eth_formatpacket(&s->hisethaddr[0], 0x0008);
dp = (byte *)pkt->maxsegopt; /* dp constant for multi-packet sends */
/* this is our total possible send size */
if (s->window > 0)
senddatalen = (s->datalen >= s->window)?
s->window - 1: s->datalen;
else senddatalen = 0;
sendtotlen = 0; /* running count of what we've sent */
/* step through our packets */
for (ippkt = 1; ippkt <= s->cwindow; ippkt++)
{ /* adjust size for each packet */
if (senddatalen > s->mss)
senddatalen = s->mss;
/* tcp header */
pkt->tcp.srcPort = htons(s->myport);
pkt->tcp.dstPort = htons(s->hisport);
pkt->tcp.seqnum = htonl(s->seqnum + sendtotlen);
pkt->tcp.acknum = htonl(s->acknum);
pkt->tcp.window = htons(s->rmaxdatalen - s->rdatalen);
pkt->tcp.flags = htons(s->flags | 0x5000);
pkt->tcp.checksum = 0;
pkt->tcp.urgentPointer = 0;
/* do options if this is our first packet */
if ((s->flags & (tcp_FlagSYN | tcp_FlagACK)) == tcp_FlagSYN)
{
sendpktlen = sizeof(tcp_Header) +
sizeof(in_Header) + 4;
pkt->tcp.flags = htons(ntohs(pkt->tcp.flags) + 0x1000);
pkt->maxsegopt[0] = 0x0402;
pkt->maxsegopt[1] = htons(s->mss);
dp += 4;
}
else
{
/* handle no-data, not-first-SYN packets */
sendpktlen = sizeof(tcp_Header) + sizeof(in_Header);
if (senddatalen > 0) /* handle packets with data */
{
sendpktlen += senddatalen;
bcopy(s->data, dp, senddatalen);
}
}
/* Internet header */
pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
pkt->in.tos = 0; /* crummy service is ok */
pkt->in.identification = htons(++ip_id); /* pre-inc req'd */
pkt->in.frag = 0; /* we can't do fragments */
#ifdef KERMIT
pkt->in.ttl = 60; /* seconds */
#else
pkt->in.ttl = 254;
#endif
pkt->in.proto = TCP_PROTO;
pkt->in.checksum = 0;
pkt->in.source = htonl(my_ip_addr);
pkt->in.destination = htonl(s->hisaddr);
pkt->in.length = htons(sendpktlen);
pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
/* compute tcp checksum */
ph.src = pkt->in.source; /* already in network order */
ph.dst = pkt->in.destination;
ph.mbz = 0;
ph.protocol = TCP_PROTO;
ph.length = htons(sendpktlen - sizeof(in_Header));
ph.checksum = checksum(&pkt->tcp,
(sendpktlen - sizeof(in_Header) + 1) & 0xfffe);
pkt->tcp.checksum = ~checksum(&ph, sizeof(ph));
#ifdef DEBUG
if (dbugxmit) (*dbugxmit)(s, &pkt->in, &pkt->tcp);
#endif
if (eth_send(htons(pkt->in.length)) == 0)
return (0); /* sending failed */
sendtotlen += senddatalen;
} /* do next ip pkt */
if (s->karn_count == 2)
{
if (s->rto) s->rto = (s->rto * 3) / 2;
else s->rto = 4;
}
else
{
s->vj_last = set_ttimeout(0);
s->karn_count = 0;
}
s->rtt_time = set_ttimeout(s->rto);
return (1); /* success */
}
/*
* Format and send a reset tcp packet
*/
int
tcp_rst(in_Header *his_ip, tcp_Header *oldtcpp)
{
tcp_PseudoHeader ph;
struct pkt
{
in_Header in;
tcp_Header tcp;
word maxsegopt[2];
} register *pkt;
eth_Header *eth;
register int sendtotlen; /* length of packet */
if (his_ip == NULL || oldtcpp == NULL) return (0); /* failure */
/* see RFC 793 page 65 for details */
oldtcpp->flags = ntohs(oldtcpp->flags); /* net to local */
if (oldtcpp->flags & tcp_FlagRST)
return (0);
if ((oldtcpp->flags & tcp_FlagACK) == 0)
oldtcpp->flags = tcp_FlagACK;
else
oldtcpp->flags = 0;
/* reads his Ethernet address or garbage */
eth = eth_hardware((byte *)his_ip);
pkt = (struct pkt *)eth_formatpacket(eth, 0x0008);
sendtotlen = sizeof(tcp_Header) + sizeof(in_Header);
pkt->in.length = htons(sendtotlen);
/* TCP header */
pkt->tcp.srcPort = oldtcpp->dstPort;
pkt->tcp.dstPort = oldtcpp->srcPort;
pkt->tcp.seqnum = oldtcpp->acknum;
pkt->tcp.acknum = htonl(ntohl(oldtcpp->seqnum) + 1);
pkt->tcp.window = 0;
pkt->tcp.flags = htons(tcp_FlagRST | oldtcpp->flags | 0x5000);
pkt->tcp.checksum = 0;
pkt->tcp.urgentPointer = 0;
/* Internet header */
pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
pkt->in.tos = 0;
pkt->in.identification = htons(++ip_id); /* use pre-inc here */
pkt->in.frag = 0;
#ifdef KERMIT
pkt->in.ttl = 60; /* time to live */
#else
pkt->in.ttl = 254;
#endif
pkt->in.proto = TCP_PROTO;
pkt->in.checksum = 0;
pkt->in.source = his_ip->destination;
pkt->in.destination = his_ip->source;
pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
/* compute TCP checksum */
ph.src = pkt->in.source; /* already big endian */
ph.dst = pkt->in.destination;
ph.mbz = 0;
ph.protocol = TCP_PROTO;
ph.length = htons(sendtotlen - sizeof(in_Header));
ph.checksum = checksum(&pkt->tcp, (sendtotlen - sizeof(in_Header) + 1)
& 0xfffe);
pkt->tcp.checksum = ~checksum(&ph, sizeof(ph));
#ifdef DEBUG
if (dbugxmit) (*dbugxmit)(NULL, &pkt->in, &pkt->tcp);
#endif
return (eth_send(htons(pkt->in.length)));
}
/* Handle ICMP Redirects. ICMP processor yields new gateway IP number
in "gateway" and pointer to offending IP header which we have sent.
Find socket with destination IP address matching the gateway
sending us the Redirect message, replace the socket's destination
Ethernet address with that of the new gateway (use arp_resolve).
For TCP resend the packet, for UDP give up all hope.
*/
void
do_redirect(longword gateway, in_Header *ip)
{
register udp_Socket *s_udp;
register tcp_Socket *s_tcp;
longword host;
host = ntohl(ip->destination); /* destination IP */
/* active TCP sockets */
for (s_tcp = tcp_allsocs; s_tcp != NULL; s_tcp = s_tcp->next)
if (host == s_tcp->hisaddr) /* if same dest IP */
{
arp_resolve(gateway, &s_tcp->hisethaddr[0]);
tcp_send(s_tcp); /* resend the packet */
}
/* active UDP sockets */
for (s_udp = udp_allsocs; s_udp != NULL; s_udp = s_udp->next)
if (host == s_udp->hisaddr)
arp_resolve(gateway, &s_udp->hisethaddr[0]);
}
/**********************************************************************
* socket functions
**********************************************************************/
/* socket based procedures */
/*
* sock_yield - enable user defined yield function
*/
void
sock_yield(tcp_Socket *s, void (*fn)())
{
if (s != NULL)
s->usr_yield = fn;
else
system_yield = fn;
}
/*
* sock_mode - set binary or ascii - affects sock_gets, sock_dataready
* - set udp checksums
*/
int
sock_mode(tcp_Socket *s, word mode)
{
if (s != NULL)
{
s->soc_mode = (s->soc_mode & 0xfffc) | mode;
return (1); /* success */
}
else
return (0); /* failure */
}
/*
* sock_read - read a socket with maximum n bytes
* - busywaits until buffer is full but calls s->usr_yield
* - returns count also when connection gets closed
*/
int
sock_read(sock_type *s, byte *dp, int len)
{
register int templen, count;
count = 0;
if (s == NULL || dp == NULL) return (0); /* failure */
do
{
if (s->udp.ip_type == UDP_PROTO)
templen = udp_read(s, dp, len);
else
templen = tcp_read(s, dp, len);
if (templen = 0)
if (tcp_tick(s) == 0) return (count);
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
count += templen;
len -= templen;
}
while (len > 0);
return (count);
}
/*
* sock_fastread - read a socket with maximum n bytes
* - does not busywait until buffer is full
*/
int
sock_fastread(sock_type *s, byte *dp, int len)
{
if (s == NULL || dp == NULL) return (0); /* failure */
if (s->udp.ip_type == UDP_PROTO)
len = udp_read(s, dp, len);
else
len = tcp_read(s, dp, len);
return (len);
}
/*
* sock_write - writes data and returns length written
* - does not perform flush
* - repeatedly calls s->usr_yield
*/
int
sock_write(sock_type *s, byte *dp, int len)
{
register int offset;
int oldlen; /* don't register since di gets clobbered */
/* by odipkt.com */
oldlen = len;
offset = 0;
if (s == NULL || dp == NULL) return (0); /* failure */
while (len > 0)
{
if (s->udp.ip_type == UDP_PROTO)
offset += udp_Write(s, dp + offset, len);
else
offset += tcp_Write(s, dp + offset, len);
len = oldlen - offset;
if (s->udp.usr_yield != NULL) (s->udp.usr_yield)();
if (tcp_tick(s) == 0) return (0); /* no socket */
}
return (oldlen);
}
int
sock_fastwrite(sock_type *s, byte *dp, int len)
{
tcp_tick(NULL); /* updates our output buffer*/
if (s == NULL) return (0); /* failure */
return ((s->udp.ip_type == UDP_PROTO)?
udp_Write(s, dp, len):
tcp_Write(s, dp, len));
}
int
sock_flush(sock_type *s)
{
if (s == NULL) return (0); /* failure */
if (s->tcp.ip_type == TCP_PROTO)
tcp_Flush(s);
return (1);
}
/*
* sock_flushnext - cause next transmission to have a flush
*/
int
sock_flushnext(sock_type *s)
{
if (s == NULL) return (0); /* failure */
if (s->tcp.ip_type == TCP_PROTO)
s->tcp.flags |= tcp_FlagPUSH;
return (1); /* success */
}
/*
* sock_putc - put a character
* - no expansion but flushes on '\n'
* - returns character
*/
int
sock_putc(sock_type *s, byte c)
{
byte ch;
ch = c;
if (s == NULL) return (-1); /* failure */
if ((ch == '\n') || (ch == '\r'))
sock_flushnext(s);
sock_write(s, &ch, 1);
return (ch & 0xff);
}
int
sock_getc(sock_type *s)
{
byte ch;
if (s == NULL) return (-1); /* failure */
sock_read(s, &ch, 1);
return (ch & 0xff);
}
#ifndef KERMIT
/*
* sock_puts - does not append carriage return in binary mode
* - returns length
*/
int
sock_puts(sock_type *s, byte *dp)
{
register int len;
if (s == NULL || dp == NULL) return (0); /* failure */
len = strlen(dp);
sock_flushnext(s);
sock_write(s, dp, len);
if (s->tcp.soc_mode & TCP_MODE_ASCII)
sock_putc(s, '\n');
return (len);
}
#endif /* KERMIT */
/*
* sock_update - update the socket window size to the other guy
* Note: a better and safer criterion is s->rdatalen < mss
* which allows room for a full incoming segment without loss or overflow.
*/
static int
sock_update(tcp_Socket *s)
{
if (s == NULL) return (0); /* failure */
if (s->ip_type == TCP_PROTO)
{
if (s->rdatalen < (3 * s->rmaxdatalen) / 4)
tcp_send(s); /* update the window */
else
tcp_sendsoon(s);
}
return (1); /* success */
}
#ifndef KERMIT
/*
* sock_gets - read a string from any socket
* - return length of returned string
* - removes end of line terminator
*/
word
sock_gets(sock_type *s, byte *dp, int n)
{
int len, templen;
byte *src_p, *temp, *temp2;
word *np;
if (s == NULL || dp == NULL) return (0); /* failure */
if (s->udp.ip_type == UDP_PROTO) {
if (n > udp_MaxBufSize) n = udp_MaxBufSize;
src_p = s->udp.rdata;
np = &s->udp.rdatalen;
} else {
if (n > tcp_MaxBufSize) n = tcp_MaxBufSize;
src_p = s->tcp.rdata;
np = &s->tcp.rdatalen;
}
src_p[ *np ] = 0; /* terminate string */
strncpy(dp, src_p, n); /* copy everything */
dp[ n-1 ] = 0; /* terminate */
if (temp = strchr(dp, '\n')) *temp = 0;
if (temp2= strchr(dp, '\r')) *temp2= 0;
len = strlen(dp);
/* skip if there were no crs or lfs ??? */
if ((temp2 == NULL) && (temp == NULL) && (strlen(dp) < n - 1)) {
*dp = 0;
return (0);
}
/* skip over \n and \r but stop on end */
#ifndef OLD
if (temp) templen = temp - (byte *)dp;
else if (temp2) templen = temp2 - (byte *)dp;
else templen = len + 1;
if (templen) {
templen++;
bcopy(&src_p[ templen ], src_p, *np -= templen);
} else
*np = 0;
#else
temp = &src_p[ len + 1 ];
while (*temp && ((*temp == '\n') || (*temp == '\r')))
temp++;
if (*temp)
bcopy(temp, src_p, *np = strlen(temp));
else
*np = 0;
#endif /* OLD */
sock_update((tcp_Socket *)s); /* new window */
return (len);
}
#endif /* KERMIT */
/*
* sock_dataready - returns number of bytes waiting to be read
* - if in ASCII mode, return 0 until a line is present
* or the buffer is full
*/
word
sock_dataready(sock_type *s)
{
register int len;
register byte *p;
if (s == NULL) return (0); /* failure */
if ((len = s->tcp.rdatalen) == 0) return (0);
if (s->tcp.soc_mode & TCP_MODE_ASCII)
{
if (len == tcp_MaxBufSize)
return (tcp_MaxBufSize);
/* check for terminating \n \r */
p = s->tcp.rdata;
if ((strchr(p, '\n') == NULL) || (strchr(p, '\r') == NULL))
return (len);
return (0);
}
return (len);
}
int
sock_established(sock_type *s)
{
if (s == NULL) return (0); /* failure */
switch (s->tcp.ip_type)
{
case UDP_PROTO:
return (1);
case TCP_PROTO:
return (s->tcp.state == tcp_StateESTAB);
default:
return (0);
}
}
int
sock_close(s)
sock_type *s;
{
register int status;
if (s == NULL) return (0); /* failure */
switch (s->udp.ip_type)
{
case UDP_PROTO :
status = udp_close(s);
break;
case TCP_PROTO :
status = tcp_close(s);
tcp_tick(s);
break;
}
return (status); /* success */
}
/* return count of chars in socket receive buffer which lie before char ch */
word
sock_findch(sock_type *s, byte ch)
{
int i;
int len;
register byte c = ch;
register byte * p;
tcp_tick(s); /* update socket buffer */
if ((len = sock_dataready(s)) == 0) /* get qty available */
return (-1); /* say no char available */
p = s->tcp.rdata; /* point to start of socket buffer */
for (i = 0; i < len; i++) /* scan for byte ch */
if (*p++ == ch) break; /* stop when found */
return (i);
}
/*
* ip user level timer stuff
* void ip_timer_init(void *s, int delayseconds)
* int ip_timer_expired(void *s)
* - 0 if not expired
*/
void
ip_timer_init(s, delayseconds)
udp_Socket *s;
int delayseconds;
{
if (s == NULL)
return;
if (delayseconds != 0)
s->usertimer = set_timeout(delayseconds);
else
s->usertimer = 0;
}
int
ip_timer_expired(s)
udp_Socket *s;
{
if (s == NULL) return (1); /* say time out */
if (s->usertimer == 0) /* cannot expire */
return (0);
return (chk_timeout(s->usertimer));
}
long
make_timeout(word timeout)
{
if (timeout) return (set_timeout(timeout));
return (0);
}
/*
* check_timeout - test agains timeout clock - account for overflow
*/
int
check_timeout(unsigned long timeout)
{
if (timeout) return (chk_timeout(timeout));
return (0);
}
/*
* ip_delay0 called by macro sock_wait_established()
* ip_delay1 called by macro sock_wait_intput()
* ip_delay2 called by macro sock_wait_closed();
*
*/
ip_delay0(s, timeoutseconds, fn, statusptr)
sock_type *s;
int timeoutseconds;
procref fn;
int *statusptr;
{
register int status;
if (s == NULL)
{
status = -1; /* failure */
if (statusptr != NULL) *statusptr = status;
return (status);
}
ip_timer_init(s, timeoutseconds);
do
{
if (s->tcp.ip_type == TCP_PROTO)
if (tcp_established(s))
{
status = 0;
break;
}
if (tcp_tick(s) == 0)
{
s->tcp.err_msg = "Host refused connection";
status = -1; /* get an early reset */
break;
}
if (ip_timer_expired(s))
{
sock_close(s);
status = -1;
break;
}
if (fn != NULL) if (status = fn(s)) break;
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
if (s->tcp.ip_type == UDP_PROTO)
{
status = 0;
break;
}
} while (1 == 1);
if (statusptr != NULL) *statusptr = status;
return (status);
}
int
ip_delay1(s, timeoutseconds, fn, statusptr)
sock_type *s;
int timeoutseconds;
procref fn;
int *statusptr;
{
register int status;
if (s == NULL)
{
status = -1; /* failure */
if (statusptr != NULL) *statusptr = status;
return (status);
}
ip_timer_init(s, timeoutseconds);
sock_flush(s); /* new enhancement */
do
{
if (sock_dataready(s))
{
status = 0;
break;
}
if (tcp_tick(s) == 0)
{
status = 1;
break;
}
if (ip_timer_expired(s))
{
sock_close(s);
status = -1;
break;
}
if (fn != NULL)
if (status = fn(s)) break;
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
} while (1 == 1);
if (statusptr != NULL) *statusptr = status;
return (status);
}
int
ip_delay2(s, timeoutseconds, fn, statusptr)
sock_type *s;
int timeoutseconds;
procref fn;
int *statusptr;
{
register int status;
if (s == NULL)
{
status = 0; /* failure */
if (statusptr != NULL) *statusptr = status;
return (status);
}
ip_timer_init(s, timeoutseconds);
if (s->tcp.ip_type != TCP_PROTO) return (1);
do
{
if (tcp_tick(s) == 0) /* 0 means no socket */
{
status = 1;
break;
}
if (ip_timer_expired(s))
{
sock_abort(s);
status = 0;
break;
}
if (fn != NULL)
if (status = fn(s)) break;
if (s->tcp.usr_yield != NULL) (s->tcp.usr_yield)();
} while (1 == 1);
if (statusptr != NULL) *statusptr = status;
return (status);
}
byte *
rip(byte *s) /* put null terminator on CR or LF */
{
register byte *temp;
if (s == NULL) return (NULL); /* failure */
if (temp = strchr(s, '\n')) *temp = 0;
if (temp = strchr(s, '\r')) *temp = 0;
return (s);
}