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