xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.c (revision 11262:b7ebfbf2359e)
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