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 2002 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
31 /*
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
35 *
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
39 */
40
41 /*
42 * check_bound.c
43 * Checks to see whether the program is still bound to the
44 * claimed address and returns the univeral merged address
45 *
46 */
47
48 #include <stdio.h>
49 #include <rpc/rpc.h>
50 #include <netconfig.h>
51 #include <netdir.h>
52 #include <sys/syslog.h>
53 #include <stdlib.h>
54 #include "rpcbind.h"
55 #include <string.h>
56 /* the following just to get my address */
57 #include <errno.h>
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <thread.h>
61 #include <synch.h>
62 #include <syslog.h>
63
64 struct fdlist {
65 int fd;
66 mutex_t fd_lock; /* protects fd */
67 struct netconfig *nconf;
68 struct fdlist *next;
69 int check_binding;
70 };
71
72 static struct fdlist *fdhead; /* Link list of the check fd's */
73 static struct fdlist *fdtail;
74 static char *nullstring = "";
75
76 /*
77 * Returns 1 if the given address is bound for the given addr & transport
78 * For all error cases, we assume that the address is bound
79 * Returns 0 for success.
80 *
81 * fdl: My FD list
82 * uaddr: the universal address
83 */
84 static bool_t
check_bound(struct fdlist * fdl,char * uaddr)85 check_bound(struct fdlist *fdl, char *uaddr)
86 {
87 int fd;
88 struct netbuf *na;
89 struct t_bind taddr, *baddr;
90 int ans;
91
92 if (fdl->check_binding == FALSE)
93 return (TRUE);
94
95 na = uaddr2taddr(fdl->nconf, uaddr);
96 if (!na)
97 return (TRUE); /* punt, should never happen */
98
99 taddr.addr = *na;
100 taddr.qlen = 1;
101 (void) mutex_lock(&fdl->fd_lock);
102 fd = fdl->fd;
103 baddr = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
104 if (baddr == NULL) {
105 (void) mutex_unlock(&fdl->fd_lock);
106 netdir_free((char *)na, ND_ADDR);
107 return (TRUE);
108 }
109 if (t_bind(fd, &taddr, baddr) != 0) {
110 (void) mutex_unlock(&fdl->fd_lock);
111 netdir_free((char *)na, ND_ADDR);
112 (void) t_free((char *)baddr, T_BIND);
113 return (TRUE);
114 }
115 if (t_unbind(fd) != 0) {
116 /* Bad fd. Purge this fd */
117 (void) t_close(fd);
118 fdl->fd = t_open(fdl->nconf->nc_device, O_RDWR, NULL);
119 if (fdl->fd == -1)
120 fdl->check_binding = FALSE;
121 }
122 (void) mutex_unlock(&fdl->fd_lock);
123 ans = memcmp(taddr.addr.buf, baddr->addr.buf, baddr->addr.len);
124 netdir_free((char *)na, ND_ADDR);
125 (void) t_free((char *)baddr, T_BIND);
126 return (ans == 0 ? FALSE : TRUE);
127 }
128
129 /*
130 * Keep open one more file descriptor for this transport, which
131 * will be used to determine whether the given service is up
132 * or not by trying to bind to the registered address.
133 * We are ignoring errors here. It trashes taddr and baddr;
134 * but that perhaps should not matter.
135 *
136 * We check for the following conditions:
137 * 1. Is it possible for t_bind to fail in the case where
138 * we bind to an already bound address and have any
139 * other error number besides TNOADDR.
140 * 2. If an address is specified in bind addr, can I bind to
141 * the same address.
142 * 3. If NULL is specified in bind addr, can I bind to the
143 * address to which the fd finally got bound.
144 */
145 int
add_bndlist(struct netconfig * nconf,struct t_bind * taddr,struct t_bind * baddr)146 add_bndlist(struct netconfig *nconf, struct t_bind *taddr, struct t_bind *baddr)
147 {
148 int fd;
149 struct fdlist *fdl;
150 struct netconfig *newnconf;
151 struct t_info tinfo;
152 struct t_bind tmpaddr;
153
154 newnconf = getnetconfigent(nconf->nc_netid);
155 if (newnconf == NULL)
156 return (-1);
157 fdl = (struct fdlist *)malloc((uint_t)sizeof (struct fdlist));
158 if (fdl == NULL) {
159 freenetconfigent(newnconf);
160 syslog(LOG_ERR, "no memory!");
161 return (-1);
162 }
163 (void) mutex_init(&fdl->fd_lock, USYNC_THREAD, NULL);
164 fdl->nconf = newnconf;
165 fdl->next = NULL;
166 if (fdhead == NULL) {
167 fdhead = fdl;
168 fdtail = fdl;
169 } else {
170 fdtail->next = fdl;
171 fdtail = fdl;
172 }
173 fdl->check_binding = FALSE;
174 if ((fdl->fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) {
175 /*
176 * Note that we haven't dequeued this entry nor have we freed
177 * the netconfig structure.
178 */
179 if (debugging) {
180 fprintf(stderr,
181 "%s: add_bndlist cannot open connection: %s",
182 nconf->nc_netid, t_errlist[t_errno]);
183 }
184 return (-1);
185 }
186
187 /* Set the qlen only for cots transports */
188 switch (tinfo.servtype) {
189 case T_COTS:
190 case T_COTS_ORD:
191 taddr->qlen = 1;
192 break;
193 case T_CLTS:
194 taddr->qlen = 0;
195 break;
196 default:
197 goto error;
198 }
199
200 if (t_bind(fdl->fd, taddr, baddr) != 0) {
201 if (t_errno == TNOADDR) {
202 fdl->check_binding = TRUE;
203 return (0); /* All is fine */
204 }
205 /* Perhaps condition #1 */
206 if (debugging) {
207 fprintf(stderr, "%s: add_bndlist cannot bind (1): %s",
208 nconf->nc_netid, t_errlist[t_errno]);
209 }
210 goto not_bound;
211 }
212
213 /* Condition #2 */
214 if (!memcmp(taddr->addr.buf, baddr->addr.buf,
215 (int)baddr->addr.len)) {
216 goto not_bound;
217 }
218
219 /* Condition #3 */
220 t_unbind(fdl->fd);
221 /* Set the qlen only for cots transports */
222 switch (tinfo.servtype) {
223 case T_COTS:
224 case T_COTS_ORD:
225 tmpaddr.qlen = 1;
226 break;
227 case T_CLTS:
228 tmpaddr.qlen = 0;
229 break;
230 default:
231 goto error;
232 }
233 tmpaddr.addr.len = tmpaddr.addr.maxlen = 0;
234 tmpaddr.addr.buf = NULL;
235 if (t_bind(fdl->fd, &tmpaddr, taddr) != 0) {
236 if (debugging) {
237 fprintf(stderr, "%s: add_bndlist cannot bind (2): %s",
238 nconf->nc_netid, t_errlist[t_errno]);
239 }
240 goto error;
241 }
242 /* Now fdl->fd is bound to a transport chosen address */
243 if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) {
244 if (debugging) {
245 fprintf(stderr,
246 "%s: add_bndlist cannot open connection: %s",
247 nconf->nc_netid, t_errlist[t_errno]);
248 }
249 goto error;
250 }
251 if (t_bind(fd, taddr, baddr) != 0) {
252 if (t_errno == TNOADDR) {
253 /*
254 * This transport is schizo. Previously it handled a
255 * request to bind to an already bound transport by
256 * returning a different bind address, and now it's
257 * returning a TNOADDR for essentially the same
258 * request. The spec may allow this behavior, so
259 * we'll just assume we can't do bind checking with
260 * this transport.
261 */
262 t_close(fd);
263 goto not_bound;
264 }
265 if (debugging) {
266 fprintf(stderr, "%s: add_bndlist cannot bind (3): %s",
267 nconf->nc_netid, t_errlist[t_errno]);
268 }
269 t_close(fd);
270 goto error;
271 }
272 t_close(fd);
273 if (!memcmp(taddr->addr.buf, baddr->addr.buf,
274 (int)baddr->addr.len)) {
275 switch (tinfo.servtype) {
276 case T_COTS:
277 case T_COTS_ORD:
278 if (baddr->qlen == 1) {
279 goto not_bound;
280 }
281 break;
282 case T_CLTS:
283 goto not_bound;
284 default:
285 goto error;
286 }
287 }
288
289 t_unbind(fdl->fd);
290 fdl->check_binding = TRUE;
291 return (0);
292
293 not_bound:
294 t_close(fdl->fd);
295 fdl->fd = -1;
296 return (1);
297
298 error:
299 t_close(fdl->fd);
300 fdl->fd = -1;
301 return (-1);
302 }
303
304 bool_t
is_bound(char * netid,char * uaddr)305 is_bound(char *netid, char *uaddr)
306 {
307 struct fdlist *fdl;
308
309 for (fdl = fdhead; fdl; fdl = fdl->next)
310 if (strcmp(fdl->nconf->nc_netid, netid) == 0)
311 break;
312 if (fdl == NULL)
313 return (TRUE);
314 return (check_bound(fdl, uaddr));
315 }
316
317 /* Return pointer to port string in the universal address */
318 #define UADDR_PRT_INDX(UADDR, PORT) { \
319 PORT = strrchr(UADDR, '.'); \
320 while (*--PORT != '.'); }
321 /*
322 * Returns NULL if there was some system error.
323 * Returns "" if the address was not bound, i.e the server crashed.
324 * Returns the merged address otherwise.
325 */
326 char *
mergeaddr(SVCXPRT * xprt,char * netid,char * uaddr,char * saddr)327 mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr)
328 {
329 struct fdlist *fdl;
330 struct nd_mergearg ma;
331 int stat;
332
333 for (fdl = fdhead; fdl; fdl = fdl->next)
334 if (strcmp(fdl->nconf->nc_netid, netid) == 0)
335 break;
336 if (fdl == NULL)
337 return (NULL);
338 if (check_bound(fdl, uaddr) == FALSE)
339 /* that server died */
340 return (nullstring);
341 /*
342 * If saddr is not NULL, the remote client may have included the
343 * address by which it contacted us. Use that for the "client" uaddr,
344 * otherwise use the info from the SVCXPRT.
345 */
346 if (saddr != NULL) {
347 ma.c_uaddr = saddr;
348 } else {
349
350 /* retrieve the client's address */
351 ma.c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt));
352 if (ma.c_uaddr == NULL) {
353 syslog(LOG_ERR, "taddr2uaddr failed for %s: %s",
354 fdl->nconf->nc_netid, netdir_sperror());
355 return (NULL);
356 }
357
358 }
359
360 /* Not an INET address? */
361 if ((strcmp(fdl->nconf->nc_protofmly, NC_INET) != 0) &&
362 (strcmp(fdl->nconf->nc_protofmly, NC_INET6) != 0)) {
363 ma.s_uaddr = uaddr;
364 stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma);
365 }
366 /* Inet address, but no xp_ltaddr */
367 else if ((ma.s_uaddr = taddr2uaddr(fdl->nconf,
368 &(xprt)->xp_ltaddr)) == NULL) {
369 ma.s_uaddr = uaddr;
370 stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma);
371 } else {
372 /*
373 * (xprt)->xp_ltaddr contains portmap's port address.
374 * Overwrite this with actual application's port address
375 * before returning to the caller.
376 */
377 char *s_uport, *uport;
378
379 /* Get the INET/INET6 address part from ma.s_uaddr */
380 UADDR_PRT_INDX(ma.s_uaddr, s_uport);
381 *s_uport = '\0';
382
383 /* Get the port info from uaddr */
384 UADDR_PRT_INDX(uaddr, uport);
385
386 ma.m_uaddr = malloc(strlen(ma.s_uaddr) + strlen(uport) + 1);
387 if (ma.m_uaddr == NULL) {
388 syslog(LOG_ERR, "mergeaddr: no memory!");
389 free(ma.s_uaddr);
390 if (saddr == NULL)
391 free(ma.c_uaddr);
392 return (NULL);
393 }
394
395 /* Copy IP address into the Universal address holder */
396 strcpy(ma.m_uaddr, ma.s_uaddr);
397 /* Append port info to the Universal address holder */
398 strcat(ma.m_uaddr, uport);
399 free(ma.s_uaddr);
400 stat = 0;
401 }
402 if (saddr == NULL) {
403 free(ma.c_uaddr);
404 }
405 if (stat) {
406 syslog(LOG_ERR, "netdir_merge failed for %s: %s",
407 fdl->nconf->nc_netid, netdir_sperror());
408 return (NULL);
409 }
410
411 return (ma.m_uaddr);
412 }
413
414 /*
415 * Returns a netconf structure from its internal list. This
416 * structure should not be freed.
417 */
418 struct netconfig *
rpcbind_get_conf(char * netid)419 rpcbind_get_conf(char *netid)
420 {
421 struct fdlist *fdl;
422
423 for (fdl = fdhead; fdl; fdl = fdl->next)
424 if (strcmp(fdl->nconf->nc_netid, netid) == 0)
425 break;
426 if (fdl == NULL)
427 return (NULL);
428 return (fdl->nconf);
429 }
430