/* Application programming interface routines - based loosely on the * "socket" model in Berkeley UNIX. * * Copyright 1991 Phil Karn, KA9Q */ #include #ifdef __STDC__ #include #endif #include "global.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "iface.h" #include "ip.h" #include "tcp.h" #include "udp.h" #include "ax25.h" #include "lapb.h" #include "netrom.h" #include "nr4.h" #include "proc.h" #include "lzw.h" #include "usock.h" #include "socket.h" #include "config.h" static void autobind __ARGS((int s,int af)); static int checkaddr __ARGS((int type,char *name,int namelen)); static void rip_recv __ARGS((struct raw_ip *rp)); 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 s_urcall __ARGS((struct iface *iface,struct udp_cb *udp,int cnt)); static void trdiscard __ARGS((struct tcb *tcb,int cnt)); #ifdef NETROM static void s_nrcall __ARGS((struct nr4cb *cb,int cnt)); static void s_nscall __ARGS((struct nr4cb *cb,int old,int new)); static void s_ntcall __ARGS((struct nr4cb *cb,int cnt)); #endif static int16 Lport = 1024; char *Socktypes[] = { "Not Used", "TCP", "UDP", "AX25 I", "AX25 UI", "Raw IP", "NETROM3", "NETROM", "Loc St", "Loc Dg" }; char Badsocket[] = "Bad socket"; struct usock *Usock; /* Socket entry array */ int Nusock = DEFNSOCK; /* Number of socket entries */ /* The following two variables are needed because there can be only one * socket listening on each of the AX.25 modes (I and UI) */ #ifdef AX25 int Axi_sock = -1; /* Socket number listening for AX25 connections */ static int Axui_sock = -1; /* Socket number listening for AX25 UI frames */ static struct mbuf *Bcq; /* Queue of incoming UI frames */ /* Function that handles incoming UI frames from lapb.c */ void beac_input(iface,src,bp) struct iface *iface; char *src; struct mbuf *bp; { struct mbuf *hdr; struct sockaddr_ax *sax; if(Axui_sock == -1){ /* Nobody there to read it */ free_p(bp); } else { if((hdr = pushdown(NULLBUF,sizeof(struct sockaddr_ax))) == NULLBUF){ free_p(bp); return; } sax = (struct sockaddr_ax *)hdr->data; sax->sax_family = AF_AX25; memcpy(sax->ax25_addr,src,AXALEN); strncpy(sax->iface,iface->name,ILEN); hdr->next = bp; enqueue(&Bcq,hdr); } } #endif /* Initialize user socket array */ void sockinit() { if(Usock != NULLUSOCK) return; /* Already initialized */ Usock = (struct usock *)callocw(Nusock,sizeof(struct usock)); } /* Create a user socket, return socket index * The mapping to actual protocols is as follows: * * * ADDRESS FAMILY Stream Datagram Raw Seq. Packet * * AF_INET TCP UDP IP * AF_AX25 I-frames UI-frames * AF_NETROM NET/ROM L3 NET/ROM L4 * AF_LOCAL stream loopback packet loopback */ int socket(af,type,protocol) int af; /* Address family */ int type; /* Stream or datagram */ int protocol; /* Used for raw IP sockets */ { register struct usock *up; int s; for(up=Usock;up < &Usock[Nusock];up++) if(up->type == NOTUSED) break; if(up == &Usock[Nusock]){ /* None left */ errno = EMFILE; return -1; } up->refcnt = 1; s = up - Usock + SOCKBASE; errno = 0; up->noblock = 0; up->rdysock = -1; up->cb.p = NULLCHAR; up->peername = up->name = NULLCHAR; up->namelen = up->peernamelen = 0; up->owner = Curproc; up->obuf = NULLBUF; memset(up->errcodes,0,sizeof(up->errcodes)); memset(up->eol,0,sizeof(up->eol)); up->flush = '\n'; /* default is line buffered */ switch(af){ case AF_LOCAL: up->cb.local = (struct loc *) callocw(1,sizeof(struct loc)); up->cb.local->peer = up; /* connect to self */ switch(type){ case SOCK_STREAM: up->type = TYPE_LOCAL_STREAM; up->cb.local->hiwat = LOCSFLOW; break; case SOCK_DGRAM: up->type = TYPE_LOCAL_DGRAM; up->cb.local->hiwat = LOCDFLOW; break; default: free(up->cb.local); errno = ESOCKTNOSUPPORT; break; } break; #ifdef AX25 case AF_AX25: switch(type){ case SOCK_STREAM: up->type = TYPE_AX25I; break; case SOCK_DGRAM: up->type = TYPE_AX25UI; break; default: errno = ESOCKTNOSUPPORT; break; } strcpy(up->eol,AX_EOL); break; #endif #ifdef NETROM case AF_NETROM: switch(type){ case SOCK_RAW: up->type = TYPE_NETROML3; up->cb.rnr = raw_nr((char)protocol); break; case SOCK_SEQPACKET: up->type = TYPE_NETROML4; break; default: errno = ESOCKTNOSUPPORT; break; } strcpy(up->eol,AX_EOL); break; #endif case AF_INET: switch(type){ case SOCK_STREAM: up->type = TYPE_TCP; strcpy(up->eol,INET_EOL); break; case SOCK_DGRAM: up->type = TYPE_UDP; break; case SOCK_RAW: up->type = TYPE_RAW; up->cb.rip = raw_ip(protocol,rip_recv); up->cb.rip->user = s; break; default: errno = ESOCKTNOSUPPORT; break; } break; default: errno = EAFNOSUPPORT; break; } if(errno) return -1; return s; } /* Attach a local address/port to a socket. If not issued before a connect * or listen, will be issued automatically */ int bind(s,name,namelen) int s; /* Socket index */ char *name; /* Local name */ int namelen; /* Length of name */ { register struct usock *up; union sp local; struct socket lsock; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){ errno = EINVAL; return -1; } if(name == NULLCHAR){ errno = EFAULT; return -1; } if(up->name != NULLCHAR){ /* Bind has already been issued */ errno = EINVAL; return -1; } if(checkaddr(up->type,name,namelen) == -1){ /* Incorrect length or family for chosen protocol */ errno = EAFNOSUPPORT; return -1; } /* Stash name in an allocated block */ up->namelen = namelen; up->name = mallocw(namelen); memcpy(up->name,name,namelen); /* Create control block for datagram sockets */ switch(up->type){ case TYPE_UDP: local.in = (struct sockaddr_in *)up->name; lsock.address = local.in->sin_addr.s_addr; lsock.port = local.in->sin_port; up->cb.udp = open_udp(&lsock,s_urcall); up->cb.udp->user = s; break; #ifdef AX25 case TYPE_AX25UI: if(Axui_sock != -1){ errno = EADDRINUSE; return -1; } Axui_sock = s; break; #endif } return 0; } /* Post a listen on a socket */ int listen(s,backlog) int s; /* Socket index */ int backlog; /* 0 for a single connection, 1 for multiple connections */ { register struct usock *up; union sp local; struct socket lsock; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){ errno = EINVAL; return -1; } if(up->cb.p != NULLCHAR){ errno = EISCONN; return -1; } switch(up->type){ case TYPE_TCP: if(up->name == NULLCHAR) autobind(s,AF_INET); local.in = (struct sockaddr_in *)up->name; lsock.address = local.in->sin_addr.s_addr; lsock.port = local.in->sin_port; up->cb.tcb = open_tcp(&lsock,NULLSOCK, backlog ? TCP_SERVER:TCP_PASSIVE,0, s_trcall,s_ttcall,s_tscall,0,s); break; #ifdef AX25 case TYPE_AX25I: if(up->name == NULLCHAR) autobind(s,AF_AX25); if(s != Axi_sock){ errno = EOPNOTSUPP; return -1; } local.ax = (struct sockaddr_ax *)up->name; up->cb.ax25 = open_ax25(NULLIF,local.ax->ax25_addr,NULLCHAR, backlog ? AX_SERVER:AX_PASSIVE,0, s_arcall,s_atcall,s_ascall,s); break; #endif #ifdef NETROM case TYPE_NETROML4: if(up->name == NULLCHAR) autobind(s,AF_NETROM); local.nr = (struct sockaddr_nr *)up->name; up->cb.nr4 = open_nr4(&local.nr->nr_addr,NULLNRADDR, backlog ? AX_SERVER:AX_PASSIVE, s_nrcall,s_ntcall,s_nscall,s); break; #endif default: /* Listen not supported on datagram sockets */ errno = EOPNOTSUPP; return -1; } return 0; } /* Initiate active open. For datagram sockets, merely bind the remote address. */ int connect(s,peername,peernamelen) int s; /* Socket index */ char *peername; /* Peer name */ int peernamelen; /* Length of peer name */ { register struct usock *up; union cb cb; union sp local,remote; struct socket lsock,fsock; #ifdef AX25 struct iface *iface; #endif if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->type == TYPE_LOCAL_DGRAM || up->type == TYPE_LOCAL_STREAM){ errno = EINVAL; return -1; } if(peername == NULLCHAR){ /* Connect must specify a remote address */ errno = EFAULT; return -1; } if(checkaddr(up->type,peername,peernamelen) == -1){ errno = EAFNOSUPPORT; return -1; } /* Raw socket control blocks are created in socket() */ if(up->type != TYPE_RAW && up->type != TYPE_NETROML3 && up->cb.p != NULLCHAR){ errno = EISCONN; return -1; } up->peername = mallocw(peernamelen); memcpy(up->peername,peername,peernamelen); up->peernamelen = peernamelen; /* Set up the local socket structures */ if(up->name == NULLCHAR){ switch(up->type){ case TYPE_TCP: case TYPE_UDP: case TYPE_RAW: autobind(s,AF_INET); break; #ifdef AX25 case TYPE_AX25I: case TYPE_AX25UI: autobind(s,AF_AX25); break; #endif #ifdef NETROM case TYPE_NETROML3: case TYPE_NETROML4: autobind(s,AF_NETROM); break; #endif } } switch(up->type){ case TYPE_TCP: /* Construct the TCP-style ports from the sockaddr structs */ local.in = (struct sockaddr_in *)up->name; remote.in = (struct sockaddr_in *)up->peername; if(local.in->sin_addr.s_addr == INADDR_ANY) /* Choose a local address */ local.in->sin_addr.s_addr = locaddr(remote.in->sin_addr.s_addr); lsock.address = local.in->sin_addr.s_addr; lsock.port = local.in->sin_port; fsock.address = remote.in->sin_addr.s_addr; fsock.port = remote.in->sin_port; /* Open the TCB in active mode */ up->cb.tcb = open_tcp(&lsock,&fsock,TCP_ACTIVE,0, s_trcall,s_ttcall,s_tscall,0,s); /* Wait for the connection to complete */ while((cb.tcb = up->cb.tcb) != NULLTCB && cb.tcb->state != TCP_ESTABLISHED){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.tcb == NULLTCB){ /* Probably got refused */ free(up->peername); up->peername = NULLCHAR; errno = ECONNREFUSED; return -1; } break; case TYPE_UDP: #ifdef AX25 case TYPE_AX25UI: #endif #ifdef NETROM case TYPE_NETROML3: #endif case TYPE_RAW: /* Control block already created by bind() */ break; #ifdef AX25 case TYPE_AX25I: local.ax = (struct sockaddr_ax *)up->name; remote.ax = (struct sockaddr_ax *)up->peername; if((iface = if_lookup(remote.ax->iface)) == NULLIF){ errno = EINVAL; return -1; } if(local.ax->ax25_addr[0] == '\0'){ /* The local address was unspecified; set it from * the interface address */ memcpy(local.ax->ax25_addr,iface->hwaddr,AXALEN); } /* If we already have an AX25 link we can use it */ if((up->cb.ax25 = find_ax25(remote.ax->ax25_addr)) != NULLAX25 && up->cb.ax25->state != LAPB_DISCONNECTED && up->cb.ax25->user == -1) { up->cb.ax25->user = s; up->cb.ax25->r_upcall = s_arcall; up->cb.ax25->t_upcall = s_atcall; up->cb.ax25->s_upcall = s_ascall; if(up->cb.ax25->state == LAPB_CONNECTED || up->cb.ax25->state == LAPB_RECOVERY) return 0; } else up->cb.ax25 = open_ax25(iface,local.ax->ax25_addr, remote.ax->ax25_addr,AX_ACTIVE, Axwindow,s_arcall,s_atcall,s_ascall,s); /* Wait for the connection to complete */ while((cb.ax25 = up->cb.ax25) != NULLAX25 && cb.ax25->state != LAPB_CONNECTED){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.ax25 == NULLAX25){ /* Connection probably already exists */ free(up->peername); up->peername = NULLCHAR; errno = ECONNREFUSED; return -1; } break; #endif #ifdef NETROM case TYPE_NETROML4: local.nr = (struct sockaddr_nr *)up->name; remote.nr = (struct sockaddr_nr *)up->peername; up->cb.nr4 = open_nr4(&local.nr->nr_addr,&remote.nr->nr_addr, AX_ACTIVE,s_nrcall,s_ntcall,s_nscall,s); /* Wait for the connection to complete */ while((cb.nr4 = up->cb.nr4) != NULLNR4CB && cb.nr4->state != NR4STCON){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.nr4 == NULLNR4CB){ /* Connection probably already exists */ free(up->peername); up->peername = NULLCHAR; errno = ECONNREFUSED; return -1; } break; #endif } return 0; } /* Wait for a connection. Valid only for connection-oriented sockets. */ int accept(s,peername,peernamelen) int s; /* Socket index */ char *peername; /* Peer name */ int *peernamelen; /* Length of peer name */ { int i; register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->type == TYPE_LOCAL_DGRAM || up->type == TYPE_LOCAL_STREAM){ errno = EINVAL; return -1; } if(up->cb.p == NULLCHAR){ errno = EOPNOTSUPP; return -1; } /* Accept is valid only for stream sockets */ switch(up->type){ case TYPE_TCP: #ifdef AX25 case TYPE_AX25I: #endif #ifdef NETROM case TYPE_NETROML4: #endif break; default: errno = EOPNOTSUPP; return -1; } /* Wait for the state-change upcall routine to signal us */ while(up->cb.p != NULLCHAR && up->rdysock == -1){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(up->cb.p == NULLCHAR){ /* Blown away */ errno = EBADF; return -1; } i = up->rdysock; up->rdysock = -1; up = itop(i); if(peername != NULLCHAR && peernamelen != NULL){ *peernamelen = min(up->peernamelen,*peernamelen); memcpy(peername,up->peername,*peernamelen); } return i; } /* Low-level receive routine. Passes mbuf back to user; more efficient than * higher-level functions recv() and recvfrom(). Datagram sockets ignore * the len parameter. */ int recv_mbuf(s,bpp,flags,from,fromlen) int s; /* Socket index */ struct mbuf **bpp; /* Place to stash receive buffer */ int flags; /* Unused; will control out-of-band data, etc */ char *from; /* Peer address (only for datagrams) */ int *fromlen; /* Length of peer address */ { register struct usock *up; int cnt; union cb cb; struct socket fsocket; #ifdef NETROM struct nr3hdr n3hdr; #endif union sp remote; struct ip ip; struct mbuf *bp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->ibuf != NULLBUF){ /* Return input buffer */ cnt = len_p(up->ibuf); if(bpp != NULLBUFP) *bpp = up->ibuf; else free_p(up->ibuf); up->ibuf = NULLBUF; return cnt; } switch(up->type){ case TYPE_LOCAL_STREAM: case TYPE_LOCAL_DGRAM: while(up->cb.local != NULLLOC && up->cb.local->q == NULLBUF && up->cb.local->peer != NULLUSOCK){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(up->cb.local == NULLLOC){ errno = EBADF; return -1; } if(up->cb.local->q == NULLBUF && up->cb.local->peer == NULLUSOCK){ errno = ENOTCONN; return -1; } /* For datagram sockets, this will return the * first packet on the queue. For stream sockets, * this will return everything. */ bp = dequeue(&up->cb.local->q); if(up->cb.local->q == NULLBUF && (up->cb.local->flags & LOC_SHUTDOWN)) close_s(s); psignal(up,0); cnt = len_p(bp); break; case TYPE_TCP: while((cb.tcb = up->cb.tcb) != NULLTCB && cb.tcb->r_upcall != trdiscard && (cnt = recv_tcp(cb.tcb,&bp,(int16)0)) == -1){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.tcb == NULLTCB){ /* Connection went away */ errno = ENOTCONN; return -1; } else if(cb.tcb->r_upcall == trdiscard){ /* Receive shutdown has been done */ errno = ENOTCONN; /* CHANGE */ return -1; } break; case TYPE_UDP: while((cb.udp = up->cb.udp) != NULLUDP && (cnt = recv_udp(cb.udp,&fsocket,&bp)) == -1){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.udp == NULLUDP){ /* Connection went away */ errno = ENOTCONN; return -1; } if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){ remote.in = (struct sockaddr_in *)from; remote.in->sin_family = AF_INET; remote.in->sin_addr.s_addr = fsocket.address; remote.in->sin_port = fsocket.port; *fromlen = SOCKSIZE; } break; case TYPE_RAW: while((cb.rip = up->cb.rip) != NULLRIP && cb.rip->rcvq == NULLBUF){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.rip == NULLRIP){ /* Connection went away */ errno = ENOTCONN; return -1; } bp = dequeue(&cb.rip->rcvq); ntohip(&ip,&bp); cnt = len_p(bp); if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){ remote.in = (struct sockaddr_in *)from; remote.in->sin_family = AF_INET; remote.in->sin_addr.s_addr = ip.source; remote.in->sin_port = 0; *fromlen = SOCKSIZE; } break; #ifdef AX25 case TYPE_AX25I: while((cb.ax25 = up->cb.ax25) != NULLAX25 && (bp = recv_ax25(cb.ax25,(int16)0)) == NULLBUF){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.ax25 == NULLAX25){ /* Connection went away */ errno = ENOTCONN; return -1; } cnt = bp->cnt; break; case TYPE_AX25UI: while(s == Axui_sock && Bcq == NULLBUF){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(&Bcq)) != 0){ return -1; } } if(s != Axui_sock){ errno = ENOTCONN; return -1; } bp = dequeue(&Bcq); if(from != NULLCHAR && fromlen != NULLINT && *fromlen >= sizeof(struct sockaddr_ax)){ pullup(&bp,from,sizeof(struct sockaddr_ax)); *fromlen = sizeof(struct sockaddr_ax); } else { pullup(&bp,NULLCHAR,sizeof(struct sockaddr_ax)); } cnt = len_p(bp); break; #endif #ifdef NETROM case TYPE_NETROML3: while((cb.rnr = up->cb.rnr) != NULLRNR && cb.rnr->rcvq == NULLBUF){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.rnr == NULLRNR){ /* Connection went away */ errno = ENOTCONN; return -1; } bp = dequeue(&cb.rnr->rcvq); ntohnr3(&n3hdr,&bp); cnt = len_p(bp); if(from != NULLCHAR && fromlen != NULLINT && *fromlen >= sizeof(struct sockaddr_nr)){ remote.nr = (struct sockaddr_nr *)from; remote.nr->nr_family = AF_NETROM; /* The callsign of the local user is not part of NET/ROM level 3, so that field is not used here */ memcpy(remote.nr->nr_addr.node,n3hdr.source,AXALEN); *fromlen = sizeof(struct sockaddr_nr); } break; case TYPE_NETROML4: while((cb.nr4 = up->cb.nr4) != NULLNR4CB && (bp = recv_nr4(cb.nr4,(int16)0)) == NULLBUF){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.nr4 == NULLNR4CB){ /* Connection went away */ errno = ENOTCONN; return -1; } cnt = bp->cnt; break; #endif } if(bpp != NULLBUFP) *bpp = bp; else free_p(bp); return cnt; } /* Low level send routine; user supplies mbuf for transmission. More * efficient than send() or sendto(), the higher level interfaces. * The "to" and "tolen" parameters are ignored on connection-oriented * sockets. * * In case of error, bp is freed so the caller doesn't have to worry about it. */ int send_mbuf(s,bp,flags,to,tolen) int s; /* Socket index */ struct mbuf *bp; /* Buffer to send */ int flags; /* not currently used */ char *to; /* Destination, only for datagrams */ int tolen; /* Length of destination */ { register struct usock *up; union cb cb; union sp local,remote; struct socket lsock,fsock; int cnt; if((up = itop(s)) == NULLUSOCK){ free_p(bp); errno = EBADF; return -1; } #ifndef notdef if(up->obuf != NULLBUF){ /* If there's unflushed output, send it. * Note the importance of clearing up->obuf * before the recursive call! */ struct mbuf *bp1; bp1 = up->obuf; up->obuf = NULLBUF; send_mbuf(s,bp1,flags,to,tolen); } #endif if(up->name == NULLCHAR){ /* Automatic local name assignment for datagram sockets */ switch(up->type){ case TYPE_LOCAL_STREAM: case TYPE_LOCAL_DGRAM: break; /* no op */ case TYPE_UDP: case TYPE_RAW: autobind(s,AF_INET); break; #ifdef AX25 case TYPE_AX25UI: autobind(s,AF_AX25); break; #endif #ifdef NETROM case TYPE_NETROML3: case TYPE_NETROML4: autobind(s,AF_NETROM); break; #endif default: free_p(bp); errno = ENOTCONN; return -1; } } cnt = len_p(bp); switch(up->type){ case TYPE_LOCAL_DGRAM: if(up->cb.local->peer == NULLUSOCK){ free_p(bp); errno = ENOTCONN; return -1; } enqueue(&up->cb.local->peer->cb.local->q,bp); psignal(up->cb.local->peer,0); /* If high water mark has been reached, block */ while(up->cb.local->peer != NULLUSOCK && len_q(up->cb.local->peer->cb.local->q) >= up->cb.local->peer->cb.local->hiwat){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up->cb.local->peer)) != 0){ return -1; } } if(up->cb.local->peer == NULLUSOCK){ errno = ENOTCONN; return -1; } break; case TYPE_LOCAL_STREAM: if(up->cb.local->peer == NULLUSOCK){ free_p(bp); errno = ENOTCONN; return -1; } append(&up->cb.local->peer->cb.local->q,bp); psignal(up->cb.local->peer,0); /* If high water mark has been reached, block */ while(up->cb.local->peer != NULLUSOCK && len_p(up->cb.local->peer->cb.local->q) >= up->cb.local->peer->cb.local->hiwat){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up->cb.local->peer)) != 0){ return -1; } } if(up->cb.local->peer == NULLUSOCK){ errno = ENOTCONN; return -1; } break; case TYPE_TCP: if((cb.tcb = up->cb.tcb) == NULLTCB){ free_p(bp); errno = ENOTCONN; return -1; } cnt = send_tcp(cb.tcb,bp); while((cb.tcb = up->cb.tcb) != NULLTCB && cb.tcb->sndcnt > cb.tcb->window){ /* Send queue is full */ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.tcb == NULLTCB){ errno = ENOTCONN; return -1; } break; case TYPE_UDP: local.in = (struct sockaddr_in *)up->name; lsock.address = local.in->sin_addr.s_addr; lsock.port = local.in->sin_port; if(to != NULLCHAR) remote.in = (struct sockaddr_in *)to; else if(up->peername != NULLCHAR) remote.in = (struct sockaddr_in *)up->peername; else { errno = ENOTCONN; return -1; } fsock.address = remote.in->sin_addr.s_addr; fsock.port = remote.in->sin_port; send_udp(&lsock,&fsock,0,0,bp,0,0,0); break; case TYPE_RAW: local.in = (struct sockaddr_in *)up->name; if(to != NULLCHAR) remote.in = (struct sockaddr_in *)to; else if(up->peername != NULLCHAR) remote.in = (struct sockaddr_in *)up->peername; else { errno = ENOTCONN; return -1; } ip_send(local.in->sin_addr.s_addr,remote.in->sin_addr.s_addr, (char)up->cb.rip->protocol,0,0,bp,0,0,0); break; #ifdef AX25 case TYPE_AX25I: if((cb.ax25 = up->cb.ax25) == NULLAX25){ free_p(bp); errno = ENOTCONN; return -1; } send_ax25(cb.ax25,bp,PID_NO_L3); while((cb.ax25 = up->cb.ax25) != NULLAX25 && len_q(cb.ax25->txq) * cb.ax25->paclen > cb.ax25->window){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ return -1; } } if(cb.ax25 == NULLAX25){ errno = EBADF; return -1; } break; case TYPE_AX25UI: local.ax = (struct sockaddr_ax *)up->name; if(to != NULLCHAR) remote.ax = (struct sockaddr_ax *)to; else if(up->peername != NULLCHAR) remote.ax = (struct sockaddr_ax *)up->peername; else { errno = ENOTCONN; return -1; } ax_output(if_lookup(remote.ax->iface), remote.ax->ax25_addr, local.ax->ax25_addr,PID_NO_L3,bp); break; #endif #ifdef NETROM case TYPE_NETROML3: if(len_p(bp) > NR4MAXINFO) { free_p(bp); errno = EMSGSIZE; return -1; } local.nr = (struct sockaddr_nr *)up->name; if(to != NULLCHAR) remote.nr = (struct sockaddr_nr *)to; else if(up->peername != NULLCHAR) remote.nr = (struct sockaddr_nr *)up->peername; else { errno = ENOTCONN; return -1; } /* The NETROM username is always ignored in outgoing traffic */ nr_sendraw(remote.nr->nr_addr.node, up->cb.rnr->protocol,up->cb.rnr->protocol,bp); break; case TYPE_NETROML4: if((cb.nr4 = up->cb.nr4) == NULLNR4CB) { free_p(bp); errno = ENOTCONN; return -1; } if(len_p(bp) > NR4MAXINFO){ /* reject big packets */ free_p(bp); errno = EMSGSIZE; return -1; } send_nr4(cb.nr4,bp); while((cb.nr4 = up->cb.nr4) != NULLNR4CB && cb.nr4->nbuffered >= cb.nr4->window){ if(up->noblock){ errno = EWOULDBLOCK; return -1; } else if((errno = pwait(up)) != 0){ errno = EINTR; return -1; } } if(cb.nr4 == NULLNR4CB){ errno = EBADF; return -1; } break; #endif } return cnt; } /* Return local name passed in an earlier bind() call */ int getsockname(s,name,namelen) int s; /* Socket index */ char *name; /* Place to stash name */ int *namelen; /* Length of same */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(name == NULLCHAR || namelen == (int *)NULL){ errno = EFAULT; return -1; } if(up->name == NULLCHAR){ /* Not bound yet */ *namelen = 0; return 0; } if(up->name != NULLCHAR){ *namelen = min(*namelen,up->namelen); memcpy(name,up->name,*namelen); } return 0; } /* Get remote name, returning result of earlier connect() call. */ int getpeername(s,peername,peernamelen) int s; /* Socket index */ char *peername; /* Place to stash name */ int *peernamelen; /* Length of same */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->peername == NULLCHAR){ errno = ENOTCONN; return -1; } if(peername == NULLCHAR || peernamelen == (int *)NULL){ errno = EFAULT; return -1; } *peernamelen = min(*peernamelen,up->peernamelen); memcpy(peername,up->peername,*peernamelen); return 0; } /* Return length of protocol queue, either send or receive. */ int socklen(s,rtx) int s; /* Socket index */ int rtx; /* 0 = receive queue, 1 = transmit queue */ { register struct usock *up; int len = -1; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->cb.p == NULLCHAR){ errno = ENOTCONN; return -1; } if(rtx < 0 || rtx > 1){ errno = EINVAL; return -1; } switch(up->type){ case TYPE_LOCAL_DGRAM: switch(rtx){ case 0: len = len_q(up->cb.local->q); break; case 1: if(up->cb.local->peer != NULLUSOCK) len = len_q(up->cb.local->peer->cb.local->q); break; } break; case TYPE_LOCAL_STREAM: switch(rtx){ case 0: len = len_p(up->cb.local->q) + len_p(up->ibuf); break; case 1: if(up->cb.local->peer != NULLUSOCK) len = len_p(up->cb.local->peer->cb.local->q) + len_p(up->obuf); break; } break; case TYPE_TCP: switch(rtx){ case 0: len = up->cb.tcb->rcvcnt + len_p(up->ibuf); break; case 1: len = up->cb.tcb->sndcnt + len_p(up->obuf); break; } break; case TYPE_UDP: switch(rtx){ case 0: len = up->cb.udp->rcvcnt; break; case 1: len = 0; break; } break; #ifdef AX25 case TYPE_AX25I: switch(rtx){ case 0: len = len_p(up->cb.ax25->rxq) + len_p(up->ibuf); break; case 1: /* Number of packets, not bytes */ len = len_q(up->cb.ax25->txq); if(up->obuf != NULLBUF) len++; } break; case TYPE_AX25UI: switch(rtx){ case 0: len = len_q(Bcq); break; case 1: len = 0; break; } break; #endif #ifdef NETROM case TYPE_NETROML3: switch(rtx){ case 0: len = len_q(up->cb.rnr->rcvq); break; case 1: len = 0; } break; case TYPE_NETROML4: switch(rtx){ case 0: len = len_p(up->cb.nr4->rxq) + len_p(up->obuf); break; case 1: /* Number of packets, not bytes */ len = len_q(up->cb.nr4->txq); if(up->obuf != NULLBUF) len++; break; } break; #endif case TYPE_RAW: switch(rtx){ case 0: len = len_q(up->cb.rip->rcvq); break; case 1: len = 0; break; } break; } return len; } /* Force retransmission. Valid only for connection-oriented sockets. */ int sockkick(s) int s; /* Socket index */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){ errno = EINVAL; return -1; } if(up->cb.p == NULLCHAR){ errno = ENOTCONN; return -1; } switch(up->type){ case TYPE_TCP: kick_tcp(up->cb.tcb); break; #ifdef AX25 case TYPE_AX25I: kick_ax25(up->cb.ax25); break; #endif #ifdef NETROM case TYPE_NETROML4: kick_nr4(up->cb.nr4); break; #endif default: /* Datagram sockets can't be kicked */ errno = EOPNOTSUPP; return -1; } return 0; } /* Change owner of socket, return previous owner */ struct proc * sockowner(s,newowner) int s; /* Socket index */ struct proc *newowner; /* Process table address of new owner */ { register struct usock *up; struct proc *pp; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return NULLPROC; } pp = up->owner; if(newowner != NULLPROC) up->owner = newowner; return pp; } /* Close down a socket three ways. Type 0 means "no more receives"; this * replaces the incoming data upcall with a routine that discards further * data. Type 1 means "no more sends", and obviously corresponds to sending * a TCP FIN. Type 2 means "no more receives or sends". This I interpret * as "abort the connection". */ int shutdown(s,how) int s; /* Socket index */ int how; /* (see above) */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(up->cb.p == NULLCHAR){ errno = ENOTCONN; return -1; } switch(up->type){ case TYPE_LOCAL_DGRAM: case TYPE_LOCAL_STREAM: if(up->cb.local->q == NULLBUF) close_s(s); else up->cb.local->flags = LOC_SHUTDOWN; break; case TYPE_TCP: 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; } break; #ifdef AX25 case TYPE_AX25UI: close_s(s); break; case TYPE_AX25I: switch(how){ case 0: case 1: /* Attempt regular disconnect */ disc_ax25(up->cb.ax25); break; case 2: /* Blow it away */ reset_ax25(up->cb.ax25); up->cb.ax25 = NULLAX25; break; } break; #endif #ifdef NETROM case TYPE_NETROML3: close_s(s); break; case TYPE_NETROML4: switch(how){ case 0: case 1: /* Attempt regular disconnect */ disc_nr4(up->cb.nr4); break; case 2: /* Blow it away */ reset_nr4(up->cb.nr4); up->cb.nr4 = NULLNR4CB; break; } break; #endif case TYPE_RAW: case TYPE_UDP: close_s(s); break; default: errno = EOPNOTSUPP; return -1; } psignal(up,0); return 0; } /* Close a socket, freeing it for reuse. Try to do a graceful close on a * TCP socket, if possible */ int close_s(s) int s; /* Socket index */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(--up->refcnt > 0) return 0; /* Others are still using it */ usflush(s); if(up->ibuf != NULLBUF){ free_p(up->ibuf); up->ibuf = NULLBUF; } switch(up->type){ case TYPE_LOCAL_STREAM: case TYPE_LOCAL_DGRAM: if(up->cb.local->peer != NULLUSOCK){ up->cb.local->peer->cb.local->peer = NULLUSOCK; psignal(up->cb.local->peer,0); } free_q(&up->cb.local->q); free(up->cb.local); break; case TYPE_TCP: 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); } break; case TYPE_UDP: if(up->cb.udp != NULLUDP){ del_udp(up->cb.udp); } break; #ifdef AX25 case TYPE_AX25I: if(up->cb.ax25 != NULLAX25){ /* Tell the TCP_CLOSED upcall there's no more socket */ up->cb.ax25->user = -1; disc_ax25(up->cb.ax25); } break; case TYPE_AX25UI: Axui_sock = -1; free_q(&Bcq); psignal(&Bcq,0); /* Unblock any reads */ break; #endif #ifdef NETROM case TYPE_NETROML3: del_rnr(up->cb.rnr); break; case TYPE_NETROML4: if(up->cb.nr4 != NULLNR4CB){ /* Tell the TCP_CLOSED upcall there's no more socket */ up->cb.nr4->user = -1; disc_nr4(up->cb.nr4); } break; #endif case TYPE_RAW: del_ip(up->cb.rip); break; default: errno = EOPNOTSUPP; return -1; } #ifdef LZW if(up->zout != NULLLZW || up->zin != NULLLZW) lzwfree(up); #endif free(up->name); free(up->peername); up->cb.p = NULLCHAR; up->name = up->peername = NULLCHAR; up->type = NOTUSED; psignal(up,0); /* Wake up anybody doing an accept() or recv() */ return 0; } /* Increment reference count for specified socket */ int usesock(s) int s; { struct usock *up; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } up->refcnt++; return 0; } /* Blow away all sockets belonging to a certain process. Used by killproc(). */ void freesock(pp) struct proc *pp; { register struct usock *up; register int i; for(i=SOCKBASE;i < Nusock+SOCKBASE;i++){ up = itop(i); if(up != NULLUSOCK && up->type != NOTUSED && up->owner == pp) shutdown(i,2); } } /* Start of internal subroutines */ /* Raw IP receive upcall routine */ static void rip_recv(rp) struct raw_ip *rp; { psignal(itop(rp->user),1); pwait(NULL); } /* UDP receive upcall routine */ static void s_urcall(iface,udp,cnt) struct iface *iface; struct udp_cb *udp; int cnt; { psignal(itop(udp->user),1); pwait(NULL); } /* 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); /* 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); } #ifdef AX25 /* AX.25 receive upcall */ void s_arcall(axp,cnt) struct ax25_cb *axp; int cnt; { int ns; struct usock *up,*nup,*oup; union sp sp; up = itop(axp->user); /* When AX.25 data arrives for the first time the AX.25 listener is notified, if active. If the AX.25 listener is a server its socket is duplicated in the same manner as in s_tscall(). */ if (Axi_sock != -1 && axp->user == -1) { oup = up = itop(Axi_sock); /* From now on, use the same upcalls as the listener */ axp->t_upcall = up->cb.ax25->t_upcall; axp->r_upcall = up->cb.ax25->r_upcall; axp->s_upcall = up->cb.ax25->s_upcall; if (up->cb.ax25->flags.clone) { /* Clone the socket */ ns = socket(AF_AX25,SOCK_STREAM,0); nup = itop(ns); ASSIGN(*nup,*up); axp->user = ns; nup->cb.ax25 = axp; /* Allocate new memory for the name areas */ nup->name = mallocw(sizeof(struct sockaddr_ax)); nup->peername = mallocw(sizeof(struct sockaddr_ax)); /* Store the new socket # in the old one */ up->rdysock = ns; up = nup; } else { axp->user = Axi_sock; del_ax25(up->cb.ax25); up->cb.ax25 = axp; /* Allocate space for the peer's name */ up->peername = mallocw(sizeof(struct sockaddr_ax)); /* Store the old socket # in the old socket */ up->rdysock = Axi_sock; } /* Load the addresses. Memory for the name has already * been allocated, either above or in the original bind. */ sp.p = up->name; sp.ax->sax_family = AF_AX25; memcpy(sp.ax->ax25_addr,axp->local,AXALEN); memcpy(sp.ax->iface,axp->iface->name,ILEN); up->namelen = sizeof(struct sockaddr_ax); sp.p = up->peername; sp.ax->sax_family = AF_AX25; memcpy(sp.ax->ax25_addr,axp->remote,AXALEN); memcpy(sp.ax->iface,axp->iface->name,ILEN); up->peernamelen = sizeof(struct sockaddr_ax); /* Wake up the guy accepting it, and let him run */ psignal(oup,1); pwait(NULL); return; } /* Wake up anyone waiting, and let them run */ psignal(up,1); pwait(NULL); } /* AX.25 transmit upcall */ void s_atcall(axp,cnt) struct ax25_cb *axp; int cnt; { /* Wake up anyone waiting, and let them run */ psignal(itop(axp->user),1); pwait(NULL); } /* AX25 state change upcall routine */ void s_ascall(axp,old,new) register struct ax25_cb *axp; int old,new; { int s; struct usock *up; s = axp->user; up = itop(s); switch(new){ case LAPB_DISCONNECTED: /* 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 block so they will notice it disappearing. */ if(up != NULLUSOCK){ up->errcodes[0] = axp->reason; up->cb.ax25 = NULLAX25; } del_ax25(axp); break; default: /* Other transitions are ignored */ break; } psignal(up,0); /* In case anybody's waiting */ } #endif #ifdef NETROM /* NET/ROM receive upcall routine */ static void s_nrcall(cb,cnt) struct nr4cb *cb; int cnt; { /* Wake up anybody waiting for data, and let them run */ psignal(itop(cb->user),1); pwait(NULL); } /* NET/ROM transmit upcall routine */ static void s_ntcall(cb,cnt) struct nr4cb *cb; int cnt; { /* Wake up anybody waiting to send data, and let them run */ psignal(itop(cb->user),1); pwait(NULL); } /* NET/ROM state change upcall routine */ static void s_nscall(cb,old,new) struct nr4cb *cb; int old,new; { int s,ns; struct usock *up,*nup,*oup; union sp sp; s = cb->user; oup = up = itop(s); if(new == NR4STDISC && up != NULLUSOCK){ /* 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 cb so they will notice it disappearing. */ up->cb.nr4 = NULLNR4CB; up->errcodes[0] = cb->dreason; } if(new == NR4STCON && old == NR4STDISC){ /* Handle an incoming connection. If this is a server cb, * then we're being handed a "clone" cb 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(cb->clone){ /* Clone the socket */ ns = socket(AF_NETROM,SOCK_SEQPACKET,0); nup = itop(ns); ASSIGN(*nup,*up); cb->user = ns; nup->cb.nr4 = cb; cb->clone = 0; /* to avoid getting here again */ /* Allocate new memory for the name areas */ nup->name = mallocw(sizeof(struct sockaddr_nr)); nup->peername = mallocw(sizeof(struct sockaddr_nr)); /* 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(sizeof(struct sockaddr_nr)); /* 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.nr->nr_family = AF_NETROM; ASSIGN(sp.nr->nr_addr,up->cb.nr4->local); up->namelen = sizeof(struct sockaddr_nr); sp.p = up->peername; sp.nr->nr_family = AF_NETROM; ASSIGN(sp.nr->nr_addr,up->cb.nr4->remote); up->peernamelen = sizeof(struct sockaddr_nr); /* Wake up the guy accepting it, and let him run */ psignal(oup,1); pwait(NULL); } /* Ignore all other state transitions */ psignal(up,0); /* In case anybody's waiting */ } #endif /* Verify address family and length according to the socket type */ static int checkaddr(type,name,namelen) int type; char *name; int namelen; { union sp sp; sp.p = name; /* Verify length and address family according to protocol */ switch(type){ case TYPE_TCP: case TYPE_UDP: if(sp.in->sin_family != AF_INET || namelen != sizeof(struct sockaddr_in)) return -1; break; #ifdef AX25 case TYPE_AX25I: case TYPE_AX25UI: if(sp.ax->sax_family != AF_AX25 || namelen != sizeof(struct sockaddr_ax)) return -1; break; #endif #ifdef NETROM case TYPE_NETROML3: case TYPE_NETROML4: if(sp.nr->nr_family != AF_NETROM || namelen != sizeof(struct sockaddr_nr)) return -1; break; #endif } return 0; } /* Issue an automatic bind of a local address */ static void autobind(s,af) int s,af; { char buf[MAXSOCKSIZE]; union sp sp; sp.p = buf; switch(af){ case AF_INET: sp.in->sin_family = AF_INET; sp.in->sin_addr.s_addr = INADDR_ANY; sp.in->sin_port = Lport++; bind(s,sp.p,sizeof(struct sockaddr_in)); break; #ifdef AX25 case AF_AX25: sp.ax->sax_family = AF_AX25; memset(sp.ax->ax25_addr,'\0',AXALEN); memset(sp.ax->iface,'\0',ILEN); bind(s,sp.p,sizeof(struct sockaddr_ax)); break; #endif #ifdef NETROM case AF_NETROM: sp.nr->nr_family = AF_NETROM; memcpy(sp.nr->nr_addr.user,Mycall,AXALEN); memcpy(sp.nr->nr_addr.node,Mycall,AXALEN); bind(s,sp.p,sizeof(struct sockaddr_nr)); break; #endif } } /* Return a pair of mutually connected sockets in sv[0] and sv[1] */ int socketpair(af,type,protocol,sv) int af; int type; int protocol; int sv[]; { struct usock *up0, *up1; if(sv == NULLINT){ errno = EFAULT; return -1; } if(af != AF_LOCAL){ errno = EAFNOSUPPORT; return -1; } if(type != SOCK_STREAM && type != SOCK_DGRAM){ errno = ESOCKTNOSUPPORT; return -1; } if((sv[0] = socket(af,type,protocol)) == -1) return -1; if((sv[1] = socket(af,type,protocol)) == -1){ close_s(sv[0]); return -1; } up0 = itop(sv[0]); up1 = itop(sv[1]); up0->cb.local->peer = up1; up1->cb.local->peer = up0; return sv[1]; }