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 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * BrandZ lx name services translation library.
31 *
32 * This library is specified as the default name services translation
33 * library in a custom netconfig(4) file that is only used when running
34 * native solaris processes in a Linux branded zone.
35 *
36 * What this means it that when a native solaris process runs in a
37 * Linux branded zone and issues a name service request to libnsl.so
38 * (either directly or indirectly via any libraries the program may
39 * be linked against) libnsl.so will dlopen(3c) this library and call
40 * into it to service these requests.
41 *
42 * This library is in turn linked against lx_thunk.so and will attempt
43 * to call interfaces in lx_thunk.so to resolve these requests. The
44 * functions that are called in lx_thunk.so are designed to have the
45 * same signature and behavior as the existing solaris name service
46 * interfaces. The name services interfaces we call are:
47 *
48 * Native Interface -> lx_thunk.so Interface
49 * ---------------- -> ---------------------
50 * gethostbyname_r -> lxt_gethostbyname_r
51 * gethostbyaddr_r -> lxt_gethostbyaddr_r
52 * getservbyname_r -> lxt_getservbyname_r
53 * getservbyport_r -> lxt_getservbyport_r
54 *
55 * This library also uses one additional interface from lx_thunk.so:
56 * lxt_debug
57 * Information debugging messages are sent to lx_thunk.so via this
58 * interface and that library can decided if it wants to drop the
59 * messages or output them somewhere.
60 */
61
62 #include <assert.h>
63 #include <dlfcn.h>
64 #include <errno.h>
65 #include <fcntl.h>
66 #include <netdb.h>
67 #include <netdir.h>
68 #include <nss_dbdefs.h>
69 #include <rpc/clnt.h>
70 #include <stdarg.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <strings.h>
75 #include <sys/mman.h>
76 #include <sys/stat.h>
77 #include <sys/types.h>
78 #include <sys/varargs.h>
79 #include <sys/wait.h>
80 #include <thread.h>
81 #include <tiuser.h>
82 #include <unistd.h>
83 #include <sys/lx_thunk.h>
84
85
86 /*
87 * Private nametoaddr library interfaces.
88 */
89 static int
90 netconfig_is_ipv4(struct netconfig *config)
91 {
92 int i;
93 /*
94 * If we look at the rpc services registered on a Linux system
95 * (this can be done via rpcinfo(1M)) for both on the loopback
96 * interface and on any remote interfaces we only see services
97 * registered for tcp and udp. So here we'll limit our support
98 * to these transports.
99 */
100 char *ipv4_netids[] = {
101 "tcp",
102 "udp",
103 NULL
104 };
105
106 for (i = 0; ipv4_netids[i] != NULL; i++) {
107 if (strcmp(ipv4_netids[i], config->nc_netid) == 0)
108 return (1);
109 }
110 return (0);
111 }
112
113 /*
114 * Public nametoaddr library interfaces.
115 *
116 * These are the functional entry points that libnsl will lookup (via
117 * the symbol names) when it loads this nametoaddr translation library.
118 */
119
120 /*
121 * _netdir_getbyname() returns all of the addresses for
122 * a specified host and service.
123 */
124 struct nd_addrlist *
125 _netdir_getbyname(struct netconfig *netconfigp,
126 struct nd_hostserv *nd_hostservp)
127 {
128 struct nd_addrlist *rp = NULL;
129 struct netbuf *nbp = NULL;
130 struct sockaddr_in *sap = NULL;
131 struct hostent n2h_result;
132 struct servent n2s_result;
133 char *n2h_buf = NULL, *n2s_buf = NULL;
134 int h_errno, i, host_self = 0, r_count;
135 int n2h_count = 0, n2s_count = 0;
136
137 lxt_debug("_netdir_getbyname: request recieved\n");
138
139 /* Make sure this is an ipv4 request. */
140 if (!netconfig_is_ipv4(netconfigp)) {
141 _nderror = ND_BADARG;
142 goto fail;
143 }
144
145 /* Allocate memory for the queries. */
146 if (((n2h_buf = malloc(NSS_BUFLEN_HOSTS)) == NULL) ||
147 ((n2s_buf = malloc(NSS_BUFLEN_SERVICES)) == NULL))
148 goto malloc_fail;
149
150 /* Check if the host name specified is HOST_SELF. */
151 if (strcmp(nd_hostservp->h_host, HOST_SELF) == 0)
152 host_self = 1;
153
154 /*
155 * If the hostname specified is HOST_SELF, the we're just
156 * just doing a service lookup so don't bother with trying
157 * to lookup the host name.
158 */
159 if (!host_self) {
160 /* Resolve the hostname. */
161 lxt_debug("_netdir_getbyname: "
162 "resolving host name: %s\n", nd_hostservp->h_host);
163 if (lxt_gethostbyname_r(nd_hostservp->h_host, &n2h_result,
164 n2h_buf, NSS_BUFLEN_HOSTS, &h_errno) == NULL) {
165 if (errno == ERANGE) {
166 _nderror = ND_SYSTEM;
167 } else if (h_errno == HOST_NOT_FOUND) {
168 _nderror = ND_NOHOST;
169 } else if (h_errno == TRY_AGAIN) {
170 _nderror = ND_TRY_AGAIN;
171 } else if (h_errno == NO_RECOVERY) {
172 _nderror = ND_NO_RECOVERY;
173 } else if (h_errno == NO_DATA) {
174 _nderror = ND_NO_DATA;
175 } else {
176 _nderror = ND_SYSTEM;
177 }
178 goto fail;
179 }
180 while (n2h_result.h_addr_list[n2h_count++] != NULL);
181 n2h_count--;
182 }
183
184 if (nd_hostservp->h_serv != NULL) {
185 /* Resolve the service name */
186 lxt_debug("_netdir_getbyname: "
187 "resolving service name: %s\n", nd_hostservp->h_serv);
188 if (lxt_getservbyname_r(nd_hostservp->h_serv,
189 netconfigp->nc_proto, &n2s_result,
190 n2s_buf, NSS_BUFLEN_SERVICES) == NULL) {
191 _nderror = ND_SYSTEM;
192 goto fail;
193 }
194 n2s_count = 1;
195 }
196
197 /* Make sure we got some results. */
198 if ((n2h_count + n2s_count) == 0) {
199 lxt_debug("_netdir_getbyname: no results!\n");
200 goto exit;
201 }
202 r_count = (n2h_count != 0) ? n2h_count : 1;
203
204 /*
205 * Allocate the return buffers. These buffers will be free'd
206 * by libnsl`netdir_free(), so we need to allocate them in the
207 * way that libnsl`netdir_free() expects.
208 */
209 if (((rp = calloc(1, sizeof (struct nd_addrlist))) == NULL) ||
210 ((nbp = calloc(1, sizeof (struct netbuf) * r_count)) == NULL) ||
211 ((sap = calloc(1, sizeof (struct sockaddr_in) * r_count)) == NULL))
212 goto malloc_fail;
213
214 /* Initialize the structures we're going to return. */
215 rp->n_cnt = r_count;
216 rp->n_addrs = nbp;
217 for (i = 0; i < r_count; i++) {
218
219 /* Initialize the netbuf. */
220 nbp[i].maxlen = nbp[i].len = sizeof (struct sockaddr_in);
221 nbp[i].buf = (char *)&sap[i];
222
223 /* Initialize the sockaddr_in. */
224 sap[i].sin_family = AF_INET;
225
226 /* If we looked up any host address copy them out. */
227 if (!host_self)
228 bcopy(n2h_result.h_addr_list[i], &sap[i].sin_addr,
229 sizeof (sap[i].sin_addr));
230
231 /* If we looked up any service ports copy them out. */
232 if (nd_hostservp->h_serv != NULL)
233 sap[i].sin_port = n2s_result.s_port;
234 }
235
236 /* We're finally done. */
237 lxt_debug("_netdir_getbyname: success\n");
238 return (rp);
239
240 malloc_fail:
241 _nderror = ND_NOMEM;
242
243 fail:
244 lxt_debug("_netdir_getbyname: failed!\n");
245
246 exit:
247 if (n2h_buf == NULL)
248 free(n2h_buf);
249 if (n2s_buf == NULL)
250 free(n2s_buf);
251 if (rp == NULL)
252 free(rp);
253 if (nbp == NULL)
254 free(nbp);
255 if (sap == NULL)
256 free(sap);
257 return (NULL);
258 }
259
260 /*
261 * _netdir_getbyaddr() takes an address (hopefully obtained from
262 * someone doing a _netdir_getbyname()) and returns all hosts with
263 * that address.
264 */
265 struct nd_hostservlist *
266 /*ARGSUSED*/
267 _netdir_getbyaddr(struct netconfig *netconfigp, struct netbuf *nbp)
268 {
269 struct nd_hostservlist *rp = NULL;
270 struct nd_hostserv *hsp = NULL;
271 struct sockaddr_in *sap;
272 struct servent p2s_result;
273 struct hostent a2h_result;
274 char *a2h_buf = NULL, *p2s_buf = NULL;
275 int h_errno, i;
276 int r_count = 0;
277 int a2h_count = 0, p2s_count = 0;
278
279 lxt_debug("_netdir_getbyaddr: request recieved\n");
280
281 /* Make sure this is an ipv4 request. */
282 if (!netconfig_is_ipv4(netconfigp)) {
283 _nderror = ND_BADARG;
284 goto fail;
285 }
286
287 /*
288 * Make sure the netbuf contains one struct sockaddr_in of
289 * type AF_INET.
290 */
291 if ((nbp->len != sizeof (struct sockaddr_in)) ||
292 (nbp->len < nbp->maxlen)) {
293 _nderror = ND_BADARG;
294 goto fail;
295 }
296 /*LINTED*/
297 sap = (struct sockaddr_in *)nbp->buf;
298 if (sap->sin_family != AF_INET) {
299 _nderror = ND_BADARG;
300 goto fail;
301 }
302
303 /* Allocate memory for the queries. */
304 if (((a2h_buf = malloc(NSS_BUFLEN_HOSTS)) == NULL) ||
305 ((p2s_buf = malloc(NSS_BUFLEN_SERVICES)) == NULL))
306 goto malloc_fail;
307
308 if (sap->sin_addr.s_addr != INADDR_ANY) {
309 lxt_debug("_netdir_getbyaddr: "
310 "resolving host address: 0x%x\n", sap->sin_addr.s_addr);
311 if (lxt_gethostbyaddr_r((char *)&sap->sin_addr.s_addr,
312 sizeof (sap->sin_addr.s_addr), AF_INET,
313 &a2h_result, a2h_buf, NSS_BUFLEN_HOSTS,
314 &h_errno) == NULL) {
315 if (errno == ERANGE) {
316 _nderror = ND_SYSTEM;
317 } else if (h_errno == HOST_NOT_FOUND) {
318 _nderror = ND_NOHOST;
319 } else if (h_errno == TRY_AGAIN) {
320 _nderror = ND_TRY_AGAIN;
321 } else if (h_errno == NO_RECOVERY) {
322 _nderror = ND_NO_RECOVERY;
323 } else if (h_errno == NO_DATA) {
324 _nderror = ND_NO_DATA;
325 } else {
326 _nderror = ND_SYSTEM;
327 }
328 goto fail;
329 }
330 while (a2h_result.h_aliases[a2h_count++] != NULL);
331 /*
332 * We need to count a2h_result.h_name as a valid name for
333 * for the address we just looked up. Of course a2h_count
334 * is actually over estimated by one, so instead of
335 * decrementing it here we'll just leave it as it to
336 * account for a2h_result.h_name.
337 */
338 }
339
340 if (sap->sin_port != 0) {
341 lxt_debug("_netdir_getbyaddr: "
342 "resolving service port: 0x%x\n", sap->sin_port);
343 if (lxt_getservbyport_r(sap->sin_port,
344 netconfigp->nc_proto, &p2s_result,
345 p2s_buf, NSS_BUFLEN_SERVICES) == NULL) {
346 _nderror = ND_SYSTEM;
347 goto fail;
348 }
349 p2s_count = 1;
350 }
351
352 /* Make sure we got some results. */
353 if ((a2h_count + p2s_count) == 0) {
354 lxt_debug("_netdir_getbyaddr: no results!\n");
355 goto exit;
356 }
357 r_count = (a2h_count != 0) ? a2h_count : 1;
358
359 /*
360 * Allocate the return buffers. These buffers will be free'd
361 * by libnsl`netdir_free(), so we need to allocate them in the
362 * way that libnsl`netdir_free() expects.
363 */
364 if (((rp = calloc(1, sizeof (struct nd_hostservlist))) == NULL) ||
365 ((hsp = calloc(1, sizeof (struct nd_hostserv) * r_count)) == NULL))
366 goto malloc_fail;
367
368 lxt_debug("_netdir_getbyaddr: hahaha0 - %d\n", r_count);
369 rp->h_cnt = r_count;
370 rp->h_hostservs = hsp;
371 for (i = 0; i < r_count; i++) {
372 /* If we looked up any host names copy them out. */
373 lxt_debug("_netdir_getbyaddr: hahaha1 - %d\n", r_count);
374 if ((a2h_count > 0) && (i == 0) &&
375 ((hsp[i].h_host = strdup(a2h_result.h_name)) == NULL))
376 goto malloc_fail;
377
378 if ((a2h_count > 0) && (i > 0) &&
379 ((hsp[i].h_host =
380 strdup(a2h_result.h_aliases[i - 1])) == NULL))
381 goto malloc_fail;
382
383 lxt_debug("_netdir_getbyaddr: hahaha2 - %d\n", r_count);
384 /* If we looked up any service names copy them out. */
385 if ((p2s_count > 0) &&
386 ((hsp[i].h_serv = strdup(p2s_result.s_name)) == NULL))
387 goto malloc_fail;
388 lxt_debug("_netdir_getbyaddr: hahaha3 - %d\n", r_count);
389 }
390
391 /* We're finally done. */
392 lxt_debug("_netdir_getbyaddr: success\n");
393 return (rp);
394
395 malloc_fail:
396 _nderror = ND_NOMEM;
397
398 fail:
399 lxt_debug("_netdir_getbyaddr: failed!\n");
400
401 exit:
402 if (a2h_buf == NULL)
403 free(a2h_buf);
404 if (p2s_buf == NULL)
405 free(p2s_buf);
406 if (rp == NULL)
407 free(rp);
408 if (hsp != NULL) {
409 for (i = 0; i < r_count; i++) {
410 if (hsp[i].h_host != NULL)
411 free(hsp[i].h_host);
412 if (hsp[i].h_serv != NULL)
413 free(hsp[i].h_serv);
414 }
415 free(hsp);
416 }
417 return (NULL);
418 }
419
420 char *
421 /* ARGSUSED */
422 _taddr2uaddr(struct netconfig *netconfigp, struct netbuf *nbp)
423 {
424 extern char *inet_ntoa_r();
425
426 struct sockaddr_in *sa;
427 char tmp[RPC_INET6_MAXUADDRSIZE];
428 unsigned short myport;
429
430 if (netconfigp == NULL || nbp == NULL || nbp->buf == NULL) {
431 _nderror = ND_BADARG;
432 return (NULL);
433 }
434
435 if (strcmp(netconfigp->nc_protofmly, NC_INET) != 0) {
436 /* we only support inet address translation */
437 assert(0);
438 _nderror = ND_SYSTEM;
439 return (NULL);
440 }
441
442 /* LINTED pointer cast */
443 sa = (struct sockaddr_in *)(nbp->buf);
444 myport = ntohs(sa->sin_port);
445 (void) inet_ntoa_r(sa->sin_addr, tmp);
446
447 (void) sprintf(tmp + strlen(tmp), ".%d.%d",
448 myport >> 8, myport & 255);
449 return (strdup(tmp)); /* Doesn't return static data ! */
450 }
451
452 /*
453 * _uaddr2taddr() translates a universal address back into a
454 * netaddr structure. Since the universal address is a string,
455 * put that into the TLI buffer (making sure to change all \ddd
456 * characters back and strip off the trailing \0 character).
457 */
458 struct netbuf *
459 /* ARGSUSED */
460 _uaddr2taddr(struct netconfig *netconfigp, char *uaddr)
461 {
462 assert(0);
463 _nderror = ND_SYSTEM;
464 return (NULL);
465 }
466
467 /*
468 * _netdir_options() is a "catch-all" routine that does
469 * transport specific things. The only thing that these
470 * routines have to worry about is ND_MERGEADDR.
471 */
472 int
473 /* ARGSUSED */
474 _netdir_options(struct netconfig *netconfigp, int option, int fd, void *par)
475 {
476 assert(0);
477 _nderror = ND_SYSTEM;
478 return (0);
479 }