home *** CD-ROM | disk | FTP | other *** search
- #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 */
- if (up->cb.tcb == NULL) /* make it restartable after EWOULDBLOCK -Rhialto */
- 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;
- }
-
-
-