/* Emulation of 4.2 UNIX socket interface routines includes drivers for Wollongong, CMU-TEK, UCX tcp/ip interface and also emulates the SUN version of X.25 sockets. The TWG will also work for MultiNet. */ /* This is from unixlib, by P.Kay@massey.ac.nz; wonderful implementation. You can get the real thing on 130.123.1.4 as unixlib_tar.z. */ #include #include #include #include #include #include #include #include #include #include "[.vms]network.h" #define QIO_FAILED (st != SS$_NORMAL || p[s].iosb[0] != SS$_NORMAL) #define QIO_ST_FAILED (st != SS$_NORMAL) /* Socket routine. */ int VMSsocket (domain, type, protocol) int domain, type, protocol; { struct descriptor inetdesc, x25desc, mbxdesc; int i, st, s, p_initialise (); long ucx_sock_def; char *getenv (); if (!tcp_make) set_tcp_make (); if (p_initialised == 0) { for (i = 0; i < 32; i++) p_initialise (i); p_initialised = 1; } /* First of all, get a file descriptor and file ptr we can associate with the socket, allocate a buffer, and remember the socket details. */ s = dup (0); if (s > 31) { errno = EMFILE; close (s); return -1; } p[s].fptr = fdopen (s, "r"); p[s].fd_buff = (unsigned char *) malloc (BUF_SIZE); p[s].domain = domain; p[s].type = type; p[s].protocol = protocol; /* Handle the case of INET and X.25 separately. */ if (domain == AF_INET) { if (tcp_make == NONE) { printf ("Trying to obtain a TCP socket when we don't have TCP!\n"); exit (1); } if (tcp_make == CMU) { /* For CMU we need only assign a channel. */ inetdesc.size = 3; inetdesc.ptr = "IP:"; if (sys$assign (&inetdesc, &p[s].channel, 0, 0) != SS$_NORMAL) return -1; } else if (tcp_make == UCX) { /* For UCX assign channel and associate a socket with it. */ inetdesc.size = 3; inetdesc.ptr = "BG:"; if (sys$assign (&inetdesc, &p[s].channel, 0, 0) != SS$_NORMAL) return -1; ucx_sock_def = (domain << 24) + (type << 16) + protocol; st = sys$qiow (0, p[s].channel, IO$_SETMODE, p[s].iosb, 0, 0, &ucx_sock_def, 0, 0, 0, 0, 0); if (QIO_FAILED) return -1; } else { /* For TWG we assign the channel and associate a socket with it. */ inetdesc.size = 7; inetdesc.ptr = "_INET0:"; if (sys$assign (&inetdesc, &p[s].channel, 0, 0) != SS$_NORMAL) return -1; st = sys$qiow (0, p[s].channel, IO$_SOCKET, p[s].iosb, 0, 0, domain, type, 0, 0, 0, 0); if (QIO_FAILED) return -1; } } else /* We don't handle any other domains yet. */ return -1; /* For each case if we are successful we return the descriptor. */ return s; } /* Bind routine. */ VMSbind (s, name, namelen) int s; union socket_addr *name; int namelen; { char infobuff[1024], lhost[32]; int st; if (!tcp_make) set_tcp_make (); if (p[s].domain == AF_INET) { /* One main problem with bind is that if we're given a port number of 0, then we're expected to return a unique port number. Since we don't KNOW, we return 1050+s and look to Lady Luck. */ if (tcp_make == CMU) { if (name->in.sin_port == 0 && p[s].type != SOCK_DGRAM) name->in.sin_port = 1050 + s; p[s].namelen = namelen; bcopy (name, &(p[s].name), namelen); if (p[s].type == SOCK_DGRAM) { /* Another problem is that CMU still needs an OPEN request even if it's a datagram socket. */ st = sys$qiow (0, p[s].channel, TCP$OPEN, p[s].iosb, 0, 0, 0, 0, ntohs (p[s].name.in.sin_port), 0, 1, 0); if (QIO_ST_FAILED) return -1; p[s].cmu_open = 1; sys$qiow (0, p[s].channel, TCP$INFO, p[s].iosb, 0, 0, &infobuff, 1024, 0, 0, 0, 0); bcopy (infobuff + 264, &(p[s].name.in.sin_port), 2); p[s].name.in.sin_port = htons (p[s].name.in.sin_port); /* So get it another way. */ bcopy (infobuff + 136, lhost, infobuff[1]); lhost[infobuff[1]] = '\0'; sys$qiow (0, p[s].channel, GTHST, p[s].iosb, 0, 0, &infobuff, 1024, 1, lhost, 0, 0); bcopy (infobuff + 4, &(p[s].name.in.sin_addr), 4); /* Be prepared to receive a message. */ hang_a_read (s); } } else if (tcp_make == UCX) { /* UCX will select a prot for you. If the port's number is 0, translate "name" into an item_2 list. */ struct itemlist lhost; lhost.length = namelen; lhost.code = 0; lhost.dataptr = (char *) name; st = sys$qiow (0, p[s].channel, IO$_SETMODE, p[s].iosb, 0, 0, 0, 0, &lhost, 0, 0, 0); if (QIO_FAILED) return -1; if (p[s].type == SOCK_DGRAM) hang_a_read (s); } else { /* WG is more straightforward */ st = sys$qiow (0, p[s].channel, IO$_BIND, p[s].iosb, 0, 0, name, namelen, 0, 0, 0, 0); if (QIO_FAILED) return -1; /* If it's a datagram, get ready for the message. */ if (p[s].type == SOCK_DGRAM) hang_a_read (s); } } else /* We don't handle any other domain yet. */ return -1; return 0; } /* Connect routine. */ VMSconnect (s, name, namelen) int s; union socket_addr *name; int namelen; { int pr, fl, st; char *inet_ntoa (); static struct { int len; char name[128]; } gethostbuf; extern int connect_ast (); if (!tcp_make) set_tcp_make (); /* For datagrams we need to remember who the name was so we can send all messages to that address without having to specify it all the time. */ if (p[s].connected) { if (p[s].connected == 1) errno = EISCONN; else { errno = ECONNREFUSED; p[s].connected = 0; } return -1; } if (p[s].connect_pending) { errno = EALREADY; return -1; } p[s].passive = 0; p[s].tolen = namelen; bcopy (name, &(p[s].to), namelen); if (p[s].domain == AF_INET) { if (tcp_make == CMU) { /* Get the info about the remote host and open up a connection. */ st = sys$qiow (0, p[s].channel, GTHST, p[s].iosb, 0, 0, &gethostbuf, 132, 2, name->in.sin_addr.s_addr, 0, 0); if (QIO_FAILED) { strcpy (gethostbuf.name, inet_ntoa (name->in.sin_addr.s_addr)); gethostbuf.len = strlen (gethostbuf.name); } gethostbuf.name[gethostbuf.len] = 0; /* TCP */ pr = 0; /* Active */ fl = 1; /* Nothing else for datagrams. */ if (p[s].type == SOCK_DGRAM) return (0); st = sys$qio (s, p[s].channel, TCP$OPEN, p[s].iosb, connect_ast, &p[s], &(gethostbuf.name), ntohs (name->in.sin_port), ntohs (p[s].name.in.sin_port), fl, pr, 0); if (QIO_ST_FAILED) return -1; } else if (tcp_make == UCX) { /* Both UDP and TCP can use a connect - IO$_ACCESS */ p[s].rhost.length = namelen; p[s].rhost.code = 0; p[s].rhost.dataptr = (char *) name; st = sys$qio (s, p[s].channel, IO$_ACCESS, p[s].iosb, connect_ast, &p[s], 0, 0, &p[s].rhost, 0, 0, 0); if (QIO_ST_FAILED) return -1; } else { /* TWG */ if (p[s].type == SOCK_DGRAM) return (0); st = sys$qio (s, p[s].channel, IO$_CONNECT, p[s].iosb, connect_ast, &p[s], name, namelen, 0, 0, 0, 0); if (QIO_ST_FAILED) return -1; } } else /* We don't handle any other domain yet. */ return -1; if (p[s].non_blocking) { if (p[s].connected) { if (p[s].connected == 1) return 0; else { p[s].connected = 0; errno = ECONNREFUSED; return -1; } } else { p[s].connect_pending = 1; errno = EINPROGRESS; return -1; } } else { /* wait for the connection to occur */ if (p[s].connected) { if (p[s].connected == 1) return 0; else { p[s].connected = 0; errno = ECONNREFUSED; return -1; } } /* Timed out? */ if (wait_efn (s) == -1) return -1; if (p[s].connected != SS$_NORMAL) { errno = ECONNREFUSED; return -1; } return 0; } } /* Listen routine. */ VMSlisten (s, backlog) int s; int backlog; { int st; if (!tcp_make) set_tcp_make (); p[s].passive = 1; p[s].backlog = backlog; if (p[s].domain == AF_INET) { if (tcp_make == CMU) { /* For the CMU sockets we can't do the open call in listen; we have to do it in hang_an_accept, because when we close off the connection we have to be ready to accept another one. accept() also calls hang_an_accept on the old descriptor. */ /* Nothing */ } else if (tcp_make == UCX) { /* Doc Verbage sez backlog is descriptor of byte. Doc examples and common sense say backlog is value. Value doesn't work, so let's try descriptor of byte after all. */ struct descriptor bl; unsigned char ucx_backlog; ucx_backlog = (unsigned char) backlog; bl.size = sizeof (ucx_backlog); bl.ptr = (char *) &ucx_backlog; st = sys$qiow (0, p[s].channel, IO$_SETMODE, p[s].iosb, 0, 0, 0, 0, 0, &bl, 0, 0); if (QIO_FAILED) return -1; } else { /* TWG */ st = sys$qiow (0, p[s].channel, IO$_LISTEN, p[s].iosb, 0, 0, backlog, 0, 0, 0, 0, 0); if (QIO_FAILED) return -1; } } else /* We don't handle any other domain yet. */ return -1; p[s].status = LISTENING; hang_an_accept (s); return 0; } /* Accept routine. */ int VMSaccept (s, addr, addrlen) int s; union socket_addr *addr; int *addrlen; { int news, st; struct descriptor inetdesc; if (!tcp_make) set_tcp_make (); if (p[s].non_blocking && !p[s].accept_pending) { errno = EWOULDBLOCK; return -1; } /* hang_an_accept set up an incoming connection request so we have first to hang around until one appears or we time out. */ if (p[s].domain == AF_INET) { if (tcp_make == CMU) { char infobuff[1024]; /* Timed out? */ if (wait_efn (s) == -1) return -1; /* Ok, get a new descriptor ... */ news = dup (0); if (news > 31) { errno = EMFILE; close (news); return -1; } /* ... and copy all of our data across. */ bcopy (&p[s], &p[news], sizeof (p[0])); /* But not this field, of course! */ p[news].s = news; sys$qiow (0, p[news].channel, TCP$INFO, p[news].iosb, 0, 0, &infobuff, 1024, 0, 0, 0, 0); /* Copy across the connection info if necessary. */ if (addr != 0) { *addrlen = sizeof (struct sockaddr_in); bcopy (infobuff + 132, &(addr->in.sin_port), 2); addr->in.sin_port = htons (addr->in.sin_port); addr->in.sin_family = AF_INET; bcopy (infobuff + 272, &(addr->in.sin_addr), 4); p[news].fromlen = *addrlen; bcopy (addr, &(p[news].from), *addrlen); } p[news].status = PASSIVE_CONNECTION; /* Get a new file ptr for the socket. */ p[news].fptr = fdopen (news, "r"); /* Reset this field. */ p[news].accept_pending = 0; /* Allocate a buffer. */ p[news].fd_buff = (unsigned char *) malloc (BUF_SIZE); p[news].fd_leftover = 0; /* Be prepared to get msgs. */ hang_a_read (news); /* Now fix up our previous socket so it's again listening for connections. */ inetdesc.size = 3; inetdesc.ptr = "IP:"; if (sys$assign (&inetdesc, &p[s].channel, 0, 0) != SS$_NORMAL) return -1; p[s].status = LISTENING; hang_an_accept (s); /* Return the new socket descriptor. */ return news; } else if (tcp_make == UCX) { /* UCX does the actual accept from hang_an_accept. The accept info is put into the data structure for the "listening" socket. These just need to be copied into a newly allocated socket for the connect and the listening socket re-started. */ /* Wait for event flag from accept being received inside of hang_an_accept(). */ if (wait_efn (s) == -1) /* Timed out. */ return -1; /* Ok, get a new descriptor ... */ news = dup (0); if (news > 31) { errno = EMFILE; close (news); return -1; } /* ... and copy all of our data across. */ bcopy (&p[s], &p[news], sizeof (p[0])); p[news].s = news; /* but not this field */ p[news].channel = p[s].ucx_accept_chan; /* Initialize the remote host address item_list_3 struct. */ p[news].rhost.length = sizeof (struct sockaddr_in); p[news].rhost.code = 0; p[news].rhost.dataptr = (char *) &p[news].from; p[news].rhost.retlenptr = &p[news].fromdummy; if (addr != 0) { /* Return the caller's info, if requested. */ *addrlen = p[news].fromdummy; bcopy (&p[news].from, addr, p[news].fromdummy); } /* Finish fleshing out the new structure. */ p[news].status = PASSIVE_CONNECTION; /* Get a new file pointer for the socket. */ p[news].fptr = fdopen (news, "r"); /* Reset this field. */ p[news].accept_pending = 0; /* Allocate a buffer. */ p[news].fd_buff = (unsigned char *) malloc (BUF_SIZE); p[news].fd_leftover = 0; /* Get it started reading. */ hang_a_read (news); p[s].status = LISTENING; hang_an_accept (s); return news; } else { /* TWG */ struct descriptor inetdesc; int size; /* Time out? */ if (wait_efn (s) == -1) return -1; /* Ok, get a new descriptor ... */ news = dup (0); if (news > 31) { errno = EMFILE; close (news); return -1; } /* Assign a new channel. */ inetdesc.size = 7; inetdesc.ptr = "_INET0:"; st = sys$assign (&inetdesc, &p[news].channel, 0, 0); if (QIO_ST_FAILED) { p[s].accept_pending = 0; sys$clref (s); return -1; } /* From info needs an int length field! */ size = sizeof (p[s].from) + 4; st = sys$qiow (0, p[news].channel, IO$_ACCEPT, p[news].iosb, 0, 0, &p[s].fromdummy, size, p[s].channel, 0, 0, 0); if (QIO_ST_FAILED || p[news].iosb[0] != SS$_NORMAL) { p[s].accept_pending = 0; sys$clref (s); return -1; } if (addr != 0) { /* Return the caller's info if requested. */ *addrlen = p[s].fromdummy; bcopy (&p[s].from, addr, *addrlen); } /* Fix up our new data structure. */ p[news].status = PASSIVE_CONNECTION; p[news].domain = AF_INET; p[news].passive = 1; p[news].fptr = fdopen (news, "r"); /* Allocate a buffer. */ p[news].fd_buff = (unsigned char *) malloc (BUF_SIZE); /* Be prepared to accept msgs. */ hang_a_read (news); /* Get the old descriptor back onto accepting. */ hang_an_accept (s); return news; } } else /* We don't handle any other domain yet. */ return -1; } /* Recv routine. */ int VMSrecv (s, buf, len, flags) int s; char *buf; int len, flags; { return recvfrom (s, buf, len, flags, 0, 0); } /* Revfrom routine. */ int VMSrecvfrom (s, buf, len, flags, from, fromlen) int s; char *buf; int len, flags; union socket_addr *from; int *fromlen; { int number; if (!tcp_make) set_tcp_make (); if (p[s].domain != AF_INET && p[s].domain != AF_X25) return -1; /* If we're not onto datagrams, then it's possible that a previous call to recvfrom didn't read all the data, and left some behind. So first of all, look in our data buffer for any leftovers that will satisfy this read. */ /* We couldn't satisfy the request from previous calls so we must now wait for a message to come through. */ if (wait_efn (s) == -1) /* Timed out. */ return -1; if (p[s].closed_by_remote == 1) { /* This could have happened! */ errno = ECONNRESET; return -1; } if (from != NULL) { if (tcp_make == CMU) { if (p[s].type == SOCK_DGRAM) { /* Not documented but we get the from data from the beginning of the data buffer. */ *fromlen = sizeof (p[s].from.in); from->in.sin_family = AF_INET; bcopy (&p[s].fd_buff[8], &(from->in.sin_port), 2); from->in.sin_port = htons (from->in.sin_port); bcopy (&p[s].fd_buff[0], &(from->in.sin_addr), 4); /* Remove the address data from front of data buffer. */ bcopy (p[s].fd_buff + 12, p[s].fd_buff, p[s].fd_buff_size); } else { *fromlen = p[s].fromlen; bcopy (&p[s].from, from, p[s].fromlen); } } else if (tcp_make == UCX) { *fromlen = p[s].fromdummy; bcopy (&p[s].from, from, p[s].fromdummy); } else { *fromlen = p[s].fromlen; bcopy (&p[s].from, from, p[s].fromlen); } } /* We may've received too much. */ number = p[s].fd_buff_size; if (number <= len) { /* If we haven't give back all the data available. */ bcopy (p[s].fd_buff, buf, number); p[s].fd_leftover = 0; hang_a_read (s); return (number); } else { /* If we have too much data then split it up. */ p[s].fd_leftover = p[s].fd_buff; bcopy (p[s].fd_leftover, buf, len); /* And change the pointers. */ p[s].fd_leftover += len; p[s].fd_buff_size -= len; return (len); } } /* Send routine. */ int VMSsend (s, msg, len, flags) int s; char *msg; int len, flags; { return sendto (s, msg, len, flags, 0, 0); } /* Sendto routine. */ int VMSsendto (s, msg, len, flags, to, tolen) int s; unsigned char *msg; int len, flags; union socket_addr *to; int tolen; { int i, j, st, size; unsigned char udpbuf[BUF_SIZE + 12]; char infobuff[1024], lhost[32]; unsigned short int temp; if (!tcp_make) set_tcp_make (); /* First remember who we sent it to and set the value of size. */ if (to != 0) { p[s].tolen = tolen; bcopy (to, &(p[s].to), tolen); size = tolen; } else size = 0; if (p[s].domain == AF_INET) { /* We might never have started a read for udp (socket/sendto) so put one here. */ if (p[s].type == SOCK_DGRAM) hang_a_read (s); if (tcp_make == CMU) { if (p[s].type == SOCK_DGRAM) { /* We might never have opened up a udp connection yet, so check. */ if (p[s].cmu_open != 1) { st = sys$qiow (0, p[s].channel, TCP$OPEN, p[s].iosb, 0, 0, 0, 0, 0, 0, 1, 0); if (QIO_ST_FAILED) return -1; p[s].cmu_open = 1; sys$qiow (0, p[s].channel, TCP$INFO, p[s].iosb, 0, 0, &infobuff, 1024, 0, 0, 0, 0); bcopy (infobuff + 264, &(p[s].name.in.sin_port), 2); p[s].name.in.sin_port = htons (p[s].name.in.sin_port); bcopy (infobuff + 136, lhost, infobuff[1]); lhost[infobuff[1]] = '\0'; sys$qiow (0, p[s].channel, GTHST, p[s].iosb, 0, 0, &infobuff, 1024, 1, lhost, 0, 0); bcopy (infobuff + 4, &(p[s].name.in.sin_addr), 4); } /* This isn't well documented. To send to a UDP socket, we need to put the address info at the beginning of the buffer. */ bcopy (msg, udpbuf + 12, len); bcopy (&p[s].to.in.sin_addr, udpbuf + 4, 4); temp = ntohs (p[s].to.in.sin_port); bcopy (&temp, udpbuf + 10, 2); bcopy (&p[s].name.in.sin_addr, udpbuf, 4); temp = ntohs (p[s].name.in.sin_port); bcopy (&temp, udpbuf + 8, 2); temp = len + 12; st = sys$qiow (0, p[s].channel, TCP$SEND, p[s].iosb, 0, 0, udpbuf, temp, 0, 0, 0, 0); if (QIO_FAILED) return -1; } else { /* TCP (! UDP) */ st = sys$qiow (0, p[s].channel, TCP$SEND, p[s].iosb, 0, 0, msg, len, 0, 0, 0, 0); if (QIO_FAILED) return -1; } return len; } else if (tcp_make == UCX) { struct itemlist rhost; rhost.length = sizeof (struct sockaddr_in); rhost.code = 0; rhost.dataptr = (char *) &p[s].to; st = sys$qiow (0, p[s].channel, IO$_WRITEVBLK, p[s].iosb, 0, 0, msg, len, &rhost, 0, 0, 0); if (QIO_FAILED) return -1; return len; } else { /* TWG */ st = sys$qiow (0, p[s].channel, IO$_WRITEVBLK, p[s].iosb, 0, 0, msg, len, 0, &p[s].to, size, 0); if (QIO_FAILED) return -1; return len; } } else /* We don't handle any other domain yet. */ return -1; } /* Getsockname routine. */ int VMSgetsockname (s, name, namelen) int s; union socket_addr *name; int *namelen; { int st; if (!tcp_make) set_tcp_make (); if (p[s].domain == AF_INET) { if (tcp_make == CMU) { /* For CMU we just return values held in our data structure. */ *namelen = p[s].namelen; bcopy (&(p[s].name), name, *namelen); return (0); } else if (tcp_make == UCX) { /* An item_list_3 descriptor. */ struct itemlist lhost; lhost.length = *namelen; lhost.code = 0; lhost.dataptr = (char *) name; /* Fill in namelen with actual ret len value. */ lhost.retlenptr = (short int *) namelen; st = sys$qiow (0, p[s].channel, IO$_SENSEMODE, p[s].iosb, 0, 0, 0, 0, &lhost, 0, 0, 0); if (QIO_FAILED) return -1; return 0; } else { /* TWG gives us the information. */ st = sys$qiow (0, p[s].channel, IO$_GETSOCKNAME, p[s].iosb, 0, 0, name, namelen, 0, 0, 0, 0); if (QIO_FAILED) return -1; return 0; } } else /* We don't handle any other domain yet. */ return -1; } /* Select routine. */ int VMSselect (nfds, readfds, writefds, exceptfds, timeout) int nfds; fd_set *readfds, *writefds, *exceptfds; struct timeval *timeout; { int timer, fd, alarm_set, total, end; long mask, cluster; struct descriptor termdesc; static fd_set new_readfds, new_writefds, new_exceptfds; FD_ZERO (&new_readfds); FD_ZERO (&new_writefds); FD_ZERO (&new_exceptfds); total = 0; /* Assign a terminal channel if we haven't already. */ if (terminal.chan == -1) { termdesc.size = 10; termdesc.ptr = "SYS$INPUT:"; sys$assign (&termdesc, &terminal.chan, 0, 0); } alarm_set = 0; if (timeout != NULL) { /* If a timeout is given then set the alarm. */ end = timeout->tv_sec; if (timer != 0) { /* We need to reset the alarm if it didn't fire, but we set it. */ alarm_set = 1; si_alarm (end); } } else end = 1; do { if (exceptfds) { /* Nothing */ ; } if (writefds) { for (fd = 0; fd < nfds; fd++) if (FD_ISSET (fd, writefds)) { if (p[fd].connect_pending) /* Nothing */ ; else if ((p[fd].status == ACTIVE_CONNECTION) || (p[fd].status == PASSIVE_CONNECTION)) { FD_SET (fd, &new_writefds); total++; } } } if (readfds) { /* True if data pending or an accept. */ for (fd = 3; fd < nfds; fd++) if (FD_ISSET (fd, readfds) && ((p[fd].fd_buff_size != -1) || (p[fd].accept_pending == 1))) { FD_SET (fd, &new_readfds); total++; } } if (total || (end == 0)) break; /* Otherwise, wait on an event flag. It's possible that the wait can be stopped by a spurious event flag being set -- i.e. one that's got a status not normal. So we've got to be prepared to loop around the wait until a valid reason happens. */ /* Set up the wait mask. */ cluster = 0; mask = 0; for (fd = 3; fd < nfds; fd++) { sys$clref (fd); if (readfds) if FD_ISSET (fd, readfds) mask |= (1 << fd); if (writefds) if FD_ISSET (fd, writefds) mask |= (1 << fd); if (exceptfds) if FD_ISSET (fd, exceptfds) mask |= (1 << fd); } mask |= (1 << TIMER_EFN); /* Clear it off just in case. */ sys$clref (TIMER_EFN); /* Wait around. */ sys$wflor (cluster, mask); mask = 0; if (read_efn (TIMER_EFN)) { errno = EINTR; break; } } while (1); /*NOTREACHED*/ /* Unset the alarm if we set it. */ if (alarm_set == 1) alarm (0); if (readfds) *readfds = new_readfds; if (writefds) *writefds = new_writefds; if (exceptfds) *exceptfds = new_exceptfds; return total; } /* Shutdown routine. */ VMSshutdown (s, how) int s, how; { int st; int ucx_how; if (!tcp_make) set_tcp_make (); if (p[s].domain == AF_INET) { if (tcp_make == CMU) { /* For CMU we just close off. */ si_close (s); return 0; } else if (tcp_make == UCX) { st = sys$qiow (0, p[s].channel, IO$_DEACCESS | IO$M_SHUTDOWN, p[s].iosb, 0, 0, 0, 0, 0, how, 0, 0); if (QIO_FAILED) return -1; return 0; } else { /* TWG lets us do it. */ st = sys$qiow (0, p[s].channel, IO$_SHUTDOWN, p[s].iosb, 0, 0, how, 0, 0, 0, 0, 0); if (QIO_FAILED) return -1; return 0; } } else /* it wasn't a socket */ return -1; } /* */ /* The following routines are used by the above socket calls. */ /* hang_a_read sets up a read to be finished at some later time. */ hang_a_read (s) int s; { extern int read_ast (); int size, st; /* Don't bother if we already did it. */ if (p[s].read_outstanding == 1) return; /* Have a read outstanding. */ p[s].read_outstanding = 1; size = sizeof (p[s].from) + 4; sys$clref (s); /* Clear off the event flag just in case, and reset the buf size. */ p[s].fd_buff_size = -1; if (p[s].domain == AF_INET) { if (tcp_make == CMU) { st = sys$qio (s, p[s].channel, TCP$RECEIVE, p[s].iosb, read_ast, &p[s], p[s].fd_buff, BUF_SIZE, 0, 0, 0, 0); if (QIO_ST_FAILED) return -1; } else if (tcp_make == UCX) { p[s].rhost.length = sizeof (struct sockaddr_in); p[s].rhost.code = 0; p[s].rhost.dataptr = (char *) &p[s].from; p[s].rhost.retlenptr = &p[s].fromdummy; st = sys$qio (s, p[s].channel, IO$_READVBLK, p[s].iosb, read_ast, &p[s], p[s].fd_buff, BUF_SIZE, &p[s].rhost, 0, 0, 0); if (QIO_ST_FAILED) return -1; } else { /* TWG */ st = sys$qio (s, p[s].channel, IO$_READVBLK, p[s].iosb, read_ast, &p[s], p[s].fd_buff, BUF_SIZE, 0, &p[s].fromlen, size, 0); if (QIO_ST_FAILED) return -1; } } else /* We don't handle any other domain yet. */ return -1; } /* hang_an_accept waits for a connection request to come in. */ hang_an_accept (s) int s; { extern int accept_ast (); int st; /* Clear the event flag just in case. */ sys$clref (s); /* Reset our flag & buf size. */ p[s].accept_pending = 0; p[s].fd_buff_size = -1; if (p[s].domain == AF_INET) { if (tcp_make == CMU) { st = sys$qio (s, p[s].channel, TCP$OPEN, p[s].iosb, accept_ast, &p[s], 0, 0, ntohs (p[s].name.in.sin_port), 0, 0, 0); if (QIO_ST_FAILED) return -1; } else if (tcp_make == UCX) { struct descriptor inetdesc; /* Assign channel for actual connection off listener. */ inetdesc.size = 3; inetdesc.ptr = "BG:"; if (sys$assign (&inetdesc, &p[s].ucx_accept_chan, 0, 0) != SS$_NORMAL) return -1; /* UCX's accept returns remote host info and the channel for a new socket to perform reads/writes on, so a sys$assign isn't really necessary. */ p[s].rhost.length = sizeof (struct sockaddr_in); p[s].rhost.dataptr = (char *) &p[s].from; p[s].fromdummy = 0; p[s].rhost.retlenptr = &p[s].fromdummy; st = sys$qio (s, p[s].channel, IO$_ACCESS | IO$M_ACCEPT, p[s].iosb, accept_ast, &p[s], 0, 0, &p[s].rhost, &p[s].ucx_accept_chan, 0, 0); if (QIO_ST_FAILED) return -1; } else { st = sys$qio (s, p[s].channel, IO$_ACCEPT_WAIT, p[s].iosb, accept_ast, &p[s], 0, 0, 0, 0, 0, 0); if (QIO_ST_FAILED) return -1; } } else /* We don't handle any other domain yet. */ return -1; } /* wait_efn just sets up a wait on either an event or the timer. */ wait_efn (s) int s; { long mask, cluster; cluster = 0; sys$clref (TIMER_EFN); mask = (1 << s) | (1 << TIMER_EFN); sys$wflor (cluster, mask); if (read_efn (TIMER_EFN)) { errno = EINTR; return -1; } return 0; } /* read_ast is called by the system whenever a read is done. */ read_ast (p) struct fd_entry *p; { int i, j; unsigned char *v, *w; /* Reset the outstanding flag. */ p->read_outstanding = 0; if (p->iosb[0] == SS$_NORMAL) { /* Check no errors. */ p->fd_buff_size = p->iosb[1]; if (tcp_make == CMU) { /* fiddle for DGRMs */ if (p->type == SOCK_DGRAM) p->fd_buff_size -= 12; } if (p->sig_req == 1) gsignal (SIGIO); } else if (p->iosb[0] == SS$_CLEARED) p->closed_by_remote = 1; else if (tcp_make == UCX) { if (p->iosb[0] == SS$_LINKDISCON) p->closed_by_remote = 1; } } /* accept_ast is called whenever an incoming call is detected. */ accept_ast (p) struct fd_entry *p; { if (p->iosb[0] == SS$_NORMAL) p->accept_pending = 1; else /* If it failed set up another listen. */ listen (p->s, p[p->s].backlog); } /* connect_ast is called whenever an async connect is made. */ connect_ast (p) struct fd_entry *p; { p->connect_pending = 0; if ((p->connected = p->iosb[0]) == SS$_NORMAL) { /* We made the connection. */ p->status = ACTIVE_CONNECTION; /* Be prepared to accept a msg. */ hang_a_read (p->s); } } /* */ /* These routines handle stream I/O. */ /* si_close -- must close off any connection in progress. */ si_close (s) int s; { if (!tcp_make) set_tcp_make (); if ((s < 0) || (s > 31)) return -1; if (p[s].channel != 0) { /* Was it one of our descriptors? */ if (p[s].domain == AF_INET) { if (tcp_make == CMU) sys$qiow (0, p[s].channel, TCP$CLOSE, p[s].iosb, 0, 0, 0, 0, 0, 0, 0, 0); if (p[s].status != HANDED_OFF) sys$dassgn (p[s].channel); close (s); free (p[s].fd_buff); p_initialise (s); } return 0; } else { /* Re-initialise data structure just in case. */ p[s].fd_buff_size = -1; p[s].accept_pending = 0; p[s].status = INITIALISED; return close (s); } } /* si_alarm -- insert a call to our own alarm function. */ si_alarm (i) int i; { extern int pre_alarm (); /* Make the call to pre_alarm instead of what the user wants; pre_alarm will call his routine when it finishes. */ /* VAX needs this call each time! */ signal (SIGALRM, pre_alarm); alarm (i); } /* pre_alarm -- gets called first on an alarm signal. */ pre_alarm () { /* Come here first so we can set our timer event flag. */ sys$setef (TIMER_EFN); (*alarm_function) (); } /* p_initialise - initialise our data array. */ p_initialise (s) int s; { int j; for (j = 0; j < 4; j++) p[s].iosb[j] = 0; p[s].channel = 0; p[s].fd_buff_size = -1; p[s].accept_pending = 0; p[s].connect_pending = 0; p[s].connected = 0; p[s].fd_buff = NULL; p[s].fd_leftover = NULL; p[s].fptr = NULL; p[s].s = s; p[s].name.in.sin_port = 0; p[s].masklen = 4; for (j = 0; j < 16; j++) p[s].mask[j] = 0xff; p[s].need_header = 0; p[s].status = INITIALISED; p[s].read_outstanding = 0; p[s].cmu_open = 0; p[s].x25_listener = 0; p[s].mother = s; p[s].child = 0; p[s].no_more_accepts = 0; p[s].closed_by_remote = 0; p[s].non_blocking = 0; p[s].sig_req = 0; sys$clref (s); } /* read_efn -- see whether an event flag is set. */ read_efn (i) int i; { int j; sys$readef (i, &j); j &= (1 << i); return j; } static set_tcp_make () { struct descriptor inetdesc; int channel; /* first try CMU */ inetdesc.size = 3; inetdesc.ptr = "IP:"; if (sys$assign (&inetdesc, &channel, 0, 0) == SS$_NORMAL) { sys$dassgn (channel); tcp_make = CMU; return; } /* next try TWG */ inetdesc.size = 7; inetdesc.ptr = "_INET0:"; if (sys$assign (&inetdesc, &channel, 0, 0) == SS$_NORMAL) { sys$dassgn (channel); tcp_make = WG; return; } /* next try UCX */ inetdesc.size = 4; inetdesc.ptr = "BG0:"; if (sys$assign (&inetdesc, &channel, 0, 0) == SS$_NORMAL) { sys$dassgn (channel); tcp_make = UCX; return; } /* nothing there oh dear!*/ tcp_make = NONE; return; } static char * getdevicename (channel) unsigned short int channel; { int st; struct { struct itemlist id; int eol; } itmlst; static char name[64]; short int lgth; name[0] = '\0'; itmlst.id.code = DVI$_DEVNAM; itmlst.id.length = 64; itmlst.id.dataptr = name; itmlst.id.retlenptr = &lgth; itmlst.eol = 0; st = sys$getdvi (0, channel, 0, &itmlst, 0, 0, 0, 0); if (QIO_ST_FAILED) fprintf (stderr, "error getting device name %d\n", st); return (name); }