1 /*
   2  * Copyright (c) 1998, 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 
  26 
  27 package com.sun.jmx.snmp.daemon;
  28 
  29 
  30 
  31 // java import
  32 //
  33 import java.util.Vector;
  34 import java.util.Enumeration;
  35 import java.util.Hashtable;
  36 import java.util.logging.Level;
  37 import java.io.InterruptedIOException;
  38 import java.net.DatagramSocket;
  39 import java.net.DatagramPacket;
  40 import java.net.SocketException;
  41 
  42 // jmx imports
  43 //
  44 import javax.management.MBeanServer;
  45 import javax.management.ObjectName;
  46 import com.sun.jmx.snmp.SnmpMessage;
  47 import com.sun.jmx.snmp.SnmpPduFactory;
  48 import com.sun.jmx.snmp.SnmpPduBulk;
  49 import com.sun.jmx.snmp.SnmpPduPacket;
  50 import com.sun.jmx.snmp.SnmpPduRequest;
  51 import com.sun.jmx.snmp.SnmpPduTrap;
  52 import com.sun.jmx.snmp.SnmpValue;
  53 import com.sun.jmx.snmp.SnmpVarBind;
  54 import com.sun.jmx.snmp.SnmpVarBindList;
  55 import com.sun.jmx.snmp.SnmpDefinitions;
  56 import com.sun.jmx.snmp.SnmpStatusException;
  57 import com.sun.jmx.snmp.SnmpTooBigException;
  58 import com.sun.jmx.snmp.SnmpDataTypeEnums;
  59 
  60 // RI imports
  61 //
  62 import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
  63 
  64 // SNMP runtime import
  65 //
  66 import com.sun.jmx.snmp.agent.SnmpMibAgent;
  67 import com.sun.jmx.snmp.agent.SnmpUserDataFactory;
  68 //import com.sun.jmx.snmp.IPAcl.IPAcl;
  69 import com.sun.jmx.snmp.InetAddressAcl;
  70 
  71 
  72 class SnmpRequestHandler extends ClientHandler implements SnmpDefinitions {
  73 
  74     private transient DatagramSocket       socket = null ;
  75     private transient DatagramPacket       packet = null ;
  76     private transient Vector<SnmpMibAgent> mibs = null ;
  77 
  78     /**
  79      * Contains the list of sub-requests associated to the current request.
  80      */
  81     private transient Hashtable<SnmpMibAgent, SnmpSubRequestHandler> subs = null;
  82 
  83     /**
  84      * Reference on the MIBS
  85      */
  86     private transient SnmpMibTree root;
  87 
  88     private transient InetAddressAcl      ipacl = null ;
  89     private transient SnmpPduFactory      pduFactory = null ;
  90     private transient SnmpUserDataFactory userDataFactory = null ;
  91     private transient SnmpAdaptorServer adaptor = null;
  92     /**
  93      * Full constructor
  94      */
  95     public SnmpRequestHandler(SnmpAdaptorServer server, int id,
  96                               DatagramSocket s, DatagramPacket p,
  97                               SnmpMibTree tree, Vector<SnmpMibAgent> m,
  98                               InetAddressAcl a,
  99                               SnmpPduFactory factory,
 100                               SnmpUserDataFactory dataFactory,
 101                               MBeanServer f, ObjectName n)
 102     {
 103         super(server, id, f, n);
 104 
 105         // Need a reference on SnmpAdaptorServer for getNext & getBulk,
 106         // in case of oid equality (mib overlapping).
 107         //
 108         adaptor = server;
 109         socket = s;
 110         packet = p;
 111         root= tree;
 112         mibs = new Vector<>(m);
 113         subs= new Hashtable<>(mibs.size());
 114         ipacl = a;
 115         pduFactory = factory ;
 116         userDataFactory = dataFactory ;
 117         //thread.start();
 118     }
 119 
 120     /**
 121      * Treat the request available in 'packet' and send the result
 122      * back to the client.
 123      * Note: we overwrite 'packet' with the response bytes.
 124      */
 125     @Override
 126     public void doRun() {
 127 
 128         // Trace the input packet
 129         //
 130         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 131             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 132                     "doRun","Packet received:\n" +
 133                     SnmpMessage.dumpHexBuffer(packet.getData(), 0, packet.getLength()));
 134         }
 135 
 136         // Let's build the response packet
 137         //
 138         DatagramPacket respPacket = makeResponsePacket(packet) ;
 139 
 140         // Trace the output packet
 141         //
 142         if ((SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) && (respPacket != null)) {
 143             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 144                     "doRun","Packet to be sent:\n" +
 145                     SnmpMessage.dumpHexBuffer(respPacket.getData(), 0, respPacket.getLength()));
 146         }
 147 
 148         // Send the response packet if any
 149         //
 150         if (respPacket != null) {
 151             try {
 152                 socket.send(respPacket) ;
 153             } catch (SocketException e) {
 154                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 155                     if (e.getMessage().equals(InterruptSysCallMsg)) {
 156                         SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 157                             "doRun", "interrupted");
 158                     } else {
 159                       SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 160                             "doRun", "I/O exception", e);
 161                     }
 162                 }
 163             } catch(InterruptedIOException e) {
 164                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 165                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 166                         "doRun", "interrupted");
 167                 }
 168             } catch(Exception e) {
 169                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 170                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 171                         "doRun", "failure when sending response", e);
 172                 }
 173             }
 174         }
 175     }
 176 
 177     /**
 178      * Here we make a response packet from a request packet.
 179      * We return null if there no response packet to sent.
 180      */
 181     private DatagramPacket makeResponsePacket(DatagramPacket reqPacket) {
 182         DatagramPacket respPacket = null ;
 183 
 184         // Transform the request packet into a request SnmpMessage
 185         //
 186         SnmpMessage reqMsg = new SnmpMessage() ;
 187         try {
 188             reqMsg.decodeMessage(reqPacket.getData(), reqPacket.getLength()) ;
 189             reqMsg.address = reqPacket.getAddress() ;
 190             reqMsg.port = reqPacket.getPort() ;
 191         }
 192         catch(SnmpStatusException x) {
 193             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 194                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 195                     "makeResponsePacket", "packet decoding failed", x);
 196             }
 197             reqMsg = null ;
 198             ((SnmpAdaptorServer)adaptorServer).incSnmpInASNParseErrs(1) ;
 199         }
 200 
 201         // Make the response SnmpMessage if any
 202         //
 203         SnmpMessage respMsg = null ;
 204         if (reqMsg != null) {
 205             respMsg = makeResponseMessage(reqMsg) ;
 206         }
 207 
 208         // Try to transform the response SnmpMessage into response packet.
 209         // NOTE: we overwrite the request packet.
 210         //
 211         if (respMsg != null) {
 212             try {
 213                 reqPacket.setLength(respMsg.encodeMessage(reqPacket.getData())) ;
 214                 respPacket = reqPacket ;
 215             }
 216             catch(SnmpTooBigException x) {
 217                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 218                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 219                         "makeResponsePacket", "response message is too big");
 220                 }
 221                 try {
 222                     respMsg = newTooBigMessage(reqMsg) ;
 223                     reqPacket.setLength(respMsg.encodeMessage(reqPacket.getData())) ;
 224                     respPacket = reqPacket ;
 225                 }
 226                 catch(SnmpTooBigException xx) {
 227                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 228                         SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 229                             "makeResponsePacket", "'too big' is 'too big' !!!");
 230                     }
 231                     adaptor.incSnmpSilentDrops(1);
 232                 }
 233             }
 234         }
 235 
 236         return respPacket ;
 237     }
 238 
 239     /**
 240      * Here we make a response message from a request message.
 241      * We return null if there is no message to reply.
 242      */
 243     private SnmpMessage makeResponseMessage(SnmpMessage reqMsg) {
 244         SnmpMessage respMsg = null ;
 245 
 246         // Transform the request message into a request pdu
 247         //
 248         SnmpPduPacket reqPdu;
 249         Object userData = null;
 250         try {
 251             reqPdu = (SnmpPduPacket)pduFactory.decodeSnmpPdu(reqMsg) ;
 252             if (reqPdu != null && userDataFactory != null)
 253                 userData = userDataFactory.allocateUserData(reqPdu);
 254         }
 255         catch(SnmpStatusException x) {
 256             reqPdu = null ;
 257             SnmpAdaptorServer snmpServer = (SnmpAdaptorServer)adaptorServer ;
 258             snmpServer.incSnmpInASNParseErrs(1) ;
 259             if (x.getStatus()== SnmpDefinitions.snmpWrongSnmpVersion)
 260                 snmpServer.incSnmpInBadVersions(1) ;
 261             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 262                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 263                     "makeResponseMessage", "message decoding failed", x);
 264             }
 265         }
 266 
 267         // Make the response pdu if any
 268         //
 269         SnmpPduPacket respPdu = null ;
 270         if (reqPdu != null) {
 271             respPdu = makeResponsePdu(reqPdu,userData) ;
 272             try {
 273                 if (userDataFactory != null)
 274                     userDataFactory.releaseUserData(userData,respPdu);
 275             } catch (SnmpStatusException x) {
 276                 respPdu = null;
 277             }
 278         }
 279 
 280         // Try to transform the response pdu into a response message if any
 281         //
 282         if (respPdu != null) {
 283             try {
 284                 respMsg = (SnmpMessage)pduFactory.
 285                     encodeSnmpPdu(respPdu, packet.getData().length) ;
 286             }
 287             catch(SnmpStatusException x) {
 288                 respMsg = null ;
 289                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 290                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 291                         "makeResponseMessage", "failure when encoding the response message", x);
 292                 }
 293             }
 294             catch(SnmpTooBigException x) {
 295                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 296                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 297                         "makeResponseMessage", "response message is too big");
 298                 }
 299 
 300                 try {
 301                     // if the PDU is too small, why should we try to do
 302                     // recovery ?
 303                     //
 304                     if (packet.getData().length <=32)
 305                         throw x;
 306                     int pos= x.getVarBindCount();
 307                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 308                         SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 309                             "makeResponseMessage", "fail on element" + pos);
 310                     }
 311                     int old;
 312                     while (true) {
 313                         try {
 314                             respPdu = reduceResponsePdu(reqPdu, respPdu, pos) ;
 315                             respMsg = (SnmpMessage)pduFactory.
 316                                 encodeSnmpPdu(respPdu,
 317                                               packet.getData().length -32) ;
 318                             break;
 319                         } catch (SnmpTooBigException xx) {
 320                             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 321                                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 322                                     "makeResponseMessage", "response message is still too big");
 323                             }
 324                             old= pos;
 325                             pos= xx.getVarBindCount();
 326                             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 327                                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 328                                     "makeResponseMessage","fail on element" + pos);
 329                             }
 330                             if (pos == old) {
 331                                 // we can not go any further in trying to
 332                                 // reduce the message !
 333                                 //
 334                                 throw xx;
 335                             }
 336                         }
 337                     }// end of loop
 338                 } catch(SnmpStatusException xx) {
 339                     respMsg = null ;
 340                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 341                         SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 342                            "makeResponseMessage", "failure when encoding the response message", xx);
 343                     }
 344                 }
 345                 catch(SnmpTooBigException xx) {
 346                     try {
 347                         respPdu = newTooBigPdu(reqPdu) ;
 348                         respMsg = (SnmpMessage)pduFactory.
 349                             encodeSnmpPdu(respPdu, packet.getData().length) ;
 350                     }
 351                     catch(SnmpTooBigException xxx) {
 352                         respMsg = null ;
 353                         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 354                             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 355                                "makeResponseMessage", "'too big' is 'too big' !!!");
 356                         }
 357                         adaptor.incSnmpSilentDrops(1);
 358                     }
 359                     catch(Exception xxx) {
 360                         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 361                             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 362                                "makeResponseMessage", "Got unexpected exception", xxx);
 363                         }
 364                         respMsg = null ;
 365                     }
 366                 }
 367                 catch(Exception xx) {
 368                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 369                         SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 370                            "makeResponseMessage", "Got unexpected exception", xx);
 371                     }
 372                     respMsg = null ;
 373                 }
 374             }
 375         }
 376         return respMsg ;
 377     }
 378 
 379     /**
 380      * Here we make a response pdu from a request pdu.
 381      * We return null if there is no pdu to reply.
 382      */
 383     private SnmpPduPacket makeResponsePdu(SnmpPduPacket reqPdu,
 384                                           Object userData) {
 385 
 386         SnmpAdaptorServer snmpServer = (SnmpAdaptorServer)adaptorServer ;
 387         SnmpPduPacket respPdu = null ;
 388 
 389         snmpServer.updateRequestCounters(reqPdu.type) ;
 390         if (reqPdu.varBindList != null)
 391             snmpServer.updateVarCounters(reqPdu.type,
 392                                          reqPdu.varBindList.length) ;
 393 
 394         if (checkPduType(reqPdu)) {
 395             respPdu = checkAcl(reqPdu) ;
 396             if (respPdu == null) { // reqPdu is accepted by ACLs
 397                 if (mibs.size() < 1) {
 398                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 399                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 400                            "makeResponsePdu", "Request " + reqPdu.requestId +
 401                            " received but no MIB registered.");
 402                     }
 403                     return makeNoMibErrorPdu((SnmpPduRequest)reqPdu, userData);
 404                 }
 405                 switch(reqPdu.type) {
 406                 case SnmpPduPacket.pduGetRequestPdu:
 407                 case SnmpPduPacket.pduGetNextRequestPdu:
 408                 case SnmpPduPacket.pduSetRequestPdu:
 409                     respPdu = makeGetSetResponsePdu((SnmpPduRequest)reqPdu,
 410                                                     userData) ;
 411                     break ;
 412 
 413                 case SnmpPduPacket.pduGetBulkRequestPdu:
 414                     respPdu = makeGetBulkResponsePdu((SnmpPduBulk)reqPdu,
 415                                                      userData) ;
 416                     break ;
 417                 }
 418             }
 419             else { // reqPdu is rejected by ACLs
 420                 // respPdu contains the error response to be sent.
 421                 // We send this response only if authResEnabled is true.
 422                 if (!snmpServer.getAuthRespEnabled()) { // No response should be sent
 423                     respPdu = null ;
 424                 }
 425                 if (snmpServer.getAuthTrapEnabled()) { // A trap must be sent
 426                     try {
 427                         snmpServer.snmpV1Trap(SnmpPduTrap.
 428                                               trapAuthenticationFailure, 0,
 429                                               new SnmpVarBindList()) ;
 430                     }
 431                     catch(Exception x) {
 432                         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 433                             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 434                                "makeResponsePdu", "Failure when sending authentication trap", x);
 435                         }
 436                     }
 437                 }
 438             }
 439         }
 440         return respPdu ;
 441     }
 442 
 443     //
 444     // Generates a response packet, filling the values in the
 445     // varbindlist with one of endOfMibView, noSuchObject, noSuchInstance
 446     // according to the value of <code>status</code>
 447     //
 448     // @param statusTag should be one of:
 449     //        <li>SnmpDataTypeEnums.errEndOfMibViewTag</li>
 450     //        <li>SnmpDataTypeEnums.errNoSuchObjectTag</li>
 451     //        <li>SnmpDataTypeEnums.errNoSuchInstanceTag</li>
 452     //
 453     SnmpPduPacket makeErrorVarbindPdu(SnmpPduPacket req, int statusTag) {
 454 
 455         final SnmpVarBind[] vblist = req.varBindList;
 456         final int length = vblist.length;
 457 
 458         switch (statusTag) {
 459         case SnmpDataTypeEnums.errEndOfMibViewTag:
 460             for (int i=0 ; i<length ; i++)
 461                 vblist[i].value = SnmpVarBind.endOfMibView;
 462             break;
 463         case SnmpDataTypeEnums.errNoSuchObjectTag:
 464             for (int i=0 ; i<length ; i++)
 465                 vblist[i].value = SnmpVarBind.noSuchObject;
 466             break;
 467         case SnmpDataTypeEnums.errNoSuchInstanceTag:
 468             for (int i=0 ; i<length ; i++)
 469                 vblist[i].value = SnmpVarBind.noSuchInstance;
 470             break;
 471         default:
 472             return newErrorResponsePdu(req,snmpRspGenErr,1);
 473         }
 474         return newValidResponsePdu(req,vblist);
 475     }
 476 
 477     // Generates an appropriate response when no mib is registered in
 478     // the adaptor.
 479     //
 480     // <li>If the version is V1:</li>
 481     // <ul><li>Generates a NoSuchName error V1 response PDU</li></ul>
 482     // <li>If the version is V2:</li>
 483     // <ul><li>If the request is a GET, fills the varbind list with
 484     //         NoSuchObject's</li>
 485     //     <li>If the request is a GET-NEXT/GET-BULK, fills the varbind
 486     //         list with EndOfMibView's</li>
 487     //     <li>If the request is a SET, generates a NoAccess error V2
 488     //          response PDU</li>
 489     // </ul>
 490     //
 491     //
 492     SnmpPduPacket makeNoMibErrorPdu(SnmpPduRequest req, Object userData) {
 493         // There is no agent registered
 494         //
 495         if (req.version == SnmpDefinitions.snmpVersionOne) {
 496             // Version 1: => NoSuchName
 497             return
 498                 newErrorResponsePdu(req,snmpRspNoSuchName,1);
 499         } else if (req.version == SnmpDefinitions.snmpVersionTwo) {
 500             // Version 2: => depends on PDU type
 501             switch (req.type) {
 502             case pduSetRequestPdu :
 503             case pduWalkRequest :
 504                 // SET request => NoAccess
 505                 return
 506                     newErrorResponsePdu(req,snmpRspNoAccess,1);
 507             case pduGetRequestPdu :
 508                 // GET request => NoSuchObject
 509                 return
 510                     makeErrorVarbindPdu(req,SnmpDataTypeEnums.
 511                                         errNoSuchObjectTag);
 512             case pduGetNextRequestPdu :
 513             case pduGetBulkRequestPdu :
 514                 // GET-NEXT or GET-BULK => EndOfMibView
 515                 return
 516                     makeErrorVarbindPdu(req,SnmpDataTypeEnums.
 517                                         errEndOfMibViewTag);
 518             default:
 519             }
 520         }
 521         // Something wrong here: => snmpRspGenErr
 522         return newErrorResponsePdu(req,snmpRspGenErr,1);
 523     }
 524 
 525     /**
 526      * Here we make the response pdu from a get/set request pdu.
 527      * At this level, the result is never null.
 528      */
 529     private SnmpPduPacket makeGetSetResponsePdu(SnmpPduRequest req,
 530                                                 Object userData) {
 531 
 532         // Create the trhead group specific for handling sub-requests
 533         // associated to the current request. Use the invoke id
 534         //
 535         // Nice idea to use a thread group on a request basis.
 536         // However the impact on performance is terrible !
 537         // theGroup= new ThreadGroup(thread.getThreadGroup(),
 538         //                "request " + String.valueOf(req.requestId));
 539 
 540         // Let's build the varBindList for the response pdu
 541         //
 542 
 543         if (req.varBindList == null) {
 544             // Good ! Let's make a full response pdu.
 545             //
 546             return newValidResponsePdu(req, null) ;
 547         }
 548 
 549         // First we need to split the request into subrequests
 550         //
 551         splitRequest(req);
 552         int nbSubRequest= subs.size();
 553         if (nbSubRequest == 1)
 554             return turboProcessingGetSet(req,userData);
 555 
 556 
 557         // Execute all the subrequests resulting from the split of the
 558         // varbind list.
 559         //
 560         SnmpPduPacket result= executeSubRequest(req,userData);
 561         if (result != null)
 562             // It means that an error occurred. The error is already
 563             // formatted by the executeSubRequest
 564             // method.
 565             return result;
 566 
 567         // So far so good. So we need to concatenate all the answers.
 568         //
 569         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 570             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 571                "makeGetSetResponsePdu",
 572                "Build the unified response for request " + req.requestId);
 573         }
 574         return mergeResponses(req);
 575     }
 576 
 577     /**
 578      * The method runs all the sub-requests associated to the current
 579      * instance of SnmpRequestHandler.
 580      */
 581     private SnmpPduPacket executeSubRequest(SnmpPduPacket req,
 582                                             Object userData) {
 583 
 584         int errorStatus = SnmpDefinitions.snmpRspNoError ;
 585 
 586         int i;
 587         // If it's a set request, we must first check any varBind
 588         //
 589         if (req.type == pduSetRequestPdu) {
 590 
 591             i=0;
 592             for(Enumeration<SnmpSubRequestHandler> e= subs.elements(); e.hasMoreElements() ; i++) {
 593                 // Indicate to the sub request that a check must be invoked ...
 594                 // OK we should have defined out own tag for that !
 595                 //
 596                 SnmpSubRequestHandler sub= e.nextElement();
 597                 sub.setUserData(userData);
 598                 sub.type= pduWalkRequest;
 599 
 600                 sub.run();
 601 
 602                 sub.type= pduSetRequestPdu;
 603 
 604                 if (sub.getErrorStatus() != SnmpDefinitions.snmpRspNoError) {
 605                     // No point to go any further.
 606                     //
 607                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 608                         SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 609                            "executeSubRequest", "an error occurs");
 610                     }
 611 
 612                     return newErrorResponsePdu(req, errorStatus,
 613                                                sub.getErrorIndex() + 1) ;
 614                 }
 615             }
 616         }// end processing check operation for a set PDU.
 617 
 618         // Let's start the sub-requests.
 619         //
 620         i=0;
 621         for(Enumeration<SnmpSubRequestHandler> e= subs.elements(); e.hasMoreElements() ;i++) {
 622             SnmpSubRequestHandler sub= e.nextElement();
 623         /* NPCTE fix for bugId 4492741, esc 0, 16-August 2001 */
 624             sub.setUserData(userData);
 625         /* end of NPCTE fix for bugId 4492741 */
 626 
 627             sub.run();
 628 
 629             if (sub.getErrorStatus() != SnmpDefinitions.snmpRspNoError) {
 630                 // No point to go any further.
 631                 //
 632                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 633                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 634                        "executeSubRequest", "an error occurs");
 635                 }
 636 
 637                 return newErrorResponsePdu(req, errorStatus,
 638                                            sub.getErrorIndex() + 1) ;
 639             }
 640         }
 641 
 642         // everything is ok
 643         //
 644         return null;
 645     }
 646 
 647     /**
 648      * Optimize when there is only one sub request
 649      */
 650     private SnmpPduPacket turboProcessingGetSet(SnmpPduRequest req,
 651                                                 Object userData) {
 652 
 653         int errorStatus;
 654         SnmpSubRequestHandler sub = subs.elements().nextElement();
 655         sub.setUserData(userData);
 656 
 657         // Indicate to the sub request that a check must be invoked ...
 658         // OK we should have defined out own tag for that !
 659         //
 660         if (req.type == SnmpDefinitions.pduSetRequestPdu) {
 661             sub.type= pduWalkRequest;
 662             sub.run();
 663             sub.type= pduSetRequestPdu;
 664 
 665             // Check the error status.
 666             //
 667             errorStatus= sub.getErrorStatus();
 668             if (errorStatus != SnmpDefinitions.snmpRspNoError) {
 669                 // No point to go any further.
 670                 //
 671                 return newErrorResponsePdu(req, errorStatus,
 672                                            sub.getErrorIndex() + 1) ;
 673             }
 674         }
 675 
 676         // process the operation
 677         //
 678 
 679         sub.run();
 680         errorStatus= sub.getErrorStatus();
 681         if (errorStatus != SnmpDefinitions.snmpRspNoError) {
 682             // No point to go any further.
 683             //
 684             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 685                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 686                    "turboProcessingGetSet", "an error occurs");
 687             }
 688             int realIndex= sub.getErrorIndex() + 1;
 689             return newErrorResponsePdu(req, errorStatus, realIndex) ;
 690         }
 691 
 692         // So far so good. So we need to concatenate all the answers.
 693         //
 694 
 695         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 696             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 697                "turboProcessingGetSet",  "build the unified response for request "
 698                 + req.requestId);
 699         }
 700         return mergeResponses(req);
 701     }
 702 
 703     /**
 704      * Here we make the response pdu for a bulk request.
 705      * At this level, the result is never null.
 706      */
 707     private SnmpPduPacket makeGetBulkResponsePdu(SnmpPduBulk req,
 708                                                  Object userData) {
 709 
 710         SnmpVarBind[] respVarBindList;
 711 
 712         // RFC 1905, Section 4.2.3, p14
 713         int L = req.varBindList.length ;
 714         int N = Math.max(Math.min(req.nonRepeaters, L), 0) ;
 715         int M = Math.max(req.maxRepetitions, 0) ;
 716         int R = L - N ;
 717 
 718         if (req.varBindList == null) {
 719             // Good ! Let's make a full response pdu.
 720             //
 721             return newValidResponsePdu(req, null) ;
 722         }
 723 
 724         // Split the request into subrequests.
 725         //
 726         splitBulkRequest(req, N, M, R);
 727         SnmpPduPacket result= executeSubRequest(req,userData);
 728         if (result != null)
 729             return result;
 730 
 731         respVarBindList= mergeBulkResponses(N + (M * R));
 732 
 733         // Now we remove useless trailing endOfMibView.
 734         //
 735         int m2 ; // respVarBindList[m2] item and next are going to be removed
 736         int t = respVarBindList.length ;
 737         while ((t > N) && (respVarBindList[t-1].
 738                            value.equals(SnmpVarBind.endOfMibView))) {
 739             t-- ;
 740         }
 741         if (t == N)
 742             m2 = N + R ;
 743         else
 744             m2 = N + ((t -1 -N) / R + 2) * R ; // Trivial, of course...
 745         if (m2 < respVarBindList.length) {
 746             SnmpVarBind[] truncatedList = new SnmpVarBind[m2] ;
 747             for (int i = 0 ; i < m2 ; i++) {
 748                 truncatedList[i] = respVarBindList[i] ;
 749             }
 750             respVarBindList = truncatedList ;
 751         }
 752 
 753         // Good ! Let's make a full response pdu.
 754         //
 755         return newValidResponsePdu(req, respVarBindList) ;
 756     }
 757 
 758     /**
 759      * Check the type of the pdu: only the get/set/bulk request
 760      * are accepted.
 761      */
 762     private boolean checkPduType(SnmpPduPacket pdu) {
 763 
 764         boolean result;
 765 
 766         switch(pdu.type) {
 767 
 768         case SnmpDefinitions.pduGetRequestPdu:
 769         case SnmpDefinitions.pduGetNextRequestPdu:
 770         case SnmpDefinitions.pduSetRequestPdu:
 771         case SnmpDefinitions.pduGetBulkRequestPdu:
 772             result = true ;
 773             break;
 774 
 775         default:
 776             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 777                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 778                    "checkPduType", "cannot respond to this kind of PDU");
 779             }
 780             result = false ;
 781             break;
 782         }
 783 
 784         return result ;
 785     }
 786 
 787     /**
 788      * Check if the specified pdu is conform to the ACL.
 789      * This method returns null if the pdu is ok. If not, it returns
 790      * the response pdu to be replied.
 791      */
 792     private SnmpPduPacket checkAcl(SnmpPduPacket pdu) {
 793         SnmpPduPacket response = null ;
 794         String community = new String(pdu.community) ;
 795 
 796         // We check the pdu type and create an error response if
 797         // the check failed.
 798         //
 799         if (ipacl != null) {
 800             if (pdu.type == SnmpDefinitions.pduSetRequestPdu) {
 801                 if (!ipacl.checkWritePermission(pdu.address, community)) {
 802                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 803                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 804                            "checkAcl", "sender is " + pdu.address +
 805                               " with " + community +". Sender has no write permission");
 806                     }
 807                     int err = SnmpSubRequestHandler.
 808                         mapErrorStatus(SnmpDefinitions.
 809                                        snmpRspAuthorizationError,
 810                                        pdu.version, pdu.type);
 811                     response = newErrorResponsePdu(pdu, err, 0) ;
 812                 }
 813                 else {
 814                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 815                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 816                            "checkAcl", "sender is " + pdu.address +
 817                               " with " + community +". Sender has write permission");
 818                     }
 819                 }
 820             }
 821             else {
 822                 if (!ipacl.checkReadPermission(pdu.address, community)) {
 823                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 824                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 825                            "checkAcl", "sender is " + pdu.address +
 826                               " with " + community +". Sender has no read permission");
 827                     }
 828                     int err = SnmpSubRequestHandler.
 829                         mapErrorStatus(SnmpDefinitions.
 830                                        snmpRspAuthorizationError,
 831                                        pdu.version, pdu.type);
 832                     response = newErrorResponsePdu(pdu,
 833                                                    err,
 834                                                    0);
 835                     SnmpAdaptorServer snmpServer =
 836                         (SnmpAdaptorServer)adaptorServer;
 837                     snmpServer.updateErrorCounters(SnmpDefinitions.
 838                                                    snmpRspNoSuchName);
 839                 }
 840                 else {
 841                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 842                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 843                            "checkAcl", "sender is " + pdu.address +
 844                               " with " + community +". Sender has read permission");
 845                     }
 846                 }
 847             }
 848         }
 849 
 850         // If the response is not null, this means the pdu is rejected.
 851         // So let's update the statistics.
 852         //
 853         if (response != null) {
 854             SnmpAdaptorServer snmpServer = (SnmpAdaptorServer)adaptorServer ;
 855             snmpServer.incSnmpInBadCommunityUses(1) ;
 856             if (ipacl.checkCommunity(community) == false)
 857                 snmpServer.incSnmpInBadCommunityNames(1) ;
 858         }
 859 
 860         return response ;
 861     }
 862 
 863     /**
 864      * Make a response pdu with the specified error status and index.
 865      * NOTE: the response pdu share its varBindList with the request pdu.
 866      */
 867     private SnmpPduRequest newValidResponsePdu(SnmpPduPacket reqPdu,
 868                                                SnmpVarBind[] varBindList) {
 869         SnmpPduRequest result = new SnmpPduRequest() ;
 870 
 871         result.address = reqPdu.address ;
 872         result.port = reqPdu.port ;
 873         result.version = reqPdu.version ;
 874         result.community = reqPdu.community ;
 875         result.type = SnmpPduRequest.pduGetResponsePdu ;
 876         result.requestId = reqPdu.requestId ;
 877         result.errorStatus = SnmpDefinitions.snmpRspNoError ;
 878         result.errorIndex = 0 ;
 879         result.varBindList = varBindList ;
 880 
 881         ((SnmpAdaptorServer)adaptorServer).
 882             updateErrorCounters(result.errorStatus) ;
 883 
 884         return result ;
 885     }
 886 
 887     /**
 888      * Make a response pdu with the specified error status and index.
 889      * NOTE: the response pdu share its varBindList with the request pdu.
 890      */
 891     private SnmpPduRequest newErrorResponsePdu(SnmpPduPacket req,int s,int i) {
 892         SnmpPduRequest result = newValidResponsePdu(req, null) ;
 893         result.errorStatus = s ;
 894         result.errorIndex = i ;
 895         result.varBindList = req.varBindList ;
 896 
 897         ((SnmpAdaptorServer)adaptorServer).
 898             updateErrorCounters(result.errorStatus) ;
 899 
 900         return result ;
 901     }
 902 
 903     private SnmpMessage newTooBigMessage(SnmpMessage reqMsg)
 904         throws SnmpTooBigException {
 905         SnmpMessage result = null ;
 906         SnmpPduPacket reqPdu;
 907 
 908         try {
 909             reqPdu = (SnmpPduPacket)pduFactory.decodeSnmpPdu(reqMsg) ;
 910             if (reqPdu != null) {
 911                 SnmpPduPacket respPdu = newTooBigPdu(reqPdu) ;
 912                 result = (SnmpMessage)pduFactory.
 913                     encodeSnmpPdu(respPdu, packet.getData().length) ;
 914             }
 915         }
 916         catch(SnmpStatusException x) {
 917             // This should not occur because decodeIncomingRequest has normally
 918             // been successfully called before.
 919             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 920                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 921                    "newTooBigMessage", "Internal error", x);
 922             }
 923             throw new InternalError(x) ;
 924         }
 925 
 926         return result ;
 927     }
 928 
 929     private SnmpPduPacket newTooBigPdu(SnmpPduPacket req) {
 930         SnmpPduRequest result =
 931             newErrorResponsePdu(req, SnmpDefinitions.snmpRspTooBig, 0) ;
 932         result.varBindList = null ;
 933         return result ;
 934     }
 935 
 936     private SnmpPduPacket reduceResponsePdu(SnmpPduPacket req,
 937                                             SnmpPduPacket resp,
 938                                             int acceptedVbCount)
 939         throws SnmpTooBigException {
 940 
 941         // Reduction can be attempted only on bulk response
 942         //
 943         if (req.type != SnmpPduPacket.pduGetBulkRequestPdu) {
 944             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 945                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 946                    "reduceResponsePdu", "cannot remove anything");
 947             }
 948             throw new SnmpTooBigException(acceptedVbCount) ;
 949         }
 950 
 951         // We're going to reduce the varbind list.
 952         // First determine which items should be removed.
 953         // Next duplicate and replace the existing list by the reduced one.
 954         //
 955         // acceptedVbCount is the number of varbind which have been
 956         // successfully encoded before reaching bufferSize:
 957         //   * when it is >= 2, we split the varbindlist at this
 958         //     position (-1 to be safe),
 959         //   * when it is 1, we only put one (big?) item in the varbindlist
 960         //   * when it is 0 (in fact, acceptedVbCount is not available),
 961         //     we split the varbindlist by 2.
 962         //
 963         int vbCount;
 964         if (acceptedVbCount >= 3)
 965             vbCount = Math.min(acceptedVbCount - 1, resp.varBindList.length) ;
 966         else if (acceptedVbCount == 1)
 967             vbCount = 1 ;
 968         else // acceptedCount == 0 ie it is unknown
 969             vbCount = resp.varBindList.length / 2 ;
 970 
 971         if (vbCount < 1) {
 972             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 973                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 974                    "reduceResponsePdu", "cannot remove anything");
 975             }
 976             throw new SnmpTooBigException(acceptedVbCount) ;
 977         }
 978         else {
 979             SnmpVarBind[] newVbList = new SnmpVarBind[vbCount] ;
 980             for (int i = 0 ; i < vbCount ; i++) {
 981                 newVbList[i] = resp.varBindList[i] ;
 982             }
 983             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 984                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 985                    "reduceResponsePdu", (resp.varBindList.length - newVbList.length) +
 986                     " items have been removed");
 987             }
 988             resp.varBindList = newVbList ;
 989         }
 990 
 991         return resp ;
 992     }
 993 
 994     /**
 995      * The method takes the incoming requests and split it into subrequests.
 996      */
 997     private void splitRequest(SnmpPduRequest req) {
 998 
 999         int nbAgents= mibs.size();
1000         SnmpMibAgent agent = mibs.firstElement();
1001         if (nbAgents == 1) {
1002             // Take all the oids contained in the request and
1003             //
1004             subs.put(agent, new SnmpSubRequestHandler(agent, req, true));
1005             return;
1006         }
1007 
1008         // For the get next operation we are going to send the varbind list
1009         // to all agents
1010         //
1011         if (req.type == pduGetNextRequestPdu) {
1012             for(Enumeration<SnmpMibAgent> e= mibs.elements(); e.hasMoreElements(); ) {
1013                 final SnmpMibAgent ag= e.nextElement();
1014                 subs.put(ag, new SnmpSubNextRequestHandler(adaptor, ag, req));
1015             }
1016             return;
1017         }
1018 
1019         int nbReqs= req.varBindList.length;
1020         SnmpVarBind[] vars= req.varBindList;
1021         SnmpSubRequestHandler sub;
1022         for(int i=0; i < nbReqs; i++) {
1023             agent= root.getAgentMib(vars[i].oid);
1024             sub= subs.get(agent);
1025             if (sub == null) {
1026                 // We need to create the sub request handler and update
1027                 // the hashtable
1028                 //
1029                 sub= new SnmpSubRequestHandler(agent, req);
1030                 subs.put(agent, sub);
1031             }
1032 
1033             // Update the translation table within the subrequest
1034             //
1035             sub.updateRequest(vars[i], i);
1036         }
1037     }
1038 
1039     /**
1040      * The method takes the incoming get bulk requests and split it into
1041      * subrequests.
1042      */
1043     private void splitBulkRequest(SnmpPduBulk req,
1044                                   int nonRepeaters,
1045                                   int maxRepetitions,
1046                                   int R) {
1047         // Send the getBulk to all agents
1048         //
1049         for(Enumeration<SnmpMibAgent> e= mibs.elements(); e.hasMoreElements(); ) {
1050             final SnmpMibAgent agent = e.nextElement();
1051 
1052             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1053                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1054                    "splitBulkRequest", "Create a sub with : " + agent + " " + nonRepeaters
1055                    + " " + maxRepetitions + " " + R);
1056             }
1057 
1058             subs.put(agent,
1059                      new SnmpSubBulkRequestHandler(adaptor,
1060                                                    agent,
1061                                                    req,
1062                                                    nonRepeaters,
1063                                                    maxRepetitions,
1064                                                    R));
1065         }
1066     }
1067 
1068     private SnmpPduPacket mergeResponses(SnmpPduRequest req) {
1069 
1070         if (req.type == pduGetNextRequestPdu) {
1071             return mergeNextResponses(req);
1072         }
1073 
1074         SnmpVarBind[] result= req.varBindList;
1075 
1076         // Go through the list of subrequests and concatenate.
1077         // Hopefully, by now all the sub-requests should be finished
1078         //
1079         for(Enumeration<SnmpSubRequestHandler> e= subs.elements(); e.hasMoreElements();) {
1080             SnmpSubRequestHandler sub= e.nextElement();
1081             sub.updateResult(result);
1082         }
1083         return newValidResponsePdu(req,result);
1084     }
1085 
1086     private SnmpPduPacket mergeNextResponses(SnmpPduRequest req) {
1087         int max= req.varBindList.length;
1088         SnmpVarBind[] result= new SnmpVarBind[max];
1089 
1090         // Go through the list of subrequests and concatenate.
1091         // Hopefully, by now all the sub-requests should be finished
1092         //
1093         for(Enumeration<SnmpSubRequestHandler> e= subs.elements(); e.hasMoreElements();) {
1094             SnmpSubRequestHandler sub= e.nextElement();
1095             sub.updateResult(result);
1096         }
1097 
1098         if (req.version == snmpVersionTwo) {
1099             return newValidResponsePdu(req,result);
1100         }
1101 
1102         // In v1 make sure there is no endOfMibView ...
1103         //
1104         for(int i=0; i < max; i++) {
1105             SnmpValue val= result[i].value;
1106             if (val == SnmpVarBind.endOfMibView)
1107                 return newErrorResponsePdu(req,
1108                                    SnmpDefinitions.snmpRspNoSuchName, i+1);
1109         }
1110 
1111         // So far so good ...
1112         //
1113         return newValidResponsePdu(req,result);
1114     }
1115 
1116     private SnmpVarBind[] mergeBulkResponses(int size) {
1117         // Let's allocate the array for storing the result
1118         //
1119         SnmpVarBind[] result= new SnmpVarBind[size];
1120         for(int i= size-1; i >=0; --i) {
1121             result[i]= new SnmpVarBind();
1122             result[i].value= SnmpVarBind.endOfMibView;
1123         }
1124 
1125         // Go through the list of subrequests and concatenate.
1126         // Hopefully, by now all the sub-requests should be finished
1127         //
1128         for(Enumeration<SnmpSubRequestHandler> e= subs.elements(); e.hasMoreElements();) {
1129             SnmpSubRequestHandler sub= e.nextElement();
1130             sub.updateResult(result);
1131         }
1132 
1133         return result;
1134     }
1135 
1136     @Override
1137     protected String makeDebugTag() {
1138         return "SnmpRequestHandler[" + adaptorServer.getProtocol() + ":" +
1139             adaptorServer.getPort() + "]";
1140     }
1141 
1142     @Override
1143     Thread createThread(Runnable r) {
1144         return null;
1145     }
1146 
1147     static final private String InterruptSysCallMsg =
1148         "Interrupted system call";
1149 }