2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A/*
2N/A * Portions of this source code were derived from Berkeley 4.3 BSD
2N/A * under license from the Regents of the University of California.
2N/A */
2N/A
2N/A/*
2N/A * XXX This routine should be changed to use
2N/A * ND_CHECK_RESERVED_PORT and ND_SET_RESERVED_PORT
2N/A * which can be invoked via netdir_options.
2N/A */
2N/A#include <stdio.h>
2N/A#include <rpc/rpc.h>
2N/A#include <netinet/in.h>
2N/A#include <sys/socket.h>
2N/A#include <netdb.h>
2N/A#include <errno.h>
2N/A#include <rpc/nettype.h>
2N/A#include <stropts.h>
2N/A#include <string.h>
2N/A#include <tiuser.h>
2N/A#include <unistd.h>
2N/A
2N/A#define STARTPORT 600
2N/A#define ENDPORT (IPPORT_RESERVED - 1)
2N/A#define NPORTS (ENDPORT - STARTPORT + 1)
2N/A
2N/A/*
2N/A * The argument is a client handle for a UDP connection.
2N/A * Unbind its transport endpoint from the existing port
2N/A * and rebind it to a reserved port.
2N/A * On failure, the client handle can be unbound even if it
2N/A * was previously bound. Callers should destroy the client
2N/A * handle after a failure.
2N/A */
2N/Aint
2N/A__clnt_bindresvport(cl)
2N/A CLIENT *cl;
2N/A{
2N/A int fd;
2N/A int res;
2N/A short port;
2N/A struct sockaddr_in *sin;
2N/A struct sockaddr_in6 *sin6;
2N/A extern int errno;
2N/A /* extern int t_errno; */
2N/A struct t_bind *tbind, *tres;
2N/A int i;
2N/A bool_t ipv6_fl = FALSE;
2N/A struct netconfig *nconf;
2N/A
2N/A /* make sure it's a UDP connection */
2N/A nconf = getnetconfigent(cl->cl_netid);
2N/A if (nconf == NULL)
2N/A return (-1);
2N/A if ((nconf->nc_semantics != NC_TPI_CLTS) ||
2N/A (strcmp(nconf->nc_protofmly, NC_INET) &&
2N/A strcmp(nconf->nc_protofmly, NC_INET6)) ||
2N/A strcmp(nconf->nc_proto, NC_UDP)) {
2N/A freenetconfigent(nconf);
2N/A return (0); /* not udp - don't need resv port */
2N/A }
2N/A if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
2N/A ipv6_fl = TRUE;
2N/A freenetconfigent(nconf);
2N/A
2N/A if (!clnt_control(cl, CLGET_FD, (char *)&fd)) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* If fd is already bound - unbind it */
2N/A if (t_getstate(fd) != T_UNBND) {
2N/A while ((t_unbind(fd) < 0) && (t_errno == TLOOK)) {
2N/A /*
2N/A * If there is a message queued to this descriptor,
2N/A * remove it.
2N/A */
2N/A struct strbuf ctl[1], data[1];
2N/A char ctlbuf[sizeof (union T_primitives) + 32];
2N/A char databuf[256];
2N/A int flags;
2N/A
2N/A ctl->maxlen = sizeof (ctlbuf);
2N/A ctl->buf = ctlbuf;
2N/A data->maxlen = sizeof (databuf);
2N/A data->buf = databuf;
2N/A flags = 0;
2N/A if (getmsg(fd, ctl, data, &flags) < 0)
2N/A return (-1);
2N/A
2N/A }
2N/A if (t_getstate(fd) != T_UNBND)
2N/A return (-1);
2N/A }
2N/A
2N/A tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
2N/A if (tbind == NULL) {
2N/A if (t_errno == TBADF)
2N/A errno = EBADF;
2N/A return (-1);
2N/A }
2N/A tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
2N/A if (tres == NULL) {
2N/A (void) t_free((char *)tbind, T_BIND);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) memset((char *)tbind->addr.buf, 0, tbind->addr.len);
2N/A /* warning: this sockaddr_in is truncated to 8 bytes */
2N/A
2N/A if (ipv6_fl == TRUE) {
2N/A sin6 = (struct sockaddr_in6 *)tbind->addr.buf;
2N/A sin6->sin6_family = AF_INET6;
2N/A } else {
2N/A sin = (struct sockaddr_in *)tbind->addr.buf;
2N/A sin->sin_family = AF_INET;
2N/A }
2N/A
2N/A tbind->qlen = 0;
2N/A tbind->addr.len = tbind->addr.maxlen;
2N/A
2N/A /*
2N/A * Need to find a reserved port in the interval
2N/A * STARTPORT - ENDPORT. Choose a random starting
2N/A * place in the interval based on the process pid
2N/A * and sequentially search the ports for one
2N/A * that is available.
2N/A */
2N/A port = (getpid() % NPORTS) + STARTPORT;
2N/A
2N/A for (i = 0; i < NPORTS; i++) {
2N/A if (ipv6_fl == TRUE)
2N/A sin6->sin6_port = htons(port++);
2N/A else
2N/A sin->sin_port = htons(port++);
2N/A if (port > ENDPORT)
2N/A port = STARTPORT;
2N/A /*
2N/A * Try to bind to the requested address. If
2N/A * the call to t_bind succeeds, then we need
2N/A * to make sure that the address that we bound
2N/A * to was the address that we requested. If it
2N/A * was, then we are done. If not, we fake an
2N/A * EADDRINUSE error by setting res, t_errno,
2N/A * and errno to indicate that a bind failure
2N/A * occurred. Otherwise, if the t_bind call
2N/A * failed, we check to see whether it makes
2N/A * sense to continue trying to t_bind requests.
2N/A */
2N/A res = t_bind(fd, tbind, tres);
2N/A if (res == 0) {
2N/A if (memcmp(tbind->addr.buf, tres->addr.buf,
2N/A (int)tres->addr.len) == 0)
2N/A break;
2N/A (void) t_unbind(fd);
2N/A res = -1;
2N/A t_errno = TSYSERR;
2N/A errno = EADDRINUSE;
2N/A } else if (t_errno != TSYSERR || errno != EADDRINUSE) {
2N/A if (t_errno == TACCES)
2N/A errno = EACCES;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A (void) t_free((char *)tbind, T_BIND);
2N/A (void) t_free((char *)tres, T_BIND);
2N/A return (res);
2N/A}