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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   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.
  12  *
  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]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  25  */
  26 
  27 /*      Copyright (c) 1988 AT&T */
  28 /*      All Rights Reserved   */
  29 
  30 /*
  31  * svc_generic.c, Server side for RPC.
  32  *
  33  */
  34 
  35 #include "mt.h"
  36 #include <stdlib.h>
  37 #include <sys/socket.h>
  38 #include <netinet/in.h>
  39 #include <netinet/tcp.h>
  40 #include <netinet/udp.h>
  41 #include <inttypes.h>
  42 #include "rpc_mt.h"
  43 #include <stdio.h>
  44 #include <rpc/rpc.h>
  45 #include <sys/types.h>
  46 #include <errno.h>
  47 #include <syslog.h>
  48 #include <rpc/nettype.h>
  49 #include <malloc.h>
  50 #include <string.h>
  51 #include <stropts.h>
  52 #include <tsol/label.h>
  53 #include <nfs/nfs.h>
  54 #include <nfs/nfs_acl.h>
  55 #include <rpcsvc/mount.h>
  56 #include <rpcsvc/nsm_addr.h>
  57 #include <rpcsvc/rquota.h>
  58 #include <rpcsvc/sm_inter.h>
  59 #include <rpcsvc/nlm_prot.h>
  60 
  61 extern int __svc_vc_setflag(SVCXPRT *, int);
  62 
  63 extern SVCXPRT *svc_dg_create_private(int, uint_t, uint_t);
  64 extern SVCXPRT *svc_vc_create_private(int, uint_t, uint_t);
  65 extern SVCXPRT *svc_fd_create_private(int, uint_t, uint_t);
  66 
  67 extern bool_t __svc_add_to_xlist(SVCXPRT_LIST **, SVCXPRT *, mutex_t *);
  68 extern void __svc_free_xlist(SVCXPRT_LIST **, mutex_t *);
  69 
  70 extern bool_t __rpc_try_doors(const char *, bool_t *);
  71 
  72 /*
  73  * The highest level interface for server creation.
  74  * It tries for all the nettokens in that particular class of token
  75  * and returns the number of handles it can create and/or find.
  76  *
  77  * It creates a link list of all the handles it could create.
  78  * If svc_create() is called multiple times, it uses the handle
  79  * created earlier instead of creating a new handle every time.
  80  */
  81 
  82 /* VARIABLES PROTECTED BY xprtlist_lock: xprtlist */
  83 
  84 SVCXPRT_LIST *_svc_xprtlist = NULL;
  85 extern mutex_t xprtlist_lock;
  86 
  87 static SVCXPRT * svc_tli_create_common(int, const struct netconfig *,
  88     const struct t_bind *, uint_t, uint_t, boolean_t);
  89 
  90 static SVCXPRT *svc_tp_create_bind(void (*dispatch)(),
  91     const rpcprog_t, const rpcvers_t,
  92     const struct netconfig *, const struct t_bind *);
  93 
  94 boolean_t
  95 is_multilevel(rpcprog_t prognum)
  96 {
  97         /* This is a list of identified multilevel service provider */
  98         if ((prognum == MOUNTPROG) || (prognum == NFS_PROGRAM) ||
  99             (prognum == NFS_ACL_PROGRAM) || (prognum == NLM_PROG) ||
 100             (prognum == NSM_ADDR_PROGRAM) || (prognum == RQUOTAPROG) ||
 101             (prognum == SM_PROG))
 102                 return (B_TRUE);
 103 
 104         return (B_FALSE);
 105 }
 106 
 107 void
 108 __svc_free_xprtlist(void)
 109 {
 110         __svc_free_xlist(&_svc_xprtlist, &xprtlist_lock);
 111 }
 112 
 113 int
 114 svc_create(void (*dispatch)(), const rpcprog_t prognum, const rpcvers_t versnum,
 115                                                         const char *nettype)
 116 {
 117         SVCXPRT_LIST *l;
 118         int num = 0;
 119         SVCXPRT *xprt;
 120         struct netconfig *nconf;
 121         void *handle;
 122         bool_t try_others;
 123 
 124         /*
 125          * Check if service should register over doors transport.
 126          */
 127         if (__rpc_try_doors(nettype, &try_others)) {
 128                 if (svc_door_create(dispatch, prognum, versnum, 0) == NULL)
 129                         (void) syslog(LOG_ERR,
 130                             "svc_create: could not register over doors");
 131                 else
 132                         num++;
 133         }
 134         if (!try_others)
 135                 return (num);
 136         if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
 137                 (void) syslog(LOG_ERR, "svc_create: unknown protocol");
 138                 return (0);
 139         }
 140         while (nconf = __rpc_getconf(handle)) {
 141                 (void) mutex_lock(&xprtlist_lock);
 142                 for (l = _svc_xprtlist; l; l = l->next) {
 143                         if (strcmp(l->xprt->xp_netid, nconf->nc_netid) == 0) {
 144                                 /* Found an old one, use it */
 145                                 (void) rpcb_unset(prognum, versnum, nconf);
 146                                 if (svc_reg(l->xprt, prognum, versnum,
 147                                     dispatch, nconf) == FALSE)
 148                                         (void) syslog(LOG_ERR, "svc_create: "
 149                                             "could not register prog %d vers "
 150                                             "%d on %s",
 151                                             prognum, versnum, nconf->nc_netid);
 152                                 else
 153                                         num++;
 154                                 break;
 155                         }
 156                 }
 157                 (void) mutex_unlock(&xprtlist_lock);
 158                 if (l == NULL) {
 159                         /* It was not found. Now create a new one */
 160                         xprt = svc_tp_create(dispatch, prognum, versnum, nconf);
 161                         if (xprt) {
 162                                 if (!__svc_add_to_xlist(&_svc_xprtlist, xprt,
 163                                     &xprtlist_lock)) {
 164                                         (void) syslog(LOG_ERR,
 165                                             "svc_create: no memory");
 166                                         return (0);
 167                                 }
 168                                 num++;
 169                         }
 170                 }
 171         }
 172         __rpc_endconf(handle);
 173         /*
 174          * In case of num == 0; the error messages are generated by the
 175          * underlying layers; and hence not needed here.
 176          */
 177         return (num);
 178 }
 179 
 180 /*
 181  * The high level interface to svc_tli_create().
 182  * It tries to create a server for "nconf" and registers the service
 183  * with the rpcbind.
 184  */
 185 SVCXPRT *
 186 svc_tp_create(void (*dispatch)(), const rpcprog_t prognum,
 187     const rpcvers_t versnum, const struct netconfig *nconf)
 188 {
 189         return (svc_tp_create_bind(dispatch, prognum, versnum, nconf, NULL));
 190 }
 191 
 192 /*
 193  * svc_tp_create_addr()
 194  * Variant of svc_tp_create() that allows specifying just the
 195  * the binding address, for convenience.
 196  */
 197 SVCXPRT *
 198 svc_tp_create_addr(void (*dispatch)(), const rpcprog_t prognum,
 199     const rpcvers_t versnum, const struct netconfig *nconf,
 200     const struct netbuf *addr)
 201 {
 202         struct t_bind bind;
 203         struct t_bind *bindp = NULL;
 204 
 205         if (addr != NULL) {
 206 
 207                 bind.addr = *addr;
 208                 if (!rpc_control(__RPC_SVC_LSTNBKLOG_GET, &bind.qlen)) {
 209                         syslog(LOG_ERR,
 210                             "svc_tp_create: can't get listen backlog");
 211                         return (NULL);
 212                 }
 213                 bindp = &bind;
 214         }
 215 
 216         /*
 217          * When bindp == NULL, this is the same as svc_tp_create().
 218          */
 219         return (svc_tp_create_bind(dispatch, prognum, versnum,
 220             nconf, bindp));
 221 }
 222 
 223 static SVCXPRT *
 224 svc_tp_create_bind(void (*dispatch)(), const rpcprog_t prognum,
 225     const rpcvers_t versnum, const struct netconfig *nconf,
 226     const struct t_bind *bindaddr)
 227 {
 228         SVCXPRT *xprt;
 229         boolean_t anon_mlp = B_FALSE;
 230 
 231         if (nconf == NULL) {
 232                 (void) syslog(LOG_ERR, "svc_tp_create: invalid netconfig "
 233                     "structure for prog %d vers %d", prognum, versnum);
 234                 return (NULL);
 235         }
 236 
 237         /* Some programs need to allocate MLP for multilevel services */
 238         if (is_system_labeled() && is_multilevel(prognum))
 239                 anon_mlp = B_TRUE;
 240         xprt = svc_tli_create_common(RPC_ANYFD, nconf, bindaddr, 0, 0,
 241             anon_mlp);
 242         if (xprt == NULL)
 243                 return (NULL);
 244 
 245         (void) rpcb_unset(prognum, versnum, (struct netconfig *)nconf);
 246         if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) {
 247                 (void) syslog(LOG_ERR,
 248                     "svc_tp_create: Could not register prog %d vers %d on %s",
 249                     prognum, versnum, nconf->nc_netid);
 250                 SVC_DESTROY(xprt);
 251                 return (NULL);
 252         }
 253         return (xprt);
 254 }
 255 
 256 SVCXPRT *
 257 svc_tli_create(const int fd, const struct netconfig *nconf,
 258     const struct t_bind *bindaddr, const uint_t sendsz, const uint_t recvsz)
 259 {
 260         return (svc_tli_create_common(fd, nconf, bindaddr, sendsz, recvsz, 0));
 261 }
 262 
 263 /*
 264  * If fd is RPC_ANYFD, then it opens a fd for the given transport
 265  * provider (nconf cannot be NULL then). If the t_state is T_UNBND and
 266  * bindaddr is NON-NULL, it performs a t_bind using the bindaddr. For
 267  * NULL bindadr and Connection oriented transports, the value of qlen
 268  * is set arbitrarily.
 269  *
 270  * If sendsz or recvsz are zero, their default values are chosen.
 271  */
 272 SVCXPRT *
 273 svc_tli_create_common(const int ofd, const struct netconfig *nconf,
 274         const struct t_bind *bindaddr, const uint_t sendsz,
 275         const uint_t recvsz, boolean_t mlp_flag)
 276 {
 277         SVCXPRT *xprt = NULL;           /* service handle */
 278         struct t_info tinfo;            /* transport info */
 279         struct t_bind *tres = NULL;     /* bind info */
 280         bool_t madefd = FALSE;          /* whether fd opened here  */
 281         int state;                      /* state of the transport provider */
 282         int fd = ofd;
 283 
 284         if (fd == RPC_ANYFD) {
 285                 if (nconf == NULL) {
 286                         (void) syslog(LOG_ERR,
 287                             "svc_tli_create: invalid netconfig");
 288                         return (NULL);
 289                 }
 290                 fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
 291                 if (fd == -1) {
 292                         char errorstr[100];
 293 
 294                         __tli_sys_strerror(errorstr, sizeof (errorstr),
 295                             t_errno, errno);
 296                         (void) syslog(LOG_ERR, "svc_tli_create: could not open "
 297                             "connection for %s: %s", nconf->nc_netid, errorstr);
 298                         return (NULL);
 299                 }
 300                 madefd = TRUE;
 301                 state = T_UNBND;
 302         } else {
 303                 /*
 304                  * It is an open descriptor. Sync it & get the transport info.
 305                  */
 306                 if ((state = t_sync(fd)) == -1) {
 307                         char errorstr[100];
 308 
 309                         __tli_sys_strerror(errorstr, sizeof (errorstr),
 310                             t_errno, errno);
 311                         (void) syslog(LOG_ERR,
 312                             "svc_tli_create: could not do t_sync: %s",
 313                             errorstr);
 314                         return (NULL);
 315                 }
 316                 if (t_getinfo(fd, &tinfo) == -1) {
 317                         char errorstr[100];
 318 
 319                         __tli_sys_strerror(errorstr, sizeof (errorstr),
 320                             t_errno, errno);
 321                         (void) syslog(LOG_ERR, "svc_tli_create: could not get "
 322                             "transport information: %s", errorstr);
 323                         return (NULL);
 324                 }
 325                 /* Enable options of returning the ip's for udp */
 326                 if (nconf) {
 327                         int ret = 0;
 328                         if (strcmp(nconf->nc_netid, "udp6") == 0) {
 329                                 ret = __rpc_tli_set_options(fd, IPPROTO_IPV6,
 330                                     IPV6_RECVPKTINFO, 1);
 331                                 if (ret < 0) {
 332                                         char errorstr[100];
 333 
 334                                         __tli_sys_strerror(errorstr,
 335                                             sizeof (errorstr), t_errno, errno);
 336                                         (void) syslog(LOG_ERR,
 337                                             "svc_tli_create: "
 338                                             "IPV6_RECVPKTINFO(1): %s",
 339                                             errorstr);
 340                                         return (NULL);
 341                                 }
 342                         } else if (strcmp(nconf->nc_netid, "udp") == 0) {
 343                                 ret = __rpc_tli_set_options(fd, IPPROTO_IP,
 344                                     IP_RECVDSTADDR, 1);
 345                                 if (ret < 0) {
 346                                         char errorstr[100];
 347 
 348                                         __tli_sys_strerror(errorstr,
 349                                             sizeof (errorstr), t_errno, errno);
 350                                         (void) syslog(LOG_ERR,
 351                                             "svc_tli_create: "
 352                                             "IP_RECVDSTADDR(1): %s", errorstr);
 353                                         return (NULL);
 354                                 }
 355                         }
 356                 }
 357         }
 358 
 359         /*
 360          * If the fd is unbound, try to bind it.
 361          * In any case, try to get its bound info in tres
 362          */
 363 /* LINTED pointer alignment */
 364         tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
 365         if (tres == NULL) {
 366                 (void) syslog(LOG_ERR, "svc_tli_create: No memory!");
 367                 goto freedata;
 368         }
 369 
 370         switch (state) {
 371         case T_UNBND:
 372                 /* If this is a labeled system, then ask for an MLP */
 373                 if (is_system_labeled() &&
 374                     (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
 375                     strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
 376                         (void) __rpc_tli_set_options(fd, SOL_SOCKET,
 377                             SO_RECVUCRED, 1);
 378                         if (mlp_flag)
 379                                 (void) __rpc_tli_set_options(fd, SOL_SOCKET,
 380                                     SO_ANON_MLP, 1);
 381                 }
 382 
 383                 if (bindaddr) {
 384                         /*
 385                          * Services that specify a bind address typically
 386                          * use a fixed service (IP port) so we need to set
 387                          * SO_REUSEADDR to prevent bind errors on restart.
 388                          */
 389                         if (bindaddr->addr.len != 0)
 390                                 (void) __rpc_tli_set_options(fd, SOL_SOCKET,
 391                                     SO_REUSEADDR, 1);
 392                         if (t_bind(fd, (struct t_bind *)bindaddr, tres) == -1) {
 393                                 char errorstr[100];
 394 
 395                                 __tli_sys_strerror(errorstr, sizeof (errorstr),
 396                                     t_errno, errno);
 397                                 (void) syslog(LOG_ERR,
 398                                     "svc_tli_create: could not bind: %s",
 399                                     errorstr);
 400                                 goto freedata;
 401                         }
 402                         /*
 403                          * Should compare the addresses only if addr.len
 404                          * was non-zero
 405                          */
 406                         if (bindaddr->addr.len &&
 407                             (memcmp(bindaddr->addr.buf, tres->addr.buf,
 408                             (int)tres->addr.len) != 0)) {
 409                                 (void) syslog(LOG_ERR, "svc_tli_create: could "
 410                                     "not bind to requested address: address "
 411                                     "mismatch");
 412                                 goto freedata;
 413                         }
 414                 } else {
 415                         if (rpc_control(__RPC_SVC_LSTNBKLOG_GET, &tres->qlen)
 416                             == FALSE) {
 417                                 syslog(LOG_ERR,
 418                                     "svc_tli_create: can't get listen backlog");
 419                                 goto freedata;
 420                         }
 421                         tres->addr.len = 0;
 422                         if (t_bind(fd, tres, tres) == -1) {
 423                                 char errorstr[100];
 424 
 425                                 __tli_sys_strerror(errorstr, sizeof (errorstr),
 426                                     t_errno, errno);
 427                                 (void) syslog(LOG_ERR,
 428                                     "svc_tli_create: could not bind: %s",
 429                                     errorstr);
 430                                 goto freedata;
 431                         }
 432                 }
 433 
 434                 /*
 435                  * If requested, set SO_EXCLBIND on each binding.
 436                  *
 437                  * SO_EXCLBIND has the following properties
 438                  *    - an fd bound to port P via IPv4 will prevent an IPv6
 439                  *    bind to port P (and vice versa)
 440                  *    - an fd bound to a wildcard IP address for port P will
 441                  *    prevent a more specific IP address bind to port P
 442                  *    (see {tcp,udp}.c for details)
 443                  *
 444                  * We use the latter property to prevent hijacking of RPC
 445                  * services that reside at non-privileged ports.
 446                  *
 447                  * When the bind address is not specified, each bind gets a
 448                  * new port number, and (for IP transports) we should set
 449                  * the exclusive flag after every IP bind.  That's the
 450                  * strcmp nc_proto part of the expression below.
 451                  *
 452                  * When the bind address IS specified, we need to set the
 453                  * exclusive flag only after we've bound both IPv6+IPv4,
 454                  * or the IPv4 bind will fail.  Setting the exclusive flag
 455                  * after the "tcp" or "udp" transport bind does that.
 456                  * That's the strcmp nc_netid part below.
 457                  */
 458                 if (nconf != NULL && ((bindaddr == NULL &&
 459                     (strcmp(nconf->nc_proto, NC_TCP) == 0 ||
 460                     strcmp(nconf->nc_proto, NC_UDP) == 0)) ||
 461                     (strcmp(nconf->nc_netid, "tcp") == 0 ||
 462                     strcmp(nconf->nc_netid, "udp") == 0))) {
 463                         bool_t exclbind = FALSE;
 464                         (void) rpc_control(__RPC_SVC_EXCLBIND_GET, &exclbind);
 465                         if (exclbind &&
 466                             __rpc_tli_set_options(fd, SOL_SOCKET,
 467                             SO_EXCLBIND, 1) < 0) {
 468                                 syslog(LOG_ERR,
 469                             "svc_tli_create: can't set EXCLBIND [netid='%s']",
 470                                     nconf->nc_netid);
 471                                 goto freedata;
 472                         }
 473                 }
 474 
 475                 /* Enable options of returning the ip's for udp */
 476                 if (nconf) {
 477                         int ret = 0;
 478                         if (strcmp(nconf->nc_netid, "udp6") == 0) {
 479                                 ret = __rpc_tli_set_options(fd, IPPROTO_IPV6,
 480                                     IPV6_RECVPKTINFO, 1);
 481                                 if (ret < 0) {
 482                                         char errorstr[100];
 483 
 484                                         __tli_sys_strerror(errorstr,
 485                                             sizeof (errorstr), t_errno, errno);
 486                                         (void) syslog(LOG_ERR,
 487                                             "svc_tli_create: "
 488                                             "IPV6_RECVPKTINFO(2): %s",
 489                                             errorstr);
 490                                         goto freedata;
 491                                 }
 492                         } else if (strcmp(nconf->nc_netid, "udp") == 0) {
 493                                 ret = __rpc_tli_set_options(fd, IPPROTO_IP,
 494                                     IP_RECVDSTADDR, 1);
 495                                 if (ret < 0) {
 496                                         char errorstr[100];
 497 
 498                                         __tli_sys_strerror(errorstr,
 499                                             sizeof (errorstr), t_errno, errno);
 500                                         (void) syslog(LOG_ERR,
 501                                             "svc_tli_create: "
 502                                             "IP_RECVDSTADDR(2): %s", errorstr);
 503                                         goto freedata;
 504                                 }
 505                         }
 506                 }
 507                 break;
 508 
 509         case T_IDLE:
 510                 if (bindaddr) {
 511                         /* Copy the entire stuff in tres */
 512                         if (tres->addr.maxlen < bindaddr->addr.len) {
 513                                 (void) syslog(LOG_ERR,
 514                                 "svc_tli_create: illegal netbuf length");
 515                                 goto freedata;
 516                         }
 517                         tres->addr.len = bindaddr->addr.len;
 518                         (void) memcpy(tres->addr.buf, bindaddr->addr.buf,
 519                             (int)tres->addr.len);
 520                 } else
 521                         if (t_getname(fd, &(tres->addr), LOCALNAME) == -1)
 522                                 tres->addr.len = 0;
 523                 break;
 524         case T_INREL:
 525                 (void) t_rcvrel(fd);
 526                 (void) t_sndrel(fd);
 527                 (void) syslog(LOG_ERR, "svc_tli_create: other side wants to "
 528                     "release connection");
 529                 goto freedata;
 530 
 531         case T_INCON:
 532                 /* Do nothing here. Assume this is handled in rendezvous */
 533                 break;
 534         case T_DATAXFER:
 535                 /*
 536                  * This takes care of the case where a fd
 537                  * is passed on which a connection has already
 538                  * been accepted.
 539                  */
 540                 if (t_getname(fd, &(tres->addr), LOCALNAME) == -1)
 541                         tres->addr.len = 0;
 542                 break;
 543         default:
 544                 (void) syslog(LOG_ERR,
 545                     "svc_tli_create: connection in a wierd state (%d)", state);
 546                 goto freedata;
 547         }
 548 
 549         /*
 550          * call transport specific function.
 551          */
 552         switch (tinfo.servtype) {
 553                 case T_COTS_ORD:
 554                 case T_COTS:
 555                         if (state == T_DATAXFER)
 556                                 xprt = svc_fd_create_private(fd, sendsz,
 557                                     recvsz);
 558                         else
 559                                 xprt = svc_vc_create_private(fd, sendsz,
 560                                     recvsz);
 561                         if (!nconf || !xprt)
 562                                 break;
 563                         if ((tinfo.servtype == T_COTS_ORD) &&
 564                             (state != T_DATAXFER) &&
 565                             (strcmp(nconf->nc_protofmly, "inet") == 0))
 566                                 (void) __svc_vc_setflag(xprt, TRUE);
 567                         break;
 568                 case T_CLTS:
 569                         xprt = svc_dg_create_private(fd, sendsz, recvsz);
 570                         break;
 571                 default:
 572                         (void) syslog(LOG_ERR,
 573                             "svc_tli_create: bad service type");
 574                         goto freedata;
 575         }
 576         if (xprt == NULL)
 577                 /*
 578                  * The error messages here are spitted out by the lower layers:
 579                  * svc_vc_create(), svc_fd_create() and svc_dg_create().
 580                  */
 581                 goto freedata;
 582 
 583         /* fill in the other xprt information */
 584 
 585         /* Assign the local bind address */
 586         xprt->xp_ltaddr = tres->addr;
 587         /* Fill in type of service */
 588         xprt->xp_type = tinfo.servtype;
 589         tres->addr.buf = NULL;
 590         (void) t_free((char *)tres, T_BIND);
 591         tres = NULL;
 592 
 593         xprt->xp_rtaddr.len = 0;
 594         xprt->xp_rtaddr.maxlen = __rpc_get_a_size(tinfo.addr);
 595 
 596         /* Allocate space for the remote bind info */
 597         if ((xprt->xp_rtaddr.buf = malloc(xprt->xp_rtaddr.maxlen)) == NULL) {
 598                 (void) syslog(LOG_ERR, "svc_tli_create: No memory!");
 599                 goto freedata;
 600         }
 601 
 602         if (nconf) {
 603                 xprt->xp_netid = strdup(nconf->nc_netid);
 604                 if (xprt->xp_netid == NULL) {
 605                         if (xprt->xp_rtaddr.buf)
 606                                 free(xprt->xp_rtaddr.buf);
 607                         syslog(LOG_ERR, "svc_tli_create: strdup failed!");
 608                         goto freedata;
 609                 }
 610                 xprt->xp_tp = strdup(nconf->nc_device);
 611                 if (xprt->xp_tp == NULL) {
 612                         if (xprt->xp_rtaddr.buf)
 613                                 free(xprt->xp_rtaddr.buf);
 614                         if (xprt->xp_netid)
 615                                 free(xprt->xp_netid);
 616                         syslog(LOG_ERR, "svc_tli_create: strdup failed!");
 617                         goto freedata;
 618                 }
 619         }
 620 
 621 /*
 622  *      if (madefd && (tinfo.servtype == T_CLTS))
 623  *              (void) ioctl(fd, I_POP, NULL);
 624  */
 625         xprt_register(xprt);
 626         return (xprt);
 627 
 628 freedata:
 629         if (madefd)
 630                 (void) t_close(fd);
 631         if (tres)
 632                 (void) t_free((char *)tres, T_BIND);
 633         if (xprt) {
 634                 if (!madefd) /* so that svc_destroy doesnt close fd */
 635                         xprt->xp_fd = RPC_ANYFD;
 636                 SVC_DESTROY(xprt);
 637         }
 638         return (NULL);
 639 }