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
ncad_init(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
ncad_fini(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
nca_bind(int sock,const struct sockaddr * name,socklen_t namelen)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