Example #1
0
  // initialize Log4J logging
  static {
    try {
      Class.forName("org.apache.log4j.Logger");
      LogFactory.setLogFactory(new Log4jLogFactory());
    } catch (Exception e) {
      LogFactory.setLogFactory(new ConsoleLogFactory());
    }

    // Check to see if the pseudorandom byte generator device exists
    if (new File("/dev/urandom").exists()) {

      // If so, use it as the Java entropy gathering device so that we never
      // block on entropy gathering. Don't use the exact string "file:/dev/urandom"
      // because this is treated as a special value inside the JVM. Insert the
      // "." into the path to force it to use the real /dev/urandom device.
      //
      // @see https://bugs.openjdk.java.net/browse/JDK-6202721
      //
      System.setProperty("java.security.egd", "file:/dev/./urandom");
    }

    // Allow us to override default security protocols
    // SNMP4JSettings.setExtensibilityEnabled(true);
    // Override the default security protocols
    // System.setProperty(SecurityProtocols.SECURITY_PROTOCOLS_PROPERTIES,
    // "/org/opennms/mock/snmp/SecurityProtocols.properties");
  }
Example #2
0
/**
 * Class that holds a 64 bit salt value for crypto operations.
 *
 * <p>This class tries to use the SecureRandom class to initialize the salt value. If SecureRandom
 * is not available the class Random is used.
 *
 * @author Jochen Katz
 * @version 1.0
 */
class Salt {
  private long salt;

  private static Salt instance = null;
  private static final LogAdapter logger = LogFactory.getLogger(Salt.class);

  /** Default constructor, initializes the salt to a random value. */
  protected Salt() {
    byte[] rnd = new byte[8];

    try {
      SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
      sr.nextBytes(rnd);
    } catch (NoSuchAlgorithmException nsae) {
      logger.warn("Could not use SecureRandom. Using Random instead.");
      Random r = new Random();
      r.nextBytes(rnd);
    }

    salt = rnd[0];

    for (int i = 0; i < 7; i++) {
      salt = (salt * 256) + ((int) rnd[i]) + 128;
    }
    if (logger.isDebugEnabled() == true) {
      logger.debug("Initialized Salt to " + Long.toHexString(salt) + ".");
    }
  }

  /**
   * Get a initialized Salt object.
   *
   * @return the Salt object
   */
  public static Salt getInstance() {
    if (instance == null) {
      instance = new Salt();
    }
    return instance;
  }

  /**
   * Get the next value of the salt.
   *
   * @return previous value increased by one.
   */
  public synchronized long getNext() {
    return salt++;
  }
}
Example #3
0
  public static void main(String[] args) throws UnknownHostException, MalformedURLException {
    LogFactory.setLogFactory(new ConsoleLogFactory());
    AgentConfigData agentConfig = parseCli(args);
    if (agentConfig == null) {
      System.err.println("Could not parse configuration.");
      System.exit(1);
    }
    String listenSpec =
        agentConfig.getListenAddr().getHostAddress() + "/" + agentConfig.getListenPort();

    try {
      MockSnmpAgent.createAgentAndRun(agentConfig.getMoFile(), listenSpec);
    } catch (InterruptedException e) {
      System.exit(0);
    }
  }
Example #4
0
/**
 * The <code>SecurityProtocols</code> class holds all authentication and privacy protocols for a
 * SNMP entity.
 *
 * <p>To register security protocols other than the default, set the system property {@link
 * #SECURITY_PROTOCOLS_PROPERTIES} to a customized version of the <code>SecurityProtocols.properties
 * </code> file. The path has to be specified relatively to this class.
 *
 * @author Jochen Katz & Frank Fock
 * @version 1.9
 */
public class SecurityProtocols implements Serializable {

  private static final long serialVersionUID = 3800474900139635836L;

  private Hashtable<OID, AuthenticationProtocol> authProtocols;
  private Hashtable<OID, PrivacyProtocol> privProtocols;

  public static final String SECURITY_PROTOCOLS_PROPERTIES = "org.snmp4j.securityProtocols";
  private static final String SECURITY_PROTOCOLS_PROPERTIES_DEFAULT =
      "SecurityProtocols.properties";
  private static final LogAdapter logger = LogFactory.getLogger(SecurityProtocols.class);

  private static SecurityProtocols instance = null;
  private int maxAuthDigestLength = 0;
  private int maxPrivDecryptParamsLength = 0;

  protected SecurityProtocols() {
    authProtocols = new Hashtable<OID, AuthenticationProtocol>(5);
    privProtocols = new Hashtable<OID, PrivacyProtocol>(5);
  }

  /**
   * Get an instance of class SecurityProtocols.
   *
   * @return the globally used SecurityProtocols object.
   */
  public static SecurityProtocols getInstance() {
    if (instance == null) {
      instance = new SecurityProtocols();
    }
    return instance;
  }

  /**
   * Set the <code>SecurityProtocols</code>
   *
   * @param securityProtocols SecurityProtocols
   */
  public static void setSecurityProtocols(SecurityProtocols securityProtocols) {
    SecurityProtocols.instance = securityProtocols;
  }

  /**
   * Add the default SecurityProtocols.
   *
   * <p>The names of the SecurityProtocols to add are read from a properties file.
   *
   * @throws InternalError if the properties file cannot be opened/read.
   */
  public synchronized void addDefaultProtocols() {
    if (SNMP4JSettings.isExtensibilityEnabled()) {
      String secProtocols =
          System.getProperty(SECURITY_PROTOCOLS_PROPERTIES, SECURITY_PROTOCOLS_PROPERTIES_DEFAULT);
      InputStream is = SecurityProtocols.class.getResourceAsStream(secProtocols);
      if (is == null) {
        throw new InternalError("Could not read '" + secProtocols + "' from classpath!");
      }
      Properties props = new Properties();
      try {
        props.load(is);
        for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {
          String className = en.nextElement().toString();
          try {
            Class c = Class.forName(className);
            Object proto = c.newInstance();
            if (proto instanceof AuthenticationProtocol) {
              addAuthenticationProtocol((AuthenticationProtocol) proto);
            } else if (proto instanceof PrivacyProtocol) {
              addPrivacyProtocol((PrivacyProtocol) proto);
            } else {
              logger.error(
                  "Failed to register security protocol because it does "
                      + "not implement required interfaces: "
                      + className);
            }
          } catch (Exception cnfe) {
            logger.error(cnfe);
            throw new InternalError(cnfe.toString());
          }
        }
      } catch (IOException iox) {
        String txt = "Could not read '" + secProtocols + "': " + iox.getMessage();
        logger.error(txt);
        throw new InternalError(txt);
      } finally {
        try {
          is.close();
        } catch (IOException ex) {
          // ignore
          logger.warn(ex);
        }
      }
    } else {
      addAuthenticationProtocol(new AuthMD5());
      addAuthenticationProtocol(new AuthSHA());
      addPrivacyProtocol(new PrivDES());
      addPrivacyProtocol(new PrivAES128());
      addPrivacyProtocol(new PrivAES192());
      addPrivacyProtocol(new PrivAES256());
    }
  }

  /**
   * Add the given {@link AuthenticationProtocol}. If an authentication protocol with the supplied
   * ID already exists, the supplied authentication protocol will not be added and the security
   * protocols will not be unchang.
   *
   * @param auth the AuthenticationProtocol to add (an existing authentication protcol with <code>
   *     auth</code>'s ID remains unchanged).
   */
  public synchronized void addAuthenticationProtocol(AuthenticationProtocol auth) {
    if (authProtocols.get(auth.getID()) == null) {
      authProtocols.put(auth.getID(), auth);
      if (auth.getDigestLength() > maxAuthDigestLength) {
        maxAuthDigestLength = auth.getDigestLength();
      }
    }
  }

  /**
   * Get the {@link AuthenticationProtocol} with the given ID.
   *
   * @param id The unique ID (specified as {@link OID}) of the AuthenticationProtocol.
   * @return the AuthenticationProtocol object if it was added before, or null if not.
   */
  public AuthenticationProtocol getAuthenticationProtocol(OID id) {
    if (id == null) {
      return null;
    }
    return authProtocols.get(id);
  }

  /**
   * Remove the given {@link AuthenticationProtocol}.
   *
   * @param auth The protocol to remove
   */
  public void removeAuthenticationProtocol(AuthenticationProtocol auth) {
    authProtocols.remove(auth.getID());
  }

  /**
   * Add the given {@link PrivacyProtocol}. If a privacy protocol with the supplied ID already
   * exists, the supplied privacy protocol will not be added and the security protocols will not be
   * changed.
   *
   * @param priv the PrivacyProtocol to add (an existing privacy protcol with <code>priv</code>'s ID
   *     remains unchanged).
   */
  public synchronized void addPrivacyProtocol(PrivacyProtocol priv) {
    if (privProtocols.get(priv.getID()) == null) {
      privProtocols.put(priv.getID(), priv);
      if (priv.getDecryptParamsLength() > maxPrivDecryptParamsLength) {
        maxPrivDecryptParamsLength = priv.getDecryptParamsLength();
      }
    }
  }

  /**
   * Get the PrivacyProtocol with the given ID.
   *
   * @param id The unique ID (specified as {@link OID}) of the PrivacyProtocol.
   * @return the {@link PrivacyProtocol} object if it was added before, or null if not.
   */
  public PrivacyProtocol getPrivacyProtocol(OID id) {
    if (id == null) {
      return null;
    }
    return privProtocols.get(id);
  }

  /**
   * Remove the given {@link PrivacyProtocol}.
   *
   * @param priv The protocol to remove
   */
  public void removePrivacyProtocol(PrivacyProtocol priv) {
    privProtocols.remove(priv.getID());
  }

  /**
   * Generates the localized key for the given password and engine id for the authentication
   * protocol specified by the supplied OID.
   *
   * @param authProtocolID an <code>OID</code> identifying the authentication protocol to use.
   * @param passwordString the authentication pass phrase.
   * @param engineID the engine ID of the authoritative engine.
   * @return the localized authentication key.
   */
  public byte[] passwordToKey(OID authProtocolID, OctetString passwordString, byte[] engineID) {

    AuthenticationProtocol protocol = authProtocols.get(authProtocolID);
    if (protocol == null) {
      return null;
    }
    return protocol.passwordToKey(passwordString, engineID);
  }

  /**
   * Generates the localized key for the given password and engine id for the privacy protocol
   * specified by the supplied OID.
   *
   * @param privProtocolID an <code>OID</code> identifying the privacy protocol the key should be
   *     created for.
   * @param authProtocolID an <code>OID</code> identifying the authentication protocol to use.
   * @param passwordString the authentication pass phrase.
   * @param engineID the engine ID of the authoritative engine.
   * @return the localized privacy key.
   */
  public byte[] passwordToKey(
      OID privProtocolID, OID authProtocolID, OctetString passwordString, byte[] engineID) {

    AuthenticationProtocol authProtocol = authProtocols.get(authProtocolID);
    if (authProtocol == null) {
      return null;
    }
    PrivacyProtocol privProtocol = privProtocols.get(privProtocolID);
    if (privProtocol == null) {
      return null;
    }
    byte[] key = authProtocol.passwordToKey(passwordString, engineID);

    if (key == null) {
      return null;
    }
    if (key.length >= privProtocol.getMinKeyLength()) {
      if (key.length > privProtocol.getMaxKeyLength()) {
        // truncate key
        byte[] truncatedKey = new byte[privProtocol.getMaxKeyLength()];
        System.arraycopy(key, 0, truncatedKey, 0, privProtocol.getMaxKeyLength());
        return truncatedKey;
      }
      return key;
    }
    // extend key if necessary
    byte[] extKey = privProtocol.extendShortKey(key, passwordString, engineID, authProtocol);
    return extKey;
  }

  /**
   * Gets the maximum authentication key length of the all known authentication protocols.
   *
   * @return the maximum authentication key length of all authentication protocols that have been
   *     added to this <code>SecurityProtocols</code> instance.
   */
  public int getMaxAuthDigestLength() {
    return maxAuthDigestLength;
  }

  /**
   * Gets the maximum privacy key length of the currently known privacy protocols.
   *
   * @return the maximum privacy key length of all privacy protocols that have been added to this
   *     <code>SecurityProtocols</code> instance.
   */
  public int getMaxPrivDecryptParamsLength() {
    return maxPrivDecryptParamsLength;
  }

  /**
   * Limits the supplied key value to the specified maximum length
   *
   * @param key the key to truncate.
   * @param maxKeyLength the maximum length of the returned key.
   * @return the truncated key with a length of <code>min(key.length, maxKeyLength)</code>.
   * @since 1.9
   */
  public byte[] truncateKey(byte[] key, int maxKeyLength) {
    byte[] truncatedNewKey = new byte[Math.min(maxKeyLength, key.length)];
    System.arraycopy(key, 0, truncatedNewKey, 0, truncatedNewKey.length);
    return truncatedNewKey;
  }
}
Example #5
0
 static {
   LogFactory.setLogFactory(new JavaLogFactory());
 }
Example #6
0
public class SampleAgent implements VariableProvider {
  static {
    LogFactory.setLogFactory(new JavaLogFactory());
  }

  private LogAdapter logger = LogFactory.getLogger(SampleAgent.class);

  protected AgentConfigManager agent;
  protected MOServer server;
  private String configFile;
  private File bootCounterFile;

  // supported MIBs
  protected HelloModules modules;

  protected Properties tableSizeLimits;

  public SampleAgent(Map args) {
    configFile = (String) ((List) args.get("c")).get(0);
    bootCounterFile = new File((String) ((List) args.get("bc")).get(0));

    server = new DefaultMOServer();
    MOServer[] moServers = new MOServer[] {server};
    InputStream configInputStream =
        SampleAgent.class.getResourceAsStream("SampleAgentConfig.properties");
    if (args.containsKey("cfg")) {
      try {
        configInputStream = new FileInputStream((String) ArgumentParser.getValue(args, "cfg", 0));
      } catch (FileNotFoundException ex1) {
        ex1.printStackTrace();
      }
    }
    final Properties props = new Properties();
    try {
      props.load(configInputStream);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    MOInputFactory configurationFactory =
        new MOInputFactory() {
          public MOInput createMOInput() {
            return new PropertyMOInput(props, SampleAgent.this);
          }
        };
    InputStream tableSizeLimitsInputStream =
        SampleAgent.class.getResourceAsStream("SampleAgentTableSizeLimits.properties");
    if (args.containsKey("ts")) {
      try {
        tableSizeLimitsInputStream =
            new FileInputStream((String) ArgumentParser.getValue(args, "ts", 0));
      } catch (FileNotFoundException ex1) {
        ex1.printStackTrace();
      }
    }
    tableSizeLimits = new Properties();
    try {
      tableSizeLimits.load(tableSizeLimitsInputStream);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
    addListenAddresses(messageDispatcher, (List) args.get("address"));
    agent =
        new AgentConfigManager(
            new OctetString(MPv3.createLocalEngineID()),
            messageDispatcher,
            null,
            moServers,
            ThreadPool.create("SampleAgent", 3),
            configurationFactory,
            new DefaultMOPersistenceProvider(moServers, configFile),
            new EngineBootsCounterFile(bootCounterFile));
  }

  protected void addListenAddresses(MessageDispatcher md, List addresses) {
    for (Iterator it = addresses.iterator(); it.hasNext(); ) {
      Address address = GenericAddress.parse((String) it.next());
      TransportMapping tm = TransportMappings.getInstance().createTransportMapping(address);
      if (tm != null) {
        md.addTransportMapping(tm);
      } else {
        logger.warn("No transport mapping available for address '" + address + "'.");
      }
    }
  }

  public void run() {
    // initialize agent before registering our own modules
    agent.initialize();
    // this requires sysUpTime to be available.
    registerMIBs();
    // add proxy forwarder
    agent.setupProxyForwarder();
    // apply table size limits
    agent.setTableSizeLimits(tableSizeLimits);
    // now continue agent setup and launch it.
    agent.run();
  }

  /**
   * Get the {@link MOFactory} that creates the various MOs (MIB Objects).
   *
   * @return a {@link DefaultMOFactory} instance by default.
   * @since 1.3.2
   */
  protected MOFactory getFactory() {
    return DefaultMOFactory.getInstance();
  }

  /**
   * Register your own MIB modules in the specified context of the agent. The {@link MOFactory}
   * provided to the <code>Modules</code> constructor is returned by {@link #getFactory()}.
   */
  protected void registerMIBs() {

    try {
      modules = new HelloModules(getFactory());

      modules.registerMOs(server, null);
      // Some alternatives
      // Register a scalar with your OID in your enterprise subtree:
      /*
       * MOScalar myScalar = new MOScalar(new OID(new int[] {
       * 1,3,6,1,4,1,1,0 }), MOAccessImpl.ACCESS_READ_WRITE, new
       * OctetString("myText"));
       *
       * server.register(myScalar, null);
       */
      // Register a table with a string index and a single integer payload
      // column
      // a row status column to
      /*
       * DefaultMOTable myTable = new DefaultMOTable(new
       * OID("<tableEntryOID>"), new MOTableIndex(new MOTableSubIndex[] {
       * new MOTableSubIndex(new OID("<indexObjectClassOID>"),
       * SMIConstants.SYNTAX_OCTET_STRING, 1, 16) }, true), new
       * MOMutableColumn[] { new MOMutableColumn(1,
       * SMIConstants.SYNTAX_INTEGER32, MOAccessImpl.ACCESS_READ_CREATE,
       * new Integer32(10), true), new RowStatus(2) });
       * server.register(myTable, null);
       */

    } catch (DuplicateRegistrationException drex) {
      logger.error(
          "Duplicate registration: "
              + drex.getMessage()
              + "."
              + " MIB object registration may be incomplete!",
          drex);
    }
  }

  public Variable getVariable(String name) {
    OID oid;
    OctetString context = null;
    int pos = name.indexOf(':');
    if (pos >= 0) {
      context = new OctetString(name.substring(0, pos));
      oid = new OID(name.substring(pos + 1, name.length()));
    } else {
      oid = new OID(name);
    }
    final DefaultMOContextScope scope = new DefaultMOContextScope(context, oid, true, oid, true);
    MOQuery query = new DefaultMOQuery(scope, false, this);
    ManagedObject mo = server.lookup(query);
    if (mo != null) {
      final VariableBinding vb = new VariableBinding(oid);
      final RequestStatus status = new RequestStatus();
      SubRequest req =
          new SubRequest() {
            private boolean completed;
            private MOQuery query;

            public boolean hasError() {
              return false;
            }

            public void setErrorStatus(int errorStatus) {
              status.setErrorStatus(errorStatus);
            }

            public int getErrorStatus() {
              return status.getErrorStatus();
            }

            public RequestStatus getStatus() {
              return status;
            }

            public MOScope getScope() {
              return scope;
            }

            public VariableBinding getVariableBinding() {
              return vb;
            }

            public Request getRequest() {
              return null;
            }

            public Object getUndoValue() {
              return null;
            }

            public void setUndoValue(Object undoInformation) {}

            public void completed() {
              completed = true;
            }

            public boolean isComplete() {
              return completed;
            }

            public void setTargetMO(ManagedObject managedObject) {}

            public ManagedObject getTargetMO() {
              return null;
            }

            public int getIndex() {
              return 0;
            }

            public void setQuery(MOQuery query) {
              this.query = query;
            }

            public MOQuery getQuery() {
              return query;
            }

            public SubRequestIterator repetitions() {
              return null;
            }

            public void updateNextRepetition() {}

            public Object getUserObject() {
              return null;
            }

            public void setUserObject(Object userObject) {}
          };
      mo.get(req);
      return vb.getVariable();
    }
    return null;
  }

  class DemoTableRowListener implements MOTableRowListener {
    public void rowChanged(MOTableRowEvent event) {
      if ((event.getType() == MOTableRowEvent.CREATE)
          || (event.getType() == MOTableRowEvent.UPDATED)) {
        // ignore
        return;
      }
      // update counter
      Counter32 counter = (Counter32) event.getRow().getValue(Snmp4jDemoMib.idxSnmp4jDemoEntryCol3);
      if (counter == null) {
        counter = new Counter32(0);
        ((MOMutableTableRow) event.getRow())
            .setValue(Snmp4jDemoMib.idxSnmp4jDemoEntryCol3, counter);
      }
      counter.increment();
      // update timestamp
      TimeStamp timestamp =
          (TimeStamp) event.getTable().getColumn(Snmp4jDemoMib.idxSnmp4jDemoEntryCol4);
      timestamp.update((MOMutableTableRow) event.getRow(), Snmp4jDemoMib.idxSnmp4jDemoEntryCol4);
      // fire notification
      Integer32 type = new Integer32(Snmp4jDemoMib.Snmp4jDemoTableRowModificationEnum.updated);
      switch (event.getType()) {
        case MOTableRowEvent.ADD:
          type.setValue(Snmp4jDemoMib.Snmp4jDemoTableRowModificationEnum.created);
          break;
        case MOTableRowEvent.DELETE:
          type.setValue(Snmp4jDemoMib.Snmp4jDemoTableRowModificationEnum.deleted);
          break;
      }
      VariableBinding[] payload = new VariableBinding[2];
      OID table = event.getTable().getOID();
      OID updateCount = new OID(table);
      updateCount.append(Snmp4jDemoMib.colSnmp4jDemoEntryCol3);
      updateCount.append(event.getRow().getIndex());

      OID modifyType = new OID(table);
      modifyType.append(Snmp4jDemoMib.colSnmp4jDemoTableRowModification);
      modifyType.append(event.getRow().getIndex());

      payload[0] = new VariableBinding(updateCount, counter);
      payload[1] = new VariableBinding(modifyType, type);
      // modules.getSnmp4jDemoMib().snmp4jDemoEvent(
      // agent.getNotificationOriginator(), new OctetString(), payload);
    }
  }

  /**
   * Runs a sample agent with a default configuration defined by <code>SampleAgentConfig.properties
   * </code>. A sample command line is:
   *
   * <pre>
   * -c SampleAgent.cfg -bc SampleAgent.bc udp:127.0.0.1/4700 tcp:127.0.0.1/4700
   * </pre>
   *
   * @param args the command line arguments defining at least the listen addresses. The format is
   *     <code>-c[s{=SampleAgent.cfg}] -bc[s{=SampleAgent.bc}]
   *    +ts[s] +cfg[s] #address[s<(udp|tcp):.*[/[0-9]+]?>] ..</code>. For the format description see
   *     {@link ArgumentParser}.
   */
  public static void main(String[] args) {
    ArgumentParser parser =
        new ArgumentParser(
            "-c[s{=SampleAgent.cfg}] -bc[s{=SampleAgent.bc}] " + "+ts[s] +cfg[s]",
            "#address[s<(udp|tcp):.*[/[0-9]+]?>] ..");
    Map commandLineParameters = null;
    try {
      args = new String[1];
      args[0] = "udp:127.0.0.1/4700";
      commandLineParameters = parser.parse(args);
      SampleAgent sampleAgent = new SampleAgent(commandLineParameters);
      // Add all available security protocols (e.g.
      // SHA,MD5,DES,AES,3DES,..)
      SecurityProtocols.getInstance().addDefaultProtocols();
      // configure system group:
      // Set system description:
      // sampleAgent.agent.getSysDescr().setValue("My system
      // description".getBytes());
      // Set system OID (= OID of the AGENT-CAPABILITIES statement
      // describing
      // the implemented MIB objects of this agent:
      // sampleAgent.agent.getSysOID().setValue("1.3.1.6.1.4.1....");
      // Set the system services
      // sampleAgent.agent.getSysServices().setValue(72);
      sampleAgent.run();

    } catch (ParseException ex) {
      System.err.println(ex.getMessage());
    }
  }
}
public final class PhysicalNetworkInterface extends PacketReceiver implements NetworkInterface {

  private static final LogAdapter logger = LogFactory.getLogger(PhysicalNetworkInterface.class);

  private final MacAddress macAddress;
  private final boolean trunk;
  private final List<NifIpAddress> ipAddresses =
      Collections.synchronizedList(new ArrayList<NifIpAddress>());
  private final PacketListener host;
  private final List<PacketListener> users =
      Collections.synchronizedList(new ArrayList<PacketListener>());

  private volatile BlockingQueue<PacketContainer> sendPacketQueue = null;

  public PhysicalNetworkInterface(
      String name, MacAddress macAddress, boolean trunk, PacketListener host) {
    super(name);
    this.macAddress = macAddress;
    this.trunk = trunk;
    this.host = host;
  }

  public MacAddress getMacAddress() {
    return macAddress;
  }

  public boolean isTrunk() {
    return trunk;
  }

  public List<NifIpAddress> getIpAddresses() {
    return new ArrayList<NifIpAddress>(ipAddresses);
  }

  public void addIpAddress(NifIpAddress addr) {
    ipAddresses.add(addr);
  }

  public void addUser(PacketListener user) {
    users.add(user);
  }

  void setSendPacketQueue(BlockingQueue<PacketContainer> sendPacketQueue) {
    this.sendPacketQueue = sendPacketQueue;
  }

  public void sendPacket(Packet packet) throws SendPacketException {
    if (!isRunning()) {
      if (logger.isDebugEnabled()) {
        logger.warn("Not running. Can't send a packet: " + packet);
        throw new SendPacketException();
      }
    }

    BlockingQueue<PacketContainer> q = sendPacketQueue;
    if (q == null) {
      logger.warn("Not yet be connected. Dropped a packet: " + packet);
      throw new SendPacketException();
    }

    boolean offered = q.offer(new PacketContainer(packet, this));
    if (offered) {
      if (logger.isDebugEnabled()) {
        logger.debug("Sent a packet: " + packet);
      }
    } else {
      logger.error("Couldn't send a packet: " + packet);
      throw new SendPacketException();
    }
  }

  @Override
  protected void process(PacketContainer pc) {
    Packet packet = pc.getPacket();

    if (logger.isDebugEnabled()) {
      StringBuilder sb = new StringBuilder();
      sb.append("Received a packet from ");
      if (pc.getSrc() != null) {
        sb.append(pc.getSrc().getName());
      } else {
        sb.append("a L2 connection");
      }
      sb.append(": ").append(packet);
      logger.debug(sb.toString());
    }

    for (PacketListener user : users) {
      user.gotPacket(packet);
    }

    if (EthernetHelper.matchesDestination(packet, macAddress)) {
      host.gotPacket(packet);
    } else {
      if (logger.isDebugEnabled()) {
        StringBuilder sb = new StringBuilder();
        sb.append("Dropped a packet not to me(").append(macAddress).append("): ").append(packet);
        logger.debug(sb.toString());
      }
    }
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(this.getClass().getSimpleName())
        .append("{")
        .append("name[")
        .append(getName())
        .append("] ")
        .append("macAddress[")
        .append(macAddress)
        .append("]");
    for (NifIpAddress ipAddr : ipAddresses) {
      sb.append("ipAddress[").append(ipAddr).append("]");
    }
    sb.append("}");
    return sb.toString();
  }
}
/**
 * The <code>DefaultUdpTransportMapping</code> implements a UDP transport mapping based on Java
 * standard IO and using an internal thread for listening on the inbound socket.
 *
 * @author Frank Fock
 * @version 1.9
 */
public class DefaultUdpTransportMapping extends UdpTransportMapping {

  private static final LogAdapter logger = LogFactory.getLogger(DefaultUdpTransportMapping.class);

  protected DatagramSocket socket = null;
  protected WorkerTask listener;
  protected ListenThread listenerThread;
  private int socketTimeout = 0;

  private int receiveBufferSize = 0; // not set by default

  /**
   * Creates a UDP transport with an arbitrary local port on all local interfaces.
   *
   * @throws IOException if socket binding fails.
   */
  public DefaultUdpTransportMapping() throws IOException {
    super(new UdpAddress(InetAddress.getLocalHost(), 0));
    socket = new DatagramSocket(udpAddress.getPort());
  }

  /**
   * Creates a UDP transport with optional reusing the address if is currently in timeout state
   * (TIME_WAIT) after the connection is closed.
   *
   * @param udpAddress the local address for sending and receiving of UDP messages.
   * @param reuseAddress if <code>true</code> addresses are reused which provides faster socket
   *     binding if an application is restarted for instance.
   * @throws IOException if socket binding fails.
   * @since 1.7.3
   */
  public DefaultUdpTransportMapping(UdpAddress udpAddress, boolean reuseAddress)
      throws IOException {
    super(udpAddress);
    socket = new DatagramSocket(null);
    socket.setReuseAddress(reuseAddress);
    final SocketAddress addr =
        new InetSocketAddress(udpAddress.getInetAddress(), udpAddress.getPort());
    socket.bind(addr);
  }

  /**
   * Creates a UDP transport on the specified address. The address will not be reused if it is
   * currently in timeout state (TIME_WAIT).
   *
   * @param udpAddress the local address for sending and receiving of UDP messages.
   * @throws IOException if socket binding fails.
   */
  public DefaultUdpTransportMapping(UdpAddress udpAddress) throws IOException {
    super(udpAddress);
    socket = new DatagramSocket(udpAddress.getPort(), udpAddress.getInetAddress());
  }

  public void sendMessage(
      UdpAddress targetAddress, byte[] message, TransportStateReference tmStateReference)
      throws java.io.IOException {
    InetSocketAddress targetSocketAddress =
        new InetSocketAddress(targetAddress.getInetAddress(), targetAddress.getPort());
    if (logger.isDebugEnabled()) {
      logger.debug(
          "Sending message to "
              + targetAddress
              + " with length "
              + message.length
              + ": "
              + new OctetString(message).toHexString());
    }
    DatagramSocket s = ensureSocket();
    s.send(new DatagramPacket(message, message.length, targetSocketAddress));
  }

  /**
   * Closes the socket and stops the listener thread.
   *
   * @throws IOException
   */
  public void close() throws IOException {
    boolean interrupted = false;
    WorkerTask l = listener;
    if (l != null) {
      l.terminate();
      l.interrupt();
      if (socketTimeout > 0) {
        try {
          l.join();
        } catch (InterruptedException ex) {
          interrupted = true;
          logger.warn(ex);
        }
      }
      listener = null;
    }
    DatagramSocket closingSocket = socket;
    if ((closingSocket != null) && (!closingSocket.isClosed())) {
      closingSocket.close();
    }
    socket = null;
    if (interrupted) {
      Thread.currentThread().interrupt();
    }
  }

  /**
   * Starts the listener thread that accepts incoming messages. The thread is started in daemon mode
   * and thus it will not block application terminated. Nevertheless, the {@link #close()} method
   * should be called to stop the listen thread gracefully and free associated ressources.
   *
   * @throws IOException
   */
  public synchronized void listen() throws IOException {
    if (listener != null) {
      throw new SocketException("Port already listening");
    }
    ensureSocket();
    listenerThread = new ListenThread();
    listener =
        SNMP4JSettings.getThreadFactory()
            .createWorkerThread("DefaultUDPTransportMapping_" + getAddress(), listenerThread, true);
    listener.run();
  }

  private synchronized DatagramSocket ensureSocket() throws SocketException {
    DatagramSocket s = socket;
    if (s == null) {
      s = new DatagramSocket(udpAddress.getPort());
      s.setSoTimeout(socketTimeout);
      this.socket = s;
    }
    return s;
  }

  /**
   * Changes the priority of the listen thread for this UDP transport mapping. This method has no
   * effect, if called before {@link #listen()} has been called for this transport mapping.
   *
   * @param newPriority the new priority.
   * @see Thread#setPriority(int)
   * @since 1.2.2
   */
  public void setPriority(int newPriority) {
    WorkerTask lt = listener;
    if (lt instanceof Thread) {
      ((Thread) lt).setPriority(newPriority);
    }
  }

  /**
   * Returns the priority of the internal listen thread.
   *
   * @return a value between {@link Thread#MIN_PRIORITY} and {@link Thread#MAX_PRIORITY}.
   * @since 1.2.2
   */
  public int getPriority() {
    WorkerTask lt = listener;
    if (lt instanceof Thread) {
      return ((Thread) lt).getPriority();
    } else {
      return Thread.NORM_PRIORITY;
    }
  }

  /**
   * Sets the name of the listen thread for this UDP transport mapping. This method has no effect,
   * if called before {@link #listen()} has been called for this transport mapping.
   *
   * @param name the new thread name.
   * @since 1.6
   */
  public void setThreadName(String name) {
    WorkerTask lt = listener;
    if (lt instanceof Thread) {
      ((Thread) lt).setName(name);
    }
  }

  /**
   * Returns the name of the listen thread.
   *
   * @return the thread name if in listening mode, otherwise <code>null</code>.
   * @since 1.6
   */
  public String getThreadName() {
    WorkerTask lt = listener;
    if (lt instanceof Thread) {
      return ((Thread) lt).getName();
    } else {
      return null;
    }
  }

  public void setMaxInboundMessageSize(int maxInboundMessageSize) {
    this.maxInboundMessageSize = maxInboundMessageSize;
  }

  /**
   * Returns the socket timeout. 0 returns implies that the option is disabled (i.e., timeout of
   * infinity).
   *
   * @return the socket timeout setting.
   */
  public int getSocketTimeout() {
    return socketTimeout;
  }

  /**
   * Gets the requested receive buffer size for the underlying UDP socket. This size might not
   * reflect the actual size of the receive buffer, which is implementation specific.
   *
   * @return <=0 if the default buffer size of the OS is used, or a value >0 if the user specified a
   *     buffer size.
   */
  public int getReceiveBufferSize() {
    return receiveBufferSize;
  }

  /**
   * Sets the receive buffer size, which should be > the maximum inbound message size. This method
   * has to be called before {@link #listen()} to be effective.
   *
   * @param receiveBufferSize an integer value >0 and > {@link #getMaxInboundMessageSize()}.
   */
  public void setReceiveBufferSize(int receiveBufferSize) {
    if (receiveBufferSize <= 0) {
      throw new IllegalArgumentException("Receive buffer size must be > 0");
    }
    this.receiveBufferSize = receiveBufferSize;
  }

  /**
   * Sets the socket timeout in milliseconds.
   *
   * @param socketTimeout the socket timeout for incoming messages in milliseconds. A timeout of
   *     zero is interpreted as an infinite timeout.
   */
  public void setSocketTimeout(int socketTimeout) {
    this.socketTimeout = socketTimeout;
    if (socket != null) {
      try {
        socket.setSoTimeout(socketTimeout);
      } catch (SocketException ex) {
        throw new RuntimeException(ex);
      }
    }
  }

  public boolean isListening() {
    return (listener != null);
  }

  class ListenThread implements WorkerTask {

    private byte[] buf;
    private volatile boolean stop = false;

    public ListenThread() throws SocketException {
      buf = new byte[getMaxInboundMessageSize()];
    }

    public void run() {
      DatagramSocket socketCopy = socket;
      if (socketCopy != null) {
        try {
          socketCopy.setSoTimeout(getSocketTimeout());
          if (receiveBufferSize > 0) {
            socketCopy.setReceiveBufferSize(Math.max(receiveBufferSize, maxInboundMessageSize));
          }
          if (logger.isDebugEnabled()) {
            logger.debug(
                "UDP receive buffer size for socket "
                    + getAddress()
                    + " is set to: "
                    + socketCopy.getReceiveBufferSize());
          }
        } catch (SocketException ex) {
          logger.error(ex);
          setSocketTimeout(0);
        }
      }
      while (!stop) {
        DatagramPacket packet =
            new DatagramPacket(buf, buf.length, udpAddress.getInetAddress(), udpAddress.getPort());
        try {
          socketCopy = socket;
          try {
            if (socketCopy == null) {
              stop = true;
              continue;
            }
            socketCopy.receive(packet);
          } catch (InterruptedIOException iiox) {
            if (iiox.bytesTransferred <= 0) {
              continue;
            }
          }
          if (logger.isDebugEnabled()) {
            logger.debug(
                "Received message from "
                    + packet.getAddress()
                    + "/"
                    + packet.getPort()
                    + " with length "
                    + packet.getLength()
                    + ": "
                    + new OctetString(packet.getData(), 0, packet.getLength()).toHexString());
          }
          ByteBuffer bis;
          // If messages are processed asynchronously (i.e. multi-threaded)
          // then we have to copy the buffer's content here!
          if (isAsyncMsgProcessingSupported()) {
            byte[] bytes = new byte[packet.getLength()];
            System.arraycopy(packet.getData(), 0, bytes, 0, bytes.length);
            bis = ByteBuffer.wrap(bytes);
          } else {
            bis = ByteBuffer.wrap(packet.getData());
          }
          TransportStateReference stateReference =
              new TransportStateReference(
                  DefaultUdpTransportMapping.this,
                  udpAddress,
                  null,
                  SecurityLevel.undefined,
                  SecurityLevel.undefined,
                  false,
                  socketCopy);
          fireProcessMessage(
              new UdpAddress(packet.getAddress(), packet.getPort()), bis, stateReference);
        } catch (SocketTimeoutException stex) {
          // ignore
        } catch (PortUnreachableException purex) {
          synchronized (DefaultUdpTransportMapping.this) {
            listener = null;
          }
          logger.error(purex);
          if (logger.isDebugEnabled()) {
            purex.printStackTrace();
          }
          if (SNMP4JSettings.isFowardRuntimeExceptions()) {
            throw new RuntimeException(purex);
          }
          break;
        } catch (SocketException soex) {
          if (!stop) {
            logger.error(
                "Socket for transport mapping " + toString() + " error: " + soex.getMessage(),
                soex);
          }
          if (SNMP4JSettings.isFowardRuntimeExceptions()) {
            stop = true;
            throw new RuntimeException(soex);
          }
        } catch (IOException iox) {
          logger.warn(iox);
          if (logger.isDebugEnabled()) {
            iox.printStackTrace();
          }
          if (SNMP4JSettings.isFowardRuntimeExceptions()) {
            throw new RuntimeException(iox);
          }
        }
      }
      synchronized (DefaultUdpTransportMapping.this) {
        listener = null;
        stop = true;
        DatagramSocket closingSocket = socket;
        if ((closingSocket != null) && (!closingSocket.isClosed())) {
          closingSocket.close();
        }
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Worker task stopped:" + getClass().getName());
      }
    }

    public void close() {
      stop = true;
    }

    public void terminate() {
      close();
      if (logger.isDebugEnabled()) {
        logger.debug("Terminated worker task: " + getClass().getName());
      }
    }

    public void join() throws InterruptedException {
      if (logger.isDebugEnabled()) {
        logger.debug("Joining worker task: " + getClass().getName());
      }
    }

    public void interrupt() {
      if (logger.isDebugEnabled()) {
        logger.debug("Interrupting worker task: " + getClass().getName());
      }
      close();
    }
  }
}
/**
 * The <code>DefaultTcpTransportMapping</code> implements a TCP transport mapping with the Java 1.4
 * new IO API.
 *
 * <p>It uses a single thread for processing incoming and outgoing messages. The thread is started
 * when the <code>listen</code> method is called, or when an outgoing request is sent using the
 * <code>sendMessage</code> method.
 *
 * @author Frank Fock
 * @version 1.7.4a
 */
public class DefaultTcpTransportMapping extends TcpTransportMapping {

  private static final LogAdapter logger = LogFactory.getLogger(DefaultTcpTransportMapping.class);

  private Hashtable sockets = new Hashtable();
  private ServerThread server;

  private Timer socketCleaner;
  // 1 minute default timeout
  private long connectionTimeout = 60000;
  private boolean serverEnabled = false;

  private static final int MIN_SNMP_HEADER_LENGTH = 6;
  private MessageLengthDecoder messageLengthDecoder = new SnmpMesssageLengthDecoder();

  /**
   * Creates a default TCP transport mapping with the server for incoming messages disabled.
   *
   * @throws UnknownHostException
   * @throws IOException on failure of binding a local port.
   */
  public DefaultTcpTransportMapping() throws UnknownHostException, IOException {
    super(new TcpAddress(InetAddress.getLocalHost(), 0));
  }

  /**
   * Creates a default TCP transport mapping that binds to the given address (interface) on the
   * local host.
   *
   * @param serverAddress the TcpAddress instance that describes the server address to listen on
   *     incoming connection requests.
   * @throws UnknownHostException if the specified interface does not exist.
   * @throws IOException if the given address cannot be bound.
   */
  public DefaultTcpTransportMapping(TcpAddress serverAddress)
      throws UnknownHostException, IOException {
    super(serverAddress);
    this.serverEnabled = true;
  }

  /**
   * Listen for incoming and outgoing requests. If the <code>serverEnabled</code> member is <code>
   * false</code> the server for incoming requests is not started. This starts the internal server
   * thread that processes messages.
   *
   * @throws SocketException when the transport is already listening for incoming/outgoing messages.
   * @throws IOException
   */
  public synchronized void listen() throws java.io.IOException {
    if (server != null) {
      throw new SocketException("Port already listening");
    }
    server = new ServerThread();
    if (connectionTimeout > 0) {
      socketCleaner = new Timer(true); // run as daemon
    }
    server.setDaemon(true);
    server.start();
  }

  /**
   * Changes the priority of the server thread for this TCP transport mapping. This method has no
   * effect, if called before {@link #listen()} has been called for this transport mapping.
   *
   * @param newPriority the new priority.
   * @see Thread#setPriority
   * @since 1.2.2
   */
  public void setPriority(int newPriority) {
    ServerThread st = server;
    if (st != null) {
      st.setPriority(newPriority);
    }
  }

  /**
   * Returns the priority of the internal listen thread.
   *
   * @return a value between {@link Thread#MIN_PRIORITY} and {@link Thread#MAX_PRIORITY}.
   * @since 1.2.2
   */
  public int getPriority() {
    ServerThread st = server;
    if (st != null) {
      return st.getPriority();
    } else {
      return Thread.NORM_PRIORITY;
    }
  }

  /**
   * Sets the name of the listen thread for this UDP transport mapping. This method has no effect,
   * if called before {@link #listen()} has been called for this transport mapping.
   *
   * @param name the new thread name.
   * @since 1.6
   */
  public void setThreadName(String name) {
    ServerThread st = server;
    if (st != null) {
      st.setName(name);
    }
  }

  /**
   * Returns the name of the listen thread.
   *
   * @return the thread name if in listening mode, otherwise <code>null</code>.
   * @since 1.6
   */
  public String getThreadName() {
    ServerThread st = server;
    if (st != null) {
      return st.getName();
    } else {
      return null;
    }
  }

  /** Closes all open sockets and stops the internal server thread that processes messages. */
  public void close() {
    ServerThread st = server;
    if (st != null) {
      st.close();
      try {
        st.join();
      } catch (InterruptedException ex) {
        logger.warn(ex);
      }
      server = null;
      for (Iterator it = sockets.values().iterator(); it.hasNext(); ) {
        SocketEntry entry = (SocketEntry) it.next();
        try {
          synchronized (entry) {
            entry.getSocket().close();
          }
          logger.debug("Socket to " + entry.getPeerAddress() + " closed");
        } catch (IOException iox) {
          // ingore
          logger.debug(iox);
        }
      }
      if (socketCleaner != null) {
        socketCleaner.cancel();
      }
      socketCleaner = null;
    }
  }

  /**
   * Closes a connection to the supplied remote address, if it is open. This method is particularly
   * useful when not using a timeout for remote connections.
   *
   * @param remoteAddress the address of the peer socket.
   * @return <code>true</code> if the connection has been closed and <code>false</code> if there was
   *     nothing to close.
   * @since 1.7.1
   */
  public synchronized boolean close(Address remoteAddress) throws IOException {
    if (logger.isDebugEnabled()) {
      logger.debug("Closing socket for peer address " + remoteAddress);
    }
    SocketEntry entry = (SocketEntry) sockets.remove(remoteAddress);
    if (entry != null) {
      synchronized (entry) {
        entry.getSocket().close();
      }
      logger.info("Socket to " + entry.getPeerAddress() + " closed");
      return true;
    }
    return false;
  }

  /**
   * Sends a SNMP message to the supplied address.
   *
   * @param address an <code>TcpAddress</code>. A <code>ClassCastException</code> is thrown if
   *     <code>address</code> is not a <code>TcpAddress</code> instance.
   * @param message byte[] the message to sent.
   * @throws IOException
   */
  public void sendMessage(Address address, byte[] message) throws java.io.IOException {
    if (server == null) {
      listen();
    }
    server.sendMessage(address, message);
  }

  /**
   * Gets the connection timeout. This timeout specifies the time a connection may be idle before it
   * is closed.
   *
   * @return long the idle timeout in milliseconds.
   */
  public long getConnectionTimeout() {
    return connectionTimeout;
  }

  /**
   * Sets the connection timeout. This timeout specifies the time a connection may be idle before it
   * is closed.
   *
   * @param connectionTimeout the idle timeout in milliseconds. A zero or negative value will
   *     disable any timeout and connections opened by this transport mapping will stay opened until
   *     they are explicitly closed.
   */
  public void setConnectionTimeout(long connectionTimeout) {
    this.connectionTimeout = connectionTimeout;
  }

  /**
   * Checks whether a server for incoming requests is enabled.
   *
   * @return boolean
   */
  public boolean isServerEnabled() {
    return serverEnabled;
  }

  public MessageLengthDecoder getMessageLengthDecoder() {
    return messageLengthDecoder;
  }

  /**
   * Sets whether a server for incoming requests should be created when the transport is set into
   * listen state. Setting this value has no effect until the {@link #listen()} method is called (if
   * the transport is already listening, {@link #close()} has to be called before).
   *
   * @param serverEnabled if <code>true</code> if the transport will listens for incoming requests
   *     after {@link #listen()} has been called.
   */
  public void setServerEnabled(boolean serverEnabled) {
    this.serverEnabled = serverEnabled;
  }

  /**
   * Sets the message length decoder. Default message length decoder is the {@link
   * SnmpMesssageLengthDecoder}. The message length decoder must be able to decode the total length
   * of a message for this transport mapping protocol(s).
   *
   * @param messageLengthDecoder a <code>MessageLengthDecoder</code> instance.
   */
  public void setMessageLengthDecoder(MessageLengthDecoder messageLengthDecoder) {
    if (messageLengthDecoder == null) {
      throw new NullPointerException();
    }
    this.messageLengthDecoder = messageLengthDecoder;
  }

  /**
   * Gets the inbound buffer size for incoming requests. When SNMP packets are received that are
   * longer than this maximum size, the messages will be silently dropped and the connection will be
   * closed.
   *
   * @return the maximum inbound buffer size in bytes.
   */
  public int getMaxInboundMessageSize() {
    return super.getMaxInboundMessageSize();
  }

  /**
   * Sets the maximum buffer size for incoming requests. When SNMP packets are received that are
   * longer than this maximum size, the messages will be silently dropped and the connection will be
   * closed.
   *
   * @param maxInboundMessageSize the length of the inbound buffer in bytes.
   */
  public void setMaxInboundMessageSize(int maxInboundMessageSize) {
    this.maxInboundMessageSize = maxInboundMessageSize;
  }

  private synchronized void timeoutSocket(SocketEntry entry) {
    if (connectionTimeout > 0) {
      socketCleaner.schedule(new SocketTimeout(entry), connectionTimeout);
    }
  }

  public boolean isListening() {
    return (server != null);
  }

  class SocketEntry {
    private Socket socket;
    private TcpAddress peerAddress;
    private long lastUse;
    private LinkedList message = new LinkedList();
    private ByteBuffer readBuffer = null;

    public SocketEntry(TcpAddress address, Socket socket) {
      this.peerAddress = address;
      this.socket = socket;
      this.lastUse = System.currentTimeMillis();
    }

    public long getLastUse() {
      return lastUse;
    }

    public void used() {
      lastUse = System.currentTimeMillis();
    }

    public Socket getSocket() {
      return socket;
    }

    public TcpAddress getPeerAddress() {
      return peerAddress;
    }

    public synchronized void addMessage(byte[] message) {
      this.message.add(message);
    }

    public byte[] nextMessage() {
      if (this.message.size() > 0) {
        return (byte[]) this.message.removeFirst();
      }
      return null;
    }

    public void setReadBuffer(ByteBuffer byteBuffer) {
      this.readBuffer = byteBuffer;
    }

    public ByteBuffer getReadBuffer() {
      return readBuffer;
    }

    public String toString() {
      return "SocketEntry[peerAddress="
          + peerAddress
          + ",socket="
          + socket
          + ",lastUse="
          + new Date(lastUse)
          + "]";
    }
  }

  public static class SnmpMesssageLengthDecoder implements MessageLengthDecoder {
    public int getMinHeaderLength() {
      return MIN_SNMP_HEADER_LENGTH;
    }

    public MessageLength getMessageLength(ByteBuffer buf) throws IOException {
      MutableByte type = new MutableByte();
      BERInputStream is = new BERInputStream(buf);
      int ml = BER.decodeHeader(is, type);
      int hl = (int) is.getPosition();
      MessageLength messageLength = new MessageLength(hl, ml);
      return messageLength;
    }
  }

  class SocketTimeout extends TimerTask {
    private SocketEntry entry;

    public SocketTimeout(SocketEntry entry) {
      this.entry = entry;
    }

    /** run */
    public void run() {
      long now = System.currentTimeMillis();
      if ((socketCleaner == null) || (now - entry.getLastUse() >= connectionTimeout)) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Socket has not been used for "
                  + (now - entry.getLastUse())
                  + " micro seconds, closing it");
        }
        sockets.remove(entry.getPeerAddress());
        try {
          synchronized (entry) {
            entry.getSocket().close();
          }
          logger.info("Socket to " + entry.getPeerAddress() + " closed due to timeout");
        } catch (IOException ex) {
          logger.error(ex);
        }
      } else {
        if (logger.isDebugEnabled()) {
          logger.debug("Scheduling " + ((entry.getLastUse() + connectionTimeout) - now));
        }
        socketCleaner.schedule(
            new SocketTimeout(entry), (entry.getLastUse() + connectionTimeout) - now);
      }
    }
  }

  class ServerThread extends Thread {
    private byte[] buf;
    private volatile boolean stop = false;
    private Throwable lastError = null;
    private ServerSocketChannel ssc;
    private Selector selector;

    private LinkedList pending = new LinkedList();

    public ServerThread() throws IOException {
      setName("DefaultTCPTransportMapping_" + getAddress());
      buf = new byte[getMaxInboundMessageSize()];
      // Selector for incoming requests
      selector = Selector.open();

      if (serverEnabled) {
        // Create a new server socket and set to non blocking mode
        ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);

        // Bind the server socket
        InetSocketAddress isa =
            new InetSocketAddress(tcpAddress.getInetAddress(), tcpAddress.getPort());
        ssc.socket().bind(isa);
        // Register accepts on the server socket with the selector. This
        // step tells the selector that the socket wants to be put on the
        // ready list when accept operations occur, so allowing multiplexed
        // non-blocking I/O to take place.
        ssc.register(selector, SelectionKey.OP_ACCEPT);
      }
    }

    private void processPending() {
      synchronized (pending) {
        while (pending.size() > 0) {
          SocketEntry entry = (SocketEntry) pending.removeFirst();
          try {
            // Register the channel with the selector, indicating
            // interest in connection completion and attaching the
            // target object so that we can get the target back
            // after the key is added to the selector's
            // selected-key set
            if (entry.getSocket().isConnected()) {
              entry.getSocket().getChannel().register(selector, SelectionKey.OP_WRITE, entry);
            } else {
              entry.getSocket().getChannel().register(selector, SelectionKey.OP_CONNECT, entry);
            }

          } catch (IOException iox) {
            logger.error(iox);
            // Something went wrong, so close the channel and
            // record the failure
            try {
              entry.getSocket().getChannel().close();
              TransportStateEvent e =
                  new TransportStateEvent(
                      DefaultTcpTransportMapping.this,
                      entry.getPeerAddress(),
                      TransportStateEvent.STATE_CLOSED,
                      iox);
              fireConnectionStateChanged(e);
            } catch (IOException ex) {
              logger.error(ex);
            }
            lastError = iox;
          }
        }
      }
    }

    public Throwable getLastError() {
      return lastError;
    }

    public void sendMessage(Address address, byte[] message) throws java.io.IOException {
      Socket s = null;
      SocketEntry entry = (SocketEntry) sockets.get(address);
      if (logger.isDebugEnabled()) {
        logger.debug("Looking up connection for destination '" + address + "' returned: " + entry);
        logger.debug(sockets.toString());
      }
      if (entry != null) {
        s = entry.getSocket();
      }
      if ((s == null) || (s.isClosed())) {
        if (logger.isDebugEnabled()) {
          logger.debug("Socket for address '" + address + "' is closed, opening it...");
        }
        SocketChannel sc = null;
        try {
          // Open the channel, set it to non-blocking, initiate connect
          sc = SocketChannel.open();
          sc.configureBlocking(false);
          sc.connect(
              new InetSocketAddress(
                  ((TcpAddress) address).getInetAddress(), ((TcpAddress) address).getPort()));
          s = sc.socket();
          entry = new SocketEntry((TcpAddress) address, s);
          entry.addMessage(message);
          sockets.put(address, entry);

          synchronized (pending) {
            pending.add(entry);
          }

          selector.wakeup();
          logger.debug("Trying to connect to " + address);
        } catch (IOException iox) {
          logger.error(iox);
          throw iox;
        }
      } else {
        entry.addMessage(message);
        synchronized (pending) {
          pending.add(entry);
        }
        selector.wakeup();
      }
    }

    public void run() {
      // Here's where everything happens. The select method will
      // return when any operations registered above have occurred, the
      // thread has been interrupted, etc.
      try {
        while (!stop) {
          try {
            if (selector.select() > 0) {
              if (stop) {
                break;
              }
              // Someone is ready for I/O, get the ready keys
              Set readyKeys = selector.selectedKeys();
              Iterator it = readyKeys.iterator();

              // Walk through the ready keys collection and process date requests.
              while (it.hasNext()) {
                SelectionKey sk = (SelectionKey) it.next();
                it.remove();
                SocketChannel readChannel = null;
                TcpAddress incomingAddress = null;
                if (sk.isAcceptable()) {
                  // The key indexes into the selector so you
                  // can retrieve the socket that's ready for I/O
                  ServerSocketChannel nextReady = (ServerSocketChannel) sk.channel();
                  // Accept the date request and send back the date string
                  Socket s = nextReady.accept().socket();
                  readChannel = s.getChannel();
                  readChannel.configureBlocking(false);
                  readChannel.register(selector, SelectionKey.OP_READ);

                  incomingAddress = new TcpAddress(s.getInetAddress(), s.getPort());
                  SocketEntry entry = new SocketEntry(incomingAddress, s);
                  sockets.put(incomingAddress, entry);
                  timeoutSocket(entry);
                  TransportStateEvent e =
                      new TransportStateEvent(
                          DefaultTcpTransportMapping.this,
                          incomingAddress,
                          TransportStateEvent.STATE_CONNECTED,
                          null);
                  fireConnectionStateChanged(e);
                } else if (sk.isReadable()) {
                  readChannel = (SocketChannel) sk.channel();
                  incomingAddress =
                      new TcpAddress(
                          readChannel.socket().getInetAddress(), readChannel.socket().getPort());
                } else if (sk.isWritable()) {
                  try {
                    SocketEntry entry = (SocketEntry) sk.attachment();
                    SocketChannel sc = (SocketChannel) sk.channel();
                    if (entry != null) {
                      writeMessage(entry, sc);
                    }
                  } catch (IOException iox) {
                    if (logger.isDebugEnabled()) {
                      iox.printStackTrace();
                    }
                    logger.warn(iox);
                    TransportStateEvent e =
                        new TransportStateEvent(
                            DefaultTcpTransportMapping.this,
                            incomingAddress,
                            TransportStateEvent.STATE_DISCONNECTED_REMOTELY,
                            iox);
                    fireConnectionStateChanged(e);
                    sk.cancel();
                  }
                } else if (sk.isConnectable()) {
                  try {
                    SocketEntry entry = (SocketEntry) sk.attachment();
                    SocketChannel sc = (SocketChannel) sk.channel();
                    if ((!sc.isConnected()) && (sc.finishConnect())) {
                      sc.configureBlocking(false);
                      logger.debug("Connected to " + entry.getPeerAddress());
                      // make sure conncetion is closed if not used for timeout
                      // micro seconds
                      timeoutSocket(entry);
                      sc.register(selector, SelectionKey.OP_WRITE, entry);
                    }
                    TransportStateEvent e =
                        new TransportStateEvent(
                            DefaultTcpTransportMapping.this,
                            incomingAddress,
                            TransportStateEvent.STATE_CONNECTED,
                            null);
                    fireConnectionStateChanged(e);
                  } catch (IOException iox) {
                    if (logger.isDebugEnabled()) {
                      iox.printStackTrace();
                    }
                    logger.warn(iox);
                    sk.cancel();
                  }
                }

                if (readChannel != null) {
                  try {
                    readMessage(sk, readChannel, incomingAddress);
                  } catch (IOException iox) {
                    // IO exception -> channel closed remotely
                    if (logger.isDebugEnabled()) {
                      iox.printStackTrace();
                    }
                    logger.warn(iox);
                    sk.cancel();
                    readChannel.close();
                    TransportStateEvent e =
                        new TransportStateEvent(
                            DefaultTcpTransportMapping.this,
                            incomingAddress,
                            TransportStateEvent.STATE_DISCONNECTED_REMOTELY,
                            iox);
                    fireConnectionStateChanged(e);
                  }
                }
              }
            }
          } catch (NullPointerException npex) {
            // There seems to happen a NullPointerException within the select()
            npex.printStackTrace();
            logger.warn("NullPointerException within select()?");
          }
          processPending();
        }
        if (ssc != null) {
          ssc.close();
        }
      } catch (IOException iox) {
        logger.error(iox);
        lastError = iox;
      }
      if (!stop) {
        stop = true;
        synchronized (DefaultTcpTransportMapping.this) {
          server = null;
        }
      }
    }

    private void readMessage(SelectionKey sk, SocketChannel readChannel, TcpAddress incomingAddress)
        throws IOException {
      // note that socket has been used
      SocketEntry entry = (SocketEntry) sockets.get(incomingAddress);
      if (entry != null) {
        entry.used();
        ByteBuffer readBuffer = entry.getReadBuffer();
        if (readBuffer != null) {
          readChannel.read(readBuffer);
          if (readBuffer.hasRemaining()) {
            readChannel.register(selector, SelectionKey.OP_READ, entry);
          } else {
            dispatchMessage(incomingAddress, readBuffer, readBuffer.capacity());
          }
          return;
        }
      }
      ByteBuffer byteBuffer = ByteBuffer.wrap(buf);
      byteBuffer.limit(messageLengthDecoder.getMinHeaderLength());
      long bytesRead = readChannel.read(byteBuffer);
      if (logger.isDebugEnabled()) {
        logger.debug("Reading header " + bytesRead + " bytes from " + incomingAddress);
      }
      MessageLength messageLength = new MessageLength(0, Integer.MIN_VALUE);
      if (bytesRead == messageLengthDecoder.getMinHeaderLength()) {
        messageLength = messageLengthDecoder.getMessageLength(ByteBuffer.wrap(buf));
        if (logger.isDebugEnabled()) {
          logger.debug("Message length is " + messageLength);
        }
        if ((messageLength.getMessageLength() > getMaxInboundMessageSize())
            || (messageLength.getMessageLength() <= 0)) {
          logger.error(
              "Received message length "
                  + messageLength
                  + " is greater than inboundBufferSize "
                  + getMaxInboundMessageSize());
          synchronized (entry) {
            entry.getSocket().close();
            logger.info("Socket to " + entry.getPeerAddress() + " closed due to an error");
          }
        } else {
          byteBuffer.limit(messageLength.getMessageLength());
          bytesRead += readChannel.read(byteBuffer);
          if (bytesRead == messageLength.getMessageLength()) {
            dispatchMessage(incomingAddress, byteBuffer, bytesRead);
          } else {
            byte[] message = new byte[byteBuffer.limit()];
            byteBuffer.flip();
            byteBuffer.get(message, 0, byteBuffer.limit() - byteBuffer.remaining());
            entry.setReadBuffer(ByteBuffer.wrap(message));
          }
          readChannel.register(selector, SelectionKey.OP_READ, entry);
        }
      } else if (bytesRead < 0) {
        logger.debug("Socket closed remotely");
        sk.cancel();
        readChannel.close();
        TransportStateEvent e =
            new TransportStateEvent(
                DefaultTcpTransportMapping.this,
                incomingAddress,
                TransportStateEvent.STATE_DISCONNECTED_REMOTELY,
                null);
        fireConnectionStateChanged(e);
      }
    }

    private void dispatchMessage(
        TcpAddress incomingAddress, ByteBuffer byteBuffer, long bytesRead) {
      byteBuffer.flip();
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Received message from "
                + incomingAddress
                + " with length "
                + bytesRead
                + ": "
                + new OctetString(byteBuffer.array(), 0, (int) bytesRead).toHexString());
      }
      ByteBuffer bis;
      if (isAsyncMsgProcessingSupported()) {
        byte[] bytes = new byte[(int) bytesRead];
        System.arraycopy(byteBuffer.array(), 0, bytes, 0, (int) bytesRead);
        bis = ByteBuffer.wrap(bytes);
      } else {
        bis = ByteBuffer.wrap(byteBuffer.array(), 0, (int) bytesRead);
      }
      fireProcessMessage(incomingAddress, bis);
    }

    private void writeMessage(SocketEntry entry, SocketChannel sc) throws IOException {
      byte[] message = entry.nextMessage();
      if (message != null) {
        ByteBuffer buffer = ByteBuffer.wrap(message);
        sc.write(buffer);
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Send message with length "
                  + message.length
                  + " to "
                  + entry.getPeerAddress()
                  + ": "
                  + new OctetString(message).toHexString());
        }
        sc.register(selector, SelectionKey.OP_READ);
      }
    }

    public void close() {
      stop = true;
      ServerThread st = server;
      if (st != null) {
        st.interrupt();
      }
    }
  }
}
    /**
     * This method will be called whenever a pdu is received on the given port specified in the
     * listen() method.
     */
    public synchronized void processPdu(CommandResponderEvent cmdRespEvent) {
      logger.info("Received PDU...");
      PDU pdu = cmdRespEvent.getPDU();

      if (pdu != null) {
        try {
          Event event;
          Map<String, String> headers;
          StringBuilder stringBuilder = new StringBuilder();

          // getVariableBindings: Gets the variable binding vector.
          Vector<? extends VariableBinding> vbs = pdu.getVariableBindings();
          for (VariableBinding vb : vbs) {
            // To extract only the value of the OID
            // stringBuilder.append(vb.getVariable().toString());
            stringBuilder.append(vb.toString() + ",");
          }

          String messageString = stringBuilder.toString();

          // trick: remove the last comma
          messageString = messageString.replaceAll(",$", "");

          byte[] message = messageString.getBytes();

          event = new SimpleEvent();
          headers = new HashMap<String, String>();

          headers.put("timestamp", String.valueOf(System.currentTimeMillis()));
          logger.info("Message: {}", messageString);
          event.setBody(message);
          event.setHeaders(headers);

          if (event == null) {
            return;
          }

          // store the event to underlying channels(s)
          getChannelProcessor().processEvent(event);

          counterGroup.incrementAndGet("events.success");

        } catch (ChannelException ex) {
          counterGroup.incrementAndGet("events.dropped");
          logger.error("Error writting to channel", ex);
          return;
        }

        logger.info("Trap Type = " + pdu.getType());
        logger.info("Variable Bindings = " + pdu.getVariableBindings());

        int pduType = pdu.getType();

        if ((pduType != PDU.TRAP)
            && (pduType != PDU.V1TRAP)
            && (pduType != PDU.REPORT)
            && (pduType != PDU.RESPONSE)) {
          pdu.setErrorIndex(0);
          pdu.setErrorStatus(0);
          pdu.setType(PDU.RESPONSE);
          StatusInformation statusInformation = new StatusInformation();
          StateReference ref = cmdRespEvent.getStateReference();

          try {
            System.out.println(cmdRespEvent.getPDU());
            cmdRespEvent
                .getMessageDispatcher()
                .returnResponsePdu(
                    cmdRespEvent.getMessageProcessingModel(),
                    cmdRespEvent.getSecurityModel(),
                    cmdRespEvent.getSecurityName(),
                    cmdRespEvent.getSecurityLevel(),
                    pdu,
                    cmdRespEvent.getMaxSizeResponsePDU(),
                    ref,
                    statusInformation);
          } catch (MessageException ex) {
            System.err.println("Error while sending response: " + ex.getMessage());
            LogFactory.getLogger(SnmpRequest.class).error(ex);
          }
        }
      }
    }
/**
 * @author hgx
 * @version 1.0.0
 * @since 1.0.0
 */
public class SNMP4JAgent implements VariableProvider {

  static {
    LogFactory.setLogFactory(new JavaLogFactory());
  }

  private LogAdapter logger = LogFactory.getLogger(SNMP4JAgent.class);
  private File bootCounterFile;
  private String configFile;

  protected AgentConfigManager agent;
  protected MOServer server;
  protected Modules modules;

  public SNMP4JAgent() {}

  public SNMP4JAgent(String address) {
    // 不需要这两个文件一定存在
    configFile = "files/SampleAgent.cfg";
    bootCounterFile = new File("files/SampleAgent.bc");
    server = new DefaultMOServer();
    MOServer[] moServers = new MOServer[] {server};
    // 读properties文件
    final Properties props = new Properties();
    FileInputStream configInputStream = null;
    try {
      configInputStream = new FileInputStream("files/AgentConfig.properties");
      props.load(configInputStream);
    } catch (IOException e) {
      e.printStackTrace();
    }
    // 注入file
    MOInputFactory configurationFactory =
        new MOInputFactory() {
          public MOInput createMOInput() {
            return new PropertyMOInput(props, SNMP4JAgent.this);
          }
        };
    List<String> addressList = new ArrayList<String>();
    addressList.add(address);
    MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
    addListenAddresses(messageDispatcher, addressList);
    agent =
        new AgentConfigManager(
            new OctetString(MPv3.createLocalEngineID()),
            messageDispatcher,
            null,
            moServers,
            ThreadPool.create("SampleAgent", 3),
            configurationFactory,
            new DefaultMOPersistenceProvider(moServers, configFile),
            new EngineBootsCounterFile(bootCounterFile));
  }

  protected void addListenAddresses(MessageDispatcher md, List<String> addresses) {
    for (Iterator<String> it = addresses.iterator(); it.hasNext(); ) {
      Address address = GenericAddress.parse((String) it.next());
      @SuppressWarnings("unchecked")
      TransportMapping<? extends Address> tm =
          TransportMappings.getInstance().createTransportMapping(address);
      if (tm != null) {
        md.addTransportMapping(tm);
      } else {
        logger.warn("No transport mapping available for address " + address + "'.");
      }
    }
  }

  public void run() {
    // initialize agent before registering our own modules
    agent.initialize();
    try {
      registerMIBs();
    } catch (IOException | MibLoaderException | DuplicateRegistrationException e) {
      e.printStackTrace();
    }
    agent.run();
    System.out.println("Agent Start!");
    SendTrap.sendTrapMessage();
  }

  protected void registerMIBs()
      throws IOException, MibLoaderException, DuplicateRegistrationException {
    if (modules == null) {
      modules = new Modules(getFactory());
    }
    String filePath = MibsLoader.getValueFromProperties("loadSpecifiedMib");
    System.out.println("filePath = " + filePath);
    MibsLoader mibsLoader = new MibsLoader();
    mibsLoader.loadMibs(filePath);

    Mib mib = mibsLoader.getMib();

    Properties properties = MibsLoader.readProperties(MibsLoader.OIDVALUEMAPFILE);
    for (Object t : properties.keySet()) {
      String key = t.toString();
      String value = properties.getProperty(t.toString());
      System.out.println(key + " = " + value);
      MibValueSymbol mvs = mib.getSymbolByOid(key);
      if (mvs != null) {
        if (mvs.isScalar()) {
          registerScalar(key + ".0", value);
        }

        if (mvs.isTableColumn()) {
          // 模拟配置任意行
          String[] values = value.trim().split(";");
          for (int i = 0; i < values.length; i++) {
            registerScalar(key + "." + Integer.toString(i) + ".0", values[i]);
          }
        }
      }
    }
    modules.registerMOs(server, null);
  }

  protected void registerScalar(String oid, String value) {
    try {
      server.register(
          new MOScalar<Variable>(
              new OID(oid), MOAccessImpl.ACCESS_READ_CREATE, new OctetString(value)),
          null);
    } catch (DuplicateRegistrationException drex) {
      logger.error(
          "Duplicate registration: "
              + drex.getMessage()
              + "."
              + " MIB object registration may be incomplete!",
          drex);
    }
  }

  protected MOFactory getFactory() {
    return DefaultMOFactory.getInstance();
  }

  @Override
  public Variable getVariable(String name) {
    OID oid;
    OctetString context = null;
    int pos = name.indexOf(':');
    if (pos >= 0) {
      context = new OctetString(name.substring(0, pos));
      oid = new OID(name.substring(pos + 1, name.length()));
    } else {
      oid = new OID(name);
    }
    final DefaultMOContextScope scope = new DefaultMOContextScope(context, oid, true, oid, true);
    MOQuery query = new DefaultMOQuery(scope, false, this);
    ManagedObject mo = server.lookup(query);
    if (mo != null) {
      final VariableBinding vb = new VariableBinding(oid);
      final RequestStatus status = new RequestStatus();
      SubRequest req =
          new SubRequest() {
            private boolean completed;
            private MOQuery query;

            public boolean hasError() {
              return false;
            }

            public void setErrorStatus(int errorStatus) {
              status.setErrorStatus(errorStatus);
            }

            public int getErrorStatus() {
              return status.getErrorStatus();
            }

            public RequestStatus getStatus() {
              return status;
            }

            public MOScope getScope() {
              return scope;
            }

            public VariableBinding getVariableBinding() {
              return vb;
            }

            @SuppressWarnings("rawtypes")
            public Request getRequest() {
              return null;
            }

            public Object getUndoValue() {
              return null;
            }

            public void setUndoValue(Object undoInformation) {}

            public void completed() {
              completed = true;
            }

            public boolean isComplete() {
              return completed;
            }

            public void setTargetMO(ManagedObject managedObject) {}

            public ManagedObject getTargetMO() {
              return null;
            }

            public int getIndex() {
              return 0;
            }

            public void setQuery(MOQuery query) {
              this.query = query;
            }

            public MOQuery getQuery() {
              return query;
            }

            public SubRequestIterator repetitions() {
              return null;
            }

            public void updateNextRepetition() {}

            public Object getUserObject() {
              return null;
            }

            public void setUserObject(Object userObject) {}
          };
      mo.get(req);
      return vb.getVariable();
    }
    return null;
  }

  public static void start() {
    PrintStream ps;
    try {
      ps = new PrintStream(new FileOutputStream("agent_log.txt"));
      System.setOut(ps);
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }

    String address = "udp:127.0.0.1/161";
    SNMP4JAgent sampleAgent = new SNMP4JAgent(address);
    // Add all available security protocols (e.g. SHA,MD5,DES,AES,3DES,..)
    // SecurityProtocols.getInstance().addDefaultProtocols();
    sampleAgent.run();
  }
}
Example #12
0
/**
 * The <code>MockSnmpAgent</code> class extends the SNMP4J BaseAgent class to provide a mock SNMP
 * agent for SNMP-based OpenNMS tests. Large chunks of code were lifted from the
 * org.snmp4j.agent.test.TestAgent class.
 *
 * @author Jeff Gehlbach
 */
public class MockSnmpAgent extends BaseAgent implements Runnable {

  private static final String PROPERTY_SLEEP_ON_CREATE = "mockSnmpAgent.sleepOnCreate";

  // initialize Log4J logging
  static {
    try {
      Class.forName("org.apache.log4j.Logger");
      LogFactory.setLogFactory(new Log4jLogFactory());
    } catch (Exception e) {
      LogFactory.setLogFactory(new ConsoleLogFactory());
    }

    // Check to see if the pseudorandom byte generator device exists
    if (new File("/dev/urandom").exists()) {

      // If so, use it as the Java entropy gathering device so that we never
      // block on entropy gathering. Don't use the exact string "file:/dev/urandom"
      // because this is treated as a special value inside the JVM. Insert the
      // "." into the path to force it to use the real /dev/urandom device.
      //
      // @see https://bugs.openjdk.java.net/browse/JDK-6202721
      //
      System.setProperty("java.security.egd", "file:/dev/./urandom");
    }

    // Allow us to override default security protocols
    // SNMP4JSettings.setExtensibilityEnabled(true);
    // Override the default security protocols
    // System.setProperty(SecurityProtocols.SECURITY_PROTOCOLS_PROPERTIES,
    // "/org/opennms/mock/snmp/SecurityProtocols.properties");
  }

  private static final LogAdapter s_log = LogFactory.getLogger(MockSnmpAgent.class);

  private AtomicReference<String> m_address = new AtomicReference<String>();
  private AtomicReference<URL> m_moFile = new AtomicReference<URL>();
  private AtomicBoolean m_running = new AtomicBoolean();
  private AtomicBoolean m_stopped = new AtomicBoolean();
  private AtomicReference<List<ManagedObject>> m_moList =
      new AtomicReference<List<ManagedObject>>();
  private AtomicReference<MockSnmpMOLoader> m_moLoader = new AtomicReference<MockSnmpMOLoader>();
  private AtomicReference<IOException> m_failure = new AtomicReference<IOException>();

  private static File BOOT_COUNT_FILE;

  public static boolean allowSetOnMissingOid = false;

  static {
    File bootCountFile;
    try {
      bootCountFile = File.createTempFile("mockSnmpAgent", "boot");
      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(bootCountFile));
      out.writeInt(0);
      out.flush();
      out.close();
    } catch (IOException e) {
      bootCountFile = new File("/dev/null");
    }
    BOOT_COUNT_FILE = bootCountFile;
  }

  public MockSnmpAgent(final File confFile, final URL moFile) {
    super(
        BOOT_COUNT_FILE,
        confFile,
        new CommandProcessor(
            new OctetString(MPv3.createLocalEngineID(new OctetString("MOCKAGENT")))));
    m_moLoader.set(new PropertiesBackedManagedObject());
    m_moFile.set(moFile);
    agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
  }

  /**
   * Creates the mock agent with files to read and store the boot counter, to read and store the
   * agent configuration, and to read the mocked managed objects (MOs), plus a string describing the
   * address and port to bind to.
   *
   * @param moFile a MIB dump file describing the managed objects to be mocked. The current
   *     implementation expects a Java properties file, which can conveniently be generated using
   *     the Net-SNMP utility <code>snmpwalk</code> with the <code>-One</code> option set.
   */
  public MockSnmpAgent(final File confFile, final URL moFile, final String bindAddress) {
    this(confFile, moFile);
    m_address.set(bindAddress);
  }

  public static MockSnmpAgent createAgentAndRun(URL moFile, String bindAddress)
      throws InterruptedException {
    setupLogging();
    try {
      InputStream in = moFile.openStream();
      if (in == null) {
        throw new IllegalArgumentException(
            "could not get InputStream mock object resource; does it exist?  Resource: " + moFile);
      }
      in.close();

    } catch (IOException e) {
      throw new RuntimeException(
          "Got IOException while checking for existence of mock object file: " + e, e);
    }

    final MockSnmpAgent agent = new MockSnmpAgent(new File("/dev/null"), moFile, bindAddress);
    Thread thread = new Thread(agent, agent.getClass().getSimpleName() + '-' + agent.hashCode());
    thread.start();

    try {
      while (!agent.isRunning() && thread.isAlive()) {
        Thread.sleep(10);
      }
    } catch (final InterruptedException e) {
      s_log.warn("MockSnmpAgent: Agent interrupted while starting: " + e.getLocalizedMessage());
      thread.interrupt();
      agent.shutDownAndWait();
      throw e;
    }

    if (!thread.isAlive()) {
      agent.m_running.set(false);
      agent.m_stopped.set(true);
      throw new IllegalStateException(
          "MockSnmpAgent: agent failed to start on address " + bindAddress, agent.m_failure.get());
    }

    if (System.getProperty(PROPERTY_SLEEP_ON_CREATE) != null) {
      long sleep = Long.parseLong(System.getProperty(PROPERTY_SLEEP_ON_CREATE));
      Thread.sleep(sleep);
    }

    return agent;
  }

  private static void setupLogging() {
    if (LogFactory.getLogFactory() == null) {
      LogFactory.setLogFactory(new ConsoleLogFactory());
    }
  }

  public static void main(String[] args) throws UnknownHostException, MalformedURLException {
    LogFactory.setLogFactory(new ConsoleLogFactory());
    AgentConfigData agentConfig = parseCli(args);
    if (agentConfig == null) {
      System.err.println("Could not parse configuration.");
      System.exit(1);
    }
    String listenSpec =
        agentConfig.getListenAddr().getHostAddress() + "/" + agentConfig.getListenPort();

    try {
      MockSnmpAgent.createAgentAndRun(agentConfig.getMoFile(), listenSpec);
    } catch (InterruptedException e) {
      System.exit(0);
    }
  }

  public static AgentConfigData parseCli(String[] args)
      throws UnknownHostException, MalformedURLException {

    String dumpFile = null;
    String listenAddr = "127.0.0.1";
    int listenPort = 1691;

    for (int i = 0; i < args.length; i++) {
      if ("-d".equals(args[i]) || "--dump-file".equals(args[i])) {
        if (i + 1 >= args.length) {
          usage("You must specify at least a pathname or URL for the dump file.");
        } else {
          dumpFile = args[++i];
        }
      } else if ("-l".equals(args[i]) || "--listen-addr".equals(args[i])) {
        if (i + 1 >= args.length) {
          usage("You must pass an address argument when using " + args[i] + ".");
        } else {
          listenAddr = args[++i];
        }
      } else if ("-p".equals(args[i]) || "--port".equals(args[i])) {
        if (i + 1 >= args.length) {
          usage("You must pass a port number when using " + args[i] + ".");
        } else {
          listenPort = Integer.parseInt(args[++i]);
        }
      }
    }

    if (dumpFile == null) {
      usage("You must specify at least a pathname or URL for the dump file.");
    }

    return new AgentConfigData(dumpFile, listenAddr, listenPort);
  }

  private static void usage(String why) {
    System.err.println(why);
    System.err.println(
        "java -jar mock-snmp-agent-jar-with-dependencies.jar -d dump-file [other options]");
    System.err.println("-d, --dump-file {filename}\tPathname or URL of file containing MIB dump");
    System.err.println(
        "-l, --listen-addr {ip-address}\tIP address to bind to (default: all interfaces)");
    System.err.println("-p, --port {udp-port}\tUDP port to listen on (default: 1691)");
    System.exit(1);
  }

  /** {@inheritDoc} */
  @Override
  protected void initMessageDispatcher() {
    s_log.info("MockSnmpAgent: starting initMessageDispatcher()");
    try {
      dispatcher = new MessageDispatcherImpl();

      usm =
          new USM(SecurityProtocols.getInstance(), agent.getContextEngineID(), updateEngineBoots());

      mpv3 = new MPv3(usm);

      SecurityProtocols.getInstance().addDefaultProtocols();
      dispatcher.addMessageProcessingModel(new MPv1());
      dispatcher.addMessageProcessingModel(new MPv2c());
      dispatcher.addMessageProcessingModel(mpv3);
      initSnmpSession();
    } finally {
      s_log.info("MockSnmpAgent: finished initMessageDispatcher()");
    }
  }

  @Override
  public void initSnmpSession() {
    s_log.info("MockSnmpAgent: starting initTransportMappings()");
    try {
      super.initSnmpSession();
    } finally {
      s_log.info("MockSnmpAgent: finished initTransportMappings()");
    }
  }

  @Override
  public void initConfigMIB() {
    s_log.info("MockSnmpAgent: starting initConfigMIB()");
    try {
      super.initConfigMIB();
    } finally {
      s_log.info("MockSnmpAgent: finished initConfigMIB()");
    }
  }

  @Override
  public void setupDefaultProxyForwarder() {
    s_log.info("MockSnmpAgent: starting setupDefaultProxyForwarder()");
    try {
      super.setupDefaultProxyForwarder();
    } finally {
      s_log.info("MockSnmpAgent: finished setupDefaultProxyForwarder()");
    }
  }

  @Override
  public void updateSession(Session session) {
    s_log.info("MockSnmpAgent: starting updateSession()");
    try {
      super.updateSession(session);
    } finally {
      s_log.info("MockSnmpAgent: finished updateSession()");
    }
  }

  public void shutDownAndWait() throws InterruptedException {
    if (!isRunning()) {
      return;
    }

    shutDown();

    while (!isStopped()) {
      Thread.sleep(10);
    }
  }

  /**
   * Starts the <code>MockSnmpAgent</code> running. Meant to be called from the <code>start</code>
   * method of class <code>Thread</code>, but could also be used to bring up a standalone mock
   * agent.
   *
   * @see org.snmp4j.agent.BaseAgent#run()
   * @author Jeff Gehlbach
   */
  @Override
  public void run() {
    s_log.info("MockSnmpAgent: Initializing SNMP Agent");
    try {
      init();
      s_log.info("MockSnmpAgent: Finished 'init' loading config");
      loadConfig(ImportModes.UPDATE_CREATE);
      s_log.info("MockSnmpAgent: finished 'loadConfig' adding shutdown hook");
      addShutdownHook();
      s_log.info("MockSnmpAgent: finished 'addShutdownHook' finishing init");
      finishInit();
      s_log.info("MockSnmpAgent: finished 'finishInit' running agent");
      super.run();
      s_log.info("MockSnmpAgent: finished running Agent - setting running to true");
      m_running.set(true);
    } catch (final BindException e) {
      s_log.error(
          String.format(
              "MockSnmpAgent: Unable to bind to %s.  You probably specified an invalid address or a port < 1024 and are not running as root. Exception: %s",
              m_address.get(), e),
          e);
    } catch (final Throwable t) {
      s_log.error("MockSnmpAgent: An error occurred while initializing: " + t, t);
      t.printStackTrace();
    }

    boolean interrupted = false;
    s_log.info(
        "MockSnmpAgent: Initialization Complete processing message until agent is shutdown.");
    while (m_running.get()) {
      try {
        Thread.sleep(10); // fast, Fast, FAST, *FAST*!!!
      } catch (final InterruptedException e) {
        interrupted = true;
        break;
      }
    }

    s_log.info("MockSnmpAgent: Shutdown called stopping agent.");
    for (final TransportMapping transportMapping : transportMappings) {
      try {
        if (transportMapping != null) {
          transportMapping.close();
        }
      } catch (final IOException t) {
        s_log.error(
            "MockSnmpAgent: an error occurred while closing the transport mapping "
                + transportMapping
                + ": "
                + t,
            t);
      }
    }

    m_stopped.set(true);

    s_log.info("MockSnmpAgent: Agent is no longer running.");
    if (interrupted) {
      Thread.currentThread().interrupt();
    }
  }

  public void shutDown() {
    m_running.set(false);
    m_stopped.set(false);
  }

  public boolean isRunning() {
    return m_running.get();
  }

  public boolean isStopped() {
    return m_stopped.get();
  }

  /** {@inheritDoc} */
  @Override
  protected void addCommunities(SnmpCommunityMIB communityMIB) {
    Variable[] com2sec =
        new Variable[] {
          new OctetString("public"), // community name
          new OctetString("public"), // security name
          getAgent().getContextEngineID(), // local engine ID
          new OctetString(), // default context name
          new OctetString(), // transport tag
          new Integer32(StorageType.nonVolatile), // storage type
          new Integer32(RowStatus.active) // row status
        };
    SnmpCommunityEntryRow row =
        communityMIB
            .getSnmpCommunityEntry()
            .createRow(new OctetString("public2public").toSubIndex(true), com2sec);
    communityMIB.getSnmpCommunityEntry().addRow(row);
  }

  /** {@inheritDoc} */
  @Override
  protected void addViews(VacmMIB vacm) {
    vacm.addGroup(
        SecurityModel.SECURITY_MODEL_SNMPv1,
        new OctetString("public"),
        new OctetString("v1v2group"),
        StorageType.nonVolatile);
    vacm.addGroup(
        SecurityModel.SECURITY_MODEL_SNMPv2c,
        new OctetString("public"),
        new OctetString("v1v2group"),
        StorageType.nonVolatile);
    vacm.addGroup(
        SecurityModel.SECURITY_MODEL_USM,
        new OctetString("SHADES"),
        new OctetString("v3group"),
        StorageType.nonVolatile);
    vacm.addGroup(
        SecurityModel.SECURITY_MODEL_USM,
        new OctetString("TEST"),
        new OctetString("v3test"),
        StorageType.nonVolatile);
    vacm.addGroup(
        SecurityModel.SECURITY_MODEL_USM,
        new OctetString("opennmsUser"),
        new OctetString("v3group"),
        StorageType.nonVolatile);
    vacm.addGroup(
        SecurityModel.SECURITY_MODEL_USM,
        new OctetString("SHA"),
        new OctetString("v3restricted"),
        StorageType.nonVolatile);

    vacm.addAccess(
        new OctetString("v1v2group"),
        new OctetString(),
        SecurityModel.SECURITY_MODEL_ANY,
        SecurityLevel.NOAUTH_NOPRIV,
        VacmMIB.vacmExactMatch,
        new OctetString("fullReadView"),
        new OctetString("fullWriteView"),
        new OctetString("fullNotifyView"),
        StorageType.nonVolatile);
    vacm.addAccess(
        new OctetString("v3group"),
        new OctetString(),
        SecurityModel.SECURITY_MODEL_USM,
        SecurityLevel.AUTH_PRIV,
        VacmMIB.vacmExactMatch,
        new OctetString("fullReadView"),
        new OctetString("fullWriteView"),
        new OctetString("fullNotifyView"),
        StorageType.nonVolatile);
    vacm.addAccess(
        new OctetString("v3restricted"),
        new OctetString(),
        SecurityModel.SECURITY_MODEL_USM,
        SecurityLevel.AUTH_NOPRIV,
        VacmMIB.vacmExactMatch,
        new OctetString("restrictedReadView"),
        new OctetString("restrictedWriteView"),
        new OctetString("restrictedNotifyView"),
        StorageType.nonVolatile);
    vacm.addAccess(
        new OctetString("v3test"),
        new OctetString(),
        SecurityModel.SECURITY_MODEL_USM,
        SecurityLevel.AUTH_PRIV,
        VacmMIB.vacmExactMatch,
        new OctetString("testReadView"),
        new OctetString("testWriteView"),
        new OctetString("testNotifyView"),
        StorageType.nonVolatile);

    vacm.addViewTreeFamily(
        new OctetString("fullReadView"),
        new OID("1"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("fullWriteView"),
        new OID("1"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("fullNotifyView"),
        new OID("1"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);

    vacm.addViewTreeFamily(
        new OctetString("restrictedReadView"),
        new OID("1.3.6.1.2"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("restrictedWriteView"),
        new OID("1.3.6.1.2.1"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("restrictedNotifyView"),
        new OID("1.3.6.1.2"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);

    vacm.addViewTreeFamily(
        new OctetString("testReadView"),
        new OID("1.3.6.1.2"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("testReadView"),
        new OID("1.3.6.1.2.1.1"),
        new OctetString(),
        VacmMIB.vacmViewExcluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("testWriteView"),
        new OID("1.3.6.1.2.1"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
    vacm.addViewTreeFamily(
        new OctetString("testNotifyView"),
        new OID("1.3.6.1.2"),
        new OctetString(),
        VacmMIB.vacmViewIncluded,
        StorageType.nonVolatile);
  }

  /** {@inheritDoc} */
  @Override
  protected void addNotificationTargets(
      SnmpTargetMIB targetMIB, SnmpNotificationMIB notificationMIB) {
    targetMIB.addDefaultTDomains();

    targetMIB.addTargetAddress(
        new OctetString("notification"),
        TransportDomains.transportDomainUdpIpv4,
        new OctetString(new UdpAddress("127.0.0.1/162").getValue()),
        200,
        1,
        new OctetString("notify"),
        new OctetString("v2c"),
        StorageType.permanent);
    targetMIB.addTargetParams(
        new OctetString("v2c"),
        MessageProcessingModel.MPv2c,
        SecurityModel.SECURITY_MODEL_SNMPv2c,
        new OctetString("public"),
        SecurityLevel.NOAUTH_NOPRIV,
        StorageType.permanent);
    notificationMIB.addNotifyEntry(
        new OctetString("default"),
        new OctetString("notify"),
        SnmpNotificationMIB.SnmpNotifyTypeEnum.trap,
        StorageType.permanent);
  }

  /** {@inheritDoc} */
  @Override
  protected void addUsmUser(USM usm) {
    UsmUser user =
        new UsmUser(
            new OctetString("SHADES"),
            AuthSHA.ID,
            new OctetString("SHADESAuthPassword"),
            PrivDES.ID,
            new OctetString("SHADESPrivPassword"));
    usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
    user =
        new UsmUser(
            new OctetString("TEST"),
            AuthSHA.ID,
            new OctetString("maplesyrup"),
            PrivDES.ID,
            new OctetString("maplesyrup"));
    usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
    user =
        new UsmUser(
            new OctetString("opennmsUser"),
            AuthMD5.ID,
            new OctetString("0p3nNMSv3"),
            PrivDES.ID,
            new OctetString("0p3nNMSv3"));
    usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
    user =
        new UsmUser(
            new OctetString("SHA"), AuthSHA.ID, new OctetString("SHAAuthPassword"), null, null);
    usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
  }

  /** {@inheritDoc} */
  @Override
  protected void initTransportMappings() throws IOException {
    s_log.info("MockSnmpAgent: starting initTransportMappings()");
    try {
      final MockUdpTransportMapping mapping =
          new MockUdpTransportMapping(new UdpAddress(m_address.get()), true);
      mapping.setThreadName("MockSnmpAgent-UDP-Transport");
      transportMappings = new TransportMapping[] {mapping};
    } catch (final IOException e) {
      s_log.info("MockSnmpAgent: initTransportMappings() caught an IoException: " + e.getMessage());
      m_failure.set(e);
      throw e;
    } finally {
      s_log.info("MockSnmpAgent: finished initTransportMappings()");
    }
  }

  public static final class MockUdpTransportMapping extends DefaultUdpTransportMapping {
    public MockUdpTransportMapping(final UdpAddress udpAddress, final boolean reuseAddress)
        throws IOException {
      super(udpAddress, reuseAddress);
    }

    public InetAddress getInetAddress() {
      return socket.getLocalAddress();
    }

    public int getPort() {
      return socket.getLocalPort();
    }
  }

  public InetAddress getInetAddress() {
    final TransportMapping mapping = transportMappings[0];
    return ((MockUdpTransportMapping) mapping).getInetAddress();
  }

  public int getPort() {
    final TransportMapping mapping = transportMappings[0];
    return ((MockUdpTransportMapping) mapping).getPort();
  }

  // override the agent defaults since we are providing all the agent data
  /** {@inheritDoc} */
  @Override
  protected void registerSnmpMIBs() {
    s_log.info("MockSnmpAgent: starting registerSnmpMIBs()");
    try {
      registerManagedObjects();
    } finally {
      s_log.info("MockSnmpAgent: finished registerSnmpMIBs()");
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void unregisterSnmpMIBs() {
    unregisterManagedObjects();
  }

  /** {@inheritDoc} */
  @Override
  protected void registerManagedObjects() {
    final List<ManagedObject> mockMOs = Collections.unmodifiableList(createMockMOs());
    m_moList.set(mockMOs);

    for (final ManagedObject mo : mockMOs) {
      try {
        server.register(mo, null);
      } catch (final DuplicateRegistrationException ex) {
        s_log.error("MockSnmpAgent: unable to register managed object", ex);
      }
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void unregisterManagedObjects() {
    for (final ManagedObject mo : m_moList.get()) {
      server.unregister(mo, null);
    }
  }

  protected List<ManagedObject> createMockMOs() {
    return m_moLoader.get().loadMOs(m_moFile.get());
  }

  private ManagedObject findMOForOid(OID oid) {
    final List<ManagedObject> list = m_moList.get();
    for (ManagedObject mo : list) {
      if (mo.getScope().covers(oid)) {
        return mo;
      }
    }
    return null;
  }

  public void updateValue(OID oid, Variable value) {
    ManagedObject mo = findMOForOid(oid);
    assertNotNull("Unable to find oid in mib for mockAgent: " + oid, mo);
    if (mo instanceof Updatable) {
      ((Updatable) mo).updateValue(oid, value);
    }
  }

  private void assertNotNull(final String string, final Object o) {
    if (!allowSetOnMissingOid && o == null) {
      throw new IllegalStateException(string);
    }
  }

  public void updateValue(String oid, Variable value) {
    updateValue(new OID(oid), value);
  }

  public void updateIntValue(String oid, int val) {
    updateValue(oid, new Integer32(val));
  }

  public void updateStringValue(String oid, String val) {
    updateValue(oid, new OctetString(val));
  }

  public void updateCounter32Value(String oid, int val) {
    updateValue(oid, new Counter32(val));
  }

  public void updateCounter64Value(String oid, long val) {
    updateValue(oid, new Counter64(val));
  }

  public void updateValuesFromResource(final URL moFile) {
    unregisterManagedObjects();
    m_moFile.set(moFile);
    registerManagedObjects();
  }

  @Override
  public String toString() {
    return "MockSnmpAgent[" + m_address.get() + "]";
  }
}
Example #13
0
 private static void setupLogging() {
   if (LogFactory.getLogFactory() == null) {
     LogFactory.setLogFactory(new ConsoleLogFactory());
   }
 }