AgentXcpp  Revision:0.1.1
Internals Documentation
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Pages
master_proxy.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2012 Tanjeff-Nicolai Moos <tanjeff@cccmz.de>
3  *
4  * This file is part of the agentXcpp library.
5  *
6  * AgentXcpp is free software: you can redistribute it and/or modify
7  * it under the terms of the AgentXcpp library license, version 1, which
8  * consists of the GNU General Public License and some additional
9  * permissions.
10  *
11  * AgentXcpp is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * See the AgentXcpp library license in the LICENSE file of this package
17  * for more details.
18  */
19 #include <boost/bind.hpp>
20 #include <boost/cstdint.hpp>
21 
22 #include "master_proxy.hpp"
23 #include "OpenPDU.hpp"
24 #include "ClosePDU.hpp"
25 #include "ResponsePDU.hpp"
26 #include "RegisterPDU.hpp"
27 #include "GetPDU.hpp"
28 #include "util.hpp"
29 
30 #include <iostream>
31 
32 using namespace std;
33 using namespace agentxcpp;
34 using namespace boost; // Beside other things, this pulls boost::uint16_t
35 
36 
37 
38 
39 
40 
41 
42 master_proxy::master_proxy(boost::asio::io_service* _io_service,
43  std::string _description,
44  uint8_t _default_timeout,
45  oid _id,
46  std::string _filename) :
47  io_service(_io_service),
48  io_service_by_user(true),
49  socket_file(_filename.c_str()),
50  description(_description),
51  default_timeout(_default_timeout),
52  id(_id)
53 {
54  // Initialize connector (never use timeout=0)
55  uint8_t timeout;
56  timeout = (this->default_timeout == 0) ? 1 : this->default_timeout;
58  _filename.c_str(),
59  timeout*1000);
60 
61  // Register this object as %PDU handler
62  this->connection->register_handler( this );
63 
64  // Try to connect
65  try
66  {
67  // throws disconnected:
68  this->connect();
69  }
70  catch(disconnected)
71  {
72  // Ignore, stay disconnected
73  }
74 }
75 
76 
77 
78 master_proxy::master_proxy(std::string _description,
79  uint8_t _default_timeout,
80  oid _id,
81  std::string _filename) :
82  io_service(new boost::asio::io_service()),
83  io_service_by_user(false),
84  socket_file(_filename.c_str()),
85  description(_description),
86  default_timeout(_default_timeout),
87  id(_id)
88 {
89  // Initialize connector (never use timeout=0)
90  uint8_t timeout;
91  timeout = (this->default_timeout == 0) ? 1 : this->default_timeout;
93  _filename.c_str(),
94  timeout*1000);
95 
96  // Register this object as %PDU handler
97  this->connection->register_handler( this );
98 
99  // Try to connect
100  try
101  {
102  // throws disconnected:
103  this->connect();
104  }
105  catch(disconnected)
106  {
107  // Ignore, stay disconnected
108  }
109  catch(...)
110  {
111  // Ignore, stay disconnected
112  }
113 }
114 
115 
117 {
118  if( this->connection->is_connected() )
119  {
120  // we are already connected -> nothing to do
121  return;
122  }
123 
124  // Clear registrations and variables
125  registrations.clear();
126  variables.clear();
127 
128  // Connect to endpoint
129  try
130  {
131  this->connection->connect();
132  }
133  catch(boost::system::system_error)
134  {
135  throw;
136  }
137  // The response we expect from the master
138  boost::shared_ptr<ResponsePDU> response;
139 
140  try
141  {
142  // Send OpenPDU
143  OpenPDU openpdu;
144  openpdu.set_timeout(default_timeout);
145  openpdu.set_id(id);
146  // throws disconnected and timeout_error:
147  this->connection->send(openpdu);
148 
149  // Wait for response
150  // throws disconnected and timeout_error:
151  response = this->connection->wait_for_response(openpdu.get_packetID());
152  }
153  catch(disconnected)
154  {
155  throw; // forward
156  }
157  catch(timeout_error)
158  {
159  throw disconnected();
160  }
161 
162  // Check for errors
163  if(response->get_error() != ResponsePDU::noAgentXError)
164  {
165  // Some error occured, disconnect
166  this->connection->disconnect();
167  throw disconnected();
168  }
169 
170  // All went fine, we are connected now
171  this->sessionID = response->get_sessionID();
172 }
173 
174 
175 
176 
178 {
179  if( ! this->connection->is_connected() )
180  {
181  // we are already disconnected -> nothing to do
182  return;
183  }
184 
185  // According to RFC 2741, 7.1.8. "Processing the agentx-Close-PDU", the
186  // master agent unregisters all MIB regions, frees all index values and all
187  // sysORID are removed. Thus no need to clean up before ClosePDU is sent.
188 
189  // The response we expect from the master
190  boost::shared_ptr<ResponsePDU> response;
191 
192  // Try clean shutdown (ignore errors)
193  try
194  {
195  // Unregister stuff if any
196  std::list< boost::shared_ptr<RegisterPDU> >::const_iterator r;
197  r = this->registrations.begin();
198  while (r != this->registrations.end())
199  {
201  r++;
202  }
203 
204  // Send ClosePDU
205  ClosePDU closepdu(this->sessionID, reason);
206  // throws disconnected and timeout_error:
207  this->connection->send(closepdu);
208 
209  // Wait for response
210  // throws disconnected and timeout_error:
211  response = this->connection->wait_for_response(closepdu.get_packetID());
212 
213  // Check for errors
214  if(response->get_error() != ResponsePDU::noAgentXError)
215  {
216  // Some error occured. However, We will soon disconnect anyway.
217  // -> ignore
218  // TODO: Insert log message here.
219  }
220  }
221  catch(...)
222  {
223  // We will soon disconnect anyway.
224  // -> ignore all errors
225  }
226 
227  // Finally: disconnect
228  this->connection->disconnect();
229 }
230 
232 {
233  // Disconnect from master agent
235 
236  // Destroy connection
237  // Unregistering this object as %PDU handler is unneeded.
238  delete this->connection;
239 
240  // Destroy io_service object if needed
241  if( ! this->io_service_by_user )
242  {
243  delete this->io_service;
244  }
245 }
246 
247 
248 void master_proxy::do_registration(boost::shared_ptr<RegisterPDU> pdu)
249 {
250  // Are we connected?
251  if( ! is_connected())
252  {
253  throw(disconnected());
254  }
255 
256  // Send RegisterPDU
257  // (forward exceptions timeout_error and disconnected)
258  this->connection->send(*pdu);
259 
260  // Wait for response
261  // (forward exceptions timeout_error and disconnected)
262  boost::shared_ptr<ResponsePDU> response;
263  response = this->connection->wait_for_response(pdu->get_packetID());
264 
265  // Check Response
266  switch(response->get_error())
267  {
268  // General errors:
269 
271  // Oops, we sent a malformed PDU to the master
272  throw(internal_error());
273 
275  // We checked the connection state before, but maybe we lost the
276  // connection during communication...
277  throw(disconnected());
278 
280  // We do currently not really support contexts in this library. An
281  // invalid context is thus probably an agentxcpp bug.
282  throw(internal_error());
283 
285  // master was unable to process the request
286  throw(master_is_unable());
287 
289  // Hey, it worked!
290  break;
291 
292  // Register-specific errors:
293 
295  throw(duplicate_registration());
296 
298  throw(master_is_unwilling());
299 
300  default:
301  // This is a case of can-not-happen. Probably the master is buggy.
302  // The agentxcpp library is bug-free of course ;-)
303  // We throw a parse error meanwhile, because we didn't expect the
304  // response to look like that...
305  throw(parse_error());
306  }
307 
308  // Finish
309  return;
310 }
311 
312 
313 
314 
315 
317  uint8_t priority,
318  uint8_t timeout)
319 {
320  // Build PDU
321  boost::shared_ptr<RegisterPDU> pdu(new RegisterPDU);
322  pdu->set_subtree(subtree);
323  pdu->set_priority(priority);
324  pdu->set_timeout(timeout);
325  pdu->set_sessionID(this->sessionID);
326 
327  // Sent PDU
328  try
329  {
330  this->do_registration(pdu);
331  }
332  catch( internal_error )
333  {
334  // Huh, it seems that we sent a malformed PDU to the master. We convert
335  // this to parse_error.
336  throw(parse_error());
337  }
338  catch(...)
339  {
340  // All other exceptions are forwarded unmodified:
341  throw;
342  }
343 
344  // Success: store registration
345  this->registrations.push_back(pdu);
346 
347 }
348 
349 
350 
352  uint8_t priority)
353 {
354  // The UnregisterPDU
355  boost::shared_ptr<UnregisterPDU> pdu;
356 
357  // Remove the registration from registrations list
358  std::list< boost::shared_ptr<RegisterPDU> >::iterator r;
359  r = this->registrations.begin();
360  while (r != this->registrations.end())
361  {
362  if( (*r)->get_priority() == priority
363  && (*r)->get_subtree() == subtree
364  && (*r)->get_range_subid() == 0
365  && (*r)->get_upper_bound() == 0 )
366  {
367  // registration found
368 
369  // create UnregisterPDU
370  pdu = create_unregister_pdu(*r);
371 
372  // remove registration from list, forward to next one
373  r = registrations.erase(r);
374  }
375  else
376  {
377  // Ignore registration, inspect next one
378  r++;
379  }
380  }
381 
382  // Sent PDU
383  try
384  {
385  this->undo_registration(pdu);
386  }
387  catch( internal_error )
388  {
389  // Huh, it seems that we sent a malformed PDU to the master. We convert
390  // this to parse_error.
391  throw(parse_error());
392  }
393  catch(...)
394  {
395  // All other exceptions are forwarded unmodified:
396  throw;
397  }
398 }
399 
400 
401 
402 void master_proxy::undo_registration(boost::shared_ptr<UnregisterPDU> pdu)
403 {
404  // Are we connected?
405  if( ! is_connected())
406  {
407  throw(disconnected());
408  }
409 
410  // Send UnregisterPDU
411  // (forward exceptions timeout_error and disconnected)
412  this->connection->send(*pdu);
413 
414  // Wait for response
415  // (forward exceptions timeout_error and disconnected)
416  boost::shared_ptr<ResponsePDU> response;
417  response = this->connection->wait_for_response(pdu->get_packetID());
418 
419  // Check Response
420  switch(response->get_error())
421  {
422  // General errors:
423 
425  // Oops, we sent a malformed PDU to the master
426  throw(internal_error());
427 
429  // We checked the connection state before, but maybe we lost the
430  // connection during communication...
431  throw(disconnected());
432 
434  // We do currently not really support contexts in this library. An
435  // invalid context is thus probably an agentxcpp bug.
436  throw(internal_error());
437 
439  // master was unable to process the request
440  throw(master_is_unable());
441 
443  // Hey, it worked!
444  break;
445 
446  // Register-specific errors:
447 
449  throw(unknown_registration());
450 
451  default:
452  // This is a case of can-not-happen. Probably the master is buggy.
453  // The agentxcpp library is bug-free of course ;-)
454  // We throw a parse error meanwhile, because we didn't expect the
455  // response to look like that...
456  throw(parse_error());
457  }
458 
459  // Finish
460  return;
461 }
462 
463 
464 
465 boost::shared_ptr<UnregisterPDU> master_proxy::create_unregister_pdu(
466  boost::shared_ptr<RegisterPDU> pdu)
467 {
468  boost::shared_ptr<UnregisterPDU> new_pdu(new UnregisterPDU());
469  new_pdu->set_subtree( pdu->get_subtree() );
470  new_pdu->set_range_subid( pdu->get_range_subid() );
471  new_pdu->set_upper_bound( pdu->get_upper_bound() );
472  new_pdu->set_priority( pdu->get_priority() );
473 
474  return new_pdu;
475 }
476 
477 
478 
479 
480 void master_proxy::handle_pdu(shared_ptr<PDU> pdu, int error)
481 {
482  if(error == -2)
483  {
484  // Version error
485  // TODO: provide better handling. For now: ignore PDU
486  return;
487  }
488 
489  // Here we process all PDU's except ResponsePDU's, according to RFC 2741,
490  // 7.2.2. "Subagent Processing".
491 
492  // Step 1) Create a response and copy header from received pdu
493  //
494  // Notes:
495  // - The version, type and payload_length fields are filled
496  // automatically
497  // - The flags are not copied, because they have
498  // other meanings in ResponsePDU's.
499  // - Context is not yet supported.
500  ResponsePDU response;
501  response.set_sessionID( pdu->get_sessionID() );
502  response.set_transactionID( pdu->get_transactionID() );
503  response.set_packetID( pdu->get_packetID() );
505  response.set_index(0);
506 
507  // Step 2) Was there a parse error?
508  //
509  // Note: Error numbers are documented in connector.hpp
510  if(error == -1)
511  {
512  // Step 4b) Stop processing, don't send reply.
513  //
514  // Note: we cannot determine whether the header was sucessfully
515  // parsed, therefore we simply ignore the PDU. We don't send a
516  // parseError indication to the master agent.
517  //
518  // TODO: Send response if the header was parsed sucessfully.
520  return;
521  }
522 
523  // Step 3) Is the session valid?
524  if(pdu->get_sessionID() != this->sessionID)
525  {
527 
528  // Step 4a) Stop processing the PDU. Send response.
529  try
530  {
531  this->connection->send(response);
532  }
533  catch(timeout_error)
534  {
535  // connection loss. Ignore.
536  }
537  catch(disconnected)
538  {
539  // No connection. Ignore.
540  }
541 
542  return;
543  }
544 
545 
546  // Is it a GetPDU?
547  shared_ptr<GetPDU> get_pdu;
548  if( (get_pdu = dynamic_pointer_cast<GetPDU>(pdu)) != 0 )
549  {
550  // Handling according to
551  // RFC 2741, 7.2.3.1 "Subagent Processing of the agentx-Get-PDU"
552 
553  // Extract searchRange list
554  vector<oid> sr = get_pdu->get_sr();
555 
556  // Iterate over list and handle each oid separately
557  vector<oid>::const_iterator i;
558  uint16_t index = 1; // Index is 1-based (RFC 2741,
559  // 5.4. "Value Representation"):
560  for(i = sr.begin(); i != sr.end(); i++)
561  {
562  // The name
563  const oid& name = *i;
564 
565  // Find variable for current OID
566  map< oid, shared_ptr<variable> >::const_iterator var;
567  var = variables.find(name);
568  if(var != variables.end())
569  {
570  // Step (2): We have a variable for this oid
571 
572  // update variable
573  try
574  {
575  var->second->update();
576 
577  // Add variable to response (Step (1): include name)
578  response.varbindlist.push_back( varbind(name, var->second) );
579  }
580  catch(...)
581  {
582  // An error occurred
583  response.set_error( ResponsePDU::genErr );
584  response.set_index( index );
585  // Leave response.varbindlist empty
586  }
587 
588  }
589  else
590  {
591  // Interpret 'name' as prefix:
592  // append .0 and check whether we have a variable
593  // with this name
594  oid name_copy(name, 0);
595 
596  var = variables.find(name_copy);
597  if(var != variables.end())
598  {
599  // Step (4): We have a variable with the object
600  // identifier prefix 'name': Send noSuchInstance
601  // error (Step (1): include name)
602  response.varbindlist.push_back( varbind(name, varbind::noSuchInstance) );
603  }
604  else
605  {
606  // Step (3): we have no variable with the object
607  // identifier prefix 'name': Send noSuchObject
608  // error (Step (1): include name)
609  response.varbindlist.push_back( varbind(name, varbind::noSuchObject) );
610  }
611  }
612 
613  index++;
614  }
615 
616  // Finally: send the response
617  try
618  {
619  this->connection->send(response);
620  }
621  catch(timeout_error)
622  {
623  // connection loss. Ignore.
624  }
625  catch(disconnected)
626  {
627  // No connection. Ignore.
628  }
629  }
630 
631  // TODO: handle other PDU types
632 }
633 
634 
635 void master_proxy::add_variable(const oid& id, shared_ptr<variable> v)
636 {
637  // Check whether id is contained in a registration
638  bool is_registered = false;
639  std::list< boost::shared_ptr<RegisterPDU> >::const_iterator r;
640  for(r = registrations.begin(); r != registrations.end(); r++)
641  {
642  if((*r)->get_instance_registration() == false &&
643  (*r)->get_range_subid() == 0)
644  {
645  // Registration is a simple subtree
646  if( (*r)->get_subtree().contains(id) )
647  {
648  // The ID lies within a registered area
649  is_registered = true;
650  break; // stop search
651  }
652  }
653  // TODO: handle other registrations (e.g. instance registration)
654  }
655 
656  if( ! is_registered )
657  {
658  // Not in a registered area
659  throw(unknown_registration());
660  }
661  variables[id] = v;
662 }
663 
664 
665 
667 {
668  // Find variable
669  map<oid, shared_ptr<variable> >::iterator i = variables.find(id);
670 
671  if(i == variables.end())
672  {
673  // Variable was not added in advance
674  // -> ignore
675  return;
676  }
677 
678  // Remove variable
679  variables.erase(i);
680 }