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 (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
  27 /*        All Rights Reserved   */
  28 
  29 /*
  30  * Portions of this source code were derived from Berkeley 4.3 BSD
  31  * under license from the Regents of the University of California.
  32  */
  33 
  34 #include <sys/types.h>
  35 #include <sys/socket.h>
  36 #include <sys/stat.h>
  37 #include <netinet/in.h>
  38 #include <errno.h>
  39 #include <unistd.h>
  40 #include <stdlib.h>
  41 #include <string.h>
  42 
  43 /*
  44  * XXX The functions in this file are only needed to support transport
  45  * providers that have not yet been converted to use /etc/sock2path.d.
  46  * Once all transport providers have been converted this file can be
  47  * removed.
  48  */
  49 
  50 static struct netconfig *_s_match_netconf(int family, int type, int proto,
  51                 void **nethandle);
  52 
  53 /*
  54  * The following two string arrays map a number as specified
  55  * by a user of sockets, to the string as would be returned
  56  * by a call to getnetconfig().
  57  *
  58  * They are used by _s_match_netconf();
  59  *
  60  * proto_sw contains protocol entries for which there is a corresponding
  61  * /dev device. All others would presumably use raw IP and download the
  62  * desired protocol.
  63  */
  64 static char *proto_sw[] = {
  65         "",
  66         "icmp",         /* 1 = ICMP */
  67         "",
  68         "",
  69         "",
  70         "",
  71         "tcp",          /* 6 = TCP */
  72         "",
  73         "",
  74         "",
  75         "",
  76         "",
  77         "",
  78         "",
  79         "",
  80         "",
  81         "",
  82         "udp",          /* 17 = UDP */
  83 };
  84 
  85 static char *family_sw[] = {
  86         "-",            /* 0 = AF_UNSPEC */
  87         "loopback",     /* 1 = AF_UNIX */
  88         "inet",         /* 2 = AF_INET */
  89         "implink",      /* 3 = AF_IMPLINK */
  90         "pup",          /* 4 = AF_PUP */
  91         "chaos",        /* 5 = AF_CHAOS */
  92         "ns",           /* 6 = AF_NS */
  93         "nbs",          /* 7 = AF_NBS */
  94         "ecma",         /* 8 = AF_ECMA */
  95         "datakit",      /* 9 = AF_DATAKIT */
  96         "ccitt",        /* 10 = AF_CCITT */
  97         "sna",          /* 11 = AF_SNA */
  98         "decnet",       /* 12 = AF_DECnet */
  99         "dli",          /* 13 = AF_DLI */
 100         "lat",          /* 14 = AF_LAT */
 101         "hylink",       /* 15 = AF_HYLINK */
 102         "appletalk",    /* 16 = AF_APPLETALK */
 103         "nit",          /* 17 = AF_NIT */
 104         "ieee802",      /* 18 = AF_802 */
 105         "osi",          /* 19 = AF_OSI */
 106         "x25",          /* 20 = AF_X25 */
 107         "osinet",       /* 21 = AF_OSINET */
 108         "gosip",        /* 22 = AF_GOSIP */
 109         "ipx",          /* 23 = AF_IPX */
 110         "route",        /* 24 = AF_ROUTE */
 111         "link",         /* 25 = AF_LINK */
 112         "inet6",        /* 26 = AF_INET6 */
 113         "key",          /* 27 = AF_KEY */
 114 };
 115 
 116 /*
 117  * Lookup family/type/protocol in /etc/netconfig.
 118  * Returns the pathname and a prototype value (to be passed into SO_PROTOTYPE)
 119  * The path is malloc'ed and has to be freed by the caller.
 120  */
 121 int
 122 _s_netconfig_path(int family, int type, int protocol,
 123         char **pathp, int *prototype)
 124 {
 125         struct netconfig        *net;
 126         void                            *nethandle;
 127         struct stat                     stats;
 128 
 129         net = _s_match_netconf(family, type, protocol, &nethandle);
 130         if (net == NULL)
 131                 return (-1);
 132 
 133         if (strcmp(net->nc_proto, NC_NOPROTO) != 0)
 134                 *prototype = 0;
 135         else
 136                 *prototype = protocol;
 137 
 138 retry:
 139         if (stat(net->nc_device, &stats) < 0) {
 140                 switch (errno) {
 141                 case EINTR:
 142                         goto retry;
 143 
 144                 case ENOENT:
 145                 case ENOLINK:
 146                 case ELOOP:
 147                 case EMULTIHOP:
 148                 case ENOTDIR:
 149                         errno = EPFNOSUPPORT;
 150                         break;
 151                 }
 152                 endnetconfig(nethandle); /* finished with netconfig struct */
 153                 return (-1);
 154         }
 155         if (!S_ISCHR(stats.st_mode)) {
 156                 errno = EPFNOSUPPORT;
 157                 endnetconfig(nethandle); /* finished with netconfig struct */
 158                 return (-1);
 159         }
 160         *pathp = malloc(strlen(net->nc_device) + 1);
 161         if (*pathp == NULL) {
 162                 endnetconfig(nethandle);
 163                 errno = ENOMEM;
 164                 return (-1);
 165         }
 166         (void) strcpy(*pathp, net->nc_device);
 167         endnetconfig(nethandle); /* finished with netconfig struct */
 168         return (0);
 169 }
 170 
 171 /*
 172  * Match config entry for protocol
 173  * requested.
 174  */
 175 static struct netconfig *
 176 _s_match_netconf(int family, int type, int proto, void **nethandle)
 177 {
 178         struct netconfig        *net;
 179         struct netconfig        *maybe;
 180         char                    *oproto;
 181 
 182         if (family < 0 ||
 183             family >= (int)sizeof (family_sw) / (int)sizeof (char *) ||
 184             proto < 0 || proto >= IPPROTO_MAX)  {
 185                 errno = EPROTONOSUPPORT;
 186                 return (NULL);
 187         }
 188         if (proto) {
 189                 if (proto >= (int)sizeof (proto_sw) / (int)sizeof (char *))
 190                         oproto = "";
 191                 else
 192                         oproto = proto_sw[proto];
 193         }
 194 
 195         /*
 196          * Loop through each entry in netconfig
 197          * until one matches or we reach the end.
 198          */
 199         if ((*nethandle = setnetconfig()) == NULL) {
 200                 return (NULL);
 201         }
 202 
 203         maybe = NULL;
 204         while ((net = getnetconfig(*nethandle)) != NULL) {
 205                 /*
 206                  * We make a copy of net->nc_semantics rather than modifying
 207                  * it in place because the network selection code shares the
 208                  * structures returned by getnetconfig() among all its callers.
 209                  * See bug #1160886 for more details.
 210                  */
 211                 unsigned int semantics = net->nc_semantics;
 212 
 213                 if (semantics == NC_TPI_COTS_ORD)
 214                         semantics = NC_TPI_COTS;
 215                 if (proto) {
 216                         if (strcmp(net->nc_protofmly, family_sw[family]) == 0 &&
 217                             semantics == type &&
 218                             strcmp(net->nc_proto, oproto) == 0)
 219                                 break;
 220 
 221                         if (strcmp(net->nc_protofmly, family_sw[family]) == 0 &&
 222                             type == SOCK_RAW &&
 223                             semantics == SOCK_RAW &&
 224                             strcmp(net->nc_proto, NC_NOPROTO) == 0 &&
 225                             maybe == NULL)
 226                                 maybe = net;    /* in case no exact match */
 227 
 228                         continue;
 229                 } else  {
 230                         if (strcmp(net->nc_protofmly, family_sw[family]) == 0 &&
 231                             semantics == type) {
 232                                 break;
 233                         }
 234                 }
 235         }
 236         if (net == NULL && maybe)
 237                 net = maybe;
 238 
 239         if (net == NULL) {
 240                 endnetconfig(*nethandle);
 241                 errno = EPROTONOSUPPORT;
 242                 return (NULL);
 243         }
 244 
 245         return (net);
 246 }