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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <dhcp_svc_private.h> 27 #include <dirent.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <stdio.h> 31 #include <errno.h> 32 #include <signal.h> 33 #include <fcntl.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <sys/socket.h> 37 #include <sys/sockio.h> 38 #include <net/if.h> 39 #include <libintl.h> 40 #include <procfs.h> 41 #include <rpcsvc/nis.h> 42 #include <malloc.h> 43 #include <ctype.h> 44 #include <jni.h> 45 46 #include "exception.h" 47 #include "class_cache.h" 48 #include "dd_misc.h" 49 50 #define PROCFS_DIR "/proc" 51 52 /* 53 * Frees the list of data store strings. 54 */ 55 static void 56 free_enumerated_dd(char **module, int count) 57 { 58 while (count-- > 0) { 59 free(module[count]); 60 } 61 free(module); 62 } 63 64 /* 65 * Determines if a given data store name is valid. 66 */ 67 static boolean_t 68 dd_is_valid_data_store(JNIEnv *env, const char *name) 69 { 70 char **module; 71 int count; 72 int ndx; 73 int rcode; 74 boolean_t isValid = B_FALSE; 75 76 if (name != NULL) { 77 rcode = enumerate_dd(&module, &count); 78 if (rcode != DSVC_SUCCESS) { 79 throw_libdhcpsvc_exception(env, rcode); 80 } else { 81 for (ndx = 0; !isValid && ndx < count; ndx++) { 82 if (strcmp(module[ndx], name) == 0) { 83 isValid = B_TRUE; 84 } 85 } 86 free_enumerated_dd(module, count); 87 } 88 } 89 return (isValid); 90 } 91 92 /* 93 * Call a java object's int getter method. 94 */ 95 static boolean_t 96 dd_get_int_attr( 97 JNIEnv *env, 98 jclass class, 99 int id, 100 jobject obj, 101 int *value) { 102 103 jmethodID methodID; 104 jint jval; 105 boolean_t noException = B_TRUE; 106 107 methodID = get_methodID(env, class, id); 108 if (methodID == NULL) { 109 noException = B_FALSE; 110 } else { 111 jval = (*env)->CallIntMethod(env, obj, methodID); 112 if ((*env)->ExceptionOccurred(env) != NULL) { 113 noException = B_FALSE; 114 } else { 115 *value = jval; 116 } 117 } 118 return (noException); 119 } 120 121 /* 122 * Convert a possibly multi-byte string to a jstring. 123 */ 124 jstring 125 dd_native_to_jstring(JNIEnv *env, const char *str) 126 { 127 jstring result; 128 jbyteArray bytes = 0; 129 int len; 130 131 jclass strclass; 132 jmethodID mid; 133 134 strclass = (*env)->FindClass(env, "java/lang/String"); 135 if (strclass == NULL) { 136 /* exception thrown */ 137 return (NULL); 138 } 139 140 mid = (*env)->GetMethodID(env, strclass, "<init>", "([B)V"); 141 if (mid == NULL) { 142 /* exception thrown */ 143 return (NULL); 144 } 145 146 len = strlen(str); 147 bytes = (*env)->NewByteArray(env, len); 148 if (bytes == NULL) { 149 /* exception thrown */ 150 return (NULL); 151 } 152 153 (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) str); 154 result = (*env)->NewObject(env, strclass, mid, bytes); 155 156 (*env)->DeleteLocalRef(env, bytes); 157 return (result); 158 } 159 160 /* 161 * Convert a jstring to a possibly multi-byte string. 162 */ 163 char * 164 dd_jstring_to_native(JNIEnv *env, jstring jstr) { 165 166 jbyteArray bytes = 0; 167 jint len; 168 char *result; 169 170 jclass strclass; 171 jmethodID mid; 172 173 strclass = (*env)->FindClass(env, "java/lang/String"); 174 if (strclass == NULL) { 175 /* exception thrown */ 176 return (NULL); 177 } 178 179 mid = (*env)->GetMethodID(env, strclass, "getBytes", "()[B"); 180 if (mid == NULL) { 181 /* exception thrown */ 182 return (NULL); 183 } 184 185 bytes = (*env)->CallObjectMethod(env, jstr, mid); 186 if ((*env)->ExceptionOccurred(env)) { 187 /* exception thrown */ 188 return (NULL); 189 } 190 191 len = (*env)->GetArrayLength(env, bytes); 192 result = (char *)malloc(len + 1); 193 if (result == NULL) { 194 throw_memory_exception(env); 195 (*env)->DeleteLocalRef(env, bytes); 196 return (NULL); 197 } 198 199 (*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *) result); 200 result[len] = 0; 201 202 (*env)->DeleteLocalRef(env, bytes); 203 return (result); 204 } 205 206 /* 207 * Convert a jstring to a UTF-8 string. 208 */ 209 boolean_t 210 dd_jstring_to_UTF( 211 JNIEnv *env, 212 jstring javaString, 213 char **nativeString) 214 { 215 boolean_t noException = B_TRUE; 216 217 if (javaString != NULL) { 218 const char *str; 219 str = (*env)->GetStringUTFChars(env, javaString, NULL); 220 if (str == NULL) { 221 noException = B_FALSE; 222 } else { 223 *nativeString = strdup(str); 224 (*env)->ReleaseStringUTFChars(env, javaString, str); 225 if (*nativeString == NULL) { 226 throw_memory_exception(env); 227 noException = B_FALSE; 228 } 229 } 230 } else { 231 *nativeString = NULL; 232 } 233 234 return (noException); 235 } 236 237 238 /* 239 * Call a java object's string getter method. 240 */ 241 boolean_t 242 dd_get_str_attr( 243 JNIEnv *env, 244 jclass class, 245 int id, 246 jobject obj, 247 char **value) { 248 249 jmethodID methodID; 250 jstring jstr; 251 252 *value = NULL; 253 254 methodID = get_methodID(env, class, id); 255 if (methodID == NULL) { 256 return (B_FALSE); 257 } 258 259 jstr = (*env)->CallObjectMethod(env, obj, methodID); 260 if ((*env)->ExceptionOccurred(env) != NULL) { 261 return (B_FALSE); 262 } 263 264 if (jstr != NULL) { 265 *value = dd_jstring_to_native(env, jstr); 266 (*env)->DeleteLocalRef(env, jstr); 267 if (*value == NULL) { 268 return (B_FALSE); 269 } 270 } 271 272 return (B_TRUE); 273 } 274 275 /* 276 * Reads the DHCP configuration file and creates a dsvc_datastore_t. 277 */ 278 boolean_t 279 dd_get_conf_datastore_t(JNIEnv *env, dsvc_datastore_t *dsp) { 280 281 dhcp_confopt_t *confopts; 282 int result; 283 boolean_t noException = B_TRUE; 284 285 dsp->d_resource = NULL; 286 dsp->d_location = NULL; 287 dsp->d_config = NULL; 288 289 result = read_dsvc_conf(&confopts); 290 if (result != 0) { 291 throw_no_defaults_exception(env); 292 noException = B_FALSE; 293 } else { 294 result = confopt_to_datastore(confopts, dsp); 295 if (result != DSVC_SUCCESS) { 296 throw_libdhcpsvc_exception(env, result); 297 noException = B_FALSE; 298 } 299 free_dsvc_conf(confopts); 300 } 301 return (noException); 302 } 303 304 /* 305 * Makes a dsvc_datastore_t using the DHCP configuration file and overriding 306 * the settings with the arguments if they are non-NULL. 307 */ 308 boolean_t 309 dd_make_datastore_t(JNIEnv *env, 310 dsvc_datastore_t *dsp, 311 jobject jdatastore) { 312 313 jclass ds_class; 314 jthrowable e; 315 316 char *resource = NULL; 317 char *location = NULL; 318 char *config = NULL; 319 int version = DSVC_CUR_CONVER; 320 321 dsp->d_resource = NULL; 322 dsp->d_location = NULL; 323 dsp->d_config = NULL; 324 325 /* Locate the class we need */ 326 ds_class = find_class(env, DS_CLASS); 327 if (ds_class == NULL) { 328 /* exception thrown */ 329 return (B_FALSE); 330 } 331 332 /* Obtain the DHCP config file data store settings */ 333 if (!dd_get_conf_datastore_t(env, dsp)) { 334 e = (*env)->ExceptionOccurred(env); 335 (*env)->ExceptionClear(env); 336 if (!is_no_defaults_exception(env, e)) { 337 (*env)->Throw(env, e); 338 return (B_FALSE); 339 } 340 } 341 342 /* Get the resource */ 343 if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETRSRC, 344 jdatastore, &resource)) { 345 /* exception thrown */ 346 dd_free_datastore_t(dsp); 347 return (B_FALSE); 348 } 349 350 /* If resource was passed in, then override config setting */ 351 if (resource != NULL) { 352 free(dsp->d_resource); 353 dsp->d_resource = resource; 354 dsp->d_conver = DSVC_CUR_CONVER; 355 } 356 357 /* Validate the resource */ 358 if (!dd_is_valid_data_store(env, dsp->d_resource)) { 359 if ((*env)->ExceptionOccurred(env) == NULL) { 360 throw_invalid_resource_exception(env, dsp->d_resource); 361 } 362 dd_free_datastore_t(dsp); 363 return (B_FALSE); 364 } 365 366 /* Get the location */ 367 if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETLOC, 368 jdatastore, &location)) { 369 /* exception thrown */ 370 dd_free_datastore_t(dsp); 371 return (B_FALSE); 372 } 373 374 /* If location was passed in, then override config setting */ 375 if (location != NULL) { 376 free(dsp->d_location); 377 dsp->d_location = location; 378 } 379 380 /* Must be defined */ 381 if (dsp->d_location == NULL) { 382 throw_invalid_path_exception(env, dsp->d_location); 383 dd_free_datastore_t(dsp); 384 return (B_FALSE); 385 } 386 387 /* Get the config string */ 388 if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETRSRCCFG, 389 jdatastore, &config)) { 390 /* exception thrown */ 391 dd_free_datastore_t(dsp); 392 return (B_FALSE); 393 } 394 395 /* If config string was passed in, then override config setting */ 396 if (config != NULL) { 397 free(dsp->d_config); 398 dsp->d_config = config; 399 } 400 401 /* Get the version */ 402 if (jdatastore != NULL && !dd_get_int_attr(env, ds_class, DS_GETVER, 403 jdatastore, &version)) { 404 /* exception thrown */ 405 dd_free_datastore_t(dsp); 406 return (B_FALSE); 407 } 408 409 /* If version was passed in, then override config setting */ 410 if (version != DSVC_CUR_CONVER) { 411 dsp->d_conver = version; 412 } 413 414 return (B_TRUE); 415 } 416 417 /* 418 * Frees the strings in a dsvc_datastore_t structure. 419 */ 420 void 421 dd_free_datastore_t(dsvc_datastore_t *dsp) { 422 free(dsp->d_resource); 423 free(dsp->d_location); 424 free(dsp->d_config); 425 } 426 427 /* 428 * Returns the list of possible data stores for DHCP data. 429 * List returned is terminated with a NULL. 430 */ 431 char ** 432 dd_data_stores(JNIEnv *env) 433 { 434 char **dsl = NULL; 435 char **module; 436 int count; 437 int ndx; 438 int rcode; 439 440 rcode = enumerate_dd(&module, &count); 441 if (rcode != DSVC_SUCCESS) { 442 throw_libdhcpsvc_exception(env, rcode); 443 return (NULL); 444 } 445 446 if (count == 0) { 447 return (NULL); 448 } 449 450 dsl = calloc((count + 1), sizeof (char *)); 451 if (dsl == NULL) { 452 free_enumerated_dd(module, count); 453 throw_memory_exception(env); 454 return (NULL); 455 } 456 457 for (ndx = 0; ndx < count; ndx++) { 458 dsl[ndx] = strdup(module[ndx]); 459 if (dsl[ndx] == NULL) { 460 break; 461 } 462 } 463 464 free_enumerated_dd(module, count); 465 466 if (ndx != count) { 467 dd_free_data_stores(dsl); 468 throw_memory_exception(env); 469 dsl = NULL; 470 } 471 472 return (dsl); 473 } 474 475 /* 476 * Free a data store list created by dd_data_stores(). 477 */ 478 void 479 dd_free_data_stores(char **dsl) 480 { 481 int i = 0; 482 483 if (dsl != NULL) { 484 for (i = 0; dsl[i] != NULL; ++i) { 485 free(dsl[i]); 486 } 487 free(dsl); 488 } 489 } 490 491 /* 492 * Send a signal to a process whose command name is as specified 493 */ 494 int 495 dd_signal(char *fname, int sig) 496 { 497 pid_t pid; 498 499 pid = dd_getpid(fname); 500 if (pid == (pid_t)-1) { 501 return (-1); 502 } 503 504 if (kill(pid, sig) != 0) { 505 return (errno); 506 } else { 507 return (0); 508 } 509 } 510 511 /* 512 * Return a process's pid 513 */ 514 pid_t 515 dd_getpid(char *fname) 516 { 517 DIR *dirptr; 518 dirent_t *direntptr; 519 psinfo_t psinfo; 520 int proc_fd; 521 char buf[MAXPATHLEN]; 522 pid_t retval = (pid_t)-1; 523 524 /* 525 * Read entries in /proc, each one is in turn a directory 526 * containing files relating to the process's state. We read 527 * the psinfo file to get the command name. 528 */ 529 dirptr = opendir(PROCFS_DIR); 530 if (dirptr == (DIR *) NULL) { 531 return (retval); 532 } 533 while ((direntptr = readdir(dirptr)) != NULL) { 534 (void) snprintf(buf, sizeof (buf), PROCFS_DIR"/%s/psinfo", 535 direntptr->d_name); 536 if ((proc_fd = open(buf, O_RDONLY)) < 0) { 537 continue; /* skip this one */ 538 } 539 if (read(proc_fd, &psinfo, sizeof (psinfo)) > 0) { 540 if (strncmp(psinfo.pr_fname, fname, PRFNSZ) == 0) { 541 retval = psinfo.pr_pid; 542 (void) close(proc_fd); 543 break; 544 } 545 } 546 (void) close(proc_fd); 547 } 548 (void) closedir(dirptr); 549 return (retval); 550 } 551 552 553 /* 554 * Get list of physical, non-loopback interfaces for the system. Those are 555 * the ones in.dhcpd will support. 556 */ 557 struct ip_interface ** 558 dd_get_interfaces() 559 { 560 int s; 561 struct ifconf ifc; 562 int num_ifs; 563 int i; 564 struct ifreq *ifr; 565 struct ip_interface **ret = NULL; 566 struct ip_interface **tmpret; 567 int retcnt = 0; 568 struct sockaddr_in *sin; 569 570 /* 571 * Open socket, needed for doing the ioctls. Then get number of 572 * interfaces so we know how much memory to allocate, then get 573 * all the interface configurations. 574 */ 575 s = socket(AF_INET, SOCK_DGRAM, 0); 576 if (ioctl(s, SIOCGIFNUM, &num_ifs) < 0) { 577 (void) close(s); 578 return (NULL); 579 } 580 ifc.ifc_len = num_ifs * sizeof (struct ifreq); 581 ifc.ifc_buf = malloc(ifc.ifc_len); 582 if (ifc.ifc_buf == NULL) { 583 (void) close(s); 584 return (NULL); 585 } 586 if (ioctl(s, SIOCGIFCONF, &ifc) < 0) { 587 free(ifc.ifc_buf); 588 (void) close(s); 589 return (NULL); 590 } 591 592 /* 593 * For each interface, stuff its name, address and netmask into the 594 * structure that we return. Filter out loopback and virtual 595 * interfaces as they are of no interest for DHCP. 596 */ 597 for (i = 0, ifr = ifc.ifc_req; i < num_ifs; ++i, ++ifr) { 598 if (strchr(ifr->ifr_name, ':') != NULL) { 599 continue; /* Ignore a virtual interface */ 600 } 601 if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) { 602 continue; /* Can't get flags? Ignore it. */ 603 } 604 605 if ((ifr->ifr_flags & IFF_LOOPBACK) || 606 !(ifr->ifr_flags & IFF_UP)) { 607 continue; /* Ignore if loopback or down */ 608 } 609 /* Get more space to store this in */ 610 tmpret = realloc(ret, 611 (retcnt+1)*sizeof (struct ip_interface *)); 612 if (tmpret == NULL) { 613 while (retcnt-- > 0) 614 free(ret[retcnt]); 615 free(ret); 616 free(ifc.ifc_buf); 617 (void) close(s); 618 return (NULL); 619 } 620 ret = tmpret; 621 ret[retcnt] = malloc(sizeof (struct ip_interface)); 622 if (ret[retcnt] == NULL) { 623 while (retcnt-- > 0) 624 free(ret[retcnt]); 625 free(ret); 626 free(ifc.ifc_buf); 627 (void) close(s); 628 return (NULL); 629 } 630 (void) strcpy(ret[retcnt]->name, ifr->ifr_name); 631 if (ioctl(s, SIOCGIFADDR, ifr) < 0) { 632 (void) close(s); 633 while (retcnt-- > 0) { 634 free(ret[retcnt]); 635 } 636 free(ret); 637 free(ifc.ifc_buf); 638 return (NULL); 639 } 640 /*LINTED - alignment*/ 641 sin = (struct sockaddr_in *)&ifr->ifr_addr; 642 ret[retcnt]->addr = sin->sin_addr; 643 644 if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) { 645 (void) close(s); 646 while (retcnt-- > 0) { 647 free(ret[retcnt]); 648 } 649 free(ret); 650 free(ifc.ifc_buf); 651 return (NULL); 652 } 653 /*LINTED - alignment*/ 654 sin = (struct sockaddr_in *)&ifr->ifr_addr; 655 ret[retcnt]->mask = sin->sin_addr; 656 ++retcnt; 657 } 658 659 /* Null-terminate the list */ 660 if (retcnt > 0) { 661 tmpret = realloc(ret, 662 (retcnt+1)*sizeof (struct ip_interface *)); 663 if (tmpret == NULL) { 664 while (retcnt-- > 0) 665 free(ret[retcnt]); 666 free(ret); 667 free(ifc.ifc_buf); 668 (void) close(s); 669 return (NULL); 670 } 671 ret = tmpret; 672 ret[retcnt] = NULL; 673 } 674 (void) close(s); 675 free(ifc.ifc_buf); 676 return (ret); 677 }