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