/***************************************************************************** * * Copyright 1989, Xylogics, Inc. ALL RIGHTS RESERVED. * * ALL RIGHTS RESERVED. Licensed Material - Property of Xylogics, Inc. * This software is made available solely pursuant to the terms of a * software license agreement which governs its use. * Unauthorized duplication, distribution or sale are strictly prohibited. * * Module Function: * * User-level interface to Xenix UDP SL/IP * Simulates much of the BSD4.2 socket system call set * * Original Author: Paul Mattes Created on: 01/04/88 * * Revision Control Information: * * $Header: /annex/common/src/./libannex/RCS/socket_layer.c,v 1.8 1994/08/04 10:58:59 sasson Exp $ * * This file created by RCS from: * $Source: /annex/common/src/./libannex/RCS/socket_layer.c,v $ * * Revision History: * * $Log: socket_layer.c,v $ * Revision 1.8 1994/08/04 10:58:59 sasson * SPR 3211: "debug" variable defined in multiple places. * * Revision 1.7 1989/04/11 01:08:13 loverso * removed bzero - now in bcopy.c * * Revision 1.6 89/04/05 12:40:22 loverso * Changed copyright notice * * Revision 1.5 88/10/25 15:54:35 mattes * I'm too embarassed to say. * * Revision 1.4 88/08/31 12:21:44 parker * changed _exit to x_exit because XENIX already has a _exit(). * * Revision 1.3 88/05/31 17:12:38 parker * fixes to get XENIX/SLIP to build again * * Revision 1.2 88/05/24 18:36:01 parker * Changes for new install-annex script * * Revision 1.1 88/04/15 11:59:07 mattes * Initial revision * * * This file is currently under revision by: * * $Locker: $ * *****************************************************************************/ #define RCSDATE $Date: 1994/08/04 10:58:59 $ #define RCSREV $Revision: 1.8 $ #define RCSID "$Header: /annex/common/src/./libannex/RCS/socket_layer.c,v 1.8 1994/08/04 10:58:59 sasson Exp $" #ifndef lint static char rcsid[] = RCSID; #endif /***************************************************************************** * * * Include files * * * *****************************************************************************/ #include #include #include #include #include #include #include #include "../inc/config.h" #include "../inc/slip/slip_user.h" /***************************************************************************** * * * Local defines and macros * * * *****************************************************************************/ #define N_SOCKS 10 /* How many simultaneous sockets */ #define SOCKET_BASE 200 /* Lowest symbolic socket number */ #define issocket(s) ((s) >= SOCKET_BASE && (s) < SOCKET_BASE + N_SOCKS) #define MIN(a, b) ((a) > (b) ? (b) : (a)) #define TRUE 1 #define FALSE 0 /***************************************************************************** * * * Structure and union definitions * * * *****************************************************************************/ static struct sockinf { int si_number; int si_flags; #define SI_OPEN 0x0001 #define SI_CONNECTED 0x0002 #define SI_TICKING 0x0004 int si_lport; struct sockaddr si_peer; int si_pid; struct sockiobuf iobuf[2]; } _socket_desc[N_SOCKS]; #define INBUF 0 #define OUTBUF 1 #define ibuf iobuf[INBUF] #define ibase iobuf[INBUF].sb_base #define icurr iobuf[INBUF].sb_curr #define ilen iobuf[INBUF].sb_len #define obuf iobuf[OUTBUF] #define obase iobuf[OUTBUF].sb_base #define ocurr iobuf[OUTBUF].sb_curr #define olen iobuf[OUTBUF].sb_len /***************************************************************************** * * * External data * * * *****************************************************************************/ extern int errno; extern char *malloc(); extern u_short _pick_a_random_port(); extern int debug; extern int getpid(); /***************************************************************************** * * * Global data * * * *****************************************************************************/ int _network_mtu = 0; int _io_buffersize = 0; /***************************************************************************** * * * Static data * * * *****************************************************************************/ static int sockets_in_use; /***************************************************************************** * * * Forward definitions * * * *****************************************************************************/ static struct sockinf *sockinf_bynumber(); static struct sockinf *sockinf_byport(); static int trivial_alarm_handler(int); struct protoent *GETPROTOBYNAME(name) char *name; { static struct protoent p; if(!strcmp(name, "udp")) return((struct protoent *)0); p.p_name = "udp"; p.p_aliases = (char **)0; p.p_proto = IPPROTO_UDP; return(&p); } int _socket(family, type, protocol) int family; /* AF_INET only */ int type; /* SOCK_DGRAM only */ int protocol; /* IPPROTO_UDP only */ { struct sockinf *s; int error; int i; /* Check arguments */ if(family != AF_INET) { errno = EAFNOSUPPORT; goto socket_abort; } if(type != SOCK_DGRAM) { errno = ESOCKTNOSUPPORT; goto socket_abort; } if(!_network_mtu) { if(error = _net_init(&_network_mtu, &_io_buffersize)) { errno = error; goto socket_abort; } } /* Allocate and initialize the state structure */ for(i = 0; i < N_SOCKS; ++i) if(!(_socket_desc[i].si_flags & SI_OPEN)) break; if(i >= N_SOCKS) { errno = ENOMEM; goto socket_abort; } s = &_socket_desc[i]; s->ibase = malloc(_io_buffersize); if(!s->ibase) { errno = ENOMEM; goto socket_abort; } s->obase = malloc(_io_buffersize); if(!s->obase) { free(s->ibase); errno = ENOMEM; goto socket_abort; } s->icurr = s->ibase; s->ocurr = s->obase; s->ilen = s->olen = 0; s->si_number = SOCKET_BASE + i; s->si_flags = SI_OPEN; s->si_lport = 0; s->si_pid = getpid(); ++sockets_in_use; return(s->si_number); socket_abort: return(-1); } /* Associate a local address with a socket */ int _bind(s, name, namelen) int s; /* returned from socket() */ struct sockaddr *name; /* limited to Inet type */ int namelen; { struct sockinf *t, *u; int port; int error; if(!(t = sockinf_bynumber(s, FALSE))) { errno = EBADF; return(-1); } if(t->si_lport || (namelen != sizeof(*name))) { errno = EINVAL; return(-1); } port = ((struct sockaddr_in *)name)->sin_port; if(!port) port = _pick_a_random_port(); else { if(sockinf_byport(port, TRUE)) { errno = EADDRINUSE; return(-1); } if(error = _try_to_reserve(port)) { errno = error; return(-1); } } t->si_lport = port; return(0); } /* Associate a remote address with a socket */ int _connect(s, name, namelen) int s; /* returned from socket() */ struct sockaddr *name; /* limited to Inet type */ int namelen; { struct sockinf *t, *u; int port; int error; if(!(t = sockinf_bynumber(s, FALSE))) { errno = EBADF; return(-1); } if(namelen != sizeof(*name)) { errno = EINVAL; return(-1); } bcopy((caddr_t)name, (caddr_t)&t->si_peer, sizeof(struct sockaddr)); t->si_flags |= SI_CONNECTED; return(0); } /* receive from anyone; return peer's name */ int _recvfrom(s, buf, len, flags, from, fromlen) int s; /* from socket() */ char *buf; /* buffer */ int flags; /* must be zero! */ struct sockaddr *from; /* whom from it come */ int *fromlen; /* value-result */ { struct sockinf *t; struct sockaddr dummy; char *rsp; int error, cc; /* Does the socket exist? */ if(!(t = sockinf_bynumber(s, FALSE))) { errno = EBADF; goto recvfrom_abort; } /* Do they want to know where it will come from? */ if(!from) { /* If not connected, must supply 'from' */ if(!(t->si_flags & SI_CONNECTED)) { from = &dummy; bzero((caddr_t)from, sizeof(*from)); } /* If connected, require it be from peer (does this mean anything?) */ else { bcopy((caddr_t)&t->si_peer, (caddr_t)&dummy, sizeof(dummy)); from = &dummy; } } else { if(*fromlen != sizeof(*from)) { errno = EINVAL; goto recvfrom_abort; } bzero((caddr_t)from, *fromlen); } /* How can you receive on an unbound socket? */ if(!t->si_lport) { if(debug) printf("recvfrom: unbound socket\n"); errno = ENOTCONN; goto recvfrom_abort; } error = _udp_input(&t->ibuf, &cc, &rsp, t->si_lport, (struct sockaddr_in *)from); if(error) { errno = error; goto recvfrom_abort; } bcopy(rsp, buf, MIN(cc, len)); /* Truncate, if need be */ trivial_alarm_handler(0); return(cc); recvfrom_abort: trivial_alarm_handler(0); return(-1); } /* receive from your "peer" */ int _recv(s, buf, len, flags) int s; /* from socket() */ char *buf; /* buffer */ int flags; /* must be zero! */ { return(recvfrom(s, buf, len, flags, (struct sockaddr *)0, 0)); } /* send to a particular destination */ int _sendto(s, msg, len, flags, to, tolen) char *msg; /* data outgoing */ int len; /* how big she is */ int flags; /* must be zero! */ struct sockaddr *to; /* where it is going */ int tolen; /* how big where is */ { struct sockinf *t; char *bgb, *bgb2; int error; if(debug) fprintf(stderr, "SL/IP SENDTO %d bytes\n", len); if(!(t = sockinf_bynumber(s, FALSE))) { errno = EBADF; return(-1); } if(!to) { if(!(t->si_flags & SI_CONNECTED)) { errno = ENOTCONN; return(-1); } to = &t->si_peer; tolen = sizeof(t->si_peer); } else if(tolen != sizeof(*to)) { errno = EINVAL; return(-1); } if(!((struct sockaddr_in *)to)->sin_port) { errno = EDESTADDRREQ; return(-1); } if(len >= _network_mtu) { if(debug) fprintf(stderr, "sendto: msg too long: %d, mtu is %d\n", len, _network_mtu); errno = EMSGSIZE; return(-1); } if(!(t->si_lport)) t->si_lport = _pick_a_random_port(); t->ocurr = t->obase + (_io_buffersize - len); bcopy(msg, t->ocurr, len); t->olen = len; error = _udp_output(&t->obuf, t->si_lport, (struct sockaddr_in *)to); if(error) { errno = error; return(-1); } return(0); } /* Send to your "peer" */ int _send(s, msg, len, flags) char *msg; /* data outgoing */ int len; /* how big she is */ int flags; /* must be zero! */ { struct sockinf *t; int error; if(!(t = sockinf_bynumber(s, FALSE))) { errno = EBADF; return(-1); } if(!(t->si_flags & SI_CONNECTED)) { errno = EDESTADDRREQ; return(-1); } return(sendto(s, msg, len, flags, &t->si_peer, sizeof(t->si_peer))); } int _setsockopt(s, level, optname, optval, optlen) int s, level, optname; char *optval; int optlen; { return(0); } static int so_close(s) /* NONSTANDARD CALL */ int s; /* returned from socket() */ { struct sockinf *t; if(!(t = sockinf_bynumber(s, TRUE))) { errno = EBADF; return(-1); } _close_port(t->si_lport, getpid() == t->si_pid); t->si_flags = 0; free(t->ibase); t->ibase = t->icurr = (char *)0; free(t->obase); t->obase = t->ocurr = (char *)0; t->ilen = t->olen = 0; if(!--sockets_in_use) { _network_mtu = 0; } return(0); } static struct sockinf *sockinf_bynumber(n, any_pid) int n; int any_pid; { struct sockinf *t; if(!issocket(n)) return((struct sockinf *)0); t = &_socket_desc[n - SOCKET_BASE]; if(!(t->si_flags & SI_OPEN)) return((struct sockinf *)0); if(!any_pid && (t->si_pid != getpid())) return((struct sockinf *)0); return(t); } static struct sockinf *sockinf_byport(n, any_pid) int n; int any_pid; { int i; struct sockinf *t; for(i = 0; i < N_SOCKS; ++i) { t = &_socket_desc[i]; if((t->si_flags & SI_OPEN) && (t->si_lport == n) && (any_pid || (t->si_pid == getpid()))) return(&_socket_desc[i]); } return((struct sockinf *)0); } /* * Standard Unix system and library calls we want to be in the way of */ #undef perror _perror(s) char *s; { static char *slip_ermsg[] = { /* EDESTADDRREQ 120 */ "Destination address required", /* EMSGSIZE 121 */ "Message too long", /* EPROTOTYPE 122 */ "Protocol wrong type for socket", /* ENOPROTOOPT 123 */ "Protocol not available", /* EPROTONOSUPPORT 124 */ "Protocol not supported", /* EOPNOTSUPP 125 */ "Operation not supported on socket", /* EPFNOSUPPORT 126 */ "Protocol family not supported", /* EAFNOSUPPORT 127 */ "Address family not supported by protocol family", /* EADDRINUSE 128 */ "Address already in use", /* EADDRNOTAVAIL 129 */ "Can't assign requested address", /* ECONNABORTED 130 */ "Software caused connection abort", /* ECONNRESET 131 */ "Connection reset by peer", /* ENOBUFS 132 */ "No buffer space available", /* EISCONN 133 */ "Socket is already connected", /* ENOTCONN 134 */ "Socket is not connected", /* ETIMEDOUT 135 */ "Connection timed out", /* ECONNREFUSED 136 */ "Connection refused", "Unknown error 137", "Unknown error 138", "Unknown error 139", "Unknown error 140", "Unknown error 141", "Unknown error 142", "Unknown error 143", /* ESOCKTNOSUPPORT 144 */ "Socket type not supported" }; if(errno < MIN_SLIP_ERR || errno > MAX_SLIP_ERR) { perror(s); return; } fprintf(stderr, "%s: %s\n", s, slip_ermsg[errno - MIN_SLIP_ERR]); } #undef read int _read(d, buf, nbytes) int d; char *buf; int nbytes; { if(!issocket(d)) return(read(d, buf, nbytes)); return(recv(d, buf, nbytes, 0)); } #undef write int _write(d, buf, nbytes) int d; char *buf; int nbytes; { if(!issocket(d)) return(write(d, buf, nbytes)); return(send(d, buf, nbytes, 0)); } #undef close int _close(d) { if(!issocket(d)) return(close(d)); return(so_close(d)); } #undef exit int x_exit(status) int status; { int i; for(i = 0; i < N_SOCKS; ++i) if(_socket_desc[i].si_flags & SI_OPEN) (void)so_close(i + SOCKET_BASE); exit(status); } /* Called after fork(), from child process */ so_closeall() { int s; for(s = 3; s < 10; ++s) if(!_private_fileno(s)) (void)close(s); for(s = 0; s < N_SOCKS; ++s) if(_socket_desc[s].si_flags & SI_OPEN) (void)so_close(s + SOCKET_BASE); } /* Substitute for "select" call: schedule an alarm to rip us out of a single socket read */ static int (*old_handler)() = SIG_DFL; static int old_alarmtime = 0; static int trivial_alarm_handler(s) int s; { int i; signal(SIGALRM, old_handler); old_handler = SIG_DFL; if(!s || old_alarmtime) { alarm(old_alarmtime); } old_alarmtime = 0; for(i = 0; i < N_SOCKS; ++i) _socket_desc[i].si_flags &= ~SI_TICKING; } int so_schedule_timeout(s, timeout) int s; int timeout; /* in seconds */ { struct sockinf *t; if(!(t = sockinf_bynumber(s))) { errno = EBADF; return(-1); } t->si_flags |= SI_TICKING; old_handler = signal(SIGALRM, trivial_alarm_handler); old_alarmtime = alarm(timeout * 5); return(0); }