1 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.jmx.snmp.agent;
  26 
  27 import java.util.Vector;
  28 import java.util.Hashtable;
  29 import java.util.Enumeration;
  30 import java.util.Iterator;
  31 import java.util.NoSuchElementException;
  32 import java.util.Arrays;
  33 import java.util.logging.Level;
  34 
  35 import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
  36 import com.sun.jmx.snmp.SnmpVarBind;
  37 import com.sun.jmx.snmp.SnmpStatusException;
  38 import com.sun.jmx.snmp.SnmpDefinitions;
  39 import com.sun.jmx.snmp.SnmpOid;
  40 import com.sun.jmx.snmp.SnmpPdu;
  41 import com.sun.jmx.snmp.SnmpEngine;
  42 
  43 //  XXX: things to do: use SnmpOid rather than `instance' for future
  44 //       evolutions.
  45 //  XXX: Maybe use hashlists rather than vectors for entries?
  46 //       => in that case, the key should be SnmpOid.toString()
  47 //
  48 /**
  49  * This class is used to register varbinds from a SNMP varbind list with
  50  * the SnmpMibNode responsible for handling the requests concerning that
  51  * varbind.
  52  * This class holds a hashtable of Handler nodes, whith the involved
  53  * SnmpMibNode as a key.
  54  * When the involved SnmpMibNode is a group, the sublist of varbind is
  55  * directly stored in the Handler node.
  56  * When the involved SnmpMibNode is a table, the sublist is stored in a
  57  * sorted array indexed by the OID of the entry involved.
  58  */
  59 final class SnmpRequestTree {
  60 
  61     // Constructor:
  62     // @param  req The SnmpMibRequest that will be segmented in this
  63     //         tree. It holds the original varbind vector passed
  64     //         by the SnmpSubRequestHandler to this MIB. This
  65     //         varbind vector is used to retrieve the "real"
  66     //         position of a varbind in the vector. There is no other easy
  67     //         way to do this - since as a result of the segmentation the
  68     //         original positions will be lost.
  69     // @param  creationflag indicates whether the operation involved
  70     //         allows for entry creation (ie: it is a SET request).
  71     // @param  pdutype indicates the type of the request PDU as defined
  72     //         in SnmpDefinitions
  73     //
  74     SnmpRequestTree(SnmpMibRequest req, boolean creationflag, int pdutype) {
  75         this.request = req;
  76         this.version  = req.getVersion();
  77         this.creationflag = creationflag;
  78         this.hashtable = new Hashtable<>();
  79         setPduType(pdutype);
  80     }
  81 
  82     public static int mapSetException(int errorStatus, int version)
  83         throws SnmpStatusException {
  84 
  85         final int errorCode = errorStatus;
  86 
  87         if (version == SnmpDefinitions.snmpVersionOne)
  88             return errorCode;
  89 
  90         int mappedErrorCode = errorCode;
  91 
  92         // Now take care of V2 errorCodes that can be stored
  93         // in the varbind itself:
  94         if (errorCode == SnmpStatusException.noSuchObject)
  95             // noSuchObject => notWritable
  96             mappedErrorCode = SnmpStatusException.snmpRspNotWritable;
  97 
  98         else if (errorCode == SnmpStatusException.noSuchInstance)
  99             // noSuchInstance => notWritable
 100             mappedErrorCode = SnmpStatusException.snmpRspNotWritable;
 101 
 102         return mappedErrorCode;
 103     }
 104 
 105     public static int mapGetException(int errorStatus, int version)
 106         throws SnmpStatusException {
 107 
 108         final int errorCode = errorStatus;
 109         if (version == SnmpDefinitions.snmpVersionOne)
 110             return errorCode;
 111 
 112         int mappedErrorCode = errorCode;
 113 
 114         // Now take care of V2 errorCodes that can be stored
 115         // in the varbind itself:
 116         if (errorCode ==
 117             SnmpStatusException.noSuchObject)
 118             // noSuchObject => noSuchObject
 119             mappedErrorCode = errorCode;
 120 
 121         else if (errorCode ==
 122                  SnmpStatusException.noSuchInstance)
 123             // noSuchInstance => noSuchInstance
 124             mappedErrorCode = errorCode;
 125 
 126         // Now we're going to try to transform every other
 127         // global code in either noSuchInstance or noSuchObject,
 128         // so that the get can return a partial result.
 129         //
 130         // Only noSuchInstance or noSuchObject can be stored
 131         // in the varbind itself.
 132         //
 133 
 134         // According to RFC 1905: noAccess is emitted when the
 135         // the access is denied because it is not in the MIB view...
 136         //
 137         else if (errorCode ==
 138                  SnmpStatusException.noAccess)
 139             // noAccess => noSuchInstance
 140             mappedErrorCode = SnmpStatusException.noSuchInstance;
 141 
 142         // According to RFC 1905: (my interpretation because it is not
 143         // really clear) The specified variable name exists - but the
 144         // variable does not exists and cannot be created under the
 145         // present circumstances (probably because the request specifies
 146         // another variable/value which is incompatible, or because the
 147         // value of some other variable in the MIB prevents the creation)
 148         //
 149         // Note that this error should never be raised in a GET context
 150         // but who knows?
 151         //
 152         else if (errorCode == SnmpStatusException.snmpRspInconsistentName)
 153             // inconsistentName => noSuchInstance
 154             mappedErrorCode = SnmpStatusException.noSuchInstance;
 155 
 156         // All the errors comprised between snmpRspWrongType and
 157         // snmpRspInconsistentValue concern values: so we're going
 158         // to assume the OID was correct, and reply with noSuchInstance.
 159         //
 160         // Note that this error should never be raised in a GET context
 161         // but who knows?
 162         //
 163         else if ((errorCode >= SnmpStatusException.snmpRspWrongType) &&
 164                  (errorCode <= SnmpStatusException.snmpRspInconsistentValue))
 165             mappedErrorCode = SnmpStatusException.noSuchInstance;
 166 
 167         // We're going to assume the OID was correct, and reply
 168         // with noSuchInstance.
 169         //
 170         else if (errorCode == SnmpStatusException.readOnly)
 171             mappedErrorCode = SnmpStatusException.noSuchInstance;
 172 
 173         // For all other errors but genErr, we're going to reply with
 174         // noSuchObject
 175         //
 176         else if (errorCode != SnmpStatusException.snmpRspAuthorizationError &&
 177                  errorCode != SnmpStatusException.snmpRspGenErr)
 178             mappedErrorCode = SnmpStatusException.noSuchObject;
 179 
 180         // Only genErr will abort the GET and be returned as global
 181         // error.
 182         //
 183         return mappedErrorCode;
 184 
 185     }
 186 
 187     //-------------------------------------------------------------------
 188     // This class is a package implementation of the enumeration of
 189     // SnmSubRequest associated with an Handler node.
 190     //-------------------------------------------------------------------
 191 
 192     static final class Enum implements Enumeration<SnmpMibSubRequest> {
 193         Enum(SnmpRequestTree hlist,Handler h) {
 194             handler = h;
 195             this.hlist = hlist;
 196             size = h.getSubReqCount();
 197         }
 198         private final Handler handler;
 199         private final SnmpRequestTree hlist;
 200         private int   entry = 0;
 201         private int   iter  = 0;
 202         private int   size  = 0;
 203 
 204         @Override
 205         public boolean hasMoreElements() {
 206             return iter < size;
 207         }
 208 
 209         @Override
 210         public SnmpMibSubRequest nextElement() throws NoSuchElementException  {
 211             if (iter == 0) {
 212                 if (handler.sublist != null) {
 213                     iter++;
 214                     return hlist.getSubRequest(handler);
 215                 }
 216             }
 217             iter ++;
 218             if (iter > size) throw new NoSuchElementException();
 219             SnmpMibSubRequest result = hlist.getSubRequest(handler,entry);
 220             entry++;
 221             return result;
 222         }
 223     }
 224 
 225     //-------------------------------------------------------------------
 226     // This class is a package implementation of the SnmpMibSubRequest
 227     // interface. It can only be instantiated by SnmpRequestTree.
 228     //-------------------------------------------------------------------
 229 
 230     static final class SnmpMibSubRequestImpl implements SnmpMibSubRequest {
 231         SnmpMibSubRequestImpl(SnmpMibRequest global, Vector<SnmpVarBind> sublist,
 232                            SnmpOid entryoid, boolean isnew,
 233                            boolean getnextflag, SnmpVarBind rs) {
 234             this.global = global;
 235             varbinds           = sublist;
 236             this.version       = global.getVersion();
 237             this.entryoid      = entryoid;
 238             this.isnew         = isnew;
 239             this.getnextflag   = getnextflag;
 240             this.statusvb      = rs;
 241         }
 242 
 243         final private Vector<SnmpVarBind> varbinds;
 244         final private SnmpMibRequest global;
 245         final private int            version;
 246         final private boolean        isnew;
 247         final private SnmpOid        entryoid;
 248         final private boolean        getnextflag;
 249         final private SnmpVarBind    statusvb;
 250 
 251         // -------------------------------------------------------------
 252         // Implements the method defined in SnmpMibRequest interface.
 253         // See SnmpMibRequest for the java doc.
 254         // -------------------------------------------------------------
 255         @Override
 256         public Enumeration<SnmpVarBind> getElements() {
 257             return varbinds.elements();
 258         }
 259 
 260         // -------------------------------------------------------------
 261         // Implements the method defined in SnmpMibRequest interface.
 262         // See SnmpMibRequest for the java doc.
 263         // -------------------------------------------------------------
 264         @Override
 265         public Vector<SnmpVarBind> getSubList() {
 266             return varbinds;
 267         }
 268 
 269         // -------------------------------------------------------------
 270         // Implements the method defined in SnmpMibRequest interface.
 271         // See SnmpMibRequest for the java doc.
 272         // -------------------------------------------------------------
 273         @Override
 274         public final int getSize()  {
 275             if (varbinds == null) return 0;
 276             return varbinds.size();
 277         }
 278 
 279         // -------------------------------------------------------------
 280         // Implements the method defined in SnmpMibRequest interface.
 281         // See SnmpMibRequest for the java doc.
 282         // -------------------------------------------------------------
 283         @Override
 284         public void addVarBind(SnmpVarBind varbind) {
 285             // XXX not sure we must also add the varbind in the global
 286             //     request? or whether we should raise an exception:
 287             //     in principle, this method should not be called!
 288             varbinds.addElement(varbind);
 289             global.addVarBind(varbind);
 290         }
 291 
 292         // -------------------------------------------------------------
 293         // Implements the method defined in SnmpMibSubRequest interface.
 294         // See SnmpMibSubRequest for the java doc.
 295         // -------------------------------------------------------------
 296         @Override
 297         public boolean isNewEntry() {
 298             return isnew;
 299         }
 300 
 301         // -------------------------------------------------------------
 302         // Implements the method defined in SnmpMibSubRequest interface.
 303         // See SnmpMibSubRequest for the java doc.
 304         // -------------------------------------------------------------
 305         @Override
 306         public SnmpOid getEntryOid() {
 307             return entryoid;
 308         }
 309 
 310         // -------------------------------------------------------------
 311         // Implements the method defined in SnmpMibRequest interface.
 312         // See SnmpMibRequest for the java doc.
 313         // -------------------------------------------------------------
 314         @Override
 315         public int getVarIndex(SnmpVarBind varbind) {
 316             if (varbind == null) return 0;
 317             return global.getVarIndex(varbind);
 318         }
 319 
 320         // -------------------------------------------------------------
 321         // Implements the method defined in SnmpMibRequest interface.
 322         // See SnmpMibRequest for the java doc.
 323         // -------------------------------------------------------------
 324         @Override
 325         public Object getUserData() { return global.getUserData(); }
 326 
 327 
 328         // -------------------------------------------------------------
 329         // Implements the method defined in SnmpMibSubRequest interface.
 330         // See SnmpMibSubRequest for the java doc.
 331         // -------------------------------------------------------------
 332 
 333         @Override
 334         public void registerGetException(SnmpVarBind var,
 335                                          SnmpStatusException exception)
 336             throws SnmpStatusException {
 337             // The index in the exception must correspond to
 338             // the SNMP index ...
 339             //
 340             if (version == SnmpDefinitions.snmpVersionOne)
 341                 throw new SnmpStatusException(exception, getVarIndex(var)+1);
 342 
 343             if (var == null)
 344                 throw exception;
 345 
 346             // If we're doing a getnext ==> endOfMibView
 347             if (getnextflag) {
 348                 var.value = SnmpVarBind.endOfMibView;
 349                 return;
 350             }
 351 
 352             final int errorCode = mapGetException(exception.getStatus(),
 353                                                   version);
 354 
 355             // Now take care of V2 errorCodes that can be stored
 356             // in the varbind itself:
 357             if (errorCode ==
 358                 SnmpStatusException.noSuchObject)
 359                 // noSuchObject => noSuchObject
 360                 var.value= SnmpVarBind.noSuchObject;
 361 
 362             else if (errorCode ==
 363                      SnmpStatusException.noSuchInstance)
 364                 // noSuchInstance => noSuchInstance
 365                 var.value= SnmpVarBind.noSuchInstance;
 366 
 367             else
 368                 throw new SnmpStatusException(errorCode, getVarIndex(var)+1);
 369 
 370         }
 371 
 372         // -------------------------------------------------------------
 373         // Implements the method defined in SnmpMibSubRequest interface.
 374         // See SnmpMibSubRequest for the java doc.
 375         // -------------------------------------------------------------
 376         @Override
 377         public void registerSetException(SnmpVarBind var,
 378                                          SnmpStatusException exception)
 379             throws SnmpStatusException {
 380             // The index in the exception must correspond to
 381             // the SNMP index ...
 382             //
 383             if (version == SnmpDefinitions.snmpVersionOne)
 384                 throw new SnmpStatusException(exception, getVarIndex(var)+1);
 385 
 386             // Although the first pass of check() did not fail,
 387             // the set() phase could not be carried out correctly.
 388             // Since we don't know how to make an "undo", and some
 389             // assignation may already have been performed, we're going
 390             // to throw an snmpRspUndoFailed.
 391             //
 392             throw new SnmpStatusException(SnmpDefinitions.snmpRspUndoFailed,
 393                                           getVarIndex(var)+1);
 394         }
 395 
 396         // -------------------------------------------------------------
 397         // Implements the method defined in SnmpMibSubRequest interface.
 398         // See SnmpMibSubRequest for the java doc.
 399         // -------------------------------------------------------------
 400         @Override
 401         public void registerCheckException(SnmpVarBind var,
 402                                            SnmpStatusException exception)
 403             throws SnmpStatusException {
 404             // The index in the exception must correspond to
 405             // the SNMP index ...
 406             //
 407             // We throw the exception in order to abort the SET operation
 408             // in an atomic way.
 409             final int errorCode = exception.getStatus();
 410             final int mappedErrorCode = mapSetException(errorCode,
 411                                                         version);
 412 
 413             if (errorCode != mappedErrorCode)
 414                 throw new
 415                     SnmpStatusException(mappedErrorCode, getVarIndex(var)+1);
 416             else
 417                 throw new SnmpStatusException(exception, getVarIndex(var)+1);
 418         }
 419 
 420         // -------------------------------------------------------------
 421         // Implements the method defined in SnmpMibRequest interface.
 422         // See SnmpMibRequest for the java doc.
 423         // -------------------------------------------------------------
 424         @Override
 425         public int getVersion() {
 426             return version;
 427         }
 428 
 429         @Override
 430         public SnmpVarBind getRowStatusVarBind() {
 431             return statusvb;
 432         }
 433 
 434         @Override
 435         public SnmpPdu getPdu() {
 436             return global.getPdu();
 437         }
 438 
 439         @Override
 440         public int getRequestPduVersion() {
 441             return global.getRequestPduVersion();
 442         }
 443 
 444         @Override
 445         public SnmpEngine getEngine() {
 446             return global.getEngine();
 447         }
 448 
 449         @Override
 450         public String getPrincipal() {
 451             return global.getPrincipal();
 452         }
 453 
 454         @Override
 455         public int getSecurityLevel() {
 456             return global.getSecurityLevel();
 457         }
 458 
 459         @Override
 460         public int getSecurityModel() {
 461             return global.getSecurityModel();
 462         }
 463 
 464         @Override
 465         public byte[] getContextName() {
 466             return global.getContextName();
 467         }
 468 
 469         @Override
 470         public byte[] getAccessContextName() {
 471             return global.getAccessContextName();
 472         }
 473     }
 474 
 475     //-------------------------------------------------------------------
 476     // This class implements a node in the SnmpRequestTree.
 477     // It stores:
 478     //    o The SnmpMibNode involved (key)
 479     //    o The sublist of varbind directly handled by this node
 480     //    o A vector of sublists concerning the entries (existing or not)
 481     //      of the SnmpMIbNode (when it is a table).
 482     //-------------------------------------------------------------------
 483 
 484     static final class Handler {
 485         SnmpMibNode meta;       // The meta  which handles the sublist.
 486         int         depth;      // The depth of the meta node.
 487         Vector<SnmpVarBind> sublist; // The sublist of varbinds to be handled.
 488         // List        entryoids;  // Sorted array of entry oids
 489         // List        entrylists; // Sorted array of entry lists
 490         // List        isentrynew; // Sorted array of booleans
 491         SnmpOid[]     entryoids  = null; // Sorted array of entry oids
 492         Vector<SnmpVarBind>[] entrylists = null; // Sorted array of entry lists
 493         boolean[]     isentrynew = null; // Sorted array of booleans
 494         SnmpVarBind[] rowstatus  = null; // RowStatus varbind, if any
 495         int entrycount = 0;
 496         int entrysize  = 0;
 497 
 498         final int type; // request PDU type as defined in SnmpDefinitions
 499         final private static int Delta = 10;
 500 
 501         public Handler(int pduType) {
 502             this.type = pduType;
 503         }
 504 
 505         /**
 506          * Adds a varbind in this node sublist.
 507          */
 508         public void addVarbind(SnmpVarBind varbind) {
 509             if (sublist == null) sublist = new Vector<>();
 510             sublist.addElement(varbind);
 511         }
 512 
 513         /**
 514          * register an entry for the given oid at the given position with
 515          * the given sublist.
 516          */
 517         @SuppressWarnings("unchecked")
 518         // We need this because of new Vector[n] instead of
 519         // new Vector<SnmpVarBind>[n], which is illegal.
 520         void add(int pos,SnmpOid oid, Vector<SnmpVarBind> v, boolean isnew,
 521                  SnmpVarBind statusvb) {
 522 
 523             if (entryoids == null) {
 524                 // Vectors are null: Allocate new vectors
 525 
 526                 entryoids  = new SnmpOid[Delta];
 527                 entrylists = (Vector<SnmpVarBind>[])new Vector<?>[Delta];
 528                 isentrynew = new boolean[Delta];
 529                 rowstatus  = new SnmpVarBind[Delta];
 530                 entrysize  = Delta;
 531                 pos = 0;
 532 
 533             } else if (pos >= entrysize || entrycount == entrysize) {
 534                 // Vectors must be enlarged
 535 
 536                 // Save old vectors
 537                 SnmpOid[]     olde = entryoids;
 538                 Vector<SnmpVarBind>[]      oldl = entrylists;
 539                 boolean[]     oldn = isentrynew;
 540                 SnmpVarBind[] oldr = rowstatus;
 541 
 542                 // Allocate larger vectors
 543                 entrysize += Delta;
 544                 entryoids =  new SnmpOid[entrysize];
 545                 entrylists = (Vector<SnmpVarBind>[])new Vector<?>[entrysize];
 546                 isentrynew = new boolean[entrysize];
 547                 rowstatus  = new SnmpVarBind[entrysize];
 548 
 549                 // Check pos validity
 550                 if (pos > entrycount) pos = entrycount;
 551                 if (pos < 0) pos = 0;
 552 
 553                 final int l1 = pos;
 554                 final int l2 = entrycount - pos;
 555 
 556                 // Copy original vectors up to `pos'
 557                 if (l1 > 0) {
 558                     java.lang.System.arraycopy(olde,0,entryoids,
 559                                                0,l1);
 560                     java.lang.System.arraycopy(oldl,0,entrylists,
 561                                                0,l1);
 562                     java.lang.System.arraycopy(oldn,0,isentrynew,
 563                                                0,l1);
 564                     java.lang.System.arraycopy(oldr,0,rowstatus,
 565                                                0,l1);
 566                 }
 567 
 568                 // Copy original vectors from `pos' to end, leaving
 569                 // an empty room at `pos' in the new vectors.
 570                 if (l2 > 0) {
 571                     final int l3 = l1+1;
 572                     java.lang.System.arraycopy(olde,l1,entryoids,
 573                                                l3,l2);
 574                     java.lang.System.arraycopy(oldl,l1,entrylists,
 575                                                l3,l2);
 576                     java.lang.System.arraycopy(oldn,l1,isentrynew,
 577                                                l3,l2);
 578                     java.lang.System.arraycopy(oldr,l1,rowstatus,
 579                                                l3,l2);
 580                 }
 581 
 582 
 583             } else if (pos < entrycount) {
 584                 // Vectors are large enough to accommodate one additional
 585                 // entry.
 586                 //
 587                 // Shift vectors, making an empty room at `pos'
 588                 final int l1 = pos+1;
 589                 final int l2 = entrycount - pos;
 590 
 591                 java.lang.System.arraycopy(entryoids,pos,entryoids,
 592                                            l1,l2);
 593                 java.lang.System.arraycopy(entrylists,pos,entrylists,
 594                                            l1,l2);
 595                 java.lang.System.arraycopy(isentrynew,pos,isentrynew,
 596                                            l1,l2);
 597                 java.lang.System.arraycopy(rowstatus,pos,rowstatus,
 598                                            l1,l2);
 599             }
 600 
 601             // Fill the gap at `pos'
 602             entryoids[pos]  = oid;
 603             entrylists[pos] = v;
 604             isentrynew[pos] = isnew;
 605             rowstatus[pos]  = statusvb;
 606             entrycount++;
 607         }
 608 
 609         public void addVarbind(SnmpVarBind varbind, SnmpOid entryoid,
 610                                boolean isnew, SnmpVarBind statusvb)
 611             throws SnmpStatusException {
 612             Vector<SnmpVarBind> v = null;
 613             SnmpVarBind rs = statusvb;
 614 
 615             if (entryoids == null) {
 616 //              entryoids = new ArrayList();
 617 //              entrylists = new ArrayList();
 618 //              isentrynew = new ArrayList();
 619                 v = new Vector<>();
 620 //              entryoids.add(entryoid);
 621 //              entrylists.add(v);
 622 //              isentrynew.add(new Boolean(isnew));
 623                 add(0,entryoid,v,isnew,rs);
 624             } else {
 625                 // int pos = findOid(entryoids,entryoid);
 626                 // int pos = findOid(entryoids,entrycount,entryoid);
 627                 final int pos =
 628                     getInsertionPoint(entryoids,entrycount,entryoid);
 629                 if (pos > -1 && pos < entrycount &&
 630                     entryoid.compareTo(entryoids[pos]) == 0) {
 631                     v  = entrylists[pos];
 632                     rs = rowstatus[pos];
 633                 } else {
 634                     // if (pos == -1 || pos >= entryoids.size() ) {
 635                     // if (pos == -1 || pos >= entrycount ) {
 636                     // pos = getInsertionPoint(entryoids,entryoid);
 637                     // pos = getInsertionPoint(entryoids,entrycount,entryoid);
 638                     v = new Vector<>();
 639 //                  entryoids.add(pos,entryoid);
 640 //                  entrylists.add(pos,v);
 641 //                  isentrynew.add(pos,new Boolean(isnew));
 642                     add(pos,entryoid,v,isnew,rs);
 643                 }
 644 //              } else v = (Vector) entrylists.get(pos);
 645                     // } else v = entrylists[pos];
 646                 if (statusvb != null) {
 647                     if ((rs != null) && (rs != statusvb) &&
 648                         ((type == SnmpDefinitions.pduWalkRequest) ||
 649                          (type == SnmpDefinitions.pduSetRequestPdu))) {
 650                         throw new SnmpStatusException(
 651                               SnmpStatusException.snmpRspInconsistentValue);
 652                     }
 653                     rowstatus[pos] = statusvb;
 654                 }
 655             }
 656 
 657             // We do not include the status variable in the varbind,
 658             // because we're going to set it separately...
 659             //
 660             if (statusvb != varbind)
 661                 v.addElement(varbind);
 662         }
 663 
 664         public int getSubReqCount() {
 665             int count = 0;
 666             if (sublist != null) count++;
 667 //          if (entryoids != null) count += entryoids.size();
 668             if (entryoids != null) count += entrycount;
 669             return count;
 670         }
 671 
 672         public Vector<SnmpVarBind> getSubList() {
 673             return sublist;
 674         }
 675 
 676         public int getEntryPos(SnmpOid entryoid) {
 677             // return findOid(entryoids,entryoid);
 678             return findOid(entryoids,entrycount,entryoid);
 679         }
 680 
 681         public SnmpOid getEntryOid(int pos) {
 682             if (entryoids == null) return null;
 683             // if (pos == -1 || pos >= entryoids.size() ) return null;
 684             if (pos == -1 || pos >= entrycount ) return null;
 685             // return (SnmpOid) entryoids.get(pos);
 686             return entryoids[pos];
 687         }
 688 
 689         public boolean isNewEntry(int pos) {
 690             if (entryoids == null) return false;
 691             // if (pos == -1 || pos >= entryoids.size() ) return false;
 692             if (pos == -1 || pos >= entrycount ) return false;
 693             // return ((Boolean)isentrynew.get(pos)).booleanValue();
 694             return isentrynew[pos];
 695         }
 696 
 697         public SnmpVarBind getRowStatusVarBind(int pos) {
 698             if (entryoids == null) return null;
 699             // if (pos == -1 || pos >= entryoids.size() ) return false;
 700             if (pos == -1 || pos >= entrycount ) return null;
 701             // return ((Boolean)isentrynew.get(pos)).booleanValue();
 702             return rowstatus[pos];
 703         }
 704 
 705         public Vector<SnmpVarBind> getEntrySubList(int pos) {
 706             if (entrylists == null) return null;
 707             // if (pos == -1 || pos >= entrylists.size() ) return null;
 708             if (pos == -1 || pos >= entrycount ) return null;
 709             // return (Vector) entrylists.get(pos);
 710             return entrylists[pos];
 711         }
 712 
 713         public Iterator<SnmpOid> getEntryOids() {
 714             if (entryoids == null) return null;
 715             // return entryoids.iterator();
 716             return Arrays.asList(entryoids).iterator();
 717         }
 718 
 719         public int getEntryCount() {
 720             if (entryoids == null) return 0;
 721             // return entryoids.size();
 722             return entrycount;
 723         }
 724 
 725     }
 726 
 727 
 728     //-------------------------------------------------------------------
 729     //-------------------------------------------------------------------
 730     // Public interface
 731     //-------------------------------------------------------------------
 732     //-------------------------------------------------------------------
 733 
 734     //-------------------------------------------------------------------
 735     // Returns the contextual object containing user-data allocated
 736     // through the SnmpUserDataFactory for this request.
 737     //-------------------------------------------------------------------
 738 
 739     public Object getUserData() { return request.getUserData(); }
 740 
 741     //-------------------------------------------------------------------
 742     // Tells whether creation of new entries is allowed with respect
 743     // to the operation involved (GET=>false/SET=>true)
 744     //-------------------------------------------------------------------
 745 
 746     public boolean isCreationAllowed() {
 747         return creationflag;
 748     }
 749 
 750     //-------------------------------------------------------------------
 751     // Tells whether we are currently processing a SET request (check/set)
 752     //-------------------------------------------------------------------
 753 
 754     public boolean isSetRequest() {
 755         return setreqflag;
 756     }
 757 
 758     //-------------------------------------------------------------------
 759     // Returns the protocol version in which the original request is
 760     // evaluated.
 761     //-------------------------------------------------------------------
 762 
 763     public int getVersion() {
 764         return version;
 765     }
 766 
 767     //-------------------------------------------------------------------
 768     // Returns the actual protocol version of the request PDU.
 769     //-------------------------------------------------------------------
 770 
 771     public int getRequestPduVersion() {
 772         return request.getRequestPduVersion();
 773     }
 774 
 775     //-------------------------------------------------------------------
 776     // Returns the SnmpMibNode associated with the given handler
 777     //-------------------------------------------------------------------
 778 
 779     public SnmpMibNode getMetaNode(Handler handler) {
 780         return handler.meta;
 781     }
 782 
 783     //-------------------------------------------------------------------
 784     // Indicates the depth of the arc in the OID that identifies the
 785     // SnmpMibNode associated with the given handler
 786     //-------------------------------------------------------------------
 787 
 788     public int getOidDepth(Handler handler) {
 789         return handler.depth;
 790     }
 791 
 792     //-------------------------------------------------------------------
 793     // returns an enumeration of the SnmpMibSubRequest's to be invoked on
 794     // the SnmpMibNode associated with a given Handler node.
 795     // If this node is a group, there will be a single subrequest.
 796     // If it is a table, there will be one subrequest per entry involved.
 797     //-------------------------------------------------------------------
 798 
 799     public Enumeration<SnmpMibSubRequest> getSubRequests(Handler handler) {
 800         return new Enum(this,handler);
 801     }
 802 
 803     //-------------------------------------------------------------------
 804     // returns an enumeration of the Handlers stored in the Hashtable.
 805     //-------------------------------------------------------------------
 806 
 807     public Enumeration<Handler> getHandlers() {
 808         return hashtable.elements();
 809     }
 810 
 811     //-------------------------------------------------------------------
 812     // adds a varbind to a handler node sublist
 813     //-------------------------------------------------------------------
 814 
 815     public void add(SnmpMibNode meta, int depth, SnmpVarBind varbind)
 816         throws SnmpStatusException {
 817         registerNode(meta,depth,null,varbind,false,null);
 818     }
 819 
 820     //-------------------------------------------------------------------
 821     // adds an entry varbind to a handler node sublist
 822     //-------------------------------------------------------------------
 823 
 824     public void add(SnmpMibNode meta, int depth, SnmpOid entryoid,
 825                     SnmpVarBind varbind, boolean isnew)
 826         throws SnmpStatusException {
 827         registerNode(meta,depth,entryoid,varbind,isnew,null);
 828     }
 829 
 830     //-------------------------------------------------------------------
 831     // adds an entry varbind to a handler node sublist - specifying the
 832     // varbind which holds the row status
 833     //-------------------------------------------------------------------
 834 
 835     public void add(SnmpMibNode meta, int depth, SnmpOid entryoid,
 836                     SnmpVarBind varbind, boolean isnew,
 837                     SnmpVarBind statusvb)
 838         throws SnmpStatusException {
 839         registerNode(meta,depth,entryoid,varbind,isnew,statusvb);
 840     }
 841 
 842 
 843     //-------------------------------------------------------------------
 844     //-------------------------------------------------------------------
 845     // Protected interface
 846     //-------------------------------------------------------------------
 847     //-------------------------------------------------------------------
 848 
 849     //-------------------------------------------------------------------
 850     // Type of the request (see SnmpDefinitions)
 851     //-------------------------------------------------------------------
 852 
 853     void setPduType(int pduType) {
 854         type = pduType;
 855         setreqflag = ((pduType == SnmpDefinitions.pduWalkRequest) ||
 856             (pduType == SnmpDefinitions.pduSetRequestPdu));
 857     }
 858 
 859     //-------------------------------------------------------------------
 860     // We deal with a GET-NEXT request
 861     //-------------------------------------------------------------------
 862 
 863     void setGetNextFlag() {
 864         getnextflag = true;
 865     }
 866 
 867     //-------------------------------------------------------------------
 868     // Tell whether creation is allowed.
 869     //-------------------------------------------------------------------
 870     void switchCreationFlag(boolean flag) {
 871         creationflag = flag;
 872     }
 873 
 874 
 875     //-------------------------------------------------------------------
 876     // Returns the subrequest handled by the SnmpMibNode itself
 877     // (in principle, only for Groups)
 878     //-------------------------------------------------------------------
 879 
 880     SnmpMibSubRequest getSubRequest(Handler handler) {
 881         if (handler == null) return null;
 882         return new SnmpMibSubRequestImpl(request,handler.getSubList(),
 883                                       null,false,getnextflag,null);
 884     }
 885 
 886     //-------------------------------------------------------------------
 887     // Returns the subrequest associated with the entry identified by
 888     // the given entry (only for tables)
 889     //-------------------------------------------------------------------
 890 
 891     SnmpMibSubRequest getSubRequest(Handler handler, SnmpOid oid) {
 892         if (handler == null) return null;
 893         final int pos = handler.getEntryPos(oid);
 894         if (pos == -1) return null;
 895         return new SnmpMibSubRequestImpl(request,
 896                                          handler.getEntrySubList(pos),
 897                                          handler.getEntryOid(pos),
 898                                          handler.isNewEntry(pos),
 899                                          getnextflag,
 900                                          handler.getRowStatusVarBind(pos));
 901     }
 902 
 903     //-------------------------------------------------------------------
 904     // Returns the subrequest associated with the entry identified by
 905     // the given entry (only for tables). The `entry' parameter is an
 906     // index relative to the position of the entry in the handler sublist.
 907     //-------------------------------------------------------------------
 908 
 909     SnmpMibSubRequest getSubRequest(Handler handler, int entry) {
 910         if (handler == null) return null;
 911         return new
 912             SnmpMibSubRequestImpl(request,handler.getEntrySubList(entry),
 913                                   handler.getEntryOid(entry),
 914                                   handler.isNewEntry(entry),getnextflag,
 915                                   handler.getRowStatusVarBind(entry));
 916     }
 917 
 918     //-------------------------------------------------------------------
 919     //-------------------------------------------------------------------
 920     // Private section
 921     //-------------------------------------------------------------------
 922     //-------------------------------------------------------------------
 923 
 924 
 925     //-------------------------------------------------------------------
 926     // stores a handler node in the Hashtable
 927     //-------------------------------------------------------------------
 928 
 929     private void put(Object key, Handler handler) {
 930         if (handler == null) return;
 931         if (key == null) return;
 932         if (hashtable == null) hashtable = new Hashtable<Object, Handler>();
 933         hashtable.put(key,handler);
 934     }
 935 
 936     //-------------------------------------------------------------------
 937     // finds a handler node in the Hashtable
 938     //-------------------------------------------------------------------
 939 
 940     private Handler get(Object key) {
 941         if (key == null) return null;
 942         if (hashtable == null) return null;
 943         return hashtable.get(key);
 944     }
 945 
 946     //-------------------------------------------------------------------
 947     // Search for the given oid in `oids'. If none is found, returns -1
 948     // otherwise, returns the index at which the oid is located.
 949     //-------------------------------------------------------------------
 950 
 951     private static int findOid(SnmpOid[] oids, int count, SnmpOid oid) {
 952         final int size = count;
 953         int low= 0;
 954         int max= size - 1;
 955         int curr= low + (max-low)/2;
 956         //System.out.println("Try to retrieve: " + oid.toString());
 957         while (low <= max) {
 958 
 959             final SnmpOid pos = oids[curr];
 960 
 961             //System.out.println("Compare with" + pos.toString());
 962             // never know ...we might find something ...
 963             //
 964             final int comp = oid.compareTo(pos);
 965             if (comp == 0)
 966                 return curr;
 967 
 968             if (oid.equals(pos)) {
 969                 return curr;
 970             }
 971             if (comp > 0) {
 972                 low = curr + 1;
 973             } else {
 974                 max = curr - 1;
 975             }
 976             curr = low + (max-low)/2;
 977         }
 978         return -1;
 979     }
 980 
 981     //-------------------------------------------------------------------
 982     // Return the index at which the given oid should be inserted in the
 983     // `oids' array.
 984     //-------------------------------------------------------------------
 985 
 986     private static int getInsertionPoint(SnmpOid[] oids, int count,
 987                                          SnmpOid oid) {
 988         final SnmpOid[] localoids = oids;
 989         final int size = count;
 990         int low= 0;
 991         int max= size - 1;
 992         int curr= low + (max-low)/2;
 993 
 994 
 995         while (low <= max) {
 996 
 997             final SnmpOid pos = localoids[curr];
 998 
 999             // never know ...we might find something ...
1000             //
1001             final int comp= oid.compareTo(pos);
1002 
1003             // In the calling method we will have to check for this case...
1004             //    if (comp == 0)
1005             //       return -1;
1006             // Returning curr instead of -1 avoids having to call
1007             // findOid() first and getInsertionPoint() afterwards.
1008             // We can simply call getInsertionPoint() and then checks whether
1009             // there's an OID at the returned position which equals the
1010             // given OID.
1011             if (comp == 0)
1012                 return curr;
1013 
1014             if (comp>0) {
1015                 low= curr +1;
1016             } else {
1017                 max= curr -1;
1018             }
1019             curr= low + (max-low)/2;
1020         }
1021         return curr;
1022     }
1023 
1024     //-------------------------------------------------------------------
1025     // adds a varbind in a handler node sublist
1026     //-------------------------------------------------------------------
1027 
1028     private void registerNode(SnmpMibNode meta, int depth, SnmpOid entryoid,
1029                               SnmpVarBind varbind, boolean isnew,
1030                               SnmpVarBind statusvb)
1031         throws SnmpStatusException {
1032         if (meta == null) {
1033             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
1034                     SnmpRequestTree.class.getName(),
1035                     "registerNode", "meta-node is null!");
1036             return;
1037         }
1038         if (varbind == null) {
1039             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
1040                     SnmpRequestTree.class.getName(),
1041                     "registerNode", "varbind is null!");
1042             return ;
1043         }
1044 
1045         final Object key = meta;
1046 
1047         // retrieve the handler node associated with the given meta,
1048         // if any
1049         Handler handler = get(key);
1050 
1051         // If no handler node was found for that meta, create one.
1052         if (handler == null) {
1053             // if (isDebugOn())
1054             //    debug("registerNode", "adding node for " +
1055             //          varbind.oid.toString());
1056             handler = new Handler(type);
1057             handler.meta  = meta;
1058             handler.depth = depth;
1059             put(key,handler);
1060         }
1061         // else {
1062         //   if (isDebugOn())
1063         //      debug("registerNode","found node for " +
1064         //            varbind.oid.toString());
1065         // }
1066 
1067         // Adds the varbind in the handler node's sublist.
1068         if (entryoid == null)
1069             handler.addVarbind(varbind);
1070         else
1071             handler.addVarbind(varbind,entryoid,isnew,statusvb);
1072     }
1073 
1074 
1075     //-------------------------------------------------------------------
1076     // private variables
1077     //-------------------------------------------------------------------
1078 
1079     private Hashtable<Object, Handler> hashtable = null;
1080                                              // Hashtable of Handler objects
1081     private SnmpMibRequest request = null;   // The original list of varbinds
1082     private int       version      = 0;      // The protocol version
1083     private boolean   creationflag = false;  // Does the operation allow
1084                                              // creation of entries
1085     private boolean   getnextflag  = false;  // Does the operation allow
1086                                              // creation of entries
1087     private int       type         = 0;      // Request PDU type as defined
1088                                              // in SnmpDefinitions
1089     private boolean   setreqflag   = false;  // True if we're processing a
1090                                              // SET request (check/set).
1091 }