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 * ident "%Z%%M% %I% %E% SMI"
24 *
25 * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28
29 package com.sun.dhcpmgr.data;
30
31 import java.util.Date;
32 import java.text.SimpleDateFormat;
33 import java.text.DateFormat;
34 import java.util.StringTokenizer;
35 import java.io.Serializable;
36
37 /**
38 * This class represents a record in a DHCP network table. It can also be used
39 * to manage an associated hosts record by setting the client name; that effect
40 * is not part of this class, but rather is provided by the DhcpNetMgr.
41 */
42 public class DhcpClientRecord implements Serializable, Comparable, Cloneable {
43
44 /**
45 * Default values for class attributes.
46 */
47 public static final String DEFAULT_CLIENT_ID = new String("00");
48 public static final String DEFAULT_FLAGS = new String("00");
49 public static final String DEFAULT_CLIENT_NAME = new String();
50 public static final String DEFAULT_EXPIRATION = new String("0");
51 public static final String DEFAULT_SIGNATURE = new String("0");
52 public static final String DEFAULT_MACRO = new String("UNKNOWN");
53 public static final String DEFAULT_COMMENT = new String();
54
55 /**
56 * Expiration special values.
57 */
58 private static final String EXPIRATION_ZERO = new String("0");
59 private static final String EXPIRATION_FOREVER = new String("-1");
60
61 private String clientId;
62 private byte flags;
63 private IPAddress clientIP;
64 private IPAddress serverIP;
65 private Date expiration;
66 private String signature = DEFAULT_SIGNATURE;
67 private String macro;
68 private String comment;
69 private String clientName = null;
70 private String serverName = null;
71
72 // Serialization id of this class
73 static final long serialVersionUID = 5007310554198923085L;
74
75 /**
76 * Constructs a basic, empty client record.
77 */
78 public DhcpClientRecord() {
79 clientId = DEFAULT_CLIENT_ID;
80 macro = comment = null;
81 flags = 0;
82 clientIP = serverIP = null;
83 expiration = null;
84 signature = DEFAULT_SIGNATURE;
85 }
86
87 /**
88 * Constructs a client record with a client IP.
89 * @param clientIP the client IP address for the record.
90 */
91 public DhcpClientRecord(String clientIP) throws ValidationException {
92 setDefaults();
93 setClientIP(new IPAddress(clientIP));
94 }
95
96 /**
97 * Constructs a fully specified client record
98 * @param clientId Client's unique identifier
99 * @param flags Status flags for the record
100 * @param clientIP Client's IP address
101 * @param serverIP IP address of owning server
102 * @param expiration Lease expiration time in seconds since Unix epoch
103 * @param macro Configuration macro associated with this record
104 * @param comment User notes on this record
105 */
106 public DhcpClientRecord(String clientId, String flags, String clientIP,
107 String serverIP, String expiration, String macro,
108 String comment) throws ValidationException {
109
110 this(clientId, flags, clientIP, serverIP, expiration, macro,
111 comment, DEFAULT_SIGNATURE);
112 }
113
114 /**
115 * Constructs a fully specified client record
116 * @param clientId Client's unique identifier
117 * @param flags Status flags for the record
118 * @param clientIP Client's IP address
119 * @param serverIP IP address of owning server
120 * @param expiration Lease expiration time in seconds since Unix epoch
121 * @param macro Configuration macro associated with this record
122 * @param comment User notes on this record
123 * @param signature Opaque signature
124 */
125 public DhcpClientRecord(String clientId, String flags, String clientIP,
126 String serverIP, String expiration, String macro,
127 String comment, String signature)
128 throws ValidationException {
129 setClientId(clientId);
130 this.flags = Byte.parseByte(flags);
131 setClientIP(new IPAddress(clientIP));
132 setServerIP(new IPAddress(serverIP));
133 setExpiration(expiration);
134 this.macro = macro;
135 this.comment = comment;
136 this.signature = signature;
137 }
138
139 /**
140 * Make a copy of this record
141 */
142 public Object clone() {
143 DhcpClientRecord newrec = new DhcpClientRecord();
144 newrec.clientId = clientId;
145 newrec.flags = flags;
146 if (clientIP != null) {
147 newrec.clientIP = (IPAddress)clientIP.clone();
148 }
149 if (serverIP != null) {
150 newrec.serverIP = (IPAddress)serverIP.clone();
151 }
152 if (expiration != null) {
153 newrec.expiration = (Date)expiration.clone();
154 }
155 newrec.macro = macro;
156 newrec.comment = comment;
157 newrec.clientName = clientName;
158 newrec.serverName = serverName;
159 newrec.signature = signature;
160 return newrec;
161 }
162
163 /**
164 * Fully specifies the defaults for a client record
165 */
166 public void setDefaults()
167 throws ValidationException {
168 setClientId(DEFAULT_CLIENT_ID);
169 setFlags(DEFAULT_FLAGS);
170 setClientName(DEFAULT_CLIENT_NAME);
171 setExpiration(DEFAULT_EXPIRATION);
172 setMacro(DEFAULT_MACRO);
173 setComment(DEFAULT_COMMENT);
174 }
175
176 /**
177 * Retrieve the client ID
178 * @return Client ID as a String
179 */
180 public String getClientId() {
181 return clientId;
182 }
183
184 /**
185 * Set the client ID. See dhcp_network(4) for the rules about client
186 * ID syntax which are implemented here.
187 * @param clientId Client's unique identifier
188 */
189 public void setClientId(String clientId) throws ValidationException {
190 if (clientId.length() > 128 || clientId.length() % 2 != 0) {
191 // Must be even number of characters, no more than 128 characters
192 String msg = ResourceStrings.getString("dcr_invalid_clientid");
193 throw new ValidationException(msg);
194 }
195 char [] c = clientId.toCharArray();
196 for (int i = 0; i < c.length; ++i) {
197 if ((c[i] < '0' || c[i] > '9') && (c[i] < 'A' || c[i] > 'F')) {
198 String msg = ResourceStrings.getString("dcr_invalid_clientid");
199 throw new ValidationException(msg);
200 }
201 }
202 this.clientId = clientId;
203 if (this.clientId.length() == 0) {
204 this.clientId = DEFAULT_CLIENT_ID;
205 }
206 }
207
208 /**
209 * Get the flags byte
210 * @return A <code>byte</code> containing the record's status flags
211 */
212 public byte getFlags() {
213 return flags;
214 }
215
216 /**
217 * Get the flags as a string
218 * @return The flag byte converted to a String
219 */
220 public String getFlagString() {
221 return getFlagString(false);
222 }
223
224 public String getFlagString(boolean verbose) {
225
226 StringBuffer b = new StringBuffer();
227 if (!verbose) {
228 b.append(flags);
229 // Make sure we always have a 2-character representation.
230 if (flags < 10) {
231 b.insert(0, 0);
232 }
233 }
234 else {
235 if (flags == 0) {
236 b.append(DhcpClientFlagTypes.DYNAMIC.getCharVal());
237 } else {
238 if (isPermanent()) {
239 b.append(DhcpClientFlagTypes.PERMANENT.getCharVal());
240 }
241 if (isManual()) {
242 b.append(DhcpClientFlagTypes.MANUAL.getCharVal());
243 }
244 if (isUnusable()) {
245 b.append(DhcpClientFlagTypes.UNUSABLE.getCharVal());
246 }
247 if (isBootp()) {
248 b.append(DhcpClientFlagTypes.BOOTP.getCharVal());
249 }
250 }
251 }
252 return b.toString();
253 }
254
255 /**
256 * Test for setting of unusable flag
257 * @return <code>true</code> if the unusable flag is set,
258 * <code>false</code> if not.
259 */
260 public boolean isUnusable() {
261 return DhcpClientFlagTypes.UNUSABLE.isSet(flags);
262 }
263
264 /**
265 * Set/reset the unusable flag.
266 * @param state <code>true</code> if address is to be unusable
267 */
268 public void setUnusable(boolean state) {
269 if (state) {
270 flags |= DhcpClientFlagTypes.UNUSABLE.getNumericVal();
271 } else {
272 flags &= ~DhcpClientFlagTypes.UNUSABLE.getNumericVal();
273 }
274 }
275
276 /**
277 * Test for setting of bootp flag
278 * @return <code>true</code> if the bootp flag is set,
279 * <code>false</code> if not.
280 */
281 public boolean isBootp() {
282 return DhcpClientFlagTypes.BOOTP.isSet(flags);
283 }
284
285 /**
286 * Set/reset the bootp flag
287 * @param state <code>true</code> if address is reserved for BOOTP clients
288 */
289 public void setBootp(boolean state) {
290 if (state) {
291 flags |= DhcpClientFlagTypes.BOOTP.getNumericVal();
292 } else {
293 flags &= ~DhcpClientFlagTypes.BOOTP.getNumericVal();
294 }
295 }
296
297 /**
298 * Test for setting of manual assignment flag
299 * @return <code>true</code> if address is manually assigned,
300 * <code>false</code> if not.
301 */
302 public boolean isManual() {
303 return DhcpClientFlagTypes.MANUAL.isSet(flags);
304 }
305
306 /**
307 * Set/reset the manual assignment flag
308 * @param state <code>true</code> if the address is manually assigned
309 */
310 public void setManual(boolean state) {
311 if (state) {
312 flags |= DhcpClientFlagTypes.MANUAL.getNumericVal();
313 } else {
314 flags &= ~DhcpClientFlagTypes.MANUAL.getNumericVal();
315 }
316 }
317
318 /**
319 * Test for setting of permanent assignment flag
320 * @return <code>true</code> if lease is permanent,
321 * <code>false</code> if dynamic
322 */
323 public boolean isPermanent() {
324 return DhcpClientFlagTypes.PERMANENT.isSet(flags);
325 }
326
327 /**
328 * Set/reset the permanent assignment flag
329 * @param state <code>true</code> if the address is permanently leased
330 */
331 public void setPermanent(boolean state) {
332 if (state) {
333 flags |= DhcpClientFlagTypes.PERMANENT.getNumericVal();
334 } else {
335 flags &= ~DhcpClientFlagTypes.PERMANENT.getNumericVal();
336 }
337 }
338
339 /**
340 * Set the flags as a unit
341 * @param flags a <code>byte</code> setting for the flags
342 */
343 public void setFlags(String flags) throws ValidationException {
344 if (flags.charAt(0) >= '0' && flags.charAt(0) <= '9') {
345 this.flags = Byte.parseByte(flags);
346 } else {
347 this.flags = 0;
348 StringTokenizer flagTokenizer = new StringTokenizer(flags, "+");
349 while (flagTokenizer.hasMoreTokens()) {
350 String keyword = flagTokenizer.nextToken();
351 if (keyword.equalsIgnoreCase(
352 DhcpClientFlagTypes.DYNAMIC.getKeyword())) {
353 // nothing to do, default is Dynamic.
354 } else if (keyword.equalsIgnoreCase(
355 DhcpClientFlagTypes.PERMANENT.getKeyword())) {
356 this.flags |= DhcpClientFlagTypes.PERMANENT.getNumericVal();
357 } else if (keyword.equalsIgnoreCase(
358 DhcpClientFlagTypes.MANUAL.getKeyword())) {
359 this.flags |= DhcpClientFlagTypes.MANUAL.getNumericVal();
360 } else if (keyword.equalsIgnoreCase(
361 DhcpClientFlagTypes.UNUSABLE.getKeyword())) {
362 this.flags |= DhcpClientFlagTypes.UNUSABLE.getNumericVal();
363 } else if (keyword.equalsIgnoreCase(
364 DhcpClientFlagTypes.BOOTP.getKeyword())) {
365 this.flags |= DhcpClientFlagTypes.BOOTP.getNumericVal();
366 } else {
367 String msg = ResourceStrings.getString("dcr_invalid_flags");
368 throw new ValidationException(msg);
369 }
370 }
371 }
372 }
373
374 /**
375 * Set the flags as a unit
376 * @param flags a <code>byte</code> setting for the flags
377 */
378 public void setFlags(byte flags) {
379 this.flags = flags;
380 }
381
382 /**
383 * Retrieve the client's IP address
384 * @return the client's IP address
385 */
386 public IPAddress getClientIP() {
387 return clientIP;
388 }
389
390 /**
391 * Retrieve a string version of the client's IP address
392 * @return A <code>String</code> containing the dotted decimal IP address.
393 */
394 public String getClientIPAddress() {
395 if (clientIP == null) {
396 return "";
397 } else {
398 return clientIP.getHostAddress();
399 }
400 }
401
402 /**
403 * Set the client's IP address
404 * @param clientIP A String representation of the <code>IPAddress</code>
405 * to assign from this record.
406 */
407 public void setClientIP(String clientIP) throws ValidationException {
408 if (clientIP == null) {
409 String msg = ResourceStrings.getString("dcr_invalid_null_clientip");
410 throw new ValidationException(msg);
411 }
412
413 try {
414 setClientIP(new IPAddress(clientIP));
415 } catch (Throwable e) {
416 String msg = ResourceStrings.getString("dcr_invalid_clientip");
417 throw new ValidationException(msg);
418 }
419 }
420
421 /**
422 * Set the client's IP address
423 * @param clientIP An <code>IPAddress</code> to assign from this record.
424 */
425 public void setClientIP(IPAddress clientIP) throws ValidationException {
426 if (clientIP == null) {
427 String msg = ResourceStrings.getString("dcr_invalid_null_clientip");
428 throw new ValidationException(msg);
429 }
430 this.clientIP = clientIP;
431 }
432
433 /**
434 * Retrieve the IP address of the owning server.
435 * @return An <code>IPAddress</code> for the server controlling this record.
436 */
437 public IPAddress getServerIP() {
438 return serverIP;
439 }
440
441 /**
442 * Retrieve a string version of the owning server's IP address
443 * @return The server's dotted decimal IP address as a <code>String</code>
444 */
445 public String getServerIPAddress() {
446 if (serverIP == null) {
447 return "";
448 } else {
449 return serverIP.getHostAddress();
450 }
451 }
452
453 /**
454 * Set the server's IP address
455 * @param serverIP A String representation of the <code>IPAddress</code>
456 * to assign from this record.
457 */
458 public void setServerIP(String serverIP) throws ValidationException {
459 if (serverIP == null) {
460 String msg = ResourceStrings.getString("dcr_invalid_null_serverip");
461 throw new ValidationException(msg);
462 }
463
464 try {
465 setServerIP(new IPAddress(serverIP));
466 } catch (Throwable e) {
467 String msg = ResourceStrings.getString("dcr_invalid_serverip");
468 throw new ValidationException(msg);
469 }
470 }
471
472 /**
473 * Assign this address to a server denoted by its IP address
474 * @param serverIP The <code>IPAddress</code> of the owning server.
475 */
476 public void setServerIP(IPAddress serverIP) throws ValidationException {
477 if (serverIP == null) {
478 String msg = ResourceStrings.getString("dcr_invalid_null_serverip");
479 throw new ValidationException(msg);
480 }
481 this.serverIP = serverIP;
482 }
483
484 /**
485 * @return The expiration time of this record's lease as a <code>Date</code>
486 */
487 public Date getExpiration() {
488 return expiration;
489 }
490
491 /**
492 * @return The expiration time of this record's lease in seconds
493 * since the epoch, as a <code>String</code>
494 */
495 public String getExpirationTime() {
496 if (expiration == null) {
497 return null;
498 }
499 if (expiration.getTime() == Long.parseLong(EXPIRATION_FOREVER)) {
500 return EXPIRATION_FOREVER;
501 } else {
502 return String.valueOf((expiration.getTime()/(long)1000));
503 }
504 }
505
506 /**
507 * Set the lease expiration date.
508 * @param expiration Lease expiration time in seconds since Unix epoch
509 */
510 public void setExpiration(String expiration) {
511 this.expiration = new Date((long)(Long.parseLong(expiration)*1000));
512 }
513
514 /**
515 * Set the lease expiration date.
516 * @param expiration The <code>Date</code> when the lease expires.
517 */
518 public void setExpiration(Date expiration) {
519 this.expiration = expiration;
520 }
521
522 /**
523 * Set the lease expiration date by parsing a formatted string. Also
524 * provides special handling of the "0" and "-1" values.
525 * @param dateFormat A DateFormat used to parse the expiration date
526 * @param date Lease expiration in desired format.
527 */
528 public void setExpiration(DateFormat dateFormat, String date)
529 throws ValidationException {
530
531 if (date == null) {
532 setExpiration(date);
533 } else if (date.equals(EXPIRATION_ZERO)) {
534 setExpiration(date);
535 } else if (date.equals(EXPIRATION_FOREVER)) {
536 setExpiration(date);
537 } else {
538 try {
539 expiration = dateFormat.parse(date);
540 } catch (Exception ex) {
541 String msg =
542 ResourceStrings.getString("dcr_invalid_expiration");
543 throw new ValidationException(msg);
544 }
545 }
546 }
547
548 /**
549 * @return The name of the macro used to explicitly configure this address
550 */
551 public String getMacro() {
552 return macro;
553 }
554
555 /**
556 * Set the name of the macro used to explicitly configure this address
557 */
558 public void setMacro(String macro) {
559 this.macro = macro;
560 }
561
562 /**
563 * @return The descriptive comment for this record
564 */
565 public String getComment() {
566 return comment;
567 }
568
569 /**
570 * Set a descriptive comment for this record
571 * @param comment The comment
572 */
573 public void setComment(String comment) {
574 this.comment = comment;
575 }
576
577 /**
578 * @return The signature for this record
579 */
580 public String getSignature() {
581 return signature;
582 }
583
584 /**
585 * Set the signature for this record
586 * @param signature The new signature value
587 */
588 public void setSignature(String signature) {
589 this.signature = signature;
590 }
591
592 /**
593 * Perform comparisons to another DhcpClientRecord instance. This is used
594 * for sorting a network table by client address.
595 * @param o A <code>DhcpClientRecord</code> to compare against.
596 * @return 0 if the objects have the same address,
597 * a negative number if this record has a lower IP address than the
598 * supplied record, a positive number if this record has a higher IP
599 * address than the supplied record.
600 */
601 public int compareTo(Object o) {
602 DhcpClientRecord r = (DhcpClientRecord)o;
603 return (int)(getBinaryAddress() - r.getBinaryAddress());
604 }
605
606 /**
607 * Retrieve the IP address as a number suitable for arithmetic operations.
608 * We use a <code>long</code> rather than an <code>int</code> in order to
609 * be able to treat it as an unsigned value, since all Java types are
610 * signed.
611 * @return The IP address as a <code>long</code>.
612 */
613 public long getBinaryAddress() {
614 return (clientIP.getBinaryAddress());
615 }
616
617 /**
618 * @return The client's hostname
619 */
620 public String getClientName() {
621 if (clientName == null && clientIP != null) {
622 clientName = clientIP.getHostName();
623 }
624 return clientName;
625 }
626
627 /**
628 * @param name The hostname for the client.
629 */
630 public void setClientName(String name) {
631 clientName = name;
632 }
633
634 /**
635 * @return The server's hostname
636 */
637 public String getServerName() {
638 if (serverName == null && serverIP != null) {
639 serverName = serverIP.getHostName();
640 }
641 return serverName;
642 }
643
644 /**
645 * @param name The server's hostname
646 */
647 public void setServerName(String name) {
648 serverName = name;
649 }
650
651 public String toString() {
652
653 String server = null;
654 if (serverIP != null) {
655 server = serverIP.getHostAddress();
656 }
657
658 String client = null;
659 if (clientIP != null) {
660 client = clientIP.getHostAddress();
661 }
662
663 String expiration = null;
664 if (this.expiration != null) {
665 expiration = this.expiration.toString();
666 }
667
668 String s = clientId + " " + String.valueOf(flags) + " "
669 + client + " " + server
670 + " " + expiration + " " + signature
671 + " " + macro + " " + comment;
672 return s;
673 }
674 }