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 }