All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.davfx.ninio.snmp.SnmpServer Maven / Gradle / Ivy

package com.davfx.ninio.snmp;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.davfx.ninio.core.Address;
import com.davfx.ninio.core.Closeable;
import com.davfx.ninio.core.CloseableByteBufferHandler;
import com.davfx.ninio.core.DatagramReady;
import com.davfx.ninio.core.FailableCloseableByteBufferHandler;
import com.davfx.ninio.core.Queue;
import com.davfx.ninio.core.QueueReady;
import com.davfx.ninio.core.Ready;
import com.davfx.ninio.core.ReadyConnection;
import com.davfx.util.Pair;

// Syntax: snmp[bulk]walk -v2c -c -On :6161 
// snmpbulkwalk -v2c -cpublic -On 127.0.0.1:6161 1.1.2
public final class SnmpServer implements AutoCloseable, Closeable {
	
	public static interface Handler {
		interface Callback {
			boolean handle(Oid oid, String value);
		}
		void from(Oid oid, Callback callback);
	}
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SnmpServer.class);
	
	private final Queue queue;
	private CloseableByteBufferHandler write = null;
	private boolean closed = false;
	
	public SnmpServer(Address address, Handler handler) {
		this(new Queue(), address, handler);
	}
	public SnmpServer(Queue queue, Address address, Handler handler) {
		this(queue, new DatagramReady(queue.getSelector(), queue.allocator()).bind(), address, handler);
	}
	public SnmpServer(Queue queue, Ready ready, Address address, final Handler handler) {
		this.queue = queue;

		new QueueReady(queue, ready).connect(address, new ReadyConnection() {
			private BerPacket ber(String s) {
				return new BytesBerPacket(BerPacketUtils.bytes(s));
			}
			
			@Override
			public void handle(Address address, ByteBuffer buffer) {
				int requestId;
				String community;
				final int bulkLength;
				int request;
				final Oid oid;
				try {
					BerReader ber = new BerReader(buffer);
					ber.beginReadSequence();
					{
						ber.readInteger(); // Version
						community = BerPacketUtils.string(ber.readBytes());
						request = ber.beginReadSequence();
						{
							requestId = ber.readInteger();
							ber.readInteger(); // Non-repeater, not used
							bulkLength = ber.readInteger();
							ber.beginReadSequence();
							{
								ber.beginReadSequence();
								{
									oid = ber.readOid();
									ber.readNull();
								}
								ber.endReadSequence();
							}
							ber.endReadSequence();
						}
						ber.endReadSequence();
					}
					ber.endReadSequence();
				} catch (IOException e) {
					LOGGER.error("Invalid packet", e);
					return;
				}

				LOGGER.trace("Request with community: {} and oid: {}", community, oid);
				
				if (request == BerConstants.GET) {
					final List> next = new LinkedList<>();

					handler.from(oid, new Handler.Callback() {
						@Override
						public boolean handle(Oid handleOid, String value) {
							// if (!oid.isPrefix(handleOid)) {
							// return false;
							// }
							if (handleOid.equals(oid)) {
								next.add(new Pair<>(handleOid, ber(value)));
								return false;
							}
							return true;
						}
					});

					if (next.isEmpty()) {
						LOGGER.trace("GET {}: None", oid);
						write(address, build(requestId, community, BerConstants.NO_SUCH_NAME_ERROR, 0, null));
						return;
					}

					LOGGER.trace("GET {}: {}", oid, next);
					write(address, build(requestId, community, 0, 0, next));
					return;
				}
				
				if (request == BerConstants.GETNEXT) {
					final List> next = new LinkedList<>();

					handler.from(oid, new Handler.Callback() {
						@Override
						public boolean handle(Oid handleOid, String value) {
							if (handleOid.equals(oid)) {
								// Skipped
								return true;
							} else {
								if (next.isEmpty()) {
									next.add(new Pair<>(handleOid, ber(value)));
								}
								return false;
							}
						}
					});

					if (next.isEmpty()) {
						LOGGER.trace("GETNEXT {}: No next", oid);
						write(address, build(requestId, community, BerConstants.NO_SUCH_NAME_ERROR, 0, null));
						return;
					}

					LOGGER.trace("GETNEXT {}: {}", oid, next);
					write(address, build(requestId, community, 0, 0, next));
					return;
				}
				
				if (request == BerConstants.GETBULK) {
					final List> next = new LinkedList<>();

					handler.from(oid, new Handler.Callback() {
						@Override
						public boolean handle(Oid handleOid, String value) {
							// if (!oid.isPrefix(handleOid)) {
							// return false;
							// }
							if (handleOid.equals(oid)) {
								// Skipped
							} else {
								next.add(new Pair<>(handleOid, ber(value)));
							}
							return next.size() < bulkLength;
						}
					});

					if (next.isEmpty()) {
						LOGGER.trace("GETBULK {}: No next", oid);
						write(address, build(requestId, community, BerConstants.NO_SUCH_NAME_ERROR, 0, null));
						return;
					}

					LOGGER.trace("GETBULK {}: {}", oid, next);
					write(address, build(requestId, community, 0, 0, next));
					return;
				}
			}
			
			private void write(final Address address, final ByteBuffer b) {
				write.handle(address, b);
			}
			
			@Override
			public void failed(IOException e) {
				LOGGER.error("Failed", e);
			}
			
			@Override
			public void connected(FailableCloseableByteBufferHandler write) {
				LOGGER.trace("Connected");
				if (closed) {
					write.close();
				} else {
					SnmpServer.this.write = write;
				}
			}
			
			@Override
			public void close() {
			}
		});
	}
	
	private static ByteBuffer build(int requestId, String community, int errorStatus, int errorIndex, Iterable> oidValues) {
		SequenceBerPacket oidSequence = new SequenceBerPacket(BerConstants.SEQUENCE);
		if (oidValues != null) {
			for (Pair ov : oidValues) {
				oidSequence.add(new SequenceBerPacket(BerConstants.SEQUENCE)
					.add(new OidBerPacket(ov.first))
					.add(ov.second));
			}
		}

		SequenceBerPacket root = new SequenceBerPacket(BerConstants.SEQUENCE)
		.add(new IntegerBerPacket(BerConstants.VERSION_2C))
		.add(new BytesBerPacket(BerPacketUtils.bytes(community)))
		.add(new SequenceBerPacket(BerConstants.RESPONSE)
			.add(new IntegerBerPacket(requestId))
			.add(new IntegerBerPacket(errorStatus))
			.add(new IntegerBerPacket(errorIndex))
			.add(oidSequence));

		ByteBuffer buffer = ByteBuffer.allocate(BerPacketUtils.typeAndLengthBufferLength(root.lengthBuffer()) + root.length());
		root.write(buffer);
		buffer.flip();

		return buffer;
	}
	
	@Override
	public void close() {
		queue.post(new Runnable() {
			@Override
			public void run() {
				closed = true;
				if (write != null) {
					write.close();
				}
			}
		});
	}
	
	/*%%%%
	public static void main(String[] args) throws Exception {
		Config config = ConfigFactory.load();
		
		final Fields fields = new Fields();
		for (Config c : config.getConfigList("fields")) {
			try (InputStream in = Main.class.getResourceAsStream(c.getString("resource"))) {
				if (in == null) {
					throw new IOException("Resource not found: " + c.getString("resource"));
				}
				String penKey;
				int pen;
				if (c.getString("pen").isEmpty()) {
					penKey = c.getString("keys.pen");
					pen = -1;
				} else {
					penKey = null;
					pen = c.getInt("pen");
				}
				fields.load(in, pen, (pen < 0) ? null : c.getString("keys.code"), c.getString("keys.name"), c.getString("keys.type"), c.getString("keys.semantics"), penKey);
			}
		}

		Mapping mapping = new Mapping<>();
		PacketField field = fields.of("INTERFACE_ID");
		mapping.of("a").put("a1_key", new PacketFieldAsValue(fields.as(field), PacketValue.of("myInterfaceId_a1")));
		mapping.of("a").put("a2_key", new PacketFieldAsValue(fields.as(field), PacketValue.of("myInterfaceId_a2")));
		mapping.of("b").put("b1_key", new PacketFieldAsValue(fields.as(field), PacketValue.of("myInterfaceId_b1")));
		mapping.of("b").put("b2_key", new PacketFieldAsValue(fields.as(field), PacketValue.of("myInterfaceId_b2")));
		
		final StringBuilder b = new StringBuilder();
		mapping.walk(new Mapping.WalkCallback() {
			@Override
			public boolean child(WalkLevel level, String key) {
				b.append(Arrays.toString(ROOT_OID) + "." + level + ":" + key + "\n");
				return true;
			}
			@Override
			public void value(WalkLevel level, PacketFieldAsValue value) {
				b.append(Arrays.toString(ROOT_OID) + "." + level + ":" + value.as.toString(value.value) + "\n");
			}
		});
		System.out.println(b);
		
		Address address = new Address(Address.LOCALHOST, 6161);
		try (SnmpServer server = new SnmpServer(fields, address, mapping)) {
			Thread.sleep(50000);
		}
	}
	*/
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy