// 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"); }
/** * 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++; } }
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); } }
/** * 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; } }
static { LogFactory.setLogFactory(new JavaLogFactory()); }
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(); } }
/** * 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() + "]"; } }
private static void setupLogging() { if (LogFactory.getLogFactory() == null) { LogFactory.setLogFactory(new ConsoleLogFactory()); } }