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