4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Contains routines that deal with TLI/XTI endpoints and rpc services.
30 #include <sys/types.h>
36 #include <sys/sysmacros.h>
37 #include <netconfig.h>
39 #include <sys/sockio.h>
40 #include "inetd_impl.h"
42 uu_list_pool_t
*conn_ind_pool
= NULL
;
49 * Returns B_TRUE if the non-address components of the 2 rpc_info_t structures
50 * are equivalent, else B_FALSE.
53 rpc_info_equal(const rpc_info_t
*ri
, const rpc_info_t
*ri2
)
55 return ((ri
->prognum
== ri2
->prognum
) &&
56 (ri
->lowver
== ri2
->lowver
) &&
57 (ri
->highver
== ri2
->highver
) &&
58 (strcmp(ri
->netid
, ri2
->netid
) == 0));
62 * Determine if we have a configured interface for the specified address
63 * family. This code is a mirror of libnsl's __can_use_af(). We mirror
64 * it because we need an exact duplicate of its behavior, yet the
65 * function isn't exported by libnsl, and this fix is considered short-
66 * term, so it's not worth exporting it.
68 * We need to duplicate __can_use_af() so we can accurately determine
69 * when getnetconfigent() returns failure for a v6 netid due to no IPv6
70 * interfaces being configured: getnetconfigent() returns failure
71 * if a netid is either 'tcp6' or 'udp6' and __can_use_af() returns 0,
72 * but it doesn't return a return code to uniquely determine this
73 * failure. If we don't accurately determine these failures, we could
74 * output error messages in a case when they weren't justified.
77 can_use_af(sa_family_t af
)
82 if ((fd
= open("/dev/udp", O_RDONLY
)) < 0) {
85 lifn
.lifn_family
= af
;
86 /* LINTED ECONST_EXPR */
87 lifn
.lifn_flags
= IFF_UP
& !(IFF_NOXMIT
| IFF_DEPRECATED
);
88 if (ioctl(fd
, SIOCGLIFNUM
, &lifn
, sizeof (lifn
)) < 0) {
93 return (lifn
.lifn_count
);
97 is_v6_netid(const char *netid
)
99 return ((strcmp(netid
, SOCKET_PROTO_TCP6
) == 0) ||
100 (strcmp(netid
, SOCKET_PROTO_UDP6
) == 0));
104 * Registers with rpcbind the program number with all versions, from low to
105 * high, with the netid, all specified in 'rpc'. If registration fails,
106 * returns -1, else 0.
109 register_rpc_service(const char *fmri
, const rpc_info_t
*rpc
)
111 struct netconfig
*nconf
;
114 if ((nconf
= getnetconfigent(rpc
->netid
)) == NULL
) {
116 * Check whether getnetconfigent() failed as a result of
117 * having no IPv6 interfaces configured for a v6 netid, or
118 * as a result of a 'real' error, and output an appropriate
119 * message with an appropriate severity.
121 if (is_v6_netid(rpc
->netid
) && !can_use_af(AF_INET6
)) {
123 "Couldn't register netid %s for RPC instance %s "
124 "because no IPv6 interfaces are plumbed"),
128 "Failed to lookup netid '%s' for instance %s: %s"),
129 rpc
->netid
, fmri
, nc_sperror());
134 for (ver
= rpc
->lowver
; ver
<= rpc
->highver
; ver
++) {
135 if (!rpcb_set(rpc
->prognum
, ver
, nconf
, &(rpc
->netbuf
))) {
136 error_msg(gettext("Failed to register version %d "
137 "of RPC service instance %s, netid %s"), ver
,
140 for (ver
--; ver
>= rpc
->lowver
; ver
--)
141 (void) rpcb_unset(rpc
->prognum
, ver
, nconf
);
143 freenetconfigent(nconf
);
148 freenetconfigent(nconf
);
152 /* Unregister all the registrations done by register_rpc_service */
154 unregister_rpc_service(const char *fmri
, const rpc_info_t
*rpc
)
157 struct netconfig
*nconf
;
159 if ((nconf
= getnetconfigent(rpc
->netid
)) == NULL
) {
161 * Don't output an error message if getnetconfigent() fails for
162 * a v6 netid when an IPv6 interface isn't configured.
164 if (!(is_v6_netid(rpc
->netid
) && !can_use_af(AF_INET6
))) {
166 "Failed to lookup netid '%s' for instance %s: %s"),
167 rpc
->netid
, fmri
, nc_sperror());
172 for (ver
= rpc
->lowver
; ver
<= rpc
->highver
; ver
++)
173 (void) rpcb_unset(rpc
->prognum
, ver
, nconf
);
175 freenetconfigent(nconf
);
185 if ((conn_ind_pool
= uu_list_pool_create("conn_ind_pool",
186 sizeof (tlx_conn_ind_t
), offsetof(tlx_conn_ind_t
, link
),
187 NULL
, UU_LIST_POOL_DEBUG
)) == NULL
) {
188 error_msg("%s: %s", gettext("Failed to create uu pool"),
189 uu_strerror(uu_error()));
199 if (conn_ind_pool
!= NULL
) {
200 uu_list_pool_destroy(conn_ind_pool
);
201 conn_ind_pool
= NULL
;
206 * Checks if the contents of the 2 tlx_info_t structures are equivalent.
207 * If 'isrpc' is false, the address components of the two structures are
208 * compared for equality as part of this. If the two structures are
209 * equivalent B_TRUE is returned, else B_FALSE.
212 tlx_info_equal(const tlx_info_t
*ti
, const tlx_info_t
*ti2
, boolean_t isrpc
)
214 return ((isrpc
|| (memcmp(ti
->local_addr
.buf
, ti2
->local_addr
.buf
,
215 sizeof (struct sockaddr_storage
)) == 0)) &&
216 (strcmp(ti
->dev_name
, ti2
->dev_name
) == 0));
220 * Attempts to bind an address to the network fd 'fd'. If 'reqaddr' is non-NULL,
221 * it attempts to bind to that requested address, else it binds to a kernel
222 * selected address. In the former case, the function returning success
223 * doesn't guarantee that the requested address was bound (the caller needs to
224 * check). If 'retaddr' is non-NULL, the bound address is returned in it. The
225 * 'qlen' parameter is used to set the connection backlog. If the bind
226 * succeeds 0 is returned, else -1.
229 tlx_bind(int fd
, const struct netbuf
*reqaddr
, struct netbuf
*retaddr
, int qlen
)
234 if (retaddr
!= NULL
) { /* caller requests bound address be returned */
235 bret
.addr
.buf
= retaddr
->buf
;
236 bret
.addr
.maxlen
= retaddr
->maxlen
;
239 if (reqaddr
!= NULL
) { /* caller requests specific address */
240 breq
.addr
.buf
= reqaddr
->buf
;
241 breq
.addr
.len
= reqaddr
->len
;
247 if (t_bind(fd
, &breq
, retaddr
!= NULL
? &bret
: NULL
) < 0)
251 retaddr
->len
= bret
.addr
.len
;
257 tlx_setsockopt(int fd
, int level
, int optname
, const void *optval
,
260 struct t_optmgmt request
, reply
;
262 struct opthdr sockopt
;
266 if (optlen
> sizeof (optbuf
.data
)) {
267 error_msg(gettext("t_optmgmt request too long"));
271 optbuf
.sockopt
.level
= level
;
272 optbuf
.sockopt
.name
= optname
;
273 optbuf
.sockopt
.len
= optlen
;
274 (void) memcpy(optbuf
.data
, optval
, optlen
);
276 request
.opt
.len
= sizeof (struct opthdr
) + optlen
;
277 request
.opt
.buf
= (char *)&optbuf
;
278 request
.flags
= T_NEGOTIATE
;
280 reply
.opt
.maxlen
= sizeof (struct opthdr
) + optlen
;
281 reply
.opt
.buf
= (char *)&optbuf
;
284 if ((t_optmgmt(fd
, &request
, &reply
) == -1) ||
285 (reply
.flags
!= T_SUCCESS
)) {
286 error_msg("t_optmgmt: %s", t_strerror(t_errno
));
293 * Compare contents of netbuf for equality. Return B_TRUE on a match and
294 * B_FALSE for mismatch.
297 netbufs_equal(struct netbuf
*n1
, struct netbuf
*n2
)
299 return ((n1
->len
== n2
->len
) &&
300 (memcmp(n1
->buf
, n2
->buf
, (size_t)n1
->len
) == 0));
304 * Create a tli/xti endpoint, either bound to the address specified in
305 * 'instance' for non-RPC services, else a kernel chosen address.
306 * Returns -1 on failure, else 0.
309 create_bound_endpoint(const instance_t
*inst
, tlx_info_t
*tlx_info
)
313 const char *fmri
= inst
->fmri
;
314 struct netbuf
*reqaddr
;
315 struct netbuf
*retaddr
;
316 struct netbuf netbuf
;
317 struct sockaddr_storage ss
;
318 rpc_info_t
*rpc
= tlx_info
->pr_info
.ri
;
320 if ((fd
= t_open(tlx_info
->dev_name
, O_RDWR
, NULL
)) == -1) {
321 error_msg(gettext("Failed to open transport %s for "
322 "instance %s, proto %s: %s"), tlx_info
->dev_name
,
323 fmri
, tlx_info
->pr_info
.proto
, t_strerror(t_errno
));
327 if (tlx_info
->pr_info
.v6only
) {
330 /* restrict to IPv6 communications only */
331 if (tlx_setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
,
332 sizeof (on
)) == -1) {
339 * Negotiate for the returning of the remote uid for loopback
340 * transports for RPC services. This needs to be done before the
341 * endpoint is bound using t_bind(), so that any requests to it
344 if ((rpc
!= NULL
) && (rpc
->is_loopback
))
345 svc_fd_negotiate_ucred(fd
);
348 * Bind the service's address to the endpoint and setup connection
349 * backlog. In the case of RPC services, we specify a NULL requested
350 * address and accept what we're given, storing the returned address
351 * for later RPC binding. In the case of non-RPC services we specify
352 * the service's associated address.
356 retaddr
= &(rpc
->netbuf
);
358 reqaddr
= &(tlx_info
->local_addr
);
359 netbuf
.buf
= (char *)&ss
;
360 netbuf
.maxlen
= sizeof (ss
);
364 /* ignored for conn/less services */
365 qlen
= inst
->config
->basic
->conn_backlog
;
367 if ((tlx_bind(fd
, reqaddr
, retaddr
, qlen
) == -1) ||
368 ((reqaddr
!= NULL
) && !netbufs_equal(reqaddr
, retaddr
))) {
369 error_msg(gettext("Failed to bind to the requested address "
370 "for instance %s, proto %s"), fmri
,
371 tlx_info
->pr_info
.proto
);
380 * Takes a connection request off 'fd' in the form of a t_call structure
381 * and returns a pointer to it.
382 * Returns NULL on failure, else pointer to t_call structure on success.
384 static struct t_call
*
385 get_new_conind(int fd
)
389 /* LINTED E_BAD_PTR_CAST_ALIGN */
390 if ((call
= (struct t_call
*)t_alloc(fd
, T_CALL
, T_ALL
)) == NULL
) {
391 error_msg("t_alloc: %s", t_strerror(t_errno
));
394 if (t_listen(fd
, call
) < 0) {
395 error_msg("t_listen: %s", t_strerror(t_errno
));
396 (void) t_free((char *)call
, T_CALL
);
403 /* Add 'call' to the connection indication queue 'queue'. */
405 queue_conind(uu_list_t
*queue
, struct t_call
*call
)
409 if ((ci
= malloc(sizeof (tlx_conn_ind_t
))) == NULL
) {
410 error_msg(strerror(errno
));
415 uu_list_node_init(ci
, &ci
->link
, conn_ind_pool
);
416 (void) uu_list_insert_after(queue
, NULL
, ci
);
422 * Remove and return a pointer to the first call on queue 'queue'. However,
423 * if the queue is empty returns NULL.
426 dequeue_conind(uu_list_t
*queue
)
429 tlx_conn_ind_t
*ci
= uu_list_first(queue
);
435 uu_list_remove(queue
, ci
);
442 * Handle a TLOOK notification received during a t_accept() call.
443 * Returns -1 on failure, else 0.
446 process_tlook(const char *fmri
, tlx_info_t
*tlx_info
)
449 int fd
= tlx_info
->pr_info
.listen_fd
;
451 switch (event
= t_look(fd
)) {
455 debug_msg("process_tlook: T_LISTEN event");
456 if ((call
= get_new_conind(fd
)) == NULL
)
458 if (queue_conind(tlx_info
->conn_ind_queue
, call
) == -1) {
459 error_msg(gettext("Failed to queue connection "
460 "indication for instance %s"), fmri
);
461 (void) t_free((char *)call
, T_CALL
);
468 * Note: In Solaris 2.X (SunOS 5.X) bundled
469 * connection-oriented transport drivers
470 * [ e.g /dev/tcp and /dev/ticots and
471 * /dev/ticotsord (tl)] we do not send disconnect
472 * indications to listening endpoints.
473 * So this will not be seen with endpoints on Solaris
474 * bundled transport devices. However, Streams TPI
475 * allows for this (broken?) behavior and so we account
476 * for it here because of the possibility of unbundled
477 * transport drivers causing this.
480 struct t_discon
*discon
;
482 debug_msg("process_tlook: T_DISCONNECT event");
485 if ((discon
= (struct t_discon
*)
486 t_alloc(fd
, T_DIS
, T_ALL
)) == NULL
) {
487 error_msg("t_alloc: %s", t_strerror(t_errno
));
490 if (t_rcvdis(fd
, discon
) < 0) {
491 error_msg("t_rcvdis: %s", t_strerror(t_errno
));
492 (void) t_free((char *)discon
, T_DIS
);
497 * Find any queued connection pending that matches this
498 * disconnect notice and remove from the pending queue.
500 cip
= uu_list_first(tlx_info
->conn_ind_queue
);
501 while ((cip
!= NULL
) &&
502 (cip
->call
->sequence
!= discon
->sequence
)) {
503 cip
= uu_list_next(tlx_info
->conn_ind_queue
, cip
);
505 if (cip
!= NULL
) { /* match found */
506 uu_list_remove(tlx_info
->conn_ind_queue
, cip
);
507 (void) t_free((char *)cip
->call
, T_CALL
);
511 (void) t_free((char *)discon
, T_DIS
);
515 error_msg("t_look: %s", t_strerror(t_errno
));
518 error_msg(gettext("do_tlook: unexpected t_look event: %d"),
527 * This call attempts to t_accept() an incoming/pending TLI connection.
528 * If it is thwarted by a TLOOK, it is deferred and whatever is on the
529 * file descriptor, removed after a t_look. (Incoming connect indications
530 * get queued for later processing and disconnect indications remove a
531 * a queued connection request if a match found).
532 * Returns -1 on failure, else 0.
535 tlx_accept(const char *fmri
, tlx_info_t
*tlx_info
,
536 struct sockaddr_storage
*remote_addr
)
538 tlx_conn_ind_t
*conind
;
541 int listen_fd
= tlx_info
->pr_info
.listen_fd
;
543 if ((fd
= t_open(tlx_info
->dev_name
, O_RDWR
, NULL
)) == -1) {
544 error_msg("t_open: %s", t_strerror(t_errno
));
548 if (tlx_info
->pr_info
.v6only
) {
551 /* restrict to IPv6 communications only */
552 if (tlx_setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
,
553 sizeof (on
)) == -1) {
559 if (t_bind(fd
, NULL
, NULL
) == -1) {
560 error_msg("t_bind: %s", t_strerror(t_errno
));
566 * Get the next connection indication - first try the pending
567 * queue, then, if none there, get a new one from the file descriptor.
569 if ((conind
= uu_list_first(tlx_info
->conn_ind_queue
)) != NULL
) {
570 debug_msg("taking con off queue");
572 } else if ((call
= get_new_conind(listen_fd
)) == NULL
) {
578 * Accept the connection indication on the newly created endpoint.
579 * If we fail, and it's the result of a tlook, queue the indication
580 * if it isn't already, and go and process the t_look.
582 if (t_accept(listen_fd
, fd
, call
) == -1) {
583 if (t_errno
== TLOOK
) {
584 if (uu_list_first(tlx_info
->conn_ind_queue
) == NULL
) {
586 * We are first one to have to defer accepting
587 * and start the pending connections list.
589 if (queue_conind(tlx_info
->conn_ind_queue
,
592 "Failed to queue connection "
593 "indication for instance %s"),
595 (void) t_free((char *)call
, T_CALL
);
599 (void) process_tlook(fmri
, tlx_info
);
600 } else { /* non-TLOOK accept failure */
601 error_msg("%s: %s", "t_accept failed",
602 t_strerror(t_errno
));
604 * If we were accepting a queued connection, dequeue
607 if (uu_list_first(tlx_info
->conn_ind_queue
) != NULL
)
608 (void) dequeue_conind(tlx_info
->conn_ind_queue
);
609 (void) t_free((char *)call
, T_CALL
);
616 /* Copy remote address into address parameter */
617 (void) memcpy(remote_addr
, call
->addr
.buf
,
618 MIN(call
->addr
.len
, sizeof (*remote_addr
)));
620 /* If we were accepting a queued connection, dequeue it. */
621 if (uu_list_first(tlx_info
->conn_ind_queue
) != NULL
)
622 (void) dequeue_conind(tlx_info
->conn_ind_queue
);
623 (void) t_free((char *)call
, T_CALL
);
628 /* protocol independent network fd close routine */
630 close_net_fd(instance_t
*inst
, int fd
)
632 if (inst
->config
->basic
->istlx
) {
640 * Consume some data from the given endpoint of the given wait-based instance.
643 consume_wait_data(instance_t
*inst
, int fd
)
646 char buf
[50]; /* same arbitrary size as old inetd */
648 if (inst
->config
->basic
->istlx
) {
649 (void) t_rcv(fd
, buf
, sizeof (buf
), &flag
);
651 (void) recv(fd
, buf
, sizeof (buf
), 0);