/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ #ident "@(#)bind.c 1.20 95/07/12 SMI" /* SVr4.0 1.9 */ /* * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * PROPRIETARY NOTICE (Combined) * * This source code is unpublished proprietary information * constituting, or derived under license from AT&T's UNIX(r) System V. * In addition, portions of such source code were derived from Berkeley * 4.3 BSD under license from the Regents of the University of * California. * * * * Copyright Notice * * Notice of copyright on this source code product does not indicate * publication. * * (c) 1986,1987,1988.1989 Sun Microsystems, Inc * (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. * All rights reserved. * */ #include "sockmt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sock.h" static int _unbind(struct _si_user *siptr); #pragma weak bind = _bind int _bind(s, name, namelen) register int s; register struct sockaddr *name; register int namelen; { register struct _si_user *siptr; sigset_t mask; int retval; u_int family; if ((siptr = _s_checkfd(s)) == NULL) return (-1); MUTEX_LOCK_SIGMASK(&siptr->lock, mask); if (name == NULL || namelen == 0) { MUTEX_UNLOCK_SIGMASK(&siptr->lock, mask); errno = EINVAL; return (-1); } if (siptr->udata.so_state & SS_ISBOUND) { MUTEX_UNLOCK_SIGMASK(&siptr->lock, mask); errno = EINVAL; return (-1); } family = name->sa_family; if (family == 0) { /* * Handle old applications that do not set the family * in the bind call. */ family = siptr->udata.sockparams.sp_family; name->sa_family = family; } /* * In UNIX domain a bind address must be given. */ if (family == AF_UNIX) { if (namelen <= sizeof (name->sa_family)) { MUTEX_UNLOCK_SIGMASK(&siptr->lock, mask); errno = EISDIR; return (-1); } if (namelen > sizeof (struct sockaddr_un)) { MUTEX_UNLOCK_SIGMASK(&siptr->lock, mask); errno = EINVAL; return (-1); } if (name->sa_data[0] == 0) { MUTEX_UNLOCK_SIGMASK(&siptr->lock, mask); errno = EINVAL; return (-1); } } retval = __bind(siptr, name, namelen, NULL, NULL); MUTEX_UNLOCK_SIGMASK(&siptr->lock, mask); return (retval); } int __bind(siptr, name, namelen, raddr, raddrlen) register struct _si_user *siptr; register struct sockaddr *name; register int namelen; register char *raddr; register int *raddrlen; { register char *buf; register struct T_bind_req *bind_req; register struct T_bind_ack *bind_ack; register int size; char *cbuf; sigset_t procmask; struct stat rstat; struct bind_ux bind_ux; int fflag; int didalloc = 0; if (siptr->udata.sockparams.sp_family == AF_UNIX) { (void) memset((caddr_t)&bind_ux, 0, sizeof (bind_ux)); if (name == NULL) { bind_ux.name.sun_family = AF_UNIX; fflag = 0; } else { struct sockaddr_un *un; int len; un = (struct sockaddr_un *)name; if (un->sun_family != AF_UNIX) { errno = EINVAL; return (-1); } if (namelen > sizeof (*un) || (len = _s_uxpathlen(un)) == sizeof (un->sun_path)) { errno = EMSGSIZE; return (-1); } un->sun_path[len] = 0; /* Null terminate */ /* * We really have to bind to the actual vnode, so * that renames will not cause a problem. First * create the file and then get the dev/ino to * bind to. */ if ((_ioctl(siptr->fd, _I_BIND_RSVD, un->sun_path) < 0) || #if defined(i386) (_xstat(_STAT_VER, (caddr_t) un->sun_path, &rstat) < 0)) { #elif defined(sparc) _stat((caddr_t)un->sun_path, &rstat) < 0) { #else #error Unknown architecture! #endif if (errno == EEXIST) errno = EADDRINUSE; return (-1); } bind_ux.extdev = rstat.st_dev; bind_ux.extino = rstat.st_ino; bind_ux.extsize = sizeof (struct ux_dev); (void) memcpy((caddr_t)&bind_ux.name, (caddr_t)name, namelen); fflag = 1; } name = (struct sockaddr *)&bind_ux; namelen = sizeof (bind_ux); } else namelen = _s_min(namelen, siptr->udata.addrsize); if (siptr->ctlbuf) { cbuf = siptr->ctlbuf; siptr->ctlbuf = NULL; } else { /* * siptr->ctlbuf is in use * allocate and free after use. */ if (_s_cbuf_alloc(siptr, &cbuf) < 0) return (-1); didalloc = 1; } buf = cbuf; bind_req = (struct T_bind_req *)buf; size = sizeof (*bind_req); bind_req->PRIM_type = T_BIND_REQ; bind_req->ADDR_length = name == NULL ? 0 : namelen; bind_req->ADDR_offset = 0; bind_req->CONIND_number = 0; if (bind_req->ADDR_length) { _s_aligned_copy(buf, bind_req->ADDR_length, size, (caddr_t)name, (int *)&bind_req->ADDR_offset); size = bind_req->ADDR_offset + bind_req->ADDR_length; } _s_blockallsignals(&procmask); if (!_s_do_ioctl(siptr->fd, buf, size, TI_BIND, NULL)) { _s_restoresigmask(&procmask); goto err_out; } _s_restoresigmask(&procmask); bind_ack = (struct T_bind_ack *)buf; buf += bind_ack->ADDR_offset; /* * Check that the address returned by the * transport provider meets the criteria. */ errno = 0; if (name != (struct sockaddr *)NULL) { /* * Some programs like inetd(1M) don't set the * family field. So we use the family field that * was passed in to the socket() call. */ switch (siptr->udata.sockparams.sp_family) { case AF_INET: { struct sockaddr_in *rname; struct sockaddr_in *aname; rname = (struct sockaddr_in *)buf; aname = (struct sockaddr_in *)name; if (aname->sin_port != 0 && aname->sin_port != rname->sin_port) errno = EADDRINUSE; if (aname->sin_addr.s_addr != INADDR_ANY && aname->sin_addr.s_addr != rname->sin_addr.s_addr) errno = EADDRNOTAVAIL; break; } case AF_UNIX: if (fflag) { register struct bind_ux *rbind_ux; rbind_ux = (struct bind_ux *)buf; if (rbind_ux->extdev != bind_ux.extdev || rbind_ux->extino != bind_ux.extino) errno = EADDRINUSE; } break; default: if (bind_ack->ADDR_length != namelen) errno = EADDRNOTAVAIL; else if (memcmp((caddr_t)name, buf, namelen) != 0) errno = EADDRNOTAVAIL; } } if (errno) { register int error; error = errno; /* Save it */ (void) _unbind(siptr); if (name && name->sa_family == AF_UNIX && fflag) { (void) _ioctl(siptr->fd, _I_RELE_RSVD); (void) unlink(name->sa_data); } errno = error; goto err_out; } /* * Copy back the bound address if requested. */ if (raddr != NULL) *raddrlen = _s_cpaddr(siptr, raddr, *raddrlen, buf, bind_ack->ADDR_length); siptr->udata.so_state |= SS_ISBOUND; if (didalloc) free(cbuf); else siptr->ctlbuf = cbuf; return (0); err_out: if (didalloc) free(cbuf); else siptr->ctlbuf = cbuf; return (-1); } static int _unbind(siptr) register struct _si_user *siptr; { sigset_t procmask; char *cbuf; int didalloc = 0; if (siptr->ctlbuf) { cbuf = siptr->ctlbuf; siptr->ctlbuf = NULL; } else { /* * siptr->ctlbuf is in use * allocate and free after use. */ if (_s_cbuf_alloc(siptr, &cbuf) < 0) return (-1); didalloc = 1; } ((struct T_unbind_req *)cbuf)->PRIM_type = T_UNBIND_REQ; _s_blockallsignals(&procmask); if (!_s_do_ioctl(siptr->fd, cbuf, sizeof (struct T_unbind_req), TI_UNBIND, NULL)) { _s_restoresigmask(&procmask); if (didalloc) free(cbuf); else siptr->ctlbuf = cbuf; return (-1); } _s_restoresigmask(&procmask); siptr->udata.so_state &= ~SS_ISBOUND; if (didalloc) free(cbuf); else siptr->ctlbuf = cbuf; return (0); }