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