home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
hamradio
/
s920603.zip
/
TCPSOCK.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-21
|
8KB
|
360 lines
#include "global.h"
#include "tcp.h"
#include "socket.h"
#include "usock.h"
static void s_trcall __ARGS((struct tcb *tcb,int cnt));
static void s_tscall __ARGS((struct tcb *tcb,int old,int new));
static void s_ttcall __ARGS((struct tcb *tcb,int cnt));
static void trdiscard __ARGS((struct tcb *tcb,int cnt));
static void autobind __ARGS((struct usock *up));
int16 Lport = 1024;
int
so_tcp(up,protocol)
struct usock *up;
int protocol;
{
up->type = TYPE_TCP;
return 0;
}
int
so_tcp_listen(up,backlog)
struct usock *up;
int backlog;
{
int s;
struct sockaddr_in *local;
struct socket lsock;
s = up->index;
if(up->name == NULLCHAR)
autobind(up);
local = (struct sockaddr_in *)up->name;
lsock.address = local->sin_addr.s_addr;
lsock.port = local->sin_port;
up->cb.tcb = open_tcp(&lsock,NULLSOCK,
backlog ? TCP_SERVER:TCP_PASSIVE,0,
s_trcall,s_ttcall,s_tscall,up->tos,s);
return 0;
}
int
so_tcp_conn(up)
struct usock *up;
{
int s;
struct tcb *tcb;
struct socket lsock,fsock;
struct sockaddr_in *local,*remote;
if(up->name == NULLCHAR){
autobind(up);
}
if(checkipaddr(up->peername,up->namelen) == -1){
errno = EAFNOSUPPORT;
return -1;
}
s = up->index;
/* Construct the TCP-style ports from the sockaddr structs */
local = (struct sockaddr_in *)up->name;
remote = (struct sockaddr_in *)up->peername;
if(local->sin_addr.s_addr == INADDR_ANY)
/* Choose a local address */
local->sin_addr.s_addr = locaddr(remote->sin_addr.s_addr);
lsock.address = local->sin_addr.s_addr;
lsock.port = local->sin_port;
fsock.address = remote->sin_addr.s_addr;
fsock.port = remote->sin_port;
/* Open the TCB in active mode */
up->cb.tcb = open_tcp(&lsock,&fsock,TCP_ACTIVE,0,
s_trcall,s_ttcall,s_tscall,up->tos,s);
/* Wait for the connection to complete */
while((tcb = up->cb.tcb) != NULLTCB && tcb->state != TCP_ESTABLISHED){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(tcb == NULLTCB){
/* Probably got refused */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
return 0;
}
int
so_tcp_recv(up,bpp,from,fromlen)
struct usock *up;
struct mbuf **bpp;
char *from;
int *fromlen;
{
int cnt;
struct tcb *tcb;
while((tcb = up->cb.tcb) != NULLTCB && tcb->r_upcall != trdiscard
&& (cnt = recv_tcp(tcb,bpp,(int16)0)) == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(tcb == NULLTCB){
/* Connection went away */
errno = ENOTCONN;
return -1;
} else if(tcb->r_upcall == trdiscard){
/* Receive shutdown has been done */
errno = ENOTCONN; /* CHANGE */
return -1;
}
return cnt;
}
int
so_tcp_send(up,bp,to)
struct usock *up;
struct mbuf *bp;
char *to;
{
struct tcb *tcb;
int cnt;
if((tcb = up->cb.tcb) == NULLTCB){
free_p(bp);
errno = ENOTCONN;
return -1;
}
cnt = send_tcp(tcb,bp);
while((tcb = up->cb.tcb) != NULLTCB &&
tcb->sndcnt > tcb->window){
/* Send queue is full */
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(tcb == NULLTCB){
errno = ENOTCONN;
return -1;
}
return cnt;
}
int
so_tcp_qlen(up,rtx)
struct usock *up;
int rtx;
{
int len;
switch(rtx){
case 0:
len = up->cb.tcb->rcvcnt;
break;
case 1:
len = up->cb.tcb->sndcnt;
break;
}
return len;
}
int
so_tcp_kick(up)
struct usock *up;
{
kick_tcp(up->cb.tcb);
return 0;
}
int
so_tcp_shut(up,how)
struct usock *up;
int how;
{
switch(how){
case 0: /* No more receives -- replace upcall */
up->cb.tcb->r_upcall = trdiscard;
break;
case 1: /* Send EOF */
close_tcp(up->cb.tcb);
break;
case 2: /* Blow away TCB */
reset_tcp(up->cb.tcb);
up->cb.tcb = NULLTCB;
break;
}
return 0;
}
int
so_tcp_close(up)
struct usock *up;
{
if(up->cb.tcb != NULLTCB){ /* In case it's been reset */
up->cb.tcb->r_upcall = trdiscard;
/* Tell the TCP_CLOSED upcall there's no more socket */
up->cb.tcb->user = -1;
close_tcp(up->cb.tcb);
}
return 0;
}
/* TCP receive upcall routine */
static void
s_trcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
/* Wake up anybody waiting for data, and let them run */
psignal(itop(tcb->user),1);
pwait(NULL);
}
/* TCP transmit upcall routine */
static void
s_ttcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
/* Wake up anybody waiting to send data, and let them run */
psignal(itop(tcb->user),1);
pwait(NULL);
}
/* TCP state change upcall routine */
static void
s_tscall(tcb,old,new)
struct tcb *tcb;
int old,new;
{
int s,ns;
struct usock *up,*nup,*oup;
union sp sp;
s = tcb->user;
oup = up = itop(s);
switch(new){
case TCP_CLOSED:
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this tcb so they will notice it disappearing.
*/
if(up != NULLUSOCK){
up->cb.tcb = NULLTCB;
up->errcodes[0] = tcb->reason;
up->errcodes[1] = tcb->type;
up->errcodes[2] = tcb->code;
}
del_tcp(tcb);
break;
case TCP_SYN_RECEIVED:
/* Handle an incoming connection. If this is a server TCB,
* then we're being handed a "clone" TCB and we need to
* create a new socket structure for it. In either case,
* find out who we're talking to and wake up the guy waiting
* for the connection.
*/
if(tcb->flags.clone){
/* Clone the socket */
ns = socket(AF_INET,SOCK_STREAM,0);
nup = itop(ns);
ASSIGN(*nup,*up);
tcb->user = ns;
nup->cb.tcb = tcb;
/* Allocate new memory for the name areas */
nup->name = mallocw(SOCKSIZE);
nup->peername = mallocw(SOCKSIZE);
nup->index = ns;
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
s = ns;
} else {
/* Allocate space for the peer's name */
up->peername = mallocw(SOCKSIZE);
/* Store the old socket # in the old socket */
up->rdysock = s;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = up->cb.tcb->conn.local.address;
sp.in->sin_port = up->cb.tcb->conn.local.port;
up->namelen = SOCKSIZE;
sp.p = up->peername;
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = up->cb.tcb->conn.remote.address;
sp.in->sin_port = up->cb.tcb->conn.remote.port;
up->peernamelen = SOCKSIZE;
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
break;
default: /* Ignore all other state transitions */
break;
}
psignal(up,0); /* In case anybody's waiting */
}
/* Discard data received on a TCP connection. Used after a receive shutdown or
* close_s until the TCB disappears.
*/
static void
trdiscard(tcb,cnt)
struct tcb *tcb;
int cnt;
{
struct mbuf *bp;
recv_tcp(tcb,&bp,(int16)cnt);
free_p(bp);
}
/* Issue an automatic bind of a local address */
static void
autobind(up)
struct usock *up;
{
struct sockaddr_in local;
int s;
s = up->index;
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = Lport++;
bind(s,(char *)&local,sizeof(struct sockaddr_in));
}
char *
tcpstate(up)
struct usock *up;
{
if(up->cb.tcb == NULLTCB)
return NULLCHAR;
return Tcpstates[up->cb.tcb->state];
}
int
so_tcp_stat(up)
struct usock *up;
{
st_tcp(up->cb.tcb);
return 0;
}