xref: /titanic_50/usr/src/lib/ncad_addr/common/ncad_addr.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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 /*
23  * Copyright 1999-2003 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  * Shim library which should be LD_PRELOADed before running applications
31  * that interact with NCA but do not explicitly use the AF_NCA family.
32  * This library overloads AF_INET's version of bind(3SOCKET) with AF_NCA's
33  * version.  The new version of bind checks to see if that the port is one
34  * NCA is listening on, closes the socket(3SOCKET), and opens a new one
35  * the family AF_NCA.  Afterwards, the real bind(3SOCKET) is called
36  * descriptors, etc. *
37  *
38  * Compile:	cc -Kpic -G -o ncad_addr.so ncad_addr.c -lsocket -lnsl
39  * Use:		LD_PRELOAD=/path/to/ncad_addr.so my_program
40  */
41 
42 #include <stdio.h>
43 #include <assert.h>
44 #include <dlfcn.h>
45 #include <door.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <inet/nd.h>
49 #include <unistd.h>
50 #include <stropts.h>
51 #include <sys/stat.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <sys/mman.h>
55 #include <netdb.h>
56 #include <ctype.h>
57 #include <sys/types.h>
58 #include <sys/socket.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 
62 #pragma	weak bind = nca_bind
63 #pragma init(ncad_init)
64 #pragma	fini(ncad_fini)
65 
66 #define	SEPARATOR	'/'
67 
68 typedef int sfunc1_t(int, int, int);
69 typedef int sfunc2_t(int, const struct sockaddr *, socklen_t);
70 
71 static sfunc1_t *real_socket;
72 static sfunc2_t *real_bind;
73 
74 /*
75  * It is used to represent an address NCA is willing to handle.
76  */
77 typedef struct nca_address_s {
78 	uint16_t	port;	/* port, in network byte order */
79 	ipaddr_t	ipaddr;	/* IP address, in network byte order */
80 } nca_address_t;
81 
82 static uint32_t		addrcount;	/* current address count */
83 static uint32_t		addrcapacity;	/* capacity of ncaaddrs */
84 static nca_address_t	*ncaaddrs;	/* array for all addresses */
85 
86 /*
87  * It loads all NCA addresses from a configuration file. A NCA address
88  * entry is: ncaport=IPaddress:port. The line above can be repeatly for other
89  * addresses. If IPaddress is '*', then it is translated into INADDR_ANY.
90  */
91 static void
92 ncad_init(void)
93 {
94 	uint16_t	port;
95 	ipaddr_t	addr;
96 	FILE		*fp;
97 	char		*s, *p, *q;
98 	char		buffer[1024];
99 	const char	*filename = "/etc/nca/ncaport.conf";
100 
101 	real_socket = (sfunc1_t *)dlsym(RTLD_NEXT, "socket");
102 	real_bind = (sfunc2_t *)dlsym(RTLD_NEXT, "bind");
103 
104 	if ((fp = fopen(filename, "r")) == NULL) {
105 		(void) fprintf(stderr, "Failed to open file %s for reading in "
106 				" ncad_addr.so. Error = %s\n",
107 				filename,
108 				(p = strerror(errno)) ? p : "unknown error");
109 		return;
110 	}
111 
112 	while (fgets(buffer, sizeof (buffer), fp) != NULL) {
113 		s = buffer;
114 
115 		/* remove '\n' at the end from fgets() */
116 		p = strchr(s, '\n');
117 		if (p != NULL)
118 			*p = '\0';
119 
120 		/* remove spaces from the front */
121 		while (*s != '\0' && isspace(*s))
122 			s++;
123 
124 		if (*s == '\0' || *s == '#')
125 			continue;
126 
127 		/* it should start with ncaport= */
128 		p = strchr(s, '=');
129 		if (p == NULL || strncasecmp(s, "ncaport", 7) != 0)
130 			continue;
131 
132 		p++;
133 		while (*p != '\0' && isspace(*p))
134 			p++;
135 
136 		q = strchr(p, SEPARATOR);
137 		if (q == NULL)
138 			continue;
139 		*q++ = '\0';
140 		if (strcmp(p, "*") == 0) {
141 			addr = INADDR_ANY;
142 		} else {
143 			if (inet_pton(AF_INET, p, &addr) != 1) {
144 				struct in6_addr addr6;
145 
146 				if (inet_pton(AF_INET6, p, &addr6) == 1) {
147 					(void) fprintf(stderr,
148 						"NCA does not support IPv6\n");
149 				} else {
150 					(void) fprintf(stderr,
151 						"Invalid IP address: %s\n", p);
152 				}
153 				continue;
154 			}
155 		}
156 		port = atoi(q);
157 
158 		/* array is full, expand it */
159 		if (addrcount == addrcapacity) {
160 			if (addrcapacity == 0)
161 				addrcapacity = 64;
162 			else
163 				addrcapacity *= 2;
164 			ncaaddrs = realloc(ncaaddrs,
165 			    addrcapacity * sizeof (nca_address_t));
166 			if (ncaaddrs == NULL) {
167 				(void) fprintf(stderr, "out of memory");
168 				break;
169 			}
170 		}
171 
172 		ncaaddrs[addrcount].ipaddr = addr;
173 		ncaaddrs[addrcount].port = htons(port);
174 		addrcount++;
175 	}
176 
177 	(void) fclose(fp);
178 }
179 
180 /*
181  * It destroys memory at the end of program.
182  */
183 static void
184 ncad_fini(void)
185 {
186 	if (ncaaddrs != NULL) {
187 		free(ncaaddrs);
188 		ncaaddrs = NULL;
189 	}
190 }
191 
192 /*
193  * If the bind is happening on a port NCA is listening on, close
194  * the socket and open a new one with family AF_NCA.
195  */
196 static int
197 nca_bind(int sock, const struct sockaddr *name, socklen_t namelen)
198 {
199 	struct sockaddr_in sin;
200 	int new_sock;
201 	int i;
202 
203 	if (sock < 0) {
204 		errno = EBADF;
205 		return (-1);
206 	}
207 
208 	if (real_socket == NULL) {
209 		if ((real_socket = (sfunc1_t *)dlsym(RTLD_NEXT, "socket"))
210 		    == NULL) {
211 			errno = EAGAIN;
212 			exit(-1);
213 		}
214 	}
215 
216 	if (real_bind == NULL) {
217 		if ((real_bind = (sfunc2_t *)dlsym(RTLD_NEXT, "bind"))
218 		    == NULL) {
219 			errno = EAGAIN;
220 			exit(-1);
221 		}
222 	}
223 
224 	if (name == NULL ||
225 	    ncaaddrs == NULL ||
226 	    name->sa_family != AF_INET ||
227 	    namelen != sizeof (sin)) {
228 		return (real_bind(sock, name, namelen));
229 	}
230 
231 	(void) memcpy(&sin, name, sizeof (sin));
232 
233 	/*
234 	 * If it is one of the addresses NCA is handling, convert it
235 	 * to NCA socket.
236 	 */
237 	for (i = 0; i < addrcount; i++) {
238 		if (sin.sin_port == ncaaddrs[i].port &&
239 		    (sin.sin_addr.s_addr == ncaaddrs[i].ipaddr ||
240 		    ncaaddrs[i].ipaddr == INADDR_ANY)) {
241 			/* convert to NCA socket */
242 			new_sock = real_socket(AF_NCA, SOCK_STREAM, 0);
243 			if (new_sock >= 0) {
244 				(void) dup2(new_sock, sock);
245 				(void) close(new_sock);
246 				sin.sin_family = AF_NCA;
247 			}
248 			break;
249 		}
250 	}
251 
252 	return (real_bind(sock, (struct sockaddr *)&sin, namelen));
253 }
254