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
free_enumerated_dd(char ** module,int count)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
dd_is_valid_data_store(JNIEnv * env,const char * name)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
dd_get_int_attr(JNIEnv * env,jclass class,int id,jobject obj,int * value)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
dd_native_to_jstring(JNIEnv * env,const char * str)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 *
dd_jstring_to_native(JNIEnv * env,jstring jstr)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
dd_jstring_to_UTF(JNIEnv * env,jstring javaString,char ** nativeString)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
dd_get_str_attr(JNIEnv * env,jclass class,int id,jobject obj,char ** value)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
dd_get_conf_datastore_t(JNIEnv * env,dsvc_datastore_t * dsp)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
dd_make_datastore_t(JNIEnv * env,dsvc_datastore_t * dsp,jobject jdatastore)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
dd_free_datastore_t(dsvc_datastore_t * dsp)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 **
dd_data_stores(JNIEnv * env)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
dd_free_data_stores(char ** dsl)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
dd_signal(char * fname,int sig)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
dd_getpid(char * fname)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 **
dd_get_interfaces()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 }
678