AgentXcpp  Revision:0.1
Internals Documentation
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends
/home/tanjeff/projekte/agentxcpp/src/master_proxy.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2011-2012 Tanjeff-Nicolai Moos <tanjeff@cccmz.de>
00003  *
00004  * This file is part of the agentXcpp library.
00005  *
00006  * AgentXcpp is free software: you can redistribute it and/or modify
00007  * it under the terms of the AgentXcpp library license, version 1, which 
00008  * consists of the GNU General Public License and some additional 
00009  * permissions.
00010  *
00011  * AgentXcpp is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * See the AgentXcpp library license in the LICENSE file of this package 
00017  * for more details.
00018  */
00019 #include <boost/bind.hpp>
00020 
00021 #include "master_proxy.hpp"
00022 #include "OpenPDU.hpp"
00023 #include "ClosePDU.hpp"
00024 #include "ResponsePDU.hpp"
00025 #include "RegisterPDU.hpp"
00026 #include "GetPDU.hpp"
00027 #include "helper.hpp"
00028 #include "types.hpp"
00029 
00030 #include <iostream>
00031 using namespace std;
00032 
00033 using namespace agentxcpp;
00034 using namespace boost;
00035 using boost::shared_ptr;
00036 
00037 
00038 
00039 
00040 
00041 
00042 master_proxy::master_proxy(boost::asio::io_service* _io_service,
00043                            std::string _description,
00044                            byte_t _default_timeout,
00045                            oid _id,
00046                            std::string _filename) :
00047     io_service(_io_service),
00048     io_service_by_user(true),
00049     socket_file(_filename.c_str()),
00050     description(_description),
00051     default_timeout(_default_timeout),
00052     id(_id)
00053 {
00054     // Initialize connector (never use timeout=0)
00055     byte_t timeout;
00056     timeout = (this->default_timeout == 0) ? 1 : this->default_timeout;
00057     connection = new connector(shared_ptr<boost::asio::io_service>(io_service),
00058                                _filename.c_str(),
00059                                timeout);
00060 
00061     // Register this object as %PDU handler
00062     this->connection->register_handler( this );
00063 
00064     // Try to connect
00065     try
00066     {
00067         // throws disconnected:
00068         this->connect();
00069     }
00070     catch(disconnected)
00071     {
00072         // Ignore, stay disconnected
00073     }
00074 }
00075 
00076 
00077 
00078 master_proxy::master_proxy(std::string _description,
00079                            byte_t _default_timeout,
00080                            oid _id,
00081                            std::string _filename) :
00082     io_service(new boost::asio::io_service()),
00083     io_service_by_user(false),
00084     socket_file(_filename.c_str()),
00085     description(_description),
00086     default_timeout(_default_timeout),
00087     id(_id)
00088 {
00089     // Initialize connector (never use timeout=0)
00090     byte_t timeout;
00091     timeout = (this->default_timeout == 0) ? 1 : this->default_timeout;
00092     connection = new connector(shared_ptr<boost::asio::io_service>(io_service),
00093                                _filename.c_str(),
00094                                timeout);
00095 
00096     // Register this object as %PDU handler
00097     this->connection->register_handler( this );
00098 
00099     // Try to connect
00100     try
00101     {
00102         // throws disconnected:
00103         this->connect();
00104     }
00105     catch(disconnected)
00106     {
00107         // Ignore, stay disconnected
00108     }
00109     catch(...)
00110     {
00111         // Ignore, stay disconnected
00112     }
00113 }
00114 
00115 
00116 void master_proxy::connect()
00117 {
00118     if( this->connection->is_connected() )
00119     {
00120         // we are already connected -> nothing to do
00121         return;
00122     }
00123 
00124     // Clear registrations and variables
00125     registrations.clear();
00126     variables.clear();
00127 
00128     // Connect to endpoint
00129     try
00130     {
00131         this->connection->connect();
00132     }
00133     catch(boost::system::system_error)
00134     {
00135         throw;
00136     }
00137     // The response we expect from the master
00138     boost::shared_ptr<ResponsePDU> response;
00139 
00140     try
00141     {
00142         // Send OpenPDU
00143         OpenPDU openpdu;
00144         openpdu.set_timeout(default_timeout);
00145         openpdu.set_id(id);
00146         // throws disconnected and timeout_error:
00147         this->connection->send(openpdu);
00148 
00149         // Wait for response
00150         // throws disconnected and timeout_error:
00151         response = this->connection->wait_for_response(openpdu.get_packetID());
00152     }
00153     catch(disconnected)
00154     {
00155         throw; // forward
00156     }
00157     catch(timeout_error)
00158     {
00159         throw disconnected();
00160     }
00161 
00162     // Check for errors
00163     if(response->get_error() != ResponsePDU::noAgentXError)
00164     {
00165         // Some error occured, disconnect
00166         this->connection->disconnect();
00167         throw disconnected();
00168     }
00169 
00170     // All went fine, we are connected now
00171     this->sessionID = response->get_sessionID();
00172 }
00173 
00174 
00175 
00176 
00177 void master_proxy::disconnect(ClosePDU::reason_t reason)
00178 {
00179     if( ! this->connection->is_connected() )
00180     {
00181         // we are already disconnected -> nothing to do
00182         return;
00183     }
00184 
00185     // According to RFC 2741, 7.1.8. "Processing the agentx-Close-PDU", the 
00186     // master agent unregisters all MIB regions, frees all index values and all 
00187     // sysORID are removed. Thus no need to clean up before ClosePDU is sent.
00188 
00189     // The response we expect from the master
00190     boost::shared_ptr<ResponsePDU> response;
00191 
00192     // Try clean shutdown (ignore errors)
00193     try
00194     {
00195         // Unregister stuff if any
00196         std::list< boost::shared_ptr<RegisterPDU> >::const_iterator r;
00197         r = this->registrations.begin();
00198         while (r != this->registrations.end())
00199         {
00200             this->undo_registration(create_unregister_pdu(*r));
00201             r++;
00202         }
00203 
00204         // Send ClosePDU
00205         ClosePDU closepdu(this->sessionID, reason);
00206         // throws disconnected and timeout_error:
00207         this->connection->send(closepdu);
00208         
00209         // Wait for response
00210         // throws disconnected and timeout_error:
00211         response = this->connection->wait_for_response(closepdu.get_packetID());
00212 
00213         // Check for errors
00214         if(response->get_error() != ResponsePDU::noAgentXError)
00215         {
00216             // Some error occured. However, We will soon disconnect anyway.
00217             // -> ignore
00218             // TODO: Insert log message here.
00219         }
00220     }
00221     catch(...)
00222     {
00223         // We will soon disconnect anyway.
00224         // -> ignore all errors
00225     }
00226 
00227     // Finally: disconnect
00228     this->connection->disconnect();
00229 }
00230 
00231 master_proxy::~master_proxy()
00232 {
00233     // Disconnect from master agent
00234     this->disconnect(ClosePDU::reasonShutdown);
00235 
00236     // Destroy connection
00237     // Unregistering this object as %PDU handler is unneeded.
00238     delete this->connection;
00239 
00240     // Destroy io_service object if needed
00241     if( ! this->io_service_by_user )
00242     {
00243         delete this->io_service;
00244     }
00245 }
00246 
00247 
00248 void master_proxy::do_registration(boost::shared_ptr<RegisterPDU> pdu)
00249 {
00250     // Are we connected?
00251     if( ! is_connected())
00252     {
00253         throw(disconnected());
00254     }
00255 
00256     // Send RegisterPDU
00257     // (forward exceptions timeout_error and disconnected)
00258     this->connection->send(*pdu);
00259 
00260     // Wait for response
00261     // (forward exceptions timeout_error and disconnected)
00262     boost::shared_ptr<ResponsePDU> response;
00263     response = this->connection->wait_for_response(pdu->get_packetID());
00264 
00265     // Check Response
00266     switch(response->get_error())
00267     {
00268         // General errors:
00269 
00270         case ResponsePDU::parseError:
00271             // Oops, we sent a malformed PDU to the master
00272             throw(internal_error());
00273 
00274         case ResponsePDU::notOpen:
00275             // We checked the connection state before, but maybe we lost the 
00276             // connection during communication...
00277             throw(disconnected());
00278 
00279         case ResponsePDU::unsupportedContext:
00280             // We do currently not really support contexts in this library. An 
00281             // invalid context is thus probably an agentxcpp bug.
00282             throw(internal_error());
00283 
00284         case ResponsePDU::processingError:
00285             // master was unable to process the request
00286             throw(master_is_unable());
00287 
00288         case ResponsePDU::noAgentXError:
00289             // Hey, it worked!
00290             break;
00291 
00292         // Register-specific errors:
00293 
00294         case ResponsePDU::duplicateRegistration:
00295             throw(duplicate_registration());
00296 
00297         case ResponsePDU::requestDenied:
00298             throw(master_is_unwilling());
00299 
00300         default:
00301             // This is a case of can-not-happen. Probably the master is buggy.  
00302             // The agentxcpp library is bug-free of course ;-)
00303             // We throw a parse error meanwhile, because we didn't expect the 
00304             // response to look like that...
00305             throw(parse_error());
00306     }
00307 
00308     // Finish
00309     return;
00310 }
00311 
00312 
00313 
00314 
00315 
00316 void master_proxy::register_subtree(oid subtree,
00317                       byte_t priority,
00318                       byte_t timeout)
00319 {
00320     // Build PDU
00321     boost::shared_ptr<RegisterPDU> pdu(new RegisterPDU);
00322     pdu->set_subtree(subtree);
00323     pdu->set_priority(priority);
00324     pdu->set_timeout(timeout);
00325     pdu->set_sessionID(this->sessionID);
00326 
00327     // Sent PDU
00328     try
00329     {
00330         this->do_registration(pdu);
00331     }
00332     catch( internal_error )
00333     {
00334         // Huh, it seems that we sent a malformed PDU to the master. We convert 
00335         // this to parse_error.
00336         throw(parse_error());
00337     }
00338     catch(...)
00339     {
00340         // All other exceptions are forwarded unmodified:
00341         throw;
00342     }
00343 
00344     // Success: store registration
00345     this->registrations.push_back(pdu);
00346 
00347 }
00348 
00349 
00350 
00351 void master_proxy::unregister_subtree(oid subtree,
00352                                       byte_t priority)
00353 {
00354     // The UnregisterPDU
00355     boost::shared_ptr<UnregisterPDU> pdu;
00356 
00357     // Remove the registration from registrations list
00358     std::list< boost::shared_ptr<RegisterPDU> >::iterator r;
00359     r = this->registrations.begin();
00360     while (r != this->registrations.end())
00361     {
00362         if(   (*r)->get_priority() == priority
00363            && (*r)->get_subtree() == subtree
00364            && (*r)->get_range_subid() == 0
00365            && (*r)->get_upper_bound() == 0 )
00366         {
00367             // registration found
00368 
00369             // create UnregisterPDU
00370             pdu = create_unregister_pdu(*r);
00371 
00372             // remove registration from list, forward to next one
00373             r = registrations.erase(r);
00374         }
00375         else
00376         {
00377             // Ignore registration, inspect next one
00378             r++;
00379         }
00380     }
00381 
00382     // Sent PDU
00383     try
00384     {
00385         this->undo_registration(pdu);
00386     }
00387     catch( internal_error )
00388     {
00389         // Huh, it seems that we sent a malformed PDU to the master. We convert 
00390         // this to parse_error.
00391         throw(parse_error());
00392     }
00393     catch(...)
00394     {
00395         // All other exceptions are forwarded unmodified:
00396         throw;
00397     }
00398 }
00399 
00400 
00401 
00402 void master_proxy::undo_registration(boost::shared_ptr<UnregisterPDU> pdu)
00403 {
00404     // Are we connected?
00405     if( ! is_connected())
00406     {
00407         throw(disconnected());
00408     }
00409 
00410     // Send UnregisterPDU
00411     // (forward exceptions timeout_error and disconnected)
00412     this->connection->send(*pdu);
00413 
00414     // Wait for response
00415     // (forward exceptions timeout_error and disconnected)
00416     boost::shared_ptr<ResponsePDU> response;
00417     response = this->connection->wait_for_response(pdu->get_packetID());
00418 
00419     // Check Response
00420     switch(response->get_error())
00421     {
00422         // General errors:
00423 
00424         case ResponsePDU::parseError:
00425             // Oops, we sent a malformed PDU to the master
00426             throw(internal_error());
00427 
00428         case ResponsePDU::notOpen:
00429             // We checked the connection state before, but maybe we lost the 
00430             // connection during communication...
00431             throw(disconnected());
00432 
00433         case ResponsePDU::unsupportedContext:
00434             // We do currently not really support contexts in this library. An 
00435             // invalid context is thus probably an agentxcpp bug.
00436             throw(internal_error());
00437 
00438         case ResponsePDU::processingError:
00439             // master was unable to process the request
00440             throw(master_is_unable());
00441 
00442         case ResponsePDU::noAgentXError:
00443             // Hey, it worked!
00444             break;
00445 
00446         // Register-specific errors:
00447 
00448         case ResponsePDU::unknownRegistration:
00449             throw(unknown_registration());
00450 
00451         default:
00452             // This is a case of can-not-happen. Probably the master is buggy. 
00453             // The agentxcpp library is bug-free of course ;-)
00454             // We throw a parse error meanwhile, because we didn't expect the 
00455             // response to look like that...
00456             throw(parse_error());
00457     }
00458 
00459     // Finish
00460     return;
00461 }
00462 
00463 
00464 
00465 boost::shared_ptr<UnregisterPDU> master_proxy::create_unregister_pdu(
00466                                     boost::shared_ptr<RegisterPDU> pdu)
00467 {
00468     boost::shared_ptr<UnregisterPDU> new_pdu(new UnregisterPDU());
00469     new_pdu->set_subtree( pdu->get_subtree() );
00470     new_pdu->set_range_subid( pdu->get_range_subid() );
00471     new_pdu->set_upper_bound( pdu->get_upper_bound() );
00472     new_pdu->set_priority( pdu->get_priority() );
00473 
00474     return new_pdu;
00475 }
00476 
00477 
00478 
00479 
00480 void master_proxy::handle_pdu(shared_ptr<PDU> pdu, int error)
00481 {
00482     if(error == -2)
00483     {
00484         // Version error
00485         // TODO: provide better handling. For now: ignore PDU
00486         return;
00487     }
00488 
00489     // Here we process all PDU's except ResponsePDU's, according to RFC 2741, 
00490     // 7.2.2. "Subagent Processing".
00491 
00492     // Step 1) Create a response and copy header from received pdu
00493     //
00494     // Notes:
00495     //   - The version, type and payload_length fields are filled
00496     //     automatically
00497     //   - The flags are not copied, because they have
00498     //     other meanings in ResponsePDU's.
00499     //   - Context is not yet supported.
00500     ResponsePDU response;
00501     response.set_sessionID( pdu->get_sessionID() );
00502     response.set_transactionID( pdu->get_transactionID() );
00503     response.set_packetID( pdu->get_packetID() );
00504     response.set_error(ResponsePDU::noAgentXError);
00505     response.set_index(0);
00506 
00507     // Step 2) Was there a parse error?
00508     //
00509     // Note: Error numbers are documented in connector.hpp
00510     if(error == -1)
00511     {
00512         // Step 4b) Stop processing, don't send reply.
00513         //
00514         // Note: we cannot determine whether the header was sucessfully
00515         //       parsed, therefore we simply ignore the PDU. We don't send a 
00516         //       parseError indication to the master agent.
00517         //
00518         // TODO: Send response if the header was parsed sucessfully.
00519         response.set_error(ResponsePDU::parseError);
00520         return;
00521     }
00522 
00523     // Step 3) Is the session valid?
00524     if(pdu->get_sessionID() != this->sessionID)
00525     {
00526         response.set_error(ResponsePDU::notOpen);
00527 
00528         // Step 4a) Stop processing the PDU. Send response.
00529         try
00530         {
00531             this->connection->send(response);
00532         }
00533         catch(timeout_error)
00534         {
00535             // connection loss. Ignore.
00536         }
00537         catch(disconnected)
00538         {
00539             // No connection. Ignore.
00540         }
00541 
00542         return;
00543     }
00544 
00545 
00546     // Is it a GetPDU?
00547     shared_ptr<GetPDU> get_pdu;
00548     if( (get_pdu = dynamic_pointer_cast<GetPDU>(pdu)) != 0 )
00549     {
00550         // Handling according to
00551         // RFC 2741, 7.2.3.1 "Subagent Processing of the agentx-Get-PDU"
00552 
00553         // Extract searchRange list
00554         vector<oid> sr = get_pdu->get_sr();
00555 
00556         // Iterate over list and handle each oid separately
00557         vector<oid>::const_iterator i;
00558         uint16_t index = 1;     // Index is 1-based (RFC 2741,
00559                                 // 5.4. "Value Representation"):
00560         for(i = sr.begin(); i != sr.end(); i++)
00561         {
00562             // The name
00563             const oid& name = *i;
00564 
00565             // Find variable for current OID
00566             map< oid, shared_ptr<variable> >::const_iterator var;
00567             var = variables.find(name);
00568             if(var != variables.end())
00569             {
00570                 // Step (2): We have a variable for this oid
00571 
00572                 // update variable
00573                 try
00574                 {
00575                     var->second->update();
00576 
00577                     // Add variable to response (Step (1): include name)
00578                     response.varbindlist.push_back( varbind(name, var->second) );
00579                 }
00580                 catch(...)
00581                 {
00582                     // An error occurred
00583                     response.set_error( ResponsePDU::genErr );
00584                     response.set_index( index );
00585                     // Leave response.varbindlist empty
00586                 }
00587 
00588             }
00589             else
00590             {
00591                 // Interpret 'name' as prefix:
00592                 // append .0 and check whether we have a variable
00593                 // with this name
00594                 oid name_copy(name, 0);
00595 
00596                 var = variables.find(name_copy);
00597                 if(var != variables.end())
00598                 {
00599                     // Step (4): We have a variable with the object
00600                     //           identifier prefix 'name': Send noSuchInstance 
00601                     //           error (Step (1): include name)
00602                     response.varbindlist.push_back( varbind(name, varbind::noSuchInstance) );
00603                 }
00604                 else
00605                 {
00606                     // Step (3): we have no variable with the object
00607                     //           identifier prefix 'name': Send noSuchObject 
00608                     //           error (Step (1): include name)
00609                     response.varbindlist.push_back( varbind(name, varbind::noSuchObject) );
00610                 }
00611             }
00612 
00613             index++;
00614         }
00615 
00616         // Finally: send the response
00617         try
00618         {
00619             this->connection->send(response);
00620         }
00621         catch(timeout_error)
00622         {
00623             // connection loss. Ignore.
00624         }
00625         catch(disconnected)
00626         {
00627             // No connection. Ignore.
00628         }
00629     }
00630 
00631     // TODO: handle other PDU types
00632 }
00633 
00634 
00635 void master_proxy::add_variable(const oid& id, shared_ptr<variable> v)
00636 {
00637     // Check whether id is contained in a registration
00638     bool is_registered = false;
00639     std::list< boost::shared_ptr<RegisterPDU> >::const_iterator r;
00640     for(r = registrations.begin(); r != registrations.end(); r++)
00641     {
00642         if((*r)->get_instance_registration() == false &&
00643            (*r)->get_range_subid() == 0)
00644         {
00645             // Registration is a simple subtree
00646             if( (*r)->get_subtree().contains(id) )
00647             {
00648                 // The ID lies within a registered area
00649                 is_registered = true;
00650                 break; // stop search
00651             }
00652         }
00653         // TODO: handle other registrations (e.g. instance registration)
00654     }
00655 
00656     if( ! is_registered )
00657     {
00658         // Not in a registered area
00659         throw(unknown_registration());
00660     }
00661     variables[id] = v;
00662 }
00663 
00664 
00665 
00666 void master_proxy::remove_variable(const oid& id)
00667 {
00668     // Find variable
00669     map<oid, shared_ptr<variable> >::iterator i = variables.find(id);
00670 
00671     if(i == variables.end())
00672     {
00673         // Variable was not added in advance
00674         // -> ignore
00675         return;
00676     }
00677 
00678     // Remove variable
00679     variables.erase(i);
00680 }