1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * XXX This routine should be changed to use 39 * ND_CHECK_RESERVED_PORT and ND_SET_RESERVED_PORT 40 * which can be invoked via netdir_options. 41 */ 42 #include <stdio.h> 43 #include <rpc/rpc.h> 44 #include <netinet/in.h> 45 #include <sys/socket.h> 46 #include <netdb.h> 47 #include <errno.h> 48 #include <rpc/nettype.h> 49 #include <stropts.h> 50 #include <string.h> 51 #include <tiuser.h> 52 #include <unistd.h> 53 54 #define STARTPORT 600 55 #define ENDPORT (IPPORT_RESERVED - 1) 56 #define NPORTS (ENDPORT - STARTPORT + 1) 57 58 /* 59 * The argument is a client handle for a UDP connection. 60 * Unbind its transport endpoint from the existing port 61 * and rebind it to a reserved port. 62 * On failure, the client handle can be unbound even if it 63 * was previously bound. Callers should destroy the client 64 * handle after a failure. 65 */ 66 int 67 __clnt_bindresvport(cl) 68 CLIENT *cl; 69 { 70 int fd; 71 int res; 72 short port; 73 struct sockaddr_in *sin; 74 struct sockaddr_in6 *sin6; 75 extern int errno; 76 /* extern int t_errno; */ 77 struct t_bind *tbind, *tres; 78 int i; 79 bool_t ipv6_fl = FALSE; 80 struct netconfig *nconf; 81 82 /* make sure it's a UDP connection */ 83 nconf = getnetconfigent(cl->cl_netid); 84 if (nconf == NULL) 85 return (-1); 86 if ((nconf->nc_semantics != NC_TPI_CLTS) || 87 (strcmp(nconf->nc_protofmly, NC_INET) && 88 strcmp(nconf->nc_protofmly, NC_INET)) || 89 strcmp(nconf->nc_proto, NC_UDP)) { 90 freenetconfigent(nconf); 91 return (0); /* not udp - don't need resv port */ 92 } 93 if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) 94 ipv6_fl = TRUE; 95 freenetconfigent(nconf); 96 97 if (!clnt_control(cl, CLGET_FD, (char *)&fd)) { 98 return (-1); 99 } 100 101 /* If fd is already bound - unbind it */ 102 if (t_getstate(fd) != T_UNBND) { 103 while ((t_unbind(fd) < 0) && (t_errno == TLOOK)) { 104 /* 105 * If there is a message queued to this descriptor, 106 * remove it. 107 */ 108 struct strbuf ctl[1], data[1]; 109 char ctlbuf[sizeof (union T_primitives) + 32]; 110 char databuf[256]; 111 int flags; 112 113 ctl->maxlen = sizeof (ctlbuf); 114 ctl->buf = ctlbuf; 115 data->maxlen = sizeof (databuf); 116 data->buf = databuf; 117 flags = 0; 118 if (getmsg(fd, ctl, data, &flags) < 0) 119 return (-1); 120 121 } 122 if (t_getstate(fd) != T_UNBND) 123 return (-1); 124 } 125 126 tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); 127 if (tbind == NULL) { 128 if (t_errno == TBADF) 129 errno = EBADF; 130 return (-1); 131 } 132 tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); 133 if (tres == NULL) { 134 (void) t_free((char *)tbind, T_BIND); 135 return (-1); 136 } 137 138 (void) memset((char *)tbind->addr.buf, 0, tbind->addr.len); 139 /* warning: this sockaddr_in is truncated to 8 bytes */ 140 141 if (ipv6_fl == TRUE) { 142 sin6 = (struct sockaddr_in6 *)tbind->addr.buf; 143 sin6->sin6_family = AF_INET6; 144 } else { 145 sin = (struct sockaddr_in *)tbind->addr.buf; 146 sin->sin_family = AF_INET; 147 } 148 149 tbind->qlen = 0; 150 tbind->addr.len = tbind->addr.maxlen; 151 152 /* 153 * Need to find a reserved port in the interval 154 * STARTPORT - ENDPORT. Choose a random starting 155 * place in the interval based on the process pid 156 * and sequentially search the ports for one 157 * that is available. 158 */ 159 port = (getpid() % NPORTS) + STARTPORT; 160 161 for (i = 0; i < NPORTS; i++) { 162 sin->sin_port = htons(port++); 163 if (port > ENDPORT) 164 port = STARTPORT; 165 /* 166 * Try to bind to the requested address. If 167 * the call to t_bind succeeds, then we need 168 * to make sure that the address that we bound 169 * to was the address that we requested. If it 170 * was, then we are done. If not, we fake an 171 * EADDRINUSE error by setting res, t_errno, 172 * and errno to indicate that a bind failure 173 * occurred. Otherwise, if the t_bind call 174 * failed, we check to see whether it makes 175 * sense to continue trying to t_bind requests. 176 */ 177 res = t_bind(fd, tbind, tres); 178 if (res == 0) { 179 if (memcmp(tbind->addr.buf, tres->addr.buf, 180 (int)tres->addr.len) == 0) 181 break; 182 (void) t_unbind(fd); 183 res = -1; 184 t_errno = TSYSERR; 185 errno = EADDRINUSE; 186 } else if (t_errno != TSYSERR || errno != EADDRINUSE) { 187 if (t_errno == TACCES) 188 errno = EACCES; 189 break; 190 } 191 } 192 193 (void) t_free((char *)tbind, T_BIND); 194 (void) t_free((char *)tres, T_BIND); 195 return (res); 196 } 197