From b0110de18050dd8148c227cfadd2f6ffbea3c13f Mon Sep 17 00:00:00 2001 From: yohan <783b8c87@scimetis.net> Date: Sun, 14 Apr 2019 20:37:40 +0200 Subject: [PATCH] Initial commit. --- .gitignore | 1 + backend/camembert/Makefile | 56 + backend/camembert/action.cpp | 22 + backend/camembert/action.h | 24 + backend/camembert/action.o | Bin 0 -> 2676 bytes backend/camembert/arp.cpp | 31 + backend/camembert/arp.h | 23 + backend/camembert/arp.o | Bin 0 -> 3208 bytes backend/camembert/camembert.h | 30 + backend/camembert/fdb.cpp | 35 + backend/camembert/fdb.h | 29 + backend/camembert/fdb.o | Bin 0 -> 2440 bytes backend/camembert/functions.cpp | 465 ++ backend/camembert/functions.h | 12 + backend/camembert/functions.o | Bin 0 -> 20420 bytes backend/camembert/interface.cpp | 231 + backend/camembert/interface.h | 97 + backend/camembert/interface.o | Bin 0 -> 6592 bytes backend/camembert/ip.cpp | 219 + backend/camembert/ip.h | 147 + backend/camembert/ip.o | Bin 0 -> 9392 bytes backend/camembert/libkmsnmp/CVS/Entries | 37 + backend/camembert/libkmsnmp/CVS/Repository | 1 + backend/camembert/libkmsnmp/CVS/Root | 1 + backend/camembert/libkmsnmp/Makefile | 44 + backend/camembert/libkmsnmp/address.cpp | 2446 ++++++++ backend/camembert/libkmsnmp/address.h | 1081 ++++ backend/camembert/libkmsnmp/asn1.cpp | 2093 +++++++ backend/camembert/libkmsnmp/asn1.h | 440 ++ backend/camembert/libkmsnmp/collect.h | 98 + backend/camembert/libkmsnmp/collect1.h | 344 ++ backend/camembert/libkmsnmp/config_snmp_pp.h | 142 + backend/camembert/libkmsnmp/counter.cpp | 99 + backend/camembert/libkmsnmp/counter.h | 145 + backend/camembert/libkmsnmp/ctr64.cpp | 300 + backend/camembert/libkmsnmp/ctr64.h | 318 + backend/camembert/libkmsnmp/gauge.cpp | 75 + backend/camembert/libkmsnmp/gauge.h | 149 + backend/camembert/libkmsnmp/integer.cpp | 245 + backend/camembert/libkmsnmp/integer.h | 271 + backend/camembert/libkmsnmp/libkmsnmp.a | Bin 0 -> 211856 bytes backend/camembert/libkmsnmp/mp_v3.h | 544 ++ backend/camembert/libkmsnmp/octet.cpp | 709 +++ backend/camembert/libkmsnmp/octet.h | 387 ++ backend/camembert/libkmsnmp/oid.cpp | 741 +++ backend/camembert/libkmsnmp/oid.h | 419 ++ backend/camembert/libkmsnmp/oid_def.h | 118 + backend/camembert/libkmsnmp/pdu.cpp | 413 ++ backend/camembert/libkmsnmp/pdu.h | 492 ++ backend/camembert/libkmsnmp/smi.h | 235 + backend/camembert/libkmsnmp/smival.h | 170 + backend/camembert/libkmsnmp/snmperrs.h | 273 + backend/camembert/libkmsnmp/snmpmsg.cpp | 791 +++ backend/camembert/libkmsnmp/snmpmsg.h | 165 + backend/camembert/libkmsnmp/target.h | 652 ++ backend/camembert/libkmsnmp/timetick.cpp | 135 + backend/camembert/libkmsnmp/timetick.h | 161 + backend/camembert/libkmsnmp/usm_v3.h | 942 +++ backend/camembert/libkmsnmp/v3.h | 243 + backend/camembert/libkmsnmp/vb.cpp | 363 ++ backend/camembert/libkmsnmp/vb.h | 384 ++ backend/camembert/main | Bin 0 -> 172722 bytes backend/camembert/main.cpp | 529 ++ backend/camembert/main.o | Bin 0 -> 18688 bytes backend/camembert/materiel.cpp | 260 + backend/camembert/materiel.h | 246 + backend/camembert/materiel.o | Bin 0 -> 9236 bytes backend/camembert/monitor.cpp | 100 + backend/camembert/monitor.h | 35 + backend/camembert/monitor.o | Bin 0 -> 2748 bytes backend/camembert/neighbours.cpp | 275 + backend/camembert/neighbours.h | 25 + backend/camembert/neighbours.o | Bin 0 -> 8424 bytes backend/camembert/pgdb.cpp | 77 + backend/camembert/pgdb.h | 69 + backend/camembert/pgdb.o | Bin 0 -> 3236 bytes backend/camembert/snmp.cpp | 226 + backend/camembert/snmp.h | 123 + backend/camembert/snmp.o | Bin 0 -> 13236 bytes backend/camembert/snmpobject.cpp | 225 + backend/camembert/snmpobject.h | 74 + backend/camembert/snmpobject.o | Bin 0 -> 8528 bytes backend/camembert/vlan.cpp | 49 + backend/camembert/vlan.h | 25 + backend/camembert/vlan.o | Bin 0 -> 1752 bytes backend/multi-telnet/commandes.txt | 4 + backend/multi-telnet/liste.txt | 8 + backend/multi-telnet/script.sh | 177 + backend/snmp.py | 85 + camembert-before-ldap/.htaccess | 24 + camembert-before-ldap/.htpasswd | 2 + camembert-before-ldap/404.php | 10 + camembert-before-ldap/anomalies.php | 32 + camembert-before-ldap/arrow.gif | Bin 0 -> 847 bytes camembert-before-ldap/arrow2.gif | Bin 0 -> 839 bytes camembert-before-ldap/cam_users.php | 86 + camembert-before-ldap/comp.php | 234 + camembert-before-ldap/denied.php | 37 + camembert-before-ldap/disconnected_user.php | 10 + camembert-before-ldap/findip.php | 100 + camembert-before-ldap/findmac.php | 199 + camembert-before-ldap/findname.php | 69 + camembert-before-ldap/groups.php | 92 + camembert-before-ldap/img/admin.png | Bin 0 -> 5686 bytes camembert-before-ldap/img/blank.png | Bin 0 -> 144 bytes camembert-before-ldap/img/membre_ca.gif | Bin 0 -> 2335 bytes camembert-before-ldap/img/root.gif | Bin 0 -> 2467 bytes camembert-before-ldap/img/tresorier.gif | Bin 0 -> 265 bytes camembert-before-ldap/inc/inc.body.php | 54 + camembert-before-ldap/inc/inc.db.php | 15 + camembert-before-ldap/inc/inc.footer.php | 7 + camembert-before-ldap/inc/inc.header.php | 4 + camembert-before-ldap/inc/inc.header2.php | 16 + camembert-before-ldap/inc/libdate.php | 44 + camembert-before-ldap/inc/libsql.php | 68 + camembert-before-ldap/inc/roles.php | 71 + camembert-before-ldap/inc/roles.php-apache | 33 + camembert-before-ldap/index.php | 61 + camembert-before-ldap/interface.php | 351 ++ camembert-before-ldap/interface.php-bak | 295 + camembert-before-ldap/libfind.php | 59 + camembert-before-ldap/list.php | 163 + camembert-before-ldap/logs.php | 148 + camembert-before-ldap/maintenance.php | 37 + camembert-before-ldap/materiel.php | 328 + camembert-before-ldap/myinfo.php | 33 + .../page_blocage_AG_PacatNet/index.html | 45 + .../logoPacatnet2.png | Bin 0 -> 224760 bytes camembert-before-ldap/pushbutton | 1 + camembert-before-ldap/redirect.php | 18 + camembert-before-ldap/roles_edit.php | 51 + camembert-before-ldap/room.php | 312 + camembert-before-ldap/si905.php | 295 + camembert-before-ldap/style.css | 141 + .../TFExt_ColsVisibility.css | 41 + .../TFExt_ColsVisibility.js | 398 ++ .../TF_Modules/tf_alternateRows.js | 71 + .../tablefilter/TF_Modules/tf_colOps.js | 271 + .../tablefilter/TF_Modules/tf_cookies.js | 163 + .../tablefilter/TF_Modules/tf_extensions.js | 53 + .../tablefilter/TF_Modules/tf_fixedHeaders.js | 95 + .../tablefilter/TF_Modules/tf_gridLayout.js | 283 + .../TF_Modules/tf_highlightKeywords.js | 97 + .../tablefilter/TF_Modules/tf_loader.js | 59 + .../tablefilter/TF_Modules/tf_paging.js | 551 ++ .../TF_Modules/tf_populateCheckList.js | 311 + .../TF_Modules/tf_populateSelect.js | 260 + .../TF_Modules/tf_publicMethods.js | 107 + .../TF_Modules/tf_refreshFilters.js | 51 + .../tablefilter/TF_Modules/tf_resetBtn.js | 48 + .../tablefilter/TF_Modules/tf_rowsCounter.js | 87 + .../tablefilter/TF_Modules/tf_sort.js | 58 + .../tablefilter/TF_Modules/tf_statusBar.js | 93 + .../tablefilter/TF_Modules/tf_themes.js | 80 + .../tablefilter/TF_Modules/tf_watermark.js | 27 + .../TF_Themes/Default/TF_Default.css | 98 + .../TF_Themes/Default/images/bg_infDiv.jpg | Bin 0 -> 303 bytes .../TF_Themes/Default/images/bg_th.jpg | Bin 0 -> 326 bytes .../TF_Themes/Default/images/btn_eraser.gif | Bin 0 -> 356 bytes .../Default/images/btn_first_page.gif | Bin 0 -> 332 bytes .../Default/images/btn_last_page.gif | Bin 0 -> 331 bytes .../Default/images/btn_next_page.gif | Bin 0 -> 187 bytes .../Default/images/btn_over_eraser.gif | Bin 0 -> 440 bytes .../Default/images/btn_over_first_page.gif | Bin 0 -> 640 bytes .../Default/images/btn_over_last_page.gif | Bin 0 -> 427 bytes .../Default/images/btn_over_next_page.gif | Bin 0 -> 393 bytes .../Default/images/btn_over_previous_page.gif | Bin 0 -> 395 bytes .../Default/images/btn_previous_page.gif | Bin 0 -> 290 bytes .../TF_Themes/Default/images/img_loading.gif | Bin 0 -> 3236 bytes .../tablefilter/TF_Themes/MyTheme/MyTheme.css | 119 + .../TF_Themes/MyTheme/images/bg_headers.jpg | Bin 0 -> 300 bytes .../TF_Themes/MyTheme/images/bg_infDiv.jpg | Bin 0 -> 303 bytes .../TF_Themes/MyTheme/images/btn_filter.png | Bin 0 -> 928 bytes .../MyTheme/images/btn_first_page.gif | Bin 0 -> 63 bytes .../MyTheme/images/btn_last_page.gif | Bin 0 -> 61 bytes .../MyTheme/images/btn_next_page.gif | Bin 0 -> 59 bytes .../MyTheme/images/btn_previous_page.gif | Bin 0 -> 58 bytes .../TF_Themes/MyTheme/images/img_loading.gif | Bin 0 -> 8787 bytes .../tablefilter/filtergrid.css | 229 + .../tablefilter/img/bg_th.jpg | Bin 0 -> 308 bytes .../tablefilter/img/downsimple.png | Bin 0 -> 201 bytes .../tablefilter/img/upsimple.png | Bin 0 -> 201 bytes .../tablefilter/sortabletable.js | 438 ++ .../tablefilter/tablefilter.js | 2758 +++++++++ .../tablefilter/tablefilter_all.js | 5390 +++++++++++++++++ .../tablefilter/tablefilter_all_min.js | 846 +++ .../tablefilter/tablefilter_min.js | 443 ++ .../tablefilter/tfAdapter.sortabletable.js | 302 + camembert-before-ldap/test.php | 31 + camembert-before-ldap/toto.html | 1 + camembert-before-ldap/tresorerie.php | 96 + camembert-before-ldap/user.php | 630 ++ camembert-before-ldap/www | 1 + crontab | 25 + scripts/deco_certif.mail | 7 + scripts/deco_certif.sh | 32 + scripts/deco_periode.mail | 7 + scripts/deco_periode.sh | 39 + scripts/deco_vide.sh | 17 + scripts/dhcpd.conf.head | 36 + scripts/gen_dhcp_dns.sh | 56 + scripts/mail_everybody.mail | 9 + scripts/mail_everybody.sh | 10 + scripts/nettoyage.sh | 22 + scripts/rappel_certif.sh | 13 + scripts/rappel_certif_admin.mail | 8 + scripts/rappel_certif_user.mail | 14 + scripts/test_mail_me.mail | 9 + scripts/test_mail_me.sh | 10 + web2/.htaccess | 24 + web2/.htpasswd | 2 + web2/404.php | 10 + web2/anomalies.php | 27 + web2/arrow.gif | Bin 0 -> 847 bytes web2/arrow2.gif | Bin 0 -> 839 bytes web2/cam_users.php | 86 + web2/comp.php | 234 + web2/denied.php | 37 + web2/disconnected_user.php | 10 + web2/findip.php | 100 + web2/findmac.php | 199 + web2/findname.php | 69 + web2/groups.php | 92 + web2/img/admin.png | Bin 0 -> 5686 bytes web2/img/blank.png | Bin 0 -> 144 bytes web2/img/membre_ca.gif | Bin 0 -> 2335 bytes web2/img/root.gif | Bin 0 -> 2467 bytes web2/img/tresorier.gif | Bin 0 -> 265 bytes web2/inc/inc.body.php | 54 + web2/inc/inc.db.php | 15 + web2/inc/inc.footer.php | 7 + web2/inc/inc.header.php | 4 + web2/inc/inc.header2.php | 16 + web2/inc/libdate.php | 44 + web2/inc/libsql.php | 68 + web2/inc/roles.php | 71 + web2/inc/roles.php-apache | 33 + web2/index.php | 61 + web2/interface.php | 351 ++ web2/interface.php-bak | 295 + web2/libfind.php | 59 + web2/list.php | 163 + web2/logs.php | 148 + web2/maintenance.php | 37 + web2/materiel.php | 328 + web2/myinfo.php | 33 + web2/page_blocage_AG_PacatNet/index.html | 45 + .../logoPacatnet2.png | Bin 0 -> 224760 bytes web2/pushbutton | 1 + web2/redirect.php | 18 + web2/roles_edit.php | 51 + web2/room.php | 312 + web2/si905.php | 295 + web2/style.css | 141 + .../TFExt_ColsVisibility.css | 41 + .../TFExt_ColsVisibility.js | 398 ++ .../TF_Modules/tf_alternateRows.js | 71 + web2/tablefilter/TF_Modules/tf_colOps.js | 271 + web2/tablefilter/TF_Modules/tf_cookies.js | 163 + web2/tablefilter/TF_Modules/tf_extensions.js | 53 + .../tablefilter/TF_Modules/tf_fixedHeaders.js | 95 + web2/tablefilter/TF_Modules/tf_gridLayout.js | 283 + .../TF_Modules/tf_highlightKeywords.js | 97 + web2/tablefilter/TF_Modules/tf_loader.js | 59 + web2/tablefilter/TF_Modules/tf_paging.js | 551 ++ .../TF_Modules/tf_populateCheckList.js | 311 + .../TF_Modules/tf_populateSelect.js | 260 + .../TF_Modules/tf_publicMethods.js | 107 + .../TF_Modules/tf_refreshFilters.js | 51 + web2/tablefilter/TF_Modules/tf_resetBtn.js | 48 + web2/tablefilter/TF_Modules/tf_rowsCounter.js | 87 + web2/tablefilter/TF_Modules/tf_sort.js | 58 + web2/tablefilter/TF_Modules/tf_statusBar.js | 93 + web2/tablefilter/TF_Modules/tf_themes.js | 80 + web2/tablefilter/TF_Modules/tf_watermark.js | 27 + .../TF_Themes/Default/TF_Default.css | 98 + .../TF_Themes/Default/images/bg_infDiv.jpg | Bin 0 -> 303 bytes .../TF_Themes/Default/images/bg_th.jpg | Bin 0 -> 326 bytes .../TF_Themes/Default/images/btn_eraser.gif | Bin 0 -> 356 bytes .../Default/images/btn_first_page.gif | Bin 0 -> 332 bytes .../Default/images/btn_last_page.gif | Bin 0 -> 331 bytes .../Default/images/btn_next_page.gif | Bin 0 -> 187 bytes .../Default/images/btn_over_eraser.gif | Bin 0 -> 440 bytes .../Default/images/btn_over_first_page.gif | Bin 0 -> 640 bytes .../Default/images/btn_over_last_page.gif | Bin 0 -> 427 bytes .../Default/images/btn_over_next_page.gif | Bin 0 -> 393 bytes .../Default/images/btn_over_previous_page.gif | Bin 0 -> 395 bytes .../Default/images/btn_previous_page.gif | Bin 0 -> 290 bytes .../TF_Themes/Default/images/img_loading.gif | Bin 0 -> 3236 bytes .../tablefilter/TF_Themes/MyTheme/MyTheme.css | 119 + .../TF_Themes/MyTheme/images/bg_headers.jpg | Bin 0 -> 300 bytes .../TF_Themes/MyTheme/images/bg_infDiv.jpg | Bin 0 -> 303 bytes .../TF_Themes/MyTheme/images/btn_filter.png | Bin 0 -> 928 bytes .../MyTheme/images/btn_first_page.gif | Bin 0 -> 63 bytes .../MyTheme/images/btn_last_page.gif | Bin 0 -> 61 bytes .../MyTheme/images/btn_next_page.gif | Bin 0 -> 59 bytes .../MyTheme/images/btn_previous_page.gif | Bin 0 -> 58 bytes .../TF_Themes/MyTheme/images/img_loading.gif | Bin 0 -> 8787 bytes web2/tablefilter/filtergrid.css | 229 + web2/tablefilter/img/bg_th.jpg | Bin 0 -> 308 bytes web2/tablefilter/img/downsimple.png | Bin 0 -> 201 bytes web2/tablefilter/img/upsimple.png | Bin 0 -> 201 bytes web2/tablefilter/sortabletable.js | 438 ++ web2/tablefilter/tablefilter.js | 2758 +++++++++ web2/tablefilter/tablefilter_all.js | 5390 +++++++++++++++++ web2/tablefilter/tablefilter_all_min.js | 846 +++ web2/tablefilter/tablefilter_min.js | 443 ++ web2/tablefilter/tfAdapter.sortabletable.js | 302 + web2/test.php | 31 + web2/toto.html | 1 + web2/tresorerie.php | 96 + web2/user.php | 630 ++ web2/www | 1 + 313 files changed, 57271 insertions(+) create mode 100644 .gitignore create mode 100644 backend/camembert/Makefile create mode 100644 backend/camembert/action.cpp create mode 100644 backend/camembert/action.h create mode 100644 backend/camembert/action.o create mode 100644 backend/camembert/arp.cpp create mode 100644 backend/camembert/arp.h create mode 100644 backend/camembert/arp.o create mode 100644 backend/camembert/camembert.h create mode 100644 backend/camembert/fdb.cpp create mode 100644 backend/camembert/fdb.h create mode 100644 backend/camembert/fdb.o create mode 100644 backend/camembert/functions.cpp create mode 100644 backend/camembert/functions.h create mode 100644 backend/camembert/functions.o create mode 100644 backend/camembert/interface.cpp create mode 100644 backend/camembert/interface.h create mode 100644 backend/camembert/interface.o create mode 100644 backend/camembert/ip.cpp create mode 100644 backend/camembert/ip.h create mode 100644 backend/camembert/ip.o create mode 100644 backend/camembert/libkmsnmp/CVS/Entries create mode 100644 backend/camembert/libkmsnmp/CVS/Repository create mode 100644 backend/camembert/libkmsnmp/CVS/Root create mode 100644 backend/camembert/libkmsnmp/Makefile create mode 100644 backend/camembert/libkmsnmp/address.cpp create mode 100644 backend/camembert/libkmsnmp/address.h create mode 100644 backend/camembert/libkmsnmp/asn1.cpp create mode 100644 backend/camembert/libkmsnmp/asn1.h create mode 100644 backend/camembert/libkmsnmp/collect.h create mode 100644 backend/camembert/libkmsnmp/collect1.h create mode 100644 backend/camembert/libkmsnmp/config_snmp_pp.h create mode 100644 backend/camembert/libkmsnmp/counter.cpp create mode 100644 backend/camembert/libkmsnmp/counter.h create mode 100644 backend/camembert/libkmsnmp/ctr64.cpp create mode 100644 backend/camembert/libkmsnmp/ctr64.h create mode 100644 backend/camembert/libkmsnmp/gauge.cpp create mode 100644 backend/camembert/libkmsnmp/gauge.h create mode 100644 backend/camembert/libkmsnmp/integer.cpp create mode 100644 backend/camembert/libkmsnmp/integer.h create mode 100644 backend/camembert/libkmsnmp/libkmsnmp.a create mode 100644 backend/camembert/libkmsnmp/mp_v3.h create mode 100644 backend/camembert/libkmsnmp/octet.cpp create mode 100644 backend/camembert/libkmsnmp/octet.h create mode 100644 backend/camembert/libkmsnmp/oid.cpp create mode 100644 backend/camembert/libkmsnmp/oid.h create mode 100644 backend/camembert/libkmsnmp/oid_def.h create mode 100644 backend/camembert/libkmsnmp/pdu.cpp create mode 100644 backend/camembert/libkmsnmp/pdu.h create mode 100644 backend/camembert/libkmsnmp/smi.h create mode 100644 backend/camembert/libkmsnmp/smival.h create mode 100644 backend/camembert/libkmsnmp/snmperrs.h create mode 100644 backend/camembert/libkmsnmp/snmpmsg.cpp create mode 100644 backend/camembert/libkmsnmp/snmpmsg.h create mode 100644 backend/camembert/libkmsnmp/target.h create mode 100644 backend/camembert/libkmsnmp/timetick.cpp create mode 100644 backend/camembert/libkmsnmp/timetick.h create mode 100644 backend/camembert/libkmsnmp/usm_v3.h create mode 100644 backend/camembert/libkmsnmp/v3.h create mode 100644 backend/camembert/libkmsnmp/vb.cpp create mode 100644 backend/camembert/libkmsnmp/vb.h create mode 100755 backend/camembert/main create mode 100644 backend/camembert/main.cpp create mode 100644 backend/camembert/main.o create mode 100644 backend/camembert/materiel.cpp create mode 100644 backend/camembert/materiel.h create mode 100644 backend/camembert/materiel.o create mode 100644 backend/camembert/monitor.cpp create mode 100644 backend/camembert/monitor.h create mode 100644 backend/camembert/monitor.o create mode 100644 backend/camembert/neighbours.cpp create mode 100644 backend/camembert/neighbours.h create mode 100644 backend/camembert/neighbours.o create mode 100644 backend/camembert/pgdb.cpp create mode 100644 backend/camembert/pgdb.h create mode 100644 backend/camembert/pgdb.o create mode 100644 backend/camembert/snmp.cpp create mode 100644 backend/camembert/snmp.h create mode 100644 backend/camembert/snmp.o create mode 100644 backend/camembert/snmpobject.cpp create mode 100644 backend/camembert/snmpobject.h create mode 100644 backend/camembert/snmpobject.o create mode 100644 backend/camembert/vlan.cpp create mode 100644 backend/camembert/vlan.h create mode 100644 backend/camembert/vlan.o create mode 100644 backend/multi-telnet/commandes.txt create mode 100644 backend/multi-telnet/liste.txt create mode 100644 backend/multi-telnet/script.sh create mode 100755 backend/snmp.py create mode 100644 camembert-before-ldap/.htaccess create mode 100644 camembert-before-ldap/.htpasswd create mode 100644 camembert-before-ldap/404.php create mode 100644 camembert-before-ldap/anomalies.php create mode 100644 camembert-before-ldap/arrow.gif create mode 100644 camembert-before-ldap/arrow2.gif create mode 100644 camembert-before-ldap/cam_users.php create mode 100644 camembert-before-ldap/comp.php create mode 100644 camembert-before-ldap/denied.php create mode 100644 camembert-before-ldap/disconnected_user.php create mode 100644 camembert-before-ldap/findip.php create mode 100644 camembert-before-ldap/findmac.php create mode 100644 camembert-before-ldap/findname.php create mode 100644 camembert-before-ldap/groups.php create mode 100644 camembert-before-ldap/img/admin.png create mode 100644 camembert-before-ldap/img/blank.png create mode 100644 camembert-before-ldap/img/membre_ca.gif create mode 100644 camembert-before-ldap/img/root.gif create mode 100644 camembert-before-ldap/img/tresorier.gif create mode 100644 camembert-before-ldap/inc/inc.body.php create mode 100644 camembert-before-ldap/inc/inc.db.php create mode 100644 camembert-before-ldap/inc/inc.footer.php create mode 100644 camembert-before-ldap/inc/inc.header.php create mode 100644 camembert-before-ldap/inc/inc.header2.php create mode 100644 camembert-before-ldap/inc/libdate.php create mode 100644 camembert-before-ldap/inc/libsql.php create mode 100644 camembert-before-ldap/inc/roles.php create mode 100644 camembert-before-ldap/inc/roles.php-apache create mode 100644 camembert-before-ldap/index.php create mode 100644 camembert-before-ldap/interface.php create mode 100644 camembert-before-ldap/interface.php-bak create mode 100644 camembert-before-ldap/libfind.php create mode 100644 camembert-before-ldap/list.php create mode 100644 camembert-before-ldap/logs.php create mode 100644 camembert-before-ldap/maintenance.php create mode 100644 camembert-before-ldap/materiel.php create mode 100644 camembert-before-ldap/myinfo.php create mode 100644 camembert-before-ldap/page_blocage_AG_PacatNet/index.html create mode 100644 camembert-before-ldap/page_blocage_AG_PacatNet/logoPacatnet2.png create mode 120000 camembert-before-ldap/pushbutton create mode 100644 camembert-before-ldap/redirect.php create mode 100644 camembert-before-ldap/roles_edit.php create mode 100644 camembert-before-ldap/room.php create mode 100644 camembert-before-ldap/si905.php create mode 100644 camembert-before-ldap/style.css create mode 100644 camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.css create mode 100644 camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_alternateRows.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_colOps.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_cookies.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_extensions.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_fixedHeaders.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_gridLayout.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_highlightKeywords.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_loader.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_paging.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_populateCheckList.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_populateSelect.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_publicMethods.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_refreshFilters.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_resetBtn.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_rowsCounter.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_sort.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_statusBar.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_themes.js create mode 100644 camembert-before-ldap/tablefilter/TF_Modules/tf_watermark.js create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/TF_Default.css create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/bg_infDiv.jpg create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/bg_th.jpg create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_eraser.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_first_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_last_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_next_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_over_eraser.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_over_first_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_over_last_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_over_next_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_over_previous_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/btn_previous_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/Default/images/img_loading.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/MyTheme.css create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/bg_headers.jpg create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/bg_infDiv.jpg create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/btn_filter.png create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/btn_first_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/btn_last_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/btn_next_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/btn_previous_page.gif create mode 100644 camembert-before-ldap/tablefilter/TF_Themes/MyTheme/images/img_loading.gif create mode 100644 camembert-before-ldap/tablefilter/filtergrid.css create mode 100644 camembert-before-ldap/tablefilter/img/bg_th.jpg create mode 100644 camembert-before-ldap/tablefilter/img/downsimple.png create mode 100644 camembert-before-ldap/tablefilter/img/upsimple.png create mode 100644 camembert-before-ldap/tablefilter/sortabletable.js create mode 100644 camembert-before-ldap/tablefilter/tablefilter.js create mode 100644 camembert-before-ldap/tablefilter/tablefilter_all.js create mode 100644 camembert-before-ldap/tablefilter/tablefilter_all_min.js create mode 100644 camembert-before-ldap/tablefilter/tablefilter_min.js create mode 100644 camembert-before-ldap/tablefilter/tfAdapter.sortabletable.js create mode 100644 camembert-before-ldap/test.php create mode 100644 camembert-before-ldap/toto.html create mode 100644 camembert-before-ldap/tresorerie.php create mode 100644 camembert-before-ldap/user.php create mode 120000 camembert-before-ldap/www create mode 100644 crontab create mode 100644 scripts/deco_certif.mail create mode 100755 scripts/deco_certif.sh create mode 100644 scripts/deco_periode.mail create mode 100755 scripts/deco_periode.sh create mode 100755 scripts/deco_vide.sh create mode 100644 scripts/dhcpd.conf.head create mode 100755 scripts/gen_dhcp_dns.sh create mode 100644 scripts/mail_everybody.mail create mode 100755 scripts/mail_everybody.sh create mode 100755 scripts/nettoyage.sh create mode 100755 scripts/rappel_certif.sh create mode 100644 scripts/rappel_certif_admin.mail create mode 100644 scripts/rappel_certif_user.mail create mode 100644 scripts/test_mail_me.mail create mode 100755 scripts/test_mail_me.sh create mode 100644 web2/.htaccess create mode 100644 web2/.htpasswd create mode 100644 web2/404.php create mode 100644 web2/anomalies.php create mode 100644 web2/arrow.gif create mode 100644 web2/arrow2.gif create mode 100644 web2/cam_users.php create mode 100644 web2/comp.php create mode 100644 web2/denied.php create mode 100644 web2/disconnected_user.php create mode 100644 web2/findip.php create mode 100644 web2/findmac.php create mode 100644 web2/findname.php create mode 100644 web2/groups.php create mode 100644 web2/img/admin.png create mode 100644 web2/img/blank.png create mode 100644 web2/img/membre_ca.gif create mode 100644 web2/img/root.gif create mode 100644 web2/img/tresorier.gif create mode 100644 web2/inc/inc.body.php create mode 100644 web2/inc/inc.db.php create mode 100644 web2/inc/inc.footer.php create mode 100644 web2/inc/inc.header.php create mode 100644 web2/inc/inc.header2.php create mode 100644 web2/inc/libdate.php create mode 100644 web2/inc/libsql.php create mode 100644 web2/inc/roles.php create mode 100644 web2/inc/roles.php-apache create mode 100644 web2/index.php create mode 100644 web2/interface.php create mode 100644 web2/interface.php-bak create mode 100644 web2/libfind.php create mode 100644 web2/list.php create mode 100644 web2/logs.php create mode 100644 web2/maintenance.php create mode 100644 web2/materiel.php create mode 100644 web2/myinfo.php create mode 100644 web2/page_blocage_AG_PacatNet/index.html create mode 100644 web2/page_blocage_AG_PacatNet/logoPacatnet2.png create mode 120000 web2/pushbutton create mode 100644 web2/redirect.php create mode 100644 web2/roles_edit.php create mode 100644 web2/room.php create mode 100644 web2/si905.php create mode 100644 web2/style.css create mode 100644 web2/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.css create mode 100644 web2/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.js create mode 100644 web2/tablefilter/TF_Modules/tf_alternateRows.js create mode 100644 web2/tablefilter/TF_Modules/tf_colOps.js create mode 100644 web2/tablefilter/TF_Modules/tf_cookies.js create mode 100644 web2/tablefilter/TF_Modules/tf_extensions.js create mode 100644 web2/tablefilter/TF_Modules/tf_fixedHeaders.js create mode 100644 web2/tablefilter/TF_Modules/tf_gridLayout.js create mode 100644 web2/tablefilter/TF_Modules/tf_highlightKeywords.js create mode 100644 web2/tablefilter/TF_Modules/tf_loader.js create mode 100644 web2/tablefilter/TF_Modules/tf_paging.js create mode 100644 web2/tablefilter/TF_Modules/tf_populateCheckList.js create mode 100644 web2/tablefilter/TF_Modules/tf_populateSelect.js create mode 100644 web2/tablefilter/TF_Modules/tf_publicMethods.js create mode 100644 web2/tablefilter/TF_Modules/tf_refreshFilters.js create mode 100644 web2/tablefilter/TF_Modules/tf_resetBtn.js create mode 100644 web2/tablefilter/TF_Modules/tf_rowsCounter.js create mode 100644 web2/tablefilter/TF_Modules/tf_sort.js create mode 100644 web2/tablefilter/TF_Modules/tf_statusBar.js create mode 100644 web2/tablefilter/TF_Modules/tf_themes.js create mode 100644 web2/tablefilter/TF_Modules/tf_watermark.js create mode 100644 web2/tablefilter/TF_Themes/Default/TF_Default.css create mode 100644 web2/tablefilter/TF_Themes/Default/images/bg_infDiv.jpg create mode 100644 web2/tablefilter/TF_Themes/Default/images/bg_th.jpg create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_eraser.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_first_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_last_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_next_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_over_eraser.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_over_first_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_over_last_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_over_next_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_over_previous_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/btn_previous_page.gif create mode 100644 web2/tablefilter/TF_Themes/Default/images/img_loading.gif create mode 100644 web2/tablefilter/TF_Themes/MyTheme/MyTheme.css create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/bg_headers.jpg create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/bg_infDiv.jpg create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/btn_filter.png create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/btn_first_page.gif create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/btn_last_page.gif create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/btn_next_page.gif create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/btn_previous_page.gif create mode 100644 web2/tablefilter/TF_Themes/MyTheme/images/img_loading.gif create mode 100644 web2/tablefilter/filtergrid.css create mode 100644 web2/tablefilter/img/bg_th.jpg create mode 100644 web2/tablefilter/img/downsimple.png create mode 100644 web2/tablefilter/img/upsimple.png create mode 100644 web2/tablefilter/sortabletable.js create mode 100644 web2/tablefilter/tablefilter.js create mode 100644 web2/tablefilter/tablefilter_all.js create mode 100644 web2/tablefilter/tablefilter_all_min.js create mode 100644 web2/tablefilter/tablefilter_min.js create mode 100644 web2/tablefilter/tfAdapter.sortabletable.js create mode 100644 web2/test.php create mode 100644 web2/toto.html create mode 100644 web2/tresorerie.php create mode 100644 web2/user.php create mode 120000 web2/www diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13f86e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +pg_dump_camembert.dump diff --git a/backend/camembert/Makefile b/backend/camembert/Makefile new file mode 100644 index 0000000..009f1c2 --- /dev/null +++ b/backend/camembert/Makefile @@ -0,0 +1,56 @@ + +CC=g++ +OPTIONS=-D_THREAD_SAFE -O3 -march=pentiumpro -c -Wall -I/usr/local/include -Ilibkmsnmp/ +LINKOPTIONS=-D_THREAD_SAFE -Wall -I/usr/local/include -Ilibkmsnmp/ -L/usr/local/lib + +main: main.o monitor.o neighbours.o functions.o vlan.o materiel.o interface.o action.o fdb.o arp.o pgdb.o snmpobject.o snmp.o ip.o libkmsnmp/libkmsnmp.a + $(CC) $(LINKOPTIONS) -pthread -lpq main.o monitor.o neighbours.o functions.o vlan.o materiel.o interface.o action.o fdb.o arp.o pgdb.o snmpobject.o snmp.o ip.o libkmsnmp/libkmsnmp.a -o main + +libkmsnmp/libkmsnmp.a: + cd libkmsnmp/ && make libkmsnmp.a + +main.o: main.cpp camembert.h + $(CC) $(OPTIONS) main.cpp + +monitor.o: monitor.cpp monitor.h + $(CC) $(OPTIONS) monitor.cpp + +neighbours.o: neighbours.cpp neighbours.h + $(CC) $(OPTIONS) neighbours.cpp + +functions.o: functions.h functions.cpp + $(CC) $(OPTIONS) functions.cpp + +vlan.o: vlan.h vlan.cpp + $(CC) $(OPTIONS) vlan.cpp + +action.o: action.h action.cpp + $(CC) $(OPTIONS) action.cpp + +interface.o: interface.h interface.cpp + $(CC) $(OPTIONS) interface.cpp + +materiel.o: materiel.h materiel.cpp + $(CC) $(OPTIONS) materiel.cpp + +fdb.o: fdb.h fdb.cpp + $(CC) $(OPTIONS) fdb.cpp + +arp.o: arp.h arp.cpp + $(CC) $(OPTIONS) arp.cpp + +pgdb.o: pgdb.cpp pgdb.h + $(CC) $(OPTIONS) pgdb.cpp + +snmpobject.o: snmpobject.cpp snmpobject.h + $(CC) $(OPTIONS) snmpobject.cpp + +snmp.o: snmp.cpp snmp.h + $(CC) $(OPTIONS) snmp.cpp + +ip.o: ip.cpp ip.h + $(CC) $(OPTIONS) ip.cpp + +clean: + rm -f *.o + cd libkmsnmp/ && rm -f *.o diff --git a/backend/camembert/action.cpp b/backend/camembert/action.cpp new file mode 100644 index 0000000..77541af --- /dev/null +++ b/backend/camembert/action.cpp @@ -0,0 +1,22 @@ +#include "action.h" + +Action::Action(unsigned short int num, const char *opt) { + this->_numAction = num; + this->_option = new char[strlen(opt)+1]; + strcpy(this->_option, opt); + this->_next = NULL; +} + +Action::~Action() { + delete[] _option; + if(_next) + delete _next; +} + +void Action::addAction(unsigned short int num, const char *opt) { + if(_next) // flemme de faire propre sans récurrence + _next->addAction(num, opt); + else + _next = new Action(num, opt); +} + diff --git a/backend/camembert/action.h b/backend/camembert/action.h new file mode 100644 index 0000000..8c2d7c0 --- /dev/null +++ b/backend/camembert/action.h @@ -0,0 +1,24 @@ +#ifndef __CAMEMBERT_ACTION_H +#define __CAMEMBERT_ACTION_H + +#include + +class Action { + private: + unsigned short int _numAction; + char *_option; + Action *_next; + + public: + Action(unsigned short int num, const char *opt); + ~Action(); + + void addAction(unsigned short int num, const char *opt); + unsigned short int getNum() const { return _numAction; } + const char *getOption() const { return _option; } + Action *getNext() const { return _next; } +}; + + +#endif /* __CAMEMBERT_ACTION_H */ + diff --git a/backend/camembert/action.o b/backend/camembert/action.o new file mode 100644 index 0000000000000000000000000000000000000000..8b1c2f0198359eddd06f0d1b5fa8d9ef64b50533 GIT binary patch literal 2676 zcmcIlO>7%Q6rPPYbSX(4N+cqYf{{YxfNI57QqfWgx6ay7N)#2+ih|>A>`mj~I3`}F z38gB+aoSfkv4tyu2y-qAaFeAkX^ys&2% zx~8~?5A|*DRjbu7SKSGZsNCvPrMe!`D1ePsg*{%5uif~T^#3KE-_|uAzh}q0cwyIj zr_jN#>~XzfhqcmO>kuz=*ztQ@*X+Ws4Wb-I zJuh3YtjC>I^$FKTB;RP0BS6|ygDrK)%NbsUEs zYi45DIM+A4%g+)SZYK2&Co!&XdY5;o#I5|M;J3WXKRk%v68yGzdG$g3w&2Uw(tWYZ zf~#0(?&B(UPAlzL0Xy2f;R&tutJNh7TSL)6_IQPNyLGema;-Sy+jZ+!-MU$~Zb&P? zq7}dIeS0F|9;}$J-MV4*i+-#3Zp#tmX~Qo)H z{==sa+%(22#^)GKab7L9M_1I?HIMtPTj0ZJs>>Kne$it4ih9lC1OE-i-8RN%fGs}G zo>(jrIp*`nM#l$yLxEr*ZB9tj46!#;m*-kdixrja)XeEt!0GHqlXpWxw8aDcHu zereV+CV|$xi_>z3WdX}vv{)czSOyDB=JPCYF_}!5i%BzQCE%VhS%BQk>BQ8$F$*m@ zH#=))u_ilbnE~wRaNaVK?_%Q*DbK(UrC{P*SQzu?Q7b83V)7L5QE3ErL-AwbW0=!= zsuR5m&k0?I$jAxoBu+hnFJO?rr2oxt5Iz(i$KeS19Tal{;z*(xjeZ}(?TKpb|E_0QBY2ia4V-U(* z9eN*Q7ht4En)XfM=}c(e81Dj#Nqs9t#*@0lM!U-XtV%m!^FJf$jb9+WIXMq=u6Qkk zr`AQT$BGLOm{$I54UqY8}1M$pATIsoLAeqat#QD)9Cmb~r)UC1Ck_>k{ zV`f2QjTussxh0lJTwGjCt);}mVB^w|yEHMLy_C+T5@*bO0XMxK`>2sh zxnB{E=d+h)z^5`}3k_}~L7mi;(QFODdI&kRq3`<%(fGrJXs}sfUl8_^ux}Hhf&UWx z--7Q%`m`=ch)a2t(9764!fwXi6L?eLR|0<&_=mv#Xn6AP7x=QkQNkw}J4=Z869Ur$ zErC}Bek|~|z*T|25F&m>V3iPY_o2Nh?$4ADn&E5V-y`gxn9|8uA0fC`1ZD&l2yq2I J75aAq{{bs8Xo~;< literal 0 HcmV?d00001 diff --git a/backend/camembert/arp.cpp b/backend/camembert/arp.cpp new file mode 100644 index 0000000..5b9d51f --- /dev/null +++ b/backend/camembert/arp.cpp @@ -0,0 +1,31 @@ +#include "arp.h" + +ARPCache::ARPCache() { + mac[0] = 0; + ip = NULL; + next = NULL; +} + +ARPCache::ARPCache(const char *macaddr, const char *i) { + strcpy(mac, macaddr); + ip = new IP(i); + next = NULL; +} + +ARPCache::~ARPCache() { + if(ip) + delete ip; + if(next) + delete next; +} + +void ARPCache::addEntry(const char *mac, const char *ip) { + ARPCache *arp = this, *prev = NULL; + while(arp) { + if(!strcmp(mac, arp->mac) && !strcmp(ip, arp->ip->getIPstr())) + return; + prev = arp; + arp = arp->next; + } + prev->next = new ARPCache(mac, ip); +} diff --git a/backend/camembert/arp.h b/backend/camembert/arp.h new file mode 100644 index 0000000..7a84ce2 --- /dev/null +++ b/backend/camembert/arp.h @@ -0,0 +1,23 @@ +#ifndef __CAMEMBERT_ARP_H +#define __CAMEMBERT_ARP_H + +#include "ip.h" + +class ARPCache { + private: + char mac[24]; + IP *ip; + ARPCache *next; + + public: + ARPCache(); + ARPCache(const char *mac, const char *ip); + ~ARPCache(); + void addEntry(const char *mac, const char *ip); + + const char *getMAC() const { return mac; } + IP *getIP() const { return ip; } + ARPCache *getNext() const { return next; } +}; + +#endif /* __CAMEMBERT_H */ diff --git a/backend/camembert/arp.o b/backend/camembert/arp.o new file mode 100644 index 0000000000000000000000000000000000000000..c3a3770177e5a40603873eb88ad2d2d2a306f3d8 GIT binary patch literal 3208 zcmcguZ){Ul6u*zQTBXo|pjArfhw{e=6>yrN7z^9m*r3pW1p|~)y2lu9rM9oI8UAS2 zH+*?nvJm`W0*N2-gOLx0kcehU7sd=GhM*x~9~PYf!@ig(S9+T29S9ot{iPC$KHF0W8jt;@z2s@GU2`^QRQ#oS^0d@?T7t%qsZRJyesV@@ov{h4 zHE4{Qv2lNC0>!Yl)cOf?dmu`?V9x(3c3(X~AQ@zJaYN_|Ec+j}xFI^;mTfi9A zmzX~Cn~A$NC2^NnZ9L1cp0-*+GGk-^!ix=y4UOq5W(A6k!QhM;8=l4BscN@ZEvhV0--LIrqfNwt zjG^KKJP~bqz1u6L|T@2B>@(-_4ZMUi&k(%;$E0qL42Hq46U9Y+2~t zYRp?eQOUO%5`xV3@ox$2h`ECYeHsde9o1eFh8~5aA#O-D_Ktp6Tc4Z9-3Iej2ZiE07rOWHd5j$gIymmOeizR}IpQwHegL{dH5^aB`@D-q z3ccNW`Up_Z+jvaQ@>*9!*LrjoX;15E7CBNVut-NHlhS%JT24=a-leff=0tb5mIW(& zLf0ZKI}_^)dOGtyi_m)QSn6m#-K`<>6OR>O(viiV6<*8bypVPXDi@7qkZ1VJ{<(!e zJBP7wK2%yyOT3VkLPr_+IP8c;2C2S+Ir0CCw;mjdkFxVY@ivJ$0r_Zbh0vV%G(H1w zxjp5-RgSWW1E%$-LLW3ri<_a@hgQopo8`LtS8_y3qNEN4N+g&x$D< z!{hIyi5N08q0C9HI2L~dBf|1L6p!wNaysMv2*Y2n$syxOU82+OW`9(qov`*l4bq(% zNSCL!SGgDt;TSF^8dTU^{NytP7hajH74`aJ+{jBaZho#;p@$&SAH`e_I`=L}6q1I} zdgo&&e-}g!((_UQl74QM-VgmMM8>;>c?d3V<*@&#(v1Wg^V2~^fPvf5C!>^5Y2Fz5RdwM zLhybj1n&VMcplWBcnb)@dt2!H1b!gU5_m=6ErAaO&POv*oYw@tOZXgPDWRVfcwXQI zftLkd5qMSLb%B2itV4@X{1t?VyISZU6F$$_AmIy)T@v~?ggECYAIGj^V27}u7P>(?+Vpdwe + +#include "ip.h" +#include "snmp.h" +#include "snmpobject.h" +#include "pgdb.h" +#include "interface.h" +#include "materiel.h" +#include "monitor.h" +#include "functions.h" +#include "neighbours.h" + +extern SNMP* snmp; +extern MaterielList* lstMateriel; +extern pthread_mutex_t mtx_materiels; +extern Monitor* monitor; +extern unsigned int maxIdMateriel; +extern unsigned int maxIdInterface; + +#define FIRST_IP "172.17.24.1" +#define COMMUNITY "pacadmins" + +#define MAX_JOBS 256 +#define MAX_THREADS 8 +#define THREADED 1 + +#endif /* __CAMEMBERT_H */ diff --git a/backend/camembert/fdb.cpp b/backend/camembert/fdb.cpp new file mode 100644 index 0000000..018b163 --- /dev/null +++ b/backend/camembert/fdb.cpp @@ -0,0 +1,35 @@ +#include "fdb.h" + +ForwardingDatabase::ForwardingDatabase(const char *macaddr, unsigned short int vlan, unsigned short int type) { + strcpy(mac, macaddr); + this->vlan = vlan; + this->type = type; + next = NULL; +} + +ForwardingDatabase::~ForwardingDatabase() { + if(next) + delete next; +} + +bool ForwardingDatabase::hasMAC(const char *macaddr, unsigned short int vlan) const { + const ForwardingDatabase *fdb; + for(fdb=this; fdb!=NULL; fdb=fdb->next) { + if(vlan == fdb->vlan && !strcmp(fdb->mac, macaddr)) + return true; + } + return false; +} + +void ForwardingDatabase::addEntry(const char *macaddr, unsigned short int vlan, unsigned short int type) { + ForwardingDatabase *fdb = this, *prev = NULL; + while(fdb) { + if(!strcmp(macaddr, fdb->mac) && vlan == fdb->vlan) + return; + prev = fdb; + fdb = fdb->next; + } + prev->next = new ForwardingDatabase(macaddr, vlan, type); + +} + diff --git a/backend/camembert/fdb.h b/backend/camembert/fdb.h new file mode 100644 index 0000000..334ec82 --- /dev/null +++ b/backend/camembert/fdb.h @@ -0,0 +1,29 @@ +#ifndef __CAMEMBERT_FDB_H +#define __CAMEMBERT_FDB_H + +#include +#include + +// Bon, c'est pas tres beau, mais on utilise aussi cette classe pour +// les secure Mac addresses (port-security) +class ForwardingDatabase { + private: + char mac[24]; + unsigned short int vlan, type; + ForwardingDatabase *next; + + public: + ForwardingDatabase(const char *macaddr, unsigned short int vlan, unsigned short int type); + ~ForwardingDatabase(); + + bool hasMAC(const char *macaddr, unsigned short int vlan) const; + void addEntry(const char *macaddr, unsigned short int vlan, unsigned short int type); + + const char *getMAC() const { return mac; } + unsigned short int getVLAN() const { return vlan; } + unsigned short int getType() const { return type; } + ForwardingDatabase *getNext() { return next; } +}; + +#endif /* __CAMEMBERT_H */ + diff --git a/backend/camembert/fdb.o b/backend/camembert/fdb.o new file mode 100644 index 0000000000000000000000000000000000000000..0f044f703c8bf83a9bf21fead3ee0e4687c38d89 GIT binary patch literal 2440 zcmcIlUrbw77(bA(RI7j4yu4*WdT= zeBb%LbI$EYX6H$d$HNqPn3rv9j9v4&o)!U(%+H=wp#r=4&*$ELGrz7oy;aVHUz^Vb ziuZDXhcC~3eUJj23wrUMeK^^ct(~2*53<~^{Az=b>kYqbZf$K97jr-5m-RW%g?FR9 zOXPXg%`h*Bc$v&wGDI37*?Pi5fn!sm)IWcz8Qil8@IWdA9CmNEdYxp4)IH zHu-w*((KF#o4J1If5{iTB`0tM+tcRF8YhP-OkfJ!T+3BIJTxQv;7j}+Y__G;_nu_i zc&+`a6Vfq+ReqOuuiW%C)MJlgGTcV=68E|Bj=AQ*@3!W48mi+xdVcwcJF7?gJMWlP zm=}hHK{@Af0$SlOWOlN8@ei%=BZ(jC#VxI{Okz8>pcTF%F`^f5X@$>8Jf#=E<6X;I z;bYP|gtlf|Qd+T(OXTj$4Rh$Ki{Ue?XT^+&ePudLL@Vyf%2w9hUvP7?d3-WtGP!vTr)Q z8@N;=b#fMplM|N=Q)&-|j@37Y6R`m+Ro`N?7%v`eJ?skbvRe89c|M4oWuB{5dxW;n z@AFN2=+f2;ov|Lm6VFt=R;+lsvLjzP>6`MB?>P7(*aVmSo%zc4NniVv*IgfnYwllS zsLnK)9{D68>V(VckpCKZ7XAbDAyE4`JLR499n~ zh7wfhztMh#(YD>$x-+ZBY!n5gun$=NC0nRs>@(P^!L*)Qfr%ZEx=PWh1lWlvj_3t2 z#Vhx2aZQM!{Dedl_na$=U-A*RNfbu>45-A?KruvJV3Khhf(8+{CS?M(zYfUd{YId) z!bbf*A>(NO&w|TKMUdbeY>HIf@|xtE#=x6kdQTz?CdUNEztDHdcuY%Q^BR@;VV@6*(&7wVLP6u00 z4ri`dnfO5JQW#GwW@QsE^;_Al6CpEly3c0O=%vxoXgZO}4yUZa0edVu(!}sA`-Vh< zoz{nfVyP{{(qmL-$FOD@wu`^jipR~Aof#{IrLJ7wEm=5dj=&cmj1cTtFWig}&@pfY zE^m?0&)5$Vn=u-)zeT7q79)gT!vm?nAnOQm$%7JKml&0pmN+gkFVT^>N{BoUr2d7( cKPB$PK0LVe4YVKF!V>!>ej@QpiQh~72RGq8`v3p{ literal 0 HcmV?d00001 diff --git a/backend/camembert/functions.cpp b/backend/camembert/functions.cpp new file mode 100644 index 0000000..f96d1a2 --- /dev/null +++ b/backend/camembert/functions.cpp @@ -0,0 +1,465 @@ +#include +#include "functions.h" +#include "vlan.h" + +#define ACTION_SHUTDOWN 0 +#define ACTION_NO_SHUTDOWN 1 +#define ACTION_DESCRIPTION 2 +#define ACTION_MAX_MAC_COUNT 3 +#define ACTION_NO_STICKY 4 +#define ACTION_VLAN 7 + +#define OID_DESCRIPTION "1.3.6.1.2.1.31.1.1.1.18" +#define OID_ADMINSTATUS "1.3.6.1.2.1.2.2.1.7" +#define OID_MAXMACCOUNT "1.3.6.1.4.1.9.9.315.1.2.1.1.3" +#define OID_SECUREMACROWSTATUS "1.3.6.1.4.1.9.9.315.1.2.2.1.4" +#define OID_ACCESSVLAN "1.3.6.1.4.1.9.9.68.1.2.2.1.2" + +Oid OID_ifName ("1.3.6.1.2.1.2.2.1.2"); +Oid OID_ifType ("1.3.6.1.2.1.2.2.1.3"); +Oid OID_ifAddress ("1.3.6.1.2.1.2.2.1.6"); +Oid OID_ifDescription (OID_DESCRIPTION); +Oid OID_ifSpeed ("1.3.6.1.2.1.2.2.1.5"); +Oid OID_ifAdminStatus (OID_ADMINSTATUS); +Oid OID_ifOperStatus ("1.3.6.1.2.1.2.2.1.8"); + +Oid OID_ifAccessVlan (OID_ACCESSVLAN); +Oid OID_ifVoiceVlan ("1.3.6.1.4.1.9.9.68.1.5.1.1.1"); +Oid OID_ifNativeVlan ("1.3.6.1.4.1.9.9.46.1.6.1.1.5"); + +Oid OID_ifDot1dPort("1.3.6.1.2.1.17.1.4.1.2"); + +Oid OID_PortInterface ("1.3.6.1.4.1.9.9.87.1.4.1.1.25"); +Oid OID_PortDot1D ("1.3.6.1.4.1.9.9.87.1.4.1.1.35"); +Oid OID_SpanningTreePortFast ("1.3.6.1.4.1.9.9.87.1.4.1.1.36"); + +Oid OID_ARPCache("1.3.6.1.2.1.3.1.1.2"); + +Oid OID_ForwardingInterface ("1.3.6.1.2.1.17.4.3.1.2"); +Oid OID_ForwardingType ("1.3.6.1.2.1.17.4.3.1.3"); + +Oid OID_PortSecurityEnabled ("1.3.6.1.4.1.9.9.315.1.2.1.1.1"); +Oid OID_PortSecureStatus ("1.3.6.1.4.1.9.9.315.1.2.1.1.2"); +Oid OID_MaximumSecureMacCount (OID_MAXMACCOUNT); +Oid OID_CurrentSecureMacCount ("1.3.6.1.4.1.9.9.315.1.2.1.1.4"); +Oid OID_ViolationCount ("1.3.6.1.4.1.9.9.315.1.2.1.1.9"); +Oid OID_SecureLastMac ("1.3.6.1.4.1.9.9.315.1.2.1.1.10"); +Oid OID_StickyEnabled ("1.3.6.1.4.1.9.9.315.1.2.1.1.15"); + +Oid OID_SecureMacType("1.3.6.1.4.1.9.9.315.1.2.2.1.2"); + +// ================================== +// Fonctions sur les interfaces +// ================================== + +// set_admin_status +// Blabla commentaire +void set_admin_status(Materiel *m, unsigned int ifNum, bool newStatus) { + //I = m->getInterface + char oidC[64]; + oidC[0] = 0; + sprintf(oidC, OID_ADMINSTATUS".%d", ifNum); + Oid OID_AdminStatusSetting(oidC); + m->snmpset(&OID_AdminStatusSetting, 2-newStatus); +} + +void set_description(Materiel *m, unsigned int ifNum, const char *newDesc) { + char oidC[64]; + oidC[0] = 0; + sprintf(oidC, OID_DESCRIPTION".%d", ifNum); + Oid OID_DescriptionSetting(oidC); + m->snmpset(&OID_DescriptionSetting, newDesc); +} + +void set_max_mac_count(Materiel *m, unsigned int ifNum, unsigned int newMax) { + char oidC[64]; + oidC[0] = 0; + sprintf(oidC, OID_MAXMACCOUNT".%d", ifNum); + Oid OID_MaximumSecureMacCountSetting(oidC); + m->snmpset(&OID_MaximumSecureMacCountSetting, newMax); +} + +void delete_sticky(Materiel *m, unsigned int ifNum, const char *macToDelete) { + char oidC[64]; + oidC[0] = 0; + + char *c; + char mac[24]; + strcpy(mac, macToDelete); + int intmac[6], i = 0; + c = strtok(mac, ":"); + while(c) { + intmac[i++] = strtol(c, NULL, 16); + c = strtok(NULL, ":"); + } + // ugly workaround for 2950 switch, to be removed after ios upgrade + if (ifNum < 100) { + sprintf(oidC, OID_SECUREMACROWSTATUS".%d.%d.%d.%d.%d.%d.%d", ifNum, intmac[0], intmac[1], intmac[2], intmac[3], intmac[4], intmac[5]); + } + else { + sprintf(oidC, "1.3.6.1.4.1.9.9.315.1.2.3.1.5.%d.%d.%d.%d.%d.%d.%d.%d", ifNum, intmac[0], intmac[1], intmac[2], intmac[3], intmac[4], intmac[5], 964); + } + Oid OID_SecureMacAddrRowStatus(oidC); + m->snmpset(&OID_SecureMacAddrRowStatus, 6); +} + +void set_vlan(Materiel *m, unsigned int ifNum, unsigned int newVlan) { + char oidC[64]; + oidC[0] = 0; + sprintf(oidC, OID_ACCESSVLAN".%d", ifNum); + Oid OID_ifAccessVlanSetting(oidC); + m->snmpset(&OID_ifAccessVlanSetting, newVlan); +} + +void apply_actions(Materiel *m) { + InterfaceList *ifl; + Interface *I; + Action *a; + unsigned short int n; + + for(ifl=m->getInterfaces(); ifl!=NULL; ifl=ifl->getNext()) { + I = ifl->getInterface(); + for(a=I->getActions(); a!=NULL; a=a->getNext()) { + switch(n = a->getNum()) { + case ACTION_SHUTDOWN: + case ACTION_NO_SHUTDOWN: set_admin_status(m, I->getIfNumber(), n); break; + case ACTION_DESCRIPTION: set_description(m, I->getIfNumber(), a->getOption()); break; + case ACTION_MAX_MAC_COUNT: set_max_mac_count(m, I->getIfNumber(), atoi(a->getOption())); break; + case ACTION_NO_STICKY: delete_sticky(m, I->getIfNumber(), a->getOption()); break; + case ACTION_VLAN: set_vlan(m, I->getIfNumber(), atoi(a->getOption())); break; + } + } + } +} + +// get_interfaces_infos_base +// Va chercher le nom, type, l'adresse MAC, la vitesse et le statut d'un équipement donné +// Crée les interfaces si elles n'existent pas +void get_interfaces_infos_base(Materiel *m) { + const SNMPResult *r; + unsigned short int ifNum; + Interface *i; + int val; + unsigned long value; + + SNMPResults res(m->multiplesnmpwalk(6, 0, + &OID_ifOperStatus, + &OID_ifAdminStatus, + &OID_ifSpeed, + &OID_ifAddress, + &OID_ifType, + &OID_ifName)); + // Parcourt les résultats du SNMPWalk + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + ifNum = o[10]; + i = m->getInterfaceById(ifNum); + // Si l'interface n'existe pas sur l'équipement... + if(!i) { + pthread_mutex_lock(&mtx_materiels); + // On la crée... + i = new Interface(++maxIdInterface, ifNum); + pthread_mutex_unlock(&mtx_materiels); + // et on l'ajoute au matériel. + m->addInterface(i, DBSTATUS_NEW); + } + switch(o[9]) { + // Blabla mise à jour des infos sur l'interface. + case 2: i->setName(r->get_printable_value()); break; + case 3: r->get_value(val); i->ifType = val; break; + case 6: i->setAddress(r->get_printable_value()); break; + case 5: r->get_value(value); i->speed = value; break; + case 7: r->get_value(val); i->adminStatus = 2-val; break; + case 8: r->get_value(val); i->operStatus = 2-val; break; + } + + // On signale qu'on a mis à jour les infos. + m->getInterfaces()->foundInterface(ifNum); + } +} + +void get_interfaces_dot1d(Materiel *m) { + const SNMPResult *r; + int value; + unsigned short int vlan; + VlanList vlans; + Interface *i; + + for(InterfaceList *ifl=m->getInterfaces(); ifl!=NULL; ifl=ifl->getNext()) { + if((value = ifl->getInterface()->vlan) > 0) + vlans.addVlan(value); + } + vlans.addVlan(0); + + while(vlans.getNext(vlan)) { + SNMPResults res(m->snmpwalk(&OID_ifDot1dPort, vlan)); + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + r->get_value(value); + i = m->getInterfaceById(value); + if(i) + i->ifDot1d = o[11]; + } + } +} + +// get_interfaces_description +// Récupère la description des interfaces d'un équipement donné +void get_interfaces_description(Materiel *m) { + const SNMPResult *r; + Interface *i; + + SNMPResults res(m->snmpwalk(&OID_ifDescription)); + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + if((i = m->getInterfaceById(o[11])) != NULL) + i->setDescription(r->get_printable_value()); + } +} + +// get_interfaces_vlan +// Récupère le VLAN et le VLAN voix des interfaces de l'équipement donné +void get_interfaces_vlan(Materiel *m) { + const SNMPResult *r; + Interface *i; + int val; + + SNMPResults res(m->multiplesnmpwalk(2, 0, &OID_ifVoiceVlan, &OID_ifAccessVlan)); + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + if((i = m->getInterfaceById(o[14])) != NULL) { + switch(o[10]) { + case 2: r->get_value(val); i->vlan = val; break; + case 5: r->get_value(val); i->voiceVlan = val; break; + } + } + } +} + +// get_interfaces_trunk +// Vérifie si les interfaces sont en Trunk et récupère le native VLAN +void get_interfaces_trunk(Materiel *m) { + const SNMPResult *r; + Interface *i; + int val; + + SNMPResults res(m->snmpwalk(&OID_ifNativeVlan)); + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + if((i = m->getInterfaceById(o[14])) != NULL) { + r->get_value(val); + i->vlan = -1; + i->nativeVlan = val; + } + } +} + +void get_interfaces_c2900(Materiel *m) { + Interface *i; + SNMPResult const *r; + int portnum, modulenum, rx; + + SNMPResults res(m->multiplesnmpwalk(3, 0, + &OID_SpanningTreePortFast, + &OID_PortDot1D, + &OID_PortInterface)); + + while (r = res.getNext()) { + const Oid& o = r->get_oid(); + + portnum = o[15]; + modulenum = o[14]; + r->get_value(rx); + + if (o[13] == 25 && ((i = m->getInterfaceById(rx)) != NULL)) { + i->portNum = portnum; + i->moduleNum = modulenum; + } + else if ((i = m->getInterfaceByPort(modulenum, portnum)) != NULL) { + switch(o[13]) { + case 36: i->spanningTree = 2-rx; break; + case 35: i->portDot1d = rx; break; + } + } + } +} + +bool is_mac_address_correct(const char *mac) { + regex_t preg; + int match; + + if((regcomp(&preg, "([0-9a-fA-F]{2}[ :-]){5}[0-9a-fA-F]{2}[ :-]?", REG_NOSUB | REG_EXTENDED)) == 0) { + match = regexec(&preg, mac, 0, NULL, 0); + regfree(&preg); + return match == 0; + } + return false; +} + +void get_interfaces_port_security(Materiel *m) { + Interface *i; + SNMPResult const *r; + int val; + unsigned long value; + + SNMPResults res(m->multiplesnmpwalk(7, 0, + &OID_PortSecurityEnabled, + &OID_PortSecureStatus, + &OID_MaximumSecureMacCount, + &OID_CurrentSecureMacCount, + &OID_ViolationCount, + &OID_SecureLastMac, + &OID_StickyEnabled)); + + while(r = res.getNext()) { + const Oid& o = r->get_oid(); + if((i = m->getInterfaceById(o[14])) != NULL) { + switch(o[13]) { + case 1: r->get_value(val); i->portSecEnabled = 2-val; break; + case 2: r->get_value(val); i->portSecStatus = val; break; + case 3: r->get_value(val); i->maxMacCount = val; break; + case 4: r->get_value(val); i->currMacCount = val; break; + case 9: r->get_value(value); i->violationCount = value; break; + case 10: + if(is_mac_address_correct(r->get_printable_value())) + i->setLastMacAddr(r->get_printable_value()); + break; + case 15: r->get_value(val); i->stickyEnabled = 2-val; break; + } + } + } +} + +// get_interfaces_infos +// Va chercher toutes les informations sur les interfaces +void get_interfaces_infos(Materiel *m) { + get_interfaces_infos_base(m); + get_interfaces_description(m); + get_interfaces_trunk(m); + get_interfaces_vlan(m); + get_interfaces_dot1d(m); + get_interfaces_c2900(m); + get_interfaces_port_security(m); +} + +// ========================================== +// Fonction ARP +// ========================================== + +void get_arp_infos(Materiel *m) { + const SNMPResult *r; + int len, c; + const char *oid, *pres; + + SNMPResults res(m->snmpwalk(&OID_ARPCache)); + while(r = res.getNext()) { + oid = r->get_printable_oid(); + pres = r->get_printable_value(); + + len = strlen(oid); + c = 0; + while(len > 0 && c < 4) { + if(oid[--len] == '.') + c++; + } + if(c == 4) + m->addARPEntry(pres, &oid[len+1]); + } +} + +// =========================================== +// Fonction Forwarding Database +// =========================================== + +void get_real_fdb_infos(Materiel *m) { + const SNMPResult *r; + int i; + unsigned short int vlan; + VlanList vlans; + ForwardingDatabase *fdb = NULL; + Interface *I; + char buffer[8], mac[24]; + char hextable[] = "0123456789ABCDEF"; + + for(InterfaceList *ifl=m->getInterfaces(); ifl!=NULL; ifl=ifl->getNext()) { + if((i = ifl->getInterface()->vlan) > 0) + vlans.addVlan(i); + } + //vlans.addVlan(0); + + while(vlans.getNext(vlan)) { + SNMPResults res(m->multiplesnmpwalk(2, vlan, &OID_ForwardingInterface, &OID_ForwardingType)); + + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + + mac[0] = 0; + for(i=0; i<6; i++) { + sprintf(buffer, "%c%c:", hextable[o[11+i]/16], hextable[o[11+i]%16]); + strcat(mac, buffer); + } + mac[17] = 0; + + switch(o[10]) { + case 3: + r->get_value(i); + if(i == 3) { + if(!fdb) { + fdb = new ForwardingDatabase(mac, vlan, 0); + } + else { + fdb->addEntry(mac, vlan, 0); + } + } + break; + + case 2: + r->get_value(i); + if(i > 0) { + I = m->getInterfaceByDot1d(i); + if(!I) + I = m->getInterfaceByPortDot1d(i); + if(!I) + I = m->getInterfaceById(i); + if(I && fdb->hasMAC(mac, vlan) && (I->vlan != -1 || (I->voiceVlan > 0 && I->voiceVlan < 4096))) + I->addForwardingDBEntry(mac, vlan, 0); + } + break; + } + } + } + + if(fdb) + delete fdb; +} + +void get_secure_addresses_infos(Materiel *m) { + const SNMPResult *r; + Interface *I; + char buffer[8], mac[24]; + char hextable[] = "0123456789ABCDEF"; + int val; + + SNMPResults res(m->snmpwalk(&OID_SecureMacType)); + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + + mac[0] = 0; + for(unsigned short int i=0; i<6; i++) { + sprintf(buffer, "%c%c:", hextable[o[15+i]/16], hextable[o[15+i]%16]); + strcat(mac, buffer); + } + mac[17] = 0; + + r->get_value(val); + I = m->getInterfaceById(o[14]); + if(I) + I->addForwardingDBEntry(mac, 0, val); + } +} + +void get_fdb_infos(Materiel *m) { + get_real_fdb_infos(m); + get_secure_addresses_infos(m); +} + diff --git a/backend/camembert/functions.h b/backend/camembert/functions.h new file mode 100644 index 0000000..fd560e3 --- /dev/null +++ b/backend/camembert/functions.h @@ -0,0 +1,12 @@ +#ifndef __CAMEMBERT_FUNCTIONS_H +#define __CAMEMBERT_FUNCTIONS_H + +#include "camembert.h" + +void get_interfaces_infos(Materiel *m); +void get_arp_infos(Materiel *m); +void get_fdb_infos(Materiel *m); +void apply_actions(Materiel *m); + +#endif /* __CAMEMBERT_FUNCTIONS_H */ + diff --git a/backend/camembert/functions.o b/backend/camembert/functions.o new file mode 100644 index 0000000000000000000000000000000000000000..8cec1b16411b03f1f6894b6619d5fda743f571b8 GIT binary patch literal 20420 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 KEa!J$mj4e=^f9Oa literal 0 HcmV?d00001 diff --git a/backend/camembert/interface.cpp b/backend/camembert/interface.cpp new file mode 100644 index 0000000..54ac923 --- /dev/null +++ b/backend/camembert/interface.cpp @@ -0,0 +1,231 @@ +#include "interface.h" + +Interface::Interface(unsigned int dbID, unsigned short int ifNum) { + _dbID = dbID; + _number = ifNum; + + _description = NULL; + _name = NULL; + _address[0] = 0; + _fdb = NULL; + + oldLinks = NULL; + nbOldLinks = 0; + + vlan = 0; + voiceVlan = 0; + nativeVlan = 0; + + ifType = 0; + speed = 0; + adminStatus = 0; + operStatus = 0; + ifDot1d = 0; + + moduleNum = 0; + portNum = 0; + portDot1d = 0; + spanningTree = 0; + + _dstDevice = NULL; + + portSecEnabled = 0; + portSecStatus = 0; + maxMacCount = 0; + currMacCount = 0; + violationCount = 0; + _lastSecAddr[0] = 0; + stickyEnabled = 0; + + _actions = NULL; +} + +Interface::~Interface() { + if(_description) + delete[] _description; + if(_name) + delete[] _name; + if(oldLinks) + delete[] oldLinks; + if(_fdb) + delete _fdb; + if(_dstDevice) { + dstDevice_t* dd = _dstDevice; +/* while(_dstDevice->next) { + dd = _dstDevice->next; + while(dd->next) + dd = dd->next; + delete dd->dstIfName; + delete dd; + }*/ + delete _dstDevice; + } + if(_actions) + delete _actions; +} + +void Interface::setName(const char *name) { + if(_name) + delete[] _name; + _name = new char[strlen(name)+1]; + strcpy(_name, name); +} + +void Interface::setAddress(const char *addr) { + strcpy(_address, addr); +} + +void Interface::setDescription(const char *descr) { + if(_description) + delete[] _description; + _description = new char[strlen(descr)+1]; + strcpy(_description, descr); + + char *c; + while(c = strchr(_description, '\'')) + *c = ' '; +} + +void Interface::addForwardingDBEntry(const char *mac, unsigned short int vlan, unsigned short int type) { + if(!_fdb) + _fdb = new ForwardingDatabase(mac, vlan, type); + else + _fdb->addEntry(mac, vlan, type); +} + +void Interface::addDistantDevice(const char* ifName, void const* device) { + if(!_dstDevice) { + _dstDevice = new dstDevice_t; + _dstDevice->dstIfName = new char[strlen(ifName)+1]; + strcpy(_dstDevice->dstIfName, ifName); + _dstDevice->distantDevice = device; + _dstDevice->next = NULL; + } + else { + dstDevice_t* dd = _dstDevice; + while(dd->next) + dd = dd->next; + dd->next = new dstDevice_t; + dd->next->dstIfName = new char[strlen(ifName)+1]; + strcpy(dd->next->dstIfName, ifName); + dd->next->distantDevice = device; + dd->next->next = NULL; + } +} + +void Interface::setLastMacAddr(const char *addr) { + strcpy(_lastSecAddr, addr); +} + +void Interface::addAction(unsigned short int num, const char *opt) { + if(!_actions) + _actions = new Action(num, opt); + else + _actions->addAction(num, opt); +} + +InterfaceList::InterfaceList(Interface *i, unsigned int dbStatus) { + _current = i; + _next = NULL; + _dbStatus = dbStatus; +} + +InterfaceList::~InterfaceList() { + delete _current; + if(_next) + delete _next; +} + +void InterfaceList::addInterface(Interface *i, unsigned int dbStatus) { + InterfaceList *ifl = this; + // Va jusqu'à la fin de la liste + while(ifl->_next) + ifl = ifl->_next; + // Ajoute l'interface à la fin + ifl->_next = new InterfaceList(i, dbStatus); +} + +Interface *InterfaceList::getInterfaceById(unsigned int id) const { + const InterfaceList *ifl; + Interface *i; + + // Parcourt la liste des interfaces + for(ifl=this; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + // retourne l'interface courante si son ifNum correspond à celui passé + if(i->getIfNumber() == id) + return i; + } + + return NULL; +} + +Interface *InterfaceList::getInterfaceByDot1d(unsigned int id) const { + const InterfaceList *ifl; + Interface *i; + + // Parcourt la liste des interfaces + for(ifl=this; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + // retourne l'interface courante si son ifNum correspond à celui passé + if(i->ifDot1d == id) + return i; + } + + return NULL; +} + +Interface *InterfaceList::getInterfaceByPort(unsigned int module, unsigned int port) const { + const InterfaceList *ifl; + Interface *i; + + // Parcourt la liste des interfaces + for(ifl=this; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + // retourne l'interface courante si son ifNum correspond à celui passé + if(i->moduleNum == module && i->portNum == port) + return i; + } + + return NULL; +} + +Interface *InterfaceList::getInterfaceByPortDot1d(unsigned int id) const { + const InterfaceList *ifl; + Interface *i; + + // Parcourt la liste des interfaces + for(ifl=this; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + // retourne l'interface courante si son ifNum correspond à celui passé + if(i->portDot1d == id) + return i; + } + + return NULL; +} + +/*Interface *InterfaceList::getInterfaceByDbId(unsigned int dbId) const { + const InterfaceList *ifl; + Interface *i; + + for(ifl=this; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + if(i->getID() == dbId) + return i; + } + + return NULL; +} */ + +void InterfaceList::foundInterface(unsigned int id) { + InterfaceList *ifl; + + for(ifl=this; ifl!=NULL; ifl=ifl->getNext()) { + if(ifl->getInterface()->getIfNumber() == id) { + ifl->setDBstatus(DBSTATUS_UPDATED); + break; + } + } +} + diff --git a/backend/camembert/interface.h b/backend/camembert/interface.h new file mode 100644 index 0000000..541d369 --- /dev/null +++ b/backend/camembert/interface.h @@ -0,0 +1,97 @@ +#ifndef __CAMEMBERT_INTERFACE_H +#define __CAMEMBERT_INTERFACE_H + +#include +#include "pgdb.h" +#include "fdb.h" +#include "action.h" + +typedef struct dstDevice_s { + char* dstIfName; + void const* distantDevice; + struct dstDevice_s* next; +} dstDevice_t; + +class Interface { + private: + unsigned int _dbID; + unsigned short int _number; + char *_name; + char _address[24]; + char *_description; + + ForwardingDatabase *_fdb; + char _lastSecAddr[24]; + + dstDevice_t* _dstDevice; + + Action *_actions; + + public: + unsigned int ifType; + unsigned int speed; + unsigned short int adminStatus; + unsigned short int operStatus; + unsigned int ifDot1d; + unsigned short int spanningTree; + + int vlan, voiceVlan, nativeVlan; + + unsigned int moduleNum, portNum, portDot1d; + + unsigned int *oldLinks; + unsigned short int nbOldLinks; + + unsigned short int portSecEnabled; + unsigned short int portSecStatus; + unsigned short int maxMacCount; + unsigned short int currMacCount; + unsigned short int violationCount; + unsigned short int stickyEnabled; + + Interface(unsigned int dbID, unsigned short int ifNum); + ~Interface(); + + unsigned int getID() const { return _dbID; } + unsigned short int getIfNumber() const { return _number; } + const char *getName() const { return _name; } + const char *getAddress() const { return _address; } + const char *getDescription() const { return _description; } + ForwardingDatabase *getForwardingDB() const { return _fdb; } + dstDevice_t* getDistantDevice() const { return _dstDevice; } + const char *getLastMacAddr() const { return _lastSecAddr; } + Action *getActions() const { return _actions; } + + void setName(const char *name); + void setAddress(const char *addr); + void setDescription(const char *descr); + void addForwardingDBEntry(const char *mac, unsigned short int vlan, unsigned short int type); + void addDistantDevice(const char* ifName, void const* device); + void setLastMacAddr(const char *addr); + void addAction(unsigned short int num, const char *opt); +}; + +class InterfaceList: public DBObject { + private: + InterfaceList *_next; + Interface *_current; + + public: + InterfaceList(Interface *i, unsigned int dbStatus); + ~InterfaceList(); + + void addInterface(Interface *i, unsigned int dbStatus); + Interface *getInterface() const { return _current; } + InterfaceList *getNext() const { return _next; } + + Interface *getInterfaceById(unsigned int id) const; + Interface *getInterfaceByDot1d(unsigned int id) const; + Interface *getInterfaceByPort(unsigned int module, unsigned int port) const; + Interface *getInterfaceByPortDot1d(unsigned int id) const; + //Interface *getInterfaceByDbId(unsigned int dbId) const; + + void foundInterface(unsigned int id); +}; + +#endif /* __CAMEMBERT_INTERFACE_H */ + diff --git a/backend/camembert/interface.o b/backend/camembert/interface.o new file mode 100644 index 0000000000000000000000000000000000000000..76c91c900404a9d5c40ffb48c3932a876c684b30 GIT binary patch literal 6592 zcmd5=ZERE589pXA)Uc#ZyR?FlsR7(JT_MAe(xgM1iET)LPz!26fN>nhfw16Y@il~r zFjmLF?G=@!u}RbRgCG2uy7gmHS5}R6(Grnm+7F7DMh8FAEl8abp-w}LX-L`g+2%O$Ml5@n*ugt%O3N*WBP6P04IrD|YAyRC=Z zo=cG`y{%%*@8w{Z7FK-MG`DZOOUs|afbUwDcJ_64I5K36jQQO_am8OD#{6~cj`;@& zU1R@{l>oi{)Cfv)5<}YA(_mNptI*A# zMR&~KLUD8?fA0O~*gJRmd4icAa$|}zKQlAt2tTKwWTa)}#}OmH@&9pt`C@)&A0fX8 z)=pu*TG*{esrv6SC=+P>8UO82nw>M1G)Y6kF zZ`#wRE;5{DrhUVhGhd8wN^V$Pu7{D=4yEb$nLA^l ziUllK$UXkI2QxD>rr!9-to^eyJ6)|u-BSyhk2azQ;3@OZF~wT}^Ur9iM=H#^TZ<4- zw5sfAHPS%-+37|tdKVtF!3SKM%ze+L!3sUlf`Do@^rCk)9KXzcgrW~V(59odc;qx% zWKzyFsh#jD+G}37rg*h0#-NsNfe$z^tw*#Gt-)(bYD`3-is!+sPj+}!A@%&6@``!n zyLX`?HONQLAd4WfXfN8MK7O~eK3c%7jaf1I5x5!LW18w!^nhCz)({Dgshe9Dw@`Mp zX5WEWbi`h4Ef9Q&v!LBsu*BKa&&~Z?To0~9q?*f;!Tm5;r3dPejaNetYl7^s8j)&q zWeUq;UN4BKmN7;qL zlUojxwo#eIBk4N4MXqC3GYc#3LM1~Jg>GchO02Lh*0s#Th_ijye%+hLsN|wB?<;fv zX50_kDC##Jwn=86tJ0H$>sy{1F*D<;eCbO<#L89iU01o_z~dX13DHwN?l6zwsSn^hCS3(BV* zw)`wKKJ=_yTC?SCr<9zAn<Te8YuiXeI)MsPPV<+Hc|^ ztJk0GsaKN&s;KXbt8r1^kxq;HgNZ~eIgm*9sWGVcBt?Cq_sEfC3RS>=m6zl4bA4ziRM{UnQ5z8aYvxFL<@3H-is{&`kDHTYo&<;I~wLq?4zzFTR zM*OmRR&wqcwy@0?IrZ3H#6$i6rnd$<^v{yB(cen5Yy72*tr-vd)MraW@5^S__)8g| ztrCqv{K-4wv;4)Ro;5G^hAje_mB&8x?RB}2!^rc^x-dVhF5K2uuIP#|emi%wBbzlZ z(Kd}3^z@8?4xw09$lA0j z6WsO1Rwm@-B8SS13fScf1=2AZ@8Ku0A}b^(Ft3;KOyIHPThJdy(7crN$a2e%sIIRRdaGWgM_{4Iz4JqP~{ zcsulYms0|Z7;MQGB{r4U6 zn-2bOaO=*c{!_Ls!FX%Htvi?UuRG)+aO=*ce78fMcJSB1tvi_d=fSNzm;9&T7qIzx zzmkjYl$z}CiYJowiM~D&+q=D~)fx$`4INfREOu~UAl8@cPxq$cJ>BZ@*wHl(J)fbs zce`(0!FF4BT7`YM=k9CSM|-O+E}U@ z?(J7Yhv)RVcHL}Mn`dXN4PCvNRA(VtGt$Oj^Q#?)lL^)5ODEM}Gu}9ACT+@`4%Wsy zZRU>ZrH&!SNa|R3sx!7Dnatr9iH)6|{UJKV3pdes+@ZJziquG^(}q$>Sibd$D~SCNPpQG=(e6Z&I^nHH z>8mH1g0EEEsD5#?tX%_}O}+ic;{Bc7se{4hP)hATPCFGl68ospCd3QBBc4t+!sHRu zka59Hal)68!FIg+W%!-(Ig1fYrW5_$eMX6`VjSu(Ek@86GU_;2h?Yz)i$#lJ1lLOA z(vxHU=>_<3u2~+DC~ig%~8le~t+MZxf#t;ul2t|A+|x|B~Y_d?ayvF%jcSiI{gC z@p=5`mEr}lzzaqpvBJ9fX`Xp9MTuFr8i$vJHBC(YSyK}@k zA-*ri-bFE7B0 zbqNvvzber$ajV2#65|rPC4Nid2gEwmU-Ijc-;#Wah40PbtB^7q|fLKflEN5(gxnm-wEsetIP(ip); } +IP::IP(unsigned int const ip) { this->ipint = ip; this->computestring(); } +IP::IP(IP const &ip) { this->ipint = ip.getIPint(); this->computestring(); } +IP::IP(IP const * const ip) { this->ipint = ip->getIPint(); this->computestring(); } + +void IP::computestring() { + sprintf(this->ip, "%d.%d.%d.%d", ((ipint/16777216)%256), (ipint/65536)%256, (ipint/256)%256, ipint%256); +} + +IP &IP::operator = (char const * const ip) { + this->setIP(ip); + return(*this); +} + +IP &IP::operator = (unsigned int const ip) { + this->ipint = ip; + this->computestring(); + return(*this); +} + +IP &IP::operator = (IP const &ip) { + this->ipint = ip.getIPint(); + strcpy(this->ip, ip.getIPstr()); + return(*this); +} + +char const * IP::getIPstr() const { return(ip); } +unsigned int const IP::getIPint() const { return (ipint); } +bool const IP::operator==(IP const &ip) const { return (ip.getIPint() == this->ipint); } +bool const IP::operator==(char const * const ip) const { return (!strcmp(this->ip, ip)); } +bool const IP::operator==(unsigned int const ip) const { return (ip == this->ipint); } +bool const IP::operator==(IP const * const ip) const { return (ip->getIPint() == this->ipint); } +bool const IP::operator!=(IP const &ip) const { return (ip.getIPint()!=this->ipint); } +bool const IP::operator!=(char const * const ip) const { return (strcmp(this->ip, ip)); } +bool const IP::operator!=(unsigned int const ip) const { return (ip != this->ipint); } +bool const IP::operator!=(IP const * const ip) const { return (ip->getIPint() != this->ipint); } +unsigned int const IP::operator &(IP const &ip) const { return (this->ipint & ip.getIPint()); } +unsigned int const IP::operator &(IP const * const ip) const { return (this->ipint & ip->getIPint()); } + +IP &IP::operator &= (const IP &ip) { + this->ipint &= ip.getIPint(); + this->computestring(); + return(*this); +} + +IP &IP::operator &= (const IP *ip) { + this->ipint &= ip->getIPint(); + this->computestring(); + return(*this); +} + +void IP::setIP(char const * ip) { + unsigned int ipi[4]; + int i=0; + const char *ip2 = ip; + + this->ipint = 0; + this->ip[0] = 0; + + while (i<4 && ip!=NULL) { + if (*ip != 0) { + if ((ipi[i]=atoi(ip))>255) + return; + if ((ip=strchr(ip, '.')) != NULL) { + ip+=1; + i++; + } + } + } + + if ((strlen(ip2) < IP_LENGTH) && (i==3)) { + strcpy(this->ip, ip2); + this->ipint = ipi[0]*16777216+ipi[1]*65536+ipi[2]*256+ipi[3]; + } + + return; +} + +// ---------------------------------------- +// Class IPNetmask +// ---------------------------------------- +IPNetmask::IPNetmask() { + smallmask = 0; +} + +IPNetmask::IPNetmask(char const * const ip) { + int i=31; + unsigned int mask; + + netmask = ip; + mask = netmask.getIPint(); + + smallmask=0; + + while (i>=0) { + if ((mask & (1 << i)) != 0) { + smallmask++; + i--; + } else + i=-1; + } +} + +IPNetmask::IPNetmask(unsigned int const mask) { + unsigned int newmask = 0, i; + + for (i=0; i_next) + lst = lst->_next; + lst->_next = new IPList(); + lst->_next->_current = ip; + lst->_next->setDBstatus(dbStatus); +} + +bool IPList::isIPinList(const IP *ip) const { + // L'IP passée est nulle ou il n'y a pas d'IP courante, pas la peine d'aller plus loin, + // on considère que d'IP passée n'est pas dans la liste + if(!ip || !_current) + return false; + + // On parcourt la liste à la recherche d'une même IP et on retourne VRAI si on trouve. + for(const IPList *lst=this; lst!=NULL; lst=lst->getNext()) { + if(lst->getIP()->getIPint() == ip->getIPint()) + return true; + } + + return false; +} + +void IPList::foundIP(const IP *ip) { + if(!ip || !_current) + return; + + for(IPList *lst=this; lst!=NULL; lst=lst->getNext()) { + if(lst->getIP()->getIPint() == ip->getIPint()) { + lst->setDBstatus(DBSTATUS_UPDATED); + return; + } + } +} + +void IPList::setFirst(IPList *lip) { + IP *tmpI = lip->_current; + unsigned int tmpD = lip->_dbStatus; + + lip->_current = this->_current; + lip->_dbStatus = this->_dbStatus; + + this->_current = tmpI; + this->_dbStatus = tmpD; +} + +IPList::~IPList() { + if(_current) + delete _current; + if(_next) + delete _next; +} diff --git a/backend/camembert/ip.h b/backend/camembert/ip.h new file mode 100644 index 0000000..718bfb5 --- /dev/null +++ b/backend/camembert/ip.h @@ -0,0 +1,147 @@ +/* ======================================================== + +Camembert Project +Alban FERON, 2007 + +IP et IPNetmask repris de Kindmana, par Aurélien Méré + +======================================================== */ + +#ifndef __CAMEMBERT_IP_H +#define __CAMEMBERT_IP_H + +#include +#include +#include + +#include "pgdb.h" + +#define IP_LENGTH 24 + +class IP { + + private: + char ip[IP_LENGTH]; + unsigned int ipint; + + void setIP(char const * ip); + void computestring(); + + public: + + // IP Constructor + + IP(); + IP(char const * const ip); + IP(unsigned int const ip); + IP(IP const &ip); + IP(IP const * const ip); + + // Base informations for getting, setting + // and testing an IP + + char const *getIPstr() const; + unsigned int const getIPint() const; + + bool const operator == (char const * const ip) const; + bool const operator == (IP const &ip) const; + bool const operator == (unsigned int const ip) const; + bool const operator == (IP const * const ip) const; + bool const operator != (char const * const ip) const; + bool const operator != (IP const &ip) const; + bool const operator != (unsigned int const ip) const; + bool const operator != (IP const * const ip) const; + + IP &operator = (char const * const ip); + IP &operator = (unsigned int const ip); + IP &operator = (IP const &ip); + IP &operator = (IP const * const ip); + + unsigned int const operator & (const IP &ip) const; + unsigned int const operator & (const IP * const ip) const; + + IP &operator &= (const IP &ip); + IP &operator &= (const IP * const ip); +}; + +class IPNetmask { + + private: + IP netmask; + unsigned int smallmask; + + public: + IPNetmask(); + IPNetmask(const char * const ip); + IPNetmask(const unsigned int mask); + + unsigned int const getMask() const ; + char const *getMaskStr() const ; + unsigned int const getMaskSmall() const; +}; + +/** + * Liste d'IPs + */ +class IPList: public DBObject { + private: + IPList *_next; + IP *_current; + + public: + /** + * Crée une nouvelle liste vide + */ + IPList(); + + /** + * Détruit la liste ainsi que les IPs qu'elle contient + */ + ~IPList(); + + /** + * Ajoute une IP à la fin de la liste + * + * @param ip - IP à ajouter + * @param dbStatus - L'IP est sortie de la base où est à créer ? + */ + void addIP(IP *const ip, unsigned int dbStatus); + + /** + * @return IP courante + */ + IP *getIP() const { return _current; } + + /** + * @return Sous liste contenant les IP suivantes + */ + IPList *getNext() const { return _next; } + + /** + * Cherche si une IP donnée existe dans la liste + * + * @param ip - IP à chercher + * @return VRAI si l'ip donnée est dans la liste, FAUX sinon + */ + bool isIPinList(const IP *ip) const; + + /** + * Spécifie que l'IP donnée a été trouvée, donc qu'elle peut être + * gardée dans la base de données + * + * @param ip - IP à déclarer comme "trouvée" + */ + void foundIP(const IP *ip); + + /** + * Etablit l'IP passée (ou plutot l'élément de liste contenant l'IP) comme + * première de la liste. + * Attention à ne passer que des éléments appartenant effectivement bien à la liste. + * Aucune vérification n'est faite. + * + * @param lip - Elément à passer au début de la liste. + */ + void setFirst(IPList *lip); +}; + +#endif /* __CAMEMBERT_H */ diff --git a/backend/camembert/ip.o b/backend/camembert/ip.o new file mode 100644 index 0000000000000000000000000000000000000000..1dddf857950b26a7617b074911866befe5d1ce4f GIT binary patch literal 9392 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 +#include +#include + +#include "address.h" +#include "v3.h" + +/* Borlands isdigit has a bug */ +#ifdef __BCPLUSPLUS__ +#define my_isdigit(c) ((c) >= '0' && (c) <= '9') +#else +#define my_isdigit isdigit +#endif + +#ifdef ADDRESS_DEBUG +#define ADDRESS_TRACE debugprintf(0, "ADDRESS %p Enter %s", this, __PRETTY_FUNCTION__) +#define ADDRESS_TRACE2 debugprintf(0, "ADDRESS op Enter %s", __PRETTY_FUNCTION__) +#else +#define ADDRESS_TRACE +#define ADDRESS_TRACE2 +#endif + +//================================================================= +//======== Abstract Address Class Implementation ================== +//================================================================= + +Address::Address() + : addr_changed(true), valid_flag(false) +{ + ADDRESS_TRACE; + + memset(address_buffer, 0, sizeof(unsigned char)*BUFSIZE); +} + +//------------[ Address::trim_white_space( char * ptr) ]------------ +// destructive trim white space +void Address::trim_white_space(char *ptr) +{ + ADDRESS_TRACE; + + char *tmp = ptr; // init + while (*tmp==' ') tmp++; // skip leading white space + while (*tmp && (*tmp != ' ')) *ptr++ = *tmp++; // move string to beginning + *ptr = 0; // set end of string +} + +//----------------------------------------------------------------------- +// overloaded equivlence operator, are two addresses equal? +int operator==(const Address &lhs, const Address &rhs) +{ + ADDRESS_TRACE2; + + return (strcmp((const char*)lhs, (const char*)rhs) == 0); +} + +//------------------------------------------------------------------ +// overloaded > operator, is a1 > a2 +int operator>(const Address &lhs, const Address &rhs) +{ + ADDRESS_TRACE2; + + return (strcmp((const char*)lhs, (const char*)rhs) > 0); +} + +//----------------------------------------------------------------- +// overloaded < operator, is a1 < a2 +int operator<(const Address &lhs, const Address &rhs) +{ + ADDRESS_TRACE2; + + return (strcmp((const char*)lhs, (const char*)rhs) < 0); +} + +//------------------------------------------------------------------ +// equivlence operator overloaded, are an address and a string equal? +int operator==(const Address &lhs, const char *rhs) +{ + ADDRESS_TRACE2; + + if (!rhs && !lhs.valid()) + return TRUE; + if (strcmp((const char *)lhs, rhs) == 0) + return TRUE; + return FALSE; +} + +//------------------------------------------------------------------ +// overloaded > , is a > inaddr +int operator>(const Address &lhs, const char *rhs) +{ + ADDRESS_TRACE2; + + if (!rhs) + return lhs.valid(); // if lhs valid then > NULL, else invalid !> NULL + if (strcmp((const char *)lhs, rhs) > 0) + return TRUE; + return FALSE; +} + +//------------------------------------------------------------------ +// overloaded >= , is a >= inaddr +int operator>=(const Address &lhs, const char *rhs) +{ + ADDRESS_TRACE2; + + if (!rhs) + return TRUE; // always >= NULL + if (strcmp((const char *)lhs, rhs) >= 0) + return TRUE; + return FALSE; +} + +//----------------------------------------------------------------- +// overloaded < , are an address and a string equal? +int operator<(const Address &lhs, const char *rhs) +{ + ADDRESS_TRACE2; + + if (!rhs) + return FALSE; // always >= NULL + if (strcmp((const char *)lhs, rhs) < 0) + return TRUE; + return FALSE; +} + +//----------------------------------------------------------------- +// overloaded <= , is a <= inaddr +int operator<=(const Address &lhs, const char *rhs) +{ + ADDRESS_TRACE2; + + if (!rhs) + return !lhs.valid(); // invalid == NULL, else valid > NULL + if (strcmp((const char *)lhs, rhs) <= 0) + return TRUE; + return FALSE; +} + +//===================================================================== +//============ IPAddress Implementation =============================== +//===================================================================== + +//-------[ construct an IP address with no agrs ]---------------------- +IpAddress::IpAddress() + : Address(), iv_friendly_name_status(0), ip_version(version_ipv4) +{ + ADDRESS_TRACE; + + // always initialize what type this object is + smival.syntax = sNMP_SYNTAX_IPADDR; + smival.value.string.len = IPLEN; + smival.value.string.ptr = address_buffer; + + iv_friendly_name[0]=0; +} + +//-------[ construct an IP address with a string ]--------------------- +IpAddress::IpAddress(const char *inaddr) + : Address() +{ + ADDRESS_TRACE; + + // always initialize what type this object is + smival.syntax = sNMP_SYNTAX_IPADDR; + smival.value.string.len = IPLEN; + smival.value.string.ptr = address_buffer; + + // parse_address initializes valid, address_buffer & iv_friendly_name + valid_flag = parse_address(inaddr); +} + +//-----[ IP Address copy constructor ]--------------------------------- +IpAddress::IpAddress(const IpAddress &ipaddr) + : iv_friendly_name_status(0), ip_version(ipaddr.ip_version) +{ + ADDRESS_TRACE; + + // always initialize what type this object is + smival.syntax = sNMP_SYNTAX_IPADDR; + smival.value.string.len = ipaddr.smival.value.string.len; + smival.value.string.ptr = address_buffer; + + iv_friendly_name[0]=0; + valid_flag = ipaddr.valid_flag; + if (valid_flag) + { + // copy the address data + MEMCPY(address_buffer, ipaddr.address_buffer, smival.value.string.len); + // and the friendly name + strcpy(iv_friendly_name, ipaddr.iv_friendly_name); + + if (!ipaddr.addr_changed) + { + memcpy(output_buffer, ipaddr.output_buffer, + sizeof(unsigned char) * OUTBUFF); + addr_changed = false; + } + } +} + +//-----[ construct an IP address with a GenAddress ]--------------------- +IpAddress::IpAddress(const GenAddress &genaddr) + : iv_friendly_name_status(0) +{ + ADDRESS_TRACE; + + // always initialize what type this object is + smival.syntax = sNMP_SYNTAX_IPADDR; + smival.value.string.len = IPLEN; + smival.value.string.ptr = address_buffer; + + iv_friendly_name[0]=0; + output_buffer[0]=0; + + // allow use of an ip or udp genaddress + valid_flag = genaddr.valid(); + if (valid_flag) + { + if (genaddr.get_type() == type_ip) + { + // copy in the IP address data + *this = genaddr.cast_ipaddress(); + return; + } + else if (genaddr.get_type() == type_udp) + { + // copy in the IP address data + *this = genaddr.cast_udpaddress(); + return; + } + } + valid_flag = false; + addr_changed = true; +} + +//-----[ IP Address general = operator ]------------------------------- +SnmpSyntax& IpAddress::operator=(const SnmpSyntax &val) +{ + ADDRESS_TRACE; + + if (this == &val) return *this; // protect against assignment from itself + + addr_changed = true; + valid_flag = false; // will get set TRUE if really valid + iv_friendly_name[0]=0; + + if (val.valid()) + { + switch (val.get_syntax()) + { + case sNMP_SYNTAX_IPADDR: + case sNMP_SYNTAX_OCTETS: + if ((((IpAddress &)val).smival.value.string.len == IPLEN) || + (((IpAddress &)val).smival.value.string.len == UDPIPLEN)) + { + MEMCPY(address_buffer, + ((IpAddress &)val).smival.value.string.ptr, IPLEN); + valid_flag = true; + ip_version = version_ipv4; + smival.value.string.len = IPLEN; + } + else if ((((IpAddress &)val).smival.value.string.len == IP6LEN) || + (((IpAddress &)val).smival.value.string.len == UDPIP6LEN)) + { + MEMCPY(address_buffer, + ((IpAddress &)val).smival.value.string.ptr, IP6LEN); + valid_flag = true; + ip_version = version_ipv6; + smival.value.string.len = IP6LEN; + } + break; + + // NOTE: as a value add, other types could have "logical" + // mappings, i.e. integer32 and unsigned32 + } + } + return *this; +} + +//------[ assignment to another ipaddress object overloaded ]----------------- +IpAddress& IpAddress::operator=(const IpAddress &ipaddr) +{ + ADDRESS_TRACE; + + if (this == &ipaddr) return *this; // protect against assignment from itself + + valid_flag = ipaddr.valid_flag; + iv_friendly_name[0]=0; + + if (valid_flag) + { + if (ipaddr.ip_version == version_ipv4) + { + MEMCPY(address_buffer, ipaddr.address_buffer, IPLEN); + ip_version = version_ipv4; + smival.value.string.len = IPLEN; + } + else + { + MEMCPY(address_buffer, ipaddr.address_buffer, IP6LEN); + ip_version = version_ipv6; + smival.value.string.len = IP6LEN; + } + strcpy(iv_friendly_name, ipaddr.iv_friendly_name); + + if (ipaddr.addr_changed) + addr_changed = true; + else + { + memcpy(output_buffer, ipaddr.output_buffer, + sizeof(unsigned char) * OUTBUFF); + addr_changed = false; + } + } + else + addr_changed = true; + return *this; +} + +IpAddress& IpAddress::operator=(const char *inaddr) +{ + ADDRESS_TRACE; + + valid_flag = parse_address(inaddr); + addr_changed = true; + return *this; +} + +//-------[ return the friendly name ]---------------------------------- +char *IpAddress::friendly_name(int &status) +{ + ADDRESS_TRACE; + + if ((iv_friendly_name[0]==0) && (valid_flag)) + this->addr_to_friendly(); + status = iv_friendly_name_status; + return iv_friendly_name; +} + +// parse a dotted string +int IpAddress::parse_dotted_ipstring(const char *inaddr) +{ + ADDRESS_TRACE; + + char *ip_token; + int token_count=0; + unsigned int value; + int error_status = FALSE; + char temp[30]; // temp buffer for destruction + int z,w; + + // check len, an ip can never be bigger than 15 + // 123456789012345 + // XXX.XXX.XXX.XXX + if (!inaddr || (strlen(inaddr) > 30)) return FALSE; + + strcpy(temp, inaddr); + trim_white_space(temp); + if (strlen(temp) > 15) return FALSE; + + // must only have three dots strtok will not catch this ! + char *ptr = temp; + int dot_count = 0; + while (*ptr != 0) + { + if (*ptr == '.') dot_count++; + ptr++; + } + if (dot_count != 3) + return FALSE; + + // look for dot token separator +#ifdef linux + char* lasts = 0; + ip_token = strtok_r((char *) temp,".", &lasts); +#else + ip_token = strtok((char *) temp,"."); +#endif + + // while more tokens.. + while (ip_token) + { + // verify that the token is all numerics + w = strlen(ip_token); + if (w>3) return FALSE; + for (z=0;z '9')) + return FALSE; + + value = atoi(ip_token); + if ((value > 0)&& (value <=255)) + address_buffer[token_count] = (unsigned char) value; + else + if (strcmp(ip_token,"0")==0) + address_buffer[token_count]= (unsigned char) 0; + else + error_status = TRUE; + token_count++; +#ifdef linux + ip_token = strtok_r(NULL, ".", &lasts); +#else + ip_token = strtok(NULL, "."); +#endif + } + + // gota be four in len + if (token_count != 4) + return FALSE; + + // any parsing errors? + if (error_status) + return FALSE; + + ip_version = version_ipv4; + smival.value.string.len = IPLEN; + return TRUE; +} + +#define ATOI(x) if ((x >= 48) && (x <= 57)) x = x-48; /* 0-9 */ \ + else if ((x >= 97) && (x <=102)) x = x-87; /* a-f */ \ + else if ((x >= 65) && (x <= 70)) x = x-55; /* A-F */ \ + else x=0 + +// parse a coloned string +int IpAddress::parse_coloned_ipstring(const char *inaddr) +{ + ADDRESS_TRACE; + + char temp[60]; // temp buffer for destruction + + // check len, an ipv6 can never be bigger than 39 + // 123456789012345678901234567890123456789 + // 1BCD:2BCD:3BCD:4BCD:5BCD:6BCD:7BCD:8BCD + if (!inaddr || (strlen(inaddr) > 60)) return FALSE; + strcpy(temp, inaddr); + trim_white_space(temp); + if (strlen(temp) > 39) return FALSE; + + char *in_ptr = temp; + char *out_ptr = (char*)address_buffer; + char *end_first_part = NULL; + char second[39]; + int second_used = FALSE; + int colon_count = 0; + int had_double_colon = FALSE; + int last_was_colon = FALSE; + int had_dot = FALSE; + int dot_count = 0; + int digit_count = 0; + char digits[4]; + char last_deliminiter = 0; + + while (*in_ptr != 0) + { + if (*in_ptr == '.') + { + last_deliminiter = *in_ptr; + had_dot = TRUE; + dot_count++; + if (dot_count > 3) + return FALSE; + if ((digit_count > 3) || (digit_count < 1)) + return FALSE; + for (int i=0; i 0) && (value <= 255)) + *out_ptr++ = (unsigned char) value; + else + { + if (strcmp(digits, "0") == 0) + *out_ptr++ = (unsigned char) 0; + else + return FALSE; + } + digit_count = 0; + } + else if (*in_ptr == ':') + { + last_deliminiter = *in_ptr; + + if (had_dot) + return FALSE; // don't allow : after a dot + + if (digit_count) + { + // move digits to right + { + for (int i=0; i= 4) + return FALSE; + if (!isxdigit(*in_ptr)) + return FALSE; + digits[digit_count] = tolower(*in_ptr); + + digit_count++; + if (digit_count > 4) + return FALSE; + last_was_colon = 0; + } + in_ptr++; + } + + // put last bytes from digits into buffer + if (digit_count) + { + if (last_deliminiter == ':') + { + { + // move digits to right + for (int i=0; i 3) || (digit_count < 1)) + return FALSE; + for (int i=0; i 0) && (value <= 255)) + *out_ptr++ = (unsigned char) value; + else + { + if (strcmp(digits, "0") == 0) + *out_ptr++ = (unsigned char) 0; + else + return FALSE; + } + digit_count = 0; + } + else + return FALSE; + } + + // must have between two and seven colons + if ((colon_count > 7) || (colon_count < 2)) + return FALSE; + + // if there was a dot there must be three of them + if ((dot_count > 0) && (dot_count != 3)) + return FALSE; + + if (second_used) + { + int len_first = end_first_part - (char*)address_buffer; + int len_second = out_ptr - second; + + int i=0; + for (i=0; ih_length == sizeof(in6_addr)) + { + in6_addr ipAddr; + memcpy((void *) &ipAddr, (void *) lookupResult->h_addr, + sizeof(in6_addr)); + + // now lets check out the coloned string + if (!inet_ntop(AF_INET6, &ipAddr, ds, 48)) + return FALSE; + debugprintf(4, "from inet_ntop: %s", ds); + if (!parse_coloned_ipstring(ds)) + return FALSE; + + // save the friendly name + strcpy(iv_friendly_name, inaddr); + + return TRUE; + } +#endif // SNMP_PP_IPv6 + if (lookupResult->h_length == sizeof(in_addr)) + { + in_addr ipAddr; + + memcpy((void *) &ipAddr, (void *) lookupResult->h_addr, + sizeof(in_addr)); + + // now lets check out the dotted string + strcpy(ds,inet_ntoa(ipAddr)); + + if (!parse_dotted_ipstring(ds)) + return FALSE; + + // save the friendly name + strcpy(iv_friendly_name, inaddr); + + return TRUE; + } + } // end if lookup result + else + { +#ifdef linux + iv_friendly_name_status = herrno; +#else + iv_friendly_name_status = h_errno; +#endif + return FALSE; + } + } // end else not a dotted string + return TRUE; +} + +// using the currently defined address, do a DNS +// and try to fill up the name +int IpAddress::addr_to_friendly() +{ + ADDRESS_TRACE; + + hostent *lookupResult; + char ds[48]; + + // can't look up an invalid address + if (!valid_flag) return -1; + + // lets try and get the friendly name from the DNS + strcpy(ds, this->IpAddress::get_printable()); + +#ifdef linux + int herrno = 0; + hostent lookup; + char buf[2048]; // TODO: Buf size too big? +#endif + if (ip_version == version_ipv4) + { + in_addr ipAddr; + +#if defined HAVE_INET_ATON + if (inet_aton((char*)ds, &ipAddr) == 0) + return -1; // bad address +#elif defined HAVE_INET_PTON + if (inet_pton(AF_INET, (char*)ds, &ipAddr) <= 0) + return -1; // bad address +#else + ipAddr.s_addr = inet_addr((char*)ds); + if (ipAddr.s_addr == INADDR_NONE) + return -1; // bad address +#endif + +#ifdef linux + gethostbyaddr_r((char *) &ipAddr, sizeof(in_addr), + AF_INET, &lookup, buf, 2048, &lookupResult, &herrno); +#else + lookupResult = gethostbyaddr((char *) &ipAddr, sizeof(in_addr), + AF_INET); +#endif + } + else + { +#ifdef SNMP_PP_IPv6 + in6_addr ipAddr; + + if (inet_pton(AF_INET6, (char*)ds, &ipAddr) <= 0) + return -1; // bad address + +#ifdef linux + gethostbyaddr_r((char *) &ipAddr, sizeof(in_addr), + AF_INET6, &lookup, buf, 2048, &lookupResult, &herrno); +#else + lookupResult = gethostbyaddr((char *) &ipAddr, sizeof(in6_addr), + AF_INET6); +#endif // linux +#else + return -1; +#endif // SNMP_PP_IPv6 + } + // if we found the name, then update the iv friendly name + if (lookupResult) + { + strcpy(iv_friendly_name, lookupResult->h_name); + return 0; + } + else + { +#ifdef linux + iv_friendly_name_status = herrno; +#else + iv_friendly_name_status = h_errno; +#endif + return iv_friendly_name_status; + } +} + +//----[ IP address format output ]------------------------------------ +void IpAddress::format_output() const +{ + ADDRESS_TRACE; + + // if valid format else null it + if (valid_flag) + { + if (ip_version == version_ipv4) + sprintf((char *) output_buffer,"%d.%d.%d.%d",address_buffer[0], + address_buffer[1], address_buffer[2], address_buffer[3]); + else + sprintf((char *) output_buffer, + "%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x:%02x%02x", + address_buffer[ 0], address_buffer[ 1], address_buffer[ 2], + address_buffer[ 3], address_buffer[ 4], address_buffer[ 5], + address_buffer[ 6], address_buffer[ 7], address_buffer[ 8], + address_buffer[ 9], address_buffer[10], address_buffer[11], + address_buffer[12], address_buffer[13], address_buffer[14], + address_buffer[15]); + } + else + *(char *)output_buffer = 0; + ((IpAddress *)this)->addr_changed = false; +} + +//----------------------------------------------------------------- +// logically and two IPaddresses and +// return the new one +void IpAddress::mask(const IpAddress& ipaddr) +{ + ADDRESS_TRACE; + + if (valid() && ipaddr.valid()) + { + int count = (ip_version == version_ipv4) ? IPLEN : IP6LEN; + + for (int i = 0; i < count; i++) + address_buffer[i] = address_buffer[i] & ipaddr.address_buffer[i]; + addr_changed = true; + } +} + +/** + * Map a IPv4 Address to a IPv6 address. + * + * @return - TRUE if no error occured. + */ +int IpAddress::map_to_ipv6() +{ + ADDRESS_TRACE; + + if (!valid()) + return FALSE; + + if (ip_version != version_ipv4) + return FALSE; + + /* just copy IPv4 address to the end of the buffer + zero the first 10 bytes and fill 2 Bytes with 0xff */ + memcpy(&address_buffer[12], address_buffer, 4); + memset(address_buffer, 0, 10); + address_buffer[10] = 0xff; + address_buffer[11] = 0xff; + + smival.value.string.len = IP6LEN; + ip_version = version_ipv6; + + addr_changed = true; + return TRUE; +} + +//======================================================================= +//========== Udp Address Implementation ================================= +//======================================================================= + +//-------[ construct an IP address with no agrs ]---------------------- +UdpAddress::UdpAddress() + : IpAddress() +{ + ADDRESS_TRACE; + + // Inherits IP Address attributes + // Always initialize (override) what type this object is + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = UDPIPLEN; + smival.value.string.ptr = address_buffer; + + sep = ':'; + set_port(0); +} + +//-----------------[ construct an Udp address with another Udp address ]--- +UdpAddress::UdpAddress(const UdpAddress &udpaddr) + : IpAddress(udpaddr) +{ + ADDRESS_TRACE; + + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = udpaddr.smival.value.string.len; + smival.value.string.ptr = address_buffer; + + // Copy the port value + sep = ':'; + set_port(udpaddr.get_port()); + + if (!udpaddr.addr_changed) + { + memcpy(output_buffer, udpaddr.output_buffer, + sizeof(unsigned char) * OUTBUFF); + addr_changed = false; + } +} + +// constructor with a dotted string +UdpAddress::UdpAddress(const char *inaddr) : IpAddress() +{ + ADDRESS_TRACE; + + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = UDPIPLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = parse_address(inaddr); + addr_changed = true; +} + +//-----------------[ construct a UdpAddress from a GenAddress ]-------------- +UdpAddress::UdpAddress(const GenAddress &genaddr) : IpAddress() +{ + ADDRESS_TRACE; + + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = UDPIPLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = genaddr.valid(); + + // allow use of an ip or udp genaddress + if (valid_flag) + { + if (genaddr.get_type() == type_udp) + { + *this = genaddr.cast_udpaddress(); // copy in the IP address data + } + else if (genaddr.get_type() == type_ip) + { + *this = genaddr.cast_ipaddress(); // copy in the IP address data + } + else + { + valid_flag = false; + } + } + sep = ':'; +} + + +//--------[ construct a udp from an IpAddress ]-------------------------- +UdpAddress::UdpAddress(const IpAddress &ipaddr):IpAddress(ipaddr) +{ + ADDRESS_TRACE; + + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + if (ip_version == version_ipv4) + smival.value.string.len = UDPIPLEN; + else + smival.value.string.len = UDPIP6LEN; + smival.value.string.ptr = address_buffer; + + sep = ':'; + addr_changed = true; + set_port(0); +} + +// copy an instance of this Value +SnmpSyntax& UdpAddress::operator=(const SnmpSyntax &val) +{ + ADDRESS_TRACE; + + if (this == &val) return *this; // protect against assignment from itself + + valid_flag = false; // will get set TRUE if really valid + addr_changed = true; + if (val.valid()) + { + switch (val.get_syntax()) + { + case sNMP_SYNTAX_IPADDR: + { + UdpAddress temp_udp(val.get_printable()); + *this = temp_udp; // valid_flag is set by the udp assignment + break; + } + case sNMP_SYNTAX_OCTETS: + if (((UdpAddress &)val).smival.value.string.len == UDPIPLEN) + { + MEMCPY(address_buffer,((UdpAddress &)val).smival.value.string.ptr, + UDPIPLEN); + iv_friendly_name[0]=0; + valid_flag = true; + ip_version = version_ipv4; + smival.value.string.len = UDPIPLEN; + } + else if (((UdpAddress &)val).smival.value.string.len == UDPIP6LEN) + { + MEMCPY(address_buffer,((UdpAddress &)val).smival.value.string.ptr, + UDPIP6LEN); + iv_friendly_name[0]=0; + valid_flag = true; + ip_version = version_ipv6; + smival.value.string.len = UDPIP6LEN; + } + break; + // NOTE: as a value add, other types could have "logical" + // mappings, i.e. integer32 and unsigned32 + } + } + return *this; +} + +// assignment to another UdpAddress object overloaded +UdpAddress& UdpAddress::operator=(const UdpAddress &udpaddr) +{ + ADDRESS_TRACE; + + if (this == &udpaddr) return *this; // protect against assignment from itself + + (IpAddress &)*this = udpaddr; // use ancestor assignment for ipaddr value + if (ip_version == version_ipv4) + smival.value.string.len = UDPIPLEN; + else + smival.value.string.len = UDPIP6LEN; + + set_port(udpaddr.get_port()); // copy to port value + if (udpaddr.addr_changed) + { + addr_changed = true; + } + else + { + memcpy(output_buffer, udpaddr.output_buffer, + sizeof(unsigned char) * OUTBUFF); + addr_changed = false; + } + + return *this; +} + +// assignment to another UdpAddress object overloaded +UdpAddress& UdpAddress::operator=(const IpAddress &ipaddr) +{ + ADDRESS_TRACE; + + if (this == &ipaddr) return *this; // protect against assignment from itself + + (IpAddress &)*this = ipaddr; // use ancestor assignment for ipaddr value + if (ip_version == version_ipv4) + smival.value.string.len = UDPIPLEN; + else + smival.value.string.len = UDPIP6LEN; + + set_port(0); // copy to port value + addr_changed = true; + return *this; +} + +UdpAddress& UdpAddress::operator=(const char *inaddr) +{ + ADDRESS_TRACE; + + valid_flag = parse_address(inaddr); + addr_changed = true; + return *this; +} + +//-----[ IP Address parse Address ]--------------------------------- +bool UdpAddress::parse_address(const char *inaddr) +{ + ADDRESS_TRACE; + + addr_changed = true; + + char buffer[MAX_FRIENDLY_NAME]; + + unsigned short port = 0; + if (inaddr && (strlen(inaddr)< MAX_FRIENDLY_NAME)) + { + strcpy(buffer, inaddr); + trim_white_space(buffer); + } + else + { + valid_flag = false; + return FALSE; + } + // look for port info @ the end of the string + // port can be delineated by a ':' or a '/' + // if neither are present then just treat it + // like a normal IpAddress + + int remove_brackets = FALSE; + int found = FALSE; + int pos = strlen(buffer) - 1; + int do_loop = TRUE; + int another_colon_found = FALSE; + + if (pos < 0) + { + valid_flag = false; + return FALSE; + } + + // search from the end, to find the start of the port + // [ipv4]:port [ipv4]/port ipv4/port ipv4:port [ipv4] ipv4 + // [ipv6]:port [ipv6]/port ipv6/port [ipv6] ipv6 + while (do_loop) + { + if (buffer[pos] == '/') + { + found = TRUE; + sep='/'; + if (buffer[pos -1] == ']') + remove_brackets = TRUE; + break; + } + if (buffer[pos] == ':') + { + if ((pos > 1) && (buffer[pos -1] == ']')) + { + found = TRUE; + remove_brackets = TRUE; + sep=':'; + break; + } + + for (int i=pos - 1; i >= 0 ; i--) + if (buffer[i] == ':') + { + another_colon_found = TRUE; + } + if (!another_colon_found) + { + sep=':'; + found = TRUE; + break; + } + } + if (buffer[pos] == ']') + { + // we found a ] without following a port, so increase pos + ++pos; + remove_brackets = TRUE; + break; + } + pos--; + do_loop = ((found == FALSE) && (pos >= 0) && + (another_colon_found == FALSE)); + } + + if (remove_brackets) + { + buffer[pos-1] = 0; + buffer[0] = ' '; + } + + bool result; + + if (found) + { + buffer[pos] = 0; + port = atoi(&buffer[pos+1]); + result = IpAddress::parse_address(buffer); + } + else + { + port = 0; + result = IpAddress::parse_address (buffer); + } + + if (ip_version == version_ipv4) + smival.value.string.len = UDPIPLEN; + else + smival.value.string.len = UDPIP6LEN; + + set_port(port); + return result; +} + + +//--------[ set the port number ]--------------------------------------- +void UdpAddress::set_port(const unsigned short p) +{ + ADDRESS_TRACE; + + unsigned short *port_nbo; + if (ip_version == version_ipv4) + port_nbo = (unsigned short*)(address_buffer + IPLEN); + else + port_nbo = (unsigned short*)(address_buffer + IP6LEN); + *port_nbo = htons(p); + addr_changed = true; +} + +//---------[ get the port number ]-------------------------------------- +unsigned short UdpAddress::get_port() const +{ + ADDRESS_TRACE; + + if (valid_flag) + { + unsigned short *port_nbo; + if (ip_version == version_ipv4) + port_nbo = (unsigned short*)(address_buffer + IPLEN); + else + port_nbo = (unsigned short*)(address_buffer + IP6LEN); + return ntohs(*port_nbo); + } + return 0;// don't use uninitialized memory +} + +//----[ UDP address format output ]------------------------------------ +void UdpAddress::format_output() const +{ + ADDRESS_TRACE; + + IpAddress::format_output(); // allow ancestors to format their buffers + + // if valid format else null it + if (valid_flag) + { + if (ip_version == version_ipv4) + sprintf((char *) output_buffer,"%s%c%d", + IpAddress::get_printable(), + '/',//TODO:look for problems in old code and change to "sep" + get_port() ); + else + sprintf((char *) output_buffer,"[%s]%c%d", + IpAddress::get_printable(), + '/',//TODO:look for problems in old code and change to "sep" + get_port() ); + } + else + *(char*)output_buffer = 0; + ((UdpAddress *)this)->addr_changed = false; +} + +/** + * Map a IPv4 UDP address to a IPv6 UDP address. + * + * @return - TRUE if no error occured. + */ +int UdpAddress::map_to_ipv6() +{ + ADDRESS_TRACE; + + /* Save the port, as IpAddress::map_to_ipv6 destroys it */ + unsigned short old_port = get_port(); + + /* Map IpAddress */ + if (!IpAddress::map_to_ipv6()) + return FALSE; + + set_port(old_port); + smival.value.string.len = UDPIP6LEN; + ip_version = version_ipv6; + + addr_changed = true; + return TRUE; +} + + +#ifdef _IPX_ADDRESS +//======================================================================= +//=========== IPX Address Implementation ================================ +//======================================================================= + +//----------[ constructor no args ]-------------------------------------- +IpxAddress::IpxAddress() : Address() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXLEN; + smival.value.string.ptr = address_buffer; + + separator = 0; + valid_flag = false; + addr_changed = true; +} + + +//----------[ constructor with a string arg ]--------------------------- +IpxAddress::IpxAddress(const char *inaddr):Address() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXLEN; + smival.value.string.ptr = address_buffer; + + separator = 0; + valid_flag = parse_address(inaddr); + addr_changed = true; +} + + +//-----[ IPX Address copy constructor ]---------------------------------- +IpxAddress::IpxAddress(const IpxAddress &ipxaddr) +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXLEN; + smival.value.string.ptr = address_buffer; + + separator = 0; + valid_flag = ipxaddr.valid_flag; + if (valid_flag) + MEMCPY(address_buffer, ipxaddr.address_buffer, IPXLEN); + addr_changed = true; +} + + +//----[ construct an IpxAddress from a GenAddress ]--------------------------- +IpxAddress::IpxAddress(const GenAddress &genaddr) +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = genaddr.valid(); + // allow use of an ipx or ipxsock address + if (valid_flag) + { + if ((genaddr.get_type() == type_ipx) ) + { + *this = genaddr.cast_ipxaddress(); // copy in the Ipx address data + } + else if ((genaddr.get_type() == type_ipxsock) ) + { + *this = genaddr.cast_ipxsockaddress(); // copy in the Ipx address data + } + else + valid_flag = false; + } +} + +//-----[ IPX Address general = operator ]------------------------------- +SnmpSyntax& IpxAddress::operator=(const SnmpSyntax &val) +{ + // protect against assignment from itself + if (this == &val) return *this; + + valid_flag = false; // will set to TRUE if really valid + if (val.valid()){ + switch (val.get_syntax()){ + case sNMP_SYNTAX_OCTETS: + if (((IpxAddress &)val).smival.value.string.len == IPXLEN){ + MEMCPY(address_buffer, ((IpxAddress &)val).smival.value.string.ptr, IPXLEN); + valid_flag = true; + } + break; + } + } + addr_changed = true; + return *this; +} + +//--------[ assignment to another IpAddress object overloaded ]---------- +IpxAddress& IpxAddress::operator=(const IpxAddress &ipxaddress) +{ + if (this == &ipxaddress) return *this;// protect against assignment from self + + valid_flag = ipxaddress.valid_flag; + if (valid_flag) + MEMCPY(address_buffer, ipxaddress.address_buffer, IPXLEN); + addr_changed = true; + return *this; +} + + +//-----[ IPX Address parse Address ]----------------------------------- +// Convert a string to a ten byte ipx address +// On success sets validity TRUE or FALSE +// +// IPX address format +// +// NETWORK ID| MAC ADDRESS +// 01 02 03 04|05 06 07 08 09 10 +// XX XX XX XX|XX XX XX XX XX XX +// +// Valid input format +// +// XXXXXXXX.XXXXXXXXXXXX +// Total length must be 21 +// Must have a separator in it +// First string length must be 8 +// Second string length must be 12 +// Each char must take on value 0-F +// +// +// Input formats recognized +// +// XXXXXXXX.XXXXXXXXXXXX +// XXXXXXXX:XXXXXXXXXXXX +// XXXXXXXX-XXXXXXXXXXXX +// XXXXXXXX.XXXXXX-XXXXXX +// XXXXXXXX:XXXXXX-XXXXXX +// XXXXXXXX-XXXXXX-XXXXXX +bool IpxAddress::parse_address(const char *inaddr) +{ + char unsigned *str1,*str2; + char temp[30]; // don't destroy original + char unsigned *tmp; + size_t z, tmplen; + + // save the orginal source + if (!inaddr || (strlen(inaddr) >(sizeof(temp)-1))) return FALSE; + strcpy(temp, inaddr); + trim_white_space(temp); + tmplen = strlen(temp); + + // bad total length check + // 123456789012345678901 + // XXXXXXXX-XXXXXXXXXXXX 21 len + // + // XXXXXXXX-XXXXXX-XXXXXX 22 len + // need at least 21 chars and no more than 22 + if ((tmplen <21) || (tmplen >22)) + return FALSE; + + // convert the string to all lower case + // this allows hex values to be in upper or lower + for (z=0;z< tmplen;z++) + temp[z] = tolower(temp[z]); + + // check for separated nodeid + // if found remove it + if (temp[15] == '-') + { + for(z=16;z= '0') && (*tmp <= '9'))|| // good 0-9 + ((*tmp >= 'a') && (*tmp <= 'f'))) // or a-f + tmp++; + else + return FALSE; + + // check out the MAC address + tmp = str2; + while(*tmp != 0) + if (((*tmp >= '0') && (*tmp <= '9'))|| // good 0-9 + ((*tmp >= 'a') && (*tmp <= 'f'))) // or a-f + tmp++; + else + return FALSE; + + // convert to target string + tmp = str1; + while (*tmp != 0) + { + if ((*tmp >= '0') && (*tmp <= '9')) + *tmp = *tmp - (char unsigned )'0'; + else + *tmp = *tmp - (char unsigned) 'a' + (char unsigned) 10; + tmp++; + } + + // network id portion + address_buffer[0] = (str1[0]*16) + str1[1]; + address_buffer[1] = (str1[2]*16) + str1[3]; + address_buffer[2] = (str1[4]*16) + str1[5]; + address_buffer[3] = (str1[6]*16) + str1[7]; + + tmp = str2; + while (*tmp != 0) + { + if ((*tmp >= '0') && (*tmp <= '9')) + *tmp = *tmp - (char unsigned) '0'; + else + *tmp = *tmp - (char unsigned) 'a'+ (char unsigned) 10; + tmp++; + } + + address_buffer[4] = (str2[0]*16) + str2[1]; + address_buffer[5] = (str2[2]*16) + str2[3]; + address_buffer[6] = (str2[4]*16) + str2[5]; + address_buffer[7] = (str2[6]*16) + str2[7]; + address_buffer[8] = (str2[8]*16) + str2[9]; + address_buffer[9] = (str2[10]*16) + str2[11]; + + return TRUE; +} + +//----[ IPX address format output ]------------------------------------- +void IpxAddress::format_output() const +{ + if (valid_flag) + sprintf((char *) output_buffer, + "%02x%02x%02x%02x%c%02x%02x%02x%02x%02x%02x", + address_buffer[0],address_buffer[1], + address_buffer[2],address_buffer[3],'-', + address_buffer[4],address_buffer[5], + address_buffer[6],address_buffer[7], + address_buffer[8],address_buffer[9]); + else + *(char*)output_buffer = 0; + ((IpxAddress *)this)->addr_changed = false; +} + + +#ifdef _MAC_ADDRESS +// get the host id portion of an ipx address +int IpxAddress::get_hostid(MacAddress& mac) +{ + if (valid_flag) + { + char buffer[18]; + sprintf(buffer,"%02x:%02x:%02x:%02x:%02x:%02x", address_buffer[4], + address_buffer[5], address_buffer[6], address_buffer[7], + address_buffer[8], address_buffer[9]); + MacAddress temp(buffer); + mac = temp; + if (mac.valid()) + return TRUE; + } + return FALSE; +} +#endif // function that needs _MAC_ADDRESS + +//======================================================================== +//======== IpxSockAddress Implementation ================================= +//======================================================================== + +//----------[ constructor no args ]-------------------------------------- +IpxSockAddress::IpxSockAddress() : IpxAddress() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXSOCKLEN; + smival.value.string.ptr = address_buffer; + + set_socket(0); + addr_changed = true; +} + +//-----------[ construct an IpxSockAddress with another IpxSockAddress]---- +IpxSockAddress::IpxSockAddress(const IpxSockAddress &ipxaddr) + : IpxAddress(ipxaddr) +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXSOCKLEN; + smival.value.string.ptr = address_buffer; + + // copy the socket value + set_socket(ipxaddr.get_socket()); + addr_changed = true; +} + + +//---------------[ construct a IpxSockAddress from a string ]-------------- +IpxSockAddress::IpxSockAddress(const char *inaddr):IpxAddress() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXSOCKLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = parse_address(inaddr); + addr_changed = true; +} + + +//---------------[ construct a IpxSockAddress from a GenAddress ]---------- +IpxSockAddress::IpxSockAddress(const GenAddress &genaddr):IpxAddress() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXSOCKLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = false; + unsigned short socketid = 0; + // allow use of an ipx or ipxsock address + if ((genaddr.get_type() == type_ipx) ) + { + valid_flag = genaddr.valid(); + if (valid_flag) + { + // copy in the Ipx address data + IpxAddress temp_ipx((const char *) genaddr); + *this = temp_ipx; + } + } + else if ((genaddr.get_type() == type_ipxsock) ) + { + valid_flag = genaddr.valid(); + if (valid_flag) + { + // copy in the Ipx address data + IpxSockAddress temp_ipxsock((const char *) genaddr); + *this = temp_ipxsock; + // socketid info since are making an IpxSockAddress + socketid = temp_ipxsock.get_socket(); + } + } + set_socket(socketid); + addr_changed = true; +} + +//------------[ construct an IpxSockAddress from a IpxAddress ]-------------- +IpxSockAddress::IpxSockAddress(const IpxAddress &ipxaddr):IpxAddress(ipxaddr) +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = IPXSOCKLEN; + smival.value.string.ptr = address_buffer; + + set_socket(0); + addr_changed = true; +} + +// copy an instance of this Value +SnmpSyntax& IpxSockAddress::operator=(const SnmpSyntax &val) +{ + if (this == &val) return *this; // protect against assignment from itself + + valid_flag = false; // will set to TRUE if really valid + if (val.valid()){ + switch (val.get_syntax()){ + case sNMP_SYNTAX_OCTETS: + { + // See if it is of the Ipx address family + // This handles IpxSockAddress == IpxAddress + IpxSockAddress temp_ipx(val.get_printable()); + if (temp_ipx.valid()){ + *this = temp_ipx; // ipxsock = ipxsock + } + // See if it is an OctetStr of appropriate length + else if (((IpxSockAddress &)val).smival.value.string.len == IPXSOCKLEN){ + MEMCPY(address_buffer, + ((IpxSockAddress &)val).smival.value.string.ptr, + IPXSOCKLEN); + valid_flag = true; + } + } + break; + } + } + addr_changed = true; + return *this; +} + +// assignment to another IpAddress object overloaded +IpxSockAddress& IpxSockAddress::operator=(const IpxSockAddress &ipxaddr) +{ + if (this == &ipxaddr) return *this; // protect against assignment from itself + + (IpxAddress&)*this = ipxaddr; // use ancestor assignment for ipx addr + set_socket(ipxaddr.get_socket()); // copy socket value + addr_changed = true; + return *this; +} + +//----[ IPX address format output ]------------------------------------- +void IpxSockAddress::format_output() const +{ + IpxAddress::format_output(); // allow ancestors to format their buffers + + if (valid_flag) + sprintf((char *) output_buffer,"%s/%d", + IpxAddress::get_printable(), get_socket()); + else + *(char*)output_buffer = 0; + ((IpxSockAddress *)this)->addr_changed = false; +} + +//-----[ IP Address parse Address ]--------------------------------- +bool IpxSockAddress::parse_address(const char *inaddr) +{ + char buffer[MAX_FRIENDLY_NAME]; + unsigned short socketid=0; + + if (inaddr && (strlen(inaddr)< MAX_FRIENDLY_NAME)) + strcpy(buffer, inaddr); + else + { + valid_flag = false; + return FALSE; + } + // look for port info @ the end of the string + // port can be delineated by a ':' or a '/' + // if neither are present then just treat it + // like a normal IpAddress + char *tmp; + tmp = strstr(buffer,"/"); + + if (tmp != NULL) + { + *tmp=0; // new null terminator + tmp++; + socketid = atoi(tmp); + } + set_socket(socketid); + return IpxAddress::parse_address(buffer); +} + + + +//-------------[ set the socket number ]---------------------------------- +void IpxSockAddress::set_socket(const unsigned short s) +{ + unsigned short sock_nbo = htons(s); + MEMCPY(&address_buffer[IPXLEN], &sock_nbo, 2); + addr_changed = true; +} + +//--------------[ get the socket number ]--------------------------------- +unsigned short IpxSockAddress::get_socket() const +{ + if (valid_flag) + { + unsigned short sock_nbo; + MEMCPY(&sock_nbo, &address_buffer[IPXLEN], 2); + return ntohs(sock_nbo); + } + return 0; // don't use uninitialized memory +} +#endif // _IPX_ADDRESS + +#ifdef _MAC_ADDRESS +//======================================================================== +//======== MACAddress Implementation ===================================== +//======================================================================== + +//--------[ constructor, no arguments ]----------------------------------- +MacAddress::MacAddress() : Address() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = MACLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = false; + addr_changed = true; +} + +//-----[ MAC Address copy constructor ]--------------------------------- +MacAddress::MacAddress(const MacAddress &macaddr) +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = MACLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = macaddr.valid_flag; + if (valid_flag) + MEMCPY(address_buffer, macaddr.address_buffer, MACLEN); + addr_changed = true; +} + +//---------[ constructor with a string argument ]------------------------- +MacAddress::MacAddress(const char *inaddr):Address() +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = MACLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = parse_address(inaddr); + addr_changed = true; +} + +//-----[ construct a MacAddress from a GenAddress ]------------------------ +MacAddress::MacAddress(const GenAddress &genaddr) +{ + // always initialize SMI info + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = MACLEN; + smival.value.string.ptr = address_buffer; + + valid_flag = false; + // allow use of mac address + if (genaddr.get_type() == type_mac) + { + valid_flag = genaddr.valid(); + if (valid_flag) + { + // copy in the Mac address data + *this = genaddr.cast_macaddress(); + } + } + addr_changed = true; +} + +//------[ assignment to another ipaddress object overloaded ]-------------- +MacAddress& MacAddress::operator=(const MacAddress &macaddress) +{ + if (this == &macaddress) return *this;// protect against assignment from self + + valid_flag = macaddress.valid_flag; + if (valid_flag) + MEMCPY(address_buffer, macaddress.address_buffer, MACLEN); + addr_changed = true; + return *this; +} + + + +//-----[ MAC Address general = operator ]--------------------------------- +SnmpSyntax& MacAddress::operator=(const SnmpSyntax &val) +{ + if (this == &val) return *this; // protect against assignment from itself + + valid_flag = false; // will set to TRUE if really valid + if (val.valid()) + { + switch (val.get_syntax()) + { + case sNMP_SYNTAX_OCTETS: + if (((MacAddress &)val).smival.value.string.len == MACLEN) + { + MEMCPY(address_buffer, ((MacAddress &)val).smival.value.string.ptr, + MACLEN); + valid_flag = true; + } + break; + } + } + addr_changed = true; + return *this; +} + +//-----[ MAC Address parse Address ]-------------------------------------- +// Convert a string to a six byte MAC address +// On success sets validity TRUE or FALSE +// +// MAC address format +// +// MAC ADDRESS +// 01 02 03 04 05 06 +// XX:XX:XX:XX:XX:XX +// Valid input format +// +// XXXXXXXXXXXX +// Total length must be 17 +// Each char must take on value 0-F +// +// +bool MacAddress::parse_address(const char *inaddr) +{ + char temp[30]; // don't destroy original + char unsigned *tmp; + size_t z; + + + // save the orginal source + if (!inaddr || (strlen(inaddr) > 30)) return FALSE; + strcpy(temp, inaddr); + trim_white_space(temp); + + // bad total length check + if (strlen(temp) != 17) + return FALSE; + + // check for colons + if ((temp[2] != ':')||(temp[5] != ':')||(temp[8]!=':')||(temp[11]!=':')||(temp[14] !=':')) + return FALSE; + + // strip off the colons + tmp = (unsigned char *) temp; + int i = 0; + while (*tmp != 0) + { + if (*tmp != ':') + { + temp[i] = *tmp; + i++; + } + tmp++; + } + temp[i] = 0; + + // convert to lower + for(z=0;z= '0') && (*tmp <= '9'))|| // good 0-9 + ((*tmp >= 'a') && (*tmp <= 'f'))) // or a-f + tmp++; + else + return FALSE; + + // convert to target string + tmp = (unsigned char *) temp; + while (*tmp != 0) + { + if ((*tmp >= '0') && (*tmp <= '9')) + *tmp = *tmp - (char unsigned )'0'; + else + *tmp = *tmp - (char unsigned) 'a' + (char unsigned) 10; + tmp++; + } + + address_buffer[0] = (temp[0]*16) + temp[1]; + address_buffer[1] = (temp[2]*16) + temp[3]; + address_buffer[2] = (temp[4]*16) + temp[5]; + address_buffer[3] = (temp[6]*16) + temp[7]; + address_buffer[4] = (temp[8]*16) + temp[9]; + address_buffer[5] = (temp[10]*16) + temp[11]; + + return TRUE; +} + +//----[ MAC address format output ]--------------------------------- +void MacAddress::format_output() const +{ + if (valid_flag) + sprintf((char*)output_buffer,"%02x:%02x:%02x:%02x:%02x:%02x", + address_buffer[0], address_buffer[1], address_buffer[2], + address_buffer[3], address_buffer[4], address_buffer[5]); + else + *(char*)output_buffer = 0; + ((MacAddress*)this)->addr_changed = false; +} + +unsigned int MacAddress::hashFunction() const +{ + return ((((address_buffer[0] << 8) + address_buffer[1]) * HASH0) + + (((address_buffer[2] << 8) + address_buffer[3]) * HASH1) + + (((address_buffer[4] << 8) + address_buffer[5]) * HASH2)); +} +#endif // _MAC_ADDRESS + +//======================================================================== +//========== Generic Address Implementation ============================== +//======================================================================== + +//-----------[ constructor, no arguments ]-------------------------------- +GenAddress::GenAddress() : Address() +{ + ADDRESS_TRACE; + + // initialize SMI info + // BOK: this is generally not used for GenAddress, + // but we need this to be a replica of the real address' + // smival info so that operator=SnmpSyntax will work. + smival.syntax = sNMP_SYNTAX_NULL; // to be overridden + smival.value.string.len = 0; // to be overridden + smival.value.string.ptr = address_buffer; // constant + + valid_flag = false; + address = 0; + output_buffer[0] = 0; +} + +//-----------[ constructor with a string argument ]---------------------- +GenAddress::GenAddress(const char *addr, + const Address::addr_type use_type) +{ + ADDRESS_TRACE; + + // initialize SMI info + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + smival.syntax = sNMP_SYNTAX_NULL; // to be overridden + smival.value.string.len = 0; // to be overridden + smival.value.string.ptr = address_buffer; // constant + + address = 0; + parse_address(addr, use_type); + + // Copy real address smival info into GenAddr smival + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + if (valid_flag ) { + smival.syntax = ((GenAddress *)address)->smival.syntax; + smival.value.string.len = + ((GenAddress *)address)->smival.value.string.len; + memcpy(smival.value.string.ptr, + ((GenAddress *)address)->smival.value.string.ptr, + (size_t)smival.value.string.len); + } + output_buffer[0] = 0; +} + +//-----------[ constructor with an Address argument ]-------------------- +GenAddress::GenAddress(const Address &addr) +{ + ADDRESS_TRACE; + + output_buffer[0] = 0; + // initialize SMI info + // BOK: this is generally not used for GenAddress, + // but we need this to be a replica of the real address' + // smival info so that operator=SnmpSyntax will work. + smival.syntax = sNMP_SYNTAX_NULL; // to be overridden + smival.value.string.len = 0; // to be overridden + smival.value.string.ptr = address_buffer; // constant + + valid_flag = false; + // make sure that the object is valid + if (!addr.valid()) { + address = 0; + return; + } + + // addr can be a GenAddress object and calling clone() on that is bad... + if (addr.is_gen_address()) + address = (Address *)(((const GenAddress&)addr).address->clone()); + else + address = (Address*)addr.clone(); + + if (address) + valid_flag = address->valid(); + + // Copy real address smival info into GenAddr smival + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + if (valid_flag ) + { + smival.syntax = address->get_syntax(); + smival.value.string.len = ((GenAddress *)address)->smival.value.string.len; + memcpy(smival.value.string.ptr, + ((GenAddress *)address)->smival.value.string.ptr, + (size_t)smival.value.string.len); + } +} + +//-----------------[ constructor with another GenAddress object ]------------- +GenAddress::GenAddress(const GenAddress &addr) +{ + ADDRESS_TRACE; + + output_buffer[0] = 0; + // initialize SMI info + // BOK: this is generally not used for GenAddress, + // but we need this to be a replica of the real address' + // smival info so that operator=SnmpSyntax will work. + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.len = 0; + smival.value.string.ptr = address_buffer; + + valid_flag = false; + // make sure that the object is valid + if (!addr.valid_flag) + { + address = 0; + return; + } + + address = (Address *)addr.address->clone(); + if (address) + valid_flag = address->valid(); + + // Copy real address smival info into GenAddr smival + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + if (valid_flag ) + { + smival.syntax = ((GenAddress *)address)->smival.syntax; + smival.value.string.len = ((GenAddress *)address)->smival.value.string.len; + memcpy(smival.value.string.ptr, + ((GenAddress *)address)->smival.value.string.ptr, + (size_t)smival.value.string.len); + } +} + +//------[ assignment GenAddress = GenAddress ]----------------------------- +GenAddress& GenAddress::operator=(const GenAddress &addr) +{ + ADDRESS_TRACE; + + if (this == &addr) return *this; // protect against assignment from itself + + valid_flag = false; + if (address) + { + delete address; + address = 0; + } + if (addr.address) + address = (Address *)(addr.address->clone()); + if (address) + valid_flag = address->valid(); + + // Copy real address smival info into GenAddr smival + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + if (valid_flag ) + { + smival.syntax = ((GenAddress *)address)->smival.syntax; + smival.value.string.len = ((GenAddress *)address)->smival.value.string.len; + memcpy(smival.value.string.ptr, + ((GenAddress *)address)->smival.value.string.ptr, + (size_t)smival.value.string.len); + } + + return *this; +} + +//------[ assignment GenAddress = Address ]-------------------------------- +GenAddress& GenAddress::operator=(const Address &addr) +{ + ADDRESS_TRACE; + + if (this == &addr) return *this; // protect against assignment from itself + + valid_flag = false; + if (address) + { + delete address; + address = 0; + } + + // addr can be a GenAddress object and calling clone() on that is bad... + if (addr.is_gen_address()) + address = (Address *)(((const GenAddress&)addr).address->clone()); + else + address = (Address*)addr.clone(); + + if (address) + valid_flag = address->valid(); + + // Copy real address smival info into GenAddr smival + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + if (valid_flag ) + { + smival.syntax = ((GenAddress *)address)->smival.syntax; + smival.value.string.len = ((GenAddress *)address)->smival.value.string.len; + memcpy(smival.value.string.ptr, + ((GenAddress *)address)->smival.value.string.ptr, + (size_t)smival.value.string.len); + } + + return *this; +} + + +//------[ assignment GenAddress = any SnmpSyntax ]----------------------- +SnmpSyntax& GenAddress::operator=(const SnmpSyntax &val) +{ + ADDRESS_TRACE; + + if (this == &val) return *this; // protect against assignment from itself + + valid_flag = false; // will get set to TRUE if really valid + if (address) + { + delete address; + address = 0; + } + + if (val.valid()) + { + switch (val.get_syntax() ) + { + //-----[ ip address case ]------------- + // BOK: this case shouldn't be needed since there is an explicit + // GenAddr=Address assignment that will override this assignment. + // Left here for posterity. + case sNMP_SYNTAX_IPADDR: + { + address = new IpAddress(val.get_printable()); + if (address) + valid_flag = address->valid(); + } + break; + + //-----[ udp address case ]------------ + //-----[ ipx address case ]------------ + //-----[ mac address case ]------------ + // BOK: This is here only to support GenAddr = primitive OctetStr. + // The explicit GenAddr=Address assignment will handle the cases + // GenAddr = [UdpAdd|IpxAddr|IpxSock|MacAddr]. + // Note, using the heuristic of octet str len to determine type of + // address to create is not accurate when address lengths are equal + // (e.g., UDPIPLEN == MACLEN). It gets worse if we add AppleTalk or + // OSI which use variable length addresses! + case sNMP_SYNTAX_OCTETS: + { + unsigned long val_len; + val_len = ((GenAddress &)val).smival.value.string.len; + + if ((val_len == UDPIPLEN) || (val_len == UDPIP6LEN)) + address = new UdpAddress; + else if ((val_len == IPLEN) || (val_len == IPLEN)) + address = new IpAddress; +#ifdef _IPX_ADDRESS + else if (val_len == IPXLEN) + address = new IpxAddress; + else if (val_len == IPXSOCKLEN) + address = new IpxSockAddress; +#endif +#ifdef _MAC_ADDRESS + else if (val_len == MACLEN) + address = new MacAddress; +#endif + + if (address) + { + *address = val; + valid_flag = address->valid(); + } + } + break; + } // end switch + } + + // Copy real address smival info into GenAddr smival + // BOK: smival is generally not used for GenAddress, but + // we need this to be a replica of the real address' + // smival info so that ::operator=SnmpSyntax + // will work. + if (valid_flag ) + { + smival.syntax = ((GenAddress *)address)->smival.syntax; + smival.value.string.len = ((GenAddress *)address)->smival.value.string.len; + memcpy(smival.value.string.ptr, + ((GenAddress *)address)->smival.value.string.ptr, + (size_t)smival.value.string.len); + } + + return *this; +} + + +// redefined parse address for macs +bool GenAddress::parse_address(const char *addr, + const Address::addr_type use_type) +{ + ADDRESS_TRACE; + + if (address) delete address; + + // try to create each of the addresses until the correct one + // is found + + //BOK: Need to try IPX Sock and IPX before UDP since on Win32, + // gethostbyname() seems to think the ipx network number + // portion is a valid ipaddress string... stupid WinSOCK! + +#ifdef _IPX_ADDRESS + if ((use_type == Address::type_invalid) || + (use_type == Address::type_ipxsock)) + { + // ipxsock address + address = new IpxSockAddress(addr); + valid_flag = address->valid(); + if (valid_flag && ((IpxSockAddress*)address)->get_socket()) + return TRUE; // ok its an ipxsock address + + delete address; // otherwise delete it and try another + } + + if ((use_type == Address::type_invalid) || + (use_type == Address::type_ipx)) + { + // ipx address + address = new IpxAddress(addr); + valid_flag = address->valid(); + if (valid_flag) + return TRUE; // ok its an ipx address + + delete address; // otherwise delete it and try another + } +#endif // _IPX_ADDRESS + + //TM: Must try the derived classes first...one pitfall of the + //following solution is if someone creates with a port/socket of 0 the + //class will get demoted to ip/ipx. The only proper way to do this is + //to parse the strings ourselves. + + if ((use_type == Address::type_invalid) || + (use_type == Address::type_udp)) + { + // udp address + address = new UdpAddress(addr); + valid_flag = address->valid(); + if (valid_flag && ((UdpAddress*)address)->get_port()) + return TRUE; // ok its a udp address + + delete address; // otherwise delete it and try another + } + + if ((use_type == Address::type_invalid) || + (use_type == Address::type_ip)) + { + // ip address + address = new IpAddress(addr); + valid_flag = address->valid(); + if (valid_flag) + return TRUE; // ok its an ip address + + delete address; // otherwise delete it and try another + } + +#ifdef _MAC_ADDRESS + if ((use_type == Address::type_invalid) || + (use_type == Address::type_mac)) + { + // mac address + address = new MacAddress(addr); + valid_flag = address->valid(); + if (valid_flag) + return TRUE; // ok, its a mac + + delete address; // otherwise its invalid + } +#endif // _MAC_ADDRESS + + address = 0; + return FALSE; +} diff --git a/backend/camembert/libkmsnmp/address.h b/backend/camembert/libkmsnmp/address.h new file mode 100644 index 0000000..b1a1ddf --- /dev/null +++ b/backend/camembert/libkmsnmp/address.h @@ -0,0 +1,1081 @@ +/*_############################################################################ + _## + _## address.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/* + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ A D D R E S S . H + + ADDRESS CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Address class definition. Encapsulates various network + addresses into easy to use, safe and portable classes. + +=====================================================================*/ +// $Id: address.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _ADDRESS +#define _ADDRESS + + +//----[ includes ]----------------------------------------------------- +#include +#include + +#include "config_snmp_pp.h" // for _IPX_ADDRESS and _MAC_ADDRESS +#include "smival.h" +#include "collect.h" + +// include sockets header files +// for Windows16 and Windows32 include Winsock +// otherwise assume UNIX +#ifdef __unix +// The /**/ stuff below is meant to fool MS C++ into skipping these +// files when creating its makefile. 8-Dec-95 TM +#include /**/ +#include /**/ +#include /**/ +#include /**/ +#include /**/ +#if defined _AIX +#include // This is needed for FD_SET, bzero +#endif + +#if !defined __CYGWIN32__ && !defined __hpux && !defined linux && !defined _AIX +extern int h_errno; // defined in WinSock header, but not for UX?! +#endif +#endif // __unix + + +#ifdef WIN32 +#ifndef __unix // __unix overrides WIN32 if both options are present +#include +#endif +#endif + +//----[ macros ]------------------------------------------------------- +#define BUFSIZE 40 // worst case of address lens +#define OUTBUFF 80 // worst case of output lens + +#define IPLEN 4 +#define IP6LEN 16 +#define UDPIPLEN 6 +#define UDPIP6LEN 18 +#define IPXLEN 10 +#define IPXSOCKLEN 12 +#define MACLEN 6 +#define MAX_FRIENDLY_NAME 80 +#define HASH0 19 +#define HASH1 13 +#define HASH2 7 + +//---[ forward declarations ]----------------------------------------- +class GenAddress; + +//----[ Address class ]----------------------------------------------- + +/** + * Base class of all Address classes. + */ +class DLLOPT Address : public SnmpSyntax +{ + friend class GenAddress; + + public: + //----[ enumerated types for address types ]--------------------------- + /** + * Type returned by Address::get_type(). + */ + enum addr_type + { + type_ip, ///< IpAddress (IPv4 or IPv6) + type_ipx, ///< IpxAddress + type_udp, ///< UdpAddress (IPv4 or IPv6) + type_ipxsock, ///< IpxSockAddress + type_mac, ///< MacAddress + type_invalid ///< Used by GenAddress::get_type() if address is not valid + }; + + /** + * Type returned by IpAddress::get_ip_version() and + * UdpAddress::get_ip_version(). + */ + enum version_type + { + version_ipv4, ///< IPv4 + version_ipv6 ///< IPv6 + }; + + /** + * Default constructor, clears the buffer and sets valid flag to false. + */ + Address(); + + /** + * Allow destruction of derived classes. + */ + virtual ~Address() {}; + + /// overloaded equivlence operator, are two addresses equal? + DLLOPT friend int operator==(const Address &lhs,const Address &rhs); + + /// overloaded not equivlence operator, are two addresses not equal? + DLLOPT friend int operator!=(const Address &lhs, const Address &rhs) + { return !(lhs == rhs); }; + + /// overloaded > operator, is a1 > a2 + DLLOPT friend int operator>(const Address &lhs,const Address &rhs); + + /// overloaded >= operator, is a1 >= a2 + DLLOPT friend int operator>=(const Address &lhs,const Address &rhs) + { if ((lhs > rhs) || (lhs == rhs)) return true; return false; }; + + /// overloaded < operator, is a1 < a2 + DLLOPT friend int operator<(const Address &lhs,const Address &rhs); + + /// overloaded <= operator, is a1 <= a2 + DLLOPT friend int operator<=(const Address &lhs, const Address &rhs) + { if ((lhs < rhs) || (lhs == rhs)) return true; return false; }; + + /// equivlence operator overloaded, are an address and a string equal? + DLLOPT friend int operator==(const Address &lhs,const char *rhs); + + /// overloaded not equivlence operator, are an address and string not equal? + DLLOPT friend int operator!=(const Address &lhs,const char *rhs) + { return !(lhs == rhs); }; + + /// overloaded < , is an address greater than a string? + DLLOPT friend int operator>(const Address &lhs,const char *rhs); + + /// overloaded >=, is an address greater than or equal to a string? + DLLOPT friend int operator>=(const Address &lhs,const char *rhs); + + /// overloaded < , is an address less than a string? + DLLOPT friend int operator<(const Address &lhs,const char *rhs); + + /// overloaded <=, is an address less than or equal to a string? + DLLOPT friend int operator<=(const Address &lhs,const char *rhs); + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const = 0; + + /** + * Return if the object contains a valid address. + * + * @return true if the object is valid + */ + virtual bool valid() const { return valid_flag; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const = 0; + + /** + * Access as an array (read and write). + * @note Only pass in values between 0 and get_length(). + * + * @param position - pos to return + * @return reference to the byte at the given position + */ + unsigned char& operator[](const int position) + { addr_changed = true; + return (position < BUFSIZE) ? address_buffer[position] + : address_buffer[0]; }; + + /** + * Access as an array (read only). + * @note Only pass in values between 0 and get_length(). + * + * @param position - pos to return + * @return the byte at the given position + */ + unsigned char operator[](const int position) const + { return (position < BUFSIZE) ? address_buffer[ position] : 0; } + + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() = 0; + + /** + * Get the type of the address. + * @see Address::addr_type + */ + virtual addr_type get_type() const = 0; + + /** + * Overloaded assignment operator. + */ + virtual SnmpSyntax& operator=(const SnmpSyntax &val) = 0; + + // return a hash key + virtual unsigned int hashFunction() const { return 0;}; + + protected: + bool addr_changed; + bool valid_flag; + unsigned char address_buffer[BUFSIZE]; // internal representation + + // parse the address string + // redefined for each specific address subclass + virtual bool parse_address(const char * inaddr) = 0; + + // format the output + // redefined for each specific address subclass + virtual void format_output() const = 0; + + /** + * Trim of whitespaces at the start and the end of the string. + * + * @param ptr - string to trim + */ + void trim_white_space(char * ptr); + + /** + * Is this a GenAddress object. + */ + virtual bool is_gen_address() const { return false; }; +}; + + +//----------------------------------------------------------------------- +//---------[ IP Address Class ]------------------------------------------ +//----------------------------------------------------------------------- +class DLLOPT IpAddress : public Address +{ + public: + /** + * Construct an empty invalid IP address. + */ + IpAddress(); + + /** + * Construct an IP address from a string. + * + * The following formats can be used: + * - hostname with or without domain ("www.agentpp.com", "printsrv") + * - Numerical IPv4 address ("192.168.17.1") + * - Numerical IPv6 address ("abcd:1234::a:b:1", "::abcd:1") + * + * @param inaddr - Hostname or IP address + */ + IpAddress(const char *inaddr); + + /** + * Construct an IP address from another IP address. + * + * @param ipaddr - address to copy + */ + IpAddress(const IpAddress &ipaddr); + + /** + * Construct an IP address from a GenAddress. + * + * @param genaddr - address to copy + */ + IpAddress(const GenAddress &genaddr); + + /** + * Destructor (ensure that SnmpSyntax::~SnmpSyntax() is overridden). + */ + ~IpAddress() {}; + + /** + * Map other SnmpSyntax objects to IpAddress. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Overloaded assignment operator for other IP addresses. + */ + IpAddress& operator=(const IpAddress &ipaddress); + + /** + * Overloaded assignment operator for strings. + */ + IpAddress& operator=(const char *inaddr); + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new IpAddress(*this); }; + + /** + * Return the friendly name. Does a reverse DNS lookup for the IP address. + * + * @param status - The errno value for the lookup + * + * @return the friendly name or a zero length string (no null pointer) + */ + char *friendly_name(int &status); + + /** + * Get a printable ASCII value of the address. + * + * @return String containing the numerical address + */ + virtual const char *get_printable() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Logically AND the address with the param. + * + * @param ipaddr - address to use as mask + */ + void mask(const IpAddress &ipaddr); + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() + { return (ip_version == version_ipv4) ? IPLEN : IP6LEN; }; + + /** + * Return the type of the address. + * @see Address::addr_type + * @return Always Address:type_ip + */ + virtual addr_type get_type() const { return type_ip; }; + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_IPADDR. + */ + virtual SmiUINT32 get_syntax() const { return sNMP_SYNTAX_IPADDR; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const + { return (ip_version == version_ipv4) ? (IPLEN + 2) : (IP6LEN + 2); }; + + /** + * Return the IP version of the address. + * + * @return one of Address::version_type + */ + virtual version_type get_ip_version() { return ip_version; }; + + /** + * Map a IPv4 address to a IPv6 address. + * + * @return - TRUE if no error occured. + */ + virtual int map_to_ipv6(); + + protected: + char output_buffer[OUTBUFF]; // output buffer + + // friendly name storage + char iv_friendly_name[MAX_FRIENDLY_NAME]; + int iv_friendly_name_status; + + // redefined parse address + // specific to IP addresses + virtual bool parse_address(const char *inaddr); + + + // redefined format output + // specific to IP addresses + virtual void format_output() const; + + // parse a dotted string + int parse_dotted_ipstring(const char *inaddr); + + // parse a coloned string + int parse_coloned_ipstring(const char *inaddr); + + // using the currently defined address, do a DNS + // and try to fill up the name + int addr_to_friendly(); + + // support both ipv4 and ipv6 addresses + version_type ip_version; +}; + +//------------------------------------------------------------------------ +//---------[ UDP Address Class ]------------------------------------------ +//------------------------------------------------------------------------ +class DLLOPT UdpAddress : public IpAddress +{ + public: + /** + * Construct an empty invalid UDP address. + */ + UdpAddress(); + + /** + * Construct an UDP address from a string. + * + * The following formats can be used additional to those recognized by + * IpAdress: + * - Port added to IPv4 address with '/' or ':' + * ("192.168.17.1:161", "192.168.17.1/161", "printsrv/161") + * - Port added to IPv6 address with '/' or using '[...]:' + * ("::1/162", "[::1]/162", "[::1]:162") + * + * @param inaddr - Hostname or IP address + */ + UdpAddress(const char *inaddr); + + /** + * Construct an UDP address from another UDP address. + * + * @param udpaddr - address to copy + */ + UdpAddress(const UdpAddress &udpaddr); + + /** + * Construct an UDP address from a GenAddress. + * + * @param genaddr - address to copy + */ + UdpAddress(const GenAddress &genaddr); + + /** + * Construct an UDP address from a IP address. + * The port will be set to 0. + * + * @param ipaddr - address to copy + */ + UdpAddress(const IpAddress &ipaddr); + + /** + * Destructor (ensure that SnmpSyntax::~SnmpSyntax() is overridden). + */ + ~UdpAddress() {}; + + /** + * Map other SnmpSyntax objects to UdpAddress. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Overloaded assignment operator for UdpAddress. + */ + UdpAddress& operator=(const UdpAddress &udpaddr); + + /** + * Overloaded assignment operator for IpAddress. + */ + UdpAddress& operator=(const IpAddress &ipaddr); + + /** + * Overloaded assignment operator for strings. + */ + UdpAddress& operator=(const char *inaddr); + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_OCTETS. + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OCTETS; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const + { return (ip_version == version_ipv4) ? (UDPIPLEN + 2) : (UDPIP6LEN + 2);}; + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new UdpAddress(*this); }; + + /** + * Get a printable ASCII value of the address. + * + * @return String containing the numerical address + */ + virtual const char *get_printable() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Set the port number. + * + * @note If the object is not valid(), the port may not be set. + */ + void set_port(const unsigned short p); + + /** + * Get the port number. + * + * @return The port number, or 0 is the object is not valid. + */ + unsigned short get_port() const; + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() + { return (ip_version == version_ipv4) ? UDPIPLEN : UDPIP6LEN; }; + + /** + * Return the type of the address. + * @see Address::addr_type + * @return Always Address:type_udp + */ + virtual addr_type get_type() const { return type_udp; }; + + /** + * Map a IPv4 UDP address to a IPv6 UDP address. + * + * @return - TRUE if no error occured. + */ + virtual int map_to_ipv6(); + +protected: + char output_buffer[OUTBUFF]; // output buffer + char sep; // separator + + // redefined parse address + // specific to IP addresses + virtual bool parse_address(const char *inaddr); + + // redefined format output + // specific to IP addresses + virtual void format_output() const; +}; + +#ifdef _MAC_ADDRESS +//------------------------------------------------------------------------- +//---------[ 802.3 MAC Address Class ]------------------------------------- +//------------------------------------------------------------------------- +class DLLOPT MacAddress : public Address { + +public: + // constructor, no arguments + MacAddress(); + + // constructor with a string argument + MacAddress(const char *inaddr); + + // constructor with another MAC object + MacAddress(const MacAddress &macaddr); + + // construct a MacAddress with a GenAddress + MacAddress(const GenAddress &genaddr); + + // destructor + ~MacAddress() {}; + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_OCTETS. + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OCTETS; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const { return MACLEN + 2; }; + + /** + * Map other SnmpSyntax objects to MacAddress. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + // assignment to another IpAddress object overloaded + MacAddress& operator=(const MacAddress &macaddress); + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new MacAddress(*this); }; + + /** + * Get a printable ASCII value of the address. + * + * @return String containing the numerical address + */ + virtual const char *get_printable() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() { return MACLEN; }; + + /** + * Return the type of the address. + * @see Address::addr_type + * @return Always Address:type_mac + */ + virtual addr_type get_type() const { return type_mac; }; + + // return a hash key + unsigned int hashFunction() const; + + +protected: + char output_buffer[OUTBUFF]; // output buffer + + // redefined parse address for macs + virtual bool parse_address(const char *inaddr); + + // redefined format output for MACs + virtual void format_output() const; +}; +#endif // _MAC_ADDRESS + +#ifdef _IPX_ADDRESS +//------------------------------------------------------------------------ +//---------[ IPX Address Class ]------------------------------------------ +//------------------------------------------------------------------------ +class DLLOPT IpxAddress : public Address { + +public: + // constructor no args + IpxAddress(); + + // constructor with a string arg + IpxAddress(const char *inaddr); + + // constructor with another ipx object + IpxAddress(const IpxAddress &ipxaddr); + + // construct with a GenAddress + IpxAddress(const GenAddress &genaddr); + + // destructor + ~IpxAddress() {}; + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_OCTETS. + */ + virtual SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OCTETS; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const { return IPXLEN + 2; }; + + /** + * Map other SnmpSyntax objects to IpxAddress. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + // assignment to another IpAddress object overloaded + IpxAddress& operator=(const IpxAddress &ipxaddress); + +#ifdef _MAC_ADDRESS + // get the host id portion of an ipx address + int get_hostid(MacAddress& mac); +#endif + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new IpxAddress(*this); }; + + /** + * Get a printable ASCII value of the address. + * + * @return String containing the numerical address + */ + virtual const char *get_printable() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() { return IPXLEN; }; + + /** + * Return the type of the address. + * @see Address::addr_type + * @return Always Address:type_ipx + */ + virtual addr_type get_type() const { return type_ipx; }; + +protected: + // ipx format separator + char separator; + char output_buffer[OUTBUFF]; // output buffer + + // redefined parse address for ipx strings + virtual bool parse_address(const char *inaddr); + + // redefined format output for ipx strings + // uses same separator as when constructed + virtual void format_output() const; + +}; + + + +//------------------------------------------------------------------------ +//---------[ IpxSock Address Class ]-------------------------------------- +//------------------------------------------------------------------------ +class DLLOPT IpxSockAddress : public IpxAddress { + +public: + // constructor, no args + IpxSockAddress(); + + // constructor with a dotted string + IpxSockAddress(const char *inaddr); + + // construct an Udp address with another Udp address + IpxSockAddress(const IpxSockAddress &ipxaddr); + + //constructor with a GenAddress + IpxSockAddress(const GenAddress &genaddr); + + //constructor with a IpxAddress + // default socket # is 0 + IpxSockAddress(const IpxAddress &ipxaddr); + + // destructor + ~IpxSockAddress() {}; + + // syntax type + //virtual SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OCTETS; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const { return IPXSOCKLEN + 2; }; + + /** + * Map other SnmpSyntax objects to IpxSockAddress. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + // assignment to another IpAddress object overloaded + IpxSockAddress& operator=(const IpxSockAddress &ipxaddr); + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *)new IpxSockAddress(*this); }; + + // set the socket number + void set_socket(const unsigned short s); + + // get the socket number + unsigned short get_socket() const; + + /** + * Get a printable ASCII value of the address. + * + * @return String containing the numerical address + */ + virtual const char *get_printable() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const + { if (addr_changed) format_output(); return output_buffer; }; + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() { return IPXSOCKLEN; }; + + /** + * Return the type of the address. + * @see Address::addr_type + * @return Always Address:type_ipxsock + */ + virtual addr_type get_type() const { return type_ipxsock; }; + +protected: + char output_buffer[OUTBUFF]; // output buffer + + // redefined parse address for ipx strings + virtual bool parse_address(const char *inaddr); + + // redefined format output + // specific to IP addresses + virtual void format_output() const; + +}; +#endif // _IPX_ADDRESS + + + + +//------------------------------------------------------------------------- +//--------[ Generic Address ]---------------------------------------------- +//------------------------------------------------------------------------- +class DLLOPT GenAddress : public Address +{ + public: + /** + * Construct an empty invalid generic address object. + */ + GenAddress(); + + /** + * Construct a generic address from a string. + * + * To optimize the speed of the parsing method, use_type can be used + * to indicate that the address string is of the specified type. + * + * @param addr - address string + * @param use_type - if this value is set, the input string is only + * parsed for the given type + */ + GenAddress(const char *addr, + const Address::addr_type use_type = Address::type_invalid); + + /** + * Construct a generic address from an Address object. + * + * @param addr - Any address object + */ + GenAddress(const Address &addr); + + /** + * Construct a generic address from another generic address object. + * + * @param addr - Generic address object to copy + */ + GenAddress(const GenAddress &addr); + + /** + * Destructor, free memory. + */ + ~GenAddress() { if (address) delete address; }; + + /** + * Return the syntax. + * + * @return This method returns sNMP_SYNTAX_IPADDR, sNMP_SYNTAX_OCTETS + * or sNMP_SYNTAX_NULL if the generic address does not have + * an address object. + */ + SmiUINT32 get_syntax() const + { return address ? address->get_syntax() : sNMP_SYNTAX_NULL; }; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const + { return address ? address->get_asn1_length() : 2; }; + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *)new GenAddress(*this); }; + + /** + * Overloaded assignment operator for a GenAddress. + */ + GenAddress& operator=(const GenAddress &addr); + + /** + * Overloaded assignment operator for a Address. + */ + GenAddress& operator=(const Address &addr); + + /** + * Map other SnmpSyntax objects to GenAddress. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Get a printable ASCII value of the address. + * + * @return String containing the numerical address + */ + virtual const char *get_printable() const + { return (address) ? address->get_printable() : output_buffer; }; + + /** + * Overloaded operator for streaming output. + * + * @return String containing the numerical address + */ + virtual operator const char *() const + { return address ? (const char *)*address : output_buffer; }; + + /** + * Get the length of the binary address (accessible through operator[]). + */ + virtual int get_length() { return (address) ? address->get_length() : 0; }; + + /** + * Return the type of the address. + * @see Address::addr_type + * @return Type of the contained address object or Address::type_invalid + * if it is not valid(). + */ + virtual addr_type get_type() const + { return (valid()) ? address->get_type() : type_invalid; }; + + /** + * Access the protected address. + * The caller must make sure that this GenAddress object ist valid() + * and is of the right type (get_type()). + */ + const IpAddress &cast_ipaddress() const { return (IpAddress& )*address; }; + + /** + * Access the protected address. + * The caller must make sure that this GenAddress object ist valid() + * and is of the right type (get_type()). + */ + const UdpAddress &cast_udpaddress() const { return (UdpAddress&)*address; }; + +#ifdef _MAC_ADDRESS + /** + * Access the protected address. + * The caller must make sure that this GenAddress object ist valid() + * and is of the right type (get_type()). + */ + const MacAddress &cast_macaddress() const { return (MacAddress&)*address; }; +#endif + +#ifdef _IPX_ADDRESS + /** + * Access the protected address. + * The caller must make sure that this GenAddress object ist valid() + * and is of the right type (get_type()). + */ + const IpxAddress &cast_ipxaddress() const { return (IpxAddress&)*address; }; + + /** + * Access the protected address. + * The caller must make sure that this GenAddress object ist valid() + * and is of the right type (get_type()). + */ + const IpxSockAddress &cast_ipxsockaddress() const + { return (IpxSockAddress&)*address; }; +#endif + +protected: + // pointer to a a concrete address + Address *address; + char output_buffer[1]; // output buffer + + // redefined parse address for generic address + virtual bool parse_address(const char *addr) + { return parse_address(addr, Address::type_invalid); }; + + virtual bool parse_address(const char *addr, + const Address::addr_type use_type); + + // format output for a generic address + virtual void format_output() const {}; + + /** + * Is this a GenAddress object. + */ + virtual bool is_gen_address() const { return true; }; +}; + +// create AddressCollection type +typedef SnmpCollection AddressCollection; +typedef SnmpCollection UdpAddressCollection; + +#endif //_ADDRESS diff --git a/backend/camembert/libkmsnmp/asn1.cpp b/backend/camembert/libkmsnmp/asn1.cpp new file mode 100644 index 0000000..5833990 --- /dev/null +++ b/backend/camembert/libkmsnmp/asn1.cpp @@ -0,0 +1,2093 @@ +/*_############################################################################ + _## + _## asn1.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ + +/*=================================================================== + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + A S N 1. C P P + + ASN encoder / decoder implementation + + DESIGN + AUTHOR: + Peter E. Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEM(S): + MS-Windows Win32 + BSD UNIX + +=====================================================================*/ +char asn1_cpp_version[]="#(@) SNMP++ $Id: asn1.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#ifdef __unix +#include /**/ +#include /**/ +#endif + +#include /**/ + +#ifdef WIN32 +#include +#endif + +#include "config_snmp_pp.h" +#include "asn1.h" +#include "v3.h" +#include "snmperrs.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define LENMASK 0x0ff + +#define ASN_UNI_PRIV (ASN_UNIVERSAL | ASN_PRIMITIVE) +#define ASN_SEQ_CON (ASN_SEQUENCE | ASN_CONSTRUCTOR) + +/* + * asn_parse_int - pulls a long out of an ASN int type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_parse_int( unsigned char *data, + int *datalength, + unsigned char *type, + long int *intp, + int intsize) +{ + /* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + * timestamp 0x43 asnlength byte {byte}* + */ + unsigned char *bufp = data; + unsigned long asn_length; + long value = 0; + + if (intsize != sizeof (long)){ + ASNERROR("not long"); + return NULL; + } + *type = *bufp++; + if ((*type != 0x02) && (*type != 0x43) && + (*type != 0x41)) { + ASNERROR("Wrong Type. Not an integer"); + return NULL; + } + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL){ + ASNERROR("bad length"); + return NULL; + } + if ((asn_length + (bufp - data)) > (unsigned long)(*datalength)){ + ASNERROR("overflow of message"); + return NULL; + } + if ((int)asn_length > intsize){ + ASNERROR("I don't support such large integers"); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + if (*bufp & 0x80) + value = -1; /* integer is negative */ + while(asn_length--) + value = (value << 8) | *bufp++; + *intp = value; + return bufp; +} + + +/* + * asn_parse_unsigned_int - pulls an unsigned long out of an ASN int type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_parse_unsigned_int( unsigned char *data, + int *datalength, + unsigned char *type, + unsigned long *intp, + int intsize) +{ + /* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + * 0x43 asnlength byte {byte}* + */ + unsigned char *bufp = data; + unsigned long asn_length; + unsigned long value = 0; + + // check the size of the object being requested + if (intsize != sizeof (long)){ + ASNERROR("not long"); + return NULL; + } + + // get the type + *type = *bufp++; + if ((*type != 0x02) && (*type != 0x43) && + (*type != 0x41) && (*type != 0x42) && + (*type != 0x47)) { + ASNERROR("Wrong Type. Not an unsigned integer"); + return NULL; + } + // pick up the len + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL){ + ASNERROR("bad length"); + return NULL; + } + + // check the len for message overflow + if ((asn_length + (bufp - data)) > (unsigned long)(*datalength)){ + ASNERROR("overflow of message"); + return NULL; + } + + // check for legal uint size + if (( (int)asn_length > 5) || (((int)asn_length > 4) && (*bufp != 0x00))) { + ASNERROR("I don't support such large integers"); + return NULL; + } + + // check for leading 0 octet + if (*bufp == 0x00) { + bufp++; + asn_length--; + } + + // fix the returned data length value + *datalength -= (int)asn_length + (bufp - data); + + // calculate the value + for (long i=0;i<(long)asn_length;i++) + value = (value << 8) + (unsigned long) *bufp++; + + // assign return value + *intp = value; + + // return the bumped pointer + return bufp; +} + + +/* + * asn_build_int - builds an ASN object containing an integer. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_build_int( unsigned char *data, + int *datalength, + unsigned char type, + long *intp, + int intsize) +{ + /* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + long integer; + unsigned long mask; + + if (intsize != sizeof (long)) + return NULL; + integer = *intp; + /* + * Truncate "unnecessary" bytes off of the most significant end of this + * 2's complement integer. There should be no sequence of 9 + * consecutive 1's or 0's at the most significant end of the + * integer. + */ + mask = 0x1FFul << ((8 * (sizeof(long) - 1)) - 1); + /* mask is 0xFF800000 on a big-endian machine */ + while((((integer & mask) == 0) || ((integer & mask) == mask)) + && intsize > 1){ + intsize--; + integer <<= 8; + } + data = asn_build_header(data, datalength, type, intsize); + if (data == NULL) + return NULL; + if (*datalength < intsize) + return NULL; + *datalength -= intsize; + mask = 0xFFul << (8 * (sizeof(long) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + while(intsize--){ + *data++ = (unsigned char)((integer & mask) >> (8 * (sizeof(long) - 1))); + integer <<= 8; + } + return data; +} + + +/* + * asn_build_unsigned_int - builds an ASN object containing an integer. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_build_unsigned_int( unsigned char *data, // modified data + int *datalength, // returned buffer length + unsigned char type, // SMI type + unsigned long *intp, // Uint to encode + int intsize) // size of uint to encode +{ + /* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + unsigned long u_integer; + long u_integer_len; + long x; + + // check uint size + if (intsize != sizeof (long)) + return NULL; + + + // local var point to var passed in + u_integer = *intp; + + // figure out the len + if ((( u_integer >> 24) & LENMASK) != 0) + u_integer_len = 4; + else if ((( u_integer >> 16) & LENMASK) !=0) + u_integer_len = 3; + else if ((( u_integer >> 8) & LENMASK) !=0) + u_integer_len = 2; + else + u_integer_len =1; + + // check for 5 byte len where first byte will be + // a null + if ((( u_integer >> (8 * (u_integer_len -1))) & 0x080) !=0) { + u_integer_len++; + } + + // build up the header + data = asn_build_header( data, // data buffer to be modified + datalength, // length of data buffer + type, // SMI type to enode + (int)u_integer_len); // length of BER encoded item + + // special case, add a null byte for len of 5 + if ( u_integer_len ==5) { + *data++ = (unsigned char) 0; + for (x=1;x> (8 * ((u_integer_len-1)-x)& LENMASK)); + } + else + { + for (x=0;x> (8 * ((u_integer_len-1)-x)& LENMASK)); + } + *datalength -= u_integer_len; + return data; +} + + +/* + * asn_parse_string - pulls an octet string out of an ASN octet string type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * "string" is filled with the octet string. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_parse_string( unsigned char *data, + int *datalength, + unsigned char *type, + unsigned char *string, + int *strlength) +{ + /* + * ASN.1 octet string ::= primstring | cmpdstring + * primstring ::= 0x04 asnlength byte {byte}* + * cmpdstring ::= 0x24 asnlength string {string}* + * ipaddress ::= 0x40 4 byte byte byte byte + */ + unsigned char *bufp = data; + unsigned long asn_length; + + *type = *bufp++; + if ((*type != 0x04) && (*type != 0x24) && + (*type != 0x40) && (*type != 0x44) && + (*type != 0x45)) { + ASNERROR("Wrong Type. Not a string"); + return NULL; + } + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL) + return NULL; + if ((asn_length + (bufp - data)) > (unsigned long)(*datalength)){ + ASNERROR("overflow of message"); + return NULL; + } + if ((int)asn_length > *strlength){ + ASNERROR("I don't support such long strings"); + return NULL; + } + // fixed + memcpy((char *)string, (char *)bufp, (int)asn_length); + *strlength = (int)asn_length; + *datalength -= (int)asn_length + (bufp - data); + return bufp + asn_length; +} + + +/* + * asn_build_string - Builds an ASN octet string object containing the input string. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_build_string( unsigned char *data, + int *datalength, + unsigned char type, + unsigned char *string, + int strlength) +{ + /* + * ASN.1 octet string ::= primstring | cmpdstring + * primstring ::= 0x04 asnlength byte {byte}* + * cmpdstring ::= 0x24 asnlength string {string}* + * This code will never send a compound string. + */ + data = asn_build_header(data, datalength, type, strlength); + if (data == NULL) + return NULL; + if (*datalength < strlength) + return NULL; + // fixed + memcpy((unsigned char *)data,(unsigned char *)string, strlength); + *datalength -= strlength; + return data + strlength; +} + + +/* + * asn_parse_header - interprets the ID and length of the current object. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * in this object following the id and length. + * + * Returns a pointer to the first byte of the contents of this object. + * Returns NULL on any error. + */ +unsigned char *asn_parse_header( unsigned char *data, + int *datalength, + unsigned char *type) +{ + unsigned char *bufp = data; + register int header_len; + unsigned long asn_length; + + /* this only works on data types < 30, i.e. no extension octets */ + if (IS_EXTENSION_ID(*bufp)){ + ASNERROR("can't process ID >= 30"); + return NULL; + } + *type = *bufp; + bufp = asn_parse_length(bufp + 1, &asn_length); + if (bufp == NULL) + return NULL; + header_len = bufp - data; + if ((unsigned long)(header_len + asn_length) > (unsigned long)*datalength){ + ASNERROR("asn length too long"); + return NULL; + } + *datalength = (int)asn_length; + return bufp; +} + +/* + * asn_build_header - builds an ASN header for an object with the ID and + * length specified. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * in this object following the id and length. + * + * This only works on data types < 30, i.e. no extension octets. + * The maximum length is 0xFFFF; + * + * Returns a pointer to the first byte of the contents of this object. + * Returns NULL on any error. + */ +unsigned char * asn_build_header( unsigned char *data, + int *datalength, + unsigned char type, + int length) +{ + if (*datalength < 1) + return NULL; + *data++ = type; + (*datalength)--; + return asn_build_length(data, datalength, length); + +} + +/* + * asn_build_sequence - builds an ASN header for a sequence with the ID and + * length specified. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * in this object following the id and length. + * + * This only works on data types < 30, i.e. no extension octets. + * The maximum length is 0xFFFF; + * + * Returns a pointer to the first byte of the contents of this object. + * Returns NULL on any error. + */ +unsigned char * asn_build_sequence( unsigned char *data, + int *datalength, + unsigned char type, + int length) +{ + *datalength -= 4; + if (*datalength < 0){ + *datalength += 4; /* fix up before punting */ + return NULL; + } + *data++ = type; + *data++ = (unsigned char)(0x02 | ASN_LONG_LEN); + *data++ = (unsigned char)((length >> 8) & 0xFF); + *data++ = (unsigned char)(length & 0xFF); + return data; + +} + +/* + * asn_parse_length - interprets the length of the current object. + * On exit, length contains the value of this length field. + * + * Returns a pointer to the first byte after this length + * field (aka: the start of the data field). + * Returns NULL on any error. + */ +unsigned char * asn_parse_length( unsigned char *data, + unsigned long *length) +{ + unsigned char lengthbyte = *data; + + if (lengthbyte & ASN_LONG_LEN){ + lengthbyte &= ~ASN_LONG_LEN; /* turn MSb off */ + if (lengthbyte == 0){ + ASNERROR("We don't support indefinite lengths"); + return NULL; + } + if (lengthbyte > sizeof(int)){ + ASNERROR("we can't support data lengths that long"); + return NULL; + } + // fixed + memcpy((char *)length, (char *)data + 1, (int)lengthbyte); + *length = ntohl(*length); + // ntohl even on ALPHA (DEC/COMPAQ) 64bit platforms works on 32bit int, + // whereas long is 64bit - therefore: +#ifdef alpha + *length >>= (8 * ((sizeof(int)) - lengthbyte)); +#else + *length >>= (8 * ((sizeof(long)) - lengthbyte)); +#endif + // check for length greater than 2^31 + if (*length > 0x80000000ul) { + ASNERROR("SNMP does not support data lengths > 2^31"); + return NULL; + } + return data + lengthbyte + 1; + } else { /* short asnlength */ + *length = (long)lengthbyte; + return data + 1; + } +} + +unsigned char *asn_build_length( unsigned char *data, + int *datalength, + int length) +{ + unsigned char *start_data = data; + + /* no indefinite lengths sent */ + if (length < 0x80){ + if (*datalength < 1){ + ASNERROR("build_length"); + return NULL; + } + *data++ = (unsigned char)length; + } + else if (length <= 0xFF){ + if (*datalength < 2){ + ASNERROR("build_length"); + return NULL; + } + *data++ = (unsigned char)(0x01 | ASN_LONG_LEN); + *data++ = (unsigned char)length; + } else if (length <= 0xFFFF) { /* 0xFF < length <= 0xFFFF */ + if (*datalength < 3){ + ASNERROR("build_length"); + return NULL; + } + *data++ = (unsigned char)(0x02 | ASN_LONG_LEN); + *data++ = (unsigned char)((length >> 8) & 0xFF); + *data++ = (unsigned char)(length & 0xFF); + } else if (length <= 0xFFFFFF) { /* 0xFF < length <= 0xFFFF */ + if (*datalength < 4){ + ASNERROR("build_length"); + return NULL; + } + *data++ = (unsigned char)(0x03 | ASN_LONG_LEN); + *data++ = (unsigned char)((length >> 16) & 0xFF); + *data++ = (unsigned char)((length >> 8) & 0xFF); + *data++ = (unsigned char)(length & 0xFF); + } + else { + if (*datalength < 5){ + ASNERROR("build_length"); + return NULL; + } + *data++ = (unsigned char)(0x04 | ASN_LONG_LEN); + *data++ = (unsigned char)((length >> 24) & 0xFF); + *data++ = (unsigned char)((length >> 16) & 0xFF); + *data++ = (unsigned char)((length >> 8) & 0xFF); + *data++ = (unsigned char)(length & 0xFF); + } + *datalength -= (data - start_data); + return data; + +} + +/* + * asn_parse_objid - pulls an object indentifier out of an ASN object identifier type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * "objid" is filled with the object identifier. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char *asn_parse_objid( unsigned char *data, + int *datalength, + unsigned char *type, + oid *objid, + int *objidlength) +{ + /* + * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* + * subidentifier ::= {leadingbyte}* lastbyte + * leadingbyte ::= 1 7bitvalue + * lastbyte ::= 0 7bitvalue + */ + unsigned char *bufp = data; + oid *oidp = objid + 1; + unsigned long subidentifier; + long length; + unsigned long asn_length; + + *type = *bufp++; + if (*type != 0x06) { + ASNERROR("Wrong Type. Not an oid"); + return NULL; + } + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL) + return NULL; + if ((asn_length + (bufp - data)) > (unsigned long)(*datalength)){ + ASNERROR("overflow of message"); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + + /* Handle invalid object identifier encodings of the form 06 00 robustly */ + if (asn_length == 0) + objid[0] = objid[1] = 0; + + length = asn_length; + (*objidlength)--; /* account for expansion of first byte */ + while (length > 0 && (*objidlength)-- > 0){ + subidentifier = 0; + do { /* shift and add in low order 7 bits */ + subidentifier = (subidentifier << 7) + (*(unsigned char *)bufp & ~ASN_BIT8); + length--; + } while (*(unsigned char *)bufp++ & ASN_BIT8); /* last byte has high bit clear */ + if (subidentifier > (unsigned long)MAX_SUBID){ + ASNERROR("subidentifier too long"); + return NULL; + } + *oidp++ = (oid)subidentifier; + } + + /* + * The first two subidentifiers are encoded into the first component + * with the value (X * 40) + Y, where: + * X is the value of the first subidentifier. + * Y is the value of the second subidentifier. + */ + subidentifier = (unsigned long)objid[1]; + if (subidentifier == 0x2B){ + objid[0] = 1; + objid[1] = 3; + } else { + objid[1] = (unsigned char)(subidentifier % 40); + objid[0] = (unsigned char)((subidentifier - objid[1]) / 40); + } + + *objidlength = (int)(oidp - objid); + return bufp; +} + +/* + * asn_build_objid - Builds an ASN object identifier object containing the + * input string. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char *asn_build_objid( unsigned char *data, + int *datalength, + unsigned char type, + oid *objid, + int objidlength) +{ + /* + * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* + * subidentifier ::= {leadingbyte}* lastbyte + * leadingbyte ::= 1 7bitvalue + * lastbyte ::= 0 7bitvalue + */ + // F.Fock correct buffer size must be 5*8bit*MAX_OID_LEN + unsigned char buf[MAX_OID_LEN*5]; + unsigned char *bp = buf; + oid *op = objid; + int asnlength; + unsigned long subid, mask, testmask; + int bits, testbits; + + if (objidlength < 2){ + *bp++ = 0; + objidlength = 0; + } else { + *bp++ = (unsigned char) (op[1] + (op[0] * 40)); + objidlength -= 2; + op += 2; + } + + while(objidlength-- > 0){ + subid = *op++; + if (subid < 127){ /* off by one? */ + *bp++ = (unsigned char )subid; + } else { + mask = 0x7F; /* handle subid == 0 case */ + bits = 0; + /* testmask *MUST* !!!! be of an unsigned type */ + for(testmask = 0x7F, testbits = 0; testmask != 0; + testmask <<= 7, testbits += 7){ + if (subid & testmask){ /* if any bits set */ + mask = testmask; + bits = testbits; + } + } + /* mask can't be zero here */ + for(;mask != 0x7F; mask >>= 7, bits -= 7){ + /* fix a mask that got truncated above */ + if (mask == 0x1E00000) + mask = 0xFE00000; + *bp++ = (unsigned char)(((subid & mask) >> bits) | ASN_BIT8); + } + *bp++ = (unsigned char)(subid & mask); + } + } + asnlength = bp - buf; + data = asn_build_header(data, datalength, type, asnlength); + if (data == NULL) + return NULL; + if (*datalength < asnlength) + return NULL; + // fixed + memcpy((char *)data, (char *)buf, asnlength); + *datalength -= asnlength; + return data + asnlength; +} + +/* + * asn_parse_null - Interprets an ASN null type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char *asn_parse_null(unsigned char *data, + int *datalength, + unsigned char *type) +{ + /* + * ASN.1 null ::= 0x05 0x00 + */ + unsigned char *bufp = data; + unsigned long asn_length; + + *type = *bufp++; + if (*type != 0x05) { + ASNERROR("Wrong Type. Not a null"); + return NULL; + } + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL) + return NULL; + if (asn_length != 0){ + ASNERROR("Malformed NULL"); + return NULL; + } + *datalength -= (bufp - data); + return bufp + asn_length; +} + + +/* + * asn_build_null - Builds an ASN null object. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char *asn_build_null( unsigned char *data, + int *datalength, + unsigned char type) +{ + /* + * ASN.1 null ::= 0x05 0x00 + */ + return asn_build_header(data, datalength, type, 0); +} + +/* + * asn_parse_bitstring - pulls a bitstring out of an ASN bitstring type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * "string" is filled with the bit string. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char *asn_parse_bitstring( unsigned char *data, + int *datalength, + unsigned char *type, + unsigned char *string, + int *strlength) +{ + /* + * bitstring ::= 0x03 asnlength unused {byte}* + */ + unsigned char *bufp = data; + unsigned long asn_length; + + *type = *bufp++; + if (*type != 0x03) { + ASNERROR("Wrong Type. Not a bitstring"); + return NULL; + } + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL) + return NULL; + if ((asn_length + (bufp - data)) > (unsigned long)(*datalength)){ + ASNERROR("overflow of message"); + return NULL; + } + if ((int) asn_length > *strlength){ + ASNERROR("I don't support such long bitstrings"); + return NULL; + } + if (asn_length < 1){ + ASNERROR("Invalid bitstring"); + return NULL; + } + if (*bufp > 7){ + ASNERROR("Invalid bitstring"); + return NULL; + } + // fixed + memcpy((char *)string,(char *)bufp, (int)asn_length); + *strlength = (int)asn_length; + *datalength -= (int)asn_length + (bufp - data); + return bufp + asn_length; +} + + +/* + * asn_build_bitstring - Builds an ASN bit string object containing the + * input string. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the beginning of the next object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char *asn_build_bitstring( unsigned char *data, + int *datalength, + unsigned char type, + unsigned char *string, + int strlength) +{ + /* + * ASN.1 bit string ::= 0x03 asnlength unused {byte}* + */ + if (strlength < 1 || *string > 7){ + ASNERROR("Building invalid bitstring"); + return NULL; + } + data = asn_build_header(data, datalength, type, strlength); + if (data == NULL) + return NULL; + if (*datalength < strlength) + return NULL; + // fixed + memcpy((char *)data,(char *)string, strlength); + *datalength -= strlength; + return data + strlength; +} + + +/* + * asn_parse_unsigned_int64 - pulls a 64 bit unsigned long out of an ASN int + * type. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_parse_unsigned_int64( unsigned char *data, + int *datalength, + unsigned char *type, + struct counter64 *cp, + int countersize) +{ + /* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + unsigned char *bufp = data; + unsigned long asn_length; + unsigned long low = 0, high = 0; + int intsize = 4; + + if (countersize != sizeof(struct counter64)){ + ASNERROR("not right size"); + return NULL; + } + *type = *bufp++; + if ((*type != 0x02) && (*type != 0x46)) { + ASNERROR("Wrong Type. Not an integer 64"); + return NULL; + } + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL){ + ASNERROR("bad length"); + return NULL; + } + if ((asn_length + (bufp - data)) > (unsigned long)(*datalength)){ + ASNERROR("overflow of message"); + return NULL; + } + if (((int)asn_length > (intsize * 2 + 1)) || + (((int)asn_length == (intsize * 2) + 1) && *bufp != 0x00)){ + ASNERROR("I don't support such large integers"); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + if (*bufp & 0x80){ + low = (unsigned long) -1; // integer is negative + high = (unsigned long) -1; + } + while(asn_length--){ + high = (high << 8) | ((low & 0xFF000000) >> 24); + low = (low << 8) | *bufp++; + } + cp->low = low; + cp->high = high; + return bufp; +} + + +/* + * asn_build_unsigned_int64 - builds an ASN object containing a 64 bit integer. + * On entry, datalength is input as the number of valid bytes following + * "data". On exit, it is returned as the number of valid bytes + * following the end of this object. + * + * Returns a pointer to the first byte past the end + * of this object (i.e. the start of the next object). + * Returns NULL on any error. + */ +unsigned char * asn_build_unsigned_int64( unsigned char *data, + int *datalength, + unsigned char type, + struct counter64 *cp, + int countersize) +{ + /* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + unsigned long low, high; + unsigned long mask, mask2; + int add_null_byte = 0; + int intsize; + + if (countersize != sizeof (struct counter64)) + return NULL; + intsize = 8; + low = cp->low; + high = cp->high; + mask = 0xFFul << (8 * (sizeof(long) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + if ((unsigned char)((high & mask) >> (8 * (sizeof(long) - 1))) & 0x80){ + /* if MSB is set */ + add_null_byte = 1; + intsize++; + } + else { + /* + * Truncate "unnecessary" bytes off of the most significant end of this 2's + * complement integer. + * There should be no sequence of 9 consecutive 1's or 0's at the most + * significant end of the integer. + */ + mask2 = 0x1FFul << ((8 * (sizeof(long) - 1)) - 1); + /* mask2 is 0xFF800000 on a big-endian machine */ + while((((high & mask2) == 0) || ((high & mask2) == mask2)) + && intsize > 1){ + intsize--; + high = (high << 8) + | ((low & mask) >> (8 * (sizeof(long) - 1))); + low <<= 8; + } + } + data = asn_build_header(data, datalength, type, intsize); + if (data == NULL) + return NULL; + if (*datalength < intsize) + return NULL; + *datalength -= intsize; + if (add_null_byte == 1){ + *data++ = '\0'; + intsize--; + } + while(intsize--){ + *data++ = (unsigned char)((high & mask) >> (8 * (sizeof(long) - 1))); + high = (high << 8) + | ((low & mask) >> (8 * (sizeof(long) - 1))); + low <<= 8; + + } + return data; +} + + +// create a pdu +struct snmp_pdu * snmp_pdu_create( int command) +{ + struct snmp_pdu *pdu; + + pdu = (struct snmp_pdu *)malloc(sizeof(struct snmp_pdu)); + memset((char *)pdu, 0,sizeof(struct snmp_pdu)); + pdu->command = command; +#ifdef _SNMPv3 + pdu->msgid = 0; +#endif + pdu->errstat = 0; + pdu->errindex = 0; + pdu->enterprise = NULL; + pdu->enterprise_length = 0; + pdu->variables = NULL; + return pdu; +} + +// free content and clear pointers +void clear_pdu(struct snmp_pdu *pdu) +{ + struct variable_list *vp, *ovp; + + vp = pdu->variables; + while (vp) + { + if (vp->name) free((char *)vp->name); // free the oid part + if (vp->val.string) free((char *)vp->val.string); // free deep data + ovp = vp; + vp = vp->next_variable; // go to the next one + free((char *)ovp); // free up vb itself + } + pdu->variables = NULL; + + // if enterprise free it up + if (pdu->enterprise) + free((char *)pdu->enterprise); + pdu->enterprise = NULL; +} + +// free a pdu +void snmp_free_pdu( struct snmp_pdu *pdu) +{ + clear_pdu(pdu); // clear and free content + free((char *)pdu); // free up pdu itself +} + + +// add a null var to a pdu +void snmp_add_var(struct snmp_pdu *pdu, + oid *name, + int name_length, + SmiVALUE *smival) +{ + struct variable_list *vars; + + // if we don't have a vb list ,create one + if (pdu->variables == NULL) + pdu->variables = vars = (struct variable_list *)malloc(sizeof(struct variable_list)); + else + { // we have one, find the end + for(vars = pdu->variables; vars->next_variable; vars = vars->next_variable); + // create a new one + vars->next_variable = (struct variable_list *)malloc(sizeof(struct variable_list)); + // bump ptr + vars = vars->next_variable; + } + + // add the oid with no data + vars->next_variable = NULL; + + // hook in the Oid portion + vars->name = (oid *)malloc(name_length * sizeof(oid)); + // fixed + memcpy((char *)vars->name,(char *)name, name_length * sizeof(oid)); + vars->name_length = name_length; + + // hook in the SMI value + switch( smival->syntax) + { + // null , do nothing + case sNMP_SYNTAX_NULL: + case sNMP_SYNTAX_NOSUCHOBJECT: + case sNMP_SYNTAX_NOSUCHINSTANCE: + case sNMP_SYNTAX_ENDOFMIBVIEW: + { + vars->type = (unsigned char) smival->syntax; + vars->val.string = NULL; + vars->val_len = 0; + } + break; + + // octects + case sNMP_SYNTAX_OCTETS: + case sNMP_SYNTAX_OPAQUE: + case sNMP_SYNTAX_IPADDR: + { + vars->type = (unsigned char) smival->syntax; + vars->val.string = (unsigned char *)malloc((unsigned)smival->value.string.len); + vars->val_len = (int) smival->value.string.len; + memcpy( (unsigned char *) vars->val.string, + (unsigned char *) smival->value.string.ptr, + (unsigned) smival->value.string.len); + } + break; + + // oid + case sNMP_SYNTAX_OID: + { + vars->type = (unsigned char) smival->syntax; + vars->val_len = (int) smival->value.oid.len * sizeof(oid); + vars->val.objid = (oid *)malloc((unsigned)vars->val_len); + memcpy((unsigned long *)vars->val.objid, + (unsigned long *)smival->value.oid.ptr, + (unsigned) vars->val_len); + } + break; + + + + case sNMP_SYNTAX_TIMETICKS: + case sNMP_SYNTAX_CNTR32: + case sNMP_SYNTAX_GAUGE32: + // case sNMP_SYNTAX_UINT32: + { + long templong; + vars->type = (unsigned char) smival->syntax; + vars->val.integer = (long *)malloc(sizeof(long)); + vars->val_len = sizeof(long); + templong = (long) smival->value.uNumber; + memcpy( (long*) vars->val.integer, + (long*) &templong, + sizeof(long)); + } + break; + + case sNMP_SYNTAX_INT32: + { + long templong; + vars->type = (unsigned char) smival->syntax; + vars->val.integer = (long *)malloc(sizeof(long)); + vars->val_len = sizeof(long); + templong = (long) smival->value.sNumber; + memcpy( (long*) vars->val.integer, + (long*) &templong, + sizeof(long)); + } + break; + + // 64 bit counter + case sNMP_SYNTAX_CNTR64: + { + vars->type = ( unsigned char) smival->syntax; + vars->val.counter64 = (struct counter64 *)malloc( sizeof(struct counter64) ); + vars->val_len = sizeof(struct counter64); + memcpy( (struct counter64*) vars->val.counter64, + (SmiLPCNTR64) &(smival->value.hNumber), + sizeof( SmiCNTR64)); + } + break; + + } // end switch + +} + +// build the authentication +// works for v1 or v2c +unsigned char *snmp_auth_build( unsigned char *data, + int *length, + long int version, + unsigned char *community, + int community_len, + int messagelen) +{ + unsigned char *params; + int plen; + + params = community; + plen = community_len; + + data = asn_build_sequence(data, + length, + (unsigned char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), + messagelen + plen + 5); + if (data == NULL){ + ASNERROR("buildheader"); + return NULL; + } + data = asn_build_int(data, + length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + (long *)&version, + sizeof(version)); + if (data == NULL){ + ASNERROR("buildint"); + return NULL; + } + + data = asn_build_string(data, + length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), + params, + plen ); + if (data == NULL){ + ASNERROR("buildstring"); + return NULL; + } + + return (unsigned char *)data; +} + + +// build a variable binding +unsigned char * snmp_build_var_op(unsigned char *data, + oid * var_name, + int *var_name_len, + unsigned char var_val_type, + int var_val_len, + unsigned char *var_val, + int *listlength) + +{ + int dummyLen, headerLen; + unsigned char *dataPtr; + + dummyLen = *listlength; + dataPtr = data; + + + data += 4; + dummyLen -=4; + if (dummyLen < 0) + return NULL; + + headerLen = data - dataPtr; + *listlength -= headerLen; + data = asn_build_objid( data, + listlength, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + var_name, + *var_name_len); + if (data == NULL){ + ASNERROR("data is null"); + return NULL; + } + + // based on the type... + switch(var_val_type){ + case ASN_INTEGER: + data = asn_build_int( data, + listlength, + var_val_type, + (long *)var_val, + var_val_len); + break; + + case SMI_GAUGE: + case SMI_COUNTER: + case SMI_TIMETICKS: + case SMI_UINTEGER: + data = asn_build_unsigned_int( data, + listlength, + var_val_type, + (unsigned long *)var_val, + var_val_len); + break; + + case SMI_COUNTER64: + data = asn_build_unsigned_int64(data, + listlength, + var_val_type, + (struct counter64 *)var_val, + var_val_len); + break; + + case ASN_OCTET_STR: + case SMI_IPADDRESS: + case SMI_OPAQUE: + case SMI_NSAP: + data = asn_build_string(data, listlength, var_val_type, + var_val, var_val_len); + break; + + case ASN_OBJECT_ID: + data = asn_build_objid(data, listlength, var_val_type, + (oid *)var_val, var_val_len / sizeof(oid)); + break; + + case ASN_NULL: + data = asn_build_null(data, listlength, var_val_type); + break; + + case ASN_BIT_STR: + data = asn_build_bitstring(data, listlength, var_val_type, + var_val, var_val_len); + break; + + case SNMP_NOSUCHOBJECT: + case SNMP_NOSUCHINSTANCE: + case SNMP_ENDOFMIBVIEW: + data = asn_build_null(data, listlength, var_val_type); + break; + + default: + ASNERROR("wrong type"); + return NULL; + } + if (data == NULL){ + ASNERROR("data is null"); + return NULL; + } + dummyLen = (data - dataPtr) - headerLen; + + asn_build_sequence(dataPtr, + &dummyLen, + (unsigned char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), + dummyLen); + return data; +} + + +unsigned char *build_vb(struct snmp_pdu *pdu, + unsigned char *buf, int *buf_len) +{ + struct variable_list *vp; + unsigned char *cp; + unsigned char tmp_buf[SNMP_MSG_LENGTH]; + int vb_length; + int length; + + // build varbinds into packet buffer + cp = tmp_buf; + length = SNMP_MSG_LENGTH; + for(vp = pdu->variables; vp; vp = vp->next_variable) + { + cp = snmp_build_var_op( cp, vp->name, &vp->name_length, + vp->type, vp->val_len, + (unsigned char *)vp->val.string, + &length); + if (cp == NULL) return 0; + } + vb_length = cp - tmp_buf; + *buf_len -= vb_length; + if (*buf_len <= 0) return 0; + + // encode the length of encoded varbinds into buf + cp = asn_build_header( buf, buf_len, + (unsigned char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), + vb_length); + if (cp == NULL) return 0; + + // copy varbinds from packet behind header in buf + memcpy( (char *)cp, (char *)tmp_buf, vb_length); + + return (cp + vb_length); +} + +unsigned char *build_data_pdu(struct snmp_pdu *pdu, + unsigned char *buf, int *buf_len, + unsigned char *vb_buf, int vb_buf_len) +{ + unsigned char tmp_buf[SNMP_MSG_LENGTH]; + unsigned char *cp; + int totallength; + int length; + + // build data of pdu into tmp_buf + length = SNMP_MSG_LENGTH; + if (pdu->command != TRP_REQ_MSG) + { + // request id + cp = asn_build_int( tmp_buf, &length, + (unsigned char )(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + (long *)&pdu->reqid, sizeof(pdu->reqid)); + if (cp == NULL) return 0; + + // error status + cp = asn_build_int(cp, &length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + (long *)&pdu->errstat, sizeof(pdu->errstat)); + if (cp == NULL) return 0; + + // error index + cp = asn_build_int(cp, &length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + (long *)&pdu->errindex, sizeof(pdu->errindex)); + if (cp == NULL) return 0; + } + else + { // this is a trap message + // enterprise + cp = asn_build_objid( tmp_buf, &length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + (oid *)pdu->enterprise, pdu->enterprise_length); + if (cp == NULL) return 0; + + // agent-addr ; must be IPADDRESS changed by Frank Fock + cp = asn_build_string(cp, &length, + (unsigned char)(SMI_IPADDRESS), + (unsigned char *)&pdu->agent_addr.sin_addr.s_addr, + sizeof(pdu->agent_addr.sin_addr.s_addr)); + if (cp == NULL) return 0; + + long dummy = pdu->trap_type; + // generic trap + cp = asn_build_int(cp, &length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &dummy, sizeof(dummy)); + if (cp == NULL) return 0; + + dummy = pdu->specific_type; + // specific trap + cp = asn_build_int( cp, &length, + (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &dummy, sizeof(dummy)); + if (cp == NULL) return 0; + + // timestamp + cp = asn_build_unsigned_int(cp, &length, + (unsigned char )(SMI_TIMETICKS), + &pdu->time, sizeof(pdu->time)); + if (cp == NULL) return 0; + } + + if (length < vb_buf_len) return 0; + + // save relative position of varbinds + int vb_rel_pos = cp - tmp_buf; + totallength = (cp - tmp_buf) + vb_buf_len; + + // build header for datapdu into buf + cp = asn_build_header(buf, buf_len, + (unsigned char)pdu->command, totallength); + if (cp == NULL) return 0; + if (*buf_len < totallength) return 0; + + // copy data behind header + memcpy((char *)cp, (char *)tmp_buf, totallength - vb_buf_len); + memcpy((char *)cp + vb_rel_pos, (char *)vb_buf, vb_buf_len); + *buf_len -= totallength; + return (cp + totallength); +} + +// serialize the pdu +int snmp_build( struct snmp_pdu *pdu, + unsigned char *packet, + int *out_length, + long version, + unsigned char* community, + int community_len) +{ + unsigned char buf[SNMP_MSG_LENGTH]; + unsigned char *cp; + int length; + int totallength; + + // encode vbs with header into packet + length = *out_length; + cp = build_vb(pdu, packet, &length); + if (cp == 0) return -1; + totallength = cp - packet; + if (totallength >= *out_length) return -1; + + // encode datadpu into buf + length = SNMP_MSG_LENGTH; + cp = build_data_pdu(pdu, buf, &length, + packet, totallength); + if (cp == 0) return -1; + totallength = cp - buf; + if (totallength >= *out_length) return -1; + + // build SNMP header + length = *out_length; + cp = snmp_auth_build( packet, &length, version, + community, community_len, totallength ); + if (cp == NULL) return -1; + if ((*out_length - (cp - packet)) < totallength) return -1; + + // copy data + memcpy((char *)cp, (char *)buf, totallength); + totallength += cp - packet; + *out_length = totallength; + + return 0; +} + +// parse the authentication header +unsigned char *snmp_auth_parse(unsigned char *data, + int *length, + unsigned char *sid, + int *slen, + long *version) +{ + unsigned char type; + + // get the type + data = asn_parse_header( data, + length, + &type); + if (data == NULL){ + ASNERROR("bad header"); + return NULL; + } + + if (type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)){ + ASNERROR("wrong auth header type"); + return NULL; + } + + // get the version + data = asn_parse_int(data, + length, + &type, + version, + sizeof(*version)); + if (data == NULL){ + ASNERROR("bad parse of version"); + return NULL; + } + + // get the community name + data = asn_parse_string(data, + length, + &type, + sid, + slen); + if (data == NULL){ + ASNERROR("bad parse of community"); + return NULL; + } + + return (unsigned char *)data; +} + +unsigned char * +snmp_parse_var_op( unsigned char *data, // IN - pointer to the start of object + oid *var_name, // OUT - object id of variable + int *var_name_len, // IN/OUT - length of variable name + unsigned char *var_val_type, // OUT - type of variable (int or octet string) (one byte) + int *var_val_len, // OUT - length of variable + unsigned char **var_val, // OUT - pointer to ASN1 encoded value of variable + int *listlength) // IN/OUT - number of valid bytes left in var_op_list +{ + unsigned char var_op_type; + int var_op_len = *listlength; + unsigned char *var_op_start = data; + + data = asn_parse_header(data, &var_op_len, &var_op_type); + if (data == NULL){ + ASNERROR("Error snmp_parse_var_op: 1"); + return NULL; + } + if (var_op_type != (unsigned char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)) + return NULL; + data = asn_parse_objid(data, &var_op_len, &var_op_type, var_name, var_name_len); + if (data == NULL){ + ASNERROR("Error snmp_parse_var_op: 2"); + return NULL; + } + if (var_op_type != (unsigned char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID)) + return NULL; + *var_val = data; /* save pointer to this object */ + /* find out what type of object this is */ + data = asn_parse_header(data, &var_op_len, var_val_type); + if (data == NULL){ + ASNERROR("Error snmp_parse_var_op: 3"); + return NULL; + } + if (((unsigned long)var_op_len + (data - var_op_start)) > (unsigned long)(*listlength)) { + ASNERROR("Error snmp_parse_var_op: 4"); + return NULL; + } + *var_val_len = (int)var_op_len; + data += var_op_len; + *listlength -= (int)(data - var_op_start); + return data; +} + + +int snmp_parse_vb(struct snmp_pdu *pdu, unsigned char *&data, int &data_len) +{ + unsigned char *var_val; + int len; + struct variable_list *vp; + oid objid[ASN_MAX_NAME_LEN], *op; + unsigned char type; + + // get the vb list from received data + data = asn_parse_header(data, &data_len, &type); + if (data == NULL) + return SNMP_CLASS_ASN1ERROR; + if (type != (unsigned char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)) + return SNMP_CLASS_ASN1ERROR; + pdu->variables = NULL; + while(data_len > 0){ + if (pdu->variables == NULL){ + pdu->variables = vp = (struct variable_list *)malloc(sizeof(struct variable_list)); + } else { + vp->next_variable = (struct variable_list *)malloc(sizeof(struct variable_list)); + vp = vp->next_variable; + } + vp->next_variable = NULL; + vp->val.string = NULL; + vp->name = NULL; + vp->name_length = ASN_MAX_NAME_LEN; + data = snmp_parse_var_op( data, objid, &vp->name_length, &vp->type, + &vp->val_len, &var_val, &data_len); + if (data == NULL) + return SNMP_CLASS_ASN1ERROR; + op = (oid *)malloc((unsigned)vp->name_length * sizeof(oid)); + // fixed + memcpy((char *)op, (char *)objid, vp->name_length * sizeof(oid)); + vp->name = op; + + len = SNMP_MSG_LENGTH; + switch((short)vp->type){ + case ASN_INTEGER: + vp->val.integer = (long *)malloc(sizeof(long)); + vp->val_len = sizeof(long); + asn_parse_int(var_val, &len, &vp->type, (long *)vp->val.integer, sizeof(vp->val.integer)); + break; + + case SMI_COUNTER: + case SMI_GAUGE: + case SMI_TIMETICKS: + case SMI_UINTEGER: + vp->val.integer = (long *)malloc(sizeof(long)); + vp->val_len = sizeof(long); + asn_parse_unsigned_int(var_val, &len, &vp->type, (unsigned long *)vp->val.integer, sizeof(vp->val.integer)); + break; + + case SMI_COUNTER64: + vp->val.counter64 = (struct counter64 *)malloc( sizeof(struct counter64) ); + vp->val_len = sizeof(struct counter64); + asn_parse_unsigned_int64(var_val, &len, &vp->type, + (struct counter64 *)vp->val.counter64, + sizeof(*vp->val.counter64)); + break; + + case ASN_OCTET_STR: + case SMI_IPADDRESS: + case SMI_OPAQUE: + case SMI_NSAP: + vp->val.string = (unsigned char *)malloc((unsigned)vp->val_len); + asn_parse_string(var_val, &len, &vp->type, vp->val.string, &vp->val_len); + break; + + case ASN_OBJECT_ID: + vp->val_len = ASN_MAX_NAME_LEN; + asn_parse_objid(var_val, &len, &vp->type, objid, &vp->val_len); + //vp->val_len *= sizeof(oid); + vp->val.objid = (oid *)malloc((unsigned)vp->val_len * sizeof(oid)); + // fixed + memcpy((char *)vp->val.objid, + (char *)objid, + vp->val_len * sizeof(oid)); + break; + + case SNMP_NOSUCHOBJECT: + case SNMP_NOSUCHINSTANCE: + case SNMP_ENDOFMIBVIEW: + case ASN_NULL: + break; + + default: + ASNERROR("bad type returned "); + return SNMP_CLASS_ASN1ERROR; + break; + } + } + return SNMP_CLASS_SUCCESS; +} + + +int snmp_parse_data_pdu(snmp_pdu *pdu, unsigned char *&data, int &length) +{ + oid objid[ASN_MAX_NAME_LEN]; + int four = 4; + unsigned char type; + + data = asn_parse_header(data, &length, &type); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + pdu->command = type; + + if (pdu->command != TRP_REQ_MSG) + { + // get the rid + data = asn_parse_int(data, &length, &type, + (long *)&pdu->reqid, sizeof(pdu->reqid)); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + // get the error status + data = asn_parse_int(data, &length, &type, + (long *)&pdu->errstat, sizeof(pdu->errstat)); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + // get the error index + data = asn_parse_int(data, &length, &type, + (long *)&pdu->errindex, sizeof(pdu->errindex)); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + } + else + { // is a trap + + // get the enterprise + pdu->enterprise_length = ASN_MAX_NAME_LEN; + data = asn_parse_objid(data, &length, &type, + objid, &pdu->enterprise_length); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + pdu->enterprise = (oid *)malloc(pdu->enterprise_length * sizeof(oid)); + + memcpy((char *)pdu->enterprise,(char *)objid, + pdu->enterprise_length * sizeof(oid)); + + // get source address + data = asn_parse_string(data, &length, &type, + (unsigned char *)&pdu->agent_addr.sin_addr.s_addr, + &four); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + // get trap type + long dummy = 0; + data = asn_parse_int(data, &length, &type, &dummy, sizeof(dummy)); + pdu->trap_type = dummy; + + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + // trap type + dummy = 0; + data = asn_parse_int(data, &length, &type, &dummy, sizeof(dummy)); + pdu->specific_type = dummy; + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + + // timestamp + data = asn_parse_unsigned_int(data, &length, &type, + &pdu->time, sizeof(pdu->time)); + if (data == NULL) return SNMP_CLASS_ASN1ERROR; + } + return SNMP_CLASS_SUCCESS; +} + + +// parse a pdu +int snmp_parse( struct snmp_pdu *pdu, + unsigned char *data, + unsigned char *community_name, + unsigned long &community_len, + snmp_version &spp_version, + int length) +{ + long version = -1; + unsigned char community[256]; + int community_length = 256; + + // authenticates message and returns length if valid + data = snmp_auth_parse(data, + &length, + community, + &community_length, + &version); + if (data == NULL) + return SNMP_CLASS_ASN1ERROR; + + // copy the returned community name + memcpy( (unsigned char *) community_name, + (unsigned char *) community, + community_length); + community_len = (long int) community_length; + + if( version != SNMP_VERSION_1 && version != SNMP_VERSION_2C ) { + ASNERROR("Wrong version"); + return SNMP_CLASS_BADVERSION; + } + + spp_version = (snmp_version) version; + + int res = snmp_parse_data_pdu(pdu, data, length); + if (res != SNMP_CLASS_SUCCESS) + return res; + + return snmp_parse_vb(pdu, data, length); +} + + +// Parse the field HeaderData of a SNMPv3 message and return the values. +unsigned char *asn1_parse_header_data(unsigned char *buf, int *buf_len, + long *msg_id, long *msg_max_size, + unsigned char *msg_flags, + long *msg_security_model) +{ + unsigned char *buf_ptr = buf; + int length = *buf_len; + unsigned char type; + + buf = asn_parse_header( buf, &length, &type); + if (!buf) + { + debugprintf(0, "Parse error in header HeaderData"); + return 0; + } + + if (type != (ASN_SEQ_CON)){ + debugprintf(0, "wrong type in header of msgHeaderData"); + return 0; + } + + buf = asn_parse_int(buf, &length, &type, msg_id, sizeof(*msg_id)); + if (!buf){ + debugprintf(0, "Parse error: msg_id"); + return 0; + } + + buf = asn_parse_int(buf, &length, + &type, msg_max_size, sizeof(*msg_max_size)); + if (!buf){ + debugprintf(0, "Parse error: msg_max_size"); + return 0; + } + + int dummy = 1; + buf = asn_parse_string( buf, &length, &type, msg_flags, &dummy); + + if ((dummy !=1) || (!buf)) { + debugprintf(0, "Parse error: msg_flags"); + return 0; + } + + buf = asn_parse_int(buf, &length, &type, + msg_security_model, sizeof(*msg_security_model)); + if (!buf){ + debugprintf(0, "Parse error: msg_security_model"); + return 0; + } + + if (length) { + debugprintf(0, "Parse error: wrong length in header of HeaderData"); + return 0; + } + + debugprintf(3, "Parsed HeaderData: globalDataLength(0x%x), msg_id(0x%lx), " + "msg_max_size(0x%lx), msg_flags(0x%x), msg_security_model(0x%lx)", + length, *msg_id, *msg_max_size, *msg_flags, *msg_security_model); + + *buf_len -= (buf - buf_ptr); + return buf; +} + +// Encode the given values for the HeaderData into the buffer. +unsigned char *asn1_build_header_data(unsigned char *outBuf, int *maxLength, + long msgID, + long maxMessageSize, + unsigned char msgFlags, + long securityModel) + +{ + unsigned char buf[MAXLENGTH_BUFFER]; + unsigned char *bufPtr = (unsigned char*)&buf; + unsigned char *outBufPtr = outBuf; + int length = *maxLength; + int totalLength; + + debugprintf(3, "Coding msgID(0x%lx), maxMessageSize(0x%lx), " + "msgFlags(0x%x), securityModel(0x%lx)", + maxMessageSize, msgID, msgFlags, securityModel); + + bufPtr = asn_build_int(bufPtr, &length, + (unsigned char)(ASN_UNI_PRIV | ASN_INTEGER), + (long *)&msgID, sizeof(msgID)); + if (bufPtr == NULL){ + debugprintf(0, "asn_build_header_data: Error coding msgID"); + return NULL; + } + bufPtr = asn_build_int(bufPtr, &length, + (unsigned char)(ASN_UNI_PRIV | ASN_INTEGER), + (long *)&maxMessageSize, sizeof(maxMessageSize)); + if (bufPtr == NULL){ + debugprintf(0, "asn_build_header_data: Error coding maxMessageSize"); + return NULL; + } + + bufPtr = asn_build_string(bufPtr, &length, + (unsigned char)(ASN_UNI_PRIV | ASN_OCTET_STR), + (unsigned char*)&msgFlags, 1); + if (bufPtr == NULL){ + debugprintf(0, "asn_build_header_data: Error coding msgFlags"); + return NULL; + } + + bufPtr = asn_build_int(bufPtr, &length, + (unsigned char)(ASN_UNI_PRIV | ASN_INTEGER), + (long *)&securityModel, sizeof(securityModel)); + if (bufPtr == NULL){ + debugprintf(0, "asn_build_header_data: Error coding securityModel"); + return NULL; + } + + totalLength = bufPtr - (unsigned char*)&buf; + + debugprintf(3, "Coding sequence (headerdata), length = 0x%x", totalLength); + outBufPtr = asn_build_sequence(outBufPtr, maxLength, + (unsigned char)(ASN_SEQ_CON), + totalLength); + + if (outBufPtr == NULL) { + debugprintf(0, "asn_build_header_data: Error coding seq headerdata"); + return NULL; + } + + if (*maxLength < totalLength) { + debugprintf(0, "asn_build_header_data: Length error"); + return NULL; + } + + memcpy(outBufPtr, (unsigned char*)&buf, totalLength); + outBufPtr += totalLength; + *maxLength -= totalLength; + + debugprintf(21, "bufHeaderData:"); + debughexprintf(21, outBuf, outBufPtr - outBuf); + + return outBufPtr; +} + +// Parse the ScopedPDU and return the encoded values. +unsigned char *asn1_parse_scoped_pdu( + unsigned char *scoped_pdu, int *scoped_pdu_len, + unsigned char *context_engine_id, int *context_engine_id_len, + unsigned char *context_name, int *context_name_len ) +{ + unsigned char type; + + scoped_pdu = asn_parse_header( scoped_pdu, scoped_pdu_len, &type); + if (!scoped_pdu) { + debugprintf(0, "Parse error: Wrong header in scoped_pdu."); + return 0; + } + + if (type != (ASN_SEQ_CON)){ + debugprintf(0, "Parse error: Wrong header type in scoped_pdu."); + return 0; + } + + scoped_pdu = asn_parse_string( scoped_pdu, scoped_pdu_len, &type, + context_engine_id, context_engine_id_len); + if (!scoped_pdu){ + debugprintf(0, "Parse error: context_engine_id"); + return 0; + } + + scoped_pdu = asn_parse_string( scoped_pdu, scoped_pdu_len, &type, + context_name, context_name_len); + if (!scoped_pdu){ + debugprintf(0, "mpParseScopedPDU: bad parse of context_name"); + return 0; + } + + debugprintf(3, "Parsed scoped_pdu: context_engine_id length(0x%x), " + "context_name length(0x%x)", + *context_engine_id_len, *context_name_len); + + return scoped_pdu; +} + +// Encode the given values for the scopedPDU into the buffer. +unsigned char *asn1_build_scoped_pdu( + unsigned char *outBuf, int *max_len, + unsigned char *contextEngineID, long contextEngineIDLength, + unsigned char *contextName, long contextNameLength, + unsigned char *data, long dataLength) +{ + unsigned char buf[MAXLENGTH_BUFFER]; + unsigned char *bufPtr = (unsigned char*)&buf; + unsigned char *outBufPtr = outBuf; + long bufLength = 0; + + debugprintf(3, "Coding contextEngineID, length(0x%lx)" + ", contextName, length(0x%lx)", + contextEngineIDLength, contextNameLength); + + bufPtr = asn_build_string(bufPtr, max_len, + (unsigned char)(ASN_UNI_PRIV | ASN_OCTET_STR), + contextEngineID, contextEngineIDLength); + if (bufPtr == NULL) { + debugprintf(0, "asn1_build_scoped_pdu: error coding contextEngineID"); + return NULL; + } + + bufPtr = asn_build_string(bufPtr, max_len, + (unsigned char)(ASN_UNI_PRIV | ASN_OCTET_STR), + contextName, contextNameLength); + if (bufPtr == NULL) { + debugprintf(0, "asn1_build_scoped_pdu: error coding contextName"); + return NULL; + } + + bufLength = bufPtr - (unsigned char*)&buf; + + memcpy((char *)bufPtr, (char *)data, dataLength); + bufLength += dataLength; + + debugprintf(3, "Coding sequence (scopedPDU), length = 0x%lx",bufLength); + outBufPtr = asn_build_sequence(outBufPtr, max_len, + (unsigned char)(ASN_SEQ_CON), + bufLength); + if (outBufPtr == NULL) { + debugprintf(0, "asn1_build_scoped_pdu: error coding seq scopedPDU"); + return NULL; + } + + memcpy(outBufPtr, buf, bufLength); + outBufPtr += bufLength; + + debugprintf(21, "outBuf of build_scoped_pdu:"); + debughexprintf(21, outBuf, outBufPtr - outBuf); + + return outBufPtr; +} diff --git a/backend/camembert/libkmsnmp/asn1.h b/backend/camembert/libkmsnmp/asn1.h new file mode 100644 index 0000000..f3f4638 --- /dev/null +++ b/backend/camembert/libkmsnmp/asn1.h @@ -0,0 +1,440 @@ +/*_############################################################################ + _## + _## asn1.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +// $Id: asn1.h,v 1.1.1.1 2005/03/14 10:55:30 kindman Exp $ + +#ifndef _ASN1 +#define _ASN1 + +#ifdef WIN32 +#ifndef __unix +#include +#endif +#endif + +#include "target.h" + +#define MAXLENGTH_BUFFER SNMP_MSG_LENGTH +#ifndef EIGHTBIT_SUBIDS +typedef unsigned long oid; +#define MAX_SUBID 0xFFFFFFFF +#else +typedef unsigned char oid; +#define MAX_SUBID 0xFF +#endif + +#define MAX_OID_LEN 128 /* max subid's in an oid */ + +// asn.1 values +#define ASN_BOOLEAN (0x01) +#ifndef ASN_INTEGER +#define ASN_INTEGER (0x02) +#endif +#define ASN_BIT_STR (0x03) +#define ASN_OCTET_STR (0x04) +#ifndef ASN_NULL +#define ASN_NULL (0x05) +#endif +#define ASN_OBJECT_ID (0x06) +#ifndef ASN_SEQUENCE +#define ASN_SEQUENCE (0x10) +#endif +#define ASN_SET (0x11) +#ifndef ASN_UNIVERSAL +#define ASN_UNIVERSAL (0x00) +#endif +#ifndef ASN_APPLICATION +#define ASN_APPLICATION (0x40) +#endif +#ifndef ASN_CONTEXT +#define ASN_CONTEXT (0x80) +#endif +#ifndef ASN_PRIVATE +#define ASN_PRIVATE (0xC0) +#endif +#ifndef ASN_PRIMITIVE +#define ASN_PRIMITIVE (0x00) +#endif +#ifndef ASN_CONSTRUCTOR +#define ASN_CONSTRUCTOR (0x20) +#endif +#define ASN_LONG_LEN (0x80) +#define ASN_EXTENSION_ID (0x1F) +#define ASN_BIT8 (0x80) + +#define IS_CONSTRUCTOR(byte) ((byte) & ASN_CONSTRUCTOR) +#define IS_EXTENSION_ID(byte) (((byte) & ASN_EXTENSION_ID) == ASN_EXTENSION_ID) + +#define ASN_MAX_NAME_LEN 128 +#define SNMP_VERSION_1 0 +#define SNMP_VERSION_2C 1 +#define SNMP_VERSION_2STERN 2 +#define SNMP_VERSION_3 3 + +// defined types (from the SMI, RFC 1065) +#define SMI_IPADDRESS (ASN_APPLICATION | 0) +#define SMI_COUNTER (ASN_APPLICATION | 1) +#define SMI_GAUGE (ASN_APPLICATION | 2) +#define SMI_TIMETICKS (ASN_APPLICATION | 3) +#define SMI_OPAQUE (ASN_APPLICATION | 4) +#define SMI_NSAP (ASN_APPLICATION | 5) +#define SMI_COUNTER64 (ASN_APPLICATION | 6) +#define SMI_UINTEGER (ASN_APPLICATION | 7) + +#define GET_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x0) +#define GETNEXT_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x1) +#define GET_RSP_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x2) +#define SET_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x3) +#define TRP_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x4) + +#define GETBULK_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x5) +#define INFORM_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x6) +#define TRP2_REQ_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x7) +#define REPORT_MSG (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x8) + +#define SNMP_NOSUCHOBJECT (ASN_CONTEXT | ASN_PRIMITIVE | 0x0) +#define SNMP_NOSUCHINSTANCE (ASN_CONTEXT | ASN_PRIMITIVE | 0x1) +#define SNMP_ENDOFMIBVIEW (ASN_CONTEXT | ASN_PRIMITIVE | 0x2) + +#define SNMP_MSG_LENGTH MAX_SNMP_PACKET + +#ifdef _DEBUG +#define ASNERROR( string) debugprintf(3, "ASN parse error (%s)\n", string ) +#else +#define ASNERROR( string) +#endif + + +typedef struct sockaddr_in ipaddr; + +// pdu +struct snmp_pdu { + int command; // pdu type + unsigned long reqid; // Request id +#ifdef _SNMPv3 + unsigned long msgid; + unsigned long maxsize_scopedpdu; +#endif + unsigned long errstat; // Error status + unsigned long errindex; // Error index + + // Trap information + oid *enterprise; // System OID + int enterprise_length; + ipaddr agent_addr; // address of object generating trap + int trap_type; // trap type + int specific_type; // specific type + unsigned long time; // Uptime + + // vb list + struct variable_list *variables; +}; + +// vb list +struct variable_list { + struct variable_list *next_variable; // NULL for last variable + oid *name; // Object identifier of variable + int name_length; // number of subid's in name + unsigned char type; // ASN type of variable + union { // value of variable + long *integer; + unsigned char *string; + oid *objid; + unsigned char *bitstring; + struct counter64 *counter64; + } val; + int val_len; +}; + +struct counter64 { + unsigned long high; + unsigned long low; +}; + + +// prototypes for encoding routines +DLLOPT unsigned char *asn_parse_int( unsigned char *data, int *datalength, + unsigned char *type, + long int *intp, int intsize); + + +DLLOPT unsigned char *asn_parse_unsigned_int( unsigned char *data, + int *datalength, + unsigned char *type, + unsigned long *intp, + int intsize); + +DLLOPT unsigned char *asn_build_int( unsigned char *data, int *datalength, + unsigned char type, + long *intp, int intsize); + +DLLOPT unsigned char *asn_build_unsigned_int( unsigned char *data, + int *datalength, + unsigned char type, + unsigned long *intp, + int intsize); + +DLLOPT unsigned char *asn_parse_string( unsigned char *data, int *datalength, + unsigned char *type, + unsigned char *string, + int *strlength); + +DLLOPT unsigned char *asn_build_string( unsigned char *data, int *datalength, + unsigned char type, + unsigned char *string, + int strlength); + +DLLOPT unsigned char *asn_parse_header( unsigned char *data, int *datalength, + unsigned char *type); + +DLLOPT unsigned char *asn_build_header( unsigned char *data, int *datalength, + unsigned char type, + int length); + +DLLOPT unsigned char *asn_build_sequence( unsigned char *data, + int *datalength, + unsigned char type, + int length); + +DLLOPT unsigned char *asn_parse_length( unsigned char *data, + unsigned long *length); + +DLLOPT unsigned char *asn_build_length( unsigned char *data, int *datalength, + int length); + +DLLOPT unsigned char *asn_parse_objid( unsigned char *data, int *datalength, + unsigned char *type, + oid *objid, + int *objidlength); + +DLLOPT unsigned char *asn_build_objid( unsigned char *data, + int *datalength, + unsigned char type, + oid *objid, + int objidlength); + +DLLOPT unsigned char *asn_parse_null(unsigned char *data, int *datalength, + unsigned char *type); + +DLLOPT unsigned char *asn_build_null( unsigned char *data,int *datalength, + unsigned char type); + +DLLOPT unsigned char *asn_parse_bitstring( unsigned char *data, + int *datalength, + unsigned char *type, + unsigned char *string, + int *strlength); + +DLLOPT unsigned char *asn_build_bitstring( unsigned char *data, + int *datalength, + unsigned char type, + unsigned char *string, + int strlength); + +DLLOPT unsigned char *asn_parse_unsigned_int64( unsigned char *data, + int *datalength, + unsigned char *type, + struct counter64 *cp, + int countersize); + +DLLOPT unsigned char *asn_build_unsigned_int64( unsigned char *data, + int *datalength, + unsigned char type, + struct counter64 *cp, + int countersize); + +DLLOPT struct snmp_pdu *snmp_pdu_create( int command); + +DLLOPT void snmp_free_pdu( struct snmp_pdu *pdu); + +DLLOPT int snmp_build( struct snmp_pdu *pdu, + unsigned char *packet, + int *out_length, + long version, + unsigned char* community, + int community_len); + +DLLOPT void snmp_add_var(struct snmp_pdu *pdu, + oid *name, int name_length, + SmiVALUE *smival); + +DLLOPT int snmp_parse( struct snmp_pdu *pdu, + unsigned char *data, + unsigned char *community_name, + unsigned long &community_len, + snmp_version &version, + int length); + +DLLOPT unsigned char *build_vb(struct snmp_pdu *pdu, + unsigned char *buf, int *buf_len); + +DLLOPT unsigned char *build_data_pdu(struct snmp_pdu *pdu, + unsigned char *buf, int *buf_len, + unsigned char *vb_buf, int vb_buf_len); + +DLLOPT unsigned char *snmp_build_var_op(unsigned char *data, + oid * var_name, + int *var_name_len, + unsigned char var_val_type, + int var_val_len, + unsigned char *var_val, + int *listlength); + +DLLOPT unsigned char *snmp_parse_var_op( unsigned char *data, + oid *var_name, + int *var_name_len, + unsigned char *var_val_type, + int *var_val_len, + unsigned char **var_val, + int *listlength); + +DLLOPT int snmp_parse_data_pdu(struct snmp_pdu *pdu, + unsigned char *&data, int &length); + +DLLOPT int snmp_parse_vb(struct snmp_pdu *pdu, + unsigned char *&data, int &data_len); + +DLLOPT void clear_pdu(struct snmp_pdu *pdu); + +/** + * Encode the given values for the HeaderData into the buffer. + *
+ *  HeaderData ::= SEQUENCE {
+ *    msgID      INTEGER (0..2147483647),
+ *    msgMaxSize INTEGER (484..2147483647),
+ *    msgFlags   OCTET STRING (SIZE(1)),
+ *    msgSecurityModel INTEGER (0..2147483647)
+ *  }
+ *                                                                 
+ * @param outBuf - The buffer + * @param maxLength - IN: length of the buffer + * OUT: free bytes left in the buffer + * @param msgID - The message ID + * @param maxMessageSize - The maximum size of a SNMPv3 message + * @param msgFlags - The message Flags + * @param securityModel - The security model + * + * @return - Pointer to the first free byte in the buffer or + * NULL if an error occured + */ +DLLOPT unsigned char *asn1_build_header_data(unsigned char *outBuf, + int *maxLength, + long msgID, + long maxMessageSize, + unsigned char msgFlags, + long securityModel); + +/** + * Parse the filled HeaderData of a SNMPv3 message and return + * the encoded values. + *
+ *      HeaderData ::= SEQUENCE {
+ *          msgID      INTEGER (0..2147483647),
+ *          msgMaxSize INTEGER (484..2147483647),
+ *          msgFlags   OCTET STRING (SIZE(1)),
+ *          msgSecurityModel INTEGER (0..2147483647)
+ *      }
+ *                                                                 
+ * + * @param buf - The buffer to parse + * @param buf_len - IN: The length of the buffer + * OUT: The number of bytes after this object + * int the buffer + * @param msg_id - OUT: The message id + * @param msg_max_size - OUT: THe maximum message size of the sender + * @param msg_flags - OUT: The message flags + * @param msg_security_model - OUT: The security model used to build this + * message + * + * @return - Returns a pointer to the first byte past the end of + * the object HeaderData (i.e. the start of the next object). + * Returns NULL on any error. + */ +DLLOPT unsigned char *asn1_parse_header_data(unsigned char *buf, int *buf_len, + long *msg_id, long *msg_max_size, + unsigned char *msg_flags, + long *msg_security_model); + +/** + * Parse the ScopedPDU and return the encoded values. + *
+ *      ScopedPDU ::= SEQUENCE {
+ *          contextEngineID  OCTET STRING,
+ *          contextName      OCTET STRING,
+ *          data             ANY -- e.g., PDUs as defined in RFC 1905
+ *      }
+ *                                                                 
+ * + * @param scoped_pdu - The buffer to parse + * @param scoped_pdu_len - IN: The length of the buffer + * OUT: The number of bytes after this object + * int the buffer + * @param context_engine_id - OUT: The parsed contextEngineID + * @param context_engine_id_len - OUT: The length of the contextEngineID + * @param context_name - OUT: The parsed contextName + * @param context_name_len - OUT: The length of the contextName + * + * @return - Pointer to the data object of the scopedPDU or + * NULL on any error. + */ +DLLOPT unsigned char *asn1_parse_scoped_pdu( + unsigned char *scoped_pdu, int *scoped_pdu_len, + unsigned char *context_engine_id, int *context_engine_id_len, + unsigned char *context_name, int *context_name_len ); + +/** + * Encode the given values for the scopedPDU into the buffer. + *
+ *    ScopedPDU ::= SEQUENCE {
+ *           contextEngineID OCTET STRING
+ *           contextName     OCTET STRING
+ *           data            ANY  -- PDU
+ *       }
+ *                                                                 
+ * param outBuf - The buffer + * param max_len - IN: length of the buffer + * OUT: free bytes left in the buffer + * param contextEngineID - The contextEngineID + * param contextEngineIDLength - The length of the contextEngineID + * param contextName - The contextName + * param contextNameLength - The length of the contextName + * param data - The already encoded data + * param dataLength - The length of the data + * + * @return - Pointer to the first free byte in the buffer or + * NULL if an error occured + */ +DLLOPT unsigned char *asn1_build_scoped_pdu( + unsigned char *outBuf, int *max_len, + unsigned char *contextEngineID, long contextEngineIDLength, + unsigned char *contextName, long contextNameLength, + unsigned char *data, long dataLength); + + +#endif // _ASN1 + diff --git a/backend/camembert/libkmsnmp/collect.h b/backend/camembert/libkmsnmp/collect.h new file mode 100644 index 0000000..2c7bf8a --- /dev/null +++ b/backend/camembert/libkmsnmp/collect.h @@ -0,0 +1,98 @@ +/*_############################################################################ + _## + _## collect.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ C O L L E C T . H + + COLLECTION CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + Win 32 + BSD UNIX + + DESCRIPTION: + Simple Collection classes for SNMP++ classes. + +=====================================================================*/ +// $Id: collect.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _COLLECTION_H_ +#define _COLLECTION_H_ + +#include "config_snmp_pp.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define MAXT 25 // elements per block + +// If you have problems with the collection code: +// 1. Send a mail to katz@agentpp.com with details about the used +// compile flags, compiler (for example g++ -dumpspecs),... +// so we can change the default behaviour for your system +// 2. comment in the define _OLD_TEMPLATE_COLLECTION in +// config_snmp_pp.h +#ifdef _OLD_TEMPLATE_COLLECTION + +#include "collect2.h" + +#else + +#include "collect1.h" + +#endif + +#endif // _COLLECTION_H_ + diff --git a/backend/camembert/libkmsnmp/collect1.h b/backend/camembert/libkmsnmp/collect1.h new file mode 100644 index 0000000..c29f3d2 --- /dev/null +++ b/backend/camembert/libkmsnmp/collect1.h @@ -0,0 +1,344 @@ +/*_############################################################################ + _## + _## collect1.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +// $Id: collect1.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +template class SnmpCollection +{ + class cBlock + { + public: + cBlock(cBlock *p, cBlock *n) : prev(p), next(n) {}; + T *item[MAXT]; + cBlock *prev; + cBlock *next; + }; + + public: + + /** + * Create an empty collection. + */ + SnmpCollection() + : count(0), data(0,0) {}; + + /** + * Create a collection using a single template object. + */ + SnmpCollection(const T &t) + : count(1), data(0, 0) + { + data.item[0] = (T*) (t.clone()); + }; + + /** + * Create a collection with another collection (copy constructor). + */ + SnmpCollection(const SnmpCollection &c) + : count(0), data(0, 0) + { + if (c.count == 0) return; + + // load up the new collection + cBlock *current = &data; + cBlock *nextBlock; + int cn = 0; + + while (count < c.count) + { + if (cn >= MAXT) + { + nextBlock = new cBlock(current, 0); + current->next = nextBlock; + current = nextBlock; + cn=0; + } + T *tmp; + c.get_element(tmp, count); + current->item[cn] = (T*) (tmp->clone()); + count++; + cn++; + } + }; + + /** + * Destroy the collection. + */ + ~SnmpCollection() + { + clear(); // just delete the data + }; + + /** + * Get the size of the collection. + */ + int size() + { + return count; + }; + + /** + * Append an item to the collection. + */ + SnmpCollection& operator +=(const T &i) + { + cBlock *current = &data; + cBlock *add; + int cn = (int) count % MAXT; + while (current->next) + current = current->next; + if ((count > 0) && ((count % MAXT) == 0)) + { + add = new cBlock(current, 0); + current->next = add; + add->item[0] = (T*) (i.clone()); + } + else + { + current->item[cn] = (T*) (i.clone()); + } + count++; + + return *this; + }; + + /** + * Assign one collection to another. + */ + SnmpCollection &operator =(const SnmpCollection &c) + { + if (this == &c) return *this; // check for self assignment + + clear(); // delete the data + + if (c.count == 0) return *this; + + // load up the new collection + cBlock *current = &data; + cBlock *nextBlock; + int cn = 0; + count = 0; + while (count < c.count) + { + if (cn >= MAXT) + { + nextBlock = new cBlock(current, 0); + current->next = nextBlock; + current = nextBlock; + cn=0; + } + T *tmp; + c.get_element(tmp, count); + current->item[cn] = (T*) (tmp->clone()); + count++; + cn++; + } + + return *this; + }; + + /** + * Access an element in the collection. + * + * @return The requestet element or an empty element if out of bounds. + */ + T operator[](const int p) const + { + if ((p < count) && (p >= 0)) + { + cBlock const *current = &data; + int bn = (int) (p / MAXT); + int cn = (int) p % MAXT; + for (int z=0; znext; + return *(current->item[cn]); + } + else + { + // return an instance of nothing!! + T t; + return t; + } + }; + + /** + * Set an element in the collection. + * + * @return 0 on success and -1 on failure. + */ + int set_element( const T& i, const int p) + { + if ((p < 0) || (p > count)) return -1; // not found! + + cBlock *current = &data; + int bn = (int) p / MAXT; + int cn = (int) p % MAXT; + for (int z=0; znext; + delete current->item[cn]; + current->item[cn] = (T*) (i.clone()); + return 0; + }; + + /** + * Get an element in the collection. + * + * @return 0 on success and -1 on failure. + */ + int get_element(T &t, const int p) const + { + if ((p < 0) || (p > count)) return -1; // not found! + + cBlock const *current = &data; + int bn = (int) p / MAXT; + int cn = (int) p % MAXT; + for (int z=0; znext; + t = *(current->item[cn]); + return 0; + }; + + /** + * Get a pointer to an element in the collection. + * + * @return 0 on success and -1 on failure. + */ + int get_element(T *&t, const int p) const + { + if ((p < 0) || (p > count)) return -1; // not found! + + cBlock const *current = &data; + int bn = (int) p / MAXT; + int cn = (int) p % MAXT; + for (int z=0; znext; + t = current->item[cn]; + return 0; + }; + + /** + * Apply an function to the entire collection, iterator. + */ + void apply(void f(T&)) + { + T temp; + for ( int z=0; zget_element(temp, z); + f(temp); + } + }; + + /** + * Looks for an element in the collection. + * + * @return TRUE if found. + */ + int find(const T& i, int &pos) + { + T temp; + for (int z=0; zget_element(temp, z); + if ( temp == i) { + pos = z; + return TRUE; + } + } + return FALSE; + }; + + /** + * Delete an element in the collection. + */ + int remove(const T& i) + { + // first see if we have it + int pos; + if (find(i, pos)) + { + SnmpCollection newCollection; + + for (int z=0; z= MAXT) + { + cn =0; + current = current->next; + } + delete current->item[cn]; + cn++; + z++; + } + + // delete the blocks + while (current->next) + current = current->next; + while (current->prev) + { + current = current->prev; + delete current->next; + } + + count = 0; + data.next=0; + data.prev=0; + }; + + private: + int count; + cBlock data; +}; diff --git a/backend/camembert/libkmsnmp/config_snmp_pp.h b/backend/camembert/libkmsnmp/config_snmp_pp.h new file mode 100644 index 0000000..a421cb7 --- /dev/null +++ b/backend/camembert/libkmsnmp/config_snmp_pp.h @@ -0,0 +1,142 @@ +/*_############################################################################ + _## + _## config_snmp_pp.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ + +// $Id: config_snmp_pp.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _CONFIG_SNMP_PP_H_ +#define _CONFIG_SNMP_PP_H_ + +//! This is the amount of variable bindings, a snmp++ Pdu object can contain. +#define PDU_MAX_VBS 50 + +//! The maximum size of a message that can be sent or received. +#define MAX_SNMP_PACKET 4096 + +#ifndef DLLOPT +#if defined (WIN32) && defined (SNMP_PP_DLL) +#ifdef SNMP_PP_EXPORTS +#define DLLOPT __declspec(dllexport) +#else +#define DLLOPT __declspec(dllimport) +#endif +#else +#define DLLOPT +#endif +#endif + +// define SNMP_PP_IPv6 if you want to use IPv6 +#ifndef WIN32 +#define SNMP_PP_IPv6 +#endif + +// define _NO_SNMPv3 here or in the Makefile if you do not want to use SNMPv3 +// (default is to use SNMPv3) +#define _NO_SNMPv3 + +// If you have not disabled SNMPv3, snmp++ will use libdes +// (separate package) as default. +// define _USE_LIBTOMCRYPT if you want to use libtomcrypt instead +// Note that _USE_OPENSSL will override libtomcrypt for SHA1, MD5 and DES. +// #define _USE_LIBTOMCRYPT + +// If you define _USE_OPENSSL, snmp++ will use OpenSSL for SHA1, +// MD5 and DES. +//#define _USE_OPENSSL + +// If you do not use SNMP++ for commercial purposes or if you +// have licensed IDEA (read README.v3) you may define the following +// to enable IDEA support. +// #define _USE_IDEA + +// define _NO_THREADS here or in the Makefile if you do not want thread support +// (default is to include thread support) +// #define _NO_THREADS + +// define _IPX_ADDRESS and/or _MAC_ADDRESS if you want to use the +// classess IpxAddress/IpxSockAddress and/or MacAddress +#define _IPX_ADDRESS +#define _MAC_ADDRESS + +// define this if you want to send out broadcasts +#define SNMP_BROADCAST + + +// Some older(?) compilers need a special declaration of +// template classes +// #define _OLD_TEMPLATE_COLLECTION + +// We have inet_aton() function if not compiling with VC++ +#ifndef _MSC_VER +#define HAVE_INET_ATON +#endif + +// If IPv6 is enabled assume that inet_pton() is available +// If IPv6 and gcc then assume gethostbyname2() is available +#ifdef SNMP_PP_IPv6 +#define HAVE_INET_PTON +#ifdef __GNUC__ +#define HAVE_GETHOSTBYNAME2 +#endif +#endif + +/////////////////////////////////////////////////////////////////////// +// Changes below this line should not be necessary +/////////////////////////////////////////////////////////////////////// + +#ifndef _NO_SNMPv3 +#ifndef _SNMPv3 +#define _SNMPv3 +#endif +#endif + +#ifndef _NO_THREADS +#ifdef WIN32 + +#ifndef _THREADS +#define _WIN32THREADS +#define VC_EXTRALEAN +#define _THREADS +#endif + +#else // !WIN32 + +#ifndef _THREADS +#define _THREADS +#endif + +#ifndef POSIX_THREADS +#ifdef __unix +#define POSIX_THREADS +#endif +#endif + +#endif // WIN32 +#endif // !_NO_THREADS + +#endif // _CONFIG_SNMP_PP_H_ diff --git a/backend/camembert/libkmsnmp/counter.cpp b/backend/camembert/libkmsnmp/counter.cpp new file mode 100644 index 0000000..007a28c --- /dev/null +++ b/backend/camembert/libkmsnmp/counter.cpp @@ -0,0 +1,99 @@ +/*_############################################################################ + _## + _## counter.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + C O U N T E R. C P P + + COUNTER32 CLASS IMPLEMENTATION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class implementation for SMI Counter32 class. + + +=====================================================================*/ +char counter_cpp_version[]="@(#) SNMP++ $Id: counter.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#include "counter.h" + +// copy constructor +Counter32::Counter32( const Counter32 &c) + { this->smival.value.uNumber = c.smival.value.uNumber; + smival.syntax = sNMP_SYNTAX_CNTR32; + valid_flag = true; + } + +// general assignment from any Value +SnmpSyntax& Counter32::operator=(const SnmpSyntax &in_val) +{ + if (this == &in_val) return *this; // handle assignement from itself + + valid_flag = false; // will get set true if really valid + if (in_val.valid()) + { + switch (in_val.get_syntax()) + { + case sNMP_SYNTAX_UINT32: + // case sNMP_SYNTAX_GAUGE32: .. indistinquishable from UINT32 + case sNMP_SYNTAX_CNTR32: + case sNMP_SYNTAX_TIMETICKS: + case sNMP_SYNTAX_INT32: // implied cast int -> uint + this->smival.value.uNumber = + ((Counter32 &)in_val).smival.value.uNumber; + valid_flag = true; + break; + } + } + return *this; +} diff --git a/backend/camembert/libkmsnmp/counter.h b/backend/camembert/libkmsnmp/counter.h new file mode 100644 index 0000000..bdf2be4 --- /dev/null +++ b/backend/camembert/libkmsnmp/counter.h @@ -0,0 +1,145 @@ +/*_############################################################################ + _## + _## counter.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ C O U N T E R. H + + COUNTER32 CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class definition for SMI Counter32 class. + +=====================================================================*/ +// $Id: counter.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _COUNTER +#define _COUNTER + +#include "integer.h" + +//------------[ Counter32 Class ]------------------------------------------ +/** + * The counter class allows all the functionality of unsigned + * integers but is recognized as a distinct SMI type. Counter32 + * class objects may be set or get into Vb objects. + */ +class DLLOPT Counter32: public SnmpUInt32 +{ + public: + /** + * Constructor to create a Counter object with value zero. + */ + Counter32() : SnmpUInt32() { smival.syntax = sNMP_SYNTAX_CNTR32; }; + + /** + * Constructor with a value. + * + * @param i - unsigned 32 bit value + */ + Counter32( const unsigned long i) : SnmpUInt32(i) + { smival.syntax = sNMP_SYNTAX_CNTR32; }; + + /** + * Copy constructor. + * + * @param c - Object to copy from + */ + Counter32( const Counter32 &c); + + /** + * Return the syntax. + * + * @return Returns always sNMP_SYNTAX_CNTR32 + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_CNTR32; }; + + /** + * Clone operator. + * + * @return Pointer to a newly created copy of the object. + */ + SnmpSyntax *clone() const { return (SnmpSyntax *)new Counter32(*this); }; + + /** + * Map other SnmpSyntax objects to Counter32. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Overloaded assignment for Counter32. + * + * @param uli - new value + * @return self reference + */ + Counter32& operator=(const Counter32 &uli) + { smival.value.uNumber = uli.smival.value.uNumber; return *this;}; + + /** + * Overloaded assignment for unsigned longs. + * + * @param i - new value + * @return self reference + */ + Counter32& operator=( const unsigned long i) + { smival.value.uNumber = i; return *this;}; + + /** + * Casting to unsigned long. + * + * @return Current value as an unsigned long + */ + operator unsigned long() { return this->smival.value.uNumber; }; +}; + +#endif // _COUNTER diff --git a/backend/camembert/libkmsnmp/ctr64.cpp b/backend/camembert/libkmsnmp/ctr64.cpp new file mode 100644 index 0000000..bd8e25e --- /dev/null +++ b/backend/camembert/libkmsnmp/ctr64.cpp @@ -0,0 +1,300 @@ +/*_############################################################################ + _## + _## ctr64.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + C O U N T E R 6 4. C P P + + COUNTER64 CLASS IMPLEMENTATION + + DESIGN + AUTHOR: + Peter E. Mellquist + + DESCRIPTION: + Implementation for + Counter64 ( 64 bit counter class). + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEM(S): + MS-Windows Win32 + BSD UNIX + +=====================================================================*/ +char counter64_cpp_version[]="@(#) SNMP++ $Id: ctr64.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#include "ctr64.h" +#include // for pretty printing... + +#define MAX32 4294967295u + +//------------------[ constructor with no value ]------------------------ +Counter64::Counter64() +{ + smival.syntax = sNMP_SYNTAX_CNTR64; + smival.value.hNumber.hipart = 0; + smival.value.hNumber.lopart = 0; +} + +//------------------[ constructor with values ]-------------------------- +Counter64::Counter64(unsigned long hi, unsigned long lo) +{ + smival.syntax = sNMP_SYNTAX_CNTR64; + smival.value.hNumber.hipart = hi; + smival.value.hNumber.lopart = lo; +} + +//------------------[ constructor with low value only ]------------------ +Counter64::Counter64(unsigned long lo) +{ + smival.syntax = sNMP_SYNTAX_CNTR64; + smival.value.hNumber.hipart = 0; + smival.value.hNumber.lopart = lo; +} + +//------------------[ copy constructor ]--------------------------------- +Counter64::Counter64( const Counter64 &ctr64 ) +{ + smival.syntax = sNMP_SYNTAX_CNTR64; + smival.value.hNumber.hipart = ctr64.high(); + smival.value.hNumber.lopart = ctr64.low(); +} + +//------------------[ operator=( const Counter64 &ctr64) ]------------- +// assign a ctr64 to a ctr64 +Counter64& Counter64::operator=(const Counter64 &ctr64) +{ + if (this == &ctr64) return *this; // check for self assignment + smival.value.hNumber.hipart = ctr64.high(); + smival.value.hNumber.lopart = ctr64.low(); + return *this; +} + +//-------------------[ operator=( const unsigned long int i) ]--------- +// assign a ul to a ctr64, clears the high part +// and assugns the low part +Counter64& Counter64::operator=( const unsigned long i) +{ + smival.value.hNumber.hipart = 0; + smival.value.hNumber.lopart = i; + return *this; +} + + +//-----------[ c64_to_ld( Counter64 c64) ]----------------------------- +// convert a Counter 64 to a long double +long double Counter64::c64_to_ld( const Counter64 &c64) +{ + long double ld = c64.high(); + ld *= (long double)MAX32 + 1.0l; // gotta be MAX32 + 1 to move it to next pos + ld += c64.low(); + return ld; +} + +//-----------[ c64_to_ld( ) ]------------------------------------------ +long double Counter64::c64_to_ld() const +{ + long double ld = high(); + ld *= (long double)MAX32 + 1.0l; // gotta be MAX32 + 1 to move it to next pos + ld += low(); + return ld; +} + + +//-----------[ ld_to_c64( long double ld) ]---------------------------- +// convert a long double to a Counter64 +Counter64 Counter64::ld_to_c64( const long double &ld) +{ + long double high = MAX32 + 1.0l; // look above + unsigned long h = (unsigned long)(ld / high); + return Counter64( h, (unsigned long)(ld - (h * high))); +} + +//----------[ Counter64::operator+( const Counter64 &c) ]--------------- +// add two Counter64s +Counter64 Counter64::operator+( const Counter64 &c) const +{ + long double ldsum = c64_to_ld() + c.c64_to_ld(); + return ld_to_c64( ldsum); +} + +//------------[ Counter64::operator-( const Counter64 &c) ]------------- +// subtract two Counter64s +Counter64 Counter64::operator-( const Counter64 &c) const +{ + long double lddiff = c64_to_ld() - c.c64_to_ld(); + return ld_to_c64( lddiff); +} + +//------------[ Counter64::operator*( const Counter64 &c) ]------------- +// multiply two Counter64s +Counter64 Counter64::operator*( const Counter64 &c) const +{ + long double ldmult = c64_to_ld() * c.c64_to_ld(); + return ld_to_c64( ldmult); +} + +//------------[ Counter64::operator/( const Counter64 &c) ]-------------- +// divide two Counter64s +Counter64 Counter64::operator/( const Counter64 &c) const +{ + long double lddiv = c64_to_ld() / c.c64_to_ld(); + return ld_to_c64( lddiv); +} + +//-------[ overloaded equivlence test ]---------------------------------- +bool operator==( Counter64 &lhs, Counter64 &rhs) +{ + return (( lhs.high() == rhs.high()) && ( lhs.low() == rhs.low())); +} + +//-------[ overloaded not equal test ]----------------------------------- +bool operator!=( Counter64 &lhs, Counter64 &rhs) +{ + return (( lhs.high() != rhs.high()) || ( lhs.low() != rhs.low())); +} + +//--------[ overloaded less than ]--------------------------------------- +bool operator<( Counter64 &lhs, Counter64 &rhs) +{ + return ( (lhs.high() < rhs.high()) || + ((lhs.high() == rhs.high()) && (lhs.low() < rhs.low()))); +} + +//---------[ overloaded less than or equal ]----------------------------- +bool operator<=( Counter64 &lhs, Counter64 &rhs) +{ + return ( (lhs.high() < rhs.high()) || + ((lhs.high() == rhs.high()) && (lhs.low() <= rhs.low()))); +} + +//---------[ overloaded greater than ]----------------------------------- +bool operator>( Counter64 &lhs, Counter64 &rhs) +{ + return ( (lhs.high() > rhs.high()) || + ((lhs.high() == rhs.high()) && (lhs.low() > rhs.low()))); +} + +//----------[ overloaded greater than or equal ]------------------------- +bool operator>=( Counter64 &lhs, Counter64 &rhs) +{ + return ( (lhs.high() > rhs.high()) || + ((lhs.high() == rhs.high()) && (lhs.low() >= rhs.low()))); +} + +//----------[ return ASCII format ]------------------------- +// TODO: Fix up to do real 64bit decimal value printing... +// For now, print > 32-bit values in hex +// 26Nov2002 M.Evstiounin - this method is not thread safe! +const char *Counter64::get_printable() const +{ + char *buf = PP_CONST_CAST(char*, output_buffer); + if ( high() != 0 ) + sprintf(buf, "0x%lX%08lX", high(), low()); + else + sprintf(buf, "%lu", low()); + return buf; +} + + +//----------------[ general Value = operator ]--------------------- +SnmpSyntax& Counter64::operator=(const SnmpSyntax &val) +{ + // protect against assignment from itself + if (this == &val) return *this; + + smival.value.hNumber.lopart = 0; // pessimsitic - assume no mapping + smival.value.hNumber.hipart = 0; + + // try to make assignment valid + if (val.valid()) + { + switch (val.get_syntax()) + { + case sNMP_SYNTAX_CNTR64: + smival.value.hNumber.hipart = + ((Counter64 &)val).smival.value.hNumber.hipart; + smival.value.hNumber.lopart = + ((Counter64 &)val).smival.value.hNumber.lopart; + break; + + case sNMP_SYNTAX_CNTR32: + case sNMP_SYNTAX_TIMETICKS: + case sNMP_SYNTAX_GAUGE32: + // case sNMP_SYNTAX_UINT32: .. indistinguishable from GAUGE32 + case sNMP_SYNTAX_INT32: + // take advantage of union... + smival.value.hNumber.lopart = ((Counter64 &)val).smival.value.uNumber; + smival.value.hNumber.hipart = 0; + break; + } + } + return *this; +} + +// Return the space needed for serialization +int Counter64::get_asn1_length() const +{ + if (smival.value.hNumber.hipart == 0) + { + if (smival.value.hNumber.lopart < 0x80) + return 3; + else if (smival.value.hNumber.lopart < 0x8000) + return 4; + else if (smival.value.hNumber.lopart < 0x800000) + return 5; + else if (smival.value.hNumber.lopart < 0x80000000) + return 6; + return 7; + } + if (smival.value.hNumber.hipart < 0x80) + return 7; + else if (smival.value.hNumber.hipart < 0x8000) + return 8; + else if (smival.value.hNumber.hipart < 0x800000) + return 9; + else if (smival.value.hNumber.hipart < 0x80000000) + return 10; + return 11; +} diff --git a/backend/camembert/libkmsnmp/ctr64.h b/backend/camembert/libkmsnmp/ctr64.h new file mode 100644 index 0000000..40b6999 --- /dev/null +++ b/backend/camembert/libkmsnmp/ctr64.h @@ -0,0 +1,318 @@ +/*_############################################################################ + _## + _## ctr64.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ C O U N T E R 6 4 . H + + COUNTER64 CLASSES DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + SNMP Counter64 class definition. + +=====================================================================*/ +// $Id: ctr64.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _CTR64 +#define _CTR64 + +#include "smival.h" + +#define CTR64OUTBUF 30 //!< maximum ascii string for a 64-bit counter + + +//---------[ 64 bit Counter Class ]-------------------------------- +/** + * Counter64 Class encapsulates two unsigned integers into a + * a single entity. This type has is available in SNMPv2 but + * may be used anywhere where needed. + */ +class DLLOPT Counter64: public SnmpSyntax +{ + public: + + //-----------[ Constructors and Destrucotr ]---------------------- + + /** + * Constructs a valid Couter64 with value 0. + */ + Counter64(); + + /** + * Constructs a valid Counter64 with the given value as the lower 32 bits. + * + * @param lo - value (0..MAX_UINT32) + */ + Counter64(unsigned long lo); + + /** + * Constructs a valid Counter64 with the given values. + * + * @param hi - value for the high 32 bits (0..MAX_UINT32) + * @param lo - value for the low 32 bits (0..MAX_UINT32) + */ + Counter64(unsigned long hi, unsigned long lo); + + /** + * Copy constructor. + * + * @param ctr64 - value + */ + Counter64(const Counter64 &ctr64); + + /** + * Destructor (ensure that SnmpSyntax::~SnmpSyntax() is overridden). + */ + ~Counter64() {}; + + //-----------[ conversion from/to long double ]---------------------- + + /** + * Get the value of the object as long double. + * + * @param c64 - The Counter64 object whose value should be returned + * @return value as a long double + */ + static long double c64_to_ld( const Counter64 &c64); + + /** + * Get the value of this object as long double. + * + * @return value as a long double + */ + long double c64_to_ld() const; + + /** + * Convert a long double to a Counter64. + * + * @param ld - the value to convert + * @return A Counter64 object with the value of the param ld. + */ + static Counter64 ld_to_c64( const long double &ld); + + //-----------[ get/set using 32 bit variables ]---------------------- + + /** + * Get the high 32 bit part. + * + * @return The high part of the Counter64 + */ + unsigned long high() const { return smival.value.hNumber.hipart; }; + + /** + * Get the low 32 bit part. + * + * @return The low part of the Counter64 + */ + unsigned long low() const { return smival.value.hNumber.lopart; }; + + /** + * Set the high 32 bit part. The low part will stay unchanged. + * + * @param h - The new high part of the Counter64 + */ + void set_high(const unsigned long h) { smival.value.hNumber.hipart = h; }; + + /** + * Set the low 32 bit part. The high part will stay unchanged. + * + * @param l - The new low part of the Counter64 + */ + void set_low(const unsigned long l) { smival.value.hNumber.lopart = l; }; + + + //-----------[ SnmpSyntax methods ]---------------------- + + /** + * Get a printable ASCII string representing the current value. + * + * @note The returned string is valid as long as the object is not + * modified. + * + * @return Null terminated string. + */ + const char *get_printable() const; + + /** + * Get the Syntax of the object. + * + * @return This method always returns sNMP_SYNTAX_CNTR64. + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_CNTR64; }; + + /** + * Clone the object. + * + * @return A cloned Counter64 object allocated through new. + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new Counter64(*this); }; + + /** + * Overloaded assignement operator. + * + * @param val - Try to map the given value to a Counter64 and assign it + * @return Always *this with the new value. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Return validity of the object. + * + * @return Always true + */ + bool valid() const { return true; }; + + /** + * Return the space needed for serialization. + * + * @return The needed space that depends on the current value. + */ + int get_asn1_length() const; + + + //-----------[ overloaded operators ]---------------------- + + /** + * Assign a Counter64 to a Counter64. + */ + Counter64& operator=( const Counter64 &ctr64); + + /** + * Assign a unsigned long to a Counter64. + * + * @param i - The new low part. The high part is cleared. + */ + Counter64& operator=( const unsigned long i); + + /** + * Add two Counter64. + */ + Counter64 operator+( const Counter64 &c) const; + + /** + * Add a unsigned long and a Counter64. + */ + DLLOPT friend Counter64 operator+( unsigned long ul, const Counter64 &c64) + { return Counter64( ul) + c64; }; + + /** + * Subtract two Counter64. + */ + Counter64 operator-( const Counter64 &c) const; + + /** + * Subtract a unsigned long and a Counter64. + */ + DLLOPT friend Counter64 operator-( unsigned long ul, const Counter64 &c64) + { return Counter64( ul) - c64; }; + + /** + * Multiply two Counter64. + */ + Counter64 operator*( const Counter64 &c) const; + + /** + * Multiply a unsigned long and a Counter64. + */ + DLLOPT friend Counter64 operator*( unsigned long ul, const Counter64 &c64) + { return Counter64( ul) * c64; }; + + /** + * Divide two Counter64. + */ + Counter64 operator/( const Counter64 &c) const; + + /** + * Divide a unsigned long and a Counter64. + */ + DLLOPT friend Counter64 operator/( unsigned long ul, const Counter64 &c64) + { return Counter64( ul) / c64; }; + + //-------[ overloaded comparison operators ]-------------- + + /** + * Equal operator for two Cunter64. + */ + DLLOPT friend bool operator==( Counter64 &lhs, Counter64 &rhs); + + /** + * Not equal operator for two Cunter64. + */ + DLLOPT friend bool operator!=( Counter64 &lhs, Counter64 &rhs); + + /** + * Less than operator for two Cunter64. + */ + DLLOPT friend bool operator<( Counter64 &lhs, Counter64 &rhs); + + /** + * Less than or equal operator for two Cunter64. + */ + DLLOPT friend bool operator<=( Counter64 &lhs, Counter64 &rhs); + + /** + * Greater than operator for two Cunter64. + */ + DLLOPT friend bool operator>( Counter64 &lhs, Counter64 &rhs); + + /** + * Greater than or equal operator for two Cunter64. + */ + DLLOPT friend bool operator>=( Counter64 &lhs, Counter64 &rhs); + + private: + /*mutable*/ char output_buffer[CTR64OUTBUF]; + +}; + +#endif diff --git a/backend/camembert/libkmsnmp/gauge.cpp b/backend/camembert/libkmsnmp/gauge.cpp new file mode 100644 index 0000000..6325390 --- /dev/null +++ b/backend/camembert/libkmsnmp/gauge.cpp @@ -0,0 +1,75 @@ +/*_############################################################################ + _## + _## gauge.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + G A U G E. C P P + + GAUGE32 CLASS IMPLEMTATION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class implemtation for SMI Gauge32 class. + +=====================================================================*/ +char gauge_cpp_version[]="@(#) SNMP++ $Id: gauge.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#include "gauge.h" // header file for gauge class + +// copy constructor +Gauge32::Gauge32(const Gauge32 &g32) + : SnmpUInt32() +{ + smival.value.uNumber = g32.smival.value.uNumber; + smival.syntax = sNMP_SYNTAX_GAUGE32; +} diff --git a/backend/camembert/libkmsnmp/gauge.h b/backend/camembert/libkmsnmp/gauge.h new file mode 100644 index 0000000..6a459f7 --- /dev/null +++ b/backend/camembert/libkmsnmp/gauge.h @@ -0,0 +1,149 @@ +/*_############################################################################ + _## + _## gauge.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ G A U G E. H + + GAUGE32 CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class definition for SMI Gauge32 class. + +=====================================================================*/ +// $Id: gauge.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _GAUGE_H_ +#define _GAUGE_H_ + +#include "integer.h" + +//------------[ Gauge32 Class ]------------------------------------------ +/** + * The gauge class allows all the functionality of unsigned integers + * but is recognized as a distinct SMI type. Gauge32 objects may be + * set or get into Vb objects. + */ +class DLLOPT Gauge32: public SnmpUInt32 +{ + public: + + //-----------[ Constructors and Destrucotr ]---------------------- + + /** + * Constructs a valid Gauge32 with value 0. + */ + Gauge32() : SnmpUInt32() { smival.syntax = sNMP_SYNTAX_GAUGE32; }; + + /** + * Constructs a valid Gauge32 with the given value. + * + * @param ul - value (0..MAX_UINT32) + */ + Gauge32(const unsigned long ul) : SnmpUInt32(ul) + { smival.syntax = sNMP_SYNTAX_GAUGE32; }; + + /** + * Copy constructor. + * + * @param g32 - value + */ + Gauge32(const Gauge32 &g32); + + /** + * Destructor (ensure that SnmpUInt32::~SnmpUInt32() is overridden). + */ + ~Gauge32() {}; + + //-----------[ SnmpSyntax methods ]---------------------- + + /** + * Get the Syntax of the object. + * + * @return This method always returns sNMP_SYNTAX_GAUGE32. + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_GAUGE32; }; + + /** + * Clone the object. + * + * @return A cloned Gauge32 object allocated through new. + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new Gauge32(*this); }; + + //-----------[ Overload some operators ]---------------------- + + /** + * Assign a Gauge32 to a Gauge32. + */ + Gauge32& operator=(const Gauge32 &uli) + { smival.value.uNumber = uli.smival.value.uNumber; return *this;}; + + /** + * Assign a unsigned long to a Gauge32. + * + * @param ul - New value + */ + Gauge32& operator=(const unsigned long ul) + { smival.value.uNumber = ul; return *this; }; + + // otherwise, behave like an unsigned int + /** + * Cast a Gauge32 to unsigned long. + * + * @return Current value as unsigned long. + */ + operator unsigned long() { return smival.value.uNumber; }; + +}; +#endif // _GAUGE_H_ diff --git a/backend/camembert/libkmsnmp/integer.cpp b/backend/camembert/libkmsnmp/integer.cpp new file mode 100644 index 0000000..fd914a6 --- /dev/null +++ b/backend/camembert/libkmsnmp/integer.cpp @@ -0,0 +1,245 @@ +/*_############################################################################ + _## + _## integer.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + I N T E G E R . C P P + + SMI INTEGER CLASS IMPLEMTATION + + DESIGN + AUTHOR: Jeff Meyer + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class implemtation for SMI Integer classes. + +=====================================================================*/ +char integer_cpp_version[]="#(@) SNMP++ $Id: integer.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#include "stdio.h" // for sprintf() +#include "integer.h" // header file for gauge class + +// constructor no value +SnmpUInt32::SnmpUInt32() + : valid_flag(true) +{ + smival.value.uNumber = 0; + smival.syntax = sNMP_SYNTAX_UINT32; +} + +// constructor with value +SnmpUInt32::SnmpUInt32 (const unsigned long i) + : valid_flag(true) +{ + smival.value.uNumber = i; + smival.syntax = sNMP_SYNTAX_UINT32; +} + +// copy constructor +SnmpUInt32::SnmpUInt32(const SnmpUInt32 &c) + : valid_flag(true) +{ + smival.value.uNumber=c.smival.value.uNumber; + smival.syntax = sNMP_SYNTAX_UINT32; +} + +// overloaded assignment +SnmpUInt32& SnmpUInt32::operator=(const unsigned long i) +{ + smival.value.uNumber = i; + valid_flag = true; + return *this; +} + +// general assignment from any Value +SnmpSyntax& SnmpUInt32::operator=(const SnmpSyntax &in_val) +{ + if (this == &in_val) return *this; // handle assignement from itself + + valid_flag = false; // will get set true if really valid + if (in_val.valid()) + { + switch (in_val.get_syntax()) + { + case sNMP_SYNTAX_UINT32: + // case sNMP_SYNTAX_GAUGE32: .. indistinquishable from UINT32 + case sNMP_SYNTAX_CNTR32: + case sNMP_SYNTAX_TIMETICKS: + case sNMP_SYNTAX_INT32: // implied cast int -> uint + smival.value.uNumber = + ((SnmpUInt32 &)in_val).smival.value.uNumber; + valid_flag = true; + break; + } + } + return *this; +} + +// overloaded assignment +SnmpUInt32& SnmpUInt32::operator=(const SnmpUInt32 &uli) +{ + if (this == &uli) return *this; // check for self assignment + + smival.value.uNumber = uli.smival.value.uNumber; + valid_flag = uli.valid_flag; + return *this; +} + +// ASCII format return +const char *SnmpUInt32::get_printable() const +{ + char *buf = PP_CONST_CAST(char*, output_buffer); + sprintf(buf, "%lu", smival.value.uNumber); + return buf; +} + +// Return the space needed for serialization +int SnmpUInt32::get_asn1_length() const +{ + if (smival.value.uNumber < 0x80) + return 3; + else if (smival.value.uNumber < 0x8000) + return 4; + else if (smival.value.uNumber < 0x800000) + return 5; + else if (smival.value.uNumber < 0x80000000) + return 6; + return 7; +} + +//==================================================================== +// INT 32 Implementation +//==================================================================== + +// constructor no value +SnmpInt32::SnmpInt32() : valid_flag(true) +{ + smival.value.sNumber = 0; + smival.syntax = sNMP_SYNTAX_INT32; +} + +// constructor with value +SnmpInt32::SnmpInt32(const long i) : valid_flag(true) +{ + smival.value.sNumber = i; + smival.syntax = sNMP_SYNTAX_INT32; +} + +// constructor with value +SnmpInt32::SnmpInt32(const SnmpInt32 &c) : valid_flag(true) +{ + smival.value.sNumber = c.smival.value.sNumber; + smival.syntax = sNMP_SYNTAX_INT32; +} + +// overloaded assignment +SnmpInt32& SnmpInt32::operator=(const long i) +{ + smival.value.sNumber = (unsigned long) i; + valid_flag = true; + return *this; +} + +// overloaded assignment +SnmpInt32& SnmpInt32::operator=(const SnmpInt32 &uli) +{ + if (this == &uli) return *this; // check for self assignment + + smival.value.sNumber = uli.smival.value.sNumber; + valid_flag = uli.valid_flag; + return *this; +} + +// general assignment from any Value +SnmpSyntax& SnmpInt32::operator=(const SnmpSyntax &in_val) +{ + if (this == &in_val) return *this; // handle assignement from itself + + valid_flag = false; // will get set true if really valid + if (in_val.valid()) + { + switch (in_val.get_syntax()) + { + case sNMP_SYNTAX_INT32: + case sNMP_SYNTAX_UINT32: // implied cast uint -> int + // case sNMP_SYNTAX_GAUGE32: .. indistinquishable from UINT32 + case sNMP_SYNTAX_CNTR32: // implied cast uint -> int + case sNMP_SYNTAX_TIMETICKS: // implied cast uint -> int + smival.value.sNumber = + ((SnmpInt32 &)in_val).smival.value.sNumber; + valid_flag = true; + break; + } + } + return *this; +} + +// ASCII format return +const char *SnmpInt32::get_printable() const +{ + char *buf = PP_CONST_CAST(char*, output_buffer); + sprintf(buf, "%ld", (long)smival.value.sNumber); + return buf; +} + +// Return the space needed for serialization +int SnmpInt32::get_asn1_length() const +{ + if ((smival.value.sNumber < 0x80) && + (smival.value.sNumber >= -0x80)) + return 3; + else if ((smival.value.sNumber < 0x8000) && + (smival.value.sNumber >= -0x8000)) + return 4; + else if ((smival.value.sNumber < 0x800000) && + (smival.value.sNumber >= -0x800000)) + return 5; + return 6; +} diff --git a/backend/camembert/libkmsnmp/integer.h b/backend/camembert/libkmsnmp/integer.h new file mode 100644 index 0000000..5158e91 --- /dev/null +++ b/backend/camembert/libkmsnmp/integer.h @@ -0,0 +1,271 @@ +/*_############################################################################ + _## + _## integer.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ I N T E G E R. H + + INTEGER CLASS DEFINITION + + DESIGN + AUTHOR: + Jeff Meyer + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class definition for Integer classes. + +=====================================================================*/ +// $Id: integer.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _SNMPINTEGER +#define _SNMPINTEGER + +#include "smival.h" + +#define INTOUTBUF 15 // largest ASCII formatted integer + +//------------[ Integer Classes ]------------------------------------------ + +/** + * 32 bit unsigned integer class. + * + * The integer class allows all the functionality of the various + * integers but is contained in a Value object for consistency + * among the various types. + * class objects may be set or get into Vb objects. + */ +class DLLOPT SnmpUInt32 : public SnmpSyntax +{ + public: + + /** + * Constructor, sets value to zero. + */ + SnmpUInt32(); + + /** + * Constructor with value. + * + * @param i - initial value + */ + SnmpUInt32(const unsigned long i); + + /** + * Copy constructor. + * + * @param c - initial value + */ + SnmpUInt32( const SnmpUInt32 &c); + + /** + * Destructor (ensure that SnmpSyntax::~SnmpSyntax() is overridden). + */ + virtual ~SnmpUInt32() {}; + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_UINT32. + */ + virtual SmiUINT32 get_syntax() const { return sNMP_SYNTAX_UINT32; }; + + /** + * Overloaded assignment for unsigned longs. + * + * @param i - new value + * @return self reference + */ + SnmpUInt32& operator=(const unsigned long i); + + /** + * Overloaded assignment for SnmpUInt32. + * + * @param uli - new value + * @return self reference + */ + SnmpUInt32& operator=(const SnmpUInt32 &uli); + + /** + * Map other SnmpSyntax objects to SnmpUInt32. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Behave like an unsigned long. + * + * @return value as unsigned long + */ + operator unsigned long() { return smival.value.uNumber; }; + + /** + * Get a printable ASCII value. + */ + virtual const char *get_printable() const; + + /** + * Clone operator. + * + * @return Pointer to a newly created copy of the object. + */ + virtual SnmpSyntax *clone() const + { return (SnmpSyntax *)new SnmpUInt32(*this); }; + + /** + * Return validity of the object. + * An SnmpUInt32 will only be invalid after a failed asignment + * of another SnmpSyntax object. + */ + bool valid() const { return valid_flag; }; + + /** + * Return the space needed for serialization. + */ + int get_asn1_length() const; + + protected: + bool valid_flag; + /*mutable*/ char output_buffer[INTOUTBUF]; +}; + + +/** + * 32 bit signed integer class. + */ +class DLLOPT SnmpInt32 : public SnmpSyntax +{ + public: + + /** + * Constructor, sets value to zero. + */ + SnmpInt32(); + + /** + * Constructor with value. + * + * @param i - initial value + */ + SnmpInt32 (const long i); + + /** + * Copy constructor. + * + * @param c - initial value + */ + SnmpInt32 (const SnmpInt32 &c); + + /** + * Destructor (ensure that SnmpSyntax::~SnmpSyntax() is overridden). + */ + virtual ~SnmpInt32() {}; + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_INT32. + */ + virtual SmiUINT32 get_syntax() const { return sNMP_SYNTAX_INT32; }; + + /** + * Overloaded assignment for longs. + * + * @param i - new value + * @return self reference + */ + SnmpInt32& operator=(const long i); + + /** + * Overloaded assignment for SnmpInt32. + * + * @param li - new value + * @return self reference + */ + SnmpInt32& operator=(const SnmpInt32 &li); + + /** + * Map other SnmpSyntax objects to SnmpInt32. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Behave like an long. + * + * @return value as long + */ + operator long() { return (long) smival.value.sNumber; }; + + /** + * Get a printable ASCII value. + */ + const char *get_printable() const; + + /** + * Clone operator. + * + * @return Pointer to a newly created copy of the object. + */ + SnmpSyntax *clone() const { return ( SnmpSyntax *)new SnmpInt32(*this); }; + + /** + * Return validity of the object. + * An SnmpUInt32 will only be invalid after a failed asignment + * of another SnmpSyntax object. + */ + bool valid() const { return valid_flag; }; + + /** + * Return the space needed for serialization. + */ + int get_asn1_length() const; + + protected: + bool valid_flag; + /*mutable*/ char output_buffer[INTOUTBUF]; +}; +#endif diff --git a/backend/camembert/libkmsnmp/libkmsnmp.a b/backend/camembert/libkmsnmp/libkmsnmp.a new file mode 100644 index 0000000000000000000000000000000000000000..92d555c43b1e0751435456231a71cdeb20169dd8 GIT binary patch literal 211856 zcmeFa3t&{$wKsky8DPZd1dM@3;2joPGA38ML+E zzy1C_8rH16e|zn<*Is+=$2oK6!r4_#E7x2x^0G|l6$s=_&zo_1?z9=1nK>*={Wmi+ zaCuHHPgy?FFj6)cM!~QD_4h9u{QueqJn4@b|B!!+dJNAe@mFIQo`3YewsgbuxBh*r z)$shY{q0;}c>dY{-nq)~{6F%Sl6kA~5C3=lBqQZte}9_|{&a(p@(KN=`X(9wpnsKL zSQN-9tFLP;ySYACwV|r4c=7zQN(0IBFP~Fg-Bi=uJZ)Xo+S=;kb&kZqlr>e&Yf3`( zD}%KS^?GJ*ZFA-7n)=ErE!e zv*XpC*SH~>+DdjpwG~VPwQYn$Z9CajZ96+o?Of!QHLSczs-8$U9p|o}hEjFZvfT94 zAaT0p%&n=Hdb)XrlAWj-stETcCA|Mjy2|xyYJ)YE&5cznYl=%*Eq1^_1Qj*gX}NV( z%{N(rRUM!<8u7p$0B+G$4bw$AO!Xy8)kJEo$`=KA`_$&K_%2(+3)mg21bF0 z8ft_S2I3ISn0Evs$SkfQ6fGTd2^%7f4NX$8GE>bUnJkM-RI->f+XTFe?Kw`VEKXDR zac9d41nR09D}xP~Mb}N2E@_K)$(9aU$US(;Qkg(%$xwfHREJ)OJNRfC)vNc{E6|!}g>ST6xq^#|H zq%1taPH@(5r>pgAPQ_%aG9VPSXeeeDEsY7)N-ok1+NwiSO@KpNEl<^0X&}T|-Qj4S zTdHMU2a;P?uTEn1bm2L+Z`%!8veZsxJz=wI%j{G~o9yAhB-$u$e`TOD+bcX1!dI0vj$2!^*xF9;zF8a8Q&z`eR>xs#zND8@ z<&*_zQ_t8vu!dZz>(Dr!C+Eg z!imJTTLMVjpmCka9Mgd-!d5yky$X9Yiq5N=YHRAN*WRpmH3W#9%KEChn&QQ^+yxy0 z)sAOMGKEDMtzFAFGnJ@ECF4W50-y*Q4^T$7y95Q`Cc!of8-;oWO7U`tTa;)ahz*|u}jhOdX# zG&Bcs(6CsY6q(;>VOYx4XAsy$>ZzBIEptXjiB@drGVrY6kjIA#9WunZGpqU|Az3#k zM%ppJ)d%BZwdw#|D_eqPD7D#&n<+>a1G)NZoXy=vf)4sZ)KB;nuEQvzAPo%WY1{OvH?49HCmm0$k6_St3WSt7@ujXoQG5 z>YcJmz7CloGFOy31W*mrSmCNra1B?O)eZwN3*^|6oh8>oE<;S=#jC3;QL}=nt1X>T zR#&@p&Vq6ZVtFgq)<83|f~5h~K9v<;)l^dxpT{*rk;;`#HC4eHRv)uFU;kLXRtHJE z>v=|e*Oyh6y7fJWJNh0Lt1Qc*^<98D6NgAAV+-5>^9!C~`2}jI6Xg^YZKvlYc~9}8 zYEQ9#DwVy(2NXxy&Sh&R{7V2d|AG>-1$>Ngm}(R5i0UBY7%|Au6gJ9)fAT!dKTF90 zEPeA>FLGeKt1d2G!`5=5vpH;?;))hP@kNH>ilRlOY%p+ddFy#!j(UWY`8;y1`i&&9_-~d1j}o8!oFPQ%nTKxarR>iwc&MIoVTA83~OoA7k@*hSC^+WXn9pPoytR{(+HI*JF*B)M&{ap*G!Q)xxI zX%895$9ABGq$|Z5*CJ8ueC;B2wXUhI*y?o_))wIER7$l8?kK4<`CsLB({rWUDV3=% zr6jxEUc>g-P#iQ!uQP^Ya1(mK=x={9sjaEjyo#-+wrsraq={_{Y zEky^(U5OZ9FeROzi}3~La)zTN^K3Q9r>zmje41996qWe>QkJhJBphK0!`bC?t3s=5 z+=7|RQ3H%jBY&5l6?3WDd@N6svMfK7396ULG$wg9sU2rM31PNTnNP-YF3~BWVe_b` z*!1C2%Fa|{nkoxc=TS{fB(tl$-8GJ68OT(qYwU;vSf_RNM|D2uf*{U`?x60PeA&w8+b=pQ){^L?T2G2cVFJ`qbjYj~RMh#T2aQz|h6grq z{LI#_KX5rcwPHPAQ@N&QLmb3y6j)*#7Zk4{gIWpf)*h zffyGUEEo#|4(_j?%%@&cTkSI5DOnxr#0wW#vXmG$!0AK$*rvNcl*5NL*~;d~%qd6Afiy0O zdP4dT&yt?OIq6ZHV>-D;J*r5FNP=bIl0N6s(?i&>G}Knd4K>PVqfCDI>j#)TLRLqU zbtBI0(IPw3?dhk$baI%Qrly7_gvP31sJV1%X?2Jn#oAf5_0=^S?3@|=WHGK7+8DT8 z&9lolqX}_cpfcEmXHmqB1-jE@-zB4g zz_8O9bVc#vDN9#SBNs6N?3uWAE7sOF2a8J`IeDaVoM_9YBtiCw^iPWyvk=uF#u8#3 z7)zN1(TEigQJ+4O(?Kbztu^qyDxqz2z-P&a=zSN$7BEM@gnUayNyLpHC!j2U8V$@vEN>TAy!Pc zbFltwu0cbr;hG?yyf#6W+{o}#WRl#h3Unru9A}CwZrj>5Y}?tEs&*1dwK*wqzMbT- zjqDILF%Y25+CtpQtXf@@ySAYUb2(c^PRXG=ELaZU3Hzd&=9Nvgjll-otx;~!*r9`m z3dGKadK{()m#)Bif|Xo6A7fQ3Bo35hA4;vH_AVOHxKnm8&SQIKS+y0@L-nj3i&-(t z!L`$V6vHSLy71n?V>H!e!K$X9-HqroN2-X4fhp^&n(E@Qj#P;atgWrTsi zG&C!2yC~q6$ID?#a~OkJ&}j*tq_&Nv*v(*UVhwqD7ZnT|ymig1<6?8XN@ej$^PD4( zY5_{d18P1c0j2hzK

I0($ex;!@^08(ijO=Ou4{S@P_8Zau_xb+a3_D?$8Xg_7jW zv%?63I@wdtf%BBng=x!Nmbu~+BSKibdKj-9Gd9Gphubz@@xiWyw zx0CcO5Pwq9(gbT8u@%P2sFez1Hn8?wja`$F0GtKZ+U9n7T&<`K28!CuZD0l?CaSrrM&+K#3jzO1fV8WOUat|zLFzZqF zlFCb`lg5^_sZ6+a@=Nms(iw zvY3YVR^;SNRsNlxo12+AZ3-iJ%f<4Mn~E2dcsw3MULGUWusOrnx{BxIGBVNlz3~Ri zzY#p)Dg|9GXqBKV1YId;wV?PmJu63Isr+g|*9clG=obaONzk=|)(KiKXoH}Qf__QR zCPAA84GJ0(be*8<1;qG#@R|E|U+9qg3(AxyP zUC?$xO+llA-X-YWf_`1ldjPI)4+;9PppOXp z6G0ypbi1HC1bsr#rv!ak&<;U&3A#tnp9|V8=w3nh3;K+pzYz47f*ug`IYAEzikGpl z{(C{t!-D=+&?ADrBL``PlkDT85Mc-Vu26$k25O83YpXtB>MnT;~QpDys@+Dk^YZX9ITxoa*Ml$@DfrIp5oMy z=_CCkjroTUHp_^nlF2MTN$)C2_m~5APm5N0!-L+?>)|i^hKGI^Elh&}myY&7mhVMj z&nmNIOkejF=I_4f!nC~oLBD_doKbBZ?FFMkzOI6FPlb74cXZBZvjBzWj9FzCjOjZH ze7K*?iQY4VSzh!b6hg^DDp@irG)m|K1hSdy7LJaUjgBoEZI+Bi*O+s>{>KV@tIRpR zzC%?1C(yo;XkS?xs(?qRmGFk5qNiQPn{Cf?yKeg`cQ{hsVOqaxRt$5+vcX~D!TiuG z(Lx{09Gn{-9FF|pmEpm3${8K{d7Mho0v}opuOz*?OML05)SQ#HgC;)nwjTW3w}tx2 z5cyt8FBu)^;F-OR@Lt&vN4!CA_*%a_$_S-*70xs;>W%Q8EJPXeI67u5{AxVt7|?N` zXM<*dW`L%Ho&}l)IvUhR@!P-$Z>(@8h8uH7RUY~a!#7u{Z|kVKji&oCi{((;Z4%lz9*MsZYp)$C#Z+jBAC&ENXBU+ZZ*7;I=iFoyyD*!p%=>j`9^jqJWx{ZALa(AncnDgI6S z+|ghTGv>GLbI$_vB4duU&xJ0|Ar?-z3tqb1U-r3rl)B65t>-r{L*Phb%doe%F5d9GvM3*=_cz)oB9N+$@z1Hw=_s%NJdpXqK+VbY5{8uZQ1^hegiRQ{(Akgd%R@^%IN<_BA-KMVcaQ}Ygl2D?%X zS#@~O=a0+HYliFT@CEr{xV}`XCAOg)D&X^JH1!u#U2QdyVgp;JTU+saH zq+kpLM_FF-HoT<3)AtrC5#mcAeheZI6JG-HW5F=-B@mBEpoEDpf%tR4FqKM9YcKc| zn2}U*$?5F{=W-wY{$DT~mLp1G1(6QM77)Mk+* z_A?rjY`sBf|6|2zgrJ2EmNw(}0ms+JkjB>I_WIxw=CHyQtSFM31?dzPnTP00Fi~rH z;2?Gs#lF@JSf7rKZs~yzk%OUe;f^u>#|*CG0a|DYe+YL>=zGh#pNKAG%0}-NWz%EV zj$xY5A^|2c*uAUq-bktqvTFbOf^k2Ol4aj4s2-H|EfNd;aR2}aAz17Zux#|N{L`DUQwI?TN%yGKAbqiZf!&2vW?;qKALepu<;4}V-d z^Ix);R6SNWx;vF>=`QpR<2vGBSShWma1;{RRfKE2mpKW9X=^y@9)Ka`xBEF?oU(C3#42`$Whp5xoO$fVV0%QQtwPG z&&)z>=SpZh=M_Ed=wFJae_#1Y`bV`a?_W}pb*#gliTc;Q{1=@4k*s}Kw@P0VvM4CP ze)HjNGYzugSiZ!C8J(M1-I$Ns=R1k*`S+*~_Qlx>ojp-YLch(~8CyofV9t{W}|DwmyGrdGbFrTlVS4 zbQEp9Cj9Rclc%g_q)aRqa84Me!(hG{Rs(51_2GPE_uvCXSp-vc106aY`(JsU7(;-i zMMDNV_vHpivVRWkNwWP#zu+8)iB``y{@L{x{;tI*u{L#tlXE((btNJLyr?Uji)G48 za@g)-iVTKX;lq*ovWi|h+2!ZAe{cCt`&+RqTHgCRWqs(u2klSOwfz1QIYch_FgIsLGu?Mm5N74S6j|d@gteeEn zqZhE^yUW>TKr3>NTaot;ScRPXRYz@!^#6|p`ZMkI{rWh5XN<3UaUS#;v6sqZyBJ{< z+SmB+*`@AS?DP6@Y~gEc+UlY4yLr69e(U3who=A0 zfqNsa`^CRb-aQHBUAMGsbN^EqSU3QN@y?KeC_FfO-5G(7&3hz8iRyn?2LtmWh}j-bp#B{=4Rb z_@5q-%FwC)3@VwM6&PXzG|FV)RCZtv7iaFI3UEU2DQp9}CB-miPY!*5C_?pzchbL* zdkhC>(Fj#?Rd_JPfBWmi-%EVtp#RR9D1>9uf$-6s@Zf3w+kcMS*unv{Dt0{}eWP&F z`xz_uzmQvy#|nqu;zDEP0|@0^MZ*Rt5aNc*4z>s@%JMOm#};!}EPv>8AEG<_x35H_ zqcJim8Ht`t)^V*B14J-=3KNQ6MjU{Ich@-|=)Mg!#D@-WiBC}A&fXt6 zxN!m*1DcUB#Pr1Phj$fC95C|pc7<>!s(2u}lNv?l^*kPs_AxGo!D?+{|K2ovXzo3lZ zNi5-edr8huN#z}%K|>x#9~&pl?qlEgN8UUss|XCSBOz9u5I~kTnLje~e+ObldcQ`V(lTJ~qgjV3>PQK_%zucxrxw+qpYW* zJ8wX4_Mfr`J$tivM~-Y9%hLk~3&`YoFNWUp+)rH^*|%|g0B6+RDN)9^dq0Jz8$$1R zo-r98{V7AipqJ60i7>IOittX#N%u$2CT+@nPa@yoz4*U+@DWFAq)+eQiG-eTgtA!g z+kU{!00Z=%K<`HHzm=3cvhjL8DxP#1XXg#fr*SsjWt^=)O^mU=gTjlDz`Urhll4m9 z6Z&X7n|Is&)a7D;9r!2%OzXbh*`z%ma?DV@9ptQtRQEDx-4AEQc^c-AaQ}k1$>aQW zBLkSk7qFQlR&kI`6=KgvGY6fIim6%pRd~?9{uy!CU^ixf7tQ0dC;RW|z%=OTl(*3S z5Pm;3^gU>?gWaSZpVeJFfIc#?0<)ixh;*2{gRR}gGABB{e9sTT-Gi_GnmN9bNbojy>C# zp34@&y$>Nz4Ms0hI0~MI7Lw=oeKlJA6x_J0D1Efidkci){>MJ^&CUJobVQ8%ogp8) zDT>w1yTWYji3a|7gLx;d!&%eBX$pqsXr_hpb}8MDvk;+|W5xYX7*s(~RtAC%NP4Vd zPb_lJdx(QLEAKAuhg{0$y*`_-QlsSqm^$0&2{AnKK#xUyj-E%kD_Vf!_e71TVHO`% zTm(S!Fbo^HQ65UrW?x81rZb0=|RihW!)LQ(; zjvTI4n&^?>cIK2;%Yxq*x3;Oh!d00ydjCMdG)JQ& zr<1-Aa2r`1`qO-1-(T8b-7n=G7CAz_q~626X5AQh8$-E|P0RY0ZLATZI<+&+_oJS0 zr#H4`=)(^`eDA=8yEebpiYwUr-mcbG!w3!Ud^=5x{6 z+h$+pZ@V9g;&%3Rvn%{#%Jz&Eugzu^8R= zHmYqg_iyP9p)ktzJ=b*`?foy4GJ@A*&>n&5mC3AVrl$+dF&wdnJF~f)If4JNL(sXG z?(P-@Iv^|3ff&2Z=qlcp>2JFL$$bwn7wCJ7*Eh58n~ay-ujIzhzQ5ykzlA-Fb9Psl z9@}Kk$5{J@-7-wIemUL1J(*BetZ){Z;oC78P;#XhD}`{HCY`cxr`U?*Zs~hY9E7X& zPqa2Kws72TEQ>~aATzQrc%z-pD)mR`Iny2NP8cjmPoj_pvn&nXpijXp+6pnd^_?lI z?Fv)YDVlU81U?~z`*!5UOGVJIBtL4kjr^^5HTpoN!0q}T)!O?6@`otoebWwk74IVI z(G!6FHkuIVNW~wS0&l@HC5RVfRA~TyyeA?SZ64?W*;vbA_%#b&B6_e!v#KzK+=Ni@ zq?fm1JdX$CWIV{Q;z4?>cn@l_-BBH<(c%HBjGop-*rQJ7hl1ALRk{@dG2L*JPp)i< zwH#$bPRvC@rQl&6oj!s#w5VmBD;i)&DyCATj_h6DZ~hxas=ik7oM~=vCq*oFo-WRF zTAyU6Goj(kx5q~LX=!j&rg6tgT&#OUCex{RN^(EU5y z-yLAxiBq+hlpm=XXSh~iibbNbD)@FcbSE=@ffIqtreb_#lbr17CaCwn=PIJ zEo9Gt&i6%^rCBFx!jwWNuU~p2-9L2K*VYr7MTa|YMp9!iWAdkV&va@nEJ{zYenHV5 znnO*;Q>;S$Zu?nRuZac#I6o~~>Wk*5nPn7R`Gk|kN2X|osKwX!U(N>oKiD2M-eOfM zJP7=6CJ%-{WFms)jX5VkL`dgOotoubbIf0^bthT80C>qoDEtYqQxWo`BnRV zlk{=X6~bTsZ|RfFe>K~%g_7`FG(I%K=}i-_+7dipm;eqF2VJ+P2 zDNf@_>=@qZfpPZw=hRQ$JQa6-dViEwfloOLFI0vX{Jow)_v1c_*b}pqP7*v`!F~u z$z%K@uz2U8dk)P=A@w-jpLIJ@s0WY552C_W1fqaE4^s)BAJ&`M*dG{c^ymr ztp^()J1CnUUh*6~4+kBeV9D$lO1iSOp8RC(C2w^liAT!B4U?$;$m2S0KkwFJl70V8 zoS%)SFFduRvk@2ciTi2k;h|yvdy0>>wj47;Gr~iu!KvXPPjFp$D8=9QI1CdR^0$qo zeazgGcoy1!+h%+gUycgz8BeCdT^lj_#Q@Z9t79v5Bdupxd_z3(N9fC)ni^DpFyb7@ zr<8LL`nkyQw_Sm70$EDPIi)PNFJj;6vP?rd`P)7~>~Y*rfMANCAFZ*WfQWEUL5#F{ z?~jPVvPJB@R?)_+4{6LFVOHj>%(6P_^JrP$?}VMWS|3r72ZSP5s)|ejV|G~sNDQ6? z>m{7*NdO)(DZvAY>D|L=lysNy$0AFJk=_A6WeR1@xu9`x1Yt z4_|D;7aP;XcL~K;_~>zJeAO|X&w#h}fbA`(^%gqT`Z^{*o);y^O!VbU+?V6?YXat+8EnpnTz03wZ>v+rUABxZ6abYORqX*e zl*LMbpm=Pmw-nVr$rcAp17~>Z8=&>&=KevnSvz8P`POU(a~^zaR$1zA5z4V|&b|^& zjqMyq?S$>pv6LlUS-@}InT=#4bQZo_n-)CxXG|dm?R!u?A@6(B&0VOY>D#Y+`S}{H z*8bI>rfYwqBb`_N#7}2umMvOX`q|HBX3eXfnJHg(ffwaXT9+A^%>K)ql9My-vYe@x z1#-z?D zo{yCsA1}FIz!5l>h2s16^!%DL|MPY})gRu`0}bR|lypm-`1*F_18BY}RTBC;%a2yd z^Y%qsNj{RxcTik7MfnUzdBpd4{bTGt0DbQS;*<;exZ=Mc3b^3o+pp@sgB@Vo?bY?? zk=o8rP`_etSN*6vSbyzc)08bQu0G9vJJ`0&#wVyB{xItl{=oD3b3wK|K5;1gnXtB_ z|J>WdZX$iG{-JwS@%^c{Z%3+LKl(3PDo?V@eHGEGyhE>6?4a-JF#nIwA5Zzzug?6D zSfW#TAF{(8`OLGpe{1qHZGdZj7V-lfvb?a5Jqv|Kex7NE#^UStbUy(txOyUtHS}sZ z%VCGQw0j7#`qG|6JI!%VRv_&iX!gezS2aN2;GkPP?ehW5h%ujl1g7dpySdGS9*JrVlGJ z7IJx`vcqWLPd&q8sm6ZK^Y{#PRDf@M>J(!Z2RoHcI}52HgeP605?&Sux0+rVDPNF09qP3C=~uUS6(pQJ7yJNgw{Gwo?(i{KxP|Hvayp z@zQSpqW`kM$F+dtt>!4q4$y>A9tJf#JE8eVH0bXaS6JV&Xu19_;wuoQUF|cbU(l#K!1NqDJ ze8n#J0Yld${5je!`sS%qot3w|>enN)qUI1M&|b-|1ULR_wTy zYT|ksDDEI06 zfXl(4XhIoV9>cM*lZo^WsuP&k5@4vlH-OoiZ5VZq`e0}1HShLRQj3klG#t64e-_{C z)ny5vorL^SA2?N&$}D{NExL(?vft)0+UCWLkdRt%yQ&+;K}0` zDgPCjk1=tTEu#&X0R(}ign36|>~X#g`K8khV@eA0Pbm4Y#%Z-J-ADB!i$}kh%G3lqF4}p1#>LczvRPzys!<6MHYoH_J~g8it(*8fdnm=?HU_lg$My<|LAgpkj|a z>sK*!i0#3NAewt5fY}i`0YHs|?htwG6mJGAhnZmY%9WKh?6*cLF(t39aW-4+&Ui^M zirqKvYN!bIqZ$sA1(Y+nc=3*0-d{?Os2$=~*6yzOdTsm0msIlN>(sh0zO;S{8DF>B zbuQ(VS<)^m{FHugMKvhzD@Uf?>iA4e`FMcR8!nml^c|OJ+YDKJOwkXdB-0f?m}2{m zv(zc{oTuB^SvpB0P^UdYwNi-Mr5yOhINaA@0L^O9uAQR`4!NpuQw z955?2piAf%`^4{;v$1Gbzci6JIGtXqW=-X)rm8xGiJsy4h6R_ z3GN;Rw?7H)J_Ywo65KWg_iPf}Lkf<5>%=SmbiaC3!TmA`?kNTLt0Xw;Q|Zs_%u8VP zdP#SQqu+;5WLyf})G zb~&5`m!{x;n*^7k;Mkdy!0P=mUcntnA}>q9ky#1ec}Yf=O_b6x^4S;BpmQOA_2{1(%To zSES&^Cc!OGa9K%kOB7sY65I_6E;k9TTEXQc!L3zrW)fUb!O??*iN^V@3XWn|B3zq- zizLC_q2OXkaQ7&<+mhh!Q*d`C!EIA;ZAowsDY(0m;2u?Qwq^Ps zzOwj;Tk!^NMOjzYhFx0=@Xk!}nMkJijN8!+FLC|A zsTawk3%6yte$bz;0pO?}c}e{iB7wC-;EX~%3?)IJ61PUfr2~gss@5fOQ4N=w0QZQ7 z%K?sTDlaLI*4(Oor3rBVqv0BX8!Lr@N_iApRe4(y=tnV5#cc(S-dZ9rDepQB_eg?z zH)*&7z?~_Dfl7Jb({Mcr>fNv51`^c!u7*o5;?JhZs%5?7(f@!N!-XH@C&1AQX;oY! zaBg-1juxBMET7(r_KXx%t(58m=b+?(9@&dyQiEcI0We^aQwN z8ZI*du2sY3B)~nW;qrlVYwtk~w>$xEK*O~rs5isww9A$Rxak^hYXV%ghI<6K3DQZG z@v%k2^(4UkM8l<*xcA$O8ZI*d&KTxwZw_#7cD_i%l_tPlqv0BX8}HKI^%`yqaOb(; z?$vOQB#`&GhC2XUj*Gk_8jc={x!48g9j@60I25%m8DHpGDm8B81LqbGKCj_Qfg5c} zv*TnraO%7o+{5$U1lGhKx|Pj$>os|ekmnY6n>BfDkSF&kG^a^ z*}v3A`rE4E_5c@Da3qg#|69{9|0@3cp@d5N{YcaA4(LaD)ZV-BC-r+llh>F)-a$>? zeTuxh6?yM#^0p?B_h(JsLyEkwEAlc&IQ`&20(s+sQ~UQRMc%!Nyg8aYS~N{UzPzOU zKBvihR+0A&fW7$h;jdbgmjiij{8U9 zT=n~@CU2Xf-y@2=BbvO%1p56(llQ2i-%k{If6?T%CeZJFP2N+Aevc~h&V`ez?cJI{ zzp=on{`{<>-*!b_o{*O!0!IhrU4?vkWr`2eGL+UV|p@B}Lv-ioAO?c|8f_ZPDc2&~5AYv?A}v zn!Nl)c>W#v^2!t+{#%px669?`9<{dvf3m&5)Z}eVAaB1WZ|z=N-Y!Mno0`0S$U6i1 z@*-cND+P-#`Yiq6ByedghyO$R(ej`+%(GX;{@gFnKN zyyt~Hd}LQ64nt>322}RP%Np)MIy6--i5rD9RAXI)`!mYQOX7&iwKL`I_$t0va*koV zh0pT&9O_5%4OQN_ZHQNABT-yZp5!G})4j%@hGaU?RCeNPr=%J;*bK|B(D)lQzEb0t zYkZZ)uh96F8egsPH5$K4<5z3^8jY{j_%CYwO&Y&emWK zj-2`@q~)*E`1KmULE~@M_**po%Nl>H#wN=@nMZ`)A)$S-=^`m zYka%Ln;IY0_?X7usquGdJe@??SE@lL3HFt0&`E%Or5d!8x35&=TQ-AFsca{0C#AA) z*sA=uZHD2uYW#OJ9zj95QjPB`9M$)L#y_a>4{7|v8vlsK(~iTwQVrTM*jK7SD}MV* zWh-`-f5K+)DV2Sr%T7u)p0*j5-=XolG=7i9|6Jp-_*Je{W3R$d{g@n-E7f>L;VAzX z8c!=C`$}csx>R{ux!6~#K~IX=SE@lP4EstoXoX;3sRlifWnZZV&E)o#YS5EY_La(J zT$O*>X7DMM&3JZFs`0wbu>99FUY$Lte2SK@&PbH+)$)gF{BVsQq47SAr&-UwQXMC> zRG%`T0+#An!4t2pY=}S87RK;?ji;H`zET}e&Qf{x1cLapY%o5h8W|d|OwcTUjFzw7 zsiN|#%F8%XEgq^8b3+nFV*9QjNb>;}>gunZ_^C_;QV3s`1xp{Pi0Dc`JfZWSn5!$lhNdK@_c| zhorImN<+4gBI^WWxp9*KbH2*>s^BkR`74YE9sEk8L+})tCK%P$jKa&;7{^8aNS43K zI7>_s9KYI_CU}ap6Yv&~a=~+atr2wKzi50@@Dy1lKwI|v#OyQiYmI{re4R1ikXLV{ zi7AQ8Z!kXV;2Vu=1kc<1CF4sDzR55h_-5n39DLB&?chVkYYu*$Fg5tw}kpE zvQ98I82KWf>vywpql3T2xW&PL+4zp&Comh_YV39JEyf!T{6=HAm~y%NO~%E7=lZl7 z#SVV6Q7icIO#WAlsNiX)m|%pB?>h3^j84H*WSn3`jNb{KBKZX4HtXgp&GZwD+l>ig z%IEsD8^waBNH`k%%~jy@@tGn$@wb3)#OJkw{~Gune3tnSfKNxCUoGjh8uDdk1={{?&|bp8fDQ~3)p z@H)Vs2^$c9CHMot>=*e3^^ ztok&t#2*6R1D;kI#Q&MfBiK-6CO#d3sT92I-$~%NfTx+8^5=r@Q1Gk3r(; zwu1kTz|*@XX9W+I`RhUAN7?vZ@XNu|%7E(o3;3-HKMDh_2mBh5KamC=F0RkhMxNk#eRms+ z!Ixt2NPI2$4)8RSlKeXz<)1RX3qBpoT$#TY{BrOW0{^{<-j-Oe~8R z2t1vmHY)sF@Ezc3>)_=@d8}AHd|2j~GSxpNM=aL;5}mJ{`e@-mgRXZ)o{{059L+MEPfuU~y6T^EG~=#!u1s zLh$lkPb6;@c==8z;_m=2-`_<1!{Ft+nutFDUS3rHiyHs7#=i?b{j>b+n964$cF2p$ ze;Rx#@_&TSls}X5C&6ZdzYe^7HxuOt!OQnB5q~du`OYQcw}Y4OS0es5;N`oNh#v$m z-7Gx`KiW}JZYsv+==xpsU#2eFMVj(@p-*H6zE#T$*jEMht@bcY7#6JbzZr`KE0r2v@MU?-R$mi|dZkzxw-&;ia=OWh1 zcN7ty4_>~Hi1-@t^1VaEe-%7k^YAwc=l$OnJcW%5aVPah4*r)0#d5lM{$AsG2mg%m zmV$6~k~3evm;|XxA?I6@ zoF&v3kvMa-cdKZbF0YSqRvQ0yB$A+hvEQWvN49$vP7!f0Y|$%GEaDIzZxc0Jw~G1} z16Q@}mmIjgZH6nQU=pZpBOGem$=bVRbh+_r+plbJRa?nUXl(_PKy4f0P}@#+Rol*v zQ(Jq9MM9J6ICuRtl&Yha<))_wiPKei6Njsb?H6?^(L0VD zX}NV(%{LVI}+}KoGA6zAnc2$8mB+0Vta?Q81Wdrb{D9c)QtKGa0NW7WO2sW&3SYOk`?WC#` z|7I-KIBleg(TG=`)zC|^s5{~i&3J=fGe-p75K?z;Jlp-)aB6I5l7f}j)yyE7EQ?E2 zvY0g6FuY;5P^GdsOr~WJZuR6tfi+XqmP4}E$=lby_q+C1tM5kf_w zunZ6m|F&MKRr!Vb^zEbE7EKqv@0c*?R2UJTG$DS76>GEI@)5@uX!bt ztzsb*wP+~TAT5mv)k?-Wy{$Sl)dV=S)$&x0)rQEv)0<~?mC|VLQ99CfY(h=wKTK) zz?Pt-#SL7EvfJsV4PUR(K&!1TUaZc_tmn0HB4z4R80;eT)Jw>gDIr)vE1M;kv1)~- zJU+VU1=Dfv%&PuKNVbwmjC2HaHy>62ZeFMc9?MW_vz3*UEz0f`JI&Q!<81D#gJ%AZ zCMj;vi-WjI{NSm4(KhV9#Q6qHo?yEmM`#L&Vz#S`1iP^;!FEz&ov-1fg&3k&{+t_w`T=jPhVm20b-o7r2(u?k<+K%TK=o)Xv~#zh&(b}EAeXB&{<)UHg^POSqb zacuWARRfnpZ5KUJhgu9xtEy}aHPuwEt8EH~u$_1X_i$2<;rAbK@0ck4DxGHv&QU>A zJ}`{`Mf_C5_<`VmFZd?h8>0MsK?&a>_)BnaIb{gnmnI5%JBfNcMvqAEC)#Tm?~3&6 zaEP0d;xTH80(UhId5P8$g`8W70^cUmA0P_+kl@e4y-UKMkHhDb_wapkqF#?Njp#6s z@mr!F8piuXeIDa8I20zH?vbV7hXcxpB0WYF`aVGv@?Iv2`u|8Y&0~zg;eE;(9^)dS zkQ*SH>M;%xg`Ag(;s*Z!QOFD6o{a}DfvFPoPSBLm9^)It2l%?k8z_&uM_FFi}dXxo$k#Lehd!Vsr;uwiLV#@dci*|_{Ri)T=1#57vahD z7^^_3e6!%6Cw>C@jd;B5=`X~i-4~t3)2D(``aZW{zH-eOOgH?k$xubA5;0Upj3Vl@sm8p4a7sw zb;P5dZ;SMYM0%G<{~w|#pN9L&9=v4g8c_0w)kNV3?SlUaQQ)2hC7d7kl?itVQQ)o; ze2C~2kMR{yPcD8$Qt+<{emd?iQ~s5plz$`f@S{4S@Sl5#LT(pP;C=;4xOa#L?k_}v z%ffwU!Yv{Sy;g&gUf&k{V}gH&_?aH#ec~bKJlvnA@>dW=`aDoduM_G2N%RVjaZJ#Q zaX*^)DnYjkIv)3lDg6dP9}@KUL@|zhcqobNGL|UD@dZSY{t(f59^+}E7|%E3u~r&i z?_B^rrr_^F&U+?sJ`4Aosaye3$h(#(!WnwS}x_d zdq4?yAMwC#CkoupC>?R^jLG1S<8cyD!ktGva3w^6yN1$%yPtUAzDGQ8|4lq_2Z;iA zgwhcgN-pF1i$E#Ai74{FOzFsfOXR;J@<-?J{PRF*T>Tr-KjJ+Gl#cSdME-t}UlQQ? zi$E#Ao+#wEP&&$ggLwGC_lbv`hlvMn8v2avwub1rh*v~cdW=rNKST6VkMWY=-y%B8 zV_Yzm^dU<6fHHlECw+(~eTWC{AW`66r*!n!(p>Pz z3}YoI;cg-xxQ#>+XJeF(IQw73pYAb^5)U0eJqGx zG#h>+_~k@H9^(PQKTZ^Oe}^c>Va5zljH>`q$SWX9`?Da4x+3N-3 z*Le(29w_WK8kF?=6!E}cMilrfC>{7!!~=gfQQ*Hr>A*ioJn*}SqTSC>I@&#JCisya z<9ec}d5ql;m7ZJjzFiqTV|x9rZp-Jn9{GB`E4014{UL#G`yUQIxA7 zih5Uw{E(oV1vM!j^*%lp6!H3;ah&E~2KpYJ_Y!mqjsFkv3QG-3h z=)pK;db9~Wo)dcfO6c(^p+~*YqfY4YXM^>>rN;rG$3daTA)&{wg&uzpdKez|Y^CRf z(4*7f^+!bgFNyk}7xiBv>JN(gn?(JsnjMaa`hO?te_7Q3ie?AIUGg8#Vh^{2tp7Gq z|8KPVFBJ9PBc>8S)qhfJ|LvmwXGHz`MEfrn_2cEZ z%n!aS>fflff4`{zSyBHlME$?i>PH-8^_SW0FCu&FHP|y}9>4I1cA?KMp--34Cm{4` z6#6s>ecspX(Jl1ZE%ezV^!d4_&jO*(Uu=6g^ua!qF3(qmUZ&71s-@o{($S9&d@KR} z&IIXqiFCBzA!kbh{M{n`YXbjuq31nKRS+X%TNu z6LDiC-UUYK$ZR8emSJoo3Mbt|^c?)oDADtw2TbF+0Og5dFH=M`3+0Jo&(KB`jod~Q zYy3S#v4($xDAwlQ6i_riiztG35mEeA3)(K|1A@}Kbt#{|7eO>5mD3r5-XQ3$f^H=W zV|Eb5q|ieYYb^>)9;}@*iOzt35{10wg0>2}RnQJW-w@O{jN>N>S}N!ULAMIJN6@2! zrVr=kas{tdzZLGZp2Jbx5X$e$$mX@V~m z{O1K96nu-|*}K*t|9-(gCGvkR_?L*nDc=J(GbOv5Na#Qhw z5|0NCFD|S~4V%L=*q87O8uJuc(;RPSF^qEa&2WF3IX){|k!A+7ywP%>d25z08cL70 zWJFJo%{dwAFjF^NVh*&Yn1h{zzV-sp_JY({fw!x`^OYz{QZbY<`zv+_I?U7i`nJT@ z8L_8W&5&OfEl-aw^r3?AiA@_@HlG-RCjL7&;$M6Qxd0>c(r$1D%8E z?Q=XteeKt##;)~tdBW{;Qp^L{d$Uu`1IsFQZ;1sjHo~*K8$V5`aRQA}0I^cu*tNsD zJZ5LubM(4Mqtg)h1=JzAU*o@s16xBc{?k!Epvtk7eYr;ZDhLGqZi{}qy0cu>ugWBVqhp*JYW`OrTMpag7~1L zxdi=E>@&StM@gG-%fK+RE4VsZJ`g#$aT4H9kfX$|_r%hEJ_rD_r#X1&Xt>wcUNmK> zAn(P{0AQXjYH;yDc8aYhY2;%X!OsnUB!u$j2`ES+dsl<&GK?ol+rBflsF!)zJP}L7 zVhdlzdGGL{x5FO{wJ*u>hJGLJ8rJs>^EmUz@n*%l(TZ_q%Sr$C@^mva7;Q-lf8Y)J z107N15Py=q4aSP*m;)CXM(a>;1R_G~P$(n(fhXwie9JdqX1vq)rhoe`W)xWChO9I= zxK~;w(h+l8bh^3{QhnWe?fdZxGyyrr{H~03=1rF6wXG2qj zF{7^?9i9(G`ySn*jhAr$MA4trYM2d%2&EysM2>7q*LrkI>y;zc_xZPXsNIG5Vt1EW zJP_PJ>-JZfV)({)gbF(S_rv{%Cnd_#!)Sb&1B&&t`R4)2cO7$G;y%D3+ia$@V2E4hse+Fx`ZQxkN0K!m=6>5+4plEairi^q{ z=QYuI1OZrrOh6&tswII5q@B`Zu&Y%FMuB!D(K26f&khP3NKcQkS{a%t>4yF{Ciroo z0;G>8JUos`g(-z#e_JhDojqViULuo`2C5;II6)rdZ~Gk3QHGs0!hhGL;Djk@5yWKj zT+eP!vq$ufB(ox(b5qi+6!MYJw46i?q|MppE4HC!!39r{wvSVjVkKUfIkvN(bXo$P z7r?%xKiPP;)ZN_WkI)`QkA<{;h?Zp75@54v3GblU+&i%%cXmzi32v}_o{=6aVXmpz z%Cr-QV^FvRcWck5}j-6tabvmu50w@oyh)-YYs7(=OQ(@yane{B1vFBGN_grugsLMhOeYvGF-R zD|;_W@!=e0LSq@JG-SE=QCP6XB-X9WMY1tJr1n-q%oe2~KGqOsB^S^2w_OUn0;e^C zJ(jd~M|?0+?+1vfa^TwQ=WzcF#K#QgaG5M-jf2~@7rzo69zN>5*(Ds6`yMC9z#N`#t!y0lSkKA3(12sqJ`vhSW*_Uo`@BaXZfOqqKE9*8QK%**u00TVsopI&74qj#U@=jzff~US3@L4bZe(21O|T;jZv&Zz+qh!9g)Xq%QvWg z-fI+kb19T(MA^)ShO@;34?{dk32|m8kH1($jL1HgeV7aL-&I2H%B7{T=~gIeMKP8a zIOsWSj_5td_FQW}3;zxGPr_sty%8=O8qtee3kGVzRyj1-?aiX&W9AhIASOa(PqZSv zn}UMT`8th7mKQDW34bsGRyGT~X1Dp`x-k!U{gIylBsM~#f6V7Pds9vx`yLud8|aFj zXu3Jy7d}zA{_^mNIZaj3g+1-prlD5qaxNK|g&AgHdUqlHIEoRy)@zmznCam?lh`N- zpD6LS-H(iQpN|!^h6hvDzbS;wfg50hLXXqnMF$pqSnw79?X(09pGeu_Z=-E}_=H!e z8G4{^EaK|h-f)-K91-sJ_FfH>g-;ZP?t-%IWog(wMA1aC9^;ykN@W@5Gtn|{bRM~Z znCtn-?OjYgRMFE_m{x*@(Y}s1Y+(kPL@XLdBJhvTEcHrFb<>yR_HM$I5$T&%sN;fZ{>O%gyHZw>byAM+^7z}DP!(q8_7S~n@X6WVJU-%xDca(T zR+4{t`zD*m5Ztg8wMGmAp@W?&-70cCbTB$My?ZV@zfv*Xb6M|(_oOi|kkcmWgExC= z>Jy%6R``~Q1DP%CeBnBry}(pBnC1QOINd)yX%-J+{navf?1$0vK^ofy)H8Uml6j!J zfKK3z?gH*l1;LTEemGX*Vey0_vREau~&5 z=M8j*;$xbmv0#`H92M9{b?>!foPYaSSQE6KNNGl)=Rn?Jv%F{hPs1lp+w@5A>g?U-`K@`zhG~I=fg|v@s}S4rgXiHC?dUk1 z8=SV=zuhyZ^EKaTbgIBIL$90HdCd!Yn=mR^XnPVLhWd6xO7O>9dOr`wd0vL~55l-; z3uX^-e1N_DS*<5hp}4*sbZtU$Pw+5ngSk8AO`kP<{r*{FH+4l02BBC_>x>cWpFk8w zP|sjvR}OD1Vn$$nm^~&M>hTQpeUsMz>^uY0AZ&{hopi)JrpYj9awZvgkPOTwJ*LRJ zB-dffm!ie*BDNkf_tLVg?-z0V;Tg97puOzG$iIC|>xtp0B7V6CkBaZH-ND~dYX_o! z^w{3@`=g`v2A)o3i03;y(+Qo)@xTjvU{34(2gPjHXLL z*eUh&?SQms@k>!(`2EiYOLqL?*=L`9?^V=V{!-^V!(!g;<}YywgVTut8YwB+odSU% z+xhk||Mr9CD4I4xTddXG_z!@OPmQulD$RCeR=8%I$u>i&cCC)r=Dvv3?3 z?ztd5ctI#Va8O;Pn8(qf?bv0qPNVZ&d*8h)_r7Sks%E+%pfYz2{IYEj6zen}7ESE)T!{=O{?+We)dN`sY z)(iiKy>|hOs=D^Z&txVrYHWf=MN4&npg|Ec5ReGkKnRZl8hL3IOah5Q4G_Zwu;mdA zDAOStEp5Hlf2FtDm)^>?UP{&40G5cgw}DzO6|1zRz0lBJd?EFE<@^2iUi+MV&P)RM zxb4^fx05sLti9G=`~BW)A7&!i>5Ca1(SyW;?9Hbf9C!$U?YZ<@98>_u68yo?%W4I^ z=Yd;U&t$z7<2yB(Ma}Y+w+PDHxlQjDB+hPnw=|L8^zPW!#3!2GEpP0bJS_cr=EBz) ztdb~;$=#TpZ)e@>x%9UP(a5}2FPrMd2zXTv^S@+POMZ`mXJOja`NQD)4ch~bN3lwD zq-FLys-?uBE||!^;b+r|cZw39KvpBaSvS>Q01{6nuu5_>G{mUZw0c_;Pg|ohmCkzj zXn2xaJMT9Y!yA9<_BZ0TStuNd-f1nbbdz=i+`sAFGN$)vruP`8_xRSv?f(8WW81bR z@y{$KsN{zRzMLA-5*bo*WNAzD``N43HoqTPH405@<4L2hOg^6^Lrdh$_^yHI^K($w zN36uSC64Ypj7EBVS!ip?`;BjeUT4PS9hitR!e;r|16(e#<^$ZiusAxYp2K zzF#eakrl;>Rah0f<`w%xSYvEn)0VsX3rKWJv|7l?BszEXC~%^3ui2BkMokB%M6pZO z^8i;~Ou~5O6YE!4v{1#98xh!@LkVqJ@g~{~tm&&Eb{G?MPR`w0`JIoHKg_n3nzYr#W4YL%FWn7LLWtvRnT4fg* z2^S^WvmtcoU(d(+5`+s zi6owd2=_+m0LaIBA4{GpyIB0AV8kd6=wj4x5rNx1_k(~B44wZr-Mxl0#0#|@p%}n5; zx^SWYJ7{XcfuBQ6O2tkG+0-pYMYTeT8znpSWfes|V^wwagzD6R8hSL&ol>&$xna`* zQ0eCL(+%YXIQ0=X|ZK0^DBCRR@g zFqCDvM$4mm!~c&<>S}=6N7YZRV4f@d65 zBZnvd9YV3ejD6On=&O6T;BX0tPI^FWZG1F79JED@?m>w+rhj4Sz}U1ar&L^YQLJEM z&1ErlSO&V1gO}Du(FQ{G^D_>S0D}XTG zpULNLOy%qG{1VRvc)pLv(EgDj<9pVr-b0+v+hYHZ9CyZzd2OELc_`|5z9g_L<~ZLz z7k6R`Zs`B0knwG|9^!nO#O@z8-X>nmYx6kc%gQc`6^_4h#=uxH+LIw{O@_zg@nNyT z`i9zu+Pd26#kB)tg=1^y%tw1dp7_N>;sYIL@Zwt+B&z2C;(8(l=b1M+8=R1jlfddyX#H+2Tlv!nuG#vlp_)=~wj|llO}N$#3`y z`wKyU8eid^qc-#?PL|k{ueTw>&b$`k-40(H&e0aZ1BNeK_~@THqYP+FDNYwcoyRrw zi>4fpc_;pPbqbMh+8A0dpe7EakZ*=&QNMI9HL24>A-j}tr-Vf9`C+A zVj>Bek%w2GvF+2pYi3g^-}ZsH7y!zzR}-qZc^4C8tlLS-iplOMq>6nr~i6>z9?DgL*JBiM@t&$psT z|DlfK>;-+j$cGw((}Y4kSNNv`KLmWg;81H&di7Tc|JMO|4}3Mk#PDqcz5$u_PeT7A zU@7{+J>s638^Z>`Hw6C_aNd>NU-&-_oEbb-ML2k-i^z1+mR|UV1?)##z756`+d*CL1{|TIiZA~u+o}2LJ~~8fzEMt*w|>F@GMe>dAaHZG5=r0vGJvl$b|;uwQ3W=G9i$)HWDN z*9{rsIci+Ec>Yc5>ZOGV#Za+8xgP4V;1)VfsZ*3UetE-#Gi}&KiIzMQwM2uLxa6!E*ERNuU=~88> zIhRy7R4%HAhy~2KX_fj`tPw(-Q{fXp$HSE4>c+%89bn}<(m_cs@+A9R)^dmI_R}+Ho5kd^8+6BKD?^);%)!j3IUnLHA zTgCn_gn0P95xQSV2)^eC(T}}J2>t>L*l5r1M-#3k#GGpfA;S4rLb&gZcfzX)!Pg}4 zWbjg6f4~q1enW|)AHInY{@+Q64)s9bwKw|9Fo;-lI?a-vqu&h{3{9LdbatVWZshFb2p%zxg|X=b+E0{mq21 z-zl&^;!M1b@O;PlF5v*j`MKa%B95fD5W@XVLhzk|xMmgNJ6r;vAq3wS(AN{bM+m-= z==P5ycp?CIM{Kn z4FRGq9FY8WV5lp`VGvBp8!0!lrRNzK|PYB#B@M(c<0-qK5oWK_Zb_je?;BJAh2;3`hpTPYB4+=aa@GXHy z1imK_LuQp<5rI*GIRf(pa-PKU(OY02fiZyv0t*EW63Dq5-4zSu(lGH+0!sys6*xiQ zWPud|Iq#x7mS4hI0;>ep2%IOdPGG&jgurD2mkVqXxLV*^f$Ide3S2MnK7kJi+#v8F zf!`7MsKAW^pAfiN;L`%z1U@V9Ie{+->=5{(z}*605x7_2K7soM9u#;;;9CNZ2z*Z< z=bubZrK=1W6`Wf_w9gZmFR-`3J_2I`m5wvs3k4q}ut;FBKyLj~?kIt!0*!7ox9&)v zEU-c#=XbQfR^TjwRRWDp^*q7r1l9{o2wWy`xxglYs|BtVxK3cJ!1V&}6Zn9@4FVq$ z_#J_d3fw6034xmhJ}t0K;Ijgs6ZnF_4uLNU+%51GfqMn+6S!aCL4k(^z9sO8!1n}l z%FA?(2#gBM5tt{?=%V))ypO<`zyg7V0tX4?S`6hE3mhSEl)zGfV+BqSI9XtY!07_7 z6*x;^mB1Q-^90rjtQVLNxJ=-3flUHe3tTI3oxoOs>jmB?@Bx7v1U@A2I|3gSxKZE} z0yhhMT40;NX9Yed@CAV#0$&ukTi`1K_X^x6aKFHV0uKp%OW+ZK?+Ikbukv4DR3O(~ zXrCvL+rY$S*}zp7KbEfeu;E3}7^gY5u6RKYbV@MA>bKAt;#}gOnQ|}$RWM&5hMEe; zFj>Lg0x?8XcprfndMUiG!2SXY1Y-E4=!F6?yz&`!mIt~nR5?&5zM>_sCh}6@?bzPO z#`@8U-mBYi3hWS$b;*ItR9M+Za0)zeN^8Tu=KZ62ApDh7Srm>!No>kn+>jqQD~xT@ z_HYz|XfKP52H0Mf7fO{zlV#Csp>Td8{MUis7a1U>h5h||nI(DOs-5y;@4Szl z$jNO+sl*(}hK^#+=@_CHWDj%30J_HKpVFXrNhkDDQl{6|h7Qr==@W7cW)=V%-@UQG zS~YPJad+sinvaEb6`mqI zKAy47;TY^{@X(CM(x{4efjaf55Hx+HxXmH?;Sx-e0_%zWWP z?S7Gn`^!n4F>zsH=#XJc@ENfh+}7@qy}Sy1TS#q}W%5E@_uMs=cVPRdo)7B!XCG~n zM|7_hAAHl?xs|oc=GN9HDzU9sSKFnyBIA|b)kAdgeFm@W$z~s|r}?bieL#{Xc1^;E zH@uX!_Yal308wo$D!&2*_*Od)SLNf9duMQ0`;LQeYFN}*k1}<<=6^z5ulEf54AvW# zNldd<*=6|+@{NL>;luR3OTMudUtu?VlPx~>AM`Vmztb%~Ovk(An`QA~y51#Ujm0;# z8@@V=Z+JI+35ze@4c~H$Z)7)ot1Z3}-SDlm_^^!8Wqj9Ld{=bC_khL6-pAyJ84F08 z>1+h84UaK-@`7a~-+u7AeBG|HTwy6r$z%Cp8AB0t&?G9;1Z1MT6U5}5aU#Fr-H=*}1uzA3P<`St?IF-(AlkNIx%aUO_eAJ-VZF9rA<_>ZL_*BHLL zVS}Ys*F1`JA*~1x$6<^I!~3szY=2{rhow!|7Ki9wYDS zfIQA4PZ1deAS+WB;N!f~E`O9qGY@=r`i;e)o%u?C3-B0!aebgpKZyq`N~U-;2juZi zaT5=Y?!A;@FCF?{sj$op|1d_|CVsu2bVvNA6lKEX@D z_mI337=K?1`1=I>rJsD*Rv3TPdJb@7IKR8hAb`f-Ka0PHbguD8EDRs}N{Da``64FG zVQcsZEoK+OxR(K^h{XQC>!tZA$SX`lJ8lC z9hO|rgncES{QenfSd|c)pCbg%gq@2$1aJPE*oa#hr08}IlI|}&>F0S&@2R~BJ3Ryb z*)@&w>=uBy-2)M~TNvVYOG4bPX~aL_+R&$d$o~Q>1T2>&f>TmIl!vK;)8~H98O|cX zs{l_!kA9cnELr_fKK@y7wgG*SKR*zhfbyRdoPAn9l%Ie1;paGefmh*SeWv`k0gX}h zMk;`o-&d`t+w=>B&OWIhW?NTS6^8D|0&fL0@^MYDGCKU{Ik@gsnTh!Ag}+sTGd=nu zwQzl?YbbAH0OvY04RfZi)8F}hfSzL&A-d;zB^u^OUzFd$0s80wZq6?;XZx~zTWb*5 z1;@8vyeWs7%yn_tri-~9Cw<v`57UA~DMdkmwTtYY0Ka=(>xA@Y?G4)OSmd$z10OoWtsG*YaOp+eoJ@Y_C=7aFX` zNFhwrIK~VOMBhtzfgUr3WB|asNtHZ!s^ncRI3`94|Fp;lWE~9wD*5OhI0g%$+9~at{=Gg1QYuPBlAlYFGouOz2**tL+K&avf$X=f~jvOe6qXY_aZby4;?JF1)*iWE} zBi_y24#}?gkPQkV*?h)cFN7}pI1t-QnE?RIUxznuZsLo3*9q23ahauF0!>y>A59!4 zD{{!KFKzU6B~w$Nf*4JWj<#n}aQhhLOL7dXaMKB#WR|Z(Au_$YKXC^s?k1tQsqtME zuMH|_ewKoEt8R9xSj>KPi%6Y8eqPSf2kX+%&fHV@>lKH8oz0@Rd&kt=AC@1!@l+@0 zM(Dx*dcj+-t=RwT1&32p_Ft{#V%2r`sSGo3=X>>l$Dwqw^vyEO&-UTHqqDb09MNT0 zH^IO&Ty9`gdX#q0o)x>*Qm^PuVJUadg59rf*|TE5UIE^nn*AQG-Pl1%UVPR1`XC1W z_~iZeBbK-0BbN92G3D{@5C49}In-=brLldSDx2=kMV`G(uI_qscQ4A8oZft7T;r?Q zx4@o73im>d%jxWBJ~DdoDcH||PPi)7pc zd9&Z?Xl^@&SD?(^+!NN@?7K)%o^U+_!_abV9xmG`NR{$l+Nc`FAtjWKB=pl%DZ*VU z$=%pDA&!xnJT{Q76Fv)h_uPGRptT3G(0T=U2BQqgfbP7EPR_!mi4L7yd6RerZ2cd=+!! zDQG@a9CO!i)$Z)}uFLj@Oipri&g|rK+w@%#AFw@C z-J-vVR*hqNduz4NC(3_Z?Kw1xGX%!%FOV)>oAmuV)!BZ)-8-|tW_IU*Z)O)}?bq?J zii0YxL~u5|{~HPZKE z`>(l9{MU?|hhw0jnoJR4-hH!y6BqBUI$$4<+jwmFFks^@o*tLQfVN_cm85-O50B%w zQrg1|;*X(Y|L*T@@6Aidz%(p<|801&w>c-d^HvyV(*@$PFLGaGGDh=mp9N-!AN*}Fn$8wd6hyJphnRyzAl{7I7 z|HkWAjmtaJlKh5m9*2pRp*$M)LMnVH*EIFuqdQ|v_?U+_UlaJazG#f$tA~Zn*V+YN zi|}y`kLCgJ85zV)I&+P}mbVdn#tr$5oBQ!@r{A-f@RG*#dHGEfn)fF;GyY*NU%H^LmHp+h0pBOQ6D%{I^)y4K7Ze`{Eb15iN_d`XZED3 zAg>T~V`B2cwO%_O5o|Y10FCL~508<@HDCO@dtCg!j4X}7R?`R*0lxL%%OODw<hl`{4S1KiV%q)EA!I; zUmN%eg+>sQ_buSF{Y_Zu$F&<1kKY-2;%-0WT`TVdCLXT_8ewLCV?mcuh)waRDX?0q~Fc48iax3H>Vo>lr@9czMT z!amJ)x*zBf@s_@KXSZCWUlQQQ;;&`?5%=y)8-?nI)IQyp;w+~eyV0;O<=!2~7&IC` z!?{_b*p?`z70q3{p`U`#u>FS*_V$Nv6$HR_$qt^(Y1H3PK zx*G*&Iptnyqu@YQ`hQ(;mfyap|BnUezY?5%SYNaUe+Y7>?94NxxUQD3CtVujp@^t!2bsuwe@J%3Xo&8jY)t1g{uVLDgs zEori>{I~KoxSg!ZLAoVMSq4j#EPt^8TLzn;6;Dz7N|U{VjS`1CR4K~r;PVbKtCH>Q zK5O(ucc1l9a?16)JieXf%Je;IvVJdjy_>_LlY|1gdzBkbQIPa~s@1&bP8IM?XM#u}5bgmIZ!v7f$FZ zvA;t0u?uzk%YE;B(vgT5!aAWTvX4DX%L`!$#=e95=QN?KMP7x-n=1Cx#2y0-G4feyao^OBqz}9CP16EI$$=mNe zHh(5vCiG+Lt?_05k zbJi)EO!`c$XE73CIG$xawf8zmFU5nVEh9Y!`zky%9Pjzui!RgO-F}Hqpe}WlOxg#A zfIf{PJB_`~iHv$oX_`t#eufPaLvjZ*$IwzT{9X{O{Siadn<^e*qDAo3i7d-w?QvM5 z>~uKBw#i4nGxA?(B-tMr!}ghVfMu0!Da#_CARoFnmk(V@mwaO_KDNbO$(wBPp{wnZ z58a^aFT>cCyjd1syc@n6iw{TJx|COE@eS*SFJbXvsL`doSp_JK(U#7J&dGf*)F0SPnvk&WiF(Aw{j;k0C zmNU~|V+f~A48qw48WWQjrX~3eA9XftLwPimufm6M4Gq_e>CPAvJ`Bfvd`;lXF-(Al zkNIxPYXx$02EKa&d>g>WwGv~DycSs4=@QA;elgr<;xXy+7~XAv+kkM~X^fHg2rO)Q z`H;u>QXcy(Bk$*Ux8-$oA#VrxYx0LH zFAMJR??|>e7vROnfVH9#_7*;~-@$dCOzE5tK06*`t#oD?HvSGV{))#{fQ9r(OibY5 zfV}CDHvu%pqc0vKZ&EyO9Cs|v_l4|#UHKvw3~0N;i# z;?WrJ_X#T=m@2sba=nkT%{~zC`mpPfiO0PGdDPirzA+w5M-z|70`fW_?^Mu@p$KCthjZqm(y4+7+R_)5VESg!9CoNYi~ z{5=TBx@q_y7dqQ+>N0H?T=Spr{D;tLG^o9>V$6^0r4EcKr&OV8{N!%wl zhV(&#YyZzvcYKjfey+D)DRjo4x<%Iq+^-1WUk~7019+$4tnXaE=iOGuFgzzooo9Yf zH|ZR!4Io|ZcZdVx+~1&K8>)1Z-1P*mMcOlp53ZSHW;1~~9ON5{weoJy2i-+{!^be0 zcWdp{4(110%Qhml_R@@nOYtF#%Bi)B8*zODe4J$H?o7~K);1L7t4g}GWm?0cB-ydc zsAyy}WM0{~IWn*92bOS$6jfF(o?kh)u6ps}%EYbpwUzT1-ngh7;h8?o3Y<|p(o=C# zKQ28aweE?9w6jp=xQ@#C1MhicogvP$OT8e)cZr7^dkhUcJ*0u6hXlSd+7I#gjeGX> zAxw^x`w@ZlRZP|>FN6-2y-x^}GL9!hSeK0vULfn6(%EwzR>>PB^02O}a704M$NDkr zI`xWJzg#_}B9VWw$Vb?#Rdm$Tv7`gm5TfB-KnTyUky3k8 zZG*ew>gpd2hb~f4(NODmcrk9KDv2Ne{Zy0T4^6T9EkquTRbvy*%h>d&v;yRQCh>Cx zo+mISu%AE`7rb+fNd7{B$aaMzc@#u;D#&pT`8cO0rbiDyP;pmm=e|JG76|^p({4d9r1ur|YMDwD)OI z4MXV`gRYbYiRz!)}iYTWm-4d#n^mM;9nJK;*(+B0! z$sB+BKO?8>^f^}eKi>5F5l78xQJV@gw>JU1{3w2w^lQ1@0j9nfb@@?PEy;rotW?oD zdq?w@9!f_etvrbSP0O{txb!bdHISG=sRsF@KX}JY1S;)#j`$%n^9eJfq$BzuMxWw@ z%I~rAM=$A1Ew@k%_Ue-s&~<(D=o!n?7$#UZ_097VTnI1uy9=+T zgUN5wzIA09(+{|#qT_4pm#ByY?t`0c?XP4ZK*9YLi~eEuTQc0|hVY60NeupKMZ&K1 zPnc3C!aty{##2CP@3c-YW`BCOqvL zJI6fz8nYWT$;;?2C3q~Jd-HWE-1g?16?vOI#s|AJr0*)(p-Xpbh0dL8__n(ISNk28 z-i}+a)4lTrCT(lXO9n3*-fq)Oj~v|75=|VLNchx&+`t6gO!SHKcEao+rnxx|qSQ(| z5ZaYXIMxQ+N4{0&`~^)umgii>H(4wui~>k0CWwfHzbj>r?_n{4rM z{M;4abc>JEpsx64S$q?^;j6LuCUwJCXYnztUHO}^_@;Ej$9B%7^HtsOt+x0Wr>^{6 zXYtMMhHt&acSARP4_JI<-S9nR@r~++?@^0yPB(l{SbSC8@I7tu)po=8ti@N|4c`kE zUrjfBFIs#zb;I|H#W&I7Gjq`%u%kJI=V(~HsvpkNX@Yx=^ZGhY6YNmGuvkm3ZU}-?fGkZ}yA54FYG2vs`=A75?Re>+l9%4g)kLQbXj4(hW z@16i(Qy21BK5Tz^1}l@iUj_Jhj+yJC#u$Hh!h-9U#_UHrlU9x=hR3Ayn|QbVZG$|n z=@?_={TD23c}F16)boCz8F^gGwdHkm5x!HwWXr?CuN%GsOWq{`d50ixkO>^13ExEl zd3lyRu7jBHRRrY4P+&93n;eiAv*Z<8@)n6aEQu*I8S?CM1+q$y+XMbqfiF|Otqk~E zWckbW8xs$%VN^?$Ck!3VYL!&Sh!aGX}ao)pRipR*i z5$`nGzqrG*M)i0&CL#ZwcyGmHsuLIc|_ZdEej_k^nIzeC65%?co)Ygj0&U8n(=Hw8iT%5)fcJE zM-Hy^V&DY=m%DoFeF&< ztIf8A*WF!bYbS%axAV_knRQQr_!7t>+pf}2dw5!p{$i8P8O&yVcdnIs)>#-Yk&zLB zLX#wZ=SX)b4O{N>71bF|Y29w;3~pPf;4bOo9ew7K*_8$17%jqJUp9ij2!WF1TK z&Z$iE)BOsJ-JE_2oNvK#7(0yg$}j3e>q26&ZZO)X5FpQmh00{^gCWFiXtT4f#(6iu z5ODSZ#Ccwp{9L;w|0mHl5{?vjt-xghsjp4`p9$o*jES@D452}0o1OI;4-eXNG?R6u z?mM!^=ri^%j-g?Y-{1zasSQ;LUxjYtd6tIlGIba839$&u`DbXBbxOSp@(Jn_{Y1%c5(JCRU`T;o{Zgj6=Dr?}m_(D-I>!&ApSa za8+^=cNeXyH1gW~B?YO{yftkJ_l9XiT?xcR+iTjAWAYN=M+M&~ack{a!c4y)6cXsK}lM#O4#UScNh_f6db!sz@pha1mOYz zxDcrAiJ@pFc)K*d-H1z>y=+HWcz11n50LDPItuH>Imz8@X3*G3OQY|iM*Xz?OEt1F zR9;d-+HGsh<(1-AH-J!v zcB6Qklmt`x!Nki5b!w){?o|0fmGkDZ_nA@J%tBc}`iDAqqG*O~r- z`L9E!a-JQda(1SX;=czyE%2vrAJ;n+h=6g5fRD>boMA#{tdloHJ9ogD)7hS0{?X#- zgLxmge>drkn5bHWQjL-;ABfdshB7^7KP{3N&97D zt4osmEd!PZrgx4i6)X=;D|Fw|OaFtr z>(Mou3Rs3d5HT$`ZT!}VV`#ea?&IKV5;7on^}A?5kw?jG@uRJ|n?j>U#S)RVS>wk{ zYJB~jc8>2715s0RH|4GE7*sUsq})x<<=*)!tocO<%TIO9S;=d;cYKfX8V`nE?YydW z?UuIRMshdJc(dtGMNRLXwB)?pP33P68a*m|={4H+Qh$5sJfFL1D+`GwxOrPv^Y*Ng z*OpAr-E?N|rg5RQvDic-8qOdJx|=s2aA9YcHd>bR*$ zG2;-gJ^TBw|87gG+NZy-`o;dZ2(y%y##h*UVvOMqD^Y}J?34KeM>Uh1gv*9fGx+ZnXX z-i_|z8MXte@iwFE@zIgxdlP4w(jM$^+4r-pcZU_2$K-B0)cFg4eecS@_~8%d*?R5S zN723-T@8$X-$O4cJu-$wx;1YvvwQG0ZRjileR$$zcBqZ{>_)tA%Z%XZfA#SwT<>ifs)%m z(QHzCjkx26Bd+2i-n2vmT&NkBxHRv2mHC6~3vjKGCes~KG1&9r{@=F&nWOG`|8dRP zcSPnH%Cwg)Vks;pw(lG|B4_FbQKt;_wZ~4{cAxwe4$^>kWan>w80fPe{)+J zp9+x6$Fp=XWX$?BKFDj%hDa@s_t>_9@1N56{PMULU3HT1ak1Ui)$ruSVErukny^9t zHNXhVr^~1Fh;iWEwlU1zRoHmI&gJoBk&k=bZQxU3E6L0kgY^jT@%{*GEBMQWq(6CP zQPwedn!po7+`5zV5O|uvgQfb6elgtK+vot#oFEShm!0;9z*ma3sLF2mVxaX#8QKfJ zhslRM1TVdsBjZrO$TJE&G$co6;K!t?+>aK6RTX$z(NQTmwHA-)+x*?>#kdVlU~Ab^ zig7vI{biq8iQOV~r6wZ#saj3M|DBkd=FY9GT{gG2K2fQzbk&L)Mj>XB=eeh_;@7%x zIpEUM{jhC){1dyT*fH@53lMzr{F}oc*QaCDoiyBQF{T(V>=RhE`{6-TqaNN}WAlxI zz2RfM>56Zx#W$cEzR4C}Q8#?kEk1KTm3{}qH_PIi*^Rs!i|>MN`06aa>$~AgSbW!Y z!?)bx!9^YAGi^<#_^z|~;+8zK4~FhZnGN_IW@V%r`@$}$XK*bF0QYZM4tS=8 zelowAhbRh;G+EAG$YQ%?OiW(rj{JtNkQ;fHp*$MqHRYLe*+t-^J7Y}vuubdZD+M3A zD%TjkMFGAF@Sz)Wjp4gHz*hx6{JX~RrC@`u(={D~^*$@(mygG!%fH~=jz<%Op&N0H zk@sEL*z)#5o~cLN(=_th@lJU|@X*`^c|G;3`X!CeO9IgyDf4CUEtYoz^3WW_!*`R; zdDu|3^UZ{BKX~l$?ML`xuw#6=*J{F-17VbB&Z}2JULkN}V)7CNlPxcLi9V-Hc^qOG zdHn+N)qY2ff!}h1^jITU#9e18}N6Tm3{?~Wa9B%BTwY-hrA|J`Gk+8 zhXeB3AdeZzc$|;N$oqlFgOxJ#a0L1?g8+i8jM0M-)cI%sQE8WtO^y{D#rI1i1T^zX zXv#8WHpP(6XuJmD-SkIHca|wzUMm9sQk&Tza(8Ma0J&_hfUoNW&87Mm|PPrRRon*MpgS(p2wg1k zc8~_$0>Yc`Ocq>+ufUltIC&V~8o}9j@$Rx^#F1?i0O(&!fPX!3Gp{H8hXJ};cZ6FS z%I6%@7~&kau^8+GpnG$d7X5kHVkIXdm zHNedrnDXueZsxnh9|3OWwZxwRz5&gk3EwY(oB1s1{7wN4#}fT8LinrTv}1Zwr`60` z$$z?3dGhx{`#L~y)~CMcnTHC_`qmexxkd{PRLT3C;7DT7`#N6;(7z!#TXNoQ_jrIV zhuJ>JDQkCv7nsrFB&;=^TvA;(U%tWOI>4b+eNfu9;Xz~1(TIg*ix$*ZH`JCdkORl~ z=7>k07Ow<9zBv-K!Z%0KtkSvg&56B*}u> z1&eDF{iZH)<-FQuD)DNnE0~50t3PLbn#B%u+812r>%+|SWp4eg^u-N9{p9j_ zK8q0uM`cZQqPo0d(mZv5*yYW5;M-5tN77w8NUxYQ*TENSaRS`u4P=pG2SuN4PP^aF z6^62t79@CJ4yJp2L()W>Q_e1rIpXYE#E0G3uxJ6(xDuhAzwo9mQ0{xHRXJhC_yoeBdiv`|8 z2>$N~{m)`Q6+^o$3}XIS;Li!iIL;e{;6Dw0F6CV)u!InD>ji$15d4o2f`2O^`1b>b zF2Fny9R%SmfTZ)=oTT&HmZ3Pt0L04?exZK~eKhGK1dbC9-YbOQ z|1;^3_cn3J%SGCe|C4}pf1$u4+Jo;Zp>{&vT$mhvx-$&>rD_U+7~o_+Y%|5+Yu=0+N0kam4HU#3AP`;@~^I zm*yJ?NWM66@NrI=H5|vbPX@#w9K}V^5o{mMwVkf6upcPav;VDVL?#2 z$3z<0;DieFm?kt+&g>)py=gyD&a-2X7$ZJF@Arf*0YZG1$iqUUlE(!^+TS4dl>%X} z=vCq$AfoGp02$uUQ1P!w{Oc$FfsUXlKF%9L{RPj`^MDXw9qj?v3w&DOUV#qUA<_#4 zRtQwz%taaIyn^%%0@eBj?4J{SCn4~Ig1;?zKHSh9^&d$eC3uy<)dIgG@CAWy3GBo6 z4euOR(|su+GGn6PH9}t?_@enWmgd9p=ssLpG9*TGY~6>BbC6gtoC|XGrwG~)h$hDU z2ooMG;V7u`3h!qKewIKwC*P+8a$SpfUxEDus=UNIniWMyu~)|4J9p1UM$iBoO^5bU z7E@ed=$>6wXVq|yfwyO-%JWi-qa|-FIW09ITJrqT9-SwZ>|C7vnUa?p-dVE?XBQLW zN?zJt{-)DVp%{`=4lca{$H*^CeIX}xZB8m&(z9X0(rNJ>oq1Y*Dv~OR+}%*T^wXnD zo`?I!zcj6Q(`mdSjcxxa2NeXH{;w(S4@hqGfiSHU&7R|kP zD~@s`J5t!chmr-}o!oXL*#dt#dp;-D{&ecI>scEAPC-?V*L{>`XFIJ+@(8V}z$ zM4R8wS~4niU5?5dPL!R_9;Ks8UQhH+<)@n8UA5#5-293*Z|NsTL$9Ia^~U{Ba#(q( z(r;Kf5Go8jpT)~z&vTY$L#?4xAxCqUMnOZecm4o651rq?9waaVWU2F9UVgY|Ez9$_ zW9uJh0FSa9pA4m&dGW*Xqn($;ci$!*&s@xjTRE#(Ks>*UD~J!cO4z){I% zOD<2o3LTgy5e+9bI+BWP#%Z4jc%yJ*I9q0hlG|D$c_Yj78os$1Ck>Ap4FtfJvXE9r zkRw$yRET(ho`=6#C=(F497XcNAsT|&D$f%p3Uu%&Q~*>m6?Tjc|26`cB2Tu1~v3riJ{Lq%YStkDN<@?Ew)8;z08xJCV+o9UD zx-ED0w?ToXL&=o=xy@h0`>4UWt4~37%YCL}?Ut4?9r2^hM^0LLW?Am0Q(7WFX<0s| zBlnq|u!rrEKds)8yZSjeYWgc&{Y1SKX!-}vV8eGxE6Min&vHnd`Y%k=X?Ktp4azdUdy zi^=T$TYjHq;>h#gZOQiJxJY7o^MCi`3DlRDoUKzo(JQ$nN=b+_Ec|LBG

*07~B8HWpylUt## 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 // for isprint() used by get_printable() +#include // for sprintf() used by get_printable_hex() +#include // for strlen() and memcpy() + + +//============[ constructor using no arguments ]====================== +OctetStr::OctetStr() + : output_buffer(0), output_buffer_len(0), validity(true) +{ + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.ptr = 0; + smival.value.string.len = 0; +} + +//============[ constructor using a string ]========================= +OctetStr::OctetStr(const char *str) + : output_buffer(0), output_buffer_len(0), validity(true) +{ + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.ptr = 0; + smival.value.string.len = 0; + + size_t z; + + // check for null string + if (!str || !(z = (size_t) STRLEN(str))) + return; + + // get mem needed + smival.value.string.ptr = (SmiLPBYTE) new unsigned char[z]; + + if (smival.value.string.ptr) + { + MEMCPY(smival.value.string.ptr, str, z); + smival.value.string.len = z; + } + else + validity = false; +} + + +//============[ constructor using an unsigned char * ]================ +OctetStr::OctetStr(const unsigned char *str, unsigned long len) + : output_buffer(0), output_buffer_len(0), validity(true) +{ + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.ptr = 0; + smival.value.string.len = 0; + + if (!str || !len) return; // check for zero len + + // get the mem needed + smival.value.string.ptr = (SmiLPBYTE) new unsigned char[len]; + + if (smival.value.string.ptr) + { + MEMCPY(smival.value.string.ptr, str, (size_t) len); + smival.value.string.len = len; + } + else + validity = false; +} + +//============[ constructor using another octet object ]============== +OctetStr::OctetStr (const OctetStr &octet) + : output_buffer(0), output_buffer_len(0), validity(true) +{ + smival.syntax = sNMP_SYNTAX_OCTETS; + smival.value.string.ptr = 0; + smival.value.string.len = 0; + + if (octet.smival.value.string.len == 0) return; // check for zero len case + + // must be a valid object + if (!octet.validity) + { + validity = false; + return; + } + + // get the mem needed + smival.value.string.ptr = (SmiLPBYTE) new unsigned char[octet.smival.value.string.len]; + + if (smival.value.string.ptr) + { + MEMCPY(smival.value.string.ptr, + octet.smival.value.string.ptr, + (size_t) octet.smival.value.string.len); + smival.value.string.len = octet.smival.value.string.len; + } + else + validity = false; +} + +//=============[ destructor ]========================================= +OctetStr::~OctetStr() +{ + // if not empty, free it up + if (smival.value.string.ptr) delete [] smival.value.string.ptr; + if (output_buffer) delete [] output_buffer; +} + + +//============[ set the data on an already constructed Octet ]============ +void OctetStr::set_data(const unsigned char *str, unsigned long len) +{ + // free up already used space + if (smival.value.string.ptr) + { + delete [] smival.value.string.ptr; + smival.value.string.ptr = 0; + } + smival.value.string.len = 0; + + // check for zero len + if (!str || !len) + { + validity = true; + return; + } + + // get the mem needed + smival.value.string.ptr = (SmiLPBYTE) new unsigned char[len]; + + if (smival.value.string.ptr) + { + MEMCPY(smival.value.string.ptr, str, (size_t) len); + smival.value.string.len = len; + validity = true; + } + else + validity = false; +} + +//=============[ assignment to a string operator overloaded ]========= +OctetStr& OctetStr::operator=(const char *str) +{ + size_t nz; + + // free up previous memory if needed + if (smival.value.string.ptr ) + { + delete [] smival.value.string.ptr; + smival.value.string.ptr = 0; + smival.value.string.len = 0; + } + + // if empty then we are done; get the string size + if (!str || !(nz = (size_t) STRLEN(str))) + { + validity = true; + return *this; + } + + // get memory needed + smival.value.string.ptr = (SmiLPBYTE) new unsigned char[nz]; + + if (smival.value.string.ptr) + { + MEMCPY(smival.value.string.ptr, str, (size_t) nz); + smival.value.string.len = nz; + validity = true; + } + else + validity = false; + + return *this; // return self reference +} + +//=============[ assignment to another oid object overloaded ]======== +OctetStr& OctetStr::operator=(const OctetStr &octet) +{ + if (this == &octet) return *this; // protect against assignment from self + + if (!octet.validity) return *this; // don't assign from invalid objs + + set_data(octet.smival.value.string.ptr, octet.smival.value.string.len); + + return *this; // return self reference +} + +//==============[ equivlence operator overloaded ]==================== +int operator==(const OctetStr &lhs, const OctetStr &rhs) +{ + if (lhs.smival.value.string.len != rhs.smival.value.string.len) + return false; + return (lhs.nCompare(rhs.smival.value.string.len, rhs) == 0); +} + +//==============[ not equivlence operator overloaded ]================ +int operator!=(const OctetStr &lhs, const OctetStr &rhs) +{ + if (lhs.smival.value.string.len != rhs.smival.value.string.len) + return true; + return (lhs.nCompare(rhs.smival.value.string.len, rhs) != 0); +} + +//==============[ less than < overloaded ]============================ +int operator<(const OctetStr &lhs, const OctetStr &rhs) +{ + int maxlen = lhs.smival.value.string.len > rhs.smival.value.string.len + ? lhs.smival.value.string.len : rhs.smival.value.string.len; + return (lhs.nCompare(maxlen, rhs) < 0); +} + +//==============[ less than <= overloaded ]=========================== +int operator<=(const OctetStr &lhs, const OctetStr &rhs) +{ + int maxlen = lhs.smival.value.string.len > rhs.smival.value.string.len + ? lhs.smival.value.string.len : rhs.smival.value.string.len; + return (lhs.nCompare(maxlen, rhs) <= 0); +} + +//===============[ greater than > overloaded ]======================== +int operator>(const OctetStr &lhs, const OctetStr &rhs) +{ + int maxlen = lhs.smival.value.string.len > rhs.smival.value.string.len + ? lhs.smival.value.string.len : rhs.smival.value.string.len; + return (lhs.nCompare(maxlen, rhs) > 0); +} + +//===============[ greater than >= overloaded ]======================= +int operator>=(const OctetStr &lhs, const OctetStr &rhs) +{ + int maxlen = lhs.smival.value.string.len > rhs.smival.value.string.len + ? lhs.smival.value.string.len : rhs.smival.value.string.len; + return (lhs.nCompare(maxlen, rhs) >=0); +} + +//===============[ equivlence operator overloaded ]=================== +int operator==(const OctetStr &lhs, const char *rhs) +{ + OctetStr to(rhs); + if (lhs.smival.value.string.len != to.smival.value.string.len) + return false; + return (lhs.nCompare(to.smival.value.string.len, to) == 0); +} + +//===============[ not equivlence operator overloaded ]=============== +int operator!=(const OctetStr &lhs, const char *rhs) +{ + OctetStr to(rhs); + if (lhs.smival.value.string.len != to.smival.value.string.len) + return true; + return (lhs.nCompare(to.smival.value.string.len, to) != 0); +} + +//===============[ less than < operator overloaded ]================== +int operator<(const OctetStr &lhs, const char *rhs) +{ + OctetStr to(rhs); + int maxlen = lhs.smival.value.string.len > to.smival.value.string.len + ? lhs.smival.value.string.len : to.smival.value.string.len; + return (lhs.nCompare(maxlen,to) < 0); +} + +//===============[ less than <= operator overloaded ]================= +int operator<=(const OctetStr &lhs, const char *rhs) +{ + OctetStr to(rhs); + int maxlen = lhs.smival.value.string.len > to.smival.value.string.len + ? lhs.smival.value.string.len : to.smival.value.string.len; + return (lhs.nCompare(maxlen, to) <= 0); +} + +//===============[ greater than > operator overloaded ]=============== +int operator>(const OctetStr &lhs, const char *rhs) +{ + OctetStr to(rhs); + int maxlen = lhs.smival.value.string.len > to.smival.value.string.len + ? lhs.smival.value.string.len : to.smival.value.string.len; + return (lhs.nCompare(maxlen, to) > 0); +} + +//===============[ greater than >= operator overloaded ]============== +int operator>=(const OctetStr &lhs, const char *rhs) +{ + OctetStr to(rhs); + int maxlen = lhs.smival.value.string.len > to.smival.value.string.len + ? lhs.smival.value.string.len : to.smival.value.string.len; + return (lhs.nCompare(maxlen, to) >= 0); +} + +//===============[ append operator, appends a string ]================ +OctetStr& OctetStr::operator+=(const char *a) +{ + unsigned char *tmp; + size_t slen, nlen; + + // get len of string + if (!a || ((slen = (size_t) STRLEN(a)) == 0)) + return *this; + + // total len of new octet + nlen = slen + (size_t) smival.value.string.len; + // get mem needed + tmp = (SmiLPBYTE) new unsigned char[nlen]; + + if (tmp) + { + // copy in the original 1st + MEMCPY(tmp, smival.value.string.ptr, (size_t) smival.value.string.len); + // copy in the string + MEMCPY(tmp + smival.value.string.len, a, (size_t) slen); + // delete the original + if (smival.value.string.ptr ) + delete [] smival.value.string.ptr; + // point to the new one + smival.value.string.ptr = tmp; + smival.value.string.len = nlen; + } + return *this; +} + +//================[ append one OctetStr to another ]================== +OctetStr& OctetStr::operator+=(const OctetStr& octet) +{ + unsigned char *tmp; + size_t slen, nlen; + + if (!octet.validity || + !(slen = (size_t)octet.len())) + return *this; + + // total len of new octet + nlen = slen + (size_t) smival.value.string.len; + // get mem needed + tmp = (SmiLPBYTE) new unsigned char[nlen]; + + if (tmp) + { + // copy in the original 1st + MEMCPY(tmp, smival.value.string.ptr, (size_t) smival.value.string.len); + // copy in the string + MEMCPY(tmp + smival.value.string.len, octet.data(), (size_t) slen); + // delete the original + if (smival.value.string.ptr ) + delete [] smival.value.string.ptr; + // point to the new one + smival.value.string.ptr = tmp; + smival.value.string.len = nlen; + } + return *this; +} + +//================[ appends a char ]================================== +OctetStr& OctetStr::operator+=(const unsigned char c) +{ + unsigned char *tmp; + + // get the memory needed plus one extra byte + tmp = (SmiLPBYTE) new unsigned char[smival.value.string.len + 1]; + + if (tmp) + { + MEMCPY(tmp, smival.value.string.ptr, (size_t)smival.value.string.len); + tmp[smival.value.string.len] = c; // assign in new byte + + if (smival.value.string.ptr) // delete the original + delete [] smival.value.string.ptr; + + smival.value.string.ptr = tmp; // point to new one + smival.value.string.len++; // up the len + } + return *this; // return self reference +} + + +//================[ compare n elements of an Octet ]================== +int OctetStr::nCompare(const unsigned long n, const OctetStr &o) const +{ + unsigned long n_max; + unsigned long w,strlen; + + if (n == 0) return 0; // Nothing to compare, strings are equal + + // both are empty, they are equal + if ((smival.value.string.len == 0) && (o.smival.value.string.len == 0)) + return 0; // equal + + // self is empty and param has something + if ((smival.value.string.len == 0) && (o.smival.value.string.len > 0)) + return -1; + + // self has something and param has nothing + if ((smival.value.string.len > 0) && (o.smival.value.string.len == 0)) + return 1; + + // now: n > 0; this.len > 0; o.len > 0 !!! + + // pick the Min of n, this and the param len + // this is the maximum # to iterate a search + strlen = smival.value.string.len < o.smival.value.string.len + ? smival.value.string.len : o.smival.value.string.len; + w = (n <= strlen) ? n : strlen; + + unsigned long z = 0; + while (z < w) + { + if (smival.value.string.ptr[z] < o.smival.value.string.ptr[z]) + return -1; // less than + if (smival.value.string.ptr[z] > o.smival.value.string.ptr[z]) + return 1; // greater than + z++; + } + + // now: z == w > 0 + // set n_max to min(n, max(len of strings)) + n_max = smival.value.string.len > o.smival.value.string.len + ? smival.value.string.len : o.smival.value.string.len; + if (n< n_max) n_max = n; + + if (w < n_max) // ==> we have compared too few bytes + { + if (smival.value.string.len < o.smival.value.string.len) + return -1; + else + return 1; + } + return 0; +} + +//================[ ASCII format return ]============================= +const char *OctetStr::get_printable() const +{ + for (unsigned long i=0; i < smival.value.string.len; i++) + { + if ((smival.value.string.ptr[i] != '\r')&& + (smival.value.string.ptr[i] != '\n')&& + (isprint((int) (smival.value.string.ptr[i]))==0)) + return get_printable_hex(); + } + + OctetStr *ncthis = PP_CONST_CAST(OctetStr*, this); + if (output_buffer_len < smival.value.string.len + 1) + { + if (output_buffer) delete [] ncthis->output_buffer; + + ncthis->output_buffer = new char[smival.value.string.len + 1]; + if (ncthis->output_buffer) + ncthis->output_buffer_len = smival.value.string.len + 1; + } + if (smival.value.string.len) + MEMCPY(ncthis->output_buffer, + smival.value.string.ptr, (unsigned int) smival.value.string.len); + ncthis->output_buffer[smival.value.string.len] = '\0'; + return output_buffer; +} + + +//================[ general Value = operator ]======================== +SnmpSyntax& OctetStr::operator=(const SnmpSyntax &val) +{ + if (this == &val) return *this; // protect against assignment from self + + // blow away the old value + if (smival.value.string.ptr) + { + delete [] smival.value.string.ptr; + smival.value.string.ptr = 0; + } + smival.value.string.len = 0; + validity = false; + + if (val.valid()){ + switch (val.get_syntax()){ + case sNMP_SYNTAX_OPAQUE: + case sNMP_SYNTAX_BITS: + case sNMP_SYNTAX_OCTETS: + case sNMP_SYNTAX_IPADDR: + set_data(((OctetStr &)val).smival.value.string.ptr, + ((OctetStr &)val).smival.value.string.len); + break; + } + } + return *this; +} + +#define ATOI(x) if ((x >= 48) && (x <= 57)) x = x-48; /* 0-9 */ \ + else if ((x >= 65) && (x <= 70)) x = x-55; /* A-F */ \ + else if ((x >= 97) && (x <=102)) x = x-87; /* a-f */ \ + else x = 0 + +//=======[ create an octet string from a hex string ]=================== +OctetStr OctetStr::from_hex_string(const OctetStr &hex_string) +{ + OctetStr val; + unsigned int p = 0; + unsigned int hex_len = 0; + + // make sure the string has at least one byte + if (hex_string.len() == 0) return val; + + // allocate max needed space for copy without spaces + unsigned char *hex, *hex_ptr; + hex = hex_ptr = new unsigned char[hex_string.len()]; + if (!hex) return val; + + // delete spaces + const unsigned char *ptr = hex_string.smival.value.string.ptr; + for (p = hex_string.len(); p > 0; p--) + { + unsigned char c = *ptr++; + if (c != ' ') + { + *hex_ptr++ = c; + ++hex_len; + } + } + + // leading 0 may be omitted + if (hex_len % 2) + { + unsigned char c = hex[0]; + ATOI(c); + val += c; + p = 1; + } + else + { + p = 0; + } + + while (p < hex_len) + { + unsigned char c = hex[p++]; + unsigned char d = hex[p++]; + + ATOI(c); + ATOI(d); + val += (c*16 + d); + } + delete[] hex; + return val; +} + +#undef ATOI + +//================[ format the output into hex ]======================== +const char *OctetStr::get_printable_hex() const +{ + int cnt; + char char_buf[80]; // holds ASCII representation of data + char *buf_ptr; // pointer into ASCII listing + char *line_ptr; // pointer into Hex listing + unsigned int storageNeeded; // how much space do we need ? + int local_len = (int) smival.value.string.len; + unsigned char *bytes = smival.value.string.ptr; + + storageNeeded = (unsigned int) ((smival.value.string.len/16)+1) * 72 + 1; + OctetStr *ncthis = PP_CONST_CAST(OctetStr*, this); + + if (output_buffer_len < storageNeeded) + { + if (output_buffer) delete [] ncthis->output_buffer; + + ncthis->output_buffer = new char[storageNeeded]; + if (ncthis->output_buffer) + ncthis->output_buffer_len = storageNeeded; + } + + line_ptr = ncthis->output_buffer; + + /*----------------------------------------*/ + /* processing loop for entire data buffer */ + /*----------------------------------------*/ + while (local_len > 0) + { + cnt = 16; /* print 16 bytes per line */ + buf_ptr = char_buf; + //sprintf(line_ptr, " "); + //line_ptr += 2; /* indent */ + + /*-----------------------*/ + /* process a single line */ + /*-----------------------*/ + while (cnt-- > 0 && local_len-- > 0) + { + sprintf(line_ptr, "%2.2X ", *bytes); + + line_ptr +=3; /* the display of a byte always 3 chars long */ + /* if (isprint(*bytes)) + *buf_ptr++ = *bytes; + else + *buf_ptr++ = '.';*/ + ++bytes; + } + ++cnt; + //*buf_ptr = 0; // null terminate string + + /*----------------------------------------------------------*/ + /* this is to make sure that the ASCII displays line up for */ + /* incomplete lines of hex */ + /*----------------------------------------------------------*/ +/* while (cnt-- > 0) + { + *line_ptr++ = ' '; + *line_ptr++ = ' '; + *line_ptr++ = ' '; + } +*/ + /*------------------------------------------*/ + /* append the ASCII display to the Hex line */ + /*------------------------------------------*/ +/*#ifndef __unix + sprintf(line_ptr," %s\n", char_buf); + line_ptr += 4 + strlen(char_buf); +#else + sprintf(line_ptr," %s\r\n", char_buf); + line_ptr += 5 + strlen(char_buf); +#endif // __unix + */ + } + return ncthis->output_buffer; +} + + +//==============[ Null out the contents of the string ]=================== +void OctetStr::clear() +{ + if (smival.value.string.ptr) + memset(smival.value.string.ptr, 0, smival.value.string.len); + if (output_buffer) + memset(output_buffer, 0, output_buffer_len); +} + +//============[Return the space needed for serialization]================= +int OctetStr::get_asn1_length() const +{ + if (smival.value.string.len < 0x80) + return smival.value.string.len + 2; + else if (smival.value.string.len < 0x100) + return smival.value.string.len + 3; + else if (smival.value.string.len < 0x10000) + return smival.value.string.len + 4; + else if (smival.value.string.len < 0x1000000) + return smival.value.string.len + 5; + return smival.value.string.len + 6; // should be safe for some time... +} diff --git a/backend/camembert/libkmsnmp/octet.h b/backend/camembert/libkmsnmp/octet.h new file mode 100644 index 0000000..b3629c6 --- /dev/null +++ b/backend/camembert/libkmsnmp/octet.h @@ -0,0 +1,387 @@ +/*_############################################################################ + _## + _## octet.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ O C T E T . H + + OCTETSTR CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-WINDOWS Win32 + BSD UNIX + + DESCRIPTION: + This class is fully contained and does not rely on or any other + SNMP libraries. This class is portable across any platform + which supports C++. + +=====================================================================*/ +// $Id: octet.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _OCTET_CLS +#define _OCTET_CLS + +#include "smival.h" + +//------------[ SNMP++ OCTETSTR CLASS DEF ]----------------------------- +class DLLOPT OctetStr: public SnmpSyntax +{ + public: + + //-----------[ Constructors and Destrucotr ]---------------------- + + /** + * Constructs a valid OctetStr with zero length. + */ + OctetStr(); + + /** + * Constructs a OctetStr with the given value. + * The OctetStr will be valid unless a call to new fails. + * + * @param str - Null terminated string + */ + OctetStr(const char *str); + + /** + * Constructs a OctetStr with the given value. + * The OctetStr will be valid unless a call to new fails. + * + * @param str - string that may contain null bytes + * @param len - length of the string + */ + OctetStr(const unsigned char *str, unsigned long len); + + /** + * Construct a OctetStr from another OctetStr. + * The OctetStr will be valid unless a call to new fails. + * + * @param octet - Value for the new object + */ + OctetStr(const OctetStr &octet); + + /** + * Destructor, frees allocated space. + */ + ~OctetStr(); + + //-----------[ Overloaded operators ]---------------------- + + /** + * Assign a char string to a OctetStr. + */ + OctetStr& operator=(const char *str); + + /** + * Assign a OctetStr to a OctetStr. + */ + OctetStr& operator=(const OctetStr &octet); + + /** + * Equal operator for two OctetStr. + */ + DLLOPT friend int operator==( const OctetStr &lhs, const OctetStr &rhs); + + /** + * Not equal operator for two OctetStr. + */ + DLLOPT friend int operator!=( const OctetStr &lhs, const OctetStr &rhs); + + /** + * Not equal operator for two OctetStr. + */ + DLLOPT friend int operator<( const OctetStr &lhs, const OctetStr &rhs); + + /** + * Less than operator for two OctetStr. + */ + DLLOPT friend int operator<=( const OctetStr &lhs,const OctetStr &rhs); + + /** + * Greater than operator for two OctetStr. + */ + DLLOPT friend int operator>( const OctetStr &lhs, const OctetStr &rhs); + + /** + * Greater than or equal operator for two OctetStr. + */ + DLLOPT friend int operator>=( const OctetStr &lhs, const OctetStr &rhs); + + /** + * Equal operator for OctetStr and char string. + */ + DLLOPT friend int operator==( const OctetStr &lhs, const char *rhs); + + /** + * Not equal operator for OctetStr and char string. + */ + DLLOPT friend int operator!=( const OctetStr &lhs, const char *rhs); + + /** + * Less than operator for OctetStr and char string. + */ + DLLOPT friend int operator<( const OctetStr &lhs, const char *rhs); + + /** + * Less than or equal operator for OctetStr and char string. + */ + DLLOPT friend int operator<=( const OctetStr &lhs, const char *rhs); + + /** + * Greater than operator for OctetStr and char string. + */ + DLLOPT friend int operator>( const OctetStr &lhs, const char *rhs); + + /** + * Greater than or equal operator for OctetStr and char string. + */ + DLLOPT friend int operator>=( const OctetStr &lhs, const char *rhs); + + /** + * Append a char string to this OctetStr. + */ + OctetStr& operator+=( const char *a); + + /** + * Append a single char to this OctetStr. + */ + OctetStr& operator+=( const unsigned char c); + + /** + * Append another OctetStr to this OctetStr. + */ + OctetStr& operator+=( const OctetStr& octet); + + /** + * Allow access as if it was an array. + * + * @note The given param is not checked for validity. + */ + unsigned char &operator[](int i) { return smival.value.string.ptr[i]; }; + + /** + * Allow access as if it was an array for const OctetStr objects. + * + * @note The given param is not checked for validity. + */ + unsigned char operator[](int i) const { return smival.value.string.ptr[i]; }; + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_OCTETS. + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OCTETS; }; + + /** + * Return the space needed for serialization. + */ + int get_asn1_length() const; + + /** + * Return validity of the object. + */ + bool valid() const { return validity; }; + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return ( SnmpSyntax *) new OctetStr(*this); }; + + /** + * Map other SnmpSyntax objects to OctetStr. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Get a printable ASCII value of the string. + * + * @note This method will return get_printable_hex() if the string + * contains not printable characters. + * + * @return Printable, null terminated string + */ + const char *get_printable() const; + + /** + * Get an ASCII formatted hex dump of the contents. + * The produced string of this method will look like this: + *

+   * 09 4F 63 74 65 74 53 74 72 3A 3A 67 65 74 5F 70    .OctetStr::get_p
+   * 72 69 6E 74 61 62 6C 65 5F 68 65 78 28 29          rintable_hex()
+   *                                                                     
+ * @return Printable, null terminated string. + */ + const char *get_printable_hex() const; + + /** + * Set the data on an already constructed OctetStr. + * The given string is copied to an internal member var, so the + * params can be destroyed afterwards. + * + * @param str - The new string value + * @param len - Length of the given string + */ + void set_data(const unsigned char *str, unsigned long len); + + /** + * Get the length of the string. + */ + unsigned long len() const { return smival.value.string.len; }; + + /** + * Get a pointer to internal data. + */ + unsigned char *data() const { return smival.value.string.ptr; }; + + // compare n elements of an octet + int nCompare( const unsigned long n, + const OctetStr &o) const; + + /** + * Build an OctetStr from a hex string. + * Called with "5465 737469 6e672074686973206D657468 6f 64 21" + * the returned value will be "Testing this method!" + * + * @param hex_string - The hex string (may contain spaces) + * @return created string + */ + static OctetStr from_hex_string(const OctetStr &hex_string); + + /** + * Null out the contents of the string. The string will be empty + * after calling this method + */ + void clear(); + + protected: + + /*mutable*/ char *output_buffer; // formatted Octet value + /*mutable*/ unsigned int output_buffer_len; // allocated space for outstring + bool validity; // validity boolean +}; + +//-----------[ End OctetStr Class ]------------------------------------- + +/** + * This class behaves exactly as the OctetStr class, beside the Syntax + * of this class is sNMP_SYNTAX_OPAQUE. + */ +class OpaqueStr: public OctetStr +{ + public: + /** + * Constructor creating a valid zero length OpaqueStr. + */ + OpaqueStr(): OctetStr() + { smival.syntax = sNMP_SYNTAX_OPAQUE; }; + + /** + * Constructs a OpaqueStr with the given value. + * The OpaqueStr will be valid unless a call to new fails. + * + * @param str - Null terminated string + */ + OpaqueStr(const char *str) : OctetStr(str) + { smival.syntax = sNMP_SYNTAX_OPAQUE; }; + + /** + * Constructs a OpaqueStr with the given value. + * The OpaqueStr will be valid unless a call to new fails. + * + * @param str - string that may contain null bytes + * @param len - length of the string + */ + OpaqueStr( const unsigned char *str, unsigned long len) + : OctetStr(str, len) { smival.syntax = sNMP_SYNTAX_OPAQUE; }; + + /** + * Construct a OpaqueStr from an OctetStr. + * The OpaqueStr will be valid unless a call to new fails. + * + * @param octet - Value for the new object + */ + OpaqueStr( const OctetStr &octet) : OctetStr(octet) + { smival.syntax = sNMP_SYNTAX_OPAQUE; }; + + /** + * Construct a OpaqueStr from another OpaqueStr. + * The OpaqueStr will be valid unless a call to new fails. + * + * @param opaque - Value for the new object + */ + OpaqueStr( const OpaqueStr& opaque) : OctetStr(opaque) + { smival.syntax = sNMP_SYNTAX_OPAQUE; }; + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + virtual SnmpSyntax *clone() const { return new OpaqueStr(*this); } + + /** + * Return the syntax. + * + * @return This method always returns sNMP_SYNTAX_OPAQUE. + */ + virtual SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OPAQUE; }; + + /** + * Map other SnmpSyntax objects to OpaqueStr. + */ + SnmpSyntax& operator=(const SnmpSyntax &val) + { return OctetStr::operator=(val); } + +}; + +#endif // _OCTET_CLS diff --git a/backend/camembert/libkmsnmp/oid.cpp b/backend/camembert/libkmsnmp/oid.cpp new file mode 100644 index 0000000..0648ccf --- /dev/null +++ b/backend/camembert/libkmsnmp/oid.cpp @@ -0,0 +1,741 @@ +/*_############################################################################ + _## + _## oid.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + O I D. C P P + + OID CLASS IMPLEMENTATION + + DESIGN + AUTHOR: + Peter E. Mellquist + + DESCRIPTION: + This module contains the implementation of the oid class. This + includes all protected and public member functions. The oid class + may be compiled stand alone without the use of any other library. + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEM(S): + MS-Windows Win32 + BSD UNIX + + +=====================================================================*/ +char oid_cpp_version[]="#(@) SNMP++ $Id: oid.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +//---------[ external C libaries used ]-------------------------------- +#include // standard io +#include // memcpy's +#include // strlen, etc.. +#include // standard library +#include // isdigit +#include // malloc, free + +#include "oid.h" // include def for oid class + +#define SNMPBUFFSIZE 11 // size of scratch buffer +#define SNMPCHARSIZE 11 // an individual oid instance as a string + +/* Borlands isdigit has a bug */ +#ifdef __BCPLUSPLUS__ +#define my_isdigit(c) ((c) >= '0' && (c) <= '9') +#else +#define my_isdigit isdigit +#endif + +//=============[Oid::Oid(void)]============================================ +// constructor using no arguments +// initialize octet ptr and string +// ptr to null +Oid::Oid() + : iv_str(0) +{ + smival.syntax = sNMP_SYNTAX_OID; + smival.value.oid.len = 0; + smival.value.oid.ptr = 0; +} + + +//=============[Oid::Oid(const char *dotted_string ]===================== +// constructor using a dotted string +// +// do a string to oid using the string passed in +Oid::Oid(const char *dotted_oid_string) + : iv_str(0) +{ + smival.syntax = sNMP_SYNTAX_OID; + smival.value.oid.len = 0; + smival.value.oid.ptr = 0; + StrToOid(dotted_oid_string, &smival.value.oid); +} + + +//=============[Oid::Oid(const Oid &oid) ]================================ +// constructor using another oid object +// +// do an oid copy using the oid object passed in +Oid::Oid(const Oid &oid) + : iv_str(0) +{ + smival.syntax = sNMP_SYNTAX_OID; + smival.value.oid.len = 0; + smival.value.oid.ptr = 0; + + // allocate some memory for the oid + // in this case the size to allocate is the same size as the source oid + if (oid.smival.value.oid.len) + { + smival.value.oid.ptr = (SmiLPUINT32) new unsigned long[oid.smival.value.oid.len]; + if (smival.value.oid.ptr) + OidCopy((SmiLPOID)&(oid.smival.value.oid), (SmiLPOID)&smival.value.oid); + } +} + + +//=============[Oid::Oid(const unsigned long *raw_oid, int oid_len) ]==== +// constructor using raw numeric form +// +// copy the integer values into the private member +Oid::Oid(const unsigned long *raw_oid, int oid_len) + : iv_str(0) +{ + smival.syntax = sNMP_SYNTAX_OID; + smival.value.oid.len = 0; + smival.value.oid.ptr = 0; + + if (raw_oid && (oid_len > 0)) + { + smival.value.oid.ptr = (SmiLPUINT32) new unsigned long[oid_len]; + if (smival.value.oid.ptr) + { + smival.value.oid.len = oid_len; + for (int i=0; i < oid_len; i++) + smival.value.oid.ptr[i] = raw_oid[i]; + } + } +} + +//=============[Oid::~Oid]============================================== +Oid::~Oid() +{ + delete_oid_ptr(); + if (iv_str) delete [] iv_str; // free up the output string +} + + +//=============[Oid::operator = const char * dotted_string ]============== +// assignment to a string operator overloaded +// +// free the existing oid +// create the new oid from the string +// return this object +Oid& Oid::operator=(const char *dotted_oid_string) +{ + delete_oid_ptr(); + + // assign the new value + StrToOid(dotted_oid_string, &smival.value.oid); + return *this; +} + + +//=============[Oid:: operator = const Oid &oid ]========================== +// assignment to another oid object overloaded +// +// free the existing oid +// create a new one from the object passed in +Oid& Oid::operator=(const Oid &oid) +{ + if (this == &oid) return *this; // protect against assignment from self + + delete_oid_ptr(); + + // check for zero len on source + if (oid.smival.value.oid.len == 0) + return *this; + + // allocate some memory for the oid + smival.value.oid.ptr = (SmiLPUINT32) new unsigned long[oid.smival.value.oid.len]; + if (smival.value.oid.ptr) + OidCopy((SmiLPOID)&(oid.smival.value.oid), (SmiLPOID)&smival.value.oid); + return *this; +} + + +//==============[Oid:: operator += const char *a ]========================= +// append operator, appends a string +// +// allocate some space for a max oid string +// extract current string into space +// concat new string +// free up existing oid +// make a new oid from string +// delete allocated space +Oid& Oid::operator+=(const char *a) +{ + unsigned long n; + + if (!a) return *this; + + if (*a == '.') ++a; + + n = (smival.value.oid.len * SNMPCHARSIZE) + (smival.value.oid.len) + + 1 + STRLEN(a); + char *ptr = new char[n]; + if (ptr) + { + OidToStr(&smival.value.oid, n, ptr); + if (ptr[0]) + STRCAT(ptr,"."); + STRCAT(ptr,a); + if (smival.value.oid.len) + { + delete [] smival.value.oid.ptr; + smival.value.oid.len = 0; + } + StrToOid(ptr, &smival.value.oid); + delete [] ptr; + } + return *this; +} + +//=============[ int operator == oid,oid ]================================= +// equivlence operator overloaded +int operator==(const Oid &lhs, const Oid &rhs) +{ + // ensure same len, then use nCompare + if (rhs.len() != lhs.len()) return 0; + return (lhs.nCompare(rhs.len(), rhs) == 0); +} + +//==============[ operator!=(Oid &x,Oid &y) ]============================= +//not equivlence operator overloaded +int operator!=(const Oid &lhs, const Oid &rhs) +{ + return (!(lhs==rhs)); // just invert == +} + +//==============[ operator<(Oid &x,Oid &y) ]============================= +// less than < overloaded +int operator<(const Oid &lhs, const Oid &rhs) +{ + int result; + // call nCompare with the current + // Oidx, Oidy and len of Oidx + if((result = lhs.nCompare(rhs.len(), rhs))<0) return 1; + if (result > 0) return 0; + + // if here, equivalent substrings, call the shorter one < + return (lhs.len() < rhs.len()); +} + +//==============[ operator<=(Oid &x,Oid &y) ]============================= +// less than <= overloaded +int operator<=(const Oid &x, const Oid &y) +{ + return ((x(Oid &x,Oid &y) ]============================= +// greater than > overloaded +int operator>(const Oid &x, const Oid &y) +{ + return (!(x<=y)); // just invert existing <= +} + +//==============[ operator==(Oid &x,char *) ]============================= +// equivlence operator overloaded +int operator==(const Oid &x, const char *dotted_oid_string) +{ + Oid to(dotted_oid_string); // create a temp oid object + return (x == to); // compare using existing operator +} + +//==============[ operator!=(Oid &x,char*) ]============================= +// not equivlence operator overloaded +int operator!=(const Oid &x, const char *dotted_oid_string) +{ + Oid to(dotted_oid_string); // create a temp oid object + return (x != to); // compare using existing operator +} + +//==============[ operator<(Oid &x,char*) ]============================= +// less than < operator overloaded +int operator<(const Oid &x, const char *dotted_oid_string) +{ + Oid to(dotted_oid_string); // create a temp oid object + return (x < to); // compare using existing operator +} + +//==============[ operator<=(Oid &x,char *) ]============================= +// less than <= operator overloaded +int operator<=(const Oid &x,char *dotted_oid_string) +{ + Oid to(dotted_oid_string); // create a temp oid object + return (x <= to); // compare using existing operator +} + +//==============[ operator>(Oid &x,char* ]============================= +// greater than > operator overloaded +int operator>(const Oid &x,const char *dotted_oid_string) +{ + Oid to(dotted_oid_string); // create a temp oid object + return (x > to); // compare using existing operator +} + +//==============[ operator>=(Oid &x,char*) ]============================= +// greater than >= operator overloaded +int operator>=(const Oid &x,const char *dotted_oid_string) +{ + Oid to(dotted_oid_string); // create a temp oid object + return (x >= to); // compare using existing operator +} + +//===============[Oid::set_data ]==---===================================== +// copy data from raw form... +void Oid::set_data(const unsigned long *raw_oid, + const unsigned int oid_len) +{ + if (smival.value.oid.len < oid_len) + { + delete_oid_ptr(); + + smival.value.oid.ptr = (SmiLPUINT32) new unsigned long[oid_len]; + if (!smival.value.oid.ptr) return; + } + MEMCPY((SmiLPBYTE) smival.value.oid.ptr, + (SmiLPBYTE) raw_oid, + (size_t) (oid_len*sizeof(SmiUINT32))); + smival.value.oid.len = oid_len; +} + + +//===============[Oid::trim(unsigned int) ]============================ +// trim off the n leftmost values of an oid +// Note!, does not adjust actual space for +// speed +void Oid::trim(const unsigned long n) +{ + // verify that n is legal + if ((n <= smival.value.oid.len) && (n > 0)) + { + smival.value.oid.len -= n; + if (smival.value.oid.len == 0) + delete_oid_ptr(); + } +} + +//===============[Oid::operator += const unsigned int) ]==================== +// append operator, appends an int +// +Oid& Oid::operator+=(const unsigned long i) +{ + Oid other(&i, 1); + (*this) += other; + return *this; +} + +//===============[Oid::operator += const Oid) ]======================== +// append operator, appends an Oid +// +// allocate some space for a max oid string +// extract current string into space +// concat new string +// free up existing oid +// make a new oid from string +// delete allocated space +Oid& Oid::operator+=(const Oid &o) +{ + SmiLPUINT32 new_oid; + + if (o.smival.value.oid.len == 0) + return *this; + + new_oid = (SmiLPUINT32) new unsigned long[smival.value.oid.len + o.smival.value.oid.len]; + if (new_oid == 0) + { + delete_oid_ptr(); + return *this; + } + + if (smival.value.oid.ptr) + { + MEMCPY((SmiLPBYTE) new_oid, + (SmiLPBYTE) smival.value.oid.ptr, + (size_t) (smival.value.oid.len*sizeof(SmiUINT32))); + + delete [] smival.value.oid.ptr; + } + + // out with the old, in with the new... + smival.value.oid.ptr = new_oid; + + MEMCPY((SmiLPBYTE) &new_oid[smival.value.oid.len], + (SmiLPBYTE) o.smival.value.oid.ptr, + (size_t) (o.smival.value.oid.len*sizeof(SmiUINT32))); + + smival.value.oid.len += o.smival.value.oid.len; + + return *this; +} + +//==============[Oid::get_printable(unsigned int start, n) ]============= +// return a dotted string starting at start, +// going n positions to the left +// NOTE, start is 1 based (the first id is at position #1) +const char *Oid::get_printable(const unsigned long start, + const unsigned long n) const +{ + Oid *ncthis = PP_CONST_CAST(Oid*, this); + char *&nc_iv_str = ncthis->iv_str; + unsigned long nz; + unsigned long my_start = start - 1; + unsigned long my_end = my_start + n; + + nz = (smival.value.oid.len * (SNMPCHARSIZE + 1)) + 1; + + if (iv_str) delete [] iv_str; // delete the previous output string + + nc_iv_str = new char[nz]; // allocate some space for the output string + if (iv_str == 0) + return 0; + + nc_iv_str[0] = 0; // init the string + + // cannot ask for more than there is.. + if ((my_start < 0) || (my_end > smival.value.oid.len)) + return nc_iv_str; + + char *cur_ptr = nc_iv_str; + bool first = true; + + // loop through and build up a string + for (unsigned long index = my_start; index < my_end; ++index) + { + // if not at begin, pad with a dot + if (first) + first = false; + else + *cur_ptr++ = '.'; + + // convert data element to a string + cur_ptr += sprintf(cur_ptr, "%lu", smival.value.oid.ptr[index]); + } + return nc_iv_str; +} + + +//=============[Oid::StrToOid(char *string, SmiLPOID dst) ]============== +// convert a string to an oid +int Oid::StrToOid(const char *str, SmiLPOID dstOid) +{ + unsigned long index = 0; + + // make a temp buffer to copy the data into first + SmiLPUINT32 temp; + unsigned long nz; + + if (str && *str) + { + nz = STRLEN(str); + } + else + { + dstOid->len = 0; + dstOid->ptr = 0; + return -1; + } + temp = (SmiLPUINT32) new unsigned long[nz]; + + if (temp == 0) return -1; // return if can't get the mem + + while ((*str) && (index < nz)) + { + unsigned long number = 0; + // skip over the dot + if (*str == '.') ++str; + + // grab a digit token and convert it to a long int + while (my_isdigit(*str)) + number = (number * 10) + *(str++) - '0'; + + // check for invalid chars + if ((*str) && (*str != '.')) + { + // found String -> converting it into an oid + if ((*str) && (*str == '$')) + { + ++str; + while ((*str) && (*str != '$')) { + temp[index] = (unsigned char)*str; + ++str; + ++index; + } + // skip over the $ + if (*str) ++str; + continue; + } + delete [] temp; + return -1; + } + + // stuff the value into the array and bump the counter + temp[index++] = number; + } + + // get some space for the real oid + dstOid->ptr = (SmiLPUINT32) new unsigned long[index]; + // return if can't get the mem needed + if(dstOid->ptr == 0) + { + delete [] temp; + return -1; + } + + // copy in the temp data + MEMCPY((SmiLPBYTE) dstOid->ptr, + (SmiLPBYTE) temp, + (size_t) (index*sizeof(SmiUINT32))); + + // set the len of the oid + dstOid->len = index; + + // free up temp data + delete [] temp; + + return (int) index; +} + + +//===============[Oid::OidCopy(source, destination) ]==================== +// Copy an oid +int Oid::OidCopy(SmiLPOID srcOid, SmiLPOID dstOid) +{ + // check source len ! zero + if (srcOid->len == 0) return -1; + + // copy source to destination + MEMCPY((SmiLPBYTE) dstOid->ptr, + (SmiLPBYTE) srcOid->ptr, + (size_t) (srcOid->len*sizeof(SmiUINT32))); + + //set the new len + dstOid->len = srcOid->len; + return (int) srcOid->len; +} + + +//===============[Oid::nCompare(n, Oid) ]================================= +// compare the n leftmost values of two oids (left-to_right ) +// +// self == Oid then return 0, they are equal +// self < Oid then return -1, < +// self > Oid then return 1, > +int Oid::nCompare(const unsigned long n, + const Oid &o) const +{ + unsigned long length = n; + bool reduced_len = false; + + // If both oids are too short, decrease len + while ((smival.value.oid.len < length) && (o.smival.value.oid.len < length)) + length--; + + if (length == 0) return 0; // equal + + // only compare for the minimal length + if (length > smival.value.oid.len) + { + length = smival.value.oid.len; + reduced_len = true; + } + if (length > o.smival.value.oid.len) + { + length = o.smival.value.oid.len; + reduced_len = true; + } + + unsigned long z = 0; + while (z < length) + { + if (smival.value.oid.ptr[z] < o.smival.value.oid.ptr[z]) + return -1; // less than + if (smival.value.oid.ptr[z] > o.smival.value.oid.ptr[z]) + return 1; // greater than + ++z; + } + + // if we truncated the len then these may not be equal + if (reduced_len) + { + if (smival.value.oid.len < o.smival.value.oid.len) return -1; + if (smival.value.oid.len > o.smival.value.oid.len) return 1; + } + return 0; // equal +} + + +//===============[Oid::nCompare(n, Oid) ]================================= +// compare the n rightmost bytes (right-to-left) +// returns 0, equal +// returns -1, < +// returns 1 , > +int Oid::RnCompare(const unsigned long n, const Oid &o) const +{ + unsigned long nz = n; + + // If both oids are too short, decrease nz + while ((len() < nz) && (o.len() < nz)) + nz--; + + // oid to compare must have at least the same number of sub-ids to + // comparison else the argument Oid is less than THIS + if (o.len() < nz) return -1; + + // also can't compare argument oid for sub-ids which THIS does not have + if (len() < nz) return -1; + + int start = (int) (len() -1); + int end = (int) start - (int) nz; + for (int z = start; z > end; z--) + { + // printf("s: %i e: %i z: %i o: %i, this: %i\n", + // start, end, z, o.smival.value.oid.ptr[z],this->smival.value.oid.ptr[z]); + if (smival.value.oid.ptr[z] < o.smival.value.oid.ptr[z]) return -1; + if (smival.value.oid.ptr[z] > o.smival.value.oid.ptr[z]) return 1; + } + return 0; // they are equal +} + + +//================[Oid::OidToStr ]========================================= +// convert an oid to a string +int Oid::OidToStr(const SmiOID *srcOid, + SmiUINT32 size, + char *str) const +{ + unsigned totLen = 0; + char szNumber[SNMPBUFFSIZE]; + int cur_len; + + str[0] = 0; // init the string + + // verify there is something to copy + if (srcOid->len == 0) + return -1; + + // loop through and build up a string + for (unsigned long index = 0; index < srcOid->len; ++index) + { + // convert data element to a string + cur_len = sprintf(szNumber, "%lu", srcOid->ptr[index]); + + // verify len is not over + if (totLen + cur_len + 1 >= size) + return -2; + + // if not at begin, pad with a dot + if (totLen) + str[totLen++] = '.'; + + // copy the string token into the main string + STRCPY(str + totLen, szNumber); + + // adjust the total len + totLen += cur_len; + } + return totLen+1; +} + + +//================[ general Value = operator ]======================== +SnmpSyntax& Oid::operator=(const SnmpSyntax &val) +{ + if (this == &val) return *this; // protect against assignment from self + + delete_oid_ptr(); + + // assign new value + if (val.valid()) + { + switch (val.get_syntax()) + { + case sNMP_SYNTAX_OID: + set_data(((Oid &)val).smival.value.oid.ptr, + (unsigned int)((Oid &)val).smival.value.oid.len); + break; + } + } + return *this; +} + +int Oid::get_asn1_length() const +{ + int length = 1; // for first 2 subids + + for (unsigned int i = 2; i < smival.value.oid.len; ++i) + { + unsigned long v = smival.value.oid.ptr[i]; + + if (v < 0x80) // 7 bits long subid + length += 1; + else if (v < 0x4000) // 14 bits long subid + length += 2; + else if (v < 0x200000) // 21 bits long subid + length += 3; + else if (v < 0x10000000) // 28 bits long subid + length += 4; + else // 32 bits long subid + length += 5; + } + + if (length < 128) + return length + 2; + else if (length < 256) + return length + 3; + return length + 4; +} diff --git a/backend/camembert/libkmsnmp/oid.h b/backend/camembert/libkmsnmp/oid.h new file mode 100644 index 0000000..ebcac84 --- /dev/null +++ b/backend/camembert/libkmsnmp/oid.h @@ -0,0 +1,419 @@ +/*_############################################################################ + _## + _## oid.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ O I D. H + + OID CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + This class is fully contained and does not rely on or any other + SNMP libraries. This class is portable across any platform + which supports C++. + +=====================================================================*/ +// $Id: oid.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _OID_H_ +#define _OID_H_ + +//------------------------------------------------------------------------ + +#include "smival.h" // derived class for all values +#include "collect.h" + +/** + * The Object Identifier Class. + * + * The Object Identification (Oid) class is the encapsulation of an + * SMI object identifier. The SMI object is a data identifier for a + * data element found in a Management Information Base (MIB), as + * defined by a MIB definition. The SMI Oid, its related structures + * and functions, are a natural fit for object orientation. In fact, + * the Oid class shares many common features to the C++ String + * class. For those of you familiar with the C++ String class or + * Microsoft's Foundation Classes (MFC) CString class, the Oid class + * will be familiar and easy to use. The Oid class is designed to be + * efficient and fast. The Oid class allows definition and + * manipulation of object identifiers. + */ +class DLLOPT Oid : public SnmpSyntax +{ + public: + + /** + * Construct an invalid Oid. + */ + Oid(); + + /** + * Construct an Oid from a dotted string. + * + * @param dotted_oid_string - for example "1.3.1.6.1.10" + */ + Oid(const char *dotted_oid_string); + + /** + * Constructor using another oid object (copy constructor). + * + * @param oid - Source Oid + */ + Oid (const Oid &oid); + + /** + * Constructor from array. + * + * @param raw_oid - array of oid values + * @param oid_len - length of array + */ + Oid(const unsigned long *raw_oid, int oid_len); + + /** + * Destructor. + */ + virtual ~Oid(); + + /** + * Return the current syntax. + * + * @return always sNMP_SYNTAX_OID + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_OID; }; + + /** + * Assignment from a string. + * + * @param dotted_oid_string - New value (for example "1.3.6.1.6.0"); + */ + virtual Oid& operator=(const char *dotted_oid_string); + + /** + * Assign one Oid to another. + */ + virtual Oid& operator=(const Oid &oid); + + /** + * Return the space needed for serialization. + */ + int get_asn1_length() const; + + /** + * Overloaded equal operator. + */ + DLLOPT friend int operator==(const Oid &lhs, const Oid &rhs); + + /** + * Overloaded not equal operator. + */ + DLLOPT friend int operator!=(const Oid &lhs, const Oid &rhs); + + /** + * Overloaded less than < operator. + */ + DLLOPT friend int operator<(const Oid &lhs, const Oid &rhs); + + /** + * Overloaded less than <= operator. + */ + DLLOPT friend int operator<=(const Oid &lhs, const Oid &rhs); + + /** + * Overloaded greater than > operator. + */ + DLLOPT friend int operator>(const Oid &lhs, const Oid &rhs); + + /** + * Overloaded greater than >= operator. + */ + DLLOPT friend int operator>=(const Oid &lhs, const Oid &rhs); + + /** + * Overloaded equal operator operator. + */ + DLLOPT friend int operator==(const Oid &lhs, const char *rhs); + + /** + * Overloaded not equal operator. + */ + DLLOPT friend int operator!=(const Oid &lhs, const char *rhs); + + /** + * Overloaded less than < operator. + */ + DLLOPT friend int operator<(const Oid &lhs, const char *rhs); + + /** + * Overloaded less than <= operator. + */ + DLLOPT friend int operator<=(const Oid &lhs, char *rhs); + + /** + * Overloaded greater than > operator. + */ + DLLOPT friend int operator>(const Oid &lhs, const char *rhs); + + /** + * Overloaded greater than >= operator. + */ + DLLOPT friend int operator>=(const Oid &lhs, const char *rhs); + + /** + * Overloaded operator +, Concatenate two Oids. + */ + DLLOPT friend Oid operator +(const Oid &lhs, const Oid &rhs) + { Oid tmp(lhs); tmp += rhs; return tmp;}; + + /** + * Append operator, appends the dotted oid string. + * + * @param a - dotted oid string, for example "5.192.14.6" + */ + Oid& operator+=(const char *a); + + /** + * Appends an int. + * + * @param i - Value to add at the end of the Oid + */ + Oid& operator+=(const unsigned long i); + + /** + * Appends an Oid. + * + * @param o - Oid to add at the end + */ + Oid& operator+=(const Oid &o); + + /** + * Allows element access as an array. + * This method behaves like real array: if your position + * is out of bounds, you're lost! + * + * @param position - valid position -- 0 to (len() - 1) + * + * @return Value on the given position + */ + unsigned long &operator[](int position) + { return smival.value.oid.ptr[position]; }; + + /** + * Allows element access as an array for const objects. + * This method behaves like real array: if your position + * is out of bounds, you're lost! + * + * @param position - valid position -- 0 to (len() - 1) + * + * @return Value on the given position + */ + unsigned long operator[](int position) const + { return smival.value.oid.ptr[position]; }; + + /** + * Get the WinSnmp oid part. + * @note This method returns a pointer to internal data. + * If it is modified, the Oid changes too. + * + * @return pointer to the internal oid structure. + */ + SmiLPOID oidval() { return (SmiLPOID) &smival.value.oid; }; + + /** + * Set the data from raw form. + * + * @param raw_oid - Array of new values + * @param oid_len - Length of the array raw_oid + */ + void set_data(const unsigned long *raw_oid, const unsigned int oid_len); + + + /** + * Get the length of the oid. + */ + unsigned long len() const { return smival.value.oid.len; }; + + /** + * Trim off the rightmost values of an oid. + * + * @param n - Trim off n values from the right (default is one) + */ + void trim(const unsigned long n = 1); + + /** + * Compare two Oids from the left in direction left-to-right. + * + * @param n - Subvalues to compare + * @param o - The Oid to compare with + * + * @return 0 if equal / -1 if less / 1 if greater + */ + int nCompare(const unsigned long n, const Oid &o) const; + + /** + * Compare two Oids from the right in direction right-to left. + * + * @param n - Subvalues to compare + * @param o - The Oid to compare with + * + * @return 0 if equal / -1 if less / 1 if greater + */ + int RnCompare(const unsigned long n, const Oid &o) const; + + /** + * Return validity of the object. + */ + bool valid() const { return (smival.value.oid.ptr ? true : false); }; + + /** + * Get a printable ASCII string of the whole value. + * + * @return Dotted oid string (for example "1.3.6.1.6.0") + */ + const char *get_printable() const + { return get_printable(1, smival.value.oid.len); }; + + /** + * Get a printable ASCII string of the right part of the value. + * + * @param n - positions to print, counted from right. + * + * @return Dotted oid string (for example "6.0") + */ + const char *get_printable(const unsigned long n) const + { return get_printable(smival.value.oid.len - n + 1, n); }; + + /** + * Get a printable ASCII string of a part of the value. + * + * @param start - First position to print, starting with 1 (not zero!) + * @param n - positions to print. + * + * @return Dotted oid string (for example "3.6.1.6") + */ + const char *get_printable(const unsigned long start, + const unsigned long n) const; + + /** + * Clone this object. + * + * @return Pointer to the newly created object (allocated through new). + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new Oid(*this); }; + + /** + * Map other SnmpSyntax objects to Oid. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + protected: + /** + * Convert a string to an smi oid. + * + * @param string - input string + * @param dstOid - destination oid + */ + virtual int StrToOid(const char *string, SmiLPOID dstOid); + + /** + * Clone an smi oid. + * + * @param srcOid - source oid + * @param dstOid - destination oid + */ + virtual int OidCopy(SmiLPOID srcOid, SmiLPOID dstOid); + + /** + * Convert an smi oid to its string representation. + * + * @param srcOid - source oid + * @param size - size of string + * @param string - pointer to string + */ + virtual int OidToStr(const SmiOID *srcOid, + SmiUINT32 size, + char *string) const; + + /** + * Free the internal oid pointer and set the pointer and the length to zeor. + */ + inline void delete_oid_ptr(); + + //----[ instance variables ] + + /*mutable*/ char *iv_str; // used for returning oid string +}; + +//-----------[ End Oid Class ]------------------------------------- + +// create OidCollection type +typedef SnmpCollection OidCollection; + +inline void Oid::delete_oid_ptr() +{ + // delete the old value + if (smival.value.oid.ptr) + { + delete [] smival.value.oid.ptr; + smival.value.oid.ptr = 0; + } + smival.value.oid.len = 0; +}; + +//==============[ operator>=(Oid &x,Oid &y) ]============================= +// greater than >= overloaded +inline int operator>=(const Oid &x, const Oid &y) +{ + return (!(x PDU_MAX_VBS) { validity = false; return; } + + // loop through and assign internal vbs + for (int z = 0; z < pvb_count; ++z) + { + vbs[z] = new Vb(pvbs[z]); + if (vbs[z] == 0) // check for new fail + { + for (int y = 0; y < z; ++y) delete vbs[y]; // free vbs + validity = false; + return; + } + } + + vb_count = pvb_count; // assign the vb count +} + +//=====================[ destructor ]==================================== +Pdu::~Pdu() +{ + for (int z = 0; z < vb_count; ++z) + delete vbs[z]; +} + +//=====================[ assignment to another Pdu object overloaded ]=== +Pdu& Pdu::operator=(const Pdu &pdu) +{ + if (this == &pdu) return *this; // check for self assignment + + // Initialize all mv's + error_status = pdu.error_status; + error_index = pdu.error_index; + request_id = pdu.request_id; + pdu_type = pdu.pdu_type; + notify_id = pdu.notify_id; + notify_timestamp = pdu.notify_timestamp; + notify_enterprise = pdu.notify_enterprise; +#ifdef _SNMPv3 + security_level = pdu.security_level; + message_id = pdu.message_id; + context_name = pdu.context_name; + context_engine_id = pdu.context_engine_id; + maxsize_scopedpdu = pdu.maxsize_scopedpdu; +#endif + if (pdu.v1_trap_address_set) + { + v1_trap_address = pdu.v1_trap_address; + v1_trap_address_set = true; + } + else + v1_trap_address_set = false; + + validity = true; + + // free up old vbs + for (int z = 0; z < vb_count; ++z) + delete vbs[z]; + vb_count = 0; + + // check for zero case + if (pdu.vb_count == 0) return *this; + + // loop through and fill em up + for (int y = 0; y < pdu.vb_count; ++y) + { + vbs[y] = new Vb(*(pdu.vbs[y])); + // new failure + if (vbs[y] == 0) + { + for (int x = 0; x < y; ++x) delete vbs[x]; // free vbs + validity = false; + return *this; + } + } + + vb_count = pdu.vb_count; + return *this; +} + +// append operator, appends a string +Pdu& Pdu::operator+=(Vb &vb) +{ + if (vb_count + 1> PDU_MAX_VBS) // do we have room? + return *this; + + vbs[vb_count] = new Vb(vb); // add the new one + + if (vbs[vb_count]) // up the vb count on success + { + ++vb_count; + validity = true; // set up validity + } + + return *this; // return self reference +} + +//=====================[ extract Vbs from Pdu ]========================== +int Pdu::get_vblist(Vb* pvbs, const int pvb_count) +{ + if ((!pvbs) || (pvb_count < 0) || (pvb_count > vb_count)) + return FALSE; + + // loop through all vbs and assign to params + for (int z = 0; z < pvb_count; ++z) + pvbs[z] = *vbs[z]; + + return TRUE; +} + +//=====================[ deposit Vbs ]=================================== +int Pdu::set_vblist(Vb* pvbs, const int pvb_count) +{ + // if invalid then don't destroy + if ((!pvbs) || (pvb_count < 0) || (pvb_count > PDU_MAX_VBS)) + return FALSE; + + // free up current vbs + for (int z = 0; z < vb_count; ++z) delete vbs[z]; + vb_count = 0; + + // check for zero case + if (pvb_count == 0) + { + validity = true; + error_status = 0; + error_index = 0; + request_id = 0; + return FALSE; + } + + // loop through all vbs and reassign them + for (int y = 0; y < pvb_count; ++y) + { + vbs[y] = new Vb(pvbs[y]); + // check for new fail + if (vbs[y] == 0) + { + for (int x = 0; x < y; ++x) delete vbs[x]; // free vbs + validity = false; + return FALSE; + } + } + + vb_count = pvb_count; + + // clear error status and index since no longer valid + // request id may still apply so don't reassign it + error_status = 0; + error_index = 0; + validity = true; + + return TRUE; +} + +//===================[ get a particular vb ]============================= +// here the caller has already instantiated a vb object +// index is zero based +int Pdu::get_vb(Vb &vb, const int index) const +{ + if (index < 0) return FALSE; // can't have an index less than 0 + if (index > vb_count - 1) return FALSE; // can't ask for something not there + + vb = *vbs[index]; // asssign it + + return TRUE; +} + +//===================[ set a particular vb ]============================= +int Pdu::set_vb(Vb &vb, const int index) +{ + if (index < 0) return FALSE; // can't have an index less than 0 + if (index > vb_count - 1) return FALSE; // can't ask for something not there + + Vb *victim = vbs[index]; // save in case new fails + vbs[index] = new Vb (vb); + if (vbs[index]) + delete victim; + else + { + vbs[index] = victim; + return FALSE; + } + return TRUE; +} + +// trim off the last vb +int Pdu::trim(const int count) +{ + // verify that count is legal + if ((count < 0) || (count > vb_count)) return FALSE; + + int lp = count; + + while (lp != 0) + { + if (vb_count > 0) + { + delete vbs[vb_count-1]; + vb_count--; + } + lp--; + } + return TRUE; +} + +// delete a Vb anywhere within the Pdu +int Pdu::delete_vb(const int p) +{ + // position has to be in range + if ((p<0) || (p > vb_count - 1)) return FALSE; + + // safe to remove it + delete vbs[ p]; + + for (int z = p; z < vb_count - 1; ++z) + { + vbs[z] = vbs[z+1]; + } + vb_count--; + + return TRUE; +} + + +// Get the SNMPv1 trap address +int Pdu::get_v1_trap_address(GenAddress &address) const +{ + if (v1_trap_address_set == false) + return FALSE; + + address = v1_trap_address; + return TRUE; +} + +// Set the SNMPv1 trap address +int Pdu::set_v1_trap_address(const Address &address) +{ + v1_trap_address = address; + if (v1_trap_address.valid()) + v1_trap_address_set = true; + else + v1_trap_address_set = false; + + return v1_trap_address_set; +} + +int Pdu::get_asn1_length() const +{ + int length = 0; + + // length for all vbs + for (int i = 0; i < vb_count; ++i) + { + length += vbs[i]->get_asn1_length(); + } + + // header for vbs + if (length < 0x80) + length += 2; + else if (length <= 0xFF) + length += 3; + else if (length <= 0xFFFF) + length += 4; + else if (length <= 0xFFFFFF) + length += 5; + else + length += 6; + + // req id, error status, error index + SnmpInt32 i32(request_id ? request_id : PDU_MAX_RID); + length += i32.get_asn1_length(); + i32 = error_status; + length += i32.get_asn1_length(); + i32 = error_index; + length += i32.get_asn1_length(); + + // header for data_pdu + if (length < 0x80) + length += 2; + else if (length <= 0xFF) + length += 3; + else if (length <= 0xFFFF) + length += 4; + else if (length <= 0xFFFFFF) + length += 5; + else + length += 6; + +#ifdef _SNMPv3 + // now the scopedpdu part sequence (4), context engine, id context name + length += 4 + 2 + context_engine_id.len() + 2 + context_name.len(); + + // An encrypted message is transported as an octet string + if (security_level == SNMP_SECURITY_LEVEL_AUTH_PRIV) + { + // assume that encryption increases the data to a multiple of 16 + int mod = length % 16; + if (mod) length += 16 - mod; + + length += 4; + } +#endif + + return length; +} + + + +// DEPRECATED FUNCTIONS +void set_error_status( Pdu *pdu, const int status) +{ if (pdu) pdu->set_error_status(status); } +void set_error_index( Pdu *pdu, const int index) +{ if (pdu) pdu->set_error_index(index); } +void clear_error_status( Pdu *pdu) +{ if (pdu) pdu->clear_error_status(); } +void clear_error_index( Pdu *pdu) +{ if (pdu) pdu->clear_error_index(); } +void set_request_id( Pdu *pdu, const unsigned long rid) +{ if (pdu) pdu->set_request_id(rid); } diff --git a/backend/camembert/libkmsnmp/pdu.h b/backend/camembert/libkmsnmp/pdu.h new file mode 100644 index 0000000..17befce --- /dev/null +++ b/backend/camembert/libkmsnmp/pdu.h @@ -0,0 +1,492 @@ +/*_############################################################################ + _## + _## pdu.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ P D U . H + + PDU CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Pdu class definition. Encapsulation of an SMI Protocol + Data Unit (PDU) in C++. + +=====================================================================*/ +// $Id: pdu.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _PDU_CLS +#define _PDU_CLS + +#include "config_snmp_pp.h" +#include "address.h" +#include "timetick.h" +#include "octet.h" +#include "oid.h" + +class Vb; + +/** The maximum amount of Vb objects a Pdu can contain (config_snmp_pp.h). */ +#define MAX_VBS PDU_MAX_VBS + +#define PDU_MAX_RID 32767 ///< max request id to use +#define PDU_MIN_RID 1000 ///< min request id to use + +//======================================================================= +// Pdu Class +//======================================================================= +/** + * Pdu class... + */ +class DLLOPT Pdu +{ + public: + + /** + * Constructor no args. + * + * This constructor creates a valid empty Pdu object. + */ + Pdu(); + + /** + * Constructor with vbs. + * + * The Pdu class does not take ownership of the array and the Vb + * objects, so if these were allocated with new, they must be freed + * by te user with delete. + * + * @param pvbs - Array of pointers to Vb objects + * @param pvb_count - Length of the array + */ + Pdu(Vb* pvbs, const int pvb_count); + + /** + * Constructor with another Pdu instance. + * + * @param pdu - source pdu object + */ + Pdu(const Pdu &pdu) : vb_count(0) { *this = pdu; }; + + /** + * Destructor + */ + virtual ~Pdu(); + + /** + * Overloaded assignment operator. + * + * @param pdu - Pdu that should be assigned to this object + */ + Pdu& operator=(const Pdu &pdu); + + /** + * Append a vb to the pdu. + * + * @param vb - The Vb that should be added (as last vb) to the pdu + */ + Pdu& operator+=(Vb &vb); + + /** + * Clone a Pdu object. + * + * @return Pointer to a newly created Pdu object, that is identical to this + */ + Pdu *clone() const { return new Pdu(*this); }; + + /** + * Get Pointers to all Vbs from Pdu. + * + * The caller has to allocate the array. The returned pointers point + * to the Vb objects that are internally used by the pdu. So any + * changes to the returned Vb objects will change the pdu. If the + * pdu is modified (e.g. through Pdu::trim()) afterwards, the + * returned array will contain invalid pointers. + * + * @param pvbs - Array of empty pointers of size pvb_count + * @param pvb_count - Amount of Vb pointers to get + * + * @return TRUE on success + */ + int get_vblist(Vb* pvbs, const int pvb_count); + + /** + * Deposit all Vbs to Pdu. + * + * The vb objects of the pdu will be freed and the objects from the + * array will be cloned and added to the pdu. If this method returns + * FALSE, the pdu will not conatin any Vb objects. + * + * @param pvbs - Array of valid pointers of size pvb_count + * @param pvb_count - Amount of Vb pointers i the array + * + * @return TRUE on success + */ + int set_vblist(Vb* pvbs, const int pvb_count); + + /** + * Get a particular Vb. + * + * @param vb - Object to store the vb + * @param index - The vb to get (zero is the first vb) + * + * @return TRUE on success + */ + int get_vb(Vb &vb, const int index) const; + + /** + * Return a reference to a particular Vb. + * + * @note Before calling this method, make sure that there + * is a Vb using get_vb_count(). + * + * @param index - The Vb to return starting with 0. + * @return A const reference to the Vb + */ + const Vb &get_vb(const int index) const { return *vbs[index]; }; + + /** + * Set a particular vb. + * + * If this method returns FALSE, the pdu has not been modified. + * + * @param vb - Source vb + * @param index - The vb to set (zero is the first vb) + * + * @return TRUE on success + */ + int set_vb(Vb &vb, const int index); + + /** + * Get the number of vbs. + * + * @return The number of Vb objects within the pdu. + */ + int get_vb_count() const { return vb_count; }; + + /** + * Get the error status. + * + * @return The SNMP error status + */ + int get_error_status() const { return error_status; }; + + /** + * Set the error status. + * + * @param err - The SNMP error status. + */ + void set_error_status(const int err) { error_status = err; }; + + /** + * Clear the error status. + */ + void clear_error_status() { error_status = 0; }; + + /** + * Get the error index. + * + * @return The SNMP error index + */ + int get_error_index() const { return error_index; }; + + /** + * Set the error index. + * + * @param err - The SNMP error index. + */ + void set_error_index(const int err) { error_index = err; }; + + /** + * Clear the error index. + */ + void clear_error_index() { error_index = 0; }; + + /** + * Get the request id. + * + * @return The SNMP request id + */ + unsigned long get_request_id() const { return request_id; }; + + /** + * Set the request id. + * + * @param rid - The SNMP request id + */ + void set_request_id(const unsigned long rid) { request_id = rid; }; + + /** + * Get the pdu type. + */ + unsigned short get_type() const { return pdu_type; }; + + /** + * Set the pdu type. + */ + void set_type(unsigned short type) { pdu_type = type; }; + + /** + * Returns validity of Pdu instance. + */ + bool valid() const { return validity; }; + + /** + * Trim off vbs. + * + * @param count - number of vbs to trim of, starting with the last + * @return TRUE on success, FALSE if nothing was done + */ + int trim(const int count=1); + + /** + * Delete a Vb anywhere within the Pdu. + * + * @param position - Delete the Vb at this position (starting with 0) + * @return TRUE on success + */ + int delete_vb(const int position); + + /** + * Set notify timestamp. + */ + void set_notify_timestamp(const TimeTicks &ts) { notify_timestamp = ts; }; + + /** + * Get notify timestamp. + */ + void get_notify_timestamp(TimeTicks &ts) const { ts = notify_timestamp; }; + + /** + * Set the notify id. + */ + void set_notify_id(const Oid id) { notify_id = id; }; + + /** + * Get the notify id. + */ + void get_notify_id(Oid &id) const { id = notify_id; }; + + /** + * Set the notify enterprise. + */ + void set_notify_enterprise(const Oid &e) { notify_enterprise = e; }; + + /** + * Get the notify enterprise. + */ + void get_notify_enterprise(Oid & e) const { e = notify_enterprise; }; + +#ifdef _SNMPv3 + /** + * Set the security level that should be used when this Pdu is sent. + * The default security level of a Pdu is SNMP_SECURITY_LEVEL_NOAUTH_NOPRIV. + * + * @param level - One of SNMP_SECURITY_LEVEL_NOAUTH_NOPRIV, + * SNMP_SECURITY_LEVEL_AUTH_NOPRIV, + * SNMP_SECURITY_LEVEL_AUTH_PRIV + */ + void set_security_level(const int level) { security_level = level; }; + + /** + * Return the security level of the Pdu. + * + * @return - the security level + */ + int get_security_level() const { return security_level; }; + + /** + * Set the context name of the Pdu. + * + * @param name - The context name + */ + void set_context_name(const OctetStr &name) { context_name = name; }; + + /** + * Set the context name of the Pdu. + * + * @param name - The context name + */ + void set_context_name(const char * name) { context_name = name; }; + + /** + * Get the context name of the Pdu. + * + * @param name - Object fot the context name + */ + void get_context_name(OctetStr &name) const { name = context_name; }; + + /** + * Get the context name of the Pdu. + * + * @return - Return the context name as an OctetStr + */ + const OctetStr& get_context_name() const { return context_name; }; + + /** + * Set the context engine id of the Pdu. + * + * @param id - The new context engine id + */ + void set_context_engine_id(const OctetStr &id) { context_engine_id = id; }; + + /** + * Set the context engine id of the Pdu. + * + * @param id - The new context engine id + */ + void set_context_engine_id(const char *id) { context_engine_id = id; }; + + /** + * Get the context engine id of the Pdu. + * + * @param id - Object for the context engine + */ + void get_context_engine_id(OctetStr &id) const { id = context_engine_id; }; + + /** + * Get the context engine id of the Pdu. + * + * @return - Return the context engine id as an OctetStr + */ + const OctetStr& get_context_engine_id() const { return context_engine_id; }; + + /** + * Set the SNMPv3 message id (msgID) + * + * @param msg_id - the message id of the received message + */ + void set_message_id(const unsigned long msg_id) { message_id = msg_id; } + + /** + * Get the SNMPv3 message id (msgID) + * + * @return - the message id of the received message + */ + unsigned long get_message_id() const { return message_id; } + + /** + * Set the maximum size of the scoped pdu to be included in a + * possible response message. + * + * @param l - the maximum size + */ + void set_maxsize_scopedpdu(unsigned long l) { maxsize_scopedpdu = l; }; + + /** + * Get the maximum size of the scoped pdu to be included in a + * possible response message. + * + * @return - the maximum size + */ + unsigned long get_maxsize_scopedpdu() const { return maxsize_scopedpdu; }; + +#endif // _SNMPv3 + + /** + * Get the SNMPv1 trap address + */ + int get_v1_trap_address(GenAddress &address) const; + + /** + * Set the SNMPv1 trap address + */ + int set_v1_trap_address(const Address &address); + + /** + * Return the length of the encoded vbs with pdu header. + * + * @note this method wll not work for v1 traps. + */ + int get_asn1_length() const; + + //-------------[ protected instance variables ]-------------------------- + protected: + Vb *vbs[PDU_MAX_VBS]; // pointer to array of Vbs + int vb_count; // count of Vbs + int error_status; // SMI error status + int error_index; // SMI error index + bool validity; // valid boolean + unsigned long request_id; // SMI request id + unsigned short pdu_type; // derived at run time based on request type + // for notify Pdu objects only + // traps & notifies + TimeTicks notify_timestamp; // a timestamp associated with an infor + Oid notify_id; // an id + Oid notify_enterprise; + GenAddress v1_trap_address; // address object + int v1_trap_address_set; +#ifdef _SNMPv3 + // specific Objects for SNMPv3 + int security_level; // the securityLevel with which this Pdu + // should be sent or was received + unsigned long message_id; + unsigned long maxsize_scopedpdu; + OctetStr context_name; + OctetStr context_engine_id; +#endif // _SNMPv3 + +}; + +#if 1 +//! deprecated: set the error status +DLLOPT void set_error_status(Pdu *pdu, const int status); +//! deprecated: set the error index +DLLOPT void set_error_index(Pdu *pdu, const int index); +//! deprecated: clear error status +DLLOPT void clear_error_status(Pdu *pdu); +//! deprecated: clear error index +DLLOPT void clear_error_index(Pdu *pdu); +//! deprecated: set the request id +DLLOPT void set_request_id(Pdu *pdu, const unsigned long rid); +#endif +#endif //_PDU_CLS diff --git a/backend/camembert/libkmsnmp/smi.h b/backend/camembert/libkmsnmp/smi.h new file mode 100644 index 0000000..466665b --- /dev/null +++ b/backend/camembert/libkmsnmp/smi.h @@ -0,0 +1,235 @@ +/*_############################################################################ + _## + _## smi.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ S M I . H + + SMI DEFINITIONS + + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Address class definition. Encapsulates various network + addresses into easy to use, safe and portable classes. + +=====================================================================*/ +// $Id: smi.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _SMIDEF +#define _SMIDEF + +// make sure configuration is included first +#include "config_snmp_pp.h" + +#define WINFAR +#define STRCAT strcat +#define STRLEN strlen +#define MEMCPY memcpy +#define STRCPY strcpy +#define MEMCMP memcmp +#define XPORT + +// HANDLE needs to be defined for each type of platform +// for win32 - HANDLE is a HWND +// for unix - HANDLE is an unsigned long +// unix and win32 ( Windu compile , unix takes presedence ) +#ifdef WIN32 +#ifndef SNMPHANDLE +#define SNMPHANDLE HWND +#endif +#endif + + +#ifdef __unix +#ifndef SNMPHANDLE +#define SNMPHANDLE unsigned long +#define DLLOPT +#endif +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +//----------[ ASN/BER Base Types ]----------------------------------------- +/** @name ASN/BER Base Types + * + * Basic Encoding Rules (BER) (used in forming SYNTAXes and certain + * SNMP types/values). + */ +//@{ +#define aSN_UNIVERSAL (0x00) +#define aSN_APPLICATION (0x40) +#define aSN_CONTEXT (0x80) +#define aSN_PRIVATE (0xC0) +#define aSN_PRIMITIVE (0x00) +#define aSN_CONSTRUCTOR (0x20) +//@} + +//------[ SNMP ObjectSyntax Values ]--------------------------------------- +#define sNMP_SYNTAX_SEQUENCE (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x10) + +/** @name Syntax Types + * + * These values are used in the "syntax" member of the smiVALUE + * structure which follows. + * + * The get_syntax() method of any class derived from SnmpSyntax returns + * one of these values. + * + * @note UInt32 is indistinguishable from Gauge32 per SNMPv2 Draft Standard + * @note NsapAddr is obsoleted as unique SMI type per SNMPv2 Draft Standard + */ +//@{ +#define sNMP_SYNTAX_INT (aSN_UNIVERSAL | aSN_PRIMITIVE | 0x02) +#define sNMP_SYNTAX_BITS (aSN_UNIVERSAL | aSN_PRIMITIVE | 0x03) +#define sNMP_SYNTAX_OCTETS (aSN_UNIVERSAL | aSN_PRIMITIVE | 0x04) +#define sNMP_SYNTAX_NULL (aSN_UNIVERSAL | aSN_PRIMITIVE | 0x05) +#define sNMP_SYNTAX_OID (aSN_UNIVERSAL | aSN_PRIMITIVE | 0x06) +#define sNMP_SYNTAX_INT32 sNMP_SYNTAX_INT +#define sNMP_SYNTAX_IPADDR (aSN_APPLICATION | aSN_PRIMITIVE | 0x00) +#define sNMP_SYNTAX_CNTR32 (aSN_APPLICATION | aSN_PRIMITIVE | 0x01) +#define sNMP_SYNTAX_GAUGE32 (aSN_APPLICATION | aSN_PRIMITIVE | 0x02) +#define sNMP_SYNTAX_TIMETICKS (aSN_APPLICATION | aSN_PRIMITIVE | 0x03) +#define sNMP_SYNTAX_OPAQUE (aSN_APPLICATION | aSN_PRIMITIVE | 0x04) +#define sNMP_SYNTAX_CNTR64 (aSN_APPLICATION | aSN_PRIMITIVE | 0x06) +#define sNMP_SYNTAX_UINT32 sNMP_SYNTAX_GAUGE32 +//@} + +//------------------------------------------------------------------------- + +//---------------[ Exception conditions for SNMPv2 ]----------------------- +/** @name Exception conditions for SNMPv2 */ +//@{ +#define sNMP_SYNTAX_NOSUCHOBJECT (aSN_CONTEXT | aSN_PRIMITIVE | 0x00) +#define sNMP_SYNTAX_NOSUCHINSTANCE (aSN_CONTEXT | aSN_PRIMITIVE | 0x01) +#define sNMP_SYNTAX_ENDOFMIBVIEW (aSN_CONTEXT | aSN_PRIMITIVE | 0x02) +//@} + +//--------------[ different types of PDU's ]------------------------------- +/** @name Pdu types */ +//@{ +#define sNMP_PDU_GET (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x0) +#define sNMP_PDU_GETNEXT (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x1) +#define sNMP_PDU_RESPONSE (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x2) +#define sNMP_PDU_SET (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x3) +#define sNMP_PDU_V1TRAP (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x4) +#define sNMP_PDU_GETBULK (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x5) +#define sNMP_PDU_INFORM (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x6) +#define sNMP_PDU_TRAP (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x7) +#define sNMP_PDU_REPORT (aSN_CONTEXT | aSN_CONSTRUCTOR | 0x8) +//@} + + +//------[ smi typedefs ]--------------------------------------------------- +/** @name SMI typedefs + * + * SNMP-related types from RFC1442 (SMI). + */ +//@{ + +// byte +typedef unsigned char SmiBYTE, WINFAR *SmiLPBYTE; + +// int +typedef long SmiINT, WINFAR *SmiLPINT; + +// int 32 +typedef SmiINT SmiINT32, WINFAR *SmiLPINT32; + +// unit32 +typedef unsigned long SmiUINT32, WINFAR *SmiLPUINT32; + +// octet struct +typedef struct { + SmiUINT32 len; + SmiLPBYTE ptr;} SmiOCTETS, WINFAR *SmiLPOCTETS; + +// bits +typedef SmiOCTETS SmiBITS, WINFAR *SmiLPBITS; + +// SMI oid struct +typedef struct { + SmiUINT32 len; + SmiLPUINT32 ptr;} SmiOID, WINFAR *SmiLPOID; + +// ipaddr +typedef SmiOCTETS SmiIPADDR, WINFAR *SmiLPIPADDR; + +// 32bit counter +typedef SmiUINT32 SmiCNTR32, WINFAR *SmiLPCNTR32; + +// gauge +typedef SmiUINT32 SmiGAUGE32, WINFAR *SmiLPGAUGE32; + +// timeticks +typedef SmiUINT32 SmiTIMETICKS, WINFAR *SmiLPTIMETICKS; + +// opaque +typedef SmiOCTETS SmiOPAQUE, WINFAR *SmiLPOPAQUE; + +// nsapaddr +typedef SmiOCTETS SmiNSAPADDR, WINFAR *SmiLPNSAPADDR; + +// 64 bit counter +typedef struct { + SmiUINT32 hipart; + SmiUINT32 lopart;} SmiCNTR64, WINFAR *SmiLPCNTR64; +//@} + +#endif + + diff --git a/backend/camembert/libkmsnmp/smival.h b/backend/camembert/libkmsnmp/smival.h new file mode 100644 index 0000000..638b443 --- /dev/null +++ b/backend/camembert/libkmsnmp/smival.h @@ -0,0 +1,170 @@ +/*_############################################################################ + _## + _## smival.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ S M I V A L . H + + SMIVALUE CLASS DEFINITION + + DESIGN + AUTHOR: Jeff Meyer + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + SMIValue class definition. Superclass for the various types + of SNMP values (Address, Oid, Octet, etc.). Provides + only a few functions, most info is in subclass. + +=====================================================================*/ +// $Id: smival.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _SMIVALUE +#define _SMIVALUE + +//----[ includes ]----------------------------------------------------- +#include "smi.h" + +//----[ macros ]------------------------------------------------------- +#if defined(USE_CPP_CASTS) +#define PP_CONST_CAST(___type, ___ptr) const_cast< ___type >(___ptr) +#else +#define PP_CONST_CAST(___type, ___ptr) ((___type)(___ptr)) +#endif + +//====================================================================== +// SMI value structure conforming with SMI RFC +// +typedef struct { /* smiVALUE portion of VarBind */ + SmiUINT32 syntax; /* Insert SNMP_SYNTAX_ */ + union { + SmiINT sNumber; /* SNMP_SYNTAX_INT + SNMP_SYNTAX_INT32 */ + SmiUINT32 uNumber; /* SNMP_SYNTAX_UINT32 + SNMP_SYNTAX_CNTR32 + SNMP_SYNTAX_GAUGE32 + SNMP_SYNTAX_TIMETICKS */ + SmiCNTR64 hNumber; /* SNMP_SYNTAX_CNTR64 */ + SmiOCTETS string; /* SNMP_SYNTAX_OCTETS + SNMP_SYNTAX_BITS + SNMP_SYNTAX_OPAQUE + SNMP_SYNTAX_IPADDR + SNMP_SYNTAX_NSAPADDR */ + SmiOID oid; /* SNMP_SYNTAX_OID */ + SmiBYTE empty; /* SNMP_SYNTAX_NULL + SNMP_SYNTAX_NOSUCHOBJECT + SNMP_SYNTAX_NOSUCHINSTANCE + SNMP_SYNTAX_ENDOFMIBVIEW */ + } value; + } SmiVALUE, *SmiLPVALUE; +//================================================================= + +//-------------------------------------------------------------------- +//----[ SnmpSyntax class ]-------------------------------------------- +//-------------------------------------------------------------------- + +/** + * An "abstract" (pure virtual) class that serves as the base class + * for all specific SNMP syntax types. + */ +class DLLOPT SnmpSyntax { + +public: + + /** + * Virtual function for getting a printable ASCII value for any SNMP + * value. + * + * @note The returned string is valid as long as the object is not + * modified. + */ + virtual const char *get_printable() const = 0; + + /** + * Return the current syntax. + */ + virtual SmiUINT32 get_syntax() const = 0; + + /** + * Virtual clone operation for creating a new Value from an existing + * value. + * + * @note The caller MUST use the delete operation on the return + * value when done. + */ + virtual SnmpSyntax * clone() const = 0; + + /** + * Virtual destructor to ensure deletion of derived classes... + */ + virtual ~SnmpSyntax() {}; + + /** + * Overloaded assignment operator. + * + * @note This should be pure virtual, but buggy VC++ compiler + * complains about unresolved reference at link time. + */ + virtual SnmpSyntax& operator=(const SnmpSyntax &/*val*/) { return *this; }; + + /** + * Return validity of the object. + */ + virtual bool valid() const = 0; + + /** + * Return the space needed for serialization. + */ + virtual int get_asn1_length() const = 0; + +protected: + + SmiVALUE smival; +}; + +#endif // _SMIVALUE diff --git a/backend/camembert/libkmsnmp/snmperrs.h b/backend/camembert/libkmsnmp/snmperrs.h new file mode 100644 index 0000000..a9780a1 --- /dev/null +++ b/backend/camembert/libkmsnmp/snmperrs.h @@ -0,0 +1,273 @@ +/*_############################################################################ + _## + _## snmperrs.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ S N M P E R R S. H + + SNMP++ ERROR CODE AND STRING DEFINITIONS + + DESCRIPTION: + Definition of error macros and error strings + + DESIGN + AUTHOR: + Jeff Meyer + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + WIN32 + BSD UNIX + +============================================================================*/ +// $Id: snmperrs.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _SNMPERRS_H +#define _SNMPERRS_H + +#include "config_snmp_pp.h" + +//-------[ Positive SNMP ++ Error Return Codes ]------------------------------ +/** @name Pdu error codes + * These values are error status values from RFC 1905 + * + * The values can be returned via Pdu::get_error_status() + */ +//@{ +#define SNMP_ERROR_SUCCESS 0 //!< Success Status +#define SNMP_ERROR_TOO_BIG 1 //!< Pdu encoding too big +#define SNMP_ERROR_NO_SUCH_NAME 2 //!< No such VB name, see error index +#define SNMP_ERROR_BAD_VALUE 3 //!< Bad Vb +#define SNMP_ERROR_READ_ONLY 4 //!< VB is read only, see error index +#define SNMP_ERROR_GENERAL_VB_ERR 5 //!< General VB error, see error index +#define SNMP_ERROR_NO_ACCESS 6 //!< No access to MIBs data +#define SNMP_ERROR_WRONG_TYPE 7 //!< Requested type was incorrect +#define SNMP_ERROR_WRONG_LENGTH 8 //!< Request Pdu has inccorect length +#define SNMP_ERROR_WRONG_ENCODING 9 //!< Request Pdu has wrong encoding +#define SNMP_ERROR_WRONG_VALUE 10 //!< Request Pdu has wrong value +#define SNMP_ERROR_NO_CREATION 11 //!< Unable to create object specified +#define SNMP_ERROR_INCONSIST_VAL 12 //!< Inconsistent value in request +#define SNMP_ERROR_RESOURCE_UNAVAIL 13 //!< Resources unavailable +#define SNMP_ERROR_COMITFAIL 14 //!< Unable to comit +#define SNMP_ERROR_UNDO_FAIL 15 //!< Unable to undo +#define SNMP_ERROR_AUTH_ERR 16 //!< Authentication failure +#define SNMP_ERROR_NOT_WRITEABLE 17 //!< Mib Object not writeable +#define SNMP_ERROR_INCONSIS_NAME 18 //!< Inconsistent naming used +//@} + +//-------[ Negative SNMP ++ Result/Error Return Codes ]------------------- + +/** @name Snmp class return codes + */ +//@{ + +// General +#define SNMP_CLASS_SUCCESS 0 //!< success +#define SNMP_CLASS_ERROR -1 //!< general error +#define SNMP_CLASS_RESOURCE_UNAVAIL -2 //!< e.g., malloc failed +#define SNMP_CLASS_INTERNAL_ERROR -3 //!< unexpected / internal error +#define SNMP_CLASS_UNSUPPORTED -4 //!< unsupported function + +// Callback reasons: +#define SNMP_CLASS_TIMEOUT -5 //!< outstanding request timed out +#define SNMP_CLASS_ASYNC_RESPONSE -6 //!< received response for outstd request +#define SNMP_CLASS_NOTIFICATION -7 //!< received notification (trap/inform) +#define SNMP_CLASS_SESSION_DESTROYED -8 //!< snmp::destroyed with oustanding reqs pending + +// Snmp Class: +#define SNMP_CLASS_INVALID -10 //!< snmp::mf called on invalid instance +#define SNMP_CLASS_INVALID_PDU -11 //!< invalid pdu passed to mf +#define SNMP_CLASS_INVALID_TARGET -12 //!< invalid target passed to mf +#define SNMP_CLASS_INVALID_CALLBACK -13 //!< invalid callback to mf +#define SNMP_CLASS_INVALID_REQID -14 //!< invalid request id to cancel +#define SNMP_CLASS_INVALID_NOTIFYID -15 //!< missing trap/inform oid +#define SNMP_CLASS_INVALID_OPERATION -16 //!< snmp operation not allowed for specified target +#define SNMP_CLASS_INVALID_OID -17 //!< invalid oid passed to mf +#define SNMP_CLASS_INVALID_ADDRESS -18 //!< invalid address passed to mf +#define SNMP_CLASS_ERR_STATUS_SET -19 //!< agent returned response pdu with error_status set + +// Transport Errors: +#define SNMP_CLASS_TL_UNSUPPORTED -20 //!< transport unsupported +#define SNMP_CLASS_TL_IN_USE -21 //!< transport in use +#define SNMP_CLASS_TL_FAILED -22 //!< transport operation failed + +// extras +#define SNMP_CLASS_SHUTDOWN -23 //!< used for back door shutdown + +// ASN.1 parse errors +#define SNMP_CLASS_BADVERSION -50 //!< unsupported version +#define SNMP_CLASS_ASN1ERROR -51 //!< used for ASN.1 parse errors +//@} + +#define MAX_POS_ERROR SNMP_ERROR_INCONSIS_NAME +#define MAX_NEG_ERROR SNMP_CLASS_SHUTDOWN + + +#ifdef _INCLUDE_SNMP_ERR_STRINGS + +/** + * ASCII strings returned through Snmp::error() function. + * + * @note altering the strings in this header file will not affect the + * return values of Snmp::error(), unless you rebuild the SNMP++ + * library from source. + */ +//@{ +static const char * pErrs[] = { + "Success", // 0 + "SNMP: Response PDU Too Big", // 1 + "SNMP: Variable does not exist", // 2 + "SNMP: Cannot modify variable: Bad Value", // 3 + "SNMP: Cannot modify object, Read Only", // 4 + "SNMP: Cannot perform operation, General Error", // 5 + "SNMP: Cannot access variable, No Access", // 6 + "SNMP: Cannot create/modify variable, Wrong Type", // 7 + "SNMP: Cannot create/set variable, Wrong Length", // 8 + "SNMP: Cannot create/set variable, Wrong Encoding", // 9 + "SNMP: Cannot create/set variable, Wrong Value", // 10 + "SNMP: Cannot create variable, Creation Not Allowed", // 11 + "SNMP: Cannot create/set variable, Inconsistent Value", // 12 + "SNMP: Cannot create/set variable, Resource Unavailable", // 13 + "SNMP: Cannot create/set variable, Commit Failed", // 14 + "SNMP: Cannot create/set variable, Undo Failed", // 15 + "SNMP: Cannot perform operation, Authorization Error", // 16 + "SNMP: Cannot create/set variable, Not Writable", // 17 + "SNMP: Cannot create variable, Inconsistent Name", // 18 + "SNMP: Unknown Error Status" // 19 +}; + +#ifdef _SNMPv3 +static const char * nv3Errs[] = { + "SNMPv3: v3MP error", // -1400 + "SNMPv3: v3MP ok", // -1401 + "SNMPv3: Unsupported Security Model", // -1402 + "SNMPv3: Message not in Time Window", // -1403 + "SNMPv3: received same Message twice",// -1404 + "SNMPv3: Invalid Message", // -1405 + "SNMPv3: Invalid EngineID", // -1406 + "SNMPv3: v3MP not initialized", // -1407 + "SNMPv3: Parse Error", // -1408 + "SNMPv3: Received Message with unknown MsgID", // -1409 + "SNMPv3: Message does not match known message", // -1410 + "SNMPv3: Community format error", // -1411 + "SNMPv3: Unknown UserName", //-1412 + "SNMPv3: Build error", //-1413 + "SNMPv3: USM: error", //-1414 + "SNMPv3: Unknown pdu handlers", //-1415 + "SNMPv3: Unavailable Context", //-1416 + "SNMPv3: Unknown Context", //-1417 + "SNMPv3: Report sent", //-1418 + "SNMPv3: Unknown errorcode" +}; + +static const char * pv3Errs[] = { + "SNMPv3: USM: ok", // 1400 + "SNMPv3: USM: error", // 1401 + "SNMPv3: USM: Configfile write error", // 1402 + "SNMPv3: USM: Unsupported SecurityLevel", // 1403 + "SNMPv3: USM: Unknown SecurityName", // 1404 + "SNMPv3: USM: Encryption error", // 1405 + "SNMPv3: USM: Decryption error", // 1406 + "SNMPv3: USM: Authentication error", // 1407 + "SNMPv3: USM: Authentication failure", // 1408 + "SNMPv3: USM: Parse error", // 1409 + "SNMPv3: USM: Unknown EngineID", // 1410 + "SNMPv3: USM: Message not in TimeWindow", // 1411 + "SNMPv3: USM: Unsupported AuthProtocol", // 1412 + "SNMPv3: USM: Unsupported PrivProtocol" // 1413 + "SNMPv3: USM: Address error", // 1414 + "SNMPv3: USM: Could not create file", // 1415 + "SNMPv3: USM: Could not open file", // 1416 + "SNMPv3: USM: Could not rename file", // 1417 + "SNMPv3: USM: Could not delete file", // 1418 + "SNMPv3: USM: Could not write into file", // 1419 + "SNMPv3: USM: Could not read from file", // 1420 + "SNMPv3: USM: unknown errorcode" +}; +#endif + +static const char * nErrs[] = +{ + // General: + "SNMP++: Success", // 0 SNMP_CLASS_SUCCESS + "SNMP++: Operation failed", // 1 SNMP_CLASS_ERROR + "SNMP++: Resource unavailable", // 2 SNMP_CLASS_RESOURCE_UNAVAIL + "SNMP++: Internal error", // 3 SNMP_CLASS_INTERNAL_ERROR + "SNMP++: Unsupported function", // 4 SNMP_CLASS_UNSUPPORTED + + // Callback reasons: + "SNMP++: SNMP request timed out", // 5 SNMP_CLASS_TIMEOUT + "SNMP++: Received SNMP Response", // 6 SNMP_CLASS_ASYNC_RESPONSE + // 7 SNMP_CLASS_NOTIFICATION + "SNMP++: Received SNMP Notification (trap or inform)", + // 8 SNMP_CLASS_SESSION_DESTROYED + "SNMP++: Closing session with outstanding requests", + "Unknown error code", // 9 reserved for future + + // Snmp Class errors: + "SNMP++: Class not valid", // 10 SNMP_CLASS_INVALID + "SNMP++: Invalid Pdu", // 11 SNMP_CLASS_INVALID_PDU + "SNMP++: Invalid Target", // 12 SNMP_CLASS_INVALID_TARGET + "SNMP++: Invalid (null) Callback Function", // 13 SNMP_CLASS_INVALID_CALLBACK + "SNMP++: Invalid Request Id", // 14 SNMP_CLASS_INVALID_REQID + "SNMP++: Invalid Notification Id", // 15 SNMP_CLASS_INVALID_NOTIFYID + // 16 SNMP_CLASS_INVALID_OPERATION + "SNMP++: SNMP Operation not supported on specified Target", + "SNMP++: Invalid Object Identifier", // 17 SNMP_CLASS_INVALID_OID + "SNMP++: Invalid Address", // 18 SNMP_CLASS_INVALID_ADDRESS + // 19 SNMP_CLASS_ERR_STATUS_SET + "SNMP++: Agent indicates error in SNMP request", + + // Transport Errors: + "SNMP++: Transport is not supported", // 20 SNMP_CLASS_TL_UNSUPPORTED + "SNMP++: Transport is in use", // 21 SNMP_CLASS_TL_IN_USE + "SNMP++: Transport operation failed", // 22 SNMP_CLASS_TL_FAILED + "SNMP++: Blocked Mode Shutdown", // 23 SNMP_CLASS_SHUTDOWN + + "Unknown error code", // unknown error code +}; +//@} +#endif //_INCLUDE_SNMP_ERR_STRINGS + +#endif //_SNMPERRS_H diff --git a/backend/camembert/libkmsnmp/snmpmsg.cpp b/backend/camembert/libkmsnmp/snmpmsg.cpp new file mode 100644 index 0000000..4b2faab --- /dev/null +++ b/backend/camembert/libkmsnmp/snmpmsg.cpp @@ -0,0 +1,791 @@ +/*_############################################################################ + _## + _## snmpmsg.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1996 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ S N M P M S G . C P P + + SNMPMESSAGE CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + DESCRIPTION: + ASN.1 encoding / decoding class + +=====================================================================*/ +char snmpmsg_cpp_version[]="#(@) SNMP++ $Id: snmpmsg.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#ifdef WIN32 +#include +#endif +#include // standard io file + +#include "snmpmsg.h" // header file for SnmpMessage +#include "oid_def.h" // changed (Frank Fock) +#include "v3.h" +#include "vb.h" + +const coldStartOid coldStart; +const warmStartOid warmStart; +const linkDownOid linkDown; +const linkUpOid linkUp; +const authenticationFailureOid authenticationFailure; +const egpNeighborLossOid egpNeighborLoss; +const snmpTrapEnterpriseOid snmpTrapEnterprise; + +//------------[ convert SNMP++ VB to WinSNMP smiVALUE ]---------------- +int convertVbToSmival( const Vb &tempvb, SmiVALUE *smival ) +{ + smival->syntax = tempvb.get_syntax(); + switch ( smival->syntax ) { + + // case sNMP_SYNTAX_NULL + case sNMP_SYNTAX_NULL: + case sNMP_SYNTAX_NOSUCHOBJECT: + case sNMP_SYNTAX_NOSUCHINSTANCE: + case sNMP_SYNTAX_ENDOFMIBVIEW: + break; + + // case sNMP_SYNTAX_INT32: + case sNMP_SYNTAX_INT: + tempvb.get_value(smival->value.sNumber); + break; + + // case sNMP_SYNTAX_UINT32: + case sNMP_SYNTAX_GAUGE32: + case sNMP_SYNTAX_CNTR32: + case sNMP_SYNTAX_TIMETICKS: + // case sNMP_SYNTAX_UINT32: + tempvb.get_value(smival->value.uNumber); + break; + + // case Counter64 + case sNMP_SYNTAX_CNTR64: + { + Counter64 c64; + tempvb.get_value(c64); + smival->value.hNumber.hipart = c64.high(); + smival->value.hNumber.lopart = c64.low(); + } + break; + + case sNMP_SYNTAX_BITS: + case sNMP_SYNTAX_OCTETS: + case sNMP_SYNTAX_OPAQUE: + case sNMP_SYNTAX_IPADDR: + { + OctetStr os; + tempvb.get_value(os); + smival->value.string.ptr = NULL; + smival->value.string.len = os.len(); + if ( smival->value.string.len > 0 ) + { + smival->value.string.ptr + = (SmiLPBYTE) new unsigned char [smival->value.string.len]; + if ( smival->value.string.ptr ) + { + for (int i=0; i<(int) smival->value.string.len ; i++) + smival->value.string.ptr[i] = os[i]; + } + else + { + smival->syntax = sNMP_SYNTAX_NULL; // invalidate the smival + return SNMP_CLASS_RESOURCE_UNAVAIL; + } + } + } + break; + + case sNMP_SYNTAX_OID: + { + Oid oid; + tempvb.get_value(oid); + smival->value.oid.ptr = NULL; + smival->value.oid.len = oid.len(); + if ( smival->value.oid.len > 0 ) + { + smival->value.oid.ptr + = (SmiLPUINT32) new unsigned long [ smival->value.oid.len]; + if ( smival->value.oid.ptr ) + { + for (int i=0; i<(int)smival->value.oid.len ; i++) + smival->value.oid.ptr[i] = oid[i]; + } + else + { + smival->syntax = sNMP_SYNTAX_NULL; // invalidate the smival + return SNMP_CLASS_RESOURCE_UNAVAIL; + } + } + } + break; + + default: + return SNMP_CLASS_INTERNAL_ERROR; + } + return SNMP_CLASS_SUCCESS; +} + +// free a SMI value +void freeSmivalDescriptor( SmiVALUE *smival ) +{ + switch ( smival->syntax ) { + case sNMP_SYNTAX_OCTETS: + case sNMP_SYNTAX_OPAQUE: + case sNMP_SYNTAX_IPADDR: + case sNMP_SYNTAX_BITS: // obsoleted in SNMPv2 Draft Std + delete [] smival->value.string.ptr; + break; + + case sNMP_SYNTAX_OID: + delete [] smival->value.oid.ptr; + break; + } + smival->syntax = sNMP_SYNTAX_NULL; +} + +#ifdef _SNMPv3 + +int SnmpMessage::unloadv3( Pdu &pdu, // Pdu returned + snmp_version &version, // version + OctetStr &engine_id, // optional v3 + OctetStr &security_name, // optional v3 + long int &security_model, + UdpAddress &from_addr) +{ + OctetStr tmp; + return unload(pdu, tmp, version, &engine_id, + &security_name, &security_model, &from_addr); +} + +#endif + +int SnmpMessage::load( Pdu pdu, + const OctetStr &community, + const snmp_version version +#ifdef _SNMPv3 + ,const OctetStr* engine_id, + const OctetStr* security_name, + const int security_model +#endif + ) +{ + int status; + + // make sure pdu is valid + if ( !pdu.valid()) + return SNMP_CLASS_INVALID_PDU; + + // create a raw pdu + snmp_pdu *raw_pdu; + raw_pdu = snmp_pdu_create( (int) pdu.get_type()); + + Oid enterprise; + + // load it up + raw_pdu->reqid = pdu.get_request_id(); +#ifdef _SNMPv3 + raw_pdu->msgid = pdu.get_message_id(); +#endif + raw_pdu->errstat= (unsigned long) pdu.get_error_status(); + raw_pdu->errindex= (unsigned long) pdu.get_error_index(); + + // if its a V1 trap then load up other values + // for v2, use normal pdu format + if ( raw_pdu->command == sNMP_PDU_V1TRAP) { + // DON'T forget about the v1 trap agent address (changed by Frank Fock) + GenAddress gen_addr; + IpAddress ip_addr; + int addr_set = FALSE; + + if (pdu.get_v1_trap_address(gen_addr)) + { + /* User did set the v1 trap address */ + if ((gen_addr.get_type() != Address::type_ip) && + (gen_addr.get_type() != Address::type_udp) ) + { + debugprintf(0, "Bad v1 trap address type in pdu"); + snmp_free_pdu( raw_pdu); + return SNMP_CLASS_INVALID_PDU; + } + + ip_addr = gen_addr; + if (!ip_addr.valid()) { + debugprintf(0, "Copy of v1 trap address failed"); + snmp_free_pdu( raw_pdu); + return SNMP_CLASS_RESOURCE_UNAVAIL; + } + addr_set = TRUE; + } + else + { + /* User did not set the v1 trap address */ + char addrString[256]; + if (gethostname(addrString, 255) == 0) + { + ip_addr = addrString; + addr_set = TRUE; + } + } + struct sockaddr_in agent_addr; // agent address socket struct + // prepare the agent address + memset(&agent_addr, 0, sizeof(agent_addr)); + agent_addr.sin_family = AF_INET; + if (addr_set) + { + agent_addr.sin_addr.s_addr + = inet_addr(((IpAddress &)ip_addr).IpAddress::get_printable()); + debugprintf(3, "setting v1 trap address to (%s).", + ((IpAddress &)ip_addr).IpAddress::get_printable()); + } + raw_pdu->agent_addr = agent_addr; + + //-----[ compute generic trap value ]------------------------------- + // determine the generic value + // 0 - cold start + // 1 - warm start + // 2 - link down + // 3 - link up + // 4 - authentication failure + // 5 - egpneighborloss + // 6 - enterprise specific + Oid trapid; + pdu.get_notify_id( trapid); + if ( !trapid.valid() || trapid.len() < 2 ) + { + snmp_free_pdu( raw_pdu); + return SNMP_CLASS_INVALID_NOTIFYID; + } + raw_pdu->specific_type=0; + if ( trapid == coldStart) + raw_pdu->trap_type = 0; // cold start + else if ( trapid == warmStart) + raw_pdu->trap_type = 1; // warm start + else if( trapid == linkDown) + raw_pdu->trap_type = 2; // link down + else if ( trapid == linkUp) + raw_pdu->trap_type = 3; // link up + else if ( trapid == authenticationFailure ) + raw_pdu->trap_type = 4; // authentication failure + else if ( trapid == egpNeighborLoss) + raw_pdu->trap_type = 5; // egp neighbor loss + else { + raw_pdu->trap_type = 6; // enterprise specific + // last oid subid is the specific value + // if 2nd to last subid is "0", remove it + // enterprise is always the notify oid prefix + raw_pdu->specific_type = (int) trapid[(int) (trapid.len()-1)]; + + trapid.trim(1); + if ( trapid[(int)(trapid.len()-1)] == 0 ) + trapid.trim(1); + enterprise = trapid; + } + + if ( raw_pdu->trap_type !=6) + pdu.get_notify_enterprise( enterprise); + if ( enterprise.len() >0) { + // note!! + // these are hooks into an SNMP++ oid + // and therefor the raw_pdu enterprise + // should not free them. null them out!! + SmiLPOID rawOid; + rawOid = enterprise.oidval(); + raw_pdu->enterprise = rawOid->ptr; + raw_pdu->enterprise_length = (int) rawOid->len; + } + + // timestamp + TimeTicks timestamp; + pdu.get_notify_timestamp( timestamp); + raw_pdu->time = ( unsigned long) timestamp; + + } + + // if its a v2 trap then we need to make a few adjustments + // vb #1 is the timestamp + // vb #2 is the id, represented as an Oid + if (( raw_pdu->command == sNMP_PDU_TRAP) || + ( raw_pdu->command == sNMP_PDU_INFORM)) + { + Pdu temppdu(pdu); + Vb tempvb; + + temppdu.trim(temppdu.get_vb_count()); + + // vb #1 is the timestamp + TimeTicks timestamp; + tempvb.set_oid(SNMP_MSG_OID_SYSUPTIME); // sysuptime + pdu.get_notify_timestamp( timestamp); + tempvb.set_value ( timestamp); + temppdu += tempvb; + + // vb #2 is the id + Oid trapid; + tempvb.set_oid(SNMP_MSG_OID_TRAPID); + pdu.get_notify_id( trapid); + tempvb.set_value( trapid); + temppdu += tempvb; + + // append the remaining vbs + for ( int z=0;zcommand == sNMP_PDU_GET) || + (raw_pdu->command == sNMP_PDU_GETNEXT) || + (raw_pdu->command == sNMP_PDU_GETBULK)) + tempvb.set_null(); + status = convertVbToSmival( tempvb, &smival ); + if ( status != SNMP_CLASS_SUCCESS) { + snmp_free_pdu( raw_pdu); + return status; + } + // add the vb to the raw pdu + snmp_add_var( raw_pdu, smioid->ptr, (int) smioid->len, &smival); + + freeSmivalDescriptor( &smival); + } + + // ASN1 encode the pdu +#ifdef _SNMPv3 + if (version == version3) + { + if ((!engine_id) || (!security_name)) + { + debugprintf(1, "SnmpMessage::load, security_name and engine_id needed for SNMPv3 message."); + snmp_free_pdu( raw_pdu); + return SNMP_CLASS_INVALID_TARGET; + } + + status = v3MP::I->snmp_build(raw_pdu, databuff, (int *)&bufflen, + *engine_id, *security_name, security_model, + pdu.get_security_level(), + pdu.get_context_engine_id(), + pdu.get_context_name()); + if (status == SNMPv3_MP_OK) { + if ((pdu.get_type() == sNMP_PDU_RESPONSE) && + ((int)pdu.get_maxsize_scopedpdu() < pdu.get_asn1_length())) { + debugprintf(0, "ERROR: TOO BIG should not happen."); + // prevention of SNMP++ Enterprise Oid death + if ( enterprise.len() >0) { + raw_pdu->enterprise = 0; + raw_pdu->enterprise_length=0; + } + snmp_free_pdu( raw_pdu); + return SNMP_ERROR_TOO_BIG; + } + } + } + else +#endif + status = snmp_build( raw_pdu, databuff, (int *) &bufflen, version, + community.data(), (int) community.len()); + debugprintf(3, "SnmpMsg: return status of snmpBuild (%i)",status); + + if ((status != 0) +#ifdef _SNMPv3 + && ((version != version3) || (status != SNMPv3_MP_OK)) +#endif + ) { + valid_flag = false; + // prevention of SNMP++ Enterprise Oid death + if ( enterprise.len() >0) { + raw_pdu->enterprise = 0; + raw_pdu->enterprise_length=0; + } + snmp_free_pdu( raw_pdu); +#ifdef _SNMPv3 + if (version == version3) + return status; + else +#endif + // NOTE: This is an assumption - in most cases during normal + // operation the reason is a tooBig - another could be a + // damaged variable binding. + return SNMP_ERROR_TOO_BIG; + } + valid_flag = true; + + // prevention of SNMP++ Enterprise Oid death + if ( enterprise.len() >0) { + raw_pdu->enterprise = 0; + raw_pdu->enterprise_length=0; + } + + snmp_free_pdu( raw_pdu); + + return SNMP_CLASS_SUCCESS; +} + +// load up a SnmpMessage +int SnmpMessage::load( unsigned char *data, + unsigned long len) +{ + bufflen = SNMP_MSG_LENGTH; + valid_flag = false; + + if ( len <= SNMP_MSG_LENGTH) { + memcpy( (unsigned char *) databuff, (unsigned char *) data, + (unsigned int) len); + bufflen = len; + valid_flag = true; + } + else + return SNMP_ERROR_WRONG_LENGTH; + + return SNMP_CLASS_SUCCESS; +} + +// unload the data into SNMP++ objects +int SnmpMessage::unload( Pdu &pdu, // Pdu object + OctetStr &community, // community object + snmp_version &version // SNMP version # +#ifdef _SNMPv3 + ,OctetStr *engine_id, // optional v3 + OctetStr *security_name, // optional v3 + long int *security_model, + UdpAddress *from_addr +#endif + ) +{ + unsigned char community_name[255]; + unsigned long community_len; + + Pdu tmppdu; // Only used to reset "pdu" + pdu = tmppdu; + + if ( !valid_flag) + return SNMP_CLASS_INVALID; + + snmp_pdu *raw_pdu; + raw_pdu = snmp_pdu_create(0); // do a "snmp_free_pdu( raw_pdu)" before return + + int status; + +#ifdef _SNMPv3 + OctetStr context_engine_id; + OctetStr context_name; + long int security_level; + + if ((security_model) && (security_name) && (engine_id)) { + status = v3MP::I->snmp_parse(raw_pdu, databuff, (int)bufflen, *engine_id, + *security_name, context_engine_id, context_name, + security_level, *security_model, version, *from_addr); + pdu.set_context_engine_id(context_engine_id); + pdu.set_context_name(context_name); + pdu.set_security_level(security_level); + pdu.set_message_id(raw_pdu->msgid); + pdu.set_maxsize_scopedpdu(raw_pdu->maxsize_scopedpdu); + if (status != SNMPv3_MP_OK) { + pdu.set_request_id( raw_pdu->reqid); + pdu.set_type( raw_pdu->command); + snmp_free_pdu( raw_pdu); + return status; + } + } + else { +#endif + status = snmp_parse( raw_pdu, databuff, community_name, community_len, + version, (int) bufflen); + if ( status != 0) { + snmp_free_pdu( raw_pdu); + return status; + } + community.set_data( community_name, community_len); + +#ifdef _SNMPv3 + } +#endif + // load up the SNMP++ variables + pdu.set_request_id(raw_pdu->reqid); + pdu.set_error_status((int) raw_pdu->errstat); + pdu.set_error_index((int) raw_pdu->errindex); + pdu.set_type( raw_pdu->command); + + // deal with traps a little different + if ( raw_pdu->command == sNMP_PDU_V1TRAP) { + // timestamp + TimeTicks timestamp; + timestamp = raw_pdu->time; + pdu.set_notify_timestamp( timestamp); + + // set the agent address + IpAddress agent_addr(inet_ntoa(raw_pdu->agent_addr.sin_addr)); + if (agent_addr != "0.0.0.0") + { + pdu.set_v1_trap_address(agent_addr); + debugprintf(4, "reveiced trap with v1 trap address (%s).", + agent_addr.get_printable()); + } + // set enterprise, notifyid + Oid enterprise; + + if (raw_pdu->enterprise_length >0) { + for (int i=0; i< raw_pdu->enterprise_length; i++) { + enterprise += (int) (raw_pdu->enterprise[i]); + } + pdu.set_notify_enterprise(enterprise); + } + switch (raw_pdu->trap_type) { + case 0: + pdu.set_notify_id(coldStart); + break; + + case 1: + pdu.set_notify_id(warmStart); + break; + + case 2: + pdu.set_notify_id(linkDown); + break; + + case 3: + pdu.set_notify_id(linkUp); + break; + + case 4: + pdu.set_notify_id(authenticationFailure); + break; + + case 5: + pdu.set_notify_id(egpNeighborLoss); + break; + + case 6: { // enterprise specific + // base id + specific # + Oid eOid = enterprise; + eOid += 0ul; + eOid += raw_pdu->specific_type; + pdu.set_notify_id( eOid); + break; + } + default: + debugprintf(1, "snmpmsg: Wrong trap_type"); + } + } + + // vbs + Vb tempvb; + Oid tempoid; + struct variable_list *vp; + int vb_nr = 1; + + for(vp = raw_pdu->variables; vp; vp = vp->next_variable, vb_nr++) { + + // extract the oid portion + tempoid.set_data( (unsigned long *)vp->name, + ( unsigned int) vp->name_length); + tempvb.set_oid( tempoid); + + // extract the value portion + switch(vp->type){ + + // octet string + case sNMP_SYNTAX_OCTETS: + { + OctetStr octets( (unsigned char *) vp->val.string, + (unsigned long) vp->val_len); + tempvb.set_value( octets); + } + break; + case sNMP_SYNTAX_OPAQUE: + { + OpaqueStr octets( (unsigned char *) vp->val.string, + (unsigned long) vp->val_len); + tempvb.set_value( octets); + } + break; + + // object id + case sNMP_SYNTAX_OID: + { + Oid oid( (unsigned long*) vp->val.objid, + (int) vp->val_len); + tempvb.set_value( oid); + if ((vb_nr == 2) && + ((raw_pdu->command == sNMP_PDU_TRAP) || + (raw_pdu->command == sNMP_PDU_INFORM)) && + (tempoid == SNMP_MSG_OID_TRAPID)) + { + // set notify_id + pdu.set_notify_id(oid); + continue; // don't add vb to pdu + } + } + break; + + // timeticks + case sNMP_SYNTAX_TIMETICKS: + { + TimeTicks timeticks( (unsigned long) *(vp->val.integer)); + tempvb.set_value( timeticks); + if ((vb_nr == 1) && + ((raw_pdu->command == sNMP_PDU_TRAP) || + (raw_pdu->command == sNMP_PDU_INFORM)) && + (tempoid == SNMP_MSG_OID_SYSUPTIME)) + { + // set notify_timestamp + pdu.set_notify_timestamp( timeticks); + continue; // don't add vb to pdu + } + } + break; + + // 32 bit counter + case sNMP_SYNTAX_CNTR32: + { + Counter32 counter32( (unsigned long) *(vp->val.integer)); + tempvb.set_value( counter32); + } + break; + + // 32 bit gauge + case sNMP_SYNTAX_GAUGE32: + { + Gauge32 gauge32( (unsigned long) *(vp->val.integer)); + tempvb.set_value( gauge32); + } + break; + + // ip address + case sNMP_SYNTAX_IPADDR: + { + char buffer[42]; + + if (vp->val_len == 16) + sprintf( buffer, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x:%02x%02x", + vp->val.string[ 0], vp->val.string[ 1], vp->val.string[ 2], + vp->val.string[ 3], vp->val.string[ 4], vp->val.string[ 5], + vp->val.string[ 6], vp->val.string[ 7], vp->val.string[ 8], + vp->val.string[ 9], vp->val.string[10], vp->val.string[11], + vp->val.string[12], vp->val.string[13], vp->val.string[14], + vp->val.string[15]); + else + sprintf( buffer,"%d.%d.%d.%d", + vp->val.string[0], vp->val.string[1], + vp->val.string[2], vp->val.string[3]); + IpAddress ipaddress( buffer); + tempvb.set_value( ipaddress); + } + break; + + // 32 bit integer + case sNMP_SYNTAX_INT: + { + SnmpInt32 int32( (long) *(vp->val.integer)); + tempvb.set_value( int32); + } + break; + + // 32 bit unsigned integer +/* Not distinguishable from Gauge32 + case sNMP_SYNTAX_UINT32: + { + SnmpUInt32 uint32( (unsigned long) *(vp->val.integer)); + tempvb.set_value( uint32); + } + break; +*/ + // v2 counter 64's + case sNMP_SYNTAX_CNTR64: + { // Frank Fock (was empty before) + Counter64 c64(((counter64*)vp->val.counter64)->high, + ((counter64*)vp->val.counter64)->low); + tempvb.set_value( c64); + break; + } + case sNMP_SYNTAX_NULL: + tempvb.set_null(); + break; + + // v2 vb exceptions + case sNMP_SYNTAX_NOSUCHOBJECT: + case sNMP_SYNTAX_NOSUCHINSTANCE: + case sNMP_SYNTAX_ENDOFMIBVIEW: + set_exception_status( &tempvb, vp->type); + break; + + default: + tempvb.set_null(); + + } // end switch + + // append the vb to the pdu + pdu += tempvb; + } + + snmp_free_pdu( raw_pdu); + + // if incoming number of vbs > max number + // -> return TOO BIG error! + if (vb_nr-1 > MAX_VBS) + return SNMP_ERROR_TOO_BIG; + + return SNMP_CLASS_SUCCESS; +} + diff --git a/backend/camembert/libkmsnmp/snmpmsg.h b/backend/camembert/libkmsnmp/snmpmsg.h new file mode 100644 index 0000000..8df7071 --- /dev/null +++ b/backend/camembert/libkmsnmp/snmpmsg.h @@ -0,0 +1,165 @@ +/*_############################################################################ + _## + _## snmpmsg.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ S N M P M E S S A G E . H + + SNMPMESSAGE CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + Win 32 + BSD UNIX + + DESCRIPTION: + ASN.1 encoding / decoding class + +=====================================================================*/ +// $Id: snmpmsg.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _SNMPMSG +#define _SNMPMSG + +#include "config_snmp_pp.h" + +#include "smival.h" +#include "pdu.h" +#include "target.h" +#include "asn1.h" +#include "mp_v3.h" + +DLLOPT void freeSmivalDescriptor( SmiVALUE* ); +DLLOPT int convertVbToSmival( const Vb&, SmiVALUE* ); + +#define SNMP_MSG_OID_SYSUPTIME "1.3.6.1.2.1.1.3.0" +#define SNMP_MSG_OID_TRAPID "1.3.6.1.6.3.1.1.4.1.0" + +// SnmpMessage Class +class DLLOPT SnmpMessage +{ + public: + + // construct a SnmpMessage object + SnmpMessage() : bufflen(SNMP_MSG_LENGTH), valid_flag(false) {}; + // load up using a Pdu, community and SNMP version + // performs ASN.1 serialization + // result status returned +#ifdef _SNMPv3 + int load( Pdu pdu, // Pdu to serialize + const OctetStr &community, // community name to use + const snmp_version version, // SNMP version, v1 or v2 + const OctetStr *engine_id = 0, // optional v3 + const OctetStr *security_name = 0, // optional v3 + const int security_model = 0); // optional v3 +#else + int load( Pdu pdu, // Pdu to serialize + const OctetStr &community, // community name to use + const snmp_version version); // SNMP version, v1 or v2 +#endif + + // load up message using ASN.1 data stream + // status is returned + int load( unsigned char *data, // data to be loaded + unsigned long len); // len of data to be loaded + + // unload ( unserialize ) into SNMP++ Pdu, community and version + // status is returned +#ifdef _SNMPv3 + int unload( Pdu &pdu, // Pdu returned + OctetStr &community, // community name + snmp_version &version, // version + OctetStr *engine_id = 0, // optional v3 + OctetStr *security_name = 0, // optional v3 + long int *security_model = 0, + UdpAddress *from_addr = 0); +#else + int unload( Pdu &pdu, // Pdu returned + OctetStr &community, // community name + snmp_version &version); // version +#endif + + +#ifdef _SNMPv3 + int loadv3( Pdu pdu, // Pdu to serialize + const OctetStr &engine_id, // engine_id to use + const OctetStr &sec_name, // securit_name to use + const int sec_model, // security_model to use + const snmp_version version) // SNMP version, v3 + { return load(pdu, "", version, &engine_id, &sec_name, sec_model); } + + int unloadv3( Pdu &pdu, // Pdu returned + snmp_version &version, // version + OctetStr &engine_id, // optional v3 + OctetStr &security_name, // optional v3 + long int &security_model, + UdpAddress &from_addr); + + // returns TRUE if the message in the buffer is a v3 message + bool is_v3_message() {return v3MP::is_v3_msg(databuff, (int)bufflen);}; + +#endif + + // return the validity of the message + bool valid() const { return valid_flag;}; + + // return raw data + // check validity + unsigned char *data() { return databuff; }; + + // returns len + // check validity + unsigned long len() const { return bufflen; }; + +protected: + + unsigned char databuff[SNMP_MSG_LENGTH]; + unsigned int bufflen; + bool valid_flag; +}; +#endif // _SNMPMSG diff --git a/backend/camembert/libkmsnmp/target.h b/backend/camembert/libkmsnmp/target.h new file mode 100644 index 0000000..0db129b --- /dev/null +++ b/backend/camembert/libkmsnmp/target.h @@ -0,0 +1,652 @@ +/*_############################################################################ + _## + _## target.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ T A R G E T . H + + TARGET CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + DOS/WINDOWS 3.1 + BSD UNIX + + DESCRIPTION: + Target class defines target SNMP agents. + +=====================================================================*/ +// $Id: target.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _TARGET +#define _TARGET + +//----[ includes ]----------------------------------------------------- + +#include "config_snmp_pp.h" +#include "address.h" +#include "octet.h" +#include "collect.h" + +//----[ enumerated types for SNMP versions ]--------------------------- +/** + * The SNMP version to use is passed with this enum. + */ +enum snmp_version +{ + version1, ///< (0) SNMPv1 + version2c ///< (1) SNMPv2c +#ifdef _SNMPv3 + ,version2stern, ///< (2) Dont use this! + version3 ///< (3) SNMPv3 +#endif +}; + +//----[ Target class ]------------------------------------------------- +/** + * Abstract class used to provide a virtual interface into Targets. + * + * @note Although it is possible to create an object of this class, + * you won't be happy with that... + */ +class DLLOPT SnmpTarget +{ + public: + + /** + * Enum to identify a target object through SnmpTarget::get_type() method. + */ + enum target_type + { + type_base, ///< It is a SnmpTarget object + type_ctarget, ///< It is a CTarget object + type_utarget ///< It is a Utarget object + }; + + /** + * Create a SnmpTarget object with default values. + * The validity of the target will be false. + */ + SnmpTarget() + : validity(false), timeout(default_timeout), retries(default_retries), + version(version1), ttype(type_base) {}; + + /** + * Create a SnmpTarget object with the given Address. + */ + SnmpTarget( const Address &address) + : validity(false), timeout(default_timeout), retries(default_retries), + version(version1), ttype(type_base), my_address(address) + { if (my_address.valid()) validity = true; }; + + /** + * Destructor that has nothing to do. + */ + virtual ~SnmpTarget() {}; + + /** + * Return the type of the target object. + * + * If a SNMP message is received through a callback (that only + * passes a SnmpTarget pointer to the callback function), this + * method can be used to check the type of the object before doing a + * cast to CTarget or UTarget. + */ + target_type get_type() const { return ttype; }; + + /** + * Returns the validity of the target object. + * + * @return true, if the target is valid. + */ + bool valid() const { return validity;}; + + /** + * Set the retry value. + * + * @param r - The number of retries if no response is received. + */ + void set_retry( const int r) { retries = r; }; + + /** + * Get the retry value. + * + * @return The number of retries on timeout. + */ + int get_retry() const { return retries; }; + + + /** + * Set the timeout for requests. + * + * The default timeout for requests is 1 second (100). + * + * @param t - Timeout in 10ms, so 100 will set the timeout to 1 second. + */ + void set_timeout( const unsigned long t) { timeout = t; }; + + /** + * Get the timeout. + * + * @return The timeout for requests sent using this target object. + */ + unsigned long get_timeout() const { return timeout; }; + + /** + * Change the default timeout. + * + * Changing the default timeout value will only have an effect for + * target objects that are created after setting this value. + * + * @param t - The new default timeout value + */ + static void set_default_timeout( const unsigned long t) + { default_timeout = t; }; + + /** + * Change the default retries vlaue. + * + * Changing the default retries value will only have an effect for + * target objects that are created after setting this value. + * + * @param r - The new retries value + */ + static void set_default_retries( const int r) { default_retries = r; }; + + /** + * Clone operator. + * + * Virtual clone operation for creating a new SnmpTarget from an existing + * SnmpTarget. + * + * @note The caller MUST use the delete operation on the return + * value when done. + * + * @return A pointer to the new object on success, 0 on failure. + */ + virtual SnmpTarget *clone() const; + + /** + * Get the address object. + * + * @param address - GenAddress object to store the target address. + * @return TRUE on success. + */ + int get_address( GenAddress &address) const; + + /** + * Get the address object. + * + * @param address - GenAddress object to store the target address. + * @return TRUE on success. + */ + const GenAddress &get_address() const { return my_address; }; + + /** + * Set the address object. + * + * @param address - The address that this target should use. + * @return TRUE on success. + */ + virtual int set_address( const Address &address); + + /** + * Get the SNMP version for this target. + * + * @return The SNMP version of this target object. + * @see enum snmp_version + */ + snmp_version get_version() const { return version;}; + + /** + * Set the SNMP version of this target. + * + * @param v - The SNMP version that should be used for sending messages. + */ + void set_version( const snmp_version v) { version = v; }; + + /** + * Overloeaded compare operator. + * + * Two SnmpTarget objects are considered equal, if all member + * variables are equal. + * + * @return 1 if targets are equal, 0 if not. + */ + int operator==(const SnmpTarget &rhs); + + protected: + bool validity; ///< Validity of the object + unsigned long timeout; ///< xmit timeout in 10 milli secs + int retries; ///< number of retries + snmp_version version; ///< SNMP version to use + target_type ttype; ///< Type of the target + GenAddress my_address; ///< Address object + + static unsigned long default_timeout; ///< default timeout for new objects + static int default_retries; ///< default retries for new objects +}; + +//----[ CTarget class ]---------------------------------------------- +/** + * Community based target object. + * This target can be used for SNMPv1 and SNMPv2c messages. + */ +class DLLOPT CTarget: public SnmpTarget +{ + public: + /** + * Constructor with no args. + * The validity of the target will be false. + */ + CTarget(); + + /** + * Constructor with all args. + * + * @param address - Address of the target host (cann be any address object) + * @param read_community_name - Community for get requests + * @param write_community_name - Community for set requests + */ + CTarget( const Address &address, + const char *read_community_name, + const char *write_community_name); + + /** + * Constructor with all args. + * + * @param address - Address of the target host (cann be any address object) + * @param read_community_name - Community for get requests + * @param write_community_name - Community for set requests + */ + CTarget( const Address &address, + const OctetStr &read_community_name, + const OctetStr &write_community_name); + + /** + * Constructor with only address. + * + * The read and write community names will be set to "public". + * + * @param address - Address of the target host (cann be any address object) + */ + CTarget( const Address &address); + + /** + * Constructor from existing CTarget. + */ + CTarget( const CTarget &target); + + /** + * Destructor, that has nothing to do. + */ + ~CTarget() {}; + + /** + * Clone operator. + * + * Clone operation for creating a new CTarget from an existing + * CTarget. + * + * @note The caller MUST use the delete operation on the return + * value when done. + * + * @return A pointer to the new object on success, 0 on failure. + */ + SnmpTarget *clone() const { return (SnmpTarget *) new CTarget(*this); }; + + /** + * Get the read community name. + * + * @return C string of the read community. + */ + const char * get_readcommunity() const + { return (const char *) read_community.get_printable(); }; + + /** + * Get the read community name. + * + * @param oct - OctetStr that will be filled with the read community name. + */ + void get_readcommunity( OctetStr& oct) const { oct = read_community; }; + + /** + * Set the read community name. + * + * @param str - The new read community name + */ + void set_readcommunity( const char * str) { read_community = str; }; + + /** + * Set the read community name. + * + * @param oct - The new read community name + */ + void set_readcommunity( const OctetStr& oct) { read_community = oct; }; + + /** + * Get the write community name. + * + * @return C string of the write community. + */ + const char * get_writecommunity() const + { return (const char *) write_community.get_printable(); }; + + /** + * Get the write community name. + * + * @param oct - OctetStr that will be filled with the write community name. + */ + void get_writecommunity( OctetStr &oct) const { oct = write_community; }; + + /** + * Set the write community name. + * + * @param str - The new write community name + */ + void set_writecommunity( const char * str) { write_community = str; }; + + /** + * Set the write community name. + * + * @param oct - The new write community name + */ + void set_writecommunity( const OctetStr &oct) { write_community = oct; }; + + /** + * Overloaded assignment operator. + */ + CTarget& operator=( const CTarget& target); + + /** + * Overloeaded compare operator. + * + * Two CTarget objects are considered equal, if all member variables + * and the base classes are equal. + * + * @return 1 if targets are equal, 0 if not. + */ + int operator==( const CTarget &rhs); + + /** + * Get all values of a CTarget object. + * + * @param read_comm - Read community name + * @param write_comm - Write community name + * @param address - Address of the target + * @param t - Timeout value + * @param r - Retries value + * @param v - The SNMP version of this target + * + * @return TRUE on success and FALSE on failure. + */ + int resolve_to_C( OctetStr& read_comm, + OctetStr& write_comm, + GenAddress &address, + unsigned long &t, + int &r, + unsigned char &v) const; + + protected: + OctetStr read_community; // get community + OctetStr write_community; // set community +}; + +// create OidCollection type +typedef SnmpCollection TargetCollection; + +#ifdef _SNMPv3 +#define INITIAL_USER "initial" +#else +#define INITIAL_USER "public" +#endif + +//----[ UTarget class ]---------------------------------------------- +/** + * User based Target. + * + * This class is used for SNMPv3 targets. + */ +class DLLOPT UTarget: public SnmpTarget +{ + public: + /** + * Constructor with no args. + * The validity of the target will be false. + */ + UTarget( void); + + /** + * Constructor with all args. + * + * @param address - Address of the target host (cann be any address object) + * @param sec_name - The security name + * @param sec_model - The security model to use + */ + UTarget( const Address &address, + const char *sec_name, + const int sec_model); + + /** + * Constructor with all args. + * + * @param address - Address of the target host (cann be any address object) + * @param sec_name - The security name + * @param sec_model - The security model to use + */ + UTarget( const Address &address, + const OctetStr &sec_name, + const int sec_model); + + /** + * Constructor with only address. + * + * Assumes the following defaults: security_name: initial, version: SNMPv3, + * security_model: v3MP. + * + * @param address - Address of the target host (cann be any address object) + */ + UTarget( const Address &address); + + /** + * Constructor from existing UTarget. + */ + UTarget( const UTarget &target); + + /** + * Destructor, that has nothing to do. + */ + ~UTarget() {}; + + /** + * Clone operator. + * + * Clone operation for creating a new UTarget from an existing + * UTarget. + * + * @note The caller MUST use the delete operation on the return + * value when done. + * + * @return A pointer to the new object on success, 0 on failure. + */ + SnmpTarget *clone() const { return (SnmpTarget *) new UTarget(*this); }; + + /** + * Set the address object. + * + * This method is the same as in SnmpTarget, but it deletes engine_id. + * + * @param address - The address that this target should use. + * @return TRUE on success. + */ + int set_address(const Address &address); + + /** + * Get the security name. + * + * @return A const reference to the security name. + */ + const OctetStr& get_security_name() const { return security_name;} ; + + /** + * Get the security name. + * + * @param oct - OctetStr that will be filled with the security name. + */ + void get_security_name( OctetStr& oct) const { oct = security_name; }; + + /** + * Set the security name. + * + * @param str - The new security name + */ + void set_security_name( const char * str) { security_name = str; }; + + /** + * Set the security name. + * + * @param oct - The new security name + */ + void set_security_name( const OctetStr& oct) { security_name = oct; }; + +#ifdef _SNMPv3 + /** + * Set the engine id. + * + * In most cases it is not necessary for the user to set the engine + * id as snmp++ performs engine id discovery. If the engine id is + * set by the user, no engine_id discovery is made, even if the + * engine id set by the user is wrong. + * + * @param str - The engine id to use + */ + void set_engine_id( const char * str) { engine_id = str; }; + + /** + * Set the engine id. + * + * In most cases it is not necessary for the user to set the engine + * id as snmp++ performs engine id discovery. If the engine id is + * set by the user, no engine_id discovery is made, even if the + * engine id set by the user is wrong. + * + * @param oct - The engine id to use + */ + void set_engine_id( const OctetStr &oct) { engine_id = oct; }; + + /** + * Get the engine id. + * + * @return A const reference to the enigne id of this target. + */ + const OctetStr& get_engine_id() const { return engine_id; }; + + /** + * Get the engine id. + * + * @param oct - OctetStr that will be filled with the engine id + */ + void get_engine_id( OctetStr& oct) const { oct = engine_id; }; +#endif + + /** + * Get the security_model. + * + * @return An integer representing the security_model of this target. + */ + int get_security_model() const { return security_model; }; + + /** + * Set the security_model. + * + * @param sec_model - The security model to use. + */ + void set_security_model(int sec_model) { security_model = sec_model; }; + + /** + * Overloaded assignment operator. + */ + UTarget& operator=( const UTarget& target); + + /** + * Overloeaded compare operator. + * + * Two UTarget objects are considered equal, if all member variables + * (beside the engine id) and the base classes are equal. + * + * @return 1 if targets are equal, 0 if not. + */ + virtual int operator==( const UTarget &rhs); + + /** + * Get all values of a UTarget object. + * + * @param sec_name - security name + * @param sec_model - security model + * @param address - Address of the target + * @param t - Timeout value + * @param r - Retries value + * @param v - The SNMP version of this target + * + * @return TRUE on success and FALSE on failure. + */ + int resolve_to_U( OctetStr& sec_name, + int &sec_model, + GenAddress &address, + unsigned long &t, + int &r, + unsigned char &v) const; + + protected: + OctetStr security_name; + int security_model; +#ifdef _SNMPv3 + OctetStr engine_id; +#endif +}; +#endif //_TARGET diff --git a/backend/camembert/libkmsnmp/timetick.cpp b/backend/camembert/libkmsnmp/timetick.cpp new file mode 100644 index 0000000..465e37a --- /dev/null +++ b/backend/camembert/libkmsnmp/timetick.cpp @@ -0,0 +1,135 @@ +/*_############################################################################ + _## + _## timetick.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + T I M E T I C K. C P P + + TIMETICK CLASS IMPLEMENTATION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class implentation for SMI Timeticks class. + +=====================================================================*/ +char timetick_cpp_version[]="#(@) SNMP++ $Id: timetick.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#include "timetick.h" // include header file for timetick class +#include // for sprintf() usage. + +// Copy constructor +TimeTicks::TimeTicks( const TimeTicks &t) +{ + smival.value.uNumber = t.smival.value.uNumber; + smival.syntax = sNMP_SYNTAX_TIMETICKS; +} + +// general assignment from any Value +SnmpSyntax& TimeTicks::operator=(const SnmpSyntax &in_val) +{ + if (this == &in_val) return *this; // handle assignement from itself + + valid_flag = false; // will get set true if really valid + if (in_val.valid()) + { + switch (in_val.get_syntax()) + { + case sNMP_SYNTAX_UINT32: + // case sNMP_SYNTAX_GAUGE32: .. indistinquishable from UINT32 + case sNMP_SYNTAX_CNTR32: + case sNMP_SYNTAX_TIMETICKS: + case sNMP_SYNTAX_INT32: // implied cast int -> uint + smival.value.uNumber = + ((TimeTicks &)in_val).smival.value.uNumber; + valid_flag = true; + break; + } + } + return *this; +} + +// ASCII format return +const char *TimeTicks::get_printable() const +{ + char *buf = PP_CONST_CAST(char *, output_buffer); + +/* unsigned long hseconds, seconds, minutes, hours, days; + unsigned long tt = smival.value.uNumber; + char *buf = PP_CONST_CAST(char*, output_buffer); + + days = tt / 8640000; + tt %= 8640000; + + hours = tt / 360000; + tt %= 360000; + + minutes = tt / 6000; + tt %= 6000; + + seconds = tt / 100; + tt %= 100; + + hseconds = tt; + + if (days == 0) + sprintf(buf, "%lu:%02lu:%02lu.%02lu", hours, minutes, seconds, hseconds); + else if (days == 1) + sprintf(buf, "1 day %lu:%02lu:%02lu.%02lu", + hours, minutes, seconds, hseconds); + else + sprintf(buf, "%lu days, %lu:%02lu:%02lu.%02lu", + days, hours, minutes, seconds, hseconds); +*/ + sprintf(buf, "%d", smival.value.uNumber); + return buf; +} diff --git a/backend/camembert/libkmsnmp/timetick.h b/backend/camembert/libkmsnmp/timetick.h new file mode 100644 index 0000000..1d4986c --- /dev/null +++ b/backend/camembert/libkmsnmp/timetick.h @@ -0,0 +1,161 @@ +/*_############################################################################ + _## + _## timetick.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ T I M E T I C K. H + + TIMETICK CLASS DEFINITION + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGUAGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + + DESCRIPTION: + Class definition for SMI Timeticks class. + +=====================================================================*/ +// $Id: timetick.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _TIMETICKS +#define _TIMETICKS + +#include "integer.h" + +#define TICKOUTBUF 30 // max formatted time string + +//------------[ TimeTicks Class ]----------------------------------- +/** + * The timeticks class allows all the functionality of unsigned + * integers but is recognized as a distinct SMI type. TimeTicks + * objects may be get or set into Vb objects. + */ +class DLLOPT TimeTicks : public SnmpUInt32 +{ + public: + /** + * Constructs a zero TimeTicks object. + */ + TimeTicks() : SnmpUInt32() + { smival.syntax = sNMP_SYNTAX_TIMETICKS; }; + + /** + * Constructs a TimeTicks object with the given value. + * + * @param val - time in hundredths of seconds. + */ + TimeTicks(const unsigned long val) : SnmpUInt32(val) + { smival.syntax = sNMP_SYNTAX_TIMETICKS; }; + + /** + * Copy constructor. + * + * @param t - Time for the new object. + */ + TimeTicks(const TimeTicks &t); + + /** + * Destructor. + */ + ~TimeTicks() {}; + + /** + * Return the syntax. + * + * @return Always returns sNMP_SYNTAX_TIMETICKS. + */ + SmiUINT32 get_syntax() const { return sNMP_SYNTAX_TIMETICKS; }; + + /** + * Get a printable ASCII value. + */ + const char *get_printable() const; + + /** + * Clone operator. + * + * @return Pointer to a newly created copy of the object. + */ + SnmpSyntax *clone() const { return (SnmpSyntax *) new TimeTicks(*this); }; + + /** + * Map other SnmpSyntax objects to TimeTicks. + */ + SnmpSyntax& operator=(const SnmpSyntax &val); + + /** + * Overloaded assignment for TimeTicks. + * + * @param uli - new value + * @return self reference + */ + TimeTicks& operator=(const TimeTicks &uli) + { smival.value.uNumber = uli.smival.value.uNumber; return *this; }; + + /** + * Overloaded assignment for unsigned longs. + * + * @param i - new value in hundrets of seconds + * @return self reference + */ + TimeTicks& operator=(const unsigned long i) + { smival.value.uNumber = i; return *this; }; + + /** + * Casting to unsigned long. + * + * @return Current value as hundrets of seconds + */ + operator unsigned long() { return smival.value.uNumber; }; + + protected: + /*mutable*/ char output_buffer[TICKOUTBUF]; // for storing printed form + +}; +#endif diff --git a/backend/camembert/libkmsnmp/usm_v3.h b/backend/camembert/libkmsnmp/usm_v3.h new file mode 100644 index 0000000..efb5508 --- /dev/null +++ b/backend/camembert/libkmsnmp/usm_v3.h @@ -0,0 +1,942 @@ +/*_############################################################################ + _## + _## usm_v3.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +// $Id: usm_v3.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _USM_V3 +#define _USM_V3 + +#include "config_snmp_pp.h" + +#ifdef _SNMPv3 + +#include "smi.h" +#include "octet.h" + + +#define MAXUINT32 4294967295u + +// the maximum allowed length of the username +#define MAXLEN_USMUSERNAME 32 +#define MAXLEN_USMSECURITYNAME MAXLEN_USMUSERNAME + +#define SNMPv3_AUTHFLAG 0x01 +#define SNMPv3_PRIVFLAG 0x02 +#define SNMPv3_REPORTABLEFLAG 0x04 + +#define NOKEY 0 +#define AUTHKEY 1 +#define PRIVKEY 2 +#define OWNAUTHKEY 3 +#define OWNPRIVKEY 4 + +#define SNMP_SECURITY_LEVEL_NOAUTH_NOPRIV 1 +#define SNMP_SECURITY_LEVEL_AUTH_NOPRIV 2 +#define SNMP_SECURITY_LEVEL_AUTH_PRIV 3 + +#define SNMP_AUTHPROTOCOL_NONE 1 +#define SNMP_AUTHPROTOCOL_HMACMD5 2 +#define SNMP_AUTHPROTOCOL_HMACSHA 3 + +#define SNMP_PRIVPROTOCOL_NONE 1 +#define SNMP_PRIVPROTOCOL_DES 2 +#define SNMP_PRIVPROTOCOL_IDEA 9 +#define SNMP_PRIVPROTOCOL_AES128 19 +#define SNMP_PRIVPROTOCOL_AES192 20 +#define SNMP_PRIVPROTOCOL_AES256 21 + +#define SNMPv3_USM_OK 1400 +#define SNMPv3_USM_ERROR 1401 +#define SNMPv3_USM_ERROR_CONFIGFILE 1402 +#define SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL 1403 +#define SNMPv3_USM_UNKNOWN_SECURITY_NAME 1404 +#define SNMPv3_USM_ENCRYPTION_ERROR 1405 +#define SNMPv3_USM_DECRYPTION_ERROR 1406 +#define SNMPv3_USM_AUTHENTICATION_ERROR 1407 +#define SNMPv3_USM_AUTHENTICATION_FAILURE 1408 +#define SNMPv3_USM_PARSE_ERROR 1409 +#define SNMPv3_USM_UNKNOWN_ENGINEID 1410 +#define SNMPv3_USM_NOT_IN_TIME_WINDOW 1411 +#define SNMPv3_USM_UNSUPPORTED_AUTHPROTOCOL 1412 +#define SNMPv3_USM_UNSUPPORTED_PRIVPROTOCOL 1413 +#define SNMPv3_USM_ADDRESS_ERROR 1414 +#define SNMPv3_USM_FILECREATE_ERROR 1415 +#define SNMPv3_USM_FILEOPEN_ERROR 1416 +#define SNMPv3_USM_FILERENAME_ERROR 1417 +#define SNMPv3_USM_FILEDELETE_ERROR 1418 +#define SNMPv3_USM_FILEWRITE_ERROR 1419 +#define SNMPv3_USM_FILEREAD_ERROR 1420 + + +#define SNMPv3_USM_MAX_ERROR SNMPv3_USM_FILEREAD_ERROR +#define SNMPv3_USM_MIN_ERROR SNMPv3_USM_OK +#define SNMPv3_USM_ERRORCOUNT SNMPv3_USM_MAX_ERROR - SNMPv3_USM_MIN_ERROR + +#define oidUsmStats "1.3.6.1.6.3.15.1.1" +#define oidUsmStatsUnsupportedSecLevels "1.3.6.1.6.3.15.1.1.1.0" +#define oidUsmStatsNotInTimeWindows "1.3.6.1.6.3.15.1.1.2.0" +#define oidUsmStatsUnknownUserNames "1.3.6.1.6.3.15.1.1.3.0" +#define oidUsmStatsUnknownEngineIDs "1.3.6.1.6.3.15.1.1.4.0" +#define oidUsmStatsWrongDigests "1.3.6.1.6.3.15.1.1.5.0" +#define oidUsmStatsDecryptionErrors "1.3.6.1.6.3.15.1.1.6.0" + +#define oidUsmUserTable "1.3.6.1.6.3.15.1.2.2" +#define oidUsmUserEntry "1.3.6.1.6.3.15.1.2.2.1" + +#define oidUsmAuthProtocolBase "1.3.6.1.6.3.10.1.1" +#define oidUsmNoAuthProtocol "1.3.6.1.6.3.10.1.1.1" +#define oidUsmHMACMD5AuthProtocol "1.3.6.1.6.3.10.1.1.2" +#define oidUsmHMACSHAAuthProtocol "1.3.6.1.6.3.10.1.1.3" + +#define oidUsmPrivProtocolBase "1.3.6.1.6.3.10.1.2" +#define oidUsmNoPrivProtocol "1.3.6.1.6.3.10.1.2.1" +#define oidUsmDESPrivProtocol "1.3.6.1.6.3.10.1.2.2" +#define oidUsmIDEAPrivProtocol "1.3.6.1.6.3.10.1.2.9" +#define oidUsmAES128PrivProtocol "1.3.6.1.6.3.10.1.2.19" +#define oidUsmAES192PrivProtocol "1.3.6.1.6.3.10.1.2.20" +#define oidUsmAES256PrivProtocol "1.3.6.1.6.3.10.1.2.21" + + +#define USM_KeyUpdate 1 +#define USM_PasswordUpdate 2 +#define USM_PasswordKeyUpdate 3 +#define USM_PasswordAllKeyUpdate 4 + +class SnmpTarget; +class Pdu; + +struct UsmKeyUpdate; + +struct UsmUserTableEntry { + unsigned char *usmUserEngineID; long int usmUserEngineIDLength; + unsigned char *usmUserName; long int usmUserNameLength; + unsigned char *usmUserSecurityName; long int usmUserSecurityNameLength; + long int usmUserAuthProtocol; + unsigned char *usmUserAuthKey; long int usmUserAuthKeyLength; + long int usmUserPrivProtocol; + unsigned char *usmUserPrivKey; long int usmUserPrivKeyLength; +}; + +struct UsmUser { + unsigned char *engineID; long int engineIDLength; + unsigned char *usmUserName; long int usmUserNameLength; + unsigned char *securityName; long int securityNameLength; + long int authProtocol; + unsigned char *authKey; long int authKeyLength; + long int privProtocol; + unsigned char *privKey; long int privKeyLength; +}; + +struct UsmUserNameTableEntry { + OctetStr usmUserName; + OctetStr usmUserSecurityName; + long int usmUserAuthProtocol; + long int usmUserPrivProtocol; + unsigned char *authPassword; long int authPasswordLength; + unsigned char *privPassword; long int privPasswordLength; +}; + +//-----------[ async methods callback ]----------------------------------- +typedef void (*usm_add_user_callback)(const OctetStr &engine_id, + const OctetStr &usm_user_name, + const OctetStr &usm_user_security_name, + const int auth_protocol, + const OctetStr &auth_key, + const int priv_protocol, + const OctetStr &priv_key); + +struct SecurityStateReference; + +class AuthPriv; +class USMTimeTable; +class USMUserNameTable; +class USMUserTable; +class v3MP; + +/** + * This is the class for the User Based Security Model. + * + * To add or delete users, the methods add_usm_user() and delete_usm_user() + * should be used. + */ +class DLLOPT USM +{ + friend class v3MP; + +public: + + /** + * Create an instance of the USM. + * + * @param engine_boots - The new value for the snmpEngineBoots counter + * @param engine_id - The local snmp engine id + * @param v3_mp - Pointer to the parent v3MP object. + * @param msg_id - OUT: The initial value for the msgID + * @param result - OUT: construct status, should be SNMPv3_USM_OK + */ + USM(unsigned int engine_boots, const OctetStr &engine_id, const v3MP *v3_mp, + unsigned int *msg_id, int &result); + + /** + * Destructor. + */ + ~USM(); + + /** + * Enables the discovery mode of the USM, i.e. the USM accepts all messages + * with unknown engine ids and adds these engine ids to its tables. + */ + void set_discovery_mode() { discovery_mode = 1; }; + + /** + * Disables the discovery mode of the USM, i.e. the USM will not accept any + * message with an unknown engine id. + */ + void unset_discovery_mode() { discovery_mode = 0; }; + + /** + * Return TRUE if the USM discovery mode is enabled, FALSE else. + */ + int is_discovery_enabled() const { return discovery_mode; }; + + /** + * Add a new user to the usmUserNameTable. If the User is already known + * to the USM, the old entry is replaced. + * The USM will compute a userName for the given securityName, which + * will be the same as securityName (recommended). + * + * @param security_name - Unique securityName + * @param auth_protocol - Possible values are: + * SNMP_AUTHPROTOCOL_NONE, + * SNMP_AUTHPROTOCOL_HMACMD5, + * SNMP_AUTHPROTOCOL_HMACSHA + * @param priv_protocol - Possible values are: + * SNMP_PRIVPROTOCOL_NONE, + * SNMP_PRIVPROTOCOL_DES, + * SNMP_PRIVPROTOCOL_IDEA + * @param auth_password - Secret password for authentication + * @param priv_password - Secret password for privacy + * + * @return - SNMPv3_USM_OK or + * SNMP_v3_USM_ERROR (memory error, not initialized) + */ + int add_usm_user(const OctetStr& security_name, + const long int auth_protocol, + const long int priv_protocol, + const OctetStr& auth_password, + const OctetStr& priv_password); + + /** + * Add a new user to the usmUserNameTable. If the userName is already known + * to the USM, the old entry is replaced. + * + * It is not recommended to add users with userName != securityName. + * + * @param user_name - Unique userName + * @param security_name - Unique securityName + * @param auth_protocol - Possible values are: + * SNMP_AUTHPROTOCOL_NONE, + * SNMP_AUTHPROTOCOL_HMACMD5, + * SNMP_AUTHPROTOCOL_HMACSHA + * @param priv_protocol - Possible values are: + * SNMP_PRIVPROTOCOL_NONE, + * SNMP_PRIVPROTOCOL_DES, + * SNMP_PRIVPROTOCOL_IDEA + * @param auth_password - Secret password for authentication + * @param priv_password - Secret password for privacy + * + * @return - SNMPv3_USM_OK or + * SNMP_v3_USM_ERROR (memory error, not initialized) + */ + int add_usm_user(const OctetStr& user_name, + const OctetStr& security_name, + const long int auth_protocol, + const long int priv_protocol, + const OctetStr& auth_password, + const OctetStr& priv_password); + + /** + * Delete all occurences of the user with the given security name + * from the USM. + * + * @param security_name - the securityName of the user + * + * @return - SNMPv3_USM_OK, SNMPv3_USM_ERROR (not initialized) + */ + int delete_usm_user(const OctetStr& security_name); + + + /** + * Save all localized users into a file. + * + * @param file - filename including path + * + * @return SNMPv3_USM_ERROR, SNMPv3_USM_FILECREATE_ERROR, + * SNMPv3_USM_FILERENAME_ERROR or SNMPv3_USM_OK + */ + int save_localized_users(const char *file); + + /** + * Load localized users from a file. + * + * @param file - filename including path + * + * @return SNMPv3_USM_ERROR, SNMPv3_USM_FILEOPEN_ERROR, + * SNMPv3_USM_FILEREAD_ERROR or SNMPv3_USM_OK + */ + int load_localized_users(const char *file); + + /** + * Save all users with their passwords into a file. + * + * @param file - filename including path + * + * @return SNMPv3_USM_ERROR, SNMPv3_USM_FILECREATE_ERROR, + * SNMPv3_USM_FILERENAME_ERROR or SNMPv3_USM_OK + */ + int save_users(const char *file); + + /** + * Load users with their passwords from a file. + * + * @param file - filename including path + * + * @return SNMPv3_USM_ERROR, SNMPv3_USM_FILEOPEN_ERROR, + * SNMPv3_USM_FILEREAD_ERROR or SNMPv3_USM_OK + */ + int load_users(const char *file); + + /** + * Add or replace a localized user in the USM table. Use this method + * only, if you know what you are doing. + * + * @param engine_id - The engineID, the key was localized with + * @param user_name - The name of the user (in the USM) + * @param security_name - The securityName of the user, this name + * is the same for all securityModels + * @param auth_protocol - Possible values are: + * SNMP_AUTHPROTOCOL_NONE, + * SNMP_AUTHPROTOCOL_HMACMD5, + * SNMP_AUTHPROTOCOL_HMACSHA + * @param auth_key - The key used for authentications + * @param priv_protocol - Possible values are: + * SNMP_PRIVPROTOCOL_NONE, + * SNMP_PRIVPROTOCOL_DES, + * SNMP_PRIVPROTOCOL_IDEA + * @param priv_key - The key used for privacy + * + * @return - SNMPv3_USM_OK + * SNMP_v3_USM_ERROR (not initialized, no memory) */ + int add_localized_user(const OctetStr &engine_id, + const OctetStr &user_name, + const OctetStr &security_name, + const long auth_protocol, + const OctetStr &auth_key, + const long priv_protocol, + const OctetStr &priv_key); +#if 0 + // deprecated! + int add_localized_user( + const unsigned char *engine_id, const long engine_id_len, + const unsigned char *user_name, const long user_name_len, + const unsigned char *security_name, const long security_name_len, + const long auth_protocol, + const unsigned char *auth_key, const long auth_key_len, + const long priv_protocol, + const unsigned char *priv_key, const long priv_key_len); +#endif + + /** + * Delete all localized entries of this user from the usmUserTable. + * + * @param user_name - The userName that should be deleted + * + * @return - SNMPv3_USM_ERROR (not initialized), + * SNMPv3_USM_OK (user deleted or not in table) + */ + int delete_localized_user(const OctetStr& user_name); + + + /** + * Delete the entry with the given userName and engineID + * from the usmUserTable + * + * @param engine_id - The engineID + * @param user_name - The userName that should be deleted + * + * @return - SNMPv3_USM_ERROR (not initialkized), + * SNMPv3_USM_OK (user deleted or not in table) + */ + int delete_localized_user(const OctetStr& engine_id, + const OctetStr& user_name); + + + /** + * Replace a localized key of the user and engineID in the + * usmUserTable. + * + * @param user_name - The name of the user in the USM + * @param user_name_len - The length of the user name + * @param engine_id - Change the localized key for the SNMP + * entity with this engine id + * @param engine_id_len - The length of the engine id + * @param new_key - The new key + * @param new_key_len - The length of the new key + * @param type_of_key - AUTHKEY, OWNAUTHKEY, PRIVKEY or OWNPRIVKEY + * + * @return - SNMPv3_USM_ERROR (no such entry or not initialized), + * SNMPv3_USM_OK + */ + int update_key(const unsigned char* user_name, const long user_name_len, + const unsigned char* engine_id, const long engine_id_len, + const unsigned char* new_key, const long new_key_len, + const int type_of_key); + + /** + * Search for a user with the given securityName and engineID + * in the usmUserTable and return the entry. If no entry + * could be found, the usmUserNameTable is searched for the given + * securityName. If this table has an entry of this user, a + * localized entry is generated, added to the usmUserTable and + * returned to the caller. + * + * The caller has to do a delete on the returned struct. + * + * @param engine_id - + * @param security_name - + * + * @return - a pointer to the structure if an entry could be found + * or was generated, NULL for all errors + */ + struct UsmUser *get_user(const OctetStr &engine_id, + const OctetStr &security_name); + + + /** + * Get the security name from a user name. + * + * @param user_name - + * @param user_name_len - + * @param security_name - Buffer for the securityName + * + * @return - SNMPv3_USM_ERROR (not initialized, not found, buffer too small), + * SNMPv3_USM_OK + */ + int get_security_name(const unsigned char *user_name, + const long int user_name_len, + OctetStr &security_name); + + /** + * Get the user name from a security name. + * + * @param user_name - Buffer for the userName + * @param user_name_len - Has to be set to the max length of the + * buffer. Is set to the length of the found + * securityName or to 0 if not found. + * @param security_name - + * @param security_name_len - + * + * @return - SNMPv3_USM_ERROR (not initialized, not found, buffer too small), + * SNMPv3_USM_OK + */ + int get_user_name(unsigned char *user_name, + long int *user_name_len, + const unsigned char *security_name, + const long int security_name_len); + + + /** + * Prepare a key update in the USM. The following procedure is used: To + * prepare the key update, this function adds the neccessary variable + * bindings to the Pdu to do the key update on the target SNMP entity. + * The Pdu has to be sent to the target. If the key update on the target + * is successful, usmCommitKeyUpdate() has to be called to do the local key + * update. On failure usmAbortKeyUpdate() has to be called to free + * temporary ressources. + * + * @param securityName - The name of the user + * @param target - A target to identify the SNMP entity on which the + * key will be updated + * @param newPassword - The new password for the user + * @param pdu - A PDU into which this funktion adds the VBs needed + * to change the keys on the target + * @param type - Indicates how and which key should be chaned: + * possilbe values are: AUTHKEY, PRIVKEY and + * OWNAUTHKEY, OWNPRIVKEY. + * @param status - The return status: SNMPv3_USM_OK or one of the + * error codes + * + * @return - A structure, that is needed to commit/abort the key update. + * If an error occurs, the return value is NULL + */ + struct UsmKeyUpdate* key_update_prepare(const OctetStr& securityName, + SnmpTarget& target, + const OctetStr& newPassword, + Pdu& pdu, int type, + int &status, + const OctetStr& oldpass = "", + const OctetStr& oldengid= "", + const OctetStr& newengid= ""); + + /** + * Abort the local key update. + * + * @param uku - The pointer returned by usmPrepareKeyUpdate() + */ + void key_update_abort(struct UsmKeyUpdate *uku); + + + /** + * Commit the local key update. + * + * @param uku - The pointer returned by usmPrepareKeyUpdate() + * @param update_type - One of USM_KeyUpdate, USM_PasswordKeyUpdate, + * USM_PasswordAllKeyUpdate + * + * @return - SNMPv3_USM_ERROR, SNMPv3_USM_OK + */ + int key_update_commit(struct UsmKeyUpdate *uku, int update_type); + + + /** + * Get a pointer to the AuthPriv object used by the USM. + * + */ + AuthPriv *get_auth_priv(); + + + /** + * Return engineBoots and engineTime for a given engineID + * + * @param engine_id - The engineID of the SNMP entity + * @param engine_boots - OUT: boot counter (0 if not found) + * @param engine_time - OUT: engine time (0 if not found) + * + * @return - SNMPv3_USM_ERROR (not initialized), + * SNMPv3_USM_OK (entry found, values are filled) + * SNMPv3_USM_UNKNOWN_ENGINEID ( not found) + */ + int get_time(const OctetStr &engine_id, + long int *engine_boots, long int *engine_time); + + + + /** + * Return engineBoots and engineTime of the local snmp entity + * + * @param engine_boots - OUT: boot counter (0 if not found) + * @param engine_time - OUT: engine time (0 if not found) + * + * @return - SNMPv3_USM_ERROR (not initialized), + * SNMPv3_USM_OK (entry found, values are filled) + */ + int get_local_time(long int *engine_boots, long int *engine_time) const; + + + /** + * Return the local snmp engine id. + */ + const OctetStr& get_local_engine_id() const { return local_snmp_engine_id; }; + + /** + * Get the number of received messages with an unsupported securityLevel + * + * @return - usmStatsUnsupportedSecLevels + */ + unsigned long get_stats_unsupported_sec_levels() const + { return usmStatsUnsupportedSecLevels; }; + + /** + * Get the number of received messages outside time window + * + * @return - usmStatsNotInTimeWindows + */ + unsigned long get_stats_not_in_time_windows() const + { return usmStatsNotInTimeWindows; }; + + /** + * Get the number of received messages with a unknown userName + * + * @return - usmStatsUnknownUserNames + */ + unsigned long get_stats_unknown_user_names() const + { return usmStatsUnknownUserNames; }; + + /** + * Get the number of received messages with a unknown engineID + * + * @return - usmStatsUnknownEngineIDs + */ + unsigned long get_stats_unknown_engine_ids() const + { return usmStatsUnknownEngineIDs; }; + + /** + * Get the number of received messages with a wrong digest + * + * @return - usmStatsWrongDigests + */ + unsigned long get_stats_wrong_digests() const + { return usmStatsWrongDigests; }; + + /** + * Get the number of received messages with decryption errors + * + * @return - usmStatsDecryptionErrors + */ + unsigned long get_stats_decryption_errors() const + { return usmStatsDecryptionErrors; }; + + //@{ + /** + * Increase the stats counter. Should only be used by agent++. + */ + void inc_stats_unsupported_sec_levels(); + void inc_stats_not_in_time_windows(); + void inc_stats_unknown_user_names(); + void inc_stats_unknown_engine_ids(); + void inc_stats_wrong_digests(); + void inc_stats_decryption_errors(); + //@} + + + /** + * Get a const pointer to the first entry of the UsmUserNameTable. + */ + const UsmUserNameTableEntry *peek_first_user(); + + /** + * Get a const pointer to the next entry of the UsmUserNameTable. + */ + const UsmUserNameTableEntry *peek_next_user(const UsmUserNameTableEntry *e); + + + /** + * Get a const pointer to the first entry of the UsmUserTable. + */ + const UsmUserTableEntry *peek_first_luser(); + + /** + * Get a const pointer to the next entry of the UsmUserTable. + */ + const UsmUserTableEntry *peek_next_luser(const UsmUserTableEntry *e); + +#if 0 + //! deprecated + int get_security_name(const unsigned char *user_name, + const long int user_name_len, + unsigned char *security_name, + int *security_name_len); + //! deprecated + struct UsmUser *get_user(const unsigned char *engine_id, + const long int engine_id_len, + const unsigned char *security_name, + const long int security_name_len); +#endif + + /** + * for v3MP: + * + * Delete the pointers within the structure and the structure + * itself. + * + * @param ssr - The structure that should be deleted. + */ + void delete_sec_state_reference(struct SecurityStateReference *ssr); + + /** + * Protected (for agent++): + * + * Get the user at the specified position of the usmUserTable. + * + * The caller is responsible to delete the entries usmUserEngineID, + * usmUserNamem, usmUserSecurityName of the returned struct and the + * struct. + * + * @param number - get the entry at position number (1...) + * + * @return - a pointer to the structure or NULL if number is out + * of range + */ + struct UsmUserTableEntry *get_user(int number); + + /** + * Protected (for agent++): + * + * Get the properties of the specified user. + * + * The caller is responsible to delete the returned struct. + * + * @param security_name - The security name of the user + * + * @return - a pointer to the structure or NULL if number is out + * of range + */ + struct UsmUserNameTableEntry *get_user(const OctetStr &security_name); + + /** + * Protected (for agent++): + * + * Get the number of elements in the usmUserTable + * + * @return - number of elements + */ + int get_user_count() const; + + + /** + * Protected (for agent++) + * + * Register a callback function that is called if a new localized user + * has been added to the usm user table + */ + void add_user_added_callback(const usm_add_user_callback cb); + + + protected: + + /** + * Get a new security state reference (for v3MP). + * + * @return - A newly created security state reference. + */ + struct SecurityStateReference *get_new_sec_state_reference(); + + /** + * Generate a complete message that is ready to send to the target. + * + * @param globalData - Buffer containing the serialized globalData, + * ready to be copied into the wholeMsg + * @param globalDataLength - The length of this buffer + * @param maxMessageSize - The maximum message size + * @param securityEngineID - The engineID of the authoritative SNMP entity + * @param securityName - The name of the user + * @param securityLevel - The security Level for this Message + * @param scopedPDU - Buffer containing the serialized scopedPDU, + * ready to be copied into the wholeMsg + * @param scopedPDULength - The length of this Buffer + * @param securityStateReference - The reference that was generated when + * the request was parsed. For request, this + * param has to be NULL. The reference + * is deleted by this function. + * @param wholeMsg - OUT: the buffer for the whole message + * @param wholeMsgLength - IN: lenght of the buffer. + * OUT: length of the generated message + * + * @return - SNMPv3_USM_OK on success. See snmperrs.h for the error codes + * of the USM. + */ + int generate_msg( + unsigned char *globalData, // message header, admin data + int globalDataLength, + int maxMessageSize, // of the sending SNMP entity + const OctetStr &securityEngineID,// authoritative SNMP entity + const OctetStr &securityName, // on behalf of this principal + int securityLevel, // Level of Security requested + unsigned char *scopedPDU, // message (plaintext) payload + int scopedPDULength, + struct SecurityStateReference *securityStateReference, + unsigned char *wholeMsg, // OUT complete generated message + int *wholeMsgLength); // OUT length of generated message + + + + /** + * Parse a received message. + * + * @param maxMessageSize - The maximum message size of the snding + * SNMP entity. + * @param securityParameters - The security parameters as received + * @param securityParametersLength - The length of the security parameters + * @param securityParametersPosition - The position of the security + * parameters in the message + * @param securityLevel - The securityLevel of the message + * @param wholeMsg - The buffer with the whole message + * @param wholeMsgLength - The length of the whole message + * @param msgData - The buffer with the messageData + * @param msgDataLength - The length of the messageData buffer + * @param security_engine_id - OUT: the authoritative engineID + * @param security_name - OUT: the name of the user + * @param scopedPDU - OUT: buffer containing the scopedPDU + * @param scopedPDULength - IN: length of the buffer + * OUT: length of the scopedPDU + * @param maxSizeResponseScopedPDU - OUT: maximum size for a scopedPDU in a + * response message + * @param securityStateReference - OUT: the securityStateReference + * + * @return - SNMPv3_USM_OK on success. See snmperrs.h for the error codes + * of the USM. + */ + int process_msg( + int maxMessageSize, // of the sending SNMP entity + unsigned char *securityParameters, // for the received message + int securityParametersLength, + int securityParametersPosition, + long int securityLevel, // Level of Security + unsigned char *wholeMsg, // as received on the wire + int wholeMsgLength, // length as received on the wire + unsigned char *msgData, + int msgDataLength, + OctetStr &security_engine_id, // authoritative SNMP entity + OctetStr &security_name, //identification of the principal + unsigned char *scopedPDU, // message (plaintext) payload + int *scopedPDULength, + long *maxSizeResponseScopedPDU,// maximum size of the Response PDU + struct SecurityStateReference *securityStateReference + // reference to security state + ); // information, needed for response + +private: + + /** + * Delete the pointers in the structure and set all values to 0/NULL. + * + * @param usp - The structure that should be deleted + */ + void delete_sec_parameters( struct UsmSecurityParameters *usp); + + + /** + * Serialize the given values into the buffer according to the BER. + * + * UsmSecurityParameters ::= + * SEQUENCE { + * -- global User-based security parameters + * msgAuthoritativeEngineID OCTET STRING (5..32) + * msgAuthoritativeEngineBoots INTEGER (0..2147483647), + * msgAuthoritativeEngineTime INTEGER (0..2147483647), + * msgUserName OCTET STRING (SIZE(0..32)), + * -- authentication protocol specific parameters + * msgAuthenticationParameters OCTET STRING, + * -- privacy protocol specific parameters + * msgPrivacyParameters OCTET STRING + * } + * + * @param outBuf - buffer for the serialized values + * @param maxLength - before call: length of the buffer + * after call: bytes left in the buffer + * @param sp - the values to serialize + * @param position - after call: points to the first byte of the + * field for the authentication parameter + * + * @return - a pointer to the first free byte in the buffer, + * NULL on error + */ + unsigned char *build_sec_params(unsigned char *outBuf, int *maxLength, + struct UsmSecurityParameters sp, + int *position); + + /** + * Serialize the given values acording to the BER into the + * buffer. On success, the buffer contains a valid SNMPv3 message. + * + * @param outBuf - buffer for the serialized values + * @param maxLength - before call: length of the buffer + * after call: bytes left in the buffer + * @param globalData - Buffer that contains the serialized globalData + * @param globalDataLength - The length of this buffer + * @param positionAuthPar - after call: points to the first byte of the + * field for the authentication parameter + * @param securityParameters - The security parameters + * @param msgData - Buffer that contains the serialized msgData + * @param msgDataLength - The length of this buffer + * + * @return - a pointer to the first free byte in the buffer, + * NULL on error + */ + unsigned char *build_whole_msg( + unsigned char *outBuf, int *maxLength, + unsigned char *globalData, long int globalDataLength, + int *positionAuthPar, + struct UsmSecurityParameters securityParameters, + unsigned char *msgData, long int msgDataLength); + + + /** + * Delete the pointers in the structure + * + * @param user - The structure that should be deleted + */ + inline void delete_user_ptr(struct UsmUser *user); + + + private: + + OctetStr local_snmp_engine_id; ///< local snmp engine id + const v3MP *v3mp; ///< Pointer to the v3MP that created this object + + // 0: don't accept messages from hosts with a unknown engine id + int discovery_mode; + + // MIB Counters + unsigned int usmStatsUnsupportedSecLevels; + unsigned int usmStatsNotInTimeWindows; + unsigned int usmStatsUnknownUserNames; + unsigned int usmStatsUnknownEngineIDs; + unsigned int usmStatsWrongDigests; + unsigned int usmStatsDecryptionErrors; + + // the instance of AuthPriv + AuthPriv *auth_priv; + + // this table contains time values of contacted snmp entities + USMTimeTable *usm_time_table; + + // Users that are known but not localized to a engine ID + USMUserNameTable *usm_user_name_table; + + // Table containing localized Users ready to use + USMUserTable *usm_user_table; + + // Callback for agent++ to indicate new users in usm tables + usm_add_user_callback usm_add_user_cb; + +}; + + +// only for compatibility do not use these values and functions: +// ============================================================= + +#define SecurityLevel_noAuthNoPriv SNMP_SECURITY_LEVEL_NOAUTH_NOPRIV +#define SecurityLevel_authNoPriv SNMP_SECURITY_LEVEL_AUTH_NOPRIV +#define SecurityLevel_authPriv SNMP_SECURITY_LEVEL_AUTH_PRIV + +#define SNMPv3_usmNoAuthProtocol SNMP_AUTHPROTOCOL_NONE +#define SNMPv3_usmHMACMD5AuthProtocol SNMP_AUTHPROTOCOL_HMACMD5 +#define SNMPv3_usmHMACSHAAuthProtocol SNMP_AUTHPROTOCOL_HMACSHA + +#define SNMPv3_usmNoPrivProtocol SNMP_PRIVPROTOCOL_NONE +#define SNMPv3_usmDESPrivProtocol SNMP_PRIVPROTOCOL_DES +#define SNMPv3_usmIDEAPrivProtocol SNMP_PRIVPROTOCOL_IDEA +#define SNMPv3_usmAES128PrivProtocol SNMP_PRIVPROTOCOL_AES128 +#define SNMPv3_usmAES192PrivProtocol SNMP_PRIVPROTOCOL_AES192 +#define SNMPv3_usmAES256PrivProtocol SNMP_PRIVPROTOCOL_AES256 + + +#endif // _SNMPv3 + +#endif diff --git a/backend/camembert/libkmsnmp/v3.h b/backend/camembert/libkmsnmp/v3.h new file mode 100644 index 0000000..01bf60d --- /dev/null +++ b/backend/camembert/libkmsnmp/v3.h @@ -0,0 +1,243 @@ +/*_############################################################################ + _## + _## v3.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +// $Id: v3.h,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $ + +#ifndef _V3_H +#define _V3_H + +#include +#include + +#include "config_snmp_pp.h" + +class OctetStr; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/** @name SNMPv3 Security Model values + */ +//@{ +#define SNMP_SECURITY_MODEL_ANY 0 //!< Not used in SNMP++. +#define SNMP_SECURITY_MODEL_V1 1 //!< Can be used for SNMPv1 only. +#define SNMP_SECURITY_MODEL_V2 2 //!< Can be used for SNMPv2 only. +#define SNMP_SECURITY_MODEL_USM 3 //!< Can be used for SNMPv3 only. +//@} + +/** + * Set the logfile for logging output. + * + * @param filename - Complete path and name of the logfile. + * + * @note The string may not be deleted after the call. + */ +DLLOPT void debug_set_logfile(const char *filename); + +/** + * Set the amount of log messages you want to get. To disable all + * messages, set the level to -1 + * + * @param level - New level + */ +DLLOPT void debug_set_level(const int db_level); + +#ifdef _DEBUG + +/** + * SNMP++ logging function. + * + * The default is to log all messages with a level < 19. To change + * that either edit v3.cpp or use a "extern unsigned int debug_level" + * to change the level. + * + * @param db_level - Priority of the message (0 = high) + * @param format - Just like printf + */ +DLLOPT void debugprintf(int db_level, const char *format, ...); + +/** + * SNMP++ logging function for hex strings. + * + * @param db_level - Priority of the message (0 = high) + * @param comment - Comment printed before the hex dump (may be 0) + * @param data - pointer to the hex data + * @param len - length of the hex data + */ +DLLOPT void debughexcprintf(int db_level, const char* comment, + const unsigned char *data, const unsigned int len); + +//! Wrapper for debughexcprintf() without comment. +#define debughexprintf(db_level, data, len) \ + debughexcprintf(db_level, NULL, data, len); + +#else + +#ifndef _MSC_VER +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define debugprintf(db_level,format...) +#else +void debugprintf(int db_level, const char *format, ...); +#endif +#else +// disable any warning for wrong number of arguments in macro +#pragma warning(disable:4002) +#define debugprintf(db_level,format) +#endif //_MSC_VER + +#define debughexprintf( db_level, data, len) +#define debughexcprintf(db_level, comment, data, len) + +#endif + +#ifdef _SNMPv3 + +#define V3MAXMESSAGESIZE SNMP_MSG_LENGTH +#define MAXLENGTH_ENGINEID 32 +#define MAXLENGTH_FILENAME 255 + +#define oidV3SnmpEngine "1.3.6.1.6.3.10.2.1" +#define oidV3SnmpEngineID "1.3.6.1.6.3.10.2.1.1.0" +#define oidV3SnmpEngineBoots "1.3.6.1.6.3.10.2.1.2.0" +#define oidV3SnmpEngineTime "1.3.6.1.6.3.10.2.1.3.0" +#define oidV3SnmpEngineMaxMessageSize "1.3.6.1.6.3.10.2.1.4.0" + +// also defined in agent++/include/vacm.h +#ifndef oidSnmpUnavailableContexts +#define oidSnmpUnavailableContexts "1.3.6.1.6.3.12.1.4.0" +#define oidSnmpUnknownContexts "1.3.6.1.6.3.12.1.5.0" +#endif + +/** @name Error codes (storing engineBoots) + * + * These values are returned by getBootCounter() and saveBootCounter(). + */ +//@{ +#define SNMPv3_OK 0 //!< No error +#define SNMPv3_NO_ENTRY_ERROR -1 //!< No line for the engine id found +#define SNMPv3_FILEOPEN_ERROR -2 //!< Unable to open file +#define SNMPv3_TOO_LONG_ERROR -3 //!< The given engineID is too long +#define SNMPv3_FILE_ERROR -4 //!< The given file contains a wrong line +#define SNMPv3_FILECREATE_ERROR -5 //!< The File could not be created +#define SNMPv3_FILERENAME_ERROR -6 //!< Error renaming the temporary file +//@} + +/** + * Compare two strings. + * + * @param str1 - The first byte array + * @param ptr1len - Length of first array + * @param str2 - The second byte array + * @param ptr2len - Length of second array + * + * @return 1 if the strings are identical, 0 if not. + */ +DLLOPT int unsignedCharCompare(const unsigned char *str1, + const long int ptr1len, + const unsigned char *str2, + const long int ptr2len); + +/** + * String copy function. + * + * @note The returned string has to be deleted with "delete []". + * + * @param src - Source string + * @param srclen - Length of source string + * + * @return Pointer to a null terminated copy of src (or 0 on error). + */ +DLLOPT unsigned char *v3strcpy(const unsigned char *src, const int srclen); + +/** + * Encode the given string into the output buffer. For each byte + * of the string two bytes in the output buffer are used. The + * output buffer will contain chars from 0x64 to 0x79. + * + * @param in - The string (for example engine id) to encode + * @param in_length - The length of the engineID + * @param out - The output buffer for the encoded string, must have + * lenth 2 * in_length + */ +DLLOPT void encodeString(const unsigned char* in, const int in_length, + char* out); + +/** + * Decode the given encoded string into the output buffer. + * + * @param in - The encoded string + * @param in_length - The length of the encoded string + * @param out - Buffer for the decoded string (half size of input + * string). The String will be null terminated. + */ +DLLOPT void decodeString(const unsigned char* in, const int in_length, + char* out); + +/** + * Read the bootCounter of the given engineID stored in the given file. + * + * @param fileName - The name of the file + * @param engineId - Read the bootCounter for this enigneID + * @param boot - OUT: the bootCounter that was read + * + * @return One of SNMPv3_OK, SNMPv3_TOO_LONG_ERROR, SNMPv3_FILE_ERROR, + * SNMPv3_NO_ENTRY_ERROR, SNMPv3_FILEOPEN_ERROR + * + */ +DLLOPT int getBootCounter(const char *fileName, + const OctetStr &engineId, unsigned int &boot); + +/** + * Store the bootCounter of the given engineID in the given file. + * + * @param fileName - The name of the file + * @param engineId - Store the bootCounter for this enigneID + * @param boot - The bootCounter + * + * @return One of SNMPv3_OK, SNMPv3_FILEOPEN_ERROR, SNMPv3_FILECREATE_ERROR, + * SNMPv3_FILERENAME_ERROR. + * + */ +DLLOPT int saveBootCounter(const char *fileName, + const OctetStr &engineId, const unsigned int boot); + + +#endif // _SNMPv3 + +// only for compatibility do not use these values: +#define SecurityModel_any SNMP_SECURITY_MODEL_ANY +#define SecurityModel_v1 SNMP_SECURITY_MODEL_V1 +#define SecurityModel_v2 SNMP_SECURITY_MODEL_V2 +#define SecurityModel_USM SNMP_SECURITY_MODEL_USM + +#endif // _V3_H diff --git a/backend/camembert/libkmsnmp/vb.cpp b/backend/camembert/libkmsnmp/vb.cpp new file mode 100644 index 0000000..4ad6681 --- /dev/null +++ b/backend/camembert/libkmsnmp/vb.cpp @@ -0,0 +1,363 @@ +/*_############################################################################ + _## + _## vb.cpp + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS" without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + + V B . C P P + + VARIABLE BINDING CLASS IMPLEMENTATION + + DESCRIPTION: + This module contains the class implementation of the VB class. + The Vb class is an encapsulation of the snmp variable binding. + + DESIGN + AUTHOR: + Peter E Mellquist + + LANGAUGE: + ANSI C++ + + OPERATING SYSTEMS: + MS-Windows Win32 + BSD UNIX + +=====================================================================*/ +char vb_cpp_version[]="#(@) SNMP++ $Id: vb.cpp,v 1.1.1.1 2005/03/14 10:55:29 kindman Exp $"; + +#include "vb.h" // include vb class defs + +#define IP_ADDR_SIZE 4 +#define IPX_ADDR_SIZE 10 +#define MAC_ADDR_SIZE 6 + +//--------------[ Vb::valid() ]----------------------------------------- +// returns validity of a Vb object +// must have a valid oid and value +bool Vb::valid() const +{ + if (iv_vb_oid.valid() && +#ifdef WHEN_WE_HAVE_SNMPNULL_CLASS + iv_vb_value && iv_vb_value->valid() +#else + ((iv_vb_value == NULL) || (iv_vb_value && iv_vb_value->valid())) +#endif + ) + return true; + return false; +} + +//---------------[ Vb& Vb::operator=(const Vb &vb) ]-------------------- +// overloaded assignment allows assigning one Vb to another +// this involves deep memory thus target vb needs to be freed +// before assigning source +Vb& Vb::operator=(const Vb &vb) +{ + if (this == &vb) return *this; // check for self assignment + + free_vb(); // free up target to begin with + + //-----[ reassign the Oid portion 1st ] + vb.get_oid(iv_vb_oid); + + //-----[ next set the vb value portion ] + if (vb.iv_vb_value == NULL) + iv_vb_value = NULL; + else + iv_vb_value = vb.iv_vb_value->clone(); + + exception_status = vb.exception_status; + + return *this; // return self reference +} + +//----------------[ void Vb::free_vb() ]-------------------------------- +// protected method to free memory +// this method is used to free memory when assigning new vbs +// or destructing +// in the case of oids and octets, we need to do a deep free +void Vb::free_vb() +{ + if (iv_vb_value) + { + delete iv_vb_value; + iv_vb_value = NULL; + } + exception_status = SNMP_CLASS_SUCCESS; +} + +//---------------------[ Vb::get_value(int &i) ]---------------------- +// get value int +// returns 0 on success and value +int Vb::get_value(int &i) const +{ + if (iv_vb_value && + iv_vb_value->valid() && + (iv_vb_value->get_syntax() == sNMP_SYNTAX_INT32 )) + { + long lval; + lval = *((SnmpInt32 *)iv_vb_value);// SnmpInt32 includes cast to long, + i = (int) lval; // but not to int. + return SNMP_CLASS_SUCCESS; + } + return SNMP_CLASS_INVALID; +} + + + +//--------------[ Vb::get_value(long int &i) ]------------------------- +// get the signed long int +// returns 0 on success and a value +int Vb::get_value(long &i) const +{ + if (iv_vb_value && + iv_vb_value->valid() && + (iv_vb_value->get_syntax() == sNMP_SYNTAX_INT32 )) + { + i = *((SnmpInt32 *)iv_vb_value); // SnmpInt32 includes cast to long + return SNMP_CLASS_SUCCESS; + } + return SNMP_CLASS_INVALID; +} + + +//-----------------[ Vb::get_value(unsigned long int &i) ]-------------- +// get the unsigned long int +// returns 0 on success and a value +int Vb::get_value(unsigned long &i) const +{ + if (iv_vb_value && + iv_vb_value->valid() && + ((iv_vb_value->get_syntax() == sNMP_SYNTAX_UINT32 ) || + (iv_vb_value->get_syntax() == sNMP_SYNTAX_CNTR32 ) || + (iv_vb_value->get_syntax() == sNMP_SYNTAX_GAUGE32 ) || + (iv_vb_value->get_syntax() == sNMP_SYNTAX_TIMETICKS ))) + { + i = *((SnmpUInt32 *)iv_vb_value); // SnmpUint32 has includes to ulong + return SNMP_CLASS_SUCCESS; + } + return SNMP_CLASS_INVALID; +} + + +//--------------[ Vb::get_value(unsigned char WINFAR * ptr, unsigned long &len) +// get a unsigned char string value +// destructive, copies into given ptr +// also returned is the length +// +// Note!! The user must provide a target string +// which is big enough to hold the string +int Vb::get_value(unsigned char *ptr, unsigned long &len) const +{ + if (iv_vb_value && + iv_vb_value->valid() && + (iv_vb_value->get_syntax() == sNMP_SYNTAX_OCTETS)) + { + OctetStr *p_os = (OctetStr *)iv_vb_value; + len = p_os->len(); + memcpy(ptr, p_os->data(), len); + ptr[len] = 0; + return SNMP_CLASS_SUCCESS; + } + + ptr[0] = 0; + len = 0; + return SNMP_CLASS_INVALID; +} + +//---------------[ Vb::get_value ]------------------------------------- +// get an unsigned char array +// caller specifies max len of target space +int Vb::get_value(unsigned char *ptr, unsigned long &len, + const unsigned long maxlen) const +{ + if (iv_vb_value && + iv_vb_value->valid() && + (iv_vb_value->get_syntax() == sNMP_SYNTAX_OCTETS)) + { + OctetStr *p_os = (OctetStr *)iv_vb_value; + len = p_os->len(); + if (len > maxlen) len = maxlen; + memcpy(ptr, p_os->data(), len); + ptr[len] = 0; + return SNMP_CLASS_SUCCESS; + } + + ptr[0] = 0; + len = 0; + return SNMP_CLASS_INVALID; +} + + +//---------------[ Vb::get_value(Value &val) ]-------- +int Vb::get_value(SnmpSyntax &val) const +{ + if (iv_vb_value) + { + val = *iv_vb_value; + if (val.valid()) + return SNMP_CLASS_SUCCESS; + return SNMP_CLASS_INVALID; + } + // TM: should set val to be invalid + return SNMP_CLASS_INVALID; +} + +//--------------[ Vb::get_value(char WINFAR *ptr) ]------------------- +// get a char * from an octet string +// the user must provide space or +// memory will be stepped on +int Vb::get_value(char *ptr) const +{ + if (iv_vb_value && + iv_vb_value->valid() && + (iv_vb_value->get_syntax() == sNMP_SYNTAX_OCTETS)) + { + OctetStr *p_os = (OctetStr *)iv_vb_value; + unsigned long len = p_os->len(); + memcpy(ptr, p_os->data(), len); + ptr[len] = 0; + return SNMP_CLASS_SUCCESS; + } + + ptr[0] = 0; + return SNMP_CLASS_INVALID; +} + + + +//-----[ misc]-------------------------------------------------------- + +// return the current syntax +// This method violates Object Orientation but may be useful if +// the caller has a vb object and does not know what it is. +// This would be useful in the implementation of a browser. +SmiUINT32 Vb::get_syntax() const +{ + if (exception_status != SNMP_CLASS_SUCCESS) + return exception_status; + else + return (iv_vb_value ? iv_vb_value->get_syntax() : sNMP_SYNTAX_NULL); +} + +void Vb::set_syntax(SmiUINT32 syntax) +{ + free_vb(); // setting to SNMP_SYNTAX_NULL + + exception_status = SNMP_CLASS_SUCCESS; + + switch (syntax) { + case sNMP_SYNTAX_INT32: + iv_vb_value = new SnmpInt32(); + break; + case sNMP_SYNTAX_TIMETICKS: + iv_vb_value = new TimeTicks(); + break; + case sNMP_SYNTAX_CNTR32: + iv_vb_value = new Counter32(); + break; + case sNMP_SYNTAX_GAUGE32: + iv_vb_value = new Gauge32(); + break; +/* Not distinguishable from Gauge32 + case sNMP_SYNTAX_UINT32: + iv_vb_value = new SnmpUInt32(); + break; +*/ + case sNMP_SYNTAX_CNTR64: + iv_vb_value = new Counter64(); + break; + case sNMP_SYNTAX_BITS: + case sNMP_SYNTAX_OCTETS: + iv_vb_value = new OctetStr(); + break; + case sNMP_SYNTAX_OPAQUE: + iv_vb_value = new OpaqueStr(); + break; + case sNMP_SYNTAX_IPADDR: + iv_vb_value = new IpAddress(); + break; + case sNMP_SYNTAX_OID: + iv_vb_value = new Oid(); + break; + case sNMP_SYNTAX_NULL: + break; + case sNMP_SYNTAX_NOSUCHINSTANCE: + exception_status = sNMP_SYNTAX_NOSUCHINSTANCE; + break; + case sNMP_SYNTAX_NOSUCHOBJECT: + exception_status = sNMP_SYNTAX_NOSUCHOBJECT; + break; + case sNMP_SYNTAX_ENDOFMIBVIEW: + exception_status = sNMP_SYNTAX_ENDOFMIBVIEW; + break; + case sNMP_SYNTAX_SEQUENCE: + break; + } +} + +static char blank_string[] = ""; + +// return the printabel value +const char WINFAR *Vb::get_printable_value() const +{ + if (iv_vb_value) + return iv_vb_value->get_printable(); + return blank_string; +} + +int Vb::get_asn1_length() const +{ + // Header for vbs is always 4 Bytes! FIXME + if (iv_vb_value) + return iv_vb_oid.get_asn1_length() + iv_vb_value->get_asn1_length() + 4; + + return iv_vb_oid.get_asn1_length() + 2 + 4; +} + +// deprecated friend function to set exception status +void set_exception_status(Vb *vb, const SmiUINT32 status) +{ + if (vb) + vb->set_exception_status(status); +} diff --git a/backend/camembert/libkmsnmp/vb.h b/backend/camembert/libkmsnmp/vb.h new file mode 100644 index 0000000..bc19d30 --- /dev/null +++ b/backend/camembert/libkmsnmp/vb.h @@ -0,0 +1,384 @@ +/*_############################################################################ + _## + _## vb.h + _## + _## SNMP++v3.2.9c + _## ----------------------------------------------- + _## Copyright (c) 2001-2003 Jochen Katz, Frank Fock + _## + _## This software is based on SNMP++2.6 from Hewlett Packard: + _## + _## Copyright (c) 1996 + _## Hewlett-Packard Company + _## + _## ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + _## Permission to use, copy, modify, distribute and/or sell this software + _## and/or its documentation is hereby granted without fee. User agrees + _## to display the above copyright notice and this license notice in all + _## copies of the software and any documentation of the software. User + _## agrees to assume all liability for the use of the software; + _## Hewlett-Packard and Jochen Katz make no representations about the + _## suitability of this software for any purpose. It is provided + _## "AS-IS" without warranty of any kind, either express or implied. User + _## hereby grants a royalty-free license to any and all derivatives based + _## upon this software code base. + _## + _## Stuttgart, Germany, Tue Dec 2 01:31:09 CET 2003 + _## + _##########################################################################*/ +/*=================================================================== + + Copyright (c) 1999 + Hewlett-Packard Company + + ATTENTION: USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS. + Permission to use, copy, modify, distribute and/or sell this software + and/or its documentation is hereby granted without fee. User agrees + to display the above copyright notice and this license notice in all + copies of the software and any documentation of the software. User + agrees to assume all liability for the use of the software; Hewlett-Packard + makes no representations about the suitability of this software for any + purpose. It is provided "AS-IS without warranty of any kind,either express + or implied. User hereby grants a royalty-free license to any and all + derivatives based upon this software code base. + + + SNMP++ V B . H + + VARIABLE BINDING CLASS DEFINITION + + DESCRIPTION: + This module contains the class definition for the variable binding + class. The VB class is an encapsulation of a SNMP VB. A VB object is + composed of an SNMP++ Oid and an SMI value. The Vb class utilizes Oid + objects and thus requires the Oid class. The Vb class may be used + stand alone and does not require use of any other snmp library. + + + DESIGN + AUTHOR: + Peter E. Mellquist + + LANGAUGE: + ANSI C++ + + OPERATING SYSTEM: + MS-Windows Win32 + BSD UNIX + +=====================================================================*/ +// $Id: vb.h,v 1.1.1.1 2005/03/14 10:55:30 kindman Exp $ + +#ifndef _VB_CLS +#define _VB_CLS + +#include "oid.h" // oid class def +#include "timetick.h" // time ticks +#include "counter.h" // counter +#include "gauge.h" // gauge class +#include "ctr64.h" // 64 bit counters +#include "octet.h" // octet class +#include "address.h" // address class def +#include "integer.h" // integer class +#include "snmperrs.h" + +//------------[ VB Class Def ]------------------------------------- +/** + * The Vb class is the encapsulation of the SNMP variable binding. + * + * Variable binding lists in SNMP++ are represented as arrays of Vb + * objects. Vb objects are passed to and from SNMP objects to provide + * getting or setting MIB values. The vb class keeps its own memory + * for objects and does not utilize pointers to external data + * structures. + */ +class DLLOPT Vb +{ + //-----[ public members ] + public: + + //-----[ constructors / destructors ]------------------------------- + + /** + * Constructor with no arguments. + * + * This constructor creates an unitialized vb. + */ + Vb() : iv_vb_value(0), exception_status(SNMP_CLASS_SUCCESS) {}; + + /** + * Constructor to initialize the oid. + * + * This constructor creates a vb with oid portion initialized. + */ + Vb(const Oid &oid) + : iv_vb_oid(oid), iv_vb_value(0), exception_status(SNMP_CLASS_SUCCESS) {}; + + /** + * Copy constructor. + */ + Vb(const Vb &vb) : iv_vb_value(0) { *this = vb; }; + + /** + * Destructor that frees all allocated memory. + */ + ~Vb() { free_vb(); }; + + /** + * Overloaded assignment operator. + */ + Vb& operator=(const Vb &vb); + + /** + * Clone operator. + */ + Vb *clone( ) const { return new Vb(*this); }; + + //-----[ set oid / get oid ]------------------------------------------ + + /** + * Set the oid from another oid. + */ + void set_oid(const Oid &oid) { iv_vb_oid = oid; }; + + /** + * Get the oid portion. + * + * @note Check the validity of the object through Vb::valid() before + * calling this method. + */ + void get_oid(Oid &oid) const { oid = iv_vb_oid; }; + + /** + * Get the oid portion as a const. + * + * @note Check the validity of the object through Vb::valid() before + * calling this method. + */ + const Oid &get_oid() const { return iv_vb_oid; }; + + //-----[ set value ]-------------------------------------------------- + + /** + * Set the value using any SnmpSyntax object. + */ + void set_value(const SnmpSyntax &val) + { free_vb(); iv_vb_value = val.clone(); }; + + /** + * Set the value with an int. + * + * The syntax of the Vb will be set to SMI INT32. + */ + void set_value(const int i) { free_vb(); iv_vb_value = new SnmpInt32(i); }; + + /** + * Set the value with an int. + * + * The syntax of the Vb will be set to SMI INT32. + */ + void set_value(const long i) + { free_vb(); iv_vb_value = new SnmpInt32(i); }; + + /** + * Set the value with an unsigned long int. + * + * The syntax of the Vb will be set to SMI UINT32. + */ + void set_value(const unsigned long i) + { free_vb(); iv_vb_value = new SnmpUInt32(i); }; + + /** + * Set value using a null terminated string. + * + * The syntax of the Vb will be set to SMI octet. + */ + void set_value(const char *ptr) + { free_vb(); iv_vb_value = new OctetStr(ptr); }; + + /** + * Set value using a string and length. + * + * The syntax of the Vb will be set to SMI octet. + */ + void set_value(const unsigned char *ptr, const unsigned int len) + { free_vb(); iv_vb_value = new OctetStr(ptr, len); }; + + /** + * Set the value portion of the vb to null, if its not already. + */ + void set_null() { free_vb(); }; + + //----[ get value ]------------------------------------------------ + + /** + * Get the value using a SnmpSyntax object. + * + * @param val - An object of a subclass of SnmpSyntax that will be + * assigned the value of the vb. + * + * @return SNMP_CLASS_SUCCESS if the vb value could be assigned to + * the passed SnmpSyntax object, else SNMP_CLASS_INVALID. + */ + int get_value(SnmpSyntax &val) const; + + /** + * Get the value. + * + * This method will only return success if the value of the vb is SMI INT32. + * + * @param i - returned value + * + * @return SNMP_CLASS_SUCCESS on success, else SNMP_CLASS_INVALID. + */ + int get_value(int &i) const; + + /** + * Get the value. + * + * This method will only return success if the value of the vb is SMI INT32. + * + * @param i - returned value + * + * @return SNMP_CLASS_SUCCESS on success, else SNMP_CLASS_INVALID. + */ + int get_value(long &i) const; + + /** + * Get the value. + * + * This method will only return success if the value of the vb can + * be mapped to an unsigned long (SMI types uint32, counter32, gauge + * and timeticks). + * + * @param i - returned value + * + * @return SNMP_CLASS_SUCCESS on success, else SNMP_CLASS_INVALID. + */ + int get_value(unsigned long &i) const; + + /** + * Get the value. + * + * This method will only return success if the value of the vb is SMI OCTET. + * + * @note The caller must provide a target string big enough to + * handle the vb string. No length checks are done within + * this method. + * + * @param ptr - Pointer to already allocated space to hold the vb + * value. The first char will be set to zero on failure. + * @param len - Returned length of the string. Will be set to 0 on failure. + * + * @return SNMP_CLASS_SUCCESS on success, else SNMP_CLASS_INVALID. + */ + int get_value(unsigned char *ptr, unsigned long &len) const; + + /** + * Get the value. + * + * This method will only return success if the value of the vb is SMI OCTET. + * + * @note If the target space is not big enough to hold the complete + * string only maxlen bytes are copied. In any case the returned + * string is NOT null terminated. + * + * @param ptr - Pointer to already allocated space to hold the vb + * value. The first char will be set to zero on failure. + * @param len - Returned length of the string. Will be set to 0 + * on failure. + * @param maxlen - Maximum length of the space that ptr points to. + * + * @return SNMP_CLASS_SUCCESS on success, else SNMP_CLASS_INVALID. + */ + int get_value(unsigned char *ptr, + unsigned long &len, + const unsigned long maxlen) const; + + /** + * Get the value. + * + * This method will only return success if the value of the vb is SMI OCTET. + * + * @note The caller must provide a target string big enough to + * handle the vb string. No length checks are done within + * this method. The returned string will be null terminated. + * + * @param ptr - Pointer to already allocated space to hold the vb + * value. The first char will be set to zero on failure. + * + * @return SNMP_CLASS_SUCCESS on success, else SNMP_CLASS_INVALID. + */ + int get_value(char *ptr) const; + + //-----[ misc]-------------------------------------------------------- + + /** + * Return the syntax or the exception status. + * + * @return If the SNMPv2 exception status is set, it is returned. + * otherwise the syntax of the value object is returned. + */ + SmiUINT32 get_syntax() const; + + /** + * Set the syntax. + * + * The Value portion of the Vb will be deleted and a new value portion + * is allocated with it's default value (zero). + * + * @param syntax - The new syntax. + */ + void set_syntax(const SmiUINT32 syntax); + + /** + * Set the exception status. + * + * @param status - the new SNMPv2 exception status. + */ + void set_exception_status(const SmiUINT32 status) + { exception_status = status; }; + + //! deprecated! Use Vb::set_exception_status() + DLLOPT friend void set_exception_status(Vb *vb, const SmiUINT32 status); + + /** + * Return a formatted version of the value. + * + * @return A null terminated string (empty if no value). + */ + const char *get_printable_value() const; + + /** + * Return a formatted version of the Oid. + * + * @return A null terminated string (may be empty if no Oid has been set). + */ + const char *get_printable_oid() const + { return iv_vb_oid.get_printable(); }; + + /** + * Return the validity of a Vb object. + * + * @return TRUE if oid and value have been set. + */ + bool valid() const; + + /** + * Return the space needed for serialization. + * + * @return the length of the BER encoding of this Vb. + */ + int get_asn1_length() const; + +//-----[ protected members ] +protected: + Oid iv_vb_oid; // a vb is made up of a oid + SnmpSyntax *iv_vb_value; // and a value... + SmiUINT32 exception_status; // are there any vb exceptions?? + + /** + * Free the value portion. + */ + void free_vb(); +}; +#endif diff --git a/backend/camembert/main b/backend/camembert/main new file mode 100755 index 0000000000000000000000000000000000000000..4eb853b02cf807b7a640c487b2533be86ab9af01 GIT binary patch literal 172722 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

1^xFKvH12i*VVc;b8bl?p!_?DlaH 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 literal 0 HcmV?d00001 diff --git a/backend/camembert/main.cpp b/backend/camembert/main.cpp new file mode 100644 index 0000000..6a52e31 --- /dev/null +++ b/backend/camembert/main.cpp @@ -0,0 +1,529 @@ +/* ================================= + +Camembert Project +Alban FERON, 2007 + +================================== */ + +#include "camembert.h" + +int snmp_answers, snmp_requests, snmp_dups; + +SNMP* snmp; +MaterielList* lstMateriel; + +Monitor* monitor; + +pthread_mutex_t + mtx_materiels, + mtx_activity, + mtx_jobs; +pthread_cond_t allConsumed; +unsigned int jobsrunning = 0; + +unsigned int maxIdMateriel; +unsigned int maxIdInterface; + +int tehdate; + +void addMateriel(Materiel *const mat) { + if(!lstMateriel) + lstMateriel = new MaterielList(mat); + else + lstMateriel = lstMateriel->addMateriel(mat); +} + + +// Destruction des mutex et libération de la mémoire +void free_memory() { + pthread_mutex_destroy(&mtx_materiels); + pthread_mutex_destroy(&mtx_jobs); + pthread_mutex_destroy(&mtx_activity); + pthread_cond_destroy(&allConsumed); + + delete snmp; + delete lstMateriel; + delete monitor; +} + +void terminate(int code) { + free_memory(); + DisconnectDB(); + exit(code); +} + +void init_globals() { + // Connection à la base de données. + if (ConnectDB() == 0) { + printf( "!! Impossible de se connecter à la base de données\n"); + exit(-1); + } + + // Création des grosse variables. + snmp = new SNMP(); + monitor = new Monitor(MAX_JOBS); + + lstMateriel = NULL; + + // Mutexes pour la protection des données avec les threads + pthread_mutex_init(&mtx_materiels, NULL); + pthread_mutex_init(&mtx_jobs, NULL); + pthread_mutex_init(&mtx_activity, NULL); + pthread_cond_init(&allConsumed, NULL); + + snmp_answers = 0; + snmp_requests = 0; + snmp_dups = 0; + + tehdate = time(NULL); +} + +void read_db_materiel() { + void *r, *r2; + unsigned int i, j, n, m; + Materiel *mat; + IPList *ipl; + char buffer[256]; + + // On charge tout le matériel depuis la base de données + r = QueryDB("SELECT * FROM materiel"); + n = _PQntuples(r); + for(i=0; iaddIP(new IP(_PQgetvalue(r2, j, 0)), DBSTATUS_OLD); + _PQclear(r2); + + // Instanciation du matériel + mat = new Materiel(atoi(_PQgetvalue(r, i, 0)), snmp, COMMUNITY, ipl); + mat->setHostName (_PQgetvalue(r, i, 1)); + mat->setManageable (atoi(_PQgetvalue(r, i, 2))); + mat->setVersion (atoi(_PQgetvalue(r, i, 3))); + mat->setType (_PQgetvalue(r, i, 4)); + mat->setOSType (_PQgetvalue(r, i, 5)); + mat->setCapabilities(atoi(_PQgetvalue(r, i, 6))); + mat->setDBstatus(DBSTATUS_OLD); + addMateriel(mat); + + if(mat->getManageableStatus() != STATUS_NOTMANAGEABLE) + monitor->addJob(mat); + } + _PQclear(r); +} + +void read_db_interfaces() { + void *r, *r2; + unsigned int i, j, n, m, ifDbId; + Materiel *mat; + Interface *iface; + char buffer[256]; + + r = QueryDB("SELECT idinterface, idmateriel, ifnumber FROM interface"); + n = _PQntuples(r); + for(i=0; inbOldLinks = m; + if(m > 0) { + iface->oldLinks = new unsigned int[m]; + for(j=0; joldLinks[j] = atoi(_PQgetvalue(r2, j, 0)); + } + _PQclear(r2); + + // Actions sur l'interface + sprintf(buffer, "SELECT numaction, option FROM action WHERE idinterface = %d", ifDbId); + r2 = QueryDB(buffer); + m = _PQntuples(r2); + for(j=0; jaddAction(atoi(_PQgetvalue(r2, j, 0)), _PQgetvalue(r2, j, 1)); + _PQclear(r2); + sprintf(buffer, "DELETE FROM action WHERE idinterface = %d", ifDbId); + QueryDB(buffer); + + // Ajout de l'interface au materiel correspondant + mat = lstMateriel->getMaterielById(atoi(_PQgetvalue(r, i, 1))); + if(mat) + mat->addInterface(iface, DBSTATUS_OLD); + } + _PQclear(r); +} + +void read_db() { + void *r; + + read_db_materiel(); + read_db_interfaces(); + + r = QueryDB("SELECT MAX(idmateriel) FROM materiel"); + maxIdMateriel = atoi(_PQgetvalue(r, 0, 0)); + _PQclear(r); + + r = QueryDB("SELECT MAX(idinterface) FROM interface"); + maxIdInterface = atoi(_PQgetvalue(r, 0, 0)); + _PQclear(r); +} + +void check_materiel() { + if(!lstMateriel) { + Materiel *planet = new Materiel(++maxIdMateriel, snmp, COMMUNITY, new IP(FIRST_IP)); + planet->setDBstatus(DBSTATUS_NEW); + addMateriel(planet); + monitor->addJob(planet); + } +} + +void update_db_materiel() { + MaterielList *ml; + IPList *ipl; + Materiel *m; + char *buffer = new char[4*1024]; + int ptr; + bool bFirst; + + // Parcourt la liste du matériel + for(ml=lstMateriel; ml!=NULL; ml=ml->getNext()) { + ptr = sprintf(buffer, "BEGIN;\n"); + + m = ml->getCurrentMateriel(); + // Si il est nouveau, on l'insère + if(m->getDBstatus() == DBSTATUS_NEW) { + ptr += sprintf(&buffer[ptr], "INSERT INTO materiel VALUES(%d, '%s', %d, %d, '%s', '%s', %d, %d, %d);\n", + m->getID(), m->getHostName(), m->isManageable(), m->getVersion(), m->getType(), + m->getOSType(), m->getCapabilities(), tehdate, tehdate); + } + // si il n'est pas nouveau mais qu'il a été trouvé, on l'update + else if(m->getDBstatus() == DBSTATUS_UPDATED) { + ptr += sprintf(&buffer[ptr], "UPDATE materiel SET hostname='%s', manageable=%d, snmpversion=%d, type='%s'", + m->getHostName(), m->isManageable(), m->getVersion(), m->getType()); + ptr += sprintf(&buffer[ptr], ", ostype='%s', capabilities=%d, datelast=%d WHERE idmateriel=%d;\n", + m->getOSType(), m->getCapabilities(), tehdate, m->getID()); + } + + // On fait le tour des IP pour vérifier qu'une vieille IP ne soit pas déclarée comme principale + for(ipl=m->getIPList(); ipl!=NULL; ipl=ipl->getNext()) { + if(ipl->getDBstatus() > DBSTATUS_OLD) { + m->getIPList()->setFirst(ipl); + break; + } + } + + bFirst = true; + // Parcourt la liste des IPs du matériel + for(ipl=m->getIPList(); ipl!=NULL; ipl=ipl->getNext()) { + if(ipl->getDBstatus() == DBSTATUS_NEW) { + ptr += sprintf(&buffer[ptr], "INSERT INTO ip VALUES('%s', %d, '%d', %d, %d);\n", ipl->getIP()->getIPstr(), + m->getID(), bFirst, tehdate, tehdate); + } + else if(ipl->getDBstatus() == DBSTATUS_UPDATED) { + ptr += sprintf(&buffer[ptr], "UPDATE ip SET main='%d', datelast=%d WHERE ip='%s' AND idmateriel=%d;\n", + bFirst, tehdate, ipl->getIP()->getIPstr(), m->getID()); + } + else if(ipl->getDBstatus() == DBSTATUS_OLD && !bFirst) { + ptr += sprintf(&buffer[ptr], "UPDATE ip SET main='f' WHERE ip='%s' AND idmateriel='%d';\n", + ipl->getIP()->getIPstr(), m->getID()); + } + bFirst = false; + } + + ptr += sprintf(&buffer[ptr], "END;\n"); + QueryDB(buffer); + } + + delete[] buffer; +} + +void update_db_interfaces() { + MaterielList *ml; + Materiel *m; + InterfaceList *ifl; + Interface *i; + char *buffer = new char[1024*1024]; + int ptr; + char macaddr[24], descr[256], tmpdescr[256], lastsrc[24]; + unsigned short int n; + unsigned int idLink; + bool bOldLink; + dstDevice_t* dd; + + for(ml=lstMateriel; ml!=NULL; ml=ml->getNext()) { + m = ml->getCurrentMateriel(); + ifl = m->getInterfaces(); + if(!ifl) + continue; + + ptr = sprintf(buffer, "BEGIN;\n"); + for(; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + + if(strlen(i->getAddress()) == 0) + strcpy(macaddr, "NULL"); + else + sprintf(macaddr, "'%s'", i->getAddress()); + + if(!i->getDescription()) + strcpy(descr, "NULL"); + else { + sprintf(descr, "'%s'", i->getDescription()); + //_PQescapeString(tmpdescr, i->getDescription()); + //sprintf(descr, "'%s'", tmpdescr); + } + + if(strlen(i->getLastMacAddr()) == 0) + strcpy(lastsrc, "NULL"); + else + sprintf(lastsrc, "'%s'", i->getLastMacAddr()); + + if(ifl->getDBstatus() == DBSTATUS_NEW) { + ptr += sprintf(&buffer[ptr], "INSERT INTO interface VALUES(%d, %d, %d, '%s', %s, %s, %d, '%d', '%d', %d, %d, %d, %d, %d, %d, %d, '%d', '%d', %d, %d, %d, %d, %s, '%d');\n", + i->getID(), m->getID(), i->getIfNumber(), i->getName(), descr, + macaddr, i->speed, i->adminStatus, i->operStatus, i->ifType, i->vlan, i->voiceVlan, i->nativeVlan, + i->moduleNum, i->portNum, i->portDot1d, i->spanningTree, i->portSecEnabled, i->portSecStatus, + i->maxMacCount, i->currMacCount, i->violationCount, lastsrc, i->stickyEnabled); + } + else if(ifl->getDBstatus() == DBSTATUS_UPDATED) { + ptr += sprintf(&buffer[ptr], "UPDATE interface SET ifname='%s', ifdescription=%s, ifaddress=%s, ifspeed=%d, ifadminstatus='%d', ", + i->getName(), descr, macaddr, i->speed, i->adminStatus); + ptr += sprintf(&buffer[ptr], "ifoperstatus='%d', iftype=%d, ifvlan=%d, ifvoicevlan=%d, ifnativevlan=%d, ifmodule=%d, ifport=%d, ", + i->operStatus, i->ifType, i->vlan, i->voiceVlan, i->nativeVlan, i->moduleNum, i->portNum); + ptr += sprintf(&buffer[ptr], "portdot1d=%d, portfast='%d', portsecenable='%d', portsecstatus=%d, portsecmaxmac=%d, ", + i->portDot1d, i->spanningTree, i->portSecEnabled, i->portSecStatus, i->maxMacCount); + ptr += sprintf(&buffer[ptr], "portseccurrmac=%d, portsecviolation=%d, portseclastsrcaddr=%s, portsecsticky='%d' WHERE idinterface=%d;\n", + i->currMacCount, i->violationCount, lastsrc, i->stickyEnabled, i->getID()); + } + + dd = i->getDistantDevice(); + while(dd) { + bOldLink = false; + idLink = ((Materiel*)dd->distantDevice)->getID(); + for(n=0; nnbOldLinks; n++) { + if(idLink == i->oldLinks[n]) { + ptr += sprintf(&buffer[ptr], "UPDATE link SET dstifname='%s', datelast=%d WHERE idinterface=%d AND iddstmateriel=%d;\n", + dd->dstIfName, tehdate, i->getID(), idLink); + bOldLink = true; + break; + } + } + if(!bOldLink) + ptr += sprintf(&buffer[ptr], "INSERT INTO link VALUES(%d, %d, '%s', %d, %d);\n", i->getID(), + idLink, dd->dstIfName, tehdate, tehdate); + + dd = dd->next; + } + } + + ptr += sprintf(&buffer[ptr], "END;\n"); + QueryDB(buffer); + } + + delete[] buffer; +} + +void update_db_arp() { + MaterielList *ml; + Materiel *m; + ARPCache *arp; + char *buffer = new char[2*1024*1024]; + int ptr; + + sprintf(buffer, "CREATE TABLE arp_%d (mac macaddr, ip inet)", tehdate); + QueryDB(buffer); + + for(ml=lstMateriel; ml!=NULL; ml=ml->getNext()) { + m = ml->getCurrentMateriel(); + arp = m->getARPCache(); + + if(!arp) + continue; + + ptr = sprintf(buffer, "BEGIN;\n"); + for(; arp!=NULL; arp=arp->getNext()) + ptr += sprintf(&buffer[ptr], "INSERT INTO arp_%d VALUES('%s', '%s');\n", tehdate, arp->getMAC(), arp->getIP()->getIPstr()); + + ptr += sprintf(&buffer[ptr], "UPDATE arpcache SET datelast=%d, foundon=%d WHERE (mac, ip) IN (SELECT mac, ip FROM arp_%d);\n", + tehdate, m->getID(), tehdate); + ptr += sprintf(&buffer[ptr], "INSERT INTO arpcache SELECT mac, ip, %d, %d, %d FROM arp_%d WHERE (mac, ip) NOT IN (SELECT mac, ip FROM arpcache);\n", + m->getID(), tehdate, tehdate, tehdate); + ptr += sprintf(&buffer[ptr], "DELETE FROM arp_%d; END;\n", tehdate); + QueryDB(buffer); + } + + sprintf(buffer, "DROP TABLE arp_%d", tehdate); + QueryDB(buffer); + + delete[] buffer; +} + +void update_db_fdb() { + MaterielList *ml; + InterfaceList *ifl; + Interface *i; + ForwardingDatabase *fdb; + char *buffer = new char[1024*1024]; + int ptr; + + sprintf(buffer, "CREATE TABLE fdb_%d (idinterface integer, vlan integer, mac macaddr, type integer)", tehdate); + QueryDB(buffer); + + for(ml=lstMateriel; ml!=NULL; ml=ml->getNext()) { + ifl = ml->getCurrentMateriel()->getInterfaces(); + + ptr = sprintf(buffer, "BEGIN;\n"); + + for(; ifl!=NULL; ifl=ifl->getNext()) { + i = ifl->getInterface(); + fdb = i->getForwardingDB(); + if(!fdb) + continue; + + for(; fdb!=NULL; fdb=fdb->getNext()) + ptr += sprintf(&buffer[ptr], "INSERT INTO fdb_%d VALUES(%d, %d, '%s', %d);\n", tehdate, i->getID(), fdb->getVLAN(), fdb->getMAC(), fdb->getType()); + + } + + ptr += sprintf(&buffer[ptr], "UPDATE fdb SET datelast=%d WHERE (idinterface, vlan, mac, type) IN (SELECT * FROM fdb_%d);\n", + tehdate, tehdate); + ptr += sprintf(&buffer[ptr], "INSERT INTO fdb SELECT idinterface, vlan, mac, %d, %d, type FROM fdb_%d WHERE (idinterface, vlan, mac, type) NOT IN (SELECT idinterface, vlan, mac, type FROM fdb);\n", + tehdate, tehdate, tehdate); + + ptr += sprintf(&buffer[ptr], "DELETE FROM fdb_%d; END;\n", tehdate); + QueryDB(buffer); + } + + sprintf(buffer, "DROP TABLE fdb_%d", tehdate); + QueryDB(buffer); + + delete[] buffer; +} + +void update_db() { + update_db_materiel(); + update_db_interfaces(); + update_db_arp(); + update_db_fdb(); +} + +void analyseMateriel(Materiel *mat) { + // Si on l'a déjà analysé, on zappe. + if(mat->isTreated()) + return; + + mat->setTreated(true); + + // Manageable ? + if(mat->snmpcheck()) { + mat->setManageable(MAT_IS_MANAGEABLE); + mat->setDBstatus(DBSTATUS_UPDATED); + + // Ouverture de connexion + mat->snmpopen(); + + mat->retrieveInfos(); + apply_actions(mat); + get_interfaces_infos(mat); + get_arp_infos(mat); + get_fdb_infos(mat); + look_for_neighbours(mat); + + // Fermeture de la connexion + mat->snmpclose(); + } + else if(mat->isManageable() != MAT_NOT_MANAGEABLE) + mat->setManageable(MAT_WAS_MANAGEABLE); + else + mat->setManageable(MAT_NOT_MANAGEABLE); +} + +// Thread d'analyse du réseau +void* camembert_thread(void*) { + Materiel *m; + + while(true) { + m = (Materiel *)monitor->getJob(); + + pthread_mutex_lock(&mtx_jobs); + ++jobsrunning; + pthread_mutex_unlock(&mtx_jobs); + + if (!m->isTreated()) + analyseMateriel(m); + + pthread_mutex_lock(&mtx_jobs); + if (!(--jobsrunning) && !monitor->isJobPending()) + pthread_cond_signal(&allConsumed); + + pthread_mutex_unlock(&mtx_jobs); + } + + return NULL; +} + +// Attend que le monitor soit vide, donc qu'il n'y ait plus de matériel à analyser +void wait_end_of_jobs() { +#if THREADED + pthread_mutex_lock(&mtx_activity); + pthread_cond_wait(&allConsumed, &mtx_activity); + pthread_mutex_unlock(&mtx_activity); +#endif +} + +// Crée les threads d'analyse du réseau. +void create_threads() { +#if THREADED + pthread_t p; + + for(unsigned int i=0; iisJobPending()) { + m = (Materiel *)monitor->getJob(); + if(!m->isTreated()) + analyseMateriel(m); + } +#endif +} + +// Parcourt la liste du matériel à la recherche du matériel qui n'a pas été analysé +// Et l'analyse le cas échéant (peu probable que ça se produise anyway vu comment mon appli est construite) +void check_untreated() { + unsigned int untreated = 0; + Materiel *m; + + for(MaterielList *lst=lstMateriel; lst!=NULL; lst=lst->getNext()) { + m = lst->getCurrentMateriel(); + if(!m->isTreated()) { + printf("%s not treated\n", m->getHostName()); + monitor->addJob((void*)m); + untreated++; + } + } + + if(untreated) + wait_end_of_jobs(); +} + +// Programme principal +int main(int argc, char* argv[]) { + init_globals(); + read_db(); + check_materiel(); + create_threads(); + wait_end_of_jobs(); + check_untreated(); + update_db(); + terminate(0); +} diff --git a/backend/camembert/main.o b/backend/camembert/main.o new file mode 100644 index 0000000000000000000000000000000000000000..95a7603606cb4d6325e2e4f7ff8c05fb9e038123 GIT binary patch literal 18688 zcmbtc3wV^(nLd+DOw?$iVnt2YX$==GrVIv13MeEQE|P=@30?pvlVk#;$xJ$z5WE#T z8DXBkQFbdWyIWgp*{Xfqt#!QwXf-x0DcxQub+>e-K52{FQt+v^ETz?&ecyBW=bxWT zyU)(^Ouqj;-#Op;&UZWKKZAZM(74#+@d)$rh(eJ=LcD&x;#5mAU6hGZ(^b;8>|5#M zlgGT3M8AH2Vq#*LPuYe_ud~tlynl2kIaLgOa~eBh{HpZvN!GfO&G%J{WO;h;q&4eC zhx!eFUwySFTTv+`XGT(!)A!YQt{nNpf%JGmw!TL9hPd4Y5H?m{L*0k!E3&PX-mF&* zGsFMSbX|#*f0V1*HuS<&hrY+EG z)u?;y3pAIMZ3KMqo#d0w0mmN8p3K?vw$-clP-Ob}eA%g7`6as+CJBryK@F89R$$!7 zv)ab9la#R}J2R3E94;IDxJL-9?eHtb5N6eRcT2i(el~CfW5+~YV9Bzvh2G|Q7YM8A zNM?U>0B*C|j#&4WSi$L;HyQ-F*M|zYb&{?!fHF@*_U}EwcCOU3%ykHNNOniJF97Yмim$d57VG&g7Dci6 zmPk{3_L&*VP5baThHxe<$@7jq*X5Lyy&o29tt=Vxo@b?;+?=_rz+p=$b7g+?!NdeC zx#ubjRrAo|QUx`=)8b9zFxZ6-!>9MoUvpP*-RQB4wmCn@5BT$;gH#+KY?fRpO*YbB%cGv3Z(byl=mciEb|-~%Eu+y%nJaU z2TDa*<`;4l@mSzMb6aEMzQF5cREnV@ zV}W5M=9|JQNS`V!8+;GE^r?cf!9O64Ejh@#vmx_F+29u;cjzVj5ku0uR=wBi^I1~8 zvX=+$&rBpgX0;5|&MF&x7oGeg$1dB(e6eovroZj2eYb4zztFs}?B$W}+EekPU#6s2 zPZgS3pgZGll2w>ZR+d^X(geVaM>?NrA`P7j4UaUFwWb8xWumAImb9JC9!OE+ z3T5vx5om6h*NtRBs%)qIO}bgqb25aK4>R+@O@E7hMDpVN&JXQn6v!yC^IaKh*ArL~ zRxrsbu(q2u$hBe#rC3~J-eL|I|ER?rt->neWn^br+9X)sj9B_ zF%YA=II5wt9W4*5mbG|fnxpl0Jbqd|w&HQKdTc3sx$Z%58$s8CF16~CAPrVsAIL(h zt_NhkRks18#;WTAskZ9cL1tQYjUY2Ha5d<3&{oh&tFDCxX|1f#MtEFX*QWy=@nhAM zpz9-}>&Y!oDYoZ+^uTUMgIKrv zs3dnt?lay>hK9=5w((tu(J{B(P2vE2;Q*hh1< z8(^Ea&d==6HXSL;6ryHG&G#(l5UOwntD!uX7z>&mPM350{D=K5eS9r9w{m@i#I`O) ztBrN=6EHXIoc?w(P7N-9;v|rN5@|bZ-RrfMRLFhh2N)^$mDvd2vA{TYH|ZCn6J{7q zU3%=gTs>Xm#mVK$^gK`h3DZg2SK8LJabPm_yysY%T8_2G>pzH*L90;Cyc09Yncu_1 z($x6iLDetypVngda$7bv&cn~TanGT= zT_tcNC+@IyY35+I?QmJ`Y`{WXzbrDn)TcJf@oSHS++e6t) zZLeER2g+VL2nEY|Sb>fd)QDZPv2xO`=~NfGLswQF9oU|IN8iT8Y9C&~)l&Fb#Fp&7 zU9DN?0mL%?WUxAo0HHg$n%ibrE2di;XIdYo)-)pooA{LKqSpH0vgr_0vAYGQYk*3r zGRxbN8i+2pmN3IhttCD^8J1<2qM^QGnEB`-6yYs^@cgT zgB_8-GE#+09l2YZL{rZ&z=NxlB zoR3IH3LSr!)7J^iB~W} z?74u(*8bfs0jh;_Q^$&MYCL3%%?UEYAaE`EI06L^so*ZWlFXzMjYw)KL7rl9UV5|Se#as`6$=SSoW|@)X<-4R51POGu zlYc@=RB~%o&~15&$D6!>d}uENR#hQ&y7y)%9>z2?s%6#>WZ%n<9lON5e_I*a#2Ogz z(?rl;dYZ1&0)slS z450iL%AHLSx}Op)uO6hH@+rNye9c|!M$zR3M22j~Gj{F?wU*N?aO}8ie}z4GKjT#9 zuX)ak6;-OSIhNZ7uTTezvvbn&9;kbptbdcGw(^e^C^7bz`-|ddQ}0`;lI*%ua$dxy zPo|&ty)=cj?v#~!KYM)Ls2qnylsN4Vp*k;>w{qRj5GIcI(Ki3V>|656YnA_v^zllj z!qB=C2tB!>n+4-+FjAJ`g$mN1UW4~oKpSN@uj#fM9dMmcmMQv+Jz166^9>3+r%b#? z$e!Q$i#>5>ls&in#h%#c&@&Y}_srOg*lV7DzkfuoTCSJtMh2KQqr(O1y_LN6!+s_& znfRdZi=;#M99MNMuh8UT0=}f(WCP+s-j_6@1?}rhyV{ye=&*Woe=Hj;hjox$L+9QP zxvjy`&|H>W|H(a*5IsiPX~mM|5XC8f@5o2_TNE@cg{E0q+~hiPJB>BOisyd=_eFPg zZ&L+N>EbNAkiZh9wt1B)+qvV*8o1ABD(ikdb#rb{wX)Q1+^J>1VS4M$ftwlprFUhW zQ?kfH4Of|k8VyuK?dUK}xtwTpILTI=xTHVfdg`Fml>5c2bf7$G(3sN5r_ZT|nG*p$ z2H_>xGEURZYy#W>sG->e>T8CX4V>J$^U=L_AA5dTl}+<5=0>)@R1c%j(`G$gzun9e znds;2Jj_J-OG-H_nscV1`UY?k)iNKpl*+Bz zX^@BZGMCg=-S4>A)HXe~3qH@4Qii@AZfb)%X7SJ?j{@4`GBAx#swPy;YZNKtT4wZ^kOkk>||y6E3ybAvE|*5PNS!l0!Fe- z&oe~ww9_J44QIS*2OnLAa}DM^cQAejl~w0}d8b2*>!Rb>adY0JeU7{%uP9XF46UuK zkSmgZ&bj}oP5%PEaqY)@6imUN6iQjNn5jk$d($6Bp7{^T!-SuOTb&?iE3L5nk&B!D zOR}f`}FdTMMqU%=%7PSU^t&8d!1HSIAj^LCo z-}FdVB$^DzyF;B}AD`BT<1>8yJ)vk0^@ciq_=dW=z)EiFvpZfV#Ioj9ft9VkWzDTC zjL6lC8ruS^rcddb;p2DOl*BYXU5DTH@EdJQ19iaczEy!%Usos@?g=H53!utvO9Lwd zc?u~-W^i&f@pYKUH+@y0F;L&?yWY2W<%%XfE2X=1!346OV}*GFje>GT4pD!dBF!tf zYFnAJO_C>EnIKQkobg>|bC@mx^YJT<*#%=xo5H_F|1(k5<1GCkiyWzq_D4<)dL_~w z4fTc>D9=T@yTXaic%&~GiA5Jskw`Z~A)H8Pl;{hGyWl~^>5W7a$xt$tz<8=DM5H^` z7mnvOM!IPjJ%-U{9%GTtumeX!$wM;ZRVsScH5%Hmwzf=w7{{0mPdYE9GKnXDS{yt-4=-B-R7%Xp`Jv#&RMqQ$-p__e~@t zo%d{#GwbMb#0$pD)G)cx6N%oVk^{q>o;W>O@}>9|H8=PoU0sP}FVZR=3HK<`&b*Mq zU7=<&)MRO1oe0JIWSzk3b44K1hw2bcy7FIl)zXzz{$-%)I764tQ0IoQJNak$x?`zm zmo%oyKbiHBzPy~*Z7TECd@8A&YjjK^3xFQv%owMD)8=!fwtG&w0M6>1(^%Z+u&vXs zrVi$}sj8@LV~b!4Zipq4jwI|2MMLYuGJF|riD++MKT^s>pG+o-7BhSp$kb93b%y#v z9g&_$G7?6%V&s`($V_Yk(YY>oI?tb!p%REsdHSO(HSY~YqB_UUFj=2eOh>9~VpEHp za=NFPggNs_>cZ?fQ>~A5nF&~#qN(1Fa9lawE_b;m7^Q5FJTXqTaFriww^+Kk9i4VP9t~ z8Vz?M!+oPq_vpq;ZRqi%7^{= zI)~l*v|;*eRgCTAihV0qHUw7s>h6&4^)&=m)#qYK!kkT-7Tu0hg&BUG5^NxK)3#In zY**-xHWp4Zsng0lAOG#c1)^c)ik7@%jF7SK9H45$83rhZ(LtXfZ0-7Z%;63urX+mP zSkjk-xsu_o3xt2}tSbN9s#(=lelaoOx#Tk5fBf82wC6mrt>9ZY_@eQOk}HLnS~TGi zH8|fcFM8V}4tuVZtkP?y2+>s3A!6X5>#WOO#Q5)finbSv-+QhQ;_nNJzELc`UT~KX z&lVQFR4jf{_%sgQTZ@bK6pLqz)u?M5KyND=ED)V|*y1U|znS?*&mtk-@)UipKtKbS zcS(KyCw$Wz!W|JD;;O5vt7hL&bDeq+Vlo|okLTW&mBMp>2~XETTwUt*ZuOAoBg98u z2QRv~q%Ko%5vG#k=7TQ8_i22E;VnrQa@_6szJu@Q@wIu2(}jGs>toAj0ZQjy! z(Zk*?lK&W*+p*R~oXE&+$?;!6^K*C;OFPFm<@m3IzYwu}1z%Lmc+ID7C-7~@50h7e z+I&1xH-2D?MrjP(4DzF_p=?nJycuJgAe(PD_IRzI4^2z)J%Hbv`A;9ceET&z1k&Dz zF^)P6fH!`L5PUng`OB5<9s{ra($n?xq3ce3H-nc1_0Gf}BYWAyg^g*il>IvGa+$I* z`3>~h(sZE@%}4OvHc5zYQ~!kkX1vy)y78ZhwBz9`c((Zs%6ButD@RoSlatq|Z0|!` z1Lht9e=?}I-!Xpmmcoa<>7r^hB_U({Dy#$4mcOjLu(<8~cdk1B#7E1|Q%|QpT3)Q4 z-v6kmN^I&)hB`o#aYZ+nr+B!hDj6O~iYja{AyL(lNQkOFRIpeb_T-( zo#DP@5W;%GqN+32+Z&ET2C_U{wWPW21{?)D@4-k&4{r!|$8iLPzJKNC8uX(ZR6eOZ zX_vp%C;eXS@^9AE^jk4ih=0(1NZ5zpW=-i*evYEwM9@f$IR=TldKsxAg1B$i}8FRzLZBFb57$6Jc?2zz-Nm2q&`NT!G=29 zz2e;o(@vWIuc(QgGZ33qt5Jo5g*5jmw4+kB6$*z!Xe!LJ6frH7`xGL=ObhpgOW^@u zjxE>CcKVq0P7TrL+72k#UK8-Ku`zomUkpFE1K#AXU z;Xk_Y6%GTl|BWuZ+J)mT%zy4`J{;;V0n<}l|0!UU zWgFW8jW)`^+!52nvs`#Fu-Q{N-yJS~5cmrY`RiT$m}8 zy6}rGJnF*ZE_|LN1-YIo7oH7l&Ir_ZiHqOn!X3c;8IR43m-R0GMquWe$+wJ)zr%&U z23+Eh_k!k=Q2uLp;IqL$B6JL`i#W>KXLK@+lAkD;r{_P=O~W95VhZYRSQ)7!;;k=1T8aD8pS z=A1`<#KqqNY|eP(3tj_Mb@ug%;GOA-xF1;bsaMrtZwhC{ z9D>1fb!a%{2{misAPs z!pVj@by*m=+dZy^<8a3r)x(T@|Bdl*68CWZ;bqb8SVD6BH-`H9dN#>*Vl8>f%$m7A zoDAA;zzIBJSY8+ZjqJjg*t7BYs(c1sZBHzAPp~@{4@Se0^&2{3sd&ERGgzW0mIy1Q z=XByU%=;6ZpXCigOTY9MohkSyo{B~z(e_d}Ww% zPjzc}gF}0`mN7Hfa%(i1>gx$75oJNU!Y>NYILKF;o4>9r=NO% z;O-r&jslJ4i7z{-d8{rBC;^9xG!nn~F$p~!Um%>wc08-!ue|J0_ z4k9UH@lCnlRWsqDEg6sHN|>C?G(u&6p5Yp}8plF3uEhl(ZlVxs5zaTOA(DWcoGJW% z*wffMuSU4!=XDI6t0zpD$M#ou%Ihzk^YDkAq_Q0W)fJA%MYo-W{y8Q%jThw7bQ(y% zVWmhePNl*~^JX(TS^_QL$=I~xhJJ04ZpVNY-JABZd&ug%6!+fEC{jkO&7F(fYsHQD zX^dJhR;)TB%Rd{uddJ-@NiYIR|TE<&&X3S6c>!Gc>Tw_MAd? zUeMLE7=IHn#`DgJ%hAU`L~f`TsJ8DWi@_;6z8wclA<5qjmUpapC-iJx;>`bx9RpB8h=Ui zU(@Yl8Xq9V+J3IvkCI~E4>Z3JmmuU{2FiJ^CIx>Q>GeX~N{V&d0ZKjh5F?q!H9bx` z6Zg>?-;9eRu44r#^=~JJ+($?u_c_w*h4?q}vCf|oqyGoQ=wFT(ME0LaiuQU?wl|WZ ze^~SH(>P1)7vc$G^gE#WM>IaJ`4?TJ_#XnL{3*ncw^ZZxq*X%nkbVq#Mmj@?M?op~ zF=FWdBq=V2-XWbML=kegU@q)M3S6u464DwWI!J4U=mn*mG%@D?TT;mT5-H>jYyJrF zCxkdmihjQ#MZXiI==TxiJm;CCX%i{t?5cFs=|bcsDc1c5(nUi2 z8ProJ#QCUYY_A}#7h)=?#|J-?V*Yug4Y*$+KY;irMtxkV@j7DIyhGy{F?3E6FUD^W z#7pp>uaGXqdoAfQA-0o$3+{V~;qRwOVV8eq`*I<6kv0l3N{aRTJ1N%l8&K-=TjFLR zP7*_(_cUfrrar}>pJf;jK^j5wV(yJ z3sJ4{EgIjYaZ=;08h=sa=QaM0#)mcjoyOc#DSt9336%9|Co$spc~a!TDYj$0xEvU9Sx$<$oJxv#uO>zRI?~y&D=F-A z7wK9d21zmQbGrR&r0awjA-zk8L!^lR6Qt;O;fFvW|4LHGnGQ<5{G{;n0=5T**g!f5 zaY@=P#NTQB3@OHcU*jVhmt3LxPa+NBH%g5ck#->7HI9?cMVym%LM|z6@Oet)mRrO z?Y@Z^_I;Fe4$d8<+F-oke1y@M3< ze3cY-f0lfV8zzR`M@V7!?`nKddhsjs5c)bMZNKZvOdirMcgiC`<*zKlcIkMDf+J^MgJoL6mx8Z zj+BcsD?Fu$8@3my-+M^cYCNFn4o!zOJ*4R=O|RiO9dfEcIer0gG5$@0#vzUQ9zgyM kO<&OTTbh4J;}e?xS+`Hd7>=vfv{ln}pqz*QKLhgr7j5UP#{d8T literal 0 HcmV?d00001 diff --git a/backend/camembert/materiel.cpp b/backend/camembert/materiel.cpp new file mode 100644 index 0000000..4f9e9f5 --- /dev/null +++ b/backend/camembert/materiel.cpp @@ -0,0 +1,260 @@ +/* ========================================= + +Camembert Project +Alban FERON, 2007 + +Représentation d'un équipement matériel et + liste de matériel. + +========================================== */ + +#include "materiel.h" + +Oid OID_Hostname("1.3.6.1.2.1.1.5.0"); +Oid OID_OSType ("1.3.6.1.2.1.1.1.0"); + +// --------------------------- +// Materiel::Constructeurs +// --------------------------- + +Materiel::Materiel(unsigned int id, SNMP *snmp, const char* community, IP* ip): + SNMPObject(snmp, community, ip) +{ + _id = id; + _type = NULL; + _name = NULL; + _osType = NULL; + _capabilities = 0; + bTreated = false; + _dbStatus = DBSTATUS_UNKNOWN; + _manageable = 0; + _ifs = NULL; + _arp = NULL; +} + +Materiel::Materiel(unsigned int id, SNMP *snmp, const char* community, IPList* lip): + SNMPObject(snmp, community, lip) +{ + _id = id; + _type = NULL; + _name = NULL; + _osType = NULL; + _capabilities = 0; + bTreated = false; + _dbStatus = DBSTATUS_UNKNOWN; + _manageable = 0; + _ifs = NULL; + _arp = NULL; +} + +// ---------------------------- +// Materiel::Destructeur +// ---------------------------- + +Materiel::~Materiel() { + if(_name) + delete[] _name; + if(_type) + delete[] _type; + if(_osType) + delete[] _osType; + if(_ifs) + delete _ifs; + if(_arp) + delete _arp; +} + +// -------------------------------- +// Materiel::Setters +// -------------------------------- + +void Materiel::setHostName(const char* name) { + // Si il a déjà un nom, on le vire + if(_name) + delete[] _name; + // On affecte le nom + _name = new char[strlen(name)+1]; + strcpy(_name, name); +} + +void Materiel::setType(const char* type) { + if(_type) + delete[] _type; + _type = new char[strlen(type)+1]; + strcpy(_type, type); +} + +void Materiel::setOSType(const char* os) { + if(_osType) + delete[] _osType; + _osType = new char[strlen(os)+1]; + strcpy(_osType, os); + + char *c; + // Remplace les apostrophes par des espaces sinon ça va bugger lors des modifs de la BDD + while(c = strchr(_osType, '\'')) + *c = ' '; +} + +void Materiel::setCapabilities(unsigned int capabilities) { + _capabilities = capabilities; + + // Si c'est un téléphone, on dit qu'il a été traité car + // de toutes façons il est pas manageable + if(capabilities & CAPABILITY_PHONE) { + status = STATUS_NOTMANAGEABLE; + _manageable = MAT_NOT_MANAGEABLE; + setTreated(true); + } +} + +void Materiel::addInterface(Interface *i, unsigned int dbStatus) { + if(!_ifs) + _ifs = new InterfaceList(i, dbStatus); + else + _ifs->addInterface(i, dbStatus); +} + +Interface *Materiel::getInterfaceById(unsigned int id) const { + if(!_ifs) + return NULL; + return _ifs->getInterfaceById(id); +} + +Interface *Materiel::getInterfaceByDot1d(unsigned int id) const { + if(!_ifs) + return NULL; + return _ifs->getInterfaceByDot1d(id); +} + +Interface *Materiel::getInterfaceByPort(unsigned int module, unsigned int port) const { + if(!_ifs) + return NULL; + return _ifs->getInterfaceByPort(module, port); +} + +Interface *Materiel::getInterfaceByPortDot1d(unsigned int id) const { + if(!_ifs) + return NULL; + return _ifs->getInterfaceByPortDot1d(id); +} + +/*Interface *Materiel::getInterafceByDBId(unsigned int dbId) const { + if(!_ifs) + return NULL; + return _ifs->getInterfaceByDBId(dbId); +} */ + +void Materiel::retrieveInfos() { + const SNMPResult *r; + + // Si il a pas de nom, on va le récupérer + if(!_name) { + r = this->snmpget(&OID_Hostname); + if(r) { + this->setHostName(r->get_printable_value()); + delete r; + } + } +} + +void Materiel::addARPEntry(const char *mac, const char *ip) { + if(!_arp) + _arp = new ARPCache(mac, ip); + else + _arp->addEntry(mac, ip); +} + +// ----------------------------- +// MaterielList::Constructeurs +// ----------------------------- + +MaterielList::MaterielList(Materiel *const mat) { + _mat = mat; + _next = NULL; +} + +MaterielList::MaterielList(Materiel *const mat, MaterielList *next) { + _mat = mat; + _next = next; +} + +// ------------------------------ +// MaterielList::Destructeur +// ------------------------------ + +MaterielList::~MaterielList() { + if(_mat) + delete _mat; + if(_next) + delete _next; +} + +MaterielList *MaterielList::addMateriel(Materiel *const mat) { + return new MaterielList(mat, this); +} + +Materiel *MaterielList::getMaterielById(unsigned int id) const { + const MaterielList *l; + Materiel *m; + + for(l=this; l!=NULL; l=l->getNext()) { + m = l->getCurrentMateriel(); + if(m->getID() == id) + return m; + } + + return NULL; +} + +Materiel *MaterielList::getMaterielByHostname(const char *hostname, const char *type, const char *ostype) const { + const MaterielList *l; + Materiel *m; + char *c; + + // Parcourt la liste du matos + for(l=this; l!=NULL; l=l->getNext()) { + m = l->getCurrentMateriel(); + // Si il a le même nom... + if(!strcmp(m->getHostName(), hostname)) { + // Si le nom commence par SEP, on retourne le matériel, pas besoin de faire d'autres vérifications + // vu que pour les SEP, le nom est unique. + if((c = strstr((char *)m->getHostName(), "SEP")) && (c - m->getHostName() == 0)) + return m; + // Sinon on vérifie le type et l'OS (s'ils sont définis) + if((type[0] == 0 || !m->getType() || !strcmp(m->getType(), type)) && + (ostype[0] == 0 || !m->getOSType() || !strcmp(m->getOSType(), ostype))) + return m; + } + } + return NULL; +} + +Materiel *MaterielList::getMaterielByIP(const IP* ip, const char *type, const char *ostype) const { + const MaterielList *l; + Materiel *m; + + // Parcourt la liste du matos + for(l=this; l!=NULL; l=l->getNext()) { + m = l->getCurrentMateriel(); + // Si le matos a l'IP passée et que le type et l'os correspondent, on retourne le matos. + if(m->getIPList()->isIPinList(ip) && + (type[0] == 0 || !m->getType() || !strcmp(m->getType(), type)) && + (ostype[0] == 0 || !m->getOSType() || !strcmp(m->getOSType(), ostype))) + return m; + } + + return NULL; +} + +Materiel *MaterielList::getMaterielByHostnameAndIP(const char *hostname, const IP *ip) const { + const MaterielList *l; + Materiel *m; + + for(l=this; l!=NULL; l=l->getNext()) { + m = l->getCurrentMateriel(); + if(!strcmp(m->getHostName(), hostname) && m->getIPList()->isIPinList(ip)) + return m; + } + return NULL; +} diff --git a/backend/camembert/materiel.h b/backend/camembert/materiel.h new file mode 100644 index 0000000..00c3cf4 --- /dev/null +++ b/backend/camembert/materiel.h @@ -0,0 +1,246 @@ +/* ========================================= + +Camembert Project +Alban FERON, 2007 + +Représentation d'un équipement matériel et + liste de matériel. + +========================================== */ + +#ifndef __CAMEMBERT_MATERIEL_H +#define __CAMEMBERT_MATERIEL_H + +#include "snmpobject.h" +#include "pgdb.h" +#include "arp.h" +#include "interface.h" + +/* See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#18843 + for more informations */ +#define CAPABILITY_LVL3_ROUTING 0x01 +#define CAPABILITY_LVL2_TRANSPARENT_BRIDGE 0x02 +#define CAPABILITY_LVL2_SRC_ROUTE_BRIDGING 0x04 +#define CAPABILITY_LVL2_SWITCHING 0x08 +#define CAPABILITY_SEND_RECIEVE_PACKETS 0x10 +#define CAPABILITY_IGMP_NONFORWARDING 0x20 +#define CAPABILITY_LVL1_FUNCTIONALITY 0x40 +#define CAPABILITY_PHONE 0x80 + +#define MAT_NOT_MANAGEABLE 0 +#define MAT_IS_MANAGEABLE 1 +#define MAT_WAS_MANAGEABLE 2 + +/** + * Représentation d'un équipement matériel + * Permet d'avoir accès au nom, type et capabilities du matériel + */ +class Materiel: public SNMPObject, public DBObject { + private: + unsigned int _id; + char *_name; /* Hostname du matos */ + char *_type; /* Type */ + char *_osType; + unsigned int _capabilities; /* Capabilities récupérées via CDP */ + bool bTreated; /* Si on a déjà fait l'analyse à partir de ce materiel */ + unsigned int _manageable; + InterfaceList *_ifs; + ARPCache *_arp; + + public: + /** + * Constructeur + * + * @param id - ID du matos dans la base de données + * @param snmp - Pointeur vers une instance de class SNMP pour effectuer les requêtes + * @param community - Communauté SNMP à utiliser lors des connexions + * @param ip - IP principale du matériel + */ + Materiel(unsigned int id, SNMP *snmp, const char* community, IP* ip); + + /** + * Constructeur + * + * @param id - ID du matos dans la base de données + * @param snmp - Pointeur vers une instance de class SNMP pour effectuer les requêtes + * @param community - Communauté SNMP à utiliser lors des connexions + * @param lip - Pointeur sur une liste d'IP du matériel + */ + Materiel(unsigned int id, SNMP *snmp, const char* community, IPList* lip); + + /** + * Destructeur + * Fait les libérations de mémoire nécéssaires + */ + ~Materiel(); + + /** + * Récupère les infos de base (tel que le nom) si elles ne sont pas initialisées + */ + void retrieveInfos(); + + void setManageable(unsigned int manageable) { _manageable = manageable; } + unsigned int isManageable() const { return _manageable; } + void setVersion(int v) { version = v; } + + /** + * Change le type du matériel + */ + void setType(const char *type); + + /** + * Change le nom du matériel + */ + void setHostName(const char *name); + + /** + * Change la description de l'OS + */ + void setOSType(const char *os); + + /** + * Change les capabilities + * + * @param capabilities - Masque des capabilities à affecter + * @note Si les capabilities spécifient que c'est un téléphone, le matériel est déclaré + * non manageable + * @see http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#18843 + */ + void setCapabilities(const unsigned int capabilities); + + /** + * Définit si le matériel a été analysé ou non + */ + void setTreated(const bool treated) { bTreated = treated; } + + void addInterface(Interface *i, unsigned int dbStatus); + + unsigned int getID() const { return _id; } + + /** + * Récupère le nom + * + * @return Le nom du matériel + */ + const char *getHostName() const { return _name; } + + /** + * Récupère le type + * + * @return Le type du matériel + */ + const char *getType() const { return _type; } + + /** + * Récupère l'OS + */ + const char *getOSType() const { return _osType; } + + /** + * Récupère les capabilities + * + * @return Le masque de capabilities du matériel + * @see http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#18843 + */ + const unsigned int getCapabilities() const { return _capabilities; } + + /** + * @return VRAI si le matériel a été traité. + */ + const bool isTreated() const { return bTreated; } + + InterfaceList *getInterfaces() const { return _ifs; } + Interface *getInterfaceById(unsigned int id) const; + Interface *getInterfaceByDot1d(unsigned int id) const; + Interface *getInterfaceByPort(unsigned int module, unsigned int port) const; + Interface *getInterfaceByPortDot1d(unsigned int id) const; + //Interface *getInterfaceByDBId(unsigned int dbId) const; + + void addARPEntry(const char *mac, const char *ip); + ARPCache *getARPCache() const { return _arp; } +}; + +/** + * Liste de matériel + */ +class MaterielList { + private: + MaterielList *_next; /* Sous liste permettant d'acceder au matériels suivants */ + Materiel *_mat; /* Matériel courant */ + + public: + /** + * Crée une liste avec un unique matériel + * + * @param mat - Pointeur sur le matériel à mettre dans la liste + */ + MaterielList(Materiel *const mat); + + /** + * Crée une liste avec un ancienne liste et un matériel à lui ajouter + * + * @param mat - Materiel à ajouter + * @param next - Ancienne liste utilisée pour déterminer les matériels suivants + */ + MaterielList(Materiel *const mat, MaterielList *next); + + /** + * Libère la mémoire + * + * @note Détruit aussi le matériel pointé + */ + ~MaterielList(); + + /** + * Ajoute un nouveau matériel au début de la liste + * + * @param mat - Materiel à ajouter + * @return une nouvelle liste contenant le nouveau matériel et ceux présent + * dans la liste actuelle + * @note Ne modifie pas la liste actuelle + */ + MaterielList *addMateriel(Materiel *const mat); + + /** + * @return Sous liste pour avoir les matériels suivant + */ + MaterielList *getNext() const { return _next; } + + /** + * @return Matériel actuellement pointé par la liste + */ + Materiel *getCurrentMateriel() const { return _mat; } + + Materiel *getMaterielById(unsigned int id) const; + + /** + * Cherche un matériel dans la liste à partir d'un nom donné + * + * @param hostname - Nom du matériel à chercher + * @param type - + * @param ostype - + * @return Le matériel dont le nom correspond à 'hostname' + */ + Materiel *getMaterielByHostname(const char *hostname, const char *type, const char *ostype) const; + + /** + * Cherche un matériel dans la liste à partir d'une IP donnée + * + * @param ip - L'IP du matériel à chercher + * @param type - + * @param ostype - + * @return Le matériel ayant l'IP 'ip' + */ + Materiel *getMaterielByIP(const IP* ip, const char *type, const char *ostype) const; + + /** + * Cherche un materiel dans la liste à partir d'un nom et d'une IP donnés + * + * @param hostname - Nom du matériel à chercher + * @param ip - IP du matériel à chercher + * @return Le matériel ayant le nom 'hostname' et l'IP 'ip' + */ + Materiel *getMaterielByHostnameAndIP(const char *hostname, const IP *ip) const; +}; + +#endif /* __CAMEMBERT_MATERIEL_H */ diff --git a/backend/camembert/materiel.o b/backend/camembert/materiel.o new file mode 100644 index 0000000000000000000000000000000000000000..6b4812d0e97c51ead37c719039118fa99e586785 GIT binary patch literal 9236 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 M?2x!u;)@dh4g0NR5&!@I literal 0 HcmV?d00001 diff --git a/backend/camembert/monitor.cpp b/backend/camembert/monitor.cpp new file mode 100644 index 0000000..d2224ca --- /dev/null +++ b/backend/camembert/monitor.cpp @@ -0,0 +1,100 @@ +/* ================================================= + +Camembert Project +Alban FERON, 2007 + +Monitor class. +Entièrement repris de Kindmana, par Aurélien Méré +(avec quelques commentaires en plus) + +================================================== */ + +#include +#include "monitor.h" +#include +#include + +Monitor::Monitor(unsigned int const maxjobs) { + want_to_consume = 0; + want_to_produce = 0; + firstjob = 0; + nextjob =0; + this->maxjobs = maxjobs; + jobs = (void **)malloc(sizeof(void*)*maxjobs); + + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&can_produce, NULL); + pthread_cond_init(&can_consume, NULL); +} + +Monitor::~Monitor() { + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&can_produce); + pthread_cond_destroy(&can_consume); + free(jobs); +} + +void Monitor::addJob(void * const job) { + pthread_mutex_lock(&mutex); + + // Tant que le monitor est "plein" + while (((nextjob + 1) % maxjobs) == firstjob) { + // On dit qu'on veut ajouter un job + ++want_to_produce; + // Et on attend qu'une place se libère + pthread_cond_wait(&can_produce, &mutex); + --want_to_produce; + } + + // On ajoute le job + jobs[nextjob++] = job; + nextjob %= maxjobs; + + // Si un thread attend pour ajouter un job et qu'il y a de la place, + // on le signale pour qu'il se réveille. + if (want_to_produce && (((nextjob + 1) % maxjobs) != firstjob)) + pthread_cond_signal(&can_produce); + + // Si un thread attend pour "lire" un job, comme on vient d'en ajouter un + // on lui dit qu'il peut lire. + if (want_to_consume) + pthread_cond_signal(&can_consume); + + pthread_mutex_unlock(&mutex); +} + +void * const Monitor::getJob() { + void *res; + + pthread_mutex_lock(&mutex); + + // Tant que ya rien à lire + while (firstjob == nextjob) { + // On dit qu'on veut lire + ++want_to_consume; + // Et on attend qu'il y ait quelque chose à lire + pthread_cond_wait(&can_consume, &mutex); + --want_to_consume; + } + + // On prend le job pour le retourner + res = jobs[firstjob++]; + firstjob %= maxjobs; + + // Si un thread attend d'ajouter un job, comme on vient d'en "supprimer" un, + // on lui dit qu'il peut l'ajouter + if (want_to_produce) + pthread_cond_signal(&can_produce); + + // Si un thread veut lire et qu'il y a encore des trucs à lire, on lui dit. + if ((firstjob != nextjob) && want_to_consume) + pthread_cond_signal(&can_consume); + + pthread_mutex_unlock(&mutex); + return res; +} + +bool const Monitor::isJobPending() const { + return (firstjob != nextjob); +} + diff --git a/backend/camembert/monitor.h b/backend/camembert/monitor.h new file mode 100644 index 0000000..3f5b838 --- /dev/null +++ b/backend/camembert/monitor.h @@ -0,0 +1,35 @@ +/* ================================================= + +Camembert Project +Alban FERON, 2007 + +Monitor class. +Entièrement repris de Kindmana, par Aurélien Méré + +================================================== */ + +#ifndef __CAMEMBERT_MONITOR_H +#define __CAMEMBERT_MONITOR_H + +class Monitor +{ + private: + pthread_mutex_t mutex; + pthread_cond_t can_produce; + pthread_cond_t can_consume; + unsigned int want_to_consume; + unsigned int want_to_produce; + unsigned int firstjob; + unsigned int nextjob; + unsigned int maxjobs; + void **jobs; + + public: + Monitor(unsigned int const maxjobs); + ~Monitor(); + void addJob(void * const job); + void * const getJob(); + bool const isJobPending() const; +}; + +#endif /* __CAMEMBERT_MONITOR_H */ diff --git a/backend/camembert/monitor.o b/backend/camembert/monitor.o new file mode 100644 index 0000000000000000000000000000000000000000..ceb2632f8d44ffbe9846d175d808b6405c9d30ed GIT binary patch literal 2748 zcmbtVUrbw796p8Os$)1ODrV?-sB`Mpt)#jsCU%qx47LyfXJo7vdVwl!NqfPfM2VHT zo4YPVvSbfFEXKqaA57o@qlq;vL1LnYn5Z#XcwouQe9)LM9!&82?mefy1B@n~TG(w|H^ZJ@b9xeN!}PoOz57yO zVR|}0x1R>kzu1}kZMWI$F>1Xx{@U2sFmsboG~{SI8ZbMv`AmzkmjB{W@S@S^%o=CB zZX+O#JF~X^3EKf`{TFlQ0!+=e2{Z6c(7$|Z_B`0BC1Yk1I2ve}VQ77a8Olx-M(VV%n-fzThTYr4`9_zLORt^I7BbnXH64;-lSZ#6clBV;*+}oNJOk(Xpu1|v@i@Xb z{OyvAFrrlV$QW`y|IgpQWL){lygi=iwwVlgy9P=oX@8`hwV zF5DM+x%>sA$?HZBxrJiyi{VPkjUy-(FYo8p{F6*!jrm{l+u4z^*2|>Udn?`U^DgS@ zEMABQh{vlujZeGV$>T5|4l<+lC|#4Dg5K}CIguHu~gy>iCpkPG+- zG&uFmIZJn$va;=Ub5I4L%Syi{*3@eIeGge-8=dfF#uIw952&YY98$ZqHtf^2tS)@9 zs2&x*zD!2=`csL7mV_*s(luZ6sqRA=J=*`8@X=ZuiVUQq35^B(x3v#u{NCB~Mg7a2eE?S`F0tv#xXMb@7u)JZ{nN47aaZ~ zkxIt(RNB`+G9n@`pK3dL(iZsZXoQJm5fDRkH+<)#5NjDZZ*rq2a)Ku6wz3W zDLPP1DRGm3OU{^vZ&PZ#n8Zq}gac_!yUo9z-Id~n{NYzcB07wUiUI5@;}}&@WW}^3 zPTzX?9_EqZ!SMTChQG9riMWVg603yxPU0F7c875$w0oZj{YMhNCF0@!Oxz>Hs>CXs zALS1cA@@qm67Lt{0udMf3b96rbt$jJbE3SC2>C4Ip{`rRdxbbkJK&u33rIVp5H37; WFYARaDDf4ElM+9WI4|)BiGKqdFnext) + if(key[0] == n->key[0] && key[1] == n->key[1]) + return nl; + + // Création du nouveau voisin + n = new neighbourList; + n->key[0] = key[0]; + n->key[1] = key[1]; + strcpy(n->name, name); + n->type[0] = 0; + n->os[0] = 0; + n->ifName[0] = 0; + n->capa = 0; + n->lip = new IPList(); + n->next = nl; + return n; +} + +void addNeighbourType(neighbourList *nl, const char *type, int key[]) { + neighbourList *n; + + // Cherche le voisin correspondant à la clé + for(n=nl; n!=NULL; n=n->next) { + if(key[0] == n->key[0] && key[1] == n->key[1]) { + // Change son type + strcpy(n->type, type); + return; + } + } +} + +void addNeighbourOS(neighbourList *nl, const char *os, int key[]) { + neighbourList *n; + + // Cherche le voisin correspondant à la clé + for(n=nl; n!=NULL; n=n->next) { + if(key[0] == n->key[0] && key[1] == n->key[1]) { + // Change son type + strcpy(n->os, os); + return; + } + } +} + +void addNeighbourIfName(neighbourList *nl, const char *ifname, int key[]) { + neighbourList *n; + + // Cherche le voisin correspondant à la clé + for(n=nl; n!=NULL; n=n->next) { + if(key[0] == n->key[0] && key[1] == n->key[1]) { + // Change son type + strcpy(n->ifName, ifname); + return; + } + } +} + +void addNeighbourIP(neighbourList *nl, unsigned int ipnum, int key[]) { + neighbourList *n; + IP *ip; + + // Cherche le voisin correspondant à la clé + for(n=nl; n!=NULL; n=n->next) { + if(key[0] == n->key[0] && key[1] == n->key[1]) { + ip = new IP(ipnum); + // Si l'IP trouvé n'est pas dans la liste, on l'ajoute + if(!n->lip->isIPinList(ip)) { + n->lip->addIP(ip, DBSTATUS_NEW); + } + else + delete ip; + return; + } + } +} + +void addNeighbourCapability(neighbourList *nl, unsigned int capa, int key[]) { + neighbourList *n; + + // Cherche le voisin correspondant à la clé + for(n=nl; n!=NULL; n=n->next) { + if(key[0] == n->key[0] && key[1] == n->key[1]) { + // Met à jour les capabilities + n->capa = capa; + return; + } + } +} + +// C'est censé libérer la mémoire, mais en fait ça fait des erreurs +void clearNeighbours(neighbourList *nl) { + neighbourList *next; + while(nl) { + next = nl->next; + delete nl; + nl = next; + } +} + +bool look_for_neighbours(Materiel *m) { + neighbourList *nl = NULL; + const SNMPResult *r; + int key[2]; + unsigned char buffer[16]; + unsigned int num; + unsigned long int size; + + SNMPResults res(m->multiplesnmpwalk(6, 0, + &OID_NeighboursCapab, + &OID_NeighboursIfName, + &OID_NeighboursOS, + &OID_NeighboursTypes, + &OID_NeighboursIPs, + &OID_NeighboursNames)); + // Recherche des voisins + while(r = res.getNext()) { + const Oid &o = r->get_oid(); + key[0] = o[14]; + key[1] = o[15]; + + switch (o[13]) { + case 6: + nl = addNeighbour(nl, r->get_printable_value(), key); + break; + + case 4: + r->get_value(buffer, size); + if(buffer[0] != 0) { + num = buffer[0]*16777216 + buffer[1]*65536 + buffer[2]*256 + buffer[3]; + addNeighbourIP(nl, num, key); + } + break; + + case 8: + addNeighbourType(nl, r->get_printable_value(), key); + break; + + case 5: + addNeighbourOS(nl, r->get_printable_value(), key); + break; + + case 7: + addNeighbourIfName(nl, r->get_printable_value(), key); + break; + + case 9: + r->get_value(buffer, size); + num = buffer[0]*16777216 + buffer[1]*65536 + buffer[2]*256 + buffer[3]; + addNeighbourCapability(nl, num, key); + break; + } + } + + neighbourList *n; + Materiel *mat = NULL; + IPList *lip; + bool bNew = false; + Interface *i; + + // Pour chaque voisin trouvé... + for(n=nl; n!=NULL; n=n->next) { + + // On bloque l'acces au matériel pour éviter qu'un autre thread modifie en même + // temps la liste du matériel + pthread_mutex_lock(&mtx_materiels); + + // Commence par chercher si un materiel porte le même nom et a la même IP que le voisin + lip = n->lip; + while(lip!=NULL) { + mat = lstMateriel->getMaterielByHostnameAndIP(n->name, lip->getIP()); + if(mat) + lip = NULL; + else + lip = lip->getNext(); + } + // S'il y en a aucun, on cherche juste sur le nom (en vérifiant que le type et l'OS soient les mêmes) + if(!mat) + mat = lstMateriel->getMaterielByHostname(n->name, n->type, n->os); + // S'il y en a toujours aucun, on cherche sur les IPs (en vérifiant que le type et l'OS soient les mêmes) + if(!mat) { + lip = n->lip; + while(lip!=NULL) { + mat = lstMateriel->getMaterielByIP(lip->getIP(), n->type, n->os); + if(mat) + lip = NULL; + else + lip = lip->getNext(); + } + } + + if(!mat) { + // Si aucun ne correspond aux critères, on en crée un nouveau et on l'ajoute à la liste + mat = new Materiel(++maxIdMateriel, snmp, COMMUNITY, n->lip); + mat->setDBstatus(DBSTATUS_NEW); + lstMateriel = lstMateriel->addMateriel(mat); + bNew = true; + } + else { + // Sinon on parcourt la liste des IPs du voisin et on ajoute celles qui + // ne sont pas dans la liste d'IP du matériel correspondant + for(lip=n->lip; lip!=NULL; lip=lip->getNext()) { + if(!mat->getIPList()->isIPinList(lip->getIP())) { + mat->addIP(lip->getIP(), DBSTATUS_NEW); + } + else + mat->getIPList()->foundIP(lip->getIP()); + } + } + + i = m->getInterfaceById(n->key[0]); + /*if(i) { + i->distantDevice = mat; + i->setDistantIfName(n->ifName); + }*/ + if(i) + i->addDistantDevice(n->ifName, mat); + + if(!mat->getHostName() || strcmp(mat->getHostName(), n->name)) + mat->setHostName(n->name); + // Si il a pas de type on le met à jour + if(!mat->getType() || strcmp(mat->getType(), n->type)) + mat->setType(n->type); + if(!mat->getOSType() || strcmp(mat->getOSType(), n->os)) + mat->setOSType(n->os); + // et pareil pour les capabilities + if(!mat->getCapabilities() || n->capa != mat->getCapabilities()) + mat->setCapabilities(n->capa); + mat->setDBstatus(DBSTATUS_UPDATED); + + // On débloque l'acces au matériel + pthread_mutex_unlock(&mtx_materiels); + + if(!mat->isTreated()) + monitor->addJob(mat); + } + + //clearNeighbours(nl); + return bNew; +} diff --git a/backend/camembert/neighbours.h b/backend/camembert/neighbours.h new file mode 100644 index 0000000..f09ce4c --- /dev/null +++ b/backend/camembert/neighbours.h @@ -0,0 +1,25 @@ +/* ================================================= + +Camembert Project +Alban FERON, 2007 + +Fonctions pour la recherche des voisins (CDP) + +=================================================== */ + +#ifndef __CAMEMBERT_NEIGHBOURS_H +#define __CAMEMBERT_NEIGHBOURS_H + +#include "camembert.h" + +/** + * Cherche les voisins d'un materiel donné + * Les nouveaux voisins sont ajoutés à la liste globale du matériel + * et au monitor pour exu aussi être analysés. + * + * @param m - Materiel à partir duquel on doit chercher les voisins + * @return VRAI si au moins un nouveau voisin a été trouvé, FAUX sinon. + */ +bool look_for_neighbours(Materiel *m); + +#endif /* __CAMEMBERT_NEIGHBOURS_H */ diff --git a/backend/camembert/neighbours.o b/backend/camembert/neighbours.o new file mode 100644 index 0000000000000000000000000000000000000000..3262e3abb3dec79ea7117ee80a8d9060800fb4eb GIT binary patch literal 8424 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 +#include +#include +#include "pgdb.h" +#include + +pthread_mutex_t mtxDB; +PGconn *db; + +int ConnectDB() { +#ifdef DBPASSWORD + db = PQconnectdb("host="DBHOST" user="DBUSERNAME" dbname="DBNAME" password="DBPASSWORD); +#else + db = PQconnectdb("host="DBHOST" user="DBUSERNAME" dbname="DBNAME); +#endif + + pthread_mutex_init(&mtxDB, NULL); + + if (db == NULL) { + printf("Failed to connect to PostgreSQL database\n"); + exit(-1); + } + + return 1; +} + +void DisconnectDB() { + pthread_mutex_destroy(&mtxDB); + PQfinish(db); +} + +void *QueryDB(char *query) { + PGresult *r; + ExecStatusType e; + + pthread_mutex_lock(&mtxDB); + + if ((r = PQexec(db, query)) == NULL) { + printf("POSTGRES FATAL : Query [%s] returned NULL\n", query); + exit(-1); + } + + e = PQresultStatus(r); + if ((e != PGRES_TUPLES_OK) && (e != PGRES_COMMAND_OK)) { + printf("POSTGRES FATAL : Query [%s] failed returning error [%s]\n", query, PQresultErrorMessage(r)); + exit(-1); + } + + pthread_mutex_unlock(&mtxDB); + return ((void *)r); +} + +char *_PQcmdTuples(void* a) { return PQcmdTuples((PGresult *)a); } +void _PQclear(void *a) { PQclear((PGresult *)a); } +int _PQntuples(void *a) { return PQntuples((PGresult *)a); } +char *_PQgetvalue(void *a,int b, int c) { return PQgetvalue((PGresult *)a, b, c); } +void *_PQgetResult() { return (void *)PQgetResult(db); } +int _PQescapeString(char *to, const char *from) { return PQescapeStringConn(db, to, from, strlen(from), NULL); } + +/* ======================================================= */ + +void DBObject::setDBstatus(unsigned int status) { + if(status > _dbStatus) + _dbStatus = status; +} diff --git a/backend/camembert/pgdb.h b/backend/camembert/pgdb.h new file mode 100644 index 0000000..ed14262 --- /dev/null +++ b/backend/camembert/pgdb.h @@ -0,0 +1,69 @@ +/* ================================================= + +Camembert Project +Alban FERON, 2007 + +Connexion à la base de données +Entièrement repris de Kindmana, par Aurélien Méré + +================================================== */ + +#ifndef __PGDB_H +#define __PGDB_H + +#define DBSTATUS_UNKNOWN 0 +#define DBSTATUS_OLD 1 +#define DBSTATUS_UPDATED 2 +#define DBSTATUS_NEW 3 + +#define DBNAME "camembert" +#define DBUSERNAME "camembert" +#define DBHOST "127.0.0.1" +#define DBPASSWORD "CamembertDB@Pacat" + +/** + * Effectue une connexion à la base de données. + * @return NULL en cas de problème, 1 en cas de succès + */ +int ConnectDB(); + +/** + * Déconnecte l'objet de connexion à la base de + * données. Il est important d'appeler cette fonction + * avant toute terminaison du programme. + */ +void DisconnectDB(); + +/** + * Effectue une requête dans la base de données. Le + * programme se termine dans le cas où la requête + * n'est pas valide. + */ +void *QueryDB(char *query); + +char *_PQcmdTuples(void*); +void _PQclear(void *); +int _PQntuples(void *); +char *_PQgetvalue(void *,int, int); +void *_PQgetResult(); +int _PQescapeString(char *to, const char *from); + +/** + * Décrit un objet possèdant une entrée dans la base de données + * Permet de déterminer s'il doit etre crée, mis à jour, etc. + */ +class DBObject { + protected: + unsigned int _dbStatus; + + public: + + /** + * @return Le statut de l'objet par rapport à son homologue dans la base de données + */ + unsigned int getDBstatus() const { return _dbStatus; } + void setDBstatus(unsigned int status); +}; + +#endif /* __PGDB_H */ + diff --git a/backend/camembert/pgdb.o b/backend/camembert/pgdb.o new file mode 100644 index 0000000000000000000000000000000000000000..2901bc03c2903afe478cafdd1a9aecf6d8c61de9 GIT binary patch literal 3236 zcmb7GU1%It6h8Z-U2Rg*qEc*ar?{<^woA68G>sP9&88{1w%cqX#WZeaXKyxJ_s^NT z=|%;MOQlOF=D~*wQd){2BH9O)f`WkvHlRM#2Yv7dM4@6|^g;SijNh4=yE_@G=nW^| z{myspz2}~}b7x;3N*xLW0;F1iLeybI3t>m;vtc`R)4eLx#82v95hRR>Yt@H9CAsP~}D19_D zIy^Fz(GDFPJ($u4v}1K)&T8XZtqIK(vTjyHUOO_DO5teyTkMoxEFsbvQmhm-VVYIb zp2t2^(^X6EkMG_a>&0JOt6Rd{Z|G%F&Iwa$`CJ8kkE5nr)~TwQ-#@5gl8FOp-Ower zamZOqRyCtqsR%=A=%%qi!4#QeDJ`!{J*QiOXn1gNK--=axuRat`eJ>tJ-hlJbvl@5 zJ&ar6h4csoUW(jpvkhJ0@Yw)sn$3yUG9FwLNt_9G--Rw~kHa+B0IaPIH$$u)hfTun zhqX0yS>yaq!@6L8Z5}SRJ8AF2fAJr*Yw%yWgSHXApJ5BIH(~AdGOvc3c=tJEh zY>QV%pEV8sdDw_gqnI^YMu~yabZ}brOi_wS(U275r$DisWl_wmGU4cWtdC+wwOkez zgjA|h#Dau!}IYo;r@#G;P_fi0?pF}>ol)FOusL86rRtkGWV{) zT12cM>0=c-#^)nZQ*h>(BWHfo^1MGSbACGHs-vGzfQNNmMAGqm}9)zHmHh zH%1kN91&K%BxexRCalICbw)4MMS7-Kbab1t+zYuP>$Q@w(mdAfL+XcOl!R{De!Hhu zGs^kV&X6{Pyx0DXkW8cvD$7PPK{YvT3O%1K*Kze{OI6(E*hNDa0MpJmV^_r&Sg+Wj zHB>=Pk!TcUJAR0NJv=2WOD_mJp=8l=KlaJQjBlR2K=rD#7(7+16s>6t*vEClL*ZcT z&><-9%1jY0saBU3Q4D7%hy!omt@!zrX1<1~01SXvnd30?yAybjh~JqYJ}BS0`VHo| z{QqRcr5(bV;kXDexQS>pb6hq2E(A9d^)hPCeH`3EH0bInS3mCRMfPK!3iF4FEau4P zMdneWH<)AI3(Rp5y~P~y?=T|nL-ym>G@ohaUos;8I{UX1{lJJk&T`)Ch|V+KNAx!1 u14LID@nS=w04_c(NkQD@2VCTD4D*JIue*4~#V=g6>w`GE9zeSez`p^eh-}LM literal 0 HcmV?d00001 diff --git a/backend/camembert/snmp.cpp b/backend/camembert/snmp.cpp new file mode 100644 index 0000000..3919a04 --- /dev/null +++ b/backend/camembert/snmp.cpp @@ -0,0 +1,226 @@ +/* ======================================================== + +Camembert Project +Alban FERON, 2007 + +SNMP Base class. +Totalement repris de Kindmana, par Aurélien Méré + +======================================================== */ + +#include "snmp.h" +#include + +extern int snmp_answers, snmp_requests, snmp_dups; + +SNMPResult::SNMPResult(const Vb &vb):Vb(vb),next(NULL) { } +SNMPResult::SNMPResult(const Vb *vb):Vb(*vb),next(NULL) { } +SNMPResult::SNMPResult(const Vb &vb, const SNMPResult *next):Vb(vb),next(next) { } +SNMPResult::SNMPResult(const Vb *vb, const SNMPResult *next):Vb(*vb),next(next) { } + +//inline const SNMPResult *SNMPResult::getNext() const { return next; } + + +SNMP::SNMP() { + current_rid = (rand() % (PDU_MAX_RID - PDU_MIN_RID - 1)) + PDU_MIN_RID; + pthread_mutex_init(&mtx_rid, NULL); +} + +const SNMPResult *SNMP::snmpwalk(const unsigned int idconn, + const Oid *oid, + const unsigned int version, + const char *community, + const unsigned int timeout, + const unsigned int retry, + const SNMPResult *prev) +{ + int i; + Pdu pdu; + Vb vb(*oid); + pdu += vb; + + SNMPResult *last = (SNMPResult *)prev; + + ++snmp_requests; + + while(1) { + pdu.set_type(sNMP_PDU_GETBULK); + //printf("Suite boucle ... \n"); + if ((snmpengine(idconn, &pdu, version, community, timeout, retry)) == 0) { + if (pdu.get_vb_count()>0) { + for (i=0; ilen(), *oid) != 0)) + return(last); + last = new SNMPResult(vb, last); + } + pdu.set_vblist(&vb, 1); + } + else return prev; + } + else return prev; + } + printf("Fin snmpwalk 2 +++++++++++++++\n"); +} + +const unsigned int SNMP::snmpopen(const IP *ip) { + + int sock; + struct sockaddr_in sa; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) <= 0) { + fprintf(stderr, "FATAL: Couldn't create socket\n"); + return 0; + } + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(((IP *)ip)->getIPint()); + sa.sin_port = htons(161); + + if (connect(sock, (struct sockaddr *)&sa, (socklen_t)sizeof(sa)) == -1) { + close(sock); + return 0; + } + + return sock; +} + +void SNMP::snmpclose(unsigned int idconn) { + + close(idconn); + +} + +const unsigned int SNMP::snmpengine(const unsigned int idconn, + Pdu *pdu, + const unsigned int version, + const char *community, + const unsigned int timeout, + const unsigned int retry) +{ + fd_set fd; + int status, bytes; + char buffer[4096]; + struct timeval tout; + SnmpMessage snmpmsg; + SnmpMessage snmpmsg_ret; + OctetStr community_ret; + snmp_version version_ret; + unsigned short pdu_action; + unsigned int id, tries=0; + OctetStr comm(community); + snmp_version ver; +/* + Vb vb; + pdu->get_vb(vb, 0); + + printf("snmpengine : %d %s %d %s %d %d\n", idconn, vb.get_printable_oid(), version, community, timeout, retry); +*/ + if (version==1) ver=version1; + else ver=version2c; + + pdu_action = pdu->get_type(); + pdu->set_error_index(0); + + pthread_mutex_lock(&mtx_rid); + if (++current_rid > PDU_MAX_RID) current_rid=PDU_MIN_RID; + pdu->set_request_id(id = current_rid); + pthread_mutex_unlock(&mtx_rid); + + if (pdu_action == sNMP_PDU_GETBULK) { + if (ver == version1) { + pdu->set_type(sNMP_PDU_GETNEXT); + } + else { + pdu->set_error_status((int)0); + pdu->set_error_index((int)20); + } + } + + if ((status = snmpmsg.load(*pdu, comm, ver)) != SNMP_CLASS_SUCCESS) { + return 1; + } + + while (tries < retry) { + + bytes = send(idconn, snmpmsg.data(), (size_t)snmpmsg.len(), 0); + if (bytes != (int)snmpmsg.len()) { + fprintf(stderr, "FATAL: Send failed\n"); + return 1; + } + + tout.tv_sec = timeout/1000; + tout.tv_usec = (timeout%1000)*1000; + FD_ZERO(&fd); + FD_SET(idconn, &fd); + + if (select(idconn+1, &fd, NULL, NULL, &tout)>0) { + + if ((bytes = recv(idconn, buffer, sizeof(buffer), 0))>0) { + if (snmpmsg_ret.load((unsigned char *)buffer, bytes) == SNMP_CLASS_SUCCESS) { + if (snmpmsg_ret.unload(*pdu, community_ret, version_ret) == SNMP_CLASS_SUCCESS) { + + if (pdu->get_request_id() == id) { + ++snmp_answers; + return 0; + } + } + } + } + else fprintf(stderr, "FATAL: Read error\n"); + } + ++tries; + } + + return 1; +} + +const SNMPResult *SNMP::snmpget(const unsigned int idconn, + const Oid *oid, + const unsigned int version, + const char *community, + const unsigned int timeout, + const unsigned int retry) +{ + unsigned int result; + Pdu pdu; + Vb vb(*oid); + + ++snmp_requests; + pdu += vb; + pdu.set_type(sNMP_PDU_GET); + + if ((result = snmpengine(idconn, &pdu, version, community, timeout, retry)) == 0) { + + if (pdu.get_vb_count()>0) { + pdu.get_vb(vb, 0); + return(new SNMPResult(vb)); + } + } + + return NULL; +} +const SNMPResult *SNMP::snmpset(const unsigned int idconn, + const Vb *vb, + const unsigned int version, + const char * community, + const unsigned int timeout, + const unsigned int retry) +{ + Pdu pdu; + pdu += (Vb &)(*vb); + Vb vb_res; + int res; + ++snmp_requests; + + pdu.set_type(sNMP_PDU_SET); + + if ((res = snmpengine(idconn, &pdu, version, community, timeout, retry)) == 0) { + + pdu.get_vb(vb_res, 0); + return(new SNMPResult(vb_res)); + } + + return NULL; +} diff --git a/backend/camembert/snmp.h b/backend/camembert/snmp.h new file mode 100644 index 0000000..42b2a08 --- /dev/null +++ b/backend/camembert/snmp.h @@ -0,0 +1,123 @@ +/* ======================================================== + +Camembert Project +Alban FERON, 2007 + +SNMP Base class. +Totalement repris de Kindmana, par Aurélien Méré +(juste réindenté :p) + +======================================================== */ + +#ifndef __CAMEMBERT_SNMP_H +#define __CAMEMBERT_SNMP_H + +#include "ip.h" +#include "oid.h" +#include "pdu.h" +#include "vb.h" +#include "octet.h" +#include "snmpmsg.h" +#include "ip.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class SNMPResult:public Vb { + const SNMPResult *next; + + public: + inline const SNMPResult *getNext() const { return next; } + + SNMPResult(const Vb &vb); + SNMPResult(const Vb *vb); + SNMPResult(const Vb &vb, const SNMPResult *next); + SNMPResult(const Vb *vb, const SNMPResult *next); + inline ~SNMPResult() {} +}; + + +class SNMP { + + int current_rid; + pthread_mutex_t mtx_rid; + + const unsigned int snmpengine(const unsigned int idconn, + Pdu *pdu, + const unsigned int version, + const char *community, + const unsigned int timeout, + const unsigned int retry); + + public: + + SNMP(); + + const unsigned int snmpopen(const IP *ip); + void snmpclose(const unsigned int idconn); + + const SNMPResult *snmpget(const unsigned int idconn, + const Oid *oid, + const unsigned int version, + const char *community, + const unsigned int timeout, + const unsigned int retry) ; + + const SNMPResult *snmpwalk(const unsigned int idconn, + const Oid *oid, + const unsigned int version, + const char *community, + const unsigned int timeout, + const unsigned int retry, + const SNMPResult *prev); + + const SNMPResult *snmpset(const unsigned int idconn, + const Vb *vb, + const unsigned int version, + const char * community, + const unsigned int timeout, + const unsigned int retry); +}; + +class SNMPResults { + + SNMPResult const * ptr; + SNMPResult const * current; + + public: + + SNMPResults &operator=(SNMPResult const *res) { + if (ptr != NULL) + delete ptr; + ptr=res; + current = NULL; + return *this; + } + + SNMPResults(SNMPResult const *res):ptr(res),current(NULL) {} + + inline ~SNMPResults() { + SNMPResult const *p = ptr, *next; + while (p != NULL) { + next=p->getNext(); + delete p; + p=next; + } + } + + inline SNMPResult const *getNext() { + if (current == NULL) + current=ptr; + else + current=current->getNext(); + return current; + } +}; + + +#endif /* __CAMEMBERT_H */ diff --git a/backend/camembert/snmp.o b/backend/camembert/snmp.o new file mode 100644 index 0000000000000000000000000000000000000000..82afd1e2ae84a2c16fd309d5181cc8938bd540be GIT binary patch literal 13236 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 = lip; + this->snmp = snmp; + this->community = new char[strlen(community)+1]; + strcpy(this->community, community); + snmpid = 0; + status = STATUS_UNTESTED; + version = -1; + by_vlan = SNMP_VLANUNKNOWN; +} + +SNMPObject::SNMPObject(SNMP *snmp, const char* community, IP* ip) { + this->lip = new IPList(); + this->lip->addIP(ip, DBSTATUS_NEW); + this->snmp = snmp; + this->community = new char[strlen(community)+1]; + strcpy(this->community, community); + snmpid = 0; + status = STATUS_UNTESTED; + version = -1; + by_vlan = SNMP_VLANUNKNOWN; +} + +SNMPObject::~SNMPObject() { + if(lip) + delete lip; + delete[] community; +} + +const unsigned int SNMPObject::snmpopen() { + if(status == STATUS_MANAGEABLE && lip->getIP()) + snmpid = snmp->snmpopen(lip->getIP()); + return (snmpid != 0); +} + +const unsigned int SNMPObject::snmpclose() { + if(snmpid) + snmp->snmpclose(snmpid); + snmpid = 0; + return 1; +} + +const SNMPResult *SNMPObject::snmpwalk(const Oid *oid, const unsigned int vlan, const SNMPResult *sres) +{ + int ix_vers=0; + const SNMPResult *res = sres; + char commu[128]; + + if ((status == STATUS_MANAGEABLE) && (snmpid>0)) + { + ix_vers = version; + if (vlan != 0) + { + if (by_vlan != SNMP_VLANUNSUPPORTED) + { + sprintf(commu, "%s@%d", community, vlan); + res = snmp->snmpwalk(snmpid, oid, ix_vers, commu, TIMEOUT, RETRY, sres); + + if (res!=NULL && (by_vlan != SNMP_VLANSUPPORTED)) + by_vlan = SNMP_VLANSUPPORTED; + else if (by_vlan == SNMP_VLANUNKNOWN) + by_vlan = SNMP_VLANUNSUPPORTED; + } + } + else + res = snmp->snmpwalk(snmpid, oid, ix_vers, community, TIMEOUT, RETRY, sres); + } + + return(res); +} + +const SNMPResult *SNMPObject::snmpwalk(const Oid *oid) { return this->snmpwalk(oid, 0, NULL); } +const SNMPResult *SNMPObject::snmpwalk(const Oid *oid, const unsigned int vlan) { return this->snmpwalk(oid, vlan, NULL); } + +const unsigned int SNMPObject::snmpcheck() { + + int ix_vers=0; + unsigned int id; + const SNMPResult *res; + Oid oid("1.3.6.1.2.1.1.5.0"); /* RFC 1213 -> sysName (hostname) */ + + if (status == STATUS_UNTESTED && lip->getIP()) { + + status = STATUS_TESTING; + + // Check default IP with default community + + for (ix_vers=0; ix_verssnmpopen(lip->getIP()); + res = snmp->snmpget(id, &oid, versions[ix_vers], community, TIMEOUT, RETRY); + snmp->snmpclose(id); + + if (res != NULL) { + delete res; + version = ix_vers; + status = STATUS_MANAGEABLE; + lip->setDBstatus(DBSTATUS_UPDATED); + return 1; + } + } + + // Check other IPs + + for(IPList *l=lip->getNext(); l!=NULL; l=l->getNext()) { + for (ix_vers=0; ix_verssnmpopen(l->getIP()); + res = snmp->snmpget(id, &oid, versions[ix_vers], community, TIMEOUT, RETRY); + snmp->snmpclose(id); + + if (res != NULL) { + delete res; + version = ix_vers; + status = STATUS_MANAGEABLE; + l->setDBstatus(DBSTATUS_UPDATED); + lip->setFirst(l); + return 1; + } + } + } + + status = STATUS_NOTMANAGEABLE; + return 0; + } + + return (status == STATUS_MANAGEABLE); +} + +const SNMPResult *SNMPObject::multiplesnmpwalk(const unsigned int nboid, const unsigned int vlan, ...) { + + va_list varptr; + const SNMPResult *r = NULL; + const Oid *oid; + unsigned int i; + + va_start(varptr, vlan); + for (i=0; isnmpwalk(oid, vlan, r); + } + + va_end(varptr); + return(r); +} + +const SNMPResult *SNMPObject::snmpget(const Oid *oid) +{ + if ((status == STATUS_MANAGEABLE) && (snmpid>0)) + return snmp->snmpget(snmpid, oid, versions[version], community, TIMEOUT, RETRY); + + return NULL; +} + +const SNMPResult *SNMPObject::snmpset(const Oid *oid, const char *str) +{ + Vb vb; + + if ((status == STATUS_MANAGEABLE) && (snmpid>0)) { + vb.set_value(str); + vb.set_oid(*oid); + return snmp->snmpset(snmpid, &vb, versions[version], community, TIMEOUT, RETRY); + } + + return NULL; +} + +const SNMPResult *SNMPObject::snmpset(const Oid *oid, const SnmpSyntax &stx) { + + Vb vb(*oid); + + if ((status == STATUS_MANAGEABLE) && (snmpid>0)) { + vb.set_value(stx); + return snmp->snmpset(snmpid, &vb, versions[version], community, TIMEOUT, RETRY); + } + + return NULL; +} + + +const SNMPResult *SNMPObject::snmpset(const Oid *oid, const int value) { + + Vb vb; + + if ((status == STATUS_MANAGEABLE) && (snmpid>0)) { + vb.set_value(value); + vb.set_oid(*oid); + return snmp->snmpset(snmpid, &vb, versions[version], community, TIMEOUT, RETRY); + } + + return NULL; +} + +void SNMPObject::addIP(IP *const ip, unsigned int indb) const { + lip->addIP(ip, indb); +} + +const int SNMPObject::getVersion() const { + return version; +} + +const int SNMPObject::getManageableStatus() const { + return status; +} + +IPList* SNMPObject::getIPList() const { + return lip; +} + +const char* SNMPObject::getCommunity() const { + return community; +} diff --git a/backend/camembert/snmpobject.h b/backend/camembert/snmpobject.h new file mode 100644 index 0000000..616eece --- /dev/null +++ b/backend/camembert/snmpobject.h @@ -0,0 +1,74 @@ +/* ======================================================== + +Camembert Project +Alban FERON, 2007 + +SNMPObject class. +Repris (et simplifié) de Kindmana, par Aurélien Méré + +======================================================== */ + +#ifndef __CAMEMBERT_SNMPOBJECT_H +#define __CAMEMBERT_SNMPOBJECT_H + +#include "snmp.h" +#include "ip.h" +#include + +#define STATUS_UNTESTED -1 +#define STATUS_NOTMANAGEABLE 0 +#define STATUS_MANAGEABLE 1 +#define STATUS_TESTING 2 + +#define SNMP_VLANUNKNOWN -1 +#define SNMP_VLANSUPPORTED 1 +#define SNMP_VLANUNSUPPORTED 0 + +#define RETRY 3 +#define NB_VERSIONS 2 +#define TIMEOUT 2500 + +class SNMPObject { + protected: + SNMP *snmp; + unsigned int snmpid; + + int status; + int version; + int by_vlan; + + IPList* lip; + char* community; + + const SNMPResult *snmpwalk(const Oid *oid, const unsigned int vlan, const SNMPResult *sres); + + public: + + SNMPObject(SNMP *snmp, const char* community, IPList* lip); + SNMPObject(SNMP *snmp, const char* community, IP* ip); + ~SNMPObject(); + + const unsigned int snmpopen(); + const unsigned int snmpclose(); + const unsigned int snmpcheck(); + + const SNMPResult *snmpwalk(const Oid *oid); + const SNMPResult *snmpwalk(const Oid *oid, const unsigned int vlan); + + const SNMPResult *multiplesnmpwalk(const unsigned int nboid, ...); + const SNMPResult *multiplesnmpwalk(const unsigned int nboid, const unsigned int vlan, ...); + + const SNMPResult *snmpget(const Oid *oid); + const SNMPResult *snmpset(const Oid *oid, const char *str); + const SNMPResult *snmpset(const Oid *oid, const int value); + const SNMPResult *snmpset(const Oid *oid, const SnmpSyntax &stx); + + const int getVersion() const; + const int getManageableStatus() const; + + void addIP(IP *const ip, unsigned int indb) const; + IPList* getIPList() const; + const char* getCommunity() const; +}; + +#endif /* __CAMEMBERT_H */ diff --git a/backend/camembert/snmpobject.o b/backend/camembert/snmpobject.o new file mode 100644 index 0000000000000000000000000000000000000000..049f34a5f727e1c810050216131a5ac7ff81878e GIT binary patch literal 8528 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 + delete curr; + curr = vlans; + } +} + +void VlanList::addVlan(unsigned short int vlan) { + lstVLANs *newptr, *prev, *ptr; + + for(ptr=vlans, prev=NULL; ptr!=NULL; ptr=ptr->next) { + if (ptr->vlan == vlan) return; + if (ptr->vlan < vlan) break; + prev = ptr; + } + + newptr = new lstVLANs; + newptr->vlan = vlan; + if (prev == NULL) { + newptr->next = vlans; + vlans = newptr; + return; + } + + newptr->next = prev->next; + prev->next = newptr; +} + +bool VlanList::getNext(unsigned short int &vlan) const { + if(!curr) + curr = vlans; + else + curr = curr->next; + + if(!curr) + return false; + + vlan = curr->vlan; + return true; +} diff --git a/backend/camembert/vlan.h b/backend/camembert/vlan.h new file mode 100644 index 0000000..281658d --- /dev/null +++ b/backend/camembert/vlan.h @@ -0,0 +1,25 @@ +#ifndef __CAMEMBERT_VLAN_H +#define __CAMEMBERT_VLAN_H + +#include + +typedef struct lstVLANs_s +{ + unsigned short int vlan; + struct lstVLANs_s *next; +} lstVLANs; + +class VlanList { + private: + lstVLANs *vlans; + mutable lstVLANs *curr; + + public: + VlanList(); + ~VlanList(); + + void addVlan(unsigned short int vlan); + bool getNext(unsigned short int &vlan) const; +}; + +#endif /* __CAMEMBERT_VLAN_H */ diff --git a/backend/camembert/vlan.o b/backend/camembert/vlan.o new file mode 100644 index 0000000000000000000000000000000000000000..386129dd67f86f7b7c123f1bc15eff18af61ed38 GIT binary patch literal 1752 zcmbtUOHUI~6uv_TDOdyvMhzIcK!g~^VlYtSqgW~-LPIcs;bAkt6i_I&y_Di3nlx3K z36X^hmiz=3Mi#mtfruLu;+72yi6*$fy$OD2W^S1p6G=SDH{W^PbMLvmr_Tc&w1pjzf)g(B79p3JTuaBdpDBc!l0A zXS(EuHRwfpz@?XkGo>ri&^pTLrHJf(T|~XU-n*$EmHIPh$?A+tuS#$8&XQF}*t^fK zhrJ&YXZLO(KkW4^F7O)0>lWV8IbwyGb=@zg*JgG%Vw}^1MVbw_!7O zin!&3^Hw^qI3Fs-t^sb~Z?Svu`+!S;eO)C?4n%<`KsMH%%6}=pa!}njz&>yj$i~}J zMr;Kx4<6i~F{Z@lrnou`<}s7Ug%;B$6|N;YQ$|&;GG#a(XUfR*)RY#1FEY(Fr7hS~ zALr`GL#9x+HXaJc)G3V)a5Skx8qI-ue`fE~%KulM2RT%Nn1t$`GgaeDF@o1(!->yla@s_go$!%vjXT?d@yV7a z&esl~z29Z@+YBG|TL6fDPeKj@fPSlfaZ$w`@vCwW{K=aU?H@A7T5=9UMM~f7DDmYfCo*BI7>{b5oIJAWug9H zb1#gJi8%LP4kTD8G?q+;qFO9I9Z@GI_*^LA$<1n@tp4Dg?7F5gjR!G#fo`6Y*`$t+ zQtbe_BeM^3f~{tm(aA2OTDDj#9pXLCn^(-(w#Ah=dtM1+gBF*;FX6|pra0b^&*DLg LlNK*o{MO>%hej3I literal 0 HcmV?d00001 diff --git a/backend/multi-telnet/commandes.txt b/backend/multi-telnet/commandes.txt new file mode 100644 index 0000000..688aab3 --- /dev/null +++ b/backend/multi-telnet/commandes.txt @@ -0,0 +1,4 @@ +conf t + int gi0/1 +end +write mem diff --git a/backend/multi-telnet/liste.txt b/backend/multi-telnet/liste.txt new file mode 100644 index 0000000..be1d7aa --- /dev/null +++ b/backend/multi-telnet/liste.txt @@ -0,0 +1,8 @@ +sn1 +sn2 +sn3 +sn4 +ss1 +ss2 +ss3 +ss4 diff --git a/backend/multi-telnet/script.sh b/backend/multi-telnet/script.sh new file mode 100644 index 0000000..1f7f236 --- /dev/null +++ b/backend/multi-telnet/script.sh @@ -0,0 +1,177 @@ +#!/bin/bash +#script.sh + +echo "veuillez donner le mot de passe" +stty -echo #[1] +read password +stty echo + +export ssh='./ssh.sh' #[2] +export telnet='./telnet.sh' +export erreur='./rapport_erreurs.log' +export temp='./tmp_routeur.log' +export cmdcisco='./commandes.txt' +export liste='./liste.txt' +export password +export routeur +export commande + +rm -f $erreur #[3] +rm -f $ssh +rm -f $telnet + +cat $liste | while read routeur; +do + if [ "$routeur" != "" ] + then + if[ ! -f $ssh ] #[4] + then + echo 'expect 2>&1 << EOF'>> $ssh + echo 'spawn ssh admin@$routeur' >> $ssh + echo 'expect {' >> $ssh + echo '"Password:" {send "$password\r"}' >> $ssh + echo 'timeout {exit}' >> $ssh + echo ' }' >> $ssh + echo 'expect "#"' >> $ssh + + cat $cmdcisco | while read commande + do + echo "send \"$commande\r\"" + echo 'expect "#"' + done >> $ssh + + echo 'send "exit\r"' >> $ssh + echo 'expect "closed"' >> $ssh + echo 'exit' >> $ssh + echo 'EOF' >> $ssh + + chmod +x $ssh #[5] + fi + time -p $ssh > $temp 2>&1 #[6] + + COD_RET=$? + + auth='cat $temp | grep -c "Password: "' #[7] + if [ "$auth" -gt "1" ] + then + echo "Problème d'authentification sur $routeur !" + echo "$routeur : wrong log-in/password" >> $erreur + continue + fi + + temps='grep 'real ' $temp | sed 's/real /§/' | cut -d'§' -f2 | cut -d' ' -f1 | cut -d'.' -f1' + if [ $temps -ge 10 -a ! "'grep 'closed' $temp'" ] #[8] + then + echo "L'equipement $routeur ne réponds pas !"; + echo "$routeur : connection timed out" >> $erreur + continue + fi + + if [ "$COD_RET" != "0" ] #[9] + then + #Erreur de connexion a l'équipement en SSH + if [ ! -f $telnet ] + then + echo 'expect 2>&1 << EOF'>> $telnet + echo 'spawn telnet $routeur' >> $telnet + echo 'send "admin\r"' >> $telnet + echo 'expect "Password:"' >> $telnet + echo 'send "$password\r"' >> $telnet + echo 'expect "#"' >> $telnet + + cat $cmdcisco | while read commande + do + echo "send \"$commande\r\"" + echo 'expect "#"' + done >> $telnet + + echo 'send "exit\r"' >> $telnet + echo 'expect "closed"' >> $telnet + echo 'exit' >> $telnet + echo 'EOF' >> $telnet + + chmod +x $telnet + fi + $telnet > $temp 2>&1 + fi + COD_RET=$? + + auth='cat $temp | grep -c "Password: "' #[10] + if [ "$auth" -gt "1" ] + then + echo "Problème d'authentification sur $routeur !" + echo "$routeur : wrong log-in/password" >> $erreur + elif [ "'grep 'Host name lookup failure' $temp'" ] + then + echo "l'equipement $routeur n'existe pas !" + echo "$routeur : does not exist" >> $erreur + elif [ "'grep 'Unknown host' $temp'" ] + then + echo "la saisie de l'ip ou du nom $routeur est incorrecte !" + echo "$routeur : wrong spelling" >> $erreur + elif [ "'grep 'send: spawn id exp4 not open' $temp'" ] + then + echo "/!\ ERREUR dans la procédure. Consultez le fichier log de $routeur !!!" + echo "$routeur : Expect script execution failed !" >> $erreur + cp $temp $routeur.error.log + elif [ "'grep 'Authentication failed' $temp'" ] + then + echo "Mot de passe erroné pour $routeur !" + echo "$routeur : wrong log-in/password" >> $erreur + elif [ "'grep 'Connection refused' $temp'" ] + then + echo "Connexion à distance sur $routeur désactivé !" + echo "$routeur : vty connection disabled" >> $erreur + elif [ "'grep 'No route to host' $temp'" ] + then + echo "Alias DNS $routeur existant mais IP invalide !" + echo "$routeur : No route to host" >> $erreur + elif [ "'grep 'ProCurve' $temp'" ] + then + echo "routeur $routeur HP et non Cisco !" + echo "$routeur : non Cisco router (HP ProCurve)" >> $erreur + elif [ "'grep 'Alcatel' $temp'" ] + then + echo "routeur $routeur Alcatel et non Cisco !" + echo "$routeur : non Cisco router (Alcatel)" >> $erreur + elif [ "'grep 'Welcome to X1000' $temp'" ] + then + echo "routeur $routeur X1000 et non Cisco !" + echo "$routeur : non Cisco equipement (X1000)" >> $erreur + elif [ "'grep '% Unknown command' $temp'" -o "'grep '% Invalid' $temp'" ] + then + echo "/!\ Commandes Cisco non reconnues par l'equipement. Consultez le fichier log de $routeur !!!" + echo "$routeur : Unrecognized commands found" >> $erreur + cp $temp $routeur.error.log + elif [ "'grep 'Connected to ' $temp'" -o "'grep 'Connection closed by foreign host.' $temp'" ] + then + echo "$routeur Telnet OK !" + elif [ "'grep 'Connexion enregistree sur le terminal' $temp'" -o "'grep 'Connection to ' $temp'" ] + then + echo "$routeur SSH OK !" + elif [ "$COD_RET" != "0" ] + then + echo "Problème de connexion a l'equipement $routeur !" + echo "$routeur : connection problem" >> $erreur + fi + fi +done +rm -f $temp #[11] +exit + + +#Commentaires +# +# 1 : On cache la saisie du mot de passe +# 2 : Tous les fichiers sont stockés dans des variables en chemin relatif, pour que le script puisse être exécuté de n'importe où +# 3 : on supprime les fichiers générés existants si le script à déjà été exécuté +# 4 : on génère le fichier Expect si ce n'est pas déjà fait, en rentrant la procédure de connexion SSH ainsi que les commandes provenant de commandes.txt +# 5 : on attribue les droits d'exécution au script Expect généré, si on veut qu'il s'exécute correctement +# 6 : on execute le script expect, en regroupant la sortie d'erreur avec la sortie standard, en calculant le temps d'exécution pour gérer le timeout, et en crachant le tout dans un fichier temp +# 7 : on vérifie qu'il n'y ai pas un problème d'authentification en comptant le nombre d'occurrence "Password:" dans le fichier temp +# 8 : on récupère le nombre du temps d'execution, et on vérifie qu'il ne soit pas supérieur à 10 (valeur du timeout du expect) +# 9 : En cas d'erreur de connexion en SSH, on refait la procédure en Telnet +# 10 : On gère tous les cas d'erreur pris en compte par le script. (c.f. II) +# 11 : On supprime le fichier temp, qui devient inutile + + diff --git a/backend/snmp.py b/backend/snmp.py new file mode 100755 index 0000000..c1f8529 --- /dev/null +++ b/backend/snmp.py @@ -0,0 +1,85 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Ce script fait un write memory sur chacun des switchs passés en paramètres. + +from pysnmp.entity.rfc3413.oneliner import cmdgen +from pysnmp.proto import rfc1902 +import random +import re +import os,sys + +CiscoPortSecurityMacAddresses = 1,3,6,1,4,1,9,9,315,1,2,2,1,4 +CiscoPortSecurityLastAddress = 1,3,6,1,4,1,9,9,315,1,2,1,1,10 +CiscoifAdminStatus = 1,3,6,1,2,1,2,2,1,7 # 1 = up, 2 = down, 3 = testing +CiscoifOperStatus = 1,3,6,1,2,1,2,2,1,8 # 1 = up, 2 = down, 3 = testing +Cisco_ccCopySourceFileType = 1,3,6,1,4,1,9,9,96,1,1,1,1,3 # needs a random row and a file type: 3: startupConfig, 4: runningConfig +Cisco_ccCopyDestFileType = 1,3,6,1,4,1,9,9,96,1,1,1,1,4 # needs a random row and a file type: 3: startupConfig, 4: runningConfig +Cisco_ccCopyEntryRowStatus = 1,3,6,1,4,1,9,9,96,1,1,1,1,14 # needs a random row and a status: 1: starts copying +Cisco_ccCopyState = 1,3,6,1,4,1,9,9,96,1,1,1,1,10 # needs a random row; 1: waiting, 2: running, 3: done ok, 4: failed + +CommunityData = cmdgen.CommunityData('cerbere', 'pacadmins') + +def getCMD( host, oid, value=None ): + targetAddr = cmdgen.UdpTransportTarget((host, 161)) + + if value != None: + errorIndication, errorStatus, errorIndex, varBinds = cmdgen.CommandGenerator().getCmd( + CommunityData, targetAddr, oid + ( value, ) + ) + else: + errorIndication, errorStatus, errorIndex, varBinds = cmdgen.CommandGenerator().getCmd( + CommunityData, targetAddr, oid + ) + + return (errorIndication, errorStatus, errorIndex, varBinds) + +def setCMD( host, oid, value): + targetAddr = cmdgen.UdpTransportTarget((host, 161)) + errorIndication, errorStatus, errorIndex, varBinds = cmdgen.CommandGenerator().setCmd( + CommunityData, targetAddr, ( oid , value ) + ) + return (errorIndication, errorStatus, errorIndex, varBinds) + +def writeMemory( host ): + row = random.randrange(1, 10) + + errorIndication, errorStatus, errorIndex, varBinds = setCMD( host, Cisco_ccCopySourceFileType + (row,), rfc1902.Integer(4) ) + source_result = { 'errorIndication': errorIndication, 'errorStatus': errorStatus, 'errorIndex': errorIndex, 'value': varBinds} + + errorIndication, errorStatus, errorIndex, varBinds = setCMD( host, Cisco_ccCopyDestFileType + (row,), rfc1902.Integer(3) ) + dest_result = { 'errorIndication': errorIndication, 'errorStatus': errorStatus, 'errorIndex': errorIndex, 'value': varBinds} + + errorIndication, errorStatus, errorIndex, varBinds = setCMD( host, Cisco_ccCopyEntryRowStatus + (row,), rfc1902.Integer(1) ) + dest_result = { 'errorIndication': errorIndication, 'errorStatus': errorStatus, 'errorIndex': errorIndex, 'value': varBinds} + + ok = False + while not ok: + errorIndication, errorStatus, errorIndex, varBinds = getCMD( host, Cisco_ccCopyState + (row,) ) + state_result = { 'errorIndication': errorIndication, 'errorStatus': errorStatus, 'errorIndex': errorIndex, 'value': varBinds} + if not state_result['value']: + state_result['value']=0 + return state_result + else: + tmp=state_result['value'] + state_result['value']=tmp[0][1] + if state_result['value'] == 3 or state_result['value'] == 4: + ok = True + return state_result + + +def wr_mem(switch): + wr_mem_result = writeMemory( switch ) + if wr_mem_result['value'] == 3: + print "La sauvegarde du switch "+switch+" a réussi." + else: + print "La sauvegarde du switch "+switch+" a échoué." + print "Pour information: " + print wr_mem_result + return wr_mem_result + +i = 1 +while i < len(sys.argv): + wr_mem(sys.argv[i]) + i = i+1 + diff --git a/camembert-before-ldap/.htaccess b/camembert-before-ldap/.htaccess new file mode 100644 index 0000000..fc56cc6 --- /dev/null +++ b/camembert-before-ldap/.htaccess @@ -0,0 +1,24 @@ +ErrorDocument 401 /denied.php +ErrorDocument 403 /denied.php +ErrorDocument 404 /404.php +# Customized error messages. +#ErrorDocument 404 /var/www/index.php + +# Various rewrite rules. + + RewriteEngine on + + # If your site can be accessed both with and without the 'www.' prefix, you + # can use one of the following settings to redirect users to your preferred + # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option: + # + # To redirect all users to access the site WITH the 'www.' prefix, + # (http://example.com/... will be redirected to http://www.example.com/...) + # adapt and uncomment the following: + # RewriteCond %{HTTP_HOST} ^example\.com$ [NC] + # RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301] + + RewriteCond %{HTTP_HOST} ^camembert$ [NC] + RewriteRule ^(.*)$ http://camembert.pacaterie.u-psud.fr/$1 [L,R=301] + + diff --git a/camembert-before-ldap/.htpasswd b/camembert-before-ldap/.htpasswd new file mode 100644 index 0000000..1b02c6a --- /dev/null +++ b/camembert-before-ldap/.htpasswd @@ -0,0 +1,2 @@ +test:y0Uiblabl6ciNo +test2:B0blay5Ez.qE. diff --git a/camembert-before-ldap/404.php b/camembert-before-ldap/404.php new file mode 100644 index 0000000..0d493e8 --- /dev/null +++ b/camembert-before-ldap/404.php @@ -0,0 +1,10 @@ + + +

La page que vous avez demandé est introuvable.

+ + + diff --git a/camembert-before-ldap/anomalies.php b/camembert-before-ldap/anomalies.php new file mode 100644 index 0000000..917fc8e --- /dev/null +++ b/camembert-before-ldap/anomalies.php @@ -0,0 +1,32 @@ +
Interfaces en ERR-DIS :
Incohérence DHCP-ARP :
    +
Incohérence DHCP-Sticky :
diff --git a/camembert-before-ldap/arrow.gif b/camembert-before-ldap/arrow.gif new file mode 100644 index 0000000000000000000000000000000000000000..cccf415ab15741c5950dcf9dc8bd357c253b8b76 GIT binary patch literal 847 zcmb7@v5J#X5QRsuh_ZpjZ6sJIHYv7|A+TyA1Xcx$Jb(dhrb(k+dY5b=$VwWg4EE`l zR9YFBa!GCRzd)+RYW(J|FW_y$aPOS?&Y3?iU!R^leSaVaaxFJUMNGs+LL`MsRn$aX zG(@v5O~p*i#X>CWt*f|+yLgCawT4QVgiC}(q9m!3Ch3wPNr5)fXwl5;tT}Z|)m1|^ zYb=$SnyZCc76q4X>aHH@Sx~$U({PQ@NEm6=G)>nGO{PMP7|>9H#T|puMj9=evkX9D|Vsa(lb2Ck)#O#E}j!KZi$jSz@(cvV8SpCp+*d7I>Ca2 z!Du6$wW4~>H8m&M7(wEXu~d;+3vBBzinh_(XIoN1wcKmHEy`kS>D0EH;X-a6YWWST z9JEPD^Et33!oyP=CH;PXFc=Jn!_jCo9*-xJ$#goM&1UoYe6d(8m&?^^wO+3`o6UB+ z-R*W=*X{TFo8EsP^8f!l`g8k^NGHeDTK@^@9m}oWgI6COUViEw-F@@x;_Z*~!^h9g dPWHdYpXKo0)wi$Te}3sdS#B zTHxe}uE+;y)a3PdZ~F!MnsB`DJ@Y<(m#oI6q*9eV-kycI9bj{FYD%6Mp4JBCIF&J&6(V{uaP}Lka z3$rYLjHPln5A!U$P;eP;5f+Jipm=NPmSKrK@&J==;=n%;ID{H8prPa`91KPqX|%GE zt7~dbxMu~4KgLo;cqF-E7YZ&t!;>6IngHP9IYHx=D9Hm%x`_iO4C4@L#DJy~EI1g9 zHqu!us@GgobCQh_B>osn6`8fbw*I1M8?Ak|B^6Z5z1G{JEXI~jZMzvR}L- zn}jr<16v|IJhf5M?RI;;UccWT3PUJ=B_`|2Wn=hTCz01G%A8)@Myn6HXYYcoO0PojzXaE2J literal 0 HcmV?d00001 diff --git a/camembert-before-ldap/cam_users.php b/camembert-before-ldap/cam_users.php new file mode 100644 index 0000000..7df22f5 --- /dev/null +++ b/camembert-before-ldap/cam_users.php @@ -0,0 +1,86 @@ +User ".$a['login']." supprimé.

\n"; + } + else { + if(isset($_GET['group'])) { + pg_query("UPDATE cam_user SET groupe = ".$_GET['group']." WHERE iduser = ".$a['iduser']); + $a['groupe'] = $_GET['group']; + // insertion dans la trésorerie + $roles2 = array(); + $r = pg_query("SELECT login FROM cam_user WHERE iduser = ".$a['iduser']); + $login = pg_fetch_array($r); + get_roles($login[0], $roles2); + if ($roles2['inscription']) { + $r = pg_query("SELECT * FROM account WHERE iduser = ".$a['iduser']); + // si n'existe pas déjà dans la trésorerie + if (! $a2 = pg_fetch_array($r)) { + $r = pg_query("SELECT iduser FROM id WHERE idcam_user = ".$a['iduser']); + if ($a3 = pg_fetch_array($r)) { + $r = pg_query("SELECT nom, prenom FROM user_pac WHERE iduser = ".$a3['iduser']); + $a4 = pg_fetch_array($r); + pg_query("INSERT INTO account(iduser, nom, prenom, amount) VALUES('".$a['iduser']."', '".$a4['nom']."', '".$a4['prenom']."', 0)"); + } + // else wtf !? + } + } + } + + echo "

".$a['login']."

\n"; + echo "

Groupe :

\n"; + } + } + else + echo "

Cet id n'existe pas

\n"; +} + +?> +

Liste des utilisateurs

+
+ + +"; + echo "\n"; + $a = pg_fetch_array($r); + $bline = !$bline; + } + echo "
idloginchambregroupesuppr
".$a['iduser']."".$a['login']."".$a2['name']."".$a['name']."suppr
\n"; +} +?> +
+ diff --git a/camembert-before-ldap/comp.php b/camembert-before-ldap/comp.php new file mode 100644 index 0000000..fc55b80 --- /dev/null +++ b/camembert-before-ldap/comp.php @@ -0,0 +1,234 @@ +Erreur : cet utilisateur n'existe pas

\n"; + return; + } + $name = "pc-de-".str_replace(" ", "-", strtolower($a['prenom'])); + $r = pg_query("SELECT name FROM computer"); + $a = pg_fetch_all_columns($r); + $i = 2; + while(in_array($name, $a)) { + $name .= $i; + $i++; + } +?> +

Nouvelle machine pour

+
+ + +
Nom de machine
Adresse MAC : : + : : + :
+Erreur : cette machine n'existe pas

\n"; + return; + } + $mac = explode(":", strtoupper($a['mac'])); +?> +

Modification de machine

+
+ + +
Nom de machine
Adresse MAC : + : + : + : + : +
+ $comp"); + $a = pg_fetch_array($r); + if($a) + return "Erreur : il y a deja une machine avec ce nom ou cette adresse MAC"; + + pg_query("UPDATE computer SET name = '".$_POST['name']."', mac = '$mac' WHERE idcomp = $comp"); + + UpdateInterface($user); + return true; +} + +function DeleteComp($comp) { + $r = pg_query("SELECT iduser FROM computer WHERE idcomp = $comp"); + $a = pg_fetch_array($r); + if(!$a) + return false; + + pg_query("UPDATE ip_user SET free = '1' WHERE ip IN(SELECT ip FROM computer WHERE idcomp = $comp)"); + pg_query("DELETE FROM computer WHERE idcomp = $comp"); + UpdateInterface($a['iduser']); + return $a['iduser']; +} + +function UpdateInterface($user) { + $r = pg_query("SELECT idinterface, datedeco, certif FROM room r, user_pac u WHERE u.idroom = r.idroom AND iduser = $user"); + $a = pg_fetch_array($r); + $idif = $a['idinterface']; + $ddeco = explode("-", $a['datedeco']); + $adeco = $ddeco[0]; + $mdeco = $ddeco[1]; + $certif = ($a['certif'] == 't' || (date("n") >= 9 && date("n") < 11)); + + $r = pg_query("SELECT MAX(idaction) FROM action"); + $a = pg_fetch_array($r); + if($a) + $newid = $a[0]+1; + else + $newid = 1; + + $r = pg_query("SELECT COUNT(*) FROM computer WHERE iduser = $user"); + $a = pg_fetch_array($r); + $nb = max(1, $a[0]); + + pg_query("INSERT INTO action(idaction, idinterface, numaction) VALUES($newid, $idif, 0)"); $newid++; + pg_query("INSERT INTO action(idaction, idinterface, numaction, option) VALUES($newid, $idif, 3, '$nb')"); $newid++; + $r = pg_query("SELECT mac FROM fdb WHERE idinterface = $idif AND type = 2 AND datelast = (SELECT MAX(datelast) FROM fdb)"); + while($a = pg_fetch_array($r)) { + pg_query("INSERT INTO action(idaction, idinterface, numaction, option) VALUES($newid, $idif, 4, '".$a['mac']."')"); + $newid++; + } + if(($adeco > date("Y") || $mdeco >= date("m")) && $certif) + pg_query("INSERT INTO action(idaction, idinterface, numaction) VALUES($newid, $idif, 1)"); +} + +if(isset($_POST['user']) && $_POST['user'] != "") { + if($roles['inscription']) { + $val = ValidateNew($_POST['user']); + if($val === true) + header("Location: user.php?id=".$_POST['user']); + else { + include "inc/inc.header.php"; + echo "

$val

\n"; + } + } + else { + include "denied.php"; + return; + } +} +else if(isset($_POST['comp']) && $_POST['comp'] != "") { + if($roles['edit_comp']) { + $val = ValidateEdit($_POST['comp']); + if($val === true) { + $r = pg_query("SELECT iduser FROM computer WHERE idcomp = ".$_POST['comp']); + $a = pg_fetch_array($r); + header("Location: user.php?id=".$a['iduser']); + } + else { + include "inc/inc.header.php"; + echo "

$val

\n"; + } + } + else { + include "denied.php"; + return; + } +} +else if(isset($_GET['user']) && $_GET['user'] != "") { + if($roles['inscription']) { + include "inc/inc.header.php"; + FormNew($_GET['user']); + } + else { + include "denied.php"; + return; + } +} +else if(isset($_GET['id']) && $_GET['id'] != "") { + if($roles['edit_comp']) { + include "inc/inc.header.php"; + FormEdit($_GET['id']); + } + else { + include "denied.php"; + return; + } +} +else if(isset($_GET['del']) && $_GET['del'] != "") { + if($roles['inscription']) { + $user = DeleteComp($_GET['del']); + if($user === false) { + include "inc/inc.header.php"; + echo "

Erreur : cette machine n'existe pas

\n"; + } + else + header("Location: user.php?id=$user"); + } + else { + include "denied.php"; + return; + } +} + +include "inc/inc.footer.php"; +?> diff --git a/camembert-before-ldap/denied.php b/camembert-before-ldap/denied.php new file mode 100644 index 0000000..d2032b8 --- /dev/null +++ b/camembert-before-ldap/denied.php @@ -0,0 +1,37 @@ + + + Camembert :: Accès refusé + + + + +

Camembert

+

Accès refusé

+
+ +

Vous n'êtes pas autorisé à accéder à cette page.

+ +
+ + + + diff --git a/camembert-before-ldap/disconnected_user.php b/camembert-before-ldap/disconnected_user.php new file mode 100644 index 0000000..a7262a0 --- /dev/null +++ b/camembert-before-ldap/disconnected_user.php @@ -0,0 +1,10 @@ + diff --git a/camembert-before-ldap/findip.php b/camembert-before-ldap/findip.php new file mode 100644 index 0000000..113dac5 --- /dev/null +++ b/camembert-before-ldap/findip.php @@ -0,0 +1,100 @@ + + +
Chercher une adresse IP + + +
+
+ + +

L'adresse IP a été associée au matériel suivant

+ + +${a[1]}"; + else + $mat = $a[1]; + echo ""; + echo "\n"; + $bline = !$bline; + + $a = pg_fetch_array($r); + } + echo "
MatérielAssociée leJusqu'au
$mat".date("D d/m/Y H:i", $a[2])."".date("D d/m/Y H:i", $a[3])."
\n"; +} + +// IP dans le cache ARP +$r = pg_query("SELECT mac, foundon, hostname, a.datefirst, a.datelast + FROM arpcache a, materiel m WHERE foundon = idmateriel AND ip = '$ip' ORDER BY a.datelast DESC"); +$a = pg_fetch_array($r); +if($a) { +?> +

L'adresse IP a été associée aux adresses MAC suivantes

+ + +${a[0]}"; + else + $mac = $a[0]; + echo ""; + echo ""; + echo "\n"; + $bline = !$bline; + + $a = pg_fetch_array($r); + } + echo "
MACAssocié parAssociée leJusqu'au
$mac${a[2]}".date("D d/m/Y H:i", $a[3])."".date("D d/m/Y H:i", $a[4])."
\n"; +} + +// IP dans le DHCP +$r = pg_query("SELECT u.iduser, nom, prenom, c.name, r.idroom, r.name FROM computer c, user_pac u, room r + WHERE c.iduser = u.iduser AND u.idroom = r.idroom AND ip = '$ip'"); +$a = pg_fetch_array($r); +if($a) { +?> +

L'adresse IP est présente dans le DHCP

+ + +"; + echo "\n"; + $bline = !$bline; + + $a = pg_fetch_array($r); + } + echo "
UtilisateurChambreNom de machine
".$a[1]." ".$a[2]."".$a[5]."".$a[3]."
\n"; +} + +echo "
\n"; +include "inc/inc.footer.php"; + +?> diff --git a/camembert-before-ldap/findmac.php b/camembert-before-ldap/findmac.php new file mode 100644 index 0000000..c39b957 --- /dev/null +++ b/camembert-before-ldap/findmac.php @@ -0,0 +1,199 @@ + + +
Chercher une adresse MAC + + +
+ +\n"; +if ($mac == '') + echo "

L'adresse MAC ".$_GET["mac"]." n'est pas valide.

\n"; +else + SearchMAC($mac, $unique); +echo "\n"; + +include "inc/inc.footer.php"; + + +function SearchMAC($mac, $unique) { + // Vérifions si la MAC correspond à l'adresse d'une interface + $r = pg_query("SELECT ifaddress, idinterface, ifname, i.idmateriel, hostname + FROM interface i, materiel m + WHERE i.idmateriel = m.idmateriel + AND CAST(ifaddress AS VARCHAR(24)) ILIKE '%$mac%' + ORDER BY ifaddress"); + $a = pg_fetch_array($r); + if($a) { +?> +

L'adresse correspond aux interfaces suivantes

+ + +"; + echo "\n"; + $bline = !$bline; + $a = pg_fetch_array($r); + } + echo "
Adresse MACMatérielInterface
${a[0]}${a[4]}${a[2]}
\n"; + } + + $r = pg_query("SELECT MAX(datelast) FROM arpcache"); + $datelast = pg_fetch_array($r); + $datelast = $datelast[0]; + + if($unique) { + $a = SearchMACinDHCP($mac); + if($a) { + ?> +

L'adresse MAC est présente dans le DHCP

+ + + "; + echo "\n"; + $bline = !$bline; + + $a = pg_fetch_array($r); + } + echo "
UtilisateurChambreNom de machine
".$a[1]." ".$a[2]."".$a[5]."".$a[3]."
\n"; + } + + // Recherche dans le cache ARP + $r = pg_query("SELECT ip, foundon, hostname, a.datefirst, a.datelast + FROM arpcache a, materiel m + WHERE foundon = idmateriel AND mac = '$mac' + ORDER BY datelast DESC, ip"); + $a = pg_fetch_array($r); + if($a) { +?> +

L'adresse a été associée aux IP suivantes

+ + + +"; + + $r2 = pg_query("SELECT m.idmateriel, hostname FROM materiel m, ip + WHERE m.idmateriel = ip.idmateriel AND ip = '${a[0]}' AND ip.datelast = ${a[4]}"); + $a2 = pg_fetch_array($r2); + if($a2) + echo ""; + else + echo ""; + echo "\n"; + + $a = pg_fetch_array($r); + } + echo "
IPAssocié parAssociée leJusqu'auEquipement correspondant
"; + $bline = !$bline; + if($a[4] == $datelast) + echo "${a[0]}"; + else + echo $a[0]; + echo "${a[2]}".date("D d/m/Y H:i", $a[3])."".date("D d/m/Y H:i", $a[4])."${a2[1]}
\n"; + } + + $a = SearchMAConInterface($mac); + if($a) { +?> +

L'adresse a été trouvée sur les interfaces suivantes

+ + + +".$a[1].""; + else + $host = $a[1]; + + echo ""; + echo "\n"; + $bline = !$bline; + $a = pg_fetch_array($r); + } + echo "
Matériel / InterfaceVLANDate enrMàJ
$host / ${a[2]}${a[3]}".date("d/m/Y H:i", $a[4])."".date("d/m/Y H:i", $a[5])."
\n"; + } + } + else { + // Recherche dans le cache ARP + $r = pg_query("SELECT mac, ip, foundon, hostname, a.datefirst, a.datelast + FROM arpcache a, materiel m + WHERE foundon = idmateriel AND CAST(mac AS VARCHAR(24)) ILIKE '%$mac%' + ORDER BY mac"); + $a = pg_fetch_array($r); + if($a) { +?> +

Des adresses MAC contenant ont été associées aux IPs suivantes

+ + + +"; + echo ""; + echo ""; + + $r2 = pg_query("SELECT m.idmateriel, hostname FROM materiel m, ip + WHERE m.idmateriel = ip.idmateriel AND ip = '${a[1]}' AND ip.datelast = ${a[5]}"); + $a2 = pg_fetch_array($r2); + if($a2) + echo ""; + else + echo ""; + echo "\n"; + $bline = !$bline; + + $a = pg_fetch_array($r); + } + echo "
MACIPAssocié parAssociée leJusqu'auEquipement correspondant
${a[0]}${a[1]}${a[3]}".date("D d/m/Y H:i", $a[4])."".date("D d/m/Y H:i", $a[5])."${a2[1]}
\n"; + } + + // Recherche sur les interfaces. + $r = pg_query("SELECT mac, m.idmateriel, hostname, ifname, vlan, fdb.datefirst, fdb.datelast + FROM materiel m, interface i, fdb + WHERE m.idmateriel = i.idmateriel AND i.idinterface = fdb.idinterface + AND CAST(mac AS VARCHAR(24)) ILIKE '%$mac%' ORDER BY fdb.datelast DESC, mac, i.idmateriel, ifnumber"); + $a = pg_fetch_array($r); + if($a) { +?> +

L'adresse a été trouvée sur les interfaces suivantes

+ + + +"; + echo "\n"; + $bline = !$bline; + $a = pg_fetch_array($r); + } + echo "
Matériel / InterfaceMACVLANDate enrMàJ
${a[2]} / ${a[3]}${a[0]}${a[4]}".date("d/m/Y H:i", $a[5])."".date("d/m/Y H:i", $a[6])."
\n"; + } + } +} + +?> diff --git a/camembert-before-ldap/findname.php b/camembert-before-ldap/findname.php new file mode 100644 index 0000000..83935fc --- /dev/null +++ b/camembert-before-ldap/findname.php @@ -0,0 +1,69 @@ + + +
Chercher un nom + + +
+ +\n"; +if ($name == '') +echo "

Le nom ".$_GET["name"]." n'est pas valide.

\n"; +else +SearchNAME($name); +echo "\n"; + +include "inc/inc.footer.php"; + + +function CreateTable() { + ?> +

Le nom a été trouvé dans les chambres suivantes

+ + + + $row) { + echo ""; + echo ""; + echo ""; + $bline = !$bline; + } + echo "
chambrenomprenom
$id$row[0]$row[1]
\n"; + } +} + +?> diff --git a/camembert-before-ldap/groups.php b/camembert-before-ldap/groups.php new file mode 100644 index 0000000..d2d5c25 --- /dev/null +++ b/camembert-before-ldap/groups.php @@ -0,0 +1,92 @@ +Erreur: ce groupe existe déjà

"; + else { + $r = pg_query("SELECT MAX(idgroupe) FROM groupe"); + if($a = pg_fetch_array($r)) + $newid = $a[0]+1; + else + $newid = 1; + pg_query("INSERT INTO groupe(idgroupe, name, roles) VALUES($newid, '".$_POST['name']."', 0)"); + } +} +else if(isset($_POST['act']) && $_POST['act'] == 2) { + $r = pg_query("SELECT name FROM groupe WHERE idgroupe = ".$_POST['id']); + if(($a = pg_fetch_array($r)) === false) { + echo "

Mauvais id

\n"; + } + else { + $uroles = 0; + foreach($_POST as $k => $v) + if(is_int($k) && ($v == 'on' || $v == 1 || $v = 'checked')) + $uroles += 1 << $k; + pg_query("UPDATE groupe SET roles = $uroles WHERE idgroupe = ".$_POST['id']); + } +} + +$r = pg_query("SELECT idgroupe, name FROM groupe"); +if($a = pg_fetch_array($r)) { +?> + +\n"; + $a = pg_fetch_array($r); + $bline = !$bline; + } + echo "
idnom
".$a['idgroupe']."".$a['name']."
\n"; +} +?> + +

+ + + +

+ +Mauvais id

\n"; + include "inc/inc.footer.php"; + exit(); + } + $uroles = $a['roles']; + echo "

".$a['name']."

\n"; +?> +
+ + + +\n"; + } +?> +
 role
".$a['name']."

+ diff --git a/camembert-before-ldap/img/admin.png b/camembert-before-ldap/img/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..fa538c57bcfe4951ed0af368aa7a9c9ec8982188 GIT binary patch literal 5686 zcmV-67Rl*}P)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C3sXr%K~#9!)S6k09M^S*zk92y_v-H1cXBqdXNE&kA`{RE z;0SS?$hMR~KwgrE1Vcy)Ajq_ZP!=sDjvpMYv=+))>>#$diGqP3VBkoUV2c5?#Bu=H zi6}x`hVpQRvvtqf(@S+%-Fx%U!-*m=w6+;q2PmL{u3P>8_bm7Pr&<_e_%xe+=+L3- z_T#M`-Ewkzs%d#?;dhd5d|n{hEFp_hil1t&rmnSiWMrg9;JP3ec2}WTxMOB={F{kb zl=@AbNXsG=a!|@Jm!19ViOK120d#e=UE2d2iMz9_SB#Ba{9-u7^S#~OxNZX5wh5FE z0{VLUSXfvjJ2Nx%L7yo%lE9_0(LI(F&v$lqkV?7Owgnh4(AV2fV?!h3;}gs;ENliM zA1DVl5dTiC&G$EN?jjtHU|EtN2msi;c{6qCG$&tri3{gP-$95yfCn%!IsNP5foj!% zxTUp)cs!0VhAY*tuC9()EXoTfPjdeJh0~#s^M{^SId|0%Y;W!B>S#+}4+6us?fiay zT|HnBLZFl)91c@1m-+EeUS@Rk-Pb~)@XcS@`{mKAJ-|)GZ(Cf-{~JQyyx|1OUgZms zNEpkKXl;;EB7~q?t#WzdG85yMk(TxOukO8P@oMXnuKUM+5FFgJsdGSUb@PVjfY$mq zoKT2BDID8EYmE>Btu+RNQi{9({LaeK(h>lB@41_Fx)v!1pZ?oFQc68{+pS;1_p7}6 z>g%7~d(Yh)>jB%g>m1ubD~*A*9Ee0BxUNfIUq3(m;g34rzwY4EfAr$yyNT(G_>B}#^@b8qoA_By?VUJrA~rHIQuJp94=k`P44o5B_yvAk*C2(<|o}=9r(K z2WsJSB@dKRwK8io#$171^MWx3r46p@;y5Na_7lTOsh7464Kxi64T-6#NoKRNNFj+tB2+3Blu|?@5u(v3QcBv| z+UV}?=Iq(CD6NRc;zXk{qR|LSDdy&KoWC&oX4MZKx?|7CFV?LaeeA&FL&lg>1B02S zZQHgnIXTJ9%nX)g5s$|ym&<6aaoq%ogo{#&mX;QJdV26Yk01zGTwJ8NsR?P>l*?tN zre~O#n4szh2k*LLoDwQ~V^f-}_jSzx!7eX5Eo;YS|J z2q{lyZn&Xq>(;G|jg2uoJBw}GB$G*s#Ue`Az&lO|DJAuF4P-JIN~JQ-96m;&P{?P? zUeXEK1im5~i<}OHoTrW9=v{a2sjQozdGO&!2c!_MZ|U!^9~{guF)_~E+#Hr=;kqt` zLIJHc^>uX!DY0#vR4T>5K!$R;%rl3NQYfqxY|9>OZ)=+Rzx_uDfD=#dP@{KpOb{q^l_Elf{Ovb3~>l#*C1MzL4~puWC-O}r#sm*L^9 zR4P>t9X`s^(sEH+a^UOt@0h?I7?WvjZk+tst@<^-yV3Kiwyja zm6bvRz~)U|w70jB&o5Ib6c9q-I1ZlY5eY{~rIKslT{p@0?b`{0fWt?Rv%H)y8e=k@ z?Jbj^Lkp{cPg9*Yt9KDKS6ltM~LBof9Lv!<+U+ooErl1L;-C6o9; zfH8(dBEgOw+fhn$xkZEtI7s-OI%&Y$<^!0+bsD<@OQljjvLXs27X~-v0#kZ_O*TYj(?(R z?>)Npu8LRrUPpVoh{a+Aeih5MSEmRdWsR#4U<~-aj}VfEhKAKd4X9QtYaZFPYZpSm zkz>y>KR;g-LJU8!|GrNY{2BovtZhq6i~rc(-bO4Qt96Q6BeX_JiN6{`wKkX4HkX?C zuqq5ZuyrJq4&+`yM z5DJCxeSa;M!{IQMN@Z=ASEsZnrBEtBN(-eFyLa!#wr!3*`yAO^&KE)sKCpk^yX&Pp zr0vAqa@qS_B9Wk4t%ZdUl6X8pb8`zp5Kt@@DV0j3(`gzS8qxZS@Bk@mWaR2eYgraY zpFPfOHm5Ah8eUKE0CJ^Jyh%ub0fZ1pS&QrK+qcy;?cx%XlT*yk&oebOg%E;NDz&=v z3axAHx$pb`@i-V=huB?Q!15ia%_P>Af-)1V*^8jgGftq`t%t_ zFJ8p9EygD%>FTPDZdzMguS^IqMgwA1^3Af6UqM>d;MecpcVRF@6)P>NSxd4+6t)`Y{McfoX5tATat(gZ@(z(%9dRRUMSNT8LbrMclxvU7`PAA01# zM*`H|+VtTW!rM|w_djjhHkU4q zQ!ba+L`ZE&rEAlJkb`a8T^st2ER}AUSX`W^TrQH!&2j3~DYVk3ni}hGedA|udH|D? z)9(Pg)2YOx266xL^72~m69l#LX*&+_Xq>#i6#B5c__2~WolXbUYJf)Y{0lGQgq#QC z@#ybe0smfWZfP$&woNP+N6Oj@3fr<#O4HrljgVXfMmN#}a&r8fX>Mv9mgu+ADfdoW z%D;N!XK(8F-?(e%P3}s5Y5(@^+i=~)mD3v)_%t^+5ehj>PEGd#Ki*IR0FG_WM8ct) zrQrJjpV_@@?fULNe21ao;g0F)srfAfnFts>uQpu}LeSRHL3?`#=gyr+Sk`?bBO?nN zngj3oymYl2{{DME=$xINni$?XL?jemJGhcbmzI_mVzC$(FJ9!epT5DjzWI&E*@9qpO~ve|KC+tJP75BYwu+Zf{rAg>V8 z$0Q>oARsBk#K51QpL0Vfzrn@Oz_`D^#jLHb;Nape9uKFfs_X3Vq@}0G$j3%JEuy2P zp`)b8lVq^7wof1n($du8;^&&1oqBh7FE2EytFO(@&-d`_+uY!~y1AvNtABrgAR;fG zp``cs_$UnmzP`ZA%+M4E0?EqGSy^hv#>>T^UBkr4mog*O)6?_w_xSYkxw^jX@9{7z zC$_k|w6?h~5(O>}1YzNeJZ%feh;V#mkJa5fgSwY9{>$oc8p`1<=s zMM=B8zn5n`ySu;d@bamrs+J3>5d*HRvZsAg6$}X9;Nsri;ispo+uYvT+})Iw zm1<8nN=s9Wi;c<3%#VM3S6E!s%CFJT*W}~l^Yitps;V^>32-|Y$H>j=>+SXBxW&fH z#Ky`80st!~Am-=j^Y!?xuCVj-_~p^ODJwPF+uSe~2T&sn#l^&qa!jtSw49uuUoI4i zii(+fqFDYHMtLLLWvP z3aqZMs;sUK3=4@)E}58`T`CaV-QPhh9403-rKYJk6$aVc+}YXOWjGs1NKucDke!~P z5fBlao}gwg6424pii(TBzrUb}dCJSp-ppC=?(SG74uODz`~3aG#m3at+5i9m0ssI2 z0000000IC2A^8LW004aeEC2ui05AYB000R70Q(3WNU)$mfnBnUI9Tw`9flAiA~b>J zfi`RsncxtjOrk*(6ht^E`0C07O2&-M5)cswjtU+NW@tl151vW_O=Ph}3EU$}cRobH zd5}OY4FF;iJQs>Y4O4Ug00VW*kso`Jd^M70j~dZ}0BW(gr*vSv5_quHszoDAx-e2$ zNCAPO(Wg7JRE;F)000dJWeM&AMx!vDRZ_;WD8@ib-A~J^e9*90AQpO{@B|hJDN7w1 zhZ8R@#*W#$PcR^lno~*x!E3lGbS!IU5f)zQbV(_;cwDP6fI^75l>#y;2W!!R-DnBNd~M0Zz>NK%qwoK}<+jo*@~=q}Fvt3>L__ z5U6{IHg%ojgg!CUV8$%vU~|nGvPsn08mA!f2NKU{Q4Ax+P%%XS1@Pw|fK7zhgcI{9 zaECV3VB*ax64jH+DcM9wVFW}l!VWfv@WG*nzl4wv1Sp`;#5tY_bAtqTj6sh$3q|C{ z7h^;?Mg*F4(g_{bxPXX`9rpJNH`8E%!8yK=5==0FNFc_7Y{(&zD11P1$|>?_QYAiM zAd`R{T=F*$FRTy~j0vCUG0G?=93a6JnAmcJ50x<#k1QQzGekU9b^^u(aah7b0A;uV zMOGtA%&F8)zO(V9m&KsgLL;aE9E9j4f!LjV*^VS*Uk7|{j?DCHdECPa{&u*NX%te~Y1mDB+O2j-Lj zO&uwJGRPzlsL|XiJVXJ+7u3A+Yd%1@@&pf`{Bi>-8wj9)2m>%!&GMyB z!_5=xh;WY|X+R3mz8wt1MHq^JnhP*)5JN1cVTmc&p#_sLfr`=r0WvHv8bIJiDB^bu zRir@(%V9{ZyupP>)B#jIxWok#FbQHD41T0=#*%7wBpysd1xuJ9FG!G#7qsFl46z0j z^yh~Zl%WUy7qEmGCb0qtz@r5vAVvCGQG+JdKoTlY!3IK+i3KD>2;G1kM7qGQA-KZ_ zM(6<=Hed-1K4?WDl5vDy$g(UsSi}RQsRe5S$Szp`1p(*)3lh*l z03`k>L^+ZXOJ^3uMP77-9In}j5!68nVIaUHcp!!_$ewTtbVR2?7WJ F06UxK8a)62 literal 0 HcmV?d00001 diff --git a/camembert-before-ldap/img/root.gif b/camembert-before-ldap/img/root.gif new file mode 100644 index 0000000000000000000000000000000000000000..37bb04c691e1a42d12e805643c81889b0eb614a8 GIT binary patch literal 2467 zcmZwHeOOX=8wc>i;cz$_777{^>7hwtsG&)zr5!*774bEtGy@a^(^AZIElm{67noGG znWlb;EgzH+J+?O;c1n>3xR z7oEQpGIv@E9_x3>a@E%?0z zLQ@;uwqN?`mZTV~wFDPm1{{zR+Ye>bD8RG~1*Ec_V2Qeo^6EmUX*-dCYj3JKcu1Ss zwf4(z^DL?u6aTIHc4*vrSr`1l+sy;Bw8rp3V!|qq&g<6>kcK(DTN_MZN1C zxMcViupBqc9cI}@tfpQ3S(}n(vgbI}U$2i1Qar)B%_FYEk~+t+@LJELxtS-Y<{rr9 z{&I9O>9F&~nD*R{G-J^z@%;b+bLv6Is!P&}=|K{+v4 zBH&%O#=PR`#dhlXa;)o1pFI%CrLU2kWKBL>64|!_>k}F0I1E2|Ve9*;yr*j9Ow{AL(EGGEIX+fxpho{`1TZ00E!_l-V=aTS$%qrb-qL zFh_E5lRQALB>k|Knn|LpU4$ew-tGV2ABce%OGZfpA54@;i{D2tt(ER#X0`PwSS*m$2DO`kyFp3 z^qy!b39myWxfO6r)nYygRMKw8A`gOz==cNxNZ}KuU5Q+2fb*VL3OUaHxQ9Pb$FwVL zmD}A;gxu>VC4!GeUgwwAw z)qeSryBX`?q?Tm*k%fn41`a184)ScvIp~rd4bm-D@34OFV;d|c#|mFTgVV43`AnCu zalkhic_aqOT6lSz8C7W+NX$JXA{()EAf*G(5>TLgd@__=m(bq%Oc!|j8FXsywauC> z(c`^mwyPWqr}E!Qd+zQlue+Z5^z_(D!p<@PH07v( zys11lE^)1i+L|H|N$3=gjN_dlbt+t9j8YR<&37;Lcn6;MH!!&+ZmypTHC_BhfsltV za;aT+)>N_96Jj-9V0F#Aol675ZMx5C_R#(3BUx2p61?hJ_xi4`S|vtjpC6M=usutz zMc{S>FJ?f7!5^OQMGF_4DRvv(df&x`fL9yRB3yBK{;W!TUg}0(rU&>5%RStq%)?7k zgAZAf0>QNQBSgYt^phfh$F)=!iK+1{3Ll`u;UU}qF%He#3cjfM)Lemn#-?DjF;NM& z_>qzdO%ePZzUei-JSn!p!zQuUf|{nvi6q)+(+RvPrm)3+yj$A*0z{x2(FKz&M{Wy# z>v)}cGZHLyH*R=*Gus+Akg`n{O-oTwC{zV=O|=^rqPQ1f1fTu2nZbYy*w9eyl%9?Q zmJ#G32*Cr0YxCtKeO!w)c;UVBA)I$ye2qV&qQNNsNAyANXn5CQz#^DCmZVX;qlpK& zjD<<(xDOpfW>nODe(=Z5iw44S?C+ED!egVP-?tlWN(^II`!Zb$9{!X7s!%WHuMuSf zatYGa0fClBV>x%awDoCR#_z=hVytsUC=i;du1co>us2;B0J^!>=;SMTxrCKU1jZ*O zF>uB8xf!{5fwI%Xm6x>04Hm1I?%LJ4x{&w0@N7jm%~nJwne0pQRF_;iOCO1Y!UegulEdU6gFQ5it9qMXvB%BI-d5{HO?m0*f{ z0wNm()c!gX#E#AEWsqJ9`L2&l{cXn*&)?FO5*94P(?Mi*OZ?DKi6(D%IczWy*5;TI zwhsr2)i&<*^AR}{J$J)ApH&QH6u@Bwr^c^~IT^boFLWslG}pPPT!Swx%kvWRzUIEu z|E-^oTgV?s9GTNscyEO`>ISVUqtmQsGKnTCB)tYVPYcWK%M{DFn8Wr_1s22P>Z3qR zaOj2R>c9lrMz<#l-vCRF0oVK;U;oGV)1V|igj|IXjP&r%mNj_^ZnJyl`VxW_yAY== zypu)xF`GfFsHPBien{NUT4h-m=PX&OrHLZ7d{82(HWnhFuQ7RJT+bF9u+&q*1kB%8 z8!gcl9PF)&IetDcoE=+q))|ep>N0FLPlETA>$4;Lu_jKPo}Z>pNBy}h6hjLN3Vb$c vGYYgjl)7}Ew5{2wIA5K&EeMrG%e;lrQBk5eVK`rku8kL=(K6{e09f^Z)M8&k literal 0 HcmV?d00001 diff --git a/camembert-before-ldap/img/tresorier.gif b/camembert-before-ldap/img/tresorier.gif new file mode 100644 index 0000000000000000000000000000000000000000..d8ffc9ac0df2fed14d74447567250fbc034f3546 GIT binary patch literal 265 zcmV+k0rvh!Nk%w1VIcq^0HOc@0s;a8x&r=Y0{;F20%ii$)&4?4LI3~&A^8Le000L7 zEC2ui03iS&000Bdc)HyFFv>}*RRG)zO3B(5bwS{Lp}1s4>6&QDdGOf|_>j$|eda*W zgjD+hk2s-&SS%o)Cne_e3Otdo=nmkbUXQjcK?5+DU0TL-|~i z`;Sd61q66oZ)SdpI4xBsZH0PpE{T*iYygCqh9z*6i3VMFnwoigpnaZl0g{<`g>IaP zsRE~~8LyCf8I!ZCXRVmI3?Q?#Z?~g)BLJzspum|G#D28~$ZaASs;t5)Ajg_258u(^ P%QEKa>g(+73kd)_L)&uz literal 0 HcmV?d00001 diff --git a/camembert-before-ldap/inc/inc.body.php b/camembert-before-ldap/inc/inc.body.php new file mode 100644 index 0000000..1bfc07c --- /dev/null +++ b/camembert-before-ldap/inc/inc.body.php @@ -0,0 +1,54 @@ + + + + + + + + + + + +
+ Accueil + + +
+ + + + + + + + +
+
+
diff --git a/camembert-before-ldap/inc/inc.db.php b/camembert-before-ldap/inc/inc.db.php new file mode 100644 index 0000000..8e7e646 --- /dev/null +++ b/camembert-before-ldap/inc/inc.db.php @@ -0,0 +1,15 @@ + +
+
+ + + diff --git a/camembert-before-ldap/inc/inc.header.php b/camembert-before-ldap/inc/inc.header.php new file mode 100644 index 0000000..bc26980 --- /dev/null +++ b/camembert-before-ldap/inc/inc.header.php @@ -0,0 +1,4 @@ + diff --git a/camembert-before-ldap/inc/inc.header2.php b/camembert-before-ldap/inc/inc.header2.php new file mode 100644 index 0000000..894b7d3 --- /dev/null +++ b/camembert-before-ldap/inc/inc.header2.php @@ -0,0 +1,16 @@ + + + + Camembert :: <?php echo $page_name; ?> + + + + + + + + + + + + diff --git a/camembert-before-ldap/inc/libdate.php b/camembert-before-ldap/inc/libdate.php new file mode 100644 index 0000000..0c787d3 --- /dev/null +++ b/camembert-before-ldap/inc/libdate.php @@ -0,0 +1,44 @@ + + diff --git a/camembert-before-ldap/inc/libsql.php b/camembert-before-ldap/inc/libsql.php new file mode 100644 index 0000000..8de028c --- /dev/null +++ b/camembert-before-ldap/inc/libsql.php @@ -0,0 +1,68 @@ +Utilisateur ".$a['nom']." ".$a['prenom']." supprimé (ainsi que toutes les machines associées)

\n"; + } +} + +function UpdateInterfaceForUser($iduser) { + $r = pg_query("SELECT nom, prenom, datedeco, certif, name, idinterface FROM room r, user_pac u WHERE u.idroom = r.idroom AND iduser = $iduser"); + $a = pg_fetch_array($r); + + $r = pg_query("SELECT MAX(idaction) FROM action"); + if($a2 = pg_fetch_array($r)) + $newid = $a2[0]+1; + else + $newid = 1; + + $ddeco = date("M Y", strtotime($a['datedeco'])); + $desc = iconv("UTF-8", "ASCII//TRANSLIT", "Chambre ".$a['name']." ".$a['nom']." ".$a['prenom']." $ddeco"); + if((date("n") < 9 || date("n") > 10) && $a['certif'] != 't') { // TODO: vérifier la date de deco + $desc .= " CERTIF"; + pg_query("INSERT INTO action VALUES($newid, ".$a['idinterface'].", 7, 3)"); + } + else { + pg_query("INSERT INTO action VALUES($newid, ".$a['idinterface'].", 7, 964)"); + $newid++; + pg_query("INSERT INTO action(idaction, idinterface, numaction) VALUES($newid, ".$a['idinterface'].", 1)"); + } + $newid++; + pg_query("INSERT INTO action(idaction, idinterface, numaction, option) VALUES($newid, ".$a['idinterface'].", 2, '$desc')"); +} + +?> diff --git a/camembert-before-ldap/inc/roles.php b/camembert-before-ldap/inc/roles.php new file mode 100644 index 0000000..95a2818 --- /dev/null +++ b/camembert-before-ldap/inc/roles.php @@ -0,0 +1,71 @@ +Erreur: impossible de se connecter à la base de données

"; + include "inc.footer.php"; + exit(); +} + +$roles = array(); + +# à décommenter si en maintenance : +#### +#if($user->name !== 'yohan' && $user->name !== 'Souli') { +# include "maintenance.php"; +# die(); +#} +#### + +function get_roles($login, &$roles) { +$r = pg_query("SELECT roles FROM groupe g, cam_user u WHERE groupe = idgroupe AND login = '$login'"); +if(($a = pg_fetch_array($r)) === false) { + include "denied.php"; + die(); +} + +$rolesmask = $a['roles']; + +$r = pg_query("SELECT idrole, name FROM role"); +while($a = pg_fetch_array($r)) { + $roles[$a['name']] = (($rolesmask & (1 << $a['idrole'])) > 0); +} + +if(array_key_exists('root', $roles) && $roles['root']) + foreach($roles as $k => $v) + $roles[$k] = true; +} + +get_roles($drupal_user, $roles); +global $auth_user; +$auth_user = $drupal_user; +?> diff --git a/camembert-before-ldap/inc/roles.php-apache b/camembert-before-ldap/inc/roles.php-apache new file mode 100644 index 0000000..8c52768 --- /dev/null +++ b/camembert-before-ldap/inc/roles.php-apache @@ -0,0 +1,33 @@ +Erreur: impossible de se connecter à la base de données

"; + include "inc.footer.php"; + exit(); +} + +$roles = array(); + +function get_roles($login, &$roles) { +$r = pg_query("SELECT roles FROM groupe g, cam_user u WHERE groupe = idgroupe AND login = '$login'"); +if(($a = pg_fetch_array($r)) === false) { + include "denied.php"; + die(); +} + +$rolesmask = $a['roles']; + +$r = pg_query("SELECT idrole, name FROM role"); +while($a = pg_fetch_array($r)) { + $roles[$a['name']] = (($rolesmask & (1 << $a['idrole'])) > 0); +} + +if(array_key_exists('root', $roles) && $roles['root']) + foreach($roles as $k => $v) + $roles[$k] = true; +} + +get_roles($_SERVER['PHP_AUTH_USER'], $roles); +?> diff --git a/camembert-before-ldap/index.php b/camembert-before-ldap/index.php new file mode 100644 index 0000000..0e6d843 --- /dev/null +++ b/camembert-before-ldap/index.php @@ -0,0 +1,61 @@ +"); + // TODO : trouver et afficher les anomalies + $r = pg_query("SELECT idinterface, ifname, ifdescription, ifadminstatus, ifoperstatus, + ifvlan, ifvoicevlan, portsecenable, portsecstatus, i.idmateriel AS idm, hostname FROM interface i, materiel m WHERE i.idmateriel = m.idmateriel ORDER BY ifnumber"); + fputs($fp, "
Interfaces en ERR-DIS :
    \n"); + while($a = pg_fetch_array($r)) { + if ($errdisable = (($a[7] == 't') && ($a[8] == 3) && (! $a[3]==0))) + { + fputs($fp, "
  • ${a[10]} / ${a[1]}
  • \n"); + } + } + fputs($fp, "
Incohérence DHCP-ARP :
    \n"); + // pg_query arpcache and compare with dhcp entries + + fputs($fp, "
Incohérence DHCP-Sticky :
    \n"); + // vérifions que les stickies sont tous sur le DHCP + $r = pg_query("SELECT mac, type FROM fdb WHERE type <> 0 and datelast = (SELECT MAX(datelast) FROM fdb)"); + while($a = pg_fetch_array($r)) { + $a2 = SearchMACinDHCP($a[0]); + if(! $a2) { + fputs($fp, "
  • ".$a[0]."
  • \n"); + } + } + fputs($fp, "
\n"); + fclose ($fp); +} +?> + + +

Rapport d'anomalies :

+ 600) +{ + report(); + echo ""; +} + +include "inc/inc.footer.php"; ?> diff --git a/camembert-before-ldap/interface.php b/camembert-before-ldap/interface.php new file mode 100644 index 0000000..97d5245 --- /dev/null +++ b/camembert-before-ldap/interface.php @@ -0,0 +1,351 @@ +Aucune interface ne correspond à cet identifiant

\n"; + include "inc/inc.footer.php"; + exit(); +} + +if($_POST['num'] != "") { + $r = pg_query("SELECT MAX(idaction) FROM action"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + $num = pg_escape_string($_POST['num']); + $opt = iconv("UTF-8", "ASCII//TRANSLIT", $_POST['opt']); + if($num == -1) { + pg_query("INSERT INTO action VALUES($newid, $id, 0, '$opt')"); + pg_query("INSERT INTO action VALUES($newid+1, $id, 1, '$opt')"); + } + elseif($num == 73) { + pg_query("INSERT INTO action VALUES($newid, $id, 7, 3)"); + } + elseif($num == 7964) { + pg_query("INSERT INTO action VALUES($newid, $id, 7, 964)"); + } + else { + pg_query("INSERT INTO action VALUES($newid, $id, $num, '$opt')"); + } + // Logging actions + $r = pg_query("SELECT MAX(idlog) FROM action_log"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + if($num == -1) { + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 0, ${a[7]})"); + pg_query("INSERT INTO action_log VALUES($newid+1, '$auth_user', ".time().", $id, 1, ${a[7]})"); + } + elseif($num == 73) { + $oldopt = $a[10]; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 7, '$oldopt', 3)"); + } + elseif($num == 7964) { + $oldopt = $a[10]; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 7, '$oldopt', 964)"); + } + else { + switch($num) { + case 0: + case 1: $oldopt = $a[7]; break; + case 2: $oldopt = $a[4]; break; + case 3: $oldopt = $a[19]; break; + default: $oldopt = ''; break; + } + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, $num, '$oldopt', '$opt')"); + } +} + +function DisplayActions() { + global $id; + + $r = pg_query("SELECT numaction, option FROM action WHERE idinterface = $id ORDER BY idaction"); + if($a = pg_fetch_array($r)) { +?> +

Prochaines actions

+
    +$line\n"; + $a = pg_fetch_array($r); + } +?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+
+
+
+
+
Description
Nombre de MACs autorisées
Sticky +
+ +'Other', 6=>'Ethernet CSMA/CD', 9=>'Token ring', 15=>'FDDI', 18=>'DSL', 19=>'DSL', 20=>'ISDN', + 21=>'ISDN', 22=>'Série', 23=>'PPP', 24=>'Loopback', 28=>'SLIP', 32=>'Frame relay', 53=>'Virtual', 71=>'802.11', + 117=>'Ethernet Gigabit', 135=>'VLAN'); + +$rroom = pg_query("SELECT idroom, name FROM room WHERE idinterface = '$id'"); +if($aroom = pg_fetch_array($rroom)) +{ +?> +

Chambre "; ?>

+ +

/".$a[3]; ?>

+

Les modifications effectuées ici prendront effet dans un délai maximum de 10 minutes.

+Cette interface est en isolement. Il y a surement une raison à cela…";} + //empêche toutes les actions sauf sur les chambres correctement inscrites + $forbidden = 1; + if (! $not_a_room) { + $r2 = pg_query("SELECT idroom, datedeco, certif, iduser FROM user_pac WHERE idroom = ".$aroom[0]); + if($a2 = pg_fetch_array($r2)) { + if(strtotime(date("Y-m", strtotime($a2['datedeco']." +1 month"))) < time()) { + echo "

L'adhésion n'a pas été payée.

"; + } + else if((date("n") < 9 || date("n") > 10) && $a2['certif'] != 't') { + echo "

Certificat non rendu

"; + } + else { + $forbidden = 0; + } + } + else { + echo "

Chambre vide

"; + } + } + if($a[17] == 't' && $a[21] != 0 && ($not_a_room || (! $forbidden))) { + echo "

Interface en violation

"; + echo "
Adresse MAC ayant causé la violation : ".$a[22]."
"; + if (! $not_a_room) { + $r3 = pg_query("SELECT iduser FROM user_pac WHERE idroom = ".$aroom[0]); + $a3 = pg_fetch_array($r3); + echo "
  • Vérifiez qu'il s'agit bien de l'adhérent inscrit à cette chambre.
  • "; + echo "
  • Si l'adhérent a changé d'ordinateur ou si l'adresse MAC était fausse, supprimez le sticky correspondant, redémarrez l'interface et n'oubliez pas de modifier la liste des machines.
  • "; + if(! $roles['inscription']) + echo "
  • Si l'adhérent a un ordinateur supplèmentaire à ajouter, redirigez le vers un membre CA.
  • "; + echo "
  • S'il s'agissait simplement d'une erreur, supprimez le sticky correspondant et redémarrez l'interface.
"; + } + } + elseif ($not_a_room || (! $forbidden)) { + switch($a[7]==0?"SHUT":($a[8]==1?"UP":"DOWN")) { + case "SHUT": echo "

Interface désactivée

"; + echo "Cette interface est désactivée pour une raison. Ne la rallumez pas si le problème n'est pas réglé."; + break; + case "DOWN": echo "

Interface éteinte

"; + echo "Aucun ordinateur ne semble connecté sur cette interface en ce moment."; + break; + case "UP": echo "

Interface active

"; + echo "Un ordinateur est connecté sur cette interface en ce moment."; + break; + } + } + +?> + +
+ +
+ + + +
+ + + + + + + + + + + +\n"; + if($voice != "") + echo "\n"; +?> + +
ifNumber
ifAddress
ifType
  
Description
Etat
VitesseM
VLAN
Native VLAN${a[12]}
Voice VLAN$voice
SpanningTree Portfast
+
+ +

Port Security

+ + + + + + + + + + + +
  
Activé
Etat
Nb de MAC max
Nb de MAC actuel
Nb de violations
Dernière MAC  (00:00:00:00:00:00 peut être normal)
Sticky activé
+ 0 and datelast = (SELECT MAX(datelast) FROM fdb)"); + if($a = pg_fetch_array($r)) { +?> +

Stickies

+
    +".$a[0].($a[1]==1?" (static)":"")."\n"; + $a = pg_fetch_array($r); + } +?> +
+ +
+
+ + détails techniques + + +

Matéreil connecté sur cette interface

+
    +".($a[3]==$datelast?"":"")."${a[1]} / ${a[2]}"; + echo ($a[3]==$datelast?"":"")." (".date("D d/m/Y H:i", $a[3]).")\n"; + $a = pg_fetch_array($r); + } + } +?> +
+ diff --git a/camembert-before-ldap/interface.php-bak b/camembert-before-ldap/interface.php-bak new file mode 100644 index 0000000..864cfd9 --- /dev/null +++ b/camembert-before-ldap/interface.php-bak @@ -0,0 +1,295 @@ +Aucune interface ne correspond à cet identifiant

\n"; + include "inc/inc.footer.php"; + exit(); +} + +if($mode == "conf" && $_POST['num'] != "") { + $r = pg_query("SELECT MAX(idaction) FROM action"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + $num = pg_escape_string($_POST['num']); + $opt = iconv("UTF-8", "ASCII//TRANSLIT", $_POST['opt']); + if($num == -1) { + pg_query("INSERT INTO action VALUES($newid, $id, 0, '$opt')"); + pg_query("INSERT INTO action VALUES($newid+1, $id, 1, '$opt')"); + } + elseif($num == 73) { + pg_query("INSERT INTO action VALUES($newid, $id, 7, 3)"); + } + elseif($num == 7964) { + pg_query("INSERT INTO action VALUES($newid, $id, 7, 964)"); + } + else { + pg_query("INSERT INTO action VALUES($newid, $id, $num, '$opt')"); + } + // Logging actions + $r = pg_query("SELECT MAX(idlog) FROM action_log"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + if($num == -1) { + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 0, ${a[7]})"); + pg_query("INSERT INTO action_log VALUES($newid+1, '$auth_user', ".time().", $id, 1, ${a[7]})"); + } + elseif($num == 73) { + $oldopt = $a[10]; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 7, '$oldopt', 3)"); + } + elseif($num == 7964) { + $oldopt = $a[10]; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 7, '$oldopt', 964)"); + } + else { + switch($num) { + case 0: + case 1: $oldopt = $a[7]; break; + case 2: $oldopt = $a[4]; break; + case 3: $oldopt = $a[19]; break; + default: $oldopt = ''; break; + } + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, $num, '$oldopt', '$opt')"); + } +} + +function DisplayActions() { + global $id; + + $r = pg_query("SELECT numaction, option FROM action WHERE idinterface = $id ORDER BY idaction"); + if($a = pg_fetch_array($r)) { +?> +

Prochaines actions

+
    +$line\n"; + $a = pg_fetch_array($r); + } +?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+
+
+
+
+
Description
Nombre de MACs autorisées
Sticky +
+
+'Other', 6=>'Ethernet CSMA/CD', 9=>'Token ring', 15=>'FDDI', 18=>'DSL', 19=>'DSL', 20=>'ISDN', + 21=>'ISDN', 22=>'Série', 23=>'PPP', 24=>'Loopback', 28=>'SLIP', 32=>'Frame relay', 53=>'Virtual', 71=>'802.11', + 117=>'Ethernet Gigabit', 135=>'VLAN'); + +$rroom = pg_query("SELECT idroom, name FROM room WHERE idinterface = '$id'"); +if($aroom = pg_fetch_array($rroom)) +{ +?> +

Chambre "; ?>

+ +

/".$a[3]; ?>

+
+ + + + + +
+ + + + + + + + + + En isolement"; + if($a[11] != 0 && $a[11] != 4096) $voice = $a[11]; + else $voice = ""; + break; + } +?> + +\n"; + if($voice != "") + echo "\n"; +?> + +
ifNumber
ifAddress
ifType
  
Description
Etat
VitesseM
VLAN
Native VLAN${a[12]}
Voice VLAN$voice
SpanningTree Portfast
+ + [Configurer] + +
+ +

Port Security

+ + + + + + + + + + + +
  
Activé
Etat
Nb de MAC max
Nb de MAC actuel
Nb de violations
Dernière MAC  (00:00:00:00:00:00 peut être normal)
Sticky activé
+ 0 and datelast = (SELECT MAX(datelast) FROM fdb)"); + if($a = pg_fetch_array($r)) { +?> +

Stickies

+
    +".$a[0].($a[1]==1?" (static)":"")."\n"; + $a = pg_fetch_array($r); + } +?> +
+ +
+ +
+ + +

Matéreil connecté sur cette interface

+
+ diff --git a/camembert-before-ldap/libfind.php b/camembert-before-ldap/libfind.php new file mode 100644 index 0000000..f3e3fef --- /dev/null +++ b/camembert-before-ldap/libfind.php @@ -0,0 +1,59 @@ +1 || ((strlen($m1[0])<=2) && (count($m2)==1))) { + // format XX:XX:XX:XX:XX:XX => OK + return $mac; + } + + if (count($m3)>1 && count($m1)==1 && count($m2)==1) { + // format XX-XX-XX-XX-XX-XX => convertir + return (str_replace('-', ':', $mac)); + } + + if (count($m1)>1 || ((strlen($m1[0])<=4) && (count($m1)==1))) { + // format XXXX.XXXX.XXXX => convertir + $result = ''; $curr = 0; + for ($i=0; $i convertir + $result = $mac[0].$mac[1].':'.$mac[2].$mac[3].':'; + $result .= $mac[4].$mac[5].':'.$mac[6].$mac[7].':'; + $result .= $mac[8].$mac[9].':'.$mac[10].$mac[11]; + + return ($result); + } + + return ''; +} + +?> diff --git a/camembert-before-ldap/list.php b/camembert-before-ldap/list.php new file mode 100644 index 0000000..1fb4a34 --- /dev/null +++ b/camembert-before-ldap/list.php @@ -0,0 +1,163 @@ + + +
+
+\n"; + else + echo "Filtrer par VLAN : \n"; +?> + +
+
+
+ + +

Matériel manageable

+
+0 AND main = 't' + AND (capabilities & 128) <> 128 "; + if($filter != "") + $q .= "AND hostname ILIKE '%$filter%' "; + $q .= "ORDER BY $order"; + $r = pg_query($q); + DispMatList($r); +?> +
+ +

Matériel non manageable

+
+ 128 AND hostname NOT LIKE 'SEP%' "; + if($filter != "") + $q .= "AND hostname ILIKE '%$filter%' "; + $q .= "ORDER BY $order"; + $r = pg_query($q); + DispMatList($r); +?> +
+ + +

Téléphones

+
+ +
+ +

Boitiers ATA

+
+ 128 AND m.hostname LIKE 'SEP%' "; + if($vlan != "") + $q .= "AND ifvoicevlan = $vlan "; + $q .= "ORDER BY $order"; + $r = pg_query($q); + DispMatList($r); +?> +
+ + + + + + + + + +\n"; + + $res = pg_query("SELECT MAX(datelast) FROM materiel"); + $a = pg_fetch_array($res); + $datelast = $a[0]; + + $i = 0; + $bline = false; + $added = array(); + while($a = pg_fetch_array($r)) { + if(array_search($a[0], $added) !== false) + continue; + + array_push($added, $a[0]); + + if($a[5] < $datelast) $class = "red"; + else if($a[3] == 2) $class = "blue"; + else $class = "normal"; + if($bline) $class .= "2"; + + if($mode == "phone") $proto = "http"; + else $proto = "telnet"; + + echo ""; + echo ""; + + if($mode == "phone") { + echo ""; + echo ""; + } + + echo "\n"; + + $i++; + $bline = !$bline; + } + echo "
IP Hostname Type VLANConnecté à
${a[4]}${a[1]}${a[2]}${a[6]}${a[8]} / ${a[9]}
\n"; + echo "

$i équipements trouvés.

\n"; +} + +?> diff --git a/camembert-before-ldap/logs.php b/camembert-before-ldap/logs.php new file mode 100644 index 0000000..0ddec23 --- /dev/null +++ b/camembert-before-ldap/logs.php @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + +\n"; + echo "\n"; +} +?> + +
IdDateUtilisateurUserHostnameInterfaceActionOptionAncienne valeurIduserMontant
${a[0]}".date("D d/m/Y H:i", $a[1])."".$a2['prenom']." ".$a2['nom']."${a[2]}${a[4]}${a[6]}$action$opt$oldopt".$a[10]."".$a[11]."
+
+ + + + diff --git a/camembert-before-ldap/maintenance.php b/camembert-before-ldap/maintenance.php new file mode 100644 index 0000000..0827e7e --- /dev/null +++ b/camembert-before-ldap/maintenance.php @@ -0,0 +1,37 @@ + + + Camembert :: Accès refusé + + + + +

Camembert est en maintenance.

+

Accès refusé

+
+ +

Vous n'êtes pas autorisé à accéder à cette page.

+ +
+ + + + diff --git a/camembert-before-ldap/materiel.php b/camembert-before-ldap/materiel.php new file mode 100644 index 0000000..49b9866 --- /dev/null +++ b/camembert-before-ldap/materiel.php @@ -0,0 +1,328 @@ +Aucun équipement ne correspond à cet identifiant

\n"; + include "inc/inc.footer.php"; + exit(); +} + +$action = $_GET['action']; +$confirm = $_GET['confirm']; +$last = ($_GET['last']!=""?$_GET['last']:3); + +$r = pg_query("SELECT MAX(datelast) FROM link"); +$lastlink = pg_fetch_array($r); +$lastlink = $lastlink[0]; + +$r = pg_query("SELECT MAX(datelast) FROM materiel"); +$datelast = pg_fetch_array($r); +$datelast = $datelast[0]; + +if($action == "") { + DisplayInfos($a); + DisplayIPList(); + DisplayLinkedFrom(); + + if($mana > 0) { + if($mana == 2) + echo "

Ce matériel n'est plus manageable. Les informations sont suceptibles de ne pas etre à jour.

"; + DisplayInterfaces(); + DisplayVLANList(); + } + else + echo "

Ce matériel n'est pas manageable.

"; +} +else if($action == "del") { + if($confirm == 1) + DeleteMateriel(); + else + DisplayFormDelete($a); +} +else if($action == "mac") { + DisplayInfos($a); + if($mana > 0) { + if($mana == 2) + echo "

Ce matériel n'est plus manageable. Les informations sont suceptibles de ne pas etre à jour.

"; + DisplayForwardingDatabase(); + } + else + echo "

Ce matériel n'est pas manageable.

"; +} + +// --------------------------------- +// Blabla fonctions +// --------------------------------- + +function DisplayInfos($a) { + global $id, $mana, $datelast, $roles; +?> +

+
+ + + +
+ + + + + +
Type :
OS :", $a[2]); ?>
Statut : +DOWN\n"; + else { + echo "UP "; + if($a[3] == 2) echo "anciennement manageable"; + else if($a[3] == 0) echo "non manageable"; + } + + if(substr($a[0], 0, 3)=='SEP') + $link = "phones.php?filter=${a[0]}"; + else { + if($mana == 0) + $link = "materiel_nomana.php?filter=${a[0]}"; + else + $link = "materiel_mana.php?filter=${a[0]}"; + } +?> +
Trouvé le :
Mis à jour :
+ +
+ +

Liste des IPs

+
    +$tmp"; + else $tmp = "$tmp"; + if($a[1] == 't') $tmp = "$tmp"; + echo "
  • $tmp
  • \n"; + } + echo "
\n"; +} + +function DisplayLinkedFrom() { + global $id, $lastlink; +?> +

Trouvé sur

+
+ + +"; + echo "\n"; + } +?> +
HostnameInterfaceVLANVoiceVu la dernière fois
${a[1]}${a[2]}$vlan$voice".date("D d/m/Y H:i", $a[3])."
+
+ +

Liste des interfaces physiques

+
+ + + +"; + echo ""; + echo "\n"; + } +?> +
NomDescriptionEtatVLANVoiceConnecté à
${a[1]}${a[2]}".($a[3]==0?"SHUT":($errdisable?"ERR-DIS":($a[4]==1?"UP":"DOWN")))."$vlan$voice"; + $r2 = pg_query("SELECT idmateriel, hostname, dstifname FROM materiel, link WHERE idmateriel=iddstmateriel AND idinterface=${a[0]} AND link.datelast=$lastlink"); + $bfirst = true; + while($arr = pg_fetch_array($r2)) { + echo "".($bfirst?"":"
")."".$arr[1]." / ".$arr[2]; + $bfirst = false; + } + echo "
+
+ +

Liste des interfaces virtuelles

+
+ + +"; + echo "\n"; + } +?> +
NomDescriptionEtat
${a[1]}${a[2]}".($a[3]==2?"SHUT":($a[4]==1?"UP":"DOWN"))."
+
+ +

Table de forwarding MAC

+
+
+

Afficher les MACs enregistrées depuis jours. + +

+
+ + + + ".(time()-$datelast)." + ORDER BY ifnumber, datelast DESC, vlan, mac"); + + $lastid = 0; + $bline = false; + while($a = pg_fetch_array($r)) { + $class = "normal".($bline?"2":""); + $bline = !$bline; + + if($lastid != $a[0]) { + $lastid = $a[0]; + $ifname = $a[1]; + } + else + $ifname = ""; + if($a[2] != 0) + $vlan = $a[2]; + else + $vlan = ""; + switch($a[6]) { + case 0: $type = "FDB"; break; + case 1: $type = "static"; break; + case 2: $type = "sticky"; break; + default: $type = ""; + } + + echo "\n"; + } +?> +
InterfaceVLANMACTypeIPDate enrDate MàJ
$ifname$vlan${a[3]}$type"; + $r2 = pg_query("SELECT ip FROM arpcache WHERE mac = '${a[3]}' AND datelast = ${a[5]}"); + if($a2 = pg_fetch_array($r2)) + echo $a2[0]; + echo "".date("d/m/Y H:i", $a[4])."".date("d/m/Y H:i", $a[5])."
+
+ +
+

Etes vous sur de vouloir supprimer ?
+ [Oui] + [Non]

+
+ +
+

Equipement supprimé.
+ Retourner à la liste du matériel

+
+ diff --git a/camembert-before-ldap/myinfo.php b/camembert-before-ldap/myinfo.php new file mode 100644 index 0000000..da25773 --- /dev/null +++ b/camembert-before-ldap/myinfo.php @@ -0,0 +1,33 @@ + + + + + + + + + + + + +
+ Accueil + + +
+
+
+ +

Infos :

+ diff --git a/camembert-before-ldap/page_blocage_AG_PacatNet/index.html b/camembert-before-ldap/page_blocage_AG_PacatNet/index.html new file mode 100644 index 0000000..72c3d9c --- /dev/null +++ b/camembert-before-ldap/page_blocage_AG_PacatNet/index.html @@ -0,0 +1,45 @@ + + + + + + +Assemblée générale Pac@Net + + +

À 20h : Assemblée Générale

+
  +

Votre présence est nécessaire !

+

À l'ordre du jour :

+
+
  • + Bilan moral et financier de l'année 2011-2012, +
  • Élection de la nouvelle liste du Conseil d'Administration.
+
+

Pour des raisons légales, il est obligatoire de réunir un quorum. Afin de nous assurer de votre présence, nous sommes donc contraints de couper Internet à partir de 19h30 et durant toute la réunion.

Nous nous excusons de la gène occasionnée.

diff --git a/camembert-before-ldap/page_blocage_AG_PacatNet/logoPacatnet2.png b/camembert-before-ldap/page_blocage_AG_PacatNet/logoPacatnet2.png new file mode 100644 index 0000000000000000000000000000000000000000..b0c043bfa2b6625968282f66e773654392dc0514 GIT binary patch literal 224760 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 Rn*J-1mHZ}ADQ4*R{{i0$gw6l} literal 0 HcmV?d00001 diff --git a/camembert-before-ldap/pushbutton b/camembert-before-ldap/pushbutton new file mode 120000 index 0000000..d6bf6d5 --- /dev/null +++ b/camembert-before-ldap/pushbutton @@ -0,0 +1 @@ +/usr/share/drupal6/themes/pushbutton \ No newline at end of file diff --git a/camembert-before-ldap/redirect.php b/camembert-before-ldap/redirect.php new file mode 100644 index 0000000..2655eb6 --- /dev/null +++ b/camembert-before-ldap/redirect.php @@ -0,0 +1,18 @@ + + + Camembert :: Accès refusé + + + +

Camembert

+

Accès refusé

+
+ +

Vous n'êtes pas autorisé à accéder à cette page.

+ +
+ + + + diff --git a/camembert-before-ldap/roles_edit.php b/camembert-before-ldap/roles_edit.php new file mode 100644 index 0000000..556ca71 --- /dev/null +++ b/camembert-before-ldap/roles_edit.php @@ -0,0 +1,51 @@ +Il y a deja un role avec ce nom

\n"; + break; + } + $r = pg_query("SELECT MAX(idrole) FROM role"); + $a = pg_fetch_array($r); + if($a !== false) + $newid = $a[0]+1; + else + $newid = 0; + + $r = pg_query("INSERT INTO role(idrole, name) VALUES($newid, '".$_GET['name']."')"); + } +} while(false); + +$r = pg_query("SELECT idrole, name FROM role"); +if($a = pg_fetch_array($r)) { +?> + +\n"; + $a = pg_fetch_array($r); + } + echo "
idnom
".$a['idrole']."".$a['name']."
\n"; +} +?> +

+ + + +

+ diff --git a/camembert-before-ldap/room.php b/camembert-before-ldap/room.php new file mode 100644 index 0000000..9e29599 --- /dev/null +++ b/camembert-before-ldap/room.php @@ -0,0 +1,312 @@ +"; + $count_root ++; + break; + case 3: + $room['img'] = ""; + $count_admin ++; + break; + case 4: + $room['img'] = ""; + $count_membre_ca ++; + break; + case 5: + $room['img'] = ""; + $count_tresorier ++; + break; + default: + $room['img'] = ""; + break; + } + } + if($a['special_case'] == 't') { + $room['special_case'] = "*"; + $count_special_case ++; + } + else { + $room['special_case'] = ""; + } + if(strtotime(date("Y-m", strtotime($a['datedeco']." +1 month"))) < time()) { + $room['class'] = "red"; + $count_datedeco ++; + } + else if((date("n") < 9 || date("n") > 10) && $a['certif'] != 't') { + $room['class'] = "orange"; + $count_no_cert ++; + } + else { + $room['class'] = "green"; + $count_green ++; + } + } + else { + $count_empty ++; + } + + switch($room['name'][0]) { + case 0: + case '0': + $floor0[] = $room; + break; + case 1: + case '1': + $floor1[] = $room; + break; + case 2: + case '2': + $floor2[] = $room; + break; + case 3: + case '3': + $floor3[] = $room; + break; + case 4: + case '4': + $floor4[] = $room; + break; + default: + $floor0[] = $room; + break; + } + } +?> + + +"; + echo ""; + echo ""; + echo ""; + echo ""; + echo "\n"; + $bline = !$bline; + } +?> +
rdc1er2e3e4e
".$floor0[$i]['name'].($floor0[$i]['special_case']).$floor0[$i]['img']."".$floor1[$i]['name'].($floor1[$i]['special_case']).$floor1[$i]['img']."".$floor2[$i]['name'].($floor2[$i]['special_case']).$floor2[$i]['img']."".$floor3[$i]['name'].($floor3[$i]['special_case']).$floor3[$i]['img']."".$floor4[$i]['name'].($floor4[$i]['special_case']).$floor4[$i]['img']."
rdc1er2e3e4e
+Liste des $count_all_rooms chambres\n"; + + // Tables dans une table, c'est pas beau, comme le PHP et l'HTML +?> + + +
SudNord
+ +
+ + + + + + + + + + + +
Légende
Chambre connectée
Date de fin de connexion passée
Certificat non rendu
Chambre vide
*Cas particulier
Responsable réseau
Admin
Membre CA
Trésorier et président
+
+Chambre ".$a['name']."\n
"; + $r = pg_query("SELECT iduser, nom, prenom FROM user_pac WHERE idroom = ".$a['idroom']); + if($a2 = pg_fetch_array($r)) + echo "

Occupée par ".$a2['nom']." ".$a2['prenom'].""; + else + echo "

Vide"; + if($roles['inscription']) + echo "
\n[Nouveau]"; + echo "

"; + + return true; + } + else { + echo "

Erreur : cet id n'existe pas

\n"; + return false; + } +} + +function DisplayRoomInterface($id) { + global $roles; + + $r = pg_query("SELECT idroom, idinterface, name FROM room WHERE idroom = ".$id); + $a = pg_fetch_array($r); + $r = pg_query("SELECT ifname, i.idmateriel AS idm, hostname FROM interface i, materiel m WHERE i.idmateriel = m.idmateriel AND idinterface = ".$a['idinterface']); + $a2 = pg_fetch_array($r); + echo "

Sur l'interface ".$a2['hostname']." / "; + echo "".$a2['ifname'].""; + if($roles['root']) + echo " - [Changer]"; + if($roles['inscription']) + echo " - [Déménager]"; + echo "

\n"; +} + +function DisplayInterfaceForm($id) { + global $roles; + + if(!$roles['root']) + return; + + $r = pg_query("SELECT idroom, idinterface, name FROM room WHERE idroom = ".$id); + $a = pg_fetch_array($r); + if(isset($_GET['idint'])) { + pg_query("UPDATE room SET idinterface = ".$_GET['idint']." WHERE idroom = ".$a['idroom']); + DisplayRoomInterface($id); + } + else { + $r = pg_query("SELECT idinterface, ifname, m.idmateriel AS idm, hostname + FROM interface i, materiel m + WHERE i.idmateriel = m.idmateriel + ORDER BY m.idmateriel, ifnumber"); + $last_id = 0; + echo "

Sur l'interface "; + echo "

\n"; + } +} + +function DisplayRoomForm($id) { + global $roles; + global $auth_user; + + if(!$roles['inscription']) + return; + + $r = pg_query("SELECT idroom, idinterface, name FROM room WHERE idroom = ".$id); + $a = pg_fetch_array($r); + if(isset($_GET['idr'])) { + RemoveUser($_GET['idr']); + pg_query("UPDATE user_pac SET idroom = ".$_GET['idr']." WHERE idroom = ".$a['idroom']); + // récupérer les infos de l'interface de idroom + $r2 = pg_query("SELECT ifdescription, portsecmaxmac FROM interface WHERE idinterface = (SELECT idinterface FROM room WHERE idroom = ".$a['idroom'].")"); + $a2 = pg_fetch_array($r2); + // effacer l'interface de idroom (mais pas les ordi ni l'utilisateur) + RemoveInterface($a['idroom']); + // configurer l'interface de idr + $r3 = pg_query("SELECT iduser FROM user_pac WHERE idroom = ".$_GET['idr']); + $a3 = pg_fetch_array($r3); + UpdateInterfaceForUser($a3['iduser']); + // configurer a2 cf $mode == "conf" au début de interface.php + $r = pg_query("SELECT MAX(idaction) FROM action"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + $r4 = pg_query("SELECT idinterface FROM room WHERE idroom = ".$_GET['idr']); + $a4 = pg_fetch_array($r4); + $idint = $a4['idinterface']; + $opt = $a2['portsecmaxmac']; + pg_query("INSERT INTO action VALUES($newid, $idint, 3, $opt)"); + // logging_action "déménagement" + $r = pg_query("SELECT MAX(idlog) FROM action_log"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + $oldopt = $a2['ifdescription']; + $r = pg_query("SELECT ifdescription FROM interface WHERE idinterface = ".$idint); + $a = pg_fetch_array($r); + $opt = $a['ifdescription']; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $idint, 6, '$oldopt', '$opt')"); + + echo ""; + } + else { + $r = pg_query("SELECT name, idroom FROM room ORDER BY idroom ASC"); + $last_id = 0; + echo "

Vers la chambre "; + echo "

\n"; + } +} + +if(isset($_GET['id'])) { + $ok = DisplayRoomInfo($_GET['id']); + if($ok) { + if(isset($_GET['change']) && $roles['root']) + DisplayInterfaceForm($_GET['id']); + elseif(isset($_GET['move']) && $roles['inscription']) + DisplayRoomForm($_GET['id']); + else + DisplayRoomInterface($_GET['id']); + echo "
\n"; //ouvert dans DisplayRoomInfo + } +} +DisplayRoomList(); + +include "inc/inc.footer.php"; +?> diff --git a/camembert-before-ldap/si905.php b/camembert-before-ldap/si905.php new file mode 100644 index 0000000..888f728 --- /dev/null +++ b/camembert-before-ldap/si905.php @@ -0,0 +1,295 @@ +Aucune interface ne correspond à cet identifiant

\n"; + include "inc/inc.footer.php"; + exit(); +} + +if($mode == "conf" && $_POST['num'] != "") { + $r = pg_query("SELECT MAX(idaction) FROM action"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + $num = pg_escape_string($_POST['num']); + $opt = iconv("UTF-8", "ASCII//TRANSLIT", $_POST['opt']); + if($num == -1) { + pg_query("INSERT INTO action VALUES($newid, $id, 0, '$opt')"); + pg_query("INSERT INTO action VALUES($newid+1, $id, 1, '$opt')"); + } + elseif($num == 73) { + pg_query("INSERT INTO action VALUES($newid, $id, 7, 3)"); + } + elseif($num == 7964) { + pg_query("INSERT INTO action VALUES($newid, $id, 7, 964)"); + } + else { + pg_query("INSERT INTO action VALUES($newid, $id, $num, '$opt')"); + } + // Logging actions + $r = pg_query("SELECT MAX(idlog) FROM action_log"); + if($am = pg_fetch_array($r)) + $newid = $am[0]+1; + else + $newid = 1; + if($num == -1) { + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 0, ${a[7]})"); + pg_query("INSERT INTO action_log VALUES($newid+1, '$auth_user', ".time().", $id, 1, ${a[7]})"); + } + elseif($num == 73) { + $oldopt = $a[10]; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 7, '$oldopt', 3)"); + } + elseif($num == 7964) { + $oldopt = $a[10]; + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, 7, '$oldopt', 964)"); + } + else { + switch($num) { + case 0: + case 1: $oldopt = $a[7]; break; + case 2: $oldopt = $a[4]; break; + case 3: $oldopt = $a[19]; break; + default: $oldopt = ''; break; + } + pg_query("INSERT INTO action_log VALUES($newid, '$auth_user', ".time().", $id, $num, '$oldopt', '$opt')"); + } +} + +function DisplayActions() { + global $id; + + $r = pg_query("SELECT numaction, option FROM action WHERE idinterface = $id ORDER BY idaction"); + if($a = pg_fetch_array($r)) { +?> +

Prochaines actions

+
    +$line\n"; + $a = pg_fetch_array($r); + } +?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+
+
+
+
+
Description
Nombre de MACs autorisées
Sticky +
+ +'Other', 6=>'Ethernet CSMA/CD', 9=>'Token ring', 15=>'FDDI', 18=>'DSL', 19=>'DSL', 20=>'ISDN', + 21=>'ISDN', 22=>'Série', 23=>'PPP', 24=>'Loopback', 28=>'SLIP', 32=>'Frame relay', 53=>'Virtual', 71=>'802.11', + 117=>'Ethernet Gigabit', 135=>'VLAN'); + +$rroom = pg_query("SELECT idroom, name FROM room WHERE idinterface = '$id'"); +if($aroom = pg_fetch_array($rroom)) +{ +?> +

Chambre "; ?>

+ +

/".$a[3]; ?>

+
+ + + + + +
+ + + + + + + + + + En isolement"; + if($a[11] != 0 && $a[11] != 4096) $voice = $a[11]; + else $voice = ""; + break; + } +?> + +\n"; + if($voice != "") + echo "\n"; +?> + +
ifNumber
ifAddress
ifType
  
Description
Etat
VitesseM
VLAN
Native VLAN${a[12]}
Voice VLAN$voice
SpanningTree Portfast
+ + [Configurer] + +
+ +

Port Security

+ + + + + + + + + + + +
  
Activé
Etat
Nb de MAC max
Nb de MAC actuel
Nb de violations
Dernière MAC  (00:00:00:00:00:00 peut être normal)
Sticky activé
+ 0 and datelast = (SELECT MAX(datelast) FROM fdb)"); + if($a = pg_fetch_array($r)) { +?> +

Stickies

+
    +".$a[0].($a[1]==1?" (static)":"")."\n"; + $a = pg_fetch_array($r); + } +?> +
+ +
+ +
+ + +

Matéreil connecté sur cette interface

+
+ diff --git a/camembert-before-ldap/style.css b/camembert-before-ldap/style.css new file mode 100644 index 0000000..bc1b211 --- /dev/null +++ b/camembert-before-ldap/style.css @@ -0,0 +1,141 @@ +/*body { + background-color: #F0F0F0; + font-family: Tahoma, Arial, sans serif; + font-size: 12; +} +*/ +.node { + margin-left : 10px ; +} + +#demo { + font-size : 85% ; + border:1px + solid #ccc; +} +.tdlog { padding:2px; border-bottom:1px solid #ccc; border-right:1px solid #ccc; } + +#navigation { + margin: 0 ; + margin-top: 2px ; + padding: 0 ; + list-style: none ; +} + +#navigation li { + display: inline ; + margin-right: 1px ; +} + + +#navigation li a { + padding: 4px 20px ; + border: 1px solid #600 ; + line-height: 1em ; + color: black ; + font-size: 14px; + text-align: center ; + text-decoration: none ; +} + +ul#navigation a { + float: left; +} + +.active, #navigation li a:hover, #navigation li a:focus, #navigation li a:active { +/* background: #e0edfb ;*/ + background: orange ; +} + +.inactive { + background: #FFFAF0 ; +} + +/*img { border: 0; } +*/ +/*h1,h2 { + text-align: center; + margin: 3px; +} +*/ + +table.realtable { + font-size: 12px ; + line-height: normal; + display: table; + border-spacing: 2px; + border-collapse: separate; + margin-top: 0; + margin-bottom: 0; + text-indent: 0; +} +td.realtd { + padding: 1px; + line-height: normal; +} + +th.realth { + background-color: grey; + padding: 1px; +} + +.list { + font-size: 11px ; +} +.error { + color: #CC0000; + font-weight: bold; +} + +.os { font-size: 80%; } + +.normal { background-color: lightgrey; } +.normal2 { background-color: #C0C0C0; } +.red { background-color: #EFB0B0; } +.red2 { background-color: #DFA0A0; } +.orange { background-color: #FEA347; } +.orange2 { background-color: #ED7F10; } +.green { background-color: #B0EFB0; } +.green2 { background-color: #A0DFA0; } +.blue { background-color: #B0B0EF; } +.blue2 { background-color: #A0A0DF; } + +.img { + width: 20px; + position:relative; + left: 10px; + vertical-align:top; +} + +.td { + height: 20px; + width: 70px; +} + +.block2 { + display: block; + height: 100%; + width: 100%; +} + +.footer { + font-size: 10px; + text-align: center; +} + +ul.search { + list-style-type: none; + text-align:center; + padding: 0; + margin: 0; + margin-top: 50px; +} +label.align { + position: absolute; + width: 190px; + text-align: left; +} +input.lmargin +{ + margin-left: 200px; +} diff --git a/camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.css b/camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.css new file mode 100644 index 0000000..1c7afd7 --- /dev/null +++ b/camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.css @@ -0,0 +1,41 @@ +/*==================================================== + - HTML Table Filter Generator + - Columns Visibility Manager Extension v1.1 + - do not hesitate to edit classes below to + change extension appearance +=====================================================*/ + +span.showHideColsSpan{ text-align:left; } + +span.showHideColsSpan a.showHideCols{ + /* Link */ + margin:0 5px 0 5px; +} +div.showHideColsCont{ + /* Container div */ + position:absolute; + display:none; + border:1px solid #000; + height:auto; width:250px; + background:#fff; + margin:18px 0 0 0; z-index:10000; + padding:10px 10px 10px 10px; + text-align:left; font-size:12px; +} +div.showHideColsCont p{ margin:6px auto 6px auto; } + +ul.cols_checklist{ padding:0; margin:0; list-style: none; } +li.cols_checklist_item{ /*check list item*/ + padding:1px; margin:0; font-size:11px; + border-bottom:1px solid #ccc; +} +li.cols_checklist_item:hover{ + background-color:#335EA8; + color:#fff; +} +.cols_checklist_slc_item{ /*selected check list item*/ + background-color:#335EA8; + color:#fff; +} +ul.cols_checklist label{ display:block; } +ul.cols_checklist input{ vertical-align:middle; margin:2px 5px 2px 1px; } \ No newline at end of file diff --git a/camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.js b/camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.js new file mode 100644 index 0000000..b12cb8f --- /dev/null +++ b/camembert-before-ldap/tablefilter/TFExt_ColsVisibility/TFExt_ColsVisibility.js @@ -0,0 +1,398 @@ +/*------------------------------------------------------------------------ + - HTML Table Filter Generator + - Columns Visibility Manager Extension v1.1 + - By Max Guglielmi (tablefilter.free.fr) + - Licensed under the MIT License +-------------------------------------------------------------------------- +Copyright (c) 2009 Max Guglielmi + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------*/ + +TF.prototype.SetColsVisibility = function(extName) +{ + var o = this, f = o.fObj, ext = (extName) ? o.Ext.list[extName] : o.Ext.list['ColsVisibility']; + o.colsVisibility = (f!=undefined && f.cols_visibility==undefined) ? true : f.cols_visibility; + if(!o.colsVisibility || o.showHideColsExtLoaded) return; + + o.showHideColsExtLoaded = false; + o.showHideColsExtName = ext.name; + o.showHideColsExtDesc = ext.description; + + o.showHideColsSpanEl = null; //show/hide cols span element + o.btnShowHideColsEl = null; //show/hide cols button element + o.showHideColsContEl = null; //show/hide cols container div element + + o.showHideColsTickToHide = f!=undefined && f.showHide_cols_tick_to_hide!=undefined //tick to hide or show column + ? f.showHide_cols_tick_to_hide : true; + o.showHideColsManager = f!=undefined && f.showHide_cols_manager!=undefined //enables/disables cols manager generation + ? f.showHide_cols_manager : true; + o.showHideColsHeadersTbl = f!=undefined && f.showHide_cols_headers_table!=undefined //only if external headers + ? f.showHide_cols_headers_table : null; + o.showHideColsHeadersIndex = f!=undefined && f.showHide_cols_headers_index!=undefined //only if external headers + ? f.showHide_cols_headers_index : 1; + o.showHideColsContElTgtId = f!=undefined && f.showHide_cols_container_target_id!=undefined //id of container element + ? f.showHide_cols_container_target_id : null; + o.showHideColsHeadersText = f!=undefined && f.showHide_cols_headers_text!=undefined //alternative headers text + ? f.showHide_cols_headers_text : null; + o.btnShowHideColsTgtId = f!=undefined && f.btn_showHide_cols_target_id!=undefined //id of button container element + ? f.btn_showHide_cols_target_id : null; + o.btnShowHideColsText = f!=undefined && f.btn_showHide_cols_text!=undefined + ? f.btn_showHide_cols_text : 'Display columns▼'; //defines show/hide cols text + o.btnShowHideColsHtml = f!=undefined && f.btn_showHide_cols_html!=undefined + ? f.btn_showHide_cols_html : null; //defines show/hide cols button innerHtml + o.btnShowHideColsCssClass = f!=undefined && f.btn_showHide_cols_css_class!=undefined //defines css class for show/hide cols button + ? f.btn_showHide_cols_css_class :'showHideCols'; + o.btnShowHideColsCloseText = f!=undefined && f.btn_showHide_cols_close_text!=undefined + ? f.btn_showHide_cols_close_text : 'Close'; //defines close link text + o.btnShowHideColsCloseHtml = f!=undefined && f.btn_showHide_cols_close_html!=undefined + ? f.btn_showHide_cols_close_html : null; //defines close button innerHtml + o.btnShowHideColsCloseCssClass = f!=undefined && f.btn_showHide_cols_close_css_class!=undefined //defines css class for close button + ? f.btn_showHide_cols_close_css_class :o.btnShowHideColsCssClass; + + o.showHideColsExtPath = (ext.path == undefined) ? 'TFExt_ColsVisibility/' : ext.path; + o.showHideColsStylesheet = 'TFExt_ColsVisibility.css'; + o.prfxShowHideColsSpan = 'showHideCols_'; //span containing show/hide cols button + o.showHideColsSpanCss = f!=undefined && f.showHide_cols_span_css_class!=undefined //defines css class span containing show/hide cols + ? f.showHide_cols_span_css_class : 'showHideColsSpan'; + o.prfxShowHideColsCont = 'showHideColsCont_'; + o.showHideColsContCss = f!=undefined && f.showHide_cols_cont_css_class!=undefined //defines css class div containing show/hide cols + ? f.showHide_cols_cont_css_class : 'showHideColsCont'; + o.showHideColsListCss = f!=undefined && f.showHide_cols_list_css_class!=undefined //defines css class for cols list (ul) + ? f.showHide_cols_list_css_class : 'cols_checklist'; + o.showHideColsListItemCssClass = f!=undefined && f.checklist_item_css_class!=undefined //defines css class for list item (li) + ? f.checklist_item_css_class : 'cols_checklist_item'; + o.showHideColsListSlcItemCssClass = f!=undefined && f.checklist_selected_item_css_class!=undefined //defines css class for selected list item (li) + ? f.checklist_selected_item_css_class : 'cols_checklist_slc_item'; + o.showHideColsText = f!=undefined && f.showHide_cols_text!=undefined //text preceding columns list + ? f.showHide_cols_text : 'Hide columns: '; + o.showHideColsAtStart = f!=undefined && f.showHide_cols_at_start!=undefined + ? f.showHide_cols_at_start : null; + o.showHideColsEnableHover = f!=undefined && f.showHide_cols_enable_hover!=undefined + ? f.showHide_cols_enable_hover : false; + o.showHideColsIsOpen = false; + + o.tblHasColTag = (tf_Tag(o.tbl,'col').length > 0) ? true : false; + + /*** Extension events ***/ + //calls function before cols manager is opened + o.onBeforeOpenColsManager = f!=undefined && tf_isFn(f.on_before_open_cols_manager) + ? f.on_before_open_cols_manager : null; + //calls function after cols manager is opened + o.onAfterOpenColsManager = f!=undefined && tf_isFn(f.on_after_open_cols_manager) + ? f.on_after_open_cols_manager : null; + //calls function before cols manager is closed + o.onBeforeCloseColsManager = f!=undefined && tf_isFn(f.on_before_close_cols_manager) + ? f.on_before_close_cols_manager : null; + //calls function after cols manager is closed + o.onAfterCloseColsManager = f!=undefined && tf_isFn(f.on_after_close_cols_manager) + ? f.on_after_close_cols_manager : null; + + //calls function before col is hidden + o.onBeforeColIsHidden = f!=undefined && tf_isFn(f.on_before_col_is_hidden) + ? f.on_before_col_is_hidden : null; + //calls function after col is hidden + o.onAfterColIsHidden = f!=undefined && tf_isFn(f.on_after_col_is_hidden) + ? f.on_after_col_is_hidden : null; + //calls function before col is displayed + o.onBeforeColIsDisplayed = f!=undefined && tf_isFn(f.on_before_col_is_displayed) + ? f.on_before_col_is_displayed : null; + //calls function after col is displayed + o.onAfterColIsDisplayed = f!=undefined && tf_isFn(f.on_after_col_is_displayed) + ? f.on_after_col_is_displayed : null; + /*** ***/ + + //Extension event definition + o.Evt.name.colsvisibility = 'ShowColsVisibility'; //event name for TF event manager + o.msgShowColsVisibility = 'Show/Hide columns'; //event status message + o.Evt._ShowColsVisibility = function(){ o.ShowColsVisibility(); }; //event + o.Evt.name.checkitem = 'CheckItem'; //event name for TF event manager + o.msgCheckItem = 'Showing/hiding columns'; //event status message + o.Evt._CheckItem = function(li){ o.CheckItem(li); }; //event + + //Loads extension stylesheet + o.IncludeFile(ext.name+'Style', o.showHideColsExtPath + o.showHideColsStylesheet, null, 'link'); + + //Sets button + if(o.showHideColsManager) o.SetShowHideColsBtn(); + o.showHideColsExtLoaded = true; +} + +TF.prototype.SetShowHideColsBtn = function() +/*==================================================== + - Generates show/hide cols button +=====================================================*/ +{ + if(!this.hasGrid && !this.isFirstLoad) return; + if( this.btnShowHideColsEl!=null ) return; + var showHideColsSpan = tf_CreateElm('span',['id',this.prfxShowHideColsSpan+this.id]); + showHideColsSpan.className = this.showHideColsSpanCss; + + //Container element (rdiv or custom element) + if(this.btnShowHideColsTgtId==null) this.SetTopDiv(); + var targetEl = ( this.btnShowHideColsTgtId==null ) ? this.rDiv : tf_Id( this.btnShowHideColsTgtId ); + + if(this.btnShowHideColsTgtId==null) + targetEl.firstChild.parentNode.insertBefore(showHideColsSpan,targetEl.firstChild); + else + targetEl.appendChild(showHideColsSpan); + + if(this.btnShowHideColsHtml==null) + { //Default link + var btn = tf_CreateElm( 'a', ['href','javascript:;'] ); + btn.className = this.btnShowHideColsCssClass; + btn.title = this.showHideColsExtDesc; + + btn.innerHTML = this.btnShowHideColsText; + showHideColsSpan.appendChild(btn); + if(!this.showHideColsEnableHover) + btn.onclick = this.Evt._ShowColsVisibility; + else{ + var o = this; + btn.onmouseover = this.Evt._ShowColsVisibility; + } + } else { //Custom html + showHideColsSpan.innerHTML = this.btnShowHideColsHtml; + var showHideColsEl = showHideColsSpan.firstChild; + if(!this.showHideColsEnableHover) + showHideColsEl.onclick = this.Evt._ShowColsVisibility; + else + showHideColsEl.onmouseover = this.Evt._ShowColsVisibility; + } + + this.showHideColsSpanEl = showHideColsSpan; + this.btnShowHideColsEl = this.showHideColsSpanEl.firstChild; + + this.SetColsVisibilityManager(); +} + +TF.prototype.SetColsVisibilityManager = function() +/*==================================================== + - Generates show/hide cols manager +=====================================================*/ +{ + if(!this.hasGrid && !this.isFirstLoad) return; + + var container = (!this.showHideColsContElTgtId) + ? tf_CreateElm('div',['id',this.prfxShowHideColsCont+this.id]) + : tf_Id(this.showHideColsContElTgtId); + container.className = this.showHideColsContCss; + + //Extension description + var extNameLabel = tf_CreateElm('p'); + extNameLabel.innerHTML = this.showHideColsText; + container.appendChild(extNameLabel); + + //Headers list + var ul = tf_CreateElm('ul',['id','ul'+this.showHideColsExtName]); + ul.className = this.showHideColsListCss; + + var o = this; + var tbl = (o.showHideColsHeadersTbl) ? o.showHideColsHeadersTbl : o.tbl; + var headerIndex = (o.showHideColsHeadersTbl) + ? o.showHideColsHeadersIndex : o.GetHeadersRowIndex(); + var headerRow = tbl.rows[headerIndex]; + + for(var i=0; i