1 /*
   2  * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.jmx.snmp.internal;
  26 
  27 import java.net.InetAddress;
  28 import java.net.UnknownHostException;
  29 import java.util.Hashtable;
  30 import java.util.logging.Level;
  31 import java.io.Serializable;
  32 
  33 import com.sun.jmx.snmp.SnmpDefinitions;
  34 import com.sun.jmx.snmp.SnmpEngineId;
  35 import com.sun.jmx.snmp.SnmpEngine;
  36 import com.sun.jmx.snmp.SnmpUsmKeyHandler;
  37 import com.sun.jmx.snmp.SnmpEngineFactory;
  38 import com.sun.jmx.snmp.SnmpUnknownModelException;
  39 
  40 import com.sun.jmx.snmp.internal.SnmpTools;
  41 import com.sun.jmx.snmp.SnmpBadSecurityLevelException;
  42 import static com.sun.jmx.defaults.JmxProperties.SNMP_LOGGER;
  43 
  44 /**
  45  * This engine is conformant with the RFC 2571. It is the main object within
  46  * an SNMP entity (agent, manager...).
  47  * To an engine is associated an {@link com.sun.jmx.snmp.SnmpEngineId}.
  48  * The way the engineId is retrieved is linked to the way the engine is
  49  * instantiated. See each <CODE>SnmpEngine</CODE> constructor for more details.
  50  * An engine is composed of a set of sub systems
  51  * {@link com.sun.jmx.snmp.internal.SnmpSubSystem}. An <CODE>SNMP</CODE>
  52  * engine can contain a:
  53  *<ul>
  54  *<li> Message Processing Sub System :
  55  * {@link com.sun.jmx.snmp.internal.SnmpMsgProcessingSubSystem}</li>
  56  *<li> Security Sub System :
  57  * {@link com.sun.jmx.snmp.internal.SnmpSecuritySubSystem} </li>
  58  *<li> Access Control Sub System :
  59  * {@link com.sun.jmx.snmp.internal.SnmpAccessControlSubSystem}</li>
  60  *</ul>
  61  *<P> Each sub system contains a set of models. A model is an implementation
  62  * of a particular treatement (eg: the User based Security Model defined in
  63  * RFC 2574 is a functional element dealing with authentication and privacy).
  64  *</P>
  65  * Engine instantiation is based on a factory. This factory, implementing
  66  * mandatorily {@link com.sun.jmx.snmp.SnmpEngineFactory  SnmpEngineFactory}
  67  * is set in the method <CODE>setFactory</CODE>.
  68  * <p><b>This API is a Sun Microsystems internal API  and is subject
  69  * to change without notice.</b></p>
  70  * @since 1.5
  71  */
  72 public class SnmpEngineImpl implements SnmpEngine, Serializable {
  73     private static final long serialVersionUID = -2564301391365614725L;
  74 
  75     /**
  76      * Security level. No authentication, no privacy. Value is 0,
  77      * as defined in RFC 2572
  78      */
  79     public static final int noAuthNoPriv = 0;
  80     /**
  81      * Security level. Authentication, no privacy. Value is 1, as
  82      * defined in RFC 2572
  83      */
  84     public static final int authNoPriv = 1;
  85     /**
  86      * Security level. Authentication, privacy. Value is 3,
  87      * as defined in RFC 2572
  88      */
  89     public static final int authPriv = 3;
  90     /**
  91      * Flag that indicates that a report is to be sent. Value is 4, as defined in RFC 2572
  92      */
  93     public static final int reportableFlag = 4;
  94 
  95     /**
  96      * Mask used to isolate authentication information within a message flag.
  97      */
  98     public static final int authMask = 1;
  99     /**
 100      * Mask used to isolate privacy information within a message flag.
 101      */
 102     public static final int privMask = 2;
 103     /**
 104      * Mask used to isolate authentication and privacy information within a message flag.
 105      */
 106     public static final int authPrivMask = 3;
 107 
 108     private SnmpEngineId engineid = null;
 109     private SnmpEngineFactory factory = null;
 110     private long startTime = 0;
 111 
 112     private int boot = 0;
 113     private boolean checkOid = false;
 114 
 115     transient private SnmpUsmKeyHandler usmKeyHandler = null;
 116     transient private SnmpLcd lcd = null;
 117 
 118     transient private SnmpSecuritySubSystem securitySub = null;
 119 
 120     transient private SnmpMsgProcessingSubSystem messageSub = null;
 121 
 122     transient private SnmpAccessControlSubSystem accessSub = null;
 123 
 124     /**
 125      * Gets the engine time in seconds. This is the time from the last reboot.
 126      * @return The time from the last reboot.
 127      */
 128     public synchronized int getEngineTime() {
 129         //We do the counter wrap in a lazt way. Each time Engine is asked for his time it checks. So if nobody use the Engine, the time can wrap and wrap again without incrementing nb boot. We can imagine that it is irrelevant due to the amount of time needed to wrap.
 130         long delta = (System.currentTimeMillis() / 1000) - startTime;
 131         if(delta >  0x7FFFFFFF) {
 132             //67 years of running. That is a great thing!
 133             //Reinitialize startTime.
 134             startTime = System.currentTimeMillis() / 1000;
 135 
 136             //Can't do anything with this counter.
 137             if(boot != 0x7FFFFFFF)
 138                 boot += 1;
 139             //Store for future use.
 140             storeNBBoots(boot);
 141         }
 142 
 143         return (int) ((System.currentTimeMillis() / 1000) - startTime);
 144     }
 145 
 146     /**
 147      * Gets the engine Id. This is unique for each engine.
 148      * @return The engine Id object.
 149      */
 150     public SnmpEngineId getEngineId() {
 151         return engineid;
 152     }
 153 
 154     /**
 155      * Gets the Usm key handler.
 156      * @return The key handler.
 157      */
 158     public SnmpUsmKeyHandler getUsmKeyHandler() {
 159         return usmKeyHandler;
 160     }
 161 
 162     /**
 163      * Gets the engine Lcd.
 164      * @return The engine Lcd.
 165      */
 166     public SnmpLcd getLcd() {
 167         return lcd;
 168     }
 169     /**
 170      * Gets the engine boot number. This is the number of time this engine has rebooted. Each time an <CODE>SnmpEngine</CODE> is instantiated, it will read this value in its Lcd, and store back the value incremented by one.
 171      * @return The engine's number of reboot.
 172      */
 173     public int getEngineBoots() {
 174         return boot;
 175     }
 176 
 177      /**
 178      * Constructor. A Local Configuration Datastore is passed to the engine. It will be used to store and retrieve data (engine Id, engine boots).
 179      * <P> WARNING : The SnmpEngineId is computed as follow:
 180      * <ul>
 181      * <li> If an lcd file is provided containing the property "localEngineID", this property value is used.</li>.
 182      * <li> If not, if the passed engineID is not null, this engine ID is used.</li>
 183      * <li> If not, a time based engineID is computed.</li>
 184      * </ul>
 185      * This constructor should be called by an <CODE>SnmpEngineFactory</CODE>. Don't call it directly.
 186      * @param fact The factory used to instantiate this engine.
 187      * @param lcd The local configuration datastore.
 188      * @param engineid The engine ID to use. If null is provided, an SnmpEngineId is computed using the current time.
 189      * @throws UnknownHostException Exception thrown, if the host name located in the property "localEngineID" is invalid.
 190      */
 191     public SnmpEngineImpl(SnmpEngineFactory fact,
 192                           SnmpLcd lcd,
 193                           SnmpEngineId engineid) throws UnknownHostException {
 194 
 195         init(lcd, fact);
 196         initEngineID();
 197         if(this.engineid == null) {
 198             if(engineid != null)
 199                 this.engineid = engineid;
 200             else
 201                 this.engineid = SnmpEngineId.createEngineId();
 202         }
 203         lcd.storeEngineId(this.engineid);
 204         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 205             SNMP_LOGGER.logp(Level.FINER, SnmpEngineImpl.class.getName(),
 206                     "SnmpEngineImpl(SnmpEngineFactory,SnmpLcd,SnmpEngineId)",
 207                     "LOCAL ENGINE ID: " + this.engineid);
 208         }
 209     }
 210     /**
 211      * Constructor. A Local Configuration Datastore is passed to the engine. It will be used to store and retrieve data (engine ID, engine boots).
 212      * <P> WARNING : The SnmpEngineId is computed as follow:
 213      * <ul>
 214      * <li> If an lcd file is provided containing the property "localEngineID", this property value is used.</li>.
 215      * <li> If not, the passed address and port are used to compute one.</li>
 216      * </ul>
 217      * This constructor should be called by an <CODE>SnmpEngineFactory</CODE>. Don't call it directly.
 218      * @param fact The factory used to instantiate this engine.
 219      * @param lcd The local configuration datastore.
 220      * @param port UDP port to use in order to calculate the engine ID.
 221      * @param address An IP address used to calculate the engine ID.
 222      * @throws UnknownHostException Exception thrown, if the host name located in the property "localEngineID" is invalid.
 223      */
 224     public SnmpEngineImpl(SnmpEngineFactory fact,
 225                           SnmpLcd lcd,
 226                           InetAddress address,
 227                           int port) throws UnknownHostException {
 228         init(lcd, fact);
 229         initEngineID();
 230         if(engineid == null)
 231             engineid = SnmpEngineId.createEngineId(address, port);
 232 
 233         lcd.storeEngineId(engineid);
 234 
 235         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 236             SNMP_LOGGER.logp(Level.FINER, SnmpEngineImpl.class.getName(),
 237                     "SnmpEngineImpl(SnmpEngineFactory,SnmpLcd,InetAddress,int)",
 238                     "LOCAL ENGINE ID: " + engineid + " / " +
 239                     "LOCAL ENGINE NB BOOTS: " + boot + " / " +
 240                     "LOCAL ENGINE START TIME: " + getEngineTime());
 241         }
 242     }
 243 
 244     /**
 245      * Constructor. A Local Configuration Datastore is passed to the engine. It will be used to store and retrieve data (engine ID, engine boots).
 246      * <P> WARNING : The SnmpEngineId is computed as follow:
 247      * <ul>
 248      * <li> If an lcd file is provided containing the property "localEngineID", this property value is used.</li>.
 249      * <li> If not, The passed port is used to compute one.</li>
 250      * </ul>
 251      * This constructor should be called by an <CODE>SnmpEngineFactory</CODE>. Don't call it directly.
 252      * @param fact The factory used to instantiate this engine.
 253      * @param lcd The local configuration datastore
 254      * @param port UDP port to use in order to calculate the engine ID.
 255      * @throws UnknownHostException Exception thrown, if the host name located in the property "localEngineID" is invalid.
 256      */
 257     public SnmpEngineImpl(SnmpEngineFactory fact,
 258                           SnmpLcd lcd,
 259                           int port) throws UnknownHostException {
 260         init(lcd, fact);
 261         initEngineID();
 262         if(engineid == null)
 263            engineid = SnmpEngineId.createEngineId(port);
 264 
 265         lcd.storeEngineId(engineid);
 266 
 267         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 268             SNMP_LOGGER.logp(Level.FINER, SnmpEngineImpl.class.getName(),
 269                     "SnmpEngineImpl(SnmpEngineFactory,SnmpLcd,int)",
 270                     "LOCAL ENGINE ID: " + engineid + " / " +
 271                     "LOCAL ENGINE NB BOOTS: " + boot + " / " +
 272                     "LOCAL ENGINE START TIME: " + getEngineTime());
 273         }
 274     }
 275 
 276     /**
 277      * Constructor. A Local Configuration Datastore is passed to the engine. It will be used to store and retrieve data (engine ID, engine boots).
 278      * <P> WARNING : The SnmpEngineId is computed as follow:
 279      * <ul>
 280      * <li> If an lcd file is provided containing the property "localEngineID", this property value is used.</li>.
 281      * <li> If not, a time based engineID is computed.</li>
 282      * </ul>
 283      * When no configuration nor java property is set for the engine ID value, a unique time based engine ID will be generated.
 284      * This constructor should be called by an <CODE>SnmpEngineFactory</CODE>. Don't call it directly.
 285      * @param fact The factory used to instantiate this engine.
 286      * @param lcd The local configuration datastore.
 287      */
 288     public SnmpEngineImpl(SnmpEngineFactory fact,
 289                           SnmpLcd lcd) throws UnknownHostException {
 290         init(lcd, fact);
 291         initEngineID();
 292         if(engineid == null)
 293             engineid = SnmpEngineId.createEngineId();
 294 
 295         lcd.storeEngineId(engineid);
 296 
 297         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 298             SNMP_LOGGER.logp(Level.FINER, SnmpEngineImpl.class.getName(),
 299                     "SnmpEngineImpl(SnmpEngineFactory,SnmpLcd)",
 300                     "LOCAL ENGINE ID: " + engineid + " / " +
 301                     "LOCAL ENGINE NB BOOTS: " + boot + " / " +
 302                     "LOCAL ENGINE START TIME: " + getEngineTime());
 303         }
 304     }
 305 
 306     /**
 307      * Access Control will check the oids. By default is false.
 308      */
 309     public synchronized void activateCheckOid() {
 310         checkOid = true;
 311     }
 312 
 313     /**
 314      * Access Control will not check the oids. By default is false.
 315      */
 316     public synchronized void deactivateCheckOid() {
 317         checkOid = false;
 318     }
 319 
 320     /**
 321      * Access Control check or not the oids. By default is false.
 322      */
 323     public synchronized boolean isCheckOidActivated() {
 324         return checkOid;
 325     }
 326 
 327     //Do some check and store the nb boots value.
 328     private void storeNBBoots(int boot) {
 329         if(boot < 0 || boot == 0x7FFFFFFF) {
 330             boot = 0x7FFFFFFF;
 331             lcd.storeEngineBoots(boot);
 332         }
 333         else
 334             lcd.storeEngineBoots(boot + 1);
 335     }
 336 
 337     // Initialize internal status.
 338     private void init(SnmpLcd lcd, SnmpEngineFactory fact) {
 339         this.factory = fact;
 340         this.lcd = lcd;
 341         boot = lcd.getEngineBoots();
 342 
 343         if(boot == -1 || boot == 0)
 344             boot = 1;
 345 
 346         storeNBBoots(boot);
 347 
 348         startTime = System.currentTimeMillis() / 1000;
 349 
 350     }
 351 
 352     void setUsmKeyHandler(SnmpUsmKeyHandler usmKeyHandler) {
 353         this.usmKeyHandler = usmKeyHandler;
 354     }
 355 
 356     //Initialize the engineID.
 357     private void initEngineID() throws UnknownHostException {
 358         String id = lcd.getEngineId();
 359         if(id != null) {
 360             engineid = SnmpEngineId.createEngineId(id);
 361         }
 362     }
 363 
 364 
 365     /**
 366      * Returns the Message Processing Sub System.
 367      * @return The Message Processing Sub System.
 368      */
 369     public SnmpMsgProcessingSubSystem getMsgProcessingSubSystem() {
 370         return messageSub;
 371     }
 372 
 373     /**
 374      * Sets the Message Processing Sub System.
 375      * @param sys The Message Processing Sub System.
 376      */
 377     public void setMsgProcessingSubSystem(SnmpMsgProcessingSubSystem sys) {
 378         messageSub = sys;
 379     }
 380 
 381      /**
 382      * Returns the Security Sub System.
 383      * @return The Security Sub System.
 384      */
 385     public SnmpSecuritySubSystem getSecuritySubSystem() {
 386         return securitySub;
 387     }
 388     /**
 389      * Sets the Security Sub System.
 390      * @param sys The Security Sub System.
 391      */
 392     public void setSecuritySubSystem(SnmpSecuritySubSystem sys) {
 393         securitySub = sys;
 394     }
 395      /**
 396      * Sets the Access Control Sub System.
 397      * @param sys The Access Control Sub System.
 398      */
 399     public void setAccessControlSubSystem(SnmpAccessControlSubSystem sys) {
 400         accessSub = sys;
 401     }
 402 
 403     /**
 404      * Returns the Access Control Sub System.
 405      * @return The Access Control Sub System.
 406      */
 407     public SnmpAccessControlSubSystem getAccessControlSubSystem() {
 408         return accessSub;
 409     }
 410     /**
 411      * Checks the passed msg flags according to the rules specified in RFC 2572.
 412      * @param msgFlags The msg flags.
 413      */
 414     public static void checkSecurityLevel(byte msgFlags)
 415         throws SnmpBadSecurityLevelException {
 416         int secLevel = msgFlags & SnmpDefinitions.authPriv;
 417         if((secLevel & SnmpDefinitions.privMask) != 0)
 418             if((secLevel & SnmpDefinitions.authMask) == 0) {
 419                 throw new SnmpBadSecurityLevelException("Security level:"+
 420                                                         " noAuthPriv!!!");
 421             }
 422     }
 423 
 424 }