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 }