diff options
author | 2020-10-26 15:45:56 +0000 | |
---|---|---|
committer | 2020-10-26 15:45:56 +0000 | |
commit | 81180db98f9aa7483a49bb5ffc9e320f173da837 (patch) | |
tree | 9f9f0c2199d4470c677e91bb1fa50cf3c7f28636 | |
parent | Improve I/O functions to match Linux: (diff) | |
download | wireguard-openbsd-81180db98f9aa7483a49bb5ffc9e320f173da837.tar.xz wireguard-openbsd-81180db98f9aa7483a49bb5ffc9e320f173da837.zip |
s/agentx_/ax_/g and s/subagentx_/agentx_/g
Requested by and OK deraadt@
-rw-r--r-- | lib/libagentx/Makefile | 8 | ||||
-rw-r--r-- | lib/libagentx/Symbols.list | 122 | ||||
-rw-r--r-- | lib/libagentx/agentx.3 | 645 | ||||
-rw-r--r-- | lib/libagentx/agentx.c | 4780 | ||||
-rw-r--r-- | lib/libagentx/agentx.h | 337 | ||||
-rw-r--r-- | lib/libagentx/agentx_internal.h | 124 | ||||
-rw-r--r-- | lib/libagentx/agentx_log.c | 316 | ||||
-rw-r--r-- | lib/libagentx/ax.c | 1359 | ||||
-rw-r--r-- | lib/libagentx/ax.h | 232 | ||||
-rw-r--r-- | lib/libagentx/shlib_version | 2 | ||||
-rw-r--r-- | lib/libagentx/subagentx.3 | 645 | ||||
-rw-r--r-- | lib/libagentx/subagentx.c | 4003 | ||||
-rw-r--r-- | lib/libagentx/subagentx.h | 147 | ||||
-rw-r--r-- | lib/libagentx/subagentx_internal.h | 124 | ||||
-rw-r--r-- | lib/libagentx/subagentx_log.c | 316 |
15 files changed, 6580 insertions, 6580 deletions
diff --git a/lib/libagentx/Makefile b/lib/libagentx/Makefile index 82c255f9786..cf4fb7f0767 100644 --- a/lib/libagentx/Makefile +++ b/lib/libagentx/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.1 2020/09/16 10:48:52 martijn Exp $ +# $OpenBSD: Makefile,v 1.2 2020/10/26 15:45:56 martijn Exp $ LIB= agentx -SRCS= agentx.c subagentx.c subagentx_log.c -HDRS= subagentx.h -MAN= subagentx.3 +SRCS= ax.c agentx.c agentx_log.c +HDRS= agentx.h +MAN= agentx.3 CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes diff --git a/lib/libagentx/Symbols.list b/lib/libagentx/Symbols.list index b31c7b0d95f..95804e1616d 100644 --- a/lib/libagentx/Symbols.list +++ b/lib/libagentx/Symbols.list @@ -1,61 +1,61 @@ -subagentx_log_fatal -subagentx_log_warn -subagentx_log_info -subagentx_log_debug -subagentx -subagentx_connect -subagentx_read -subagentx_write -subagentx_wantwrite -subagentx_free -subagentx_session -subagentx_session_free -subagentx_context -subagentx_context_object_find -subagentx_context_object_nfind -subagentx_context_uptime -subagentx_context_free -subagentx_agentcaps -subagentx_agentcaps_free -subagentx_region -subagentx_region_free -subagentx_index_integer_new -subagentx_index_integer_any -subagentx_index_integer_value -subagentx_index_integer_dynamic -subagentx_index_string_dynamic -subagentx_index_nstring_dynamic -subagentx_index_oid_dynamic -subagentx_index_noid_dynamic -subagentx_index_ipaddress_dynamic -subagentx_index_free -subagentx_object -subagentx_object_free -subagentx_varbind_integer -subagentx_varbind_string -subagentx_varbind_nstring -subagentx_varbind_printf -subagentx_varbind_null -subagentx_varbind_oid -subagentx_varbind_object -subagentx_varbind_index -subagentx_varbind_ipaddress -subagentx_varbind_counter32 -subagentx_varbind_gauge32 -subagentx_varbind_timeticks -subagentx_varbind_opaque -subagentx_varbind_counter64 -subagentx_varbind_notfound -subagentx_varbind_error -subagentx_varbind_request -subagentx_varbind_get_object -subagentx_varbind_get_index_integer -subagentx_varbind_get_index_string -subagentx_varbind_get_index_oid -subagentx_varbind_get_index_ipaddress -subagentx_varbind_set_index_integer -subagentx_varbind_set_index_string -subagentx_varbind_set_index_nstring -subagentx_varbind_set_index_oid -subagentx_varbind_set_index_object -subagentx_varbind_set_index_ipaddress +agentx_log_fatal +agentx_log_warn +agentx_log_info +agentx_log_debug +agentx +agentx_connect +agentx_read +agentx_write +agentx_wantwrite +agentx_free +agentx_session +agentx_session_free +agentx_context +agentx_context_object_find +agentx_context_object_nfind +agentx_context_uptime +agentx_context_free +agentx_agentcaps +agentx_agentcaps_free +agentx_region +agentx_region_free +agentx_index_integer_new +agentx_index_integer_any +agentx_index_integer_value +agentx_index_integer_dynamic +agentx_index_string_dynamic +agentx_index_nstring_dynamic +agentx_index_oid_dynamic +agentx_index_noid_dynamic +agentx_index_ipaddress_dynamic +agentx_index_free +agentx_object +agentx_object_free +agentx_varbind_integer +agentx_varbind_string +agentx_varbind_nstring +agentx_varbind_printf +agentx_varbind_null +agentx_varbind_oid +agentx_varbind_object +agentx_varbind_index +agentx_varbind_ipaddress +agentx_varbind_counter32 +agentx_varbind_gauge32 +agentx_varbind_timeticks +agentx_varbind_opaque +agentx_varbind_counter64 +agentx_varbind_notfound +agentx_varbind_error +agentx_varbind_request +agentx_varbind_get_object +agentx_varbind_get_index_integer +agentx_varbind_get_index_string +agentx_varbind_get_index_oid +agentx_varbind_get_index_ipaddress +agentx_varbind_set_index_integer +agentx_varbind_set_index_string +agentx_varbind_set_index_nstring +agentx_varbind_set_index_oid +agentx_varbind_set_index_object +agentx_varbind_set_index_ipaddress diff --git a/lib/libagentx/agentx.3 b/lib/libagentx/agentx.3 new file mode 100644 index 00000000000..0db3d32454d --- /dev/null +++ b/lib/libagentx/agentx.3 @@ -0,0 +1,645 @@ +.\" $OpenBSD: agentx.3,v 1.1 2020/10/26 15:45:56 martijn Exp $ +.\" +.\" Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: October 26 2020 $ +.Dt AGENTX 3 +.Os +.Sh NAME +.Nm agentx_log_fatal , +.Nm agentx_log_warn , +.Nm agentx_log_info , +.Nm agentx_log_debug , +.Nm agentx , +.Nm agentx_connect , +.Nm agentx_read , +.Nm agentx_write , +.Nm agentx_wantwrite , +.Nm agentx_free , +.Nm agentx_session , +.Nm agentx_session_free , +.Nm agentx_context , +.Nm agentx_context_object_find , +.Nm agentx_context_object_nfind , +.Nm agentx_context_uptime , +.Nm agentx_context_free , +.Nm agentx_region , +.Nm agentx_region_free , +.Nm agentx_agentcaps , +.Nm agentx_agentcaps_free , +.Nm agentx_index_integer_new , +.Nm agentx_index_integer_any , +.Nm agentx_index_integer_value , +.Nm agentx_index_integer_dynamic , +.Nm agentx_index_string_dynamic , +.Nm agentx_index_nstring_dynamic , +.Nm agentx_index_oid_dynamic , +.Nm agentx_index_noid_dynamic , +.Nm agentx_index_ipaddress_dynamic , +.Nm agentx_index_free , +.Nm agentx_object , +.Nm agentx_object_free , +.Nm agentx_varbind_integer , +.Nm agentx_varbind_string , +.Nm agentx_varbind_nstring , +.Nm agentx_varbind_printf , +.Nm agentx_varbind_null , +.Nm agentx_varbind_oid , +.Nm agentx_varbind_object , +.Nm agentx_varbind_index , +.Nm agentx_varbind_ipaddress , +.Nm agentx_varbind_counter32 , +.Nm agentx_varbind_gauge32 , +.Nm agentx_varbind_timeticks , +.Nm agentx_varbind_opaque , +.Nm agentx_varbind_counter64 , +.Nm agentx_varbind_notfound , +.Nm agentx_varbind_error , +.Nm agentx_varbind_request , +.Nm agentx_varbind_get_index_integer , +.Nm agentx_varbind_get_index_string , +.Nm agentx_varbind_get_index_oid , +.Nm agentx_varbind_get_index_ipaddress , +.Nm agentx_varbind_set_index_integer , +.Nm agentx_varbind_set_index_string , +.Nm agentx_varbind_set_index_nstring , +.Nm agentx_varbind_set_index_oid , +.Nm agentx_varbind_set_index_object , +.Nm agentx_varbind_set_index_ipaddress +.Nd manage an interface to an agentx master +.Sh SYNOPSIS +.In agentx.h +.Ft extern void +.Fn (*agentx_log_fatal) "const char *fmt" ... +.Ft extern void +.Fn (*agentx_log_warn) "const char *fmt" ... +.Ft extern void +.Fn (*agentx_log_info) "const char *fmt" ... +.Ft extern void +.Fn (*agentx_log_debug) "const char *fmt" ... +.Ft struct agentx * +.Fn agentx "void (*nofd)(struct agentx *, void *, int)" "void *cookie" +.Ft void +.Fn agentx_connect "struct agentx *sa" "int fd" +.Ft void +.Fn agentx_read "struct agentx *sa" +.Ft void +.Fn agentx_write "struct agentx *sa" +.Ft extern void +.Fn (*agentx_wantwrite) "struct agentx *sa" "int fd" +.Ft void +.Fn agentx_free "struct agentx *sa" +.Ft struct agentx_session * +.Fo agentx_session +.Fa "struct agentx *sa" "uint32_t oid[]" "size_t oidlen" +.Fa "const char *descr" "uint8_t timeout" +.Fc +.Ft void +.Fn agentx_session_free "struct agentx_session *sas" +.Ft struct agentx_context * +.Fn agentx_context "struct agentx_session *sas" "const char *name" +.Ft struct agentx_object * +.Fo agentx_context_object_find +.Fa "struct agentx_context *sac" "const uint32_t oid[]" "size_t oidlen" +.Fa "int active" "int instance" +.Fc +.Ft struct agentx_object * +.Fo agentx_context_object_nfind +.Fa "struct agentx_context *" "const uint32_t oid[]" "size_t oidlen" +.Fa "int active" "int inclusive" +.Fc +.Ft uint32_t +.Fn agentx_context_uptime "struct agentx_context *sac" +.Ft void +.Fn agentx_context_free "struct agentx_context *sac" +.Ft struct agentx_agentcaps * +.Fo agentx_agentcaps +.Fa "struct agentx_context *sac" "uint32_t oid[]" "size_t oidlen" +.Fa "const char *descr" +.Fc +.Ft void +.Fn agentx_agentcaps_free "struct agentx_agentcaps *saa" +.Ft struct agentx_region * +.Fo agentx_region +.Fa "struct agentx_context *sac" "uint32_t oid[]" +.Fa "size_t oidlen" "uint8_t timeout" +.Fc +.Ft void +.Fn agentx_region_free "struct agentx_region *sar" +.Ft struct agentx_index * +.Fo agentx_index_integer_new +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_integer_any +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_integer_value +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fa "uint32_t value" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_integer_dynamic +.Fa "struct agentx_region *sar" "uint32_t oid[] "size_t oidlen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_string_dynamic +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_nstring_dynamic +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fa "size_t slen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_oid_dynamic +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_noid_dynamic +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fa "size_t vlen" +.Fc +.Ft struct agentx_index * +.Fo agentx_index_ipaddress_dynamic +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fc +.Ft void +.Fn agentx_index_free "struct agentx_index *sai" +.Ft struct agentx_object * +.Fo agentx_object +.Fa "struct agentx_region *sar" "uint32_t oid[]" "size_t oidlen" +.Fa "struct agentx_index *index[]" "size_t indexlen" "int implied" +.Fa "void (*getcb)(struct agentx_varbind *)" +.Fc +.Ft void +.Fn agentx_object_free "struct agentx_object *sao" +.Ft void +.Fn agentx_varbind_integer "struct agentx_varbind *sav" "uint32_t value" +.Ft void +.Fn agentx_varbind_string "struct agentx_varbind *sav" "const char *value" +.Ft void +.Fo agentx_varbind_nstring +.Fa "struct agentx_varbind *sav" "const char *value" "size_t slen" +.Fc +.Ft void +.Fo agentx_varbind_printf +.Fa "struct agentx_varbind *sav" "const char *fmt" ... +.Fc +.Ft void +.Fn agentx_varbind_null "struct agentx_varbind *sav" +.Ft void +.Fo agentx_varbind_oid +.Fa "struct agentx_varbind *sav" "const uint32_t oid[]" "size_t oidlen" +.Fc +.Ft void +.Fo agentx_varbind_object +.Fa "struct agentx_varbind *sav" "struct agentx_object *sao" +.Fc +.Ft void +.Fo agentx_varbind_index +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fc +.Ft void +.Fo agentx_varbind_ipaddress +.Fa "struct agentx_varbind *sav" "const struct in_addr *addr" +.Fc +.Ft void +.Fn agentx_varbind_counter32 "struct agentx_varbind *sav" "uint32_t value" +.Ft void +.Fn agentx_varbind_gauge32 "struct agentx_varbind *sav" "uint32_t value" +.Ft void +.Fo agentx_varbind_timeticks +.Fa "struct agentx_varbind *sav" "uint32_t value" +.Fc +.Ft void +.Fo agentx_varbind_opaque +.Fa "struct agentx_varbind *sav" "const char *value" "size_t slen" +.Fc +.Ft void +.Fn agentx_varbind_counter64 "struct agentx_varbind *sav" "uint64_t value" +.Ft void +.Fn agentx_varbind_notfound "struct agentx_varbind *sav" +.Ft void +.Fn agentx_varbind_error "struct agentx_varbind *sav" +.Ft enum agentx_request_type +.Fn agentx_varbind_request "struct agentx_varbind *sav" +.Ft uint32_t +.Fo agentx_varbind_get_index_integer +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fc +.Ft const unsigned char * +.Fo agentx_varbind_get_index_string +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" "size_t *slen" +.Fa "int *implied" +.Fc +.Ft const uint32_t * +.Fo agentx_varbind_get_index_oid +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "size_t *oidlen" "int *implied" +.Fc +.Ft const struct in_addr * +.Fo agentx_varbind_get_index_ipaddress +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fc +.Ft void +.Fo agentx_varbind_set_index_integer +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "uint32_t value" +.Fc +.Ft void +.Fo agentx_varbind_set_index_string +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "const unsigned char *value" +.Fc +.Ft void +.Fo agentx_varbind_set_index_nstring +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "const unsigned char *value" "size_t slen" +.Fc +.Ft void +.Fo agentx_varbind_set_index_oid +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "const uint32_t *oid" "size_t oidlen" +.Fc +.Ft void +.Fo agentx_varbind_set_index_object +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "struct agentx_object *sao" +.Fc +.Ft void +.Fo agentx_varbind_set_index_ipaddress +.Fa "struct agentx_varbind *sav" "struct agentx_index *sai" +.Fa "const struct in_addr *addr" +.Fc +.Bd -literal +enum agentx_request_type { + AGENTX_REQUEST_TYPE_GET, + AGENTX_REQUEST_TYPE_GETNEXT, + AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE +}; +.Ed +.Fd #define AGENTX_MASTER_PATH \(dq/var/agentx/master\(dq +.Fd #define AGENTX_OID_MAX_LEN 128 +.Fd #define AGENTX_OID_INDEX_MAX_LEN 10 +.Fd #define AGENTX_OID(...) +.Fd #define AGENTX_MIB2 1, 3, 6, 1, 2, 1 +.Fd #define AGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1 +.Sh DESCRIPTION +The +.Nm agentx +functions allow an application to describe their MIB layout and provide an +.Fa fd +based interface to control the internal agentx state. +.Nm agentx +is not thread safe. +.Ss DESCRIBING THE MIB +.Nm agentx +is a framework to abstract away the agentx protocol from the application. +For the framework to report information to the administrator, the +.Fn agentx_log_fatal , +.Fn agentx_log_warn , +.Fn agentx_log_info +and +.Fn agentx_log_debug +functions must be set. +.Pp +When +.Fa sa +is created by +.Fn agentx +or when +.Fa sa +detects that there is no connection to the agentx master it calls out to +.Fa nofd +with itself, +.Fa cookie +and an integer +.Fa close +as arguments. +If +.Fa close +is not set +.Fn nofd +is expected to set up a new +.Fa fd +to the agentx master. +This one can usually be found at +.Dv AGENTX_MASTER_PATH . +This +.Fa fd +can be returned to +.Fa sa +at any moment via +.Fn agentx_connect , +but must always be done as a result of a call to +.Fn nofd . +Once +.Fn agentx_connect +has been called the application is responsible for retrieving data when available +on +.Fa fd +by calling +.Fn agentx_read . +If nonblocking writes are desirable the +.Fn agentx_wantwrite +pointer can be set to an application function and will be called as soon as +there's data available to be written out. +Once +.Fa fd +is ready for write the function +.Fn agentx_write +should be called. +.Pp +.Fa sa +can be freed via +.Fn agentx_free . +It will close all active sessions and free all derived objects. +Once freed no new objects can be derived from the freed objects. +Once all sessions are closed it will call out to +.Fn nofd +with +.Fa close +set, indicating that the application can clean up any context related to +.Fa sa . +.Pp +On top of the +.Fa sa +connection a +.Vt agentx_session +must be set up. +Normally there's only a single session per +.Fa sa . +The +.Fa timeout +argument specifies the maximum time in seconds the master should wait for a +reply before determining we're gone. +If set to 0 the agentx master determines the timeout. +The +.Fa oid +and +.Fa oidlen +combination identifies the subagent and will be visible through the +agentxSessionObjectID object on the agentx master. +The +.Fa descr +is a short displaystring description of the agent and will be visiable through +the agentxSessionDescr object on the agentx master. +.Pp +The +.Vt agentx_context +is the SNMPv3 context in which the objects operate and is built on top of +agentx_session +.Fa sas . +If the default context is requested +.Fa name +must be NULL. +.Pp +.Fn agentx_agentcaps +registers an entry in the agentx master's sysORTable. +The +.Fa oid , +.Fa oidlen +combination should point to an AGENT-CAPABILITIES object which describes the +capabilities of the subagent. +.Fa descr +should be a textual description of the capabilities. +If no AGENT-CAPABILITIES object is defined this function can be omitted. +.Pp +A +.Vt agentx_region +indicates a region inside the object-tree for which get- and set-requests will +be queried. +If the OID has already been claimed by another subagent it will try to claim it +on a lower priority. +The +.Fa timeout +parameter overrules its +.Vt agentx_session +counterpart. +.Pp +For objects in a table one or more +.Ft agentx_index +elements must be supplied. +.Fn agentx_index_integer_new , +.Fn agentx_index_integer_any +and +.Fn agentx_index_integer_value +register an integer index at the agentx master. +Of these +.Fn agentx_index_integer_new +registers a new, previously unused, index; +.Fn agentx_index_integer_any +registers the first available index; +and +.Fn agentx_index_integer_value +tries to register a specific value. +If the registration of an index fails an error will be logged and all objects +using it will remain disabled. +The OID where the index should be registered is documented by the MIB. +These registered indices are usually used for tables where multiple subagents +are registered. +.Pp +For dynamic indices the agentx_index_*_dynamic functions can be used, based +on the data type of the object. +The data type should match the data type in the MIB at the +.Fa oid +object. +Indices of data type string or oid with a fixed length should be created via +.Fn agentx_index_nstring_dynamic +and +.Fn agentx_index_noid_dynamic +respectively. +.Pp +.Vt agentx_object +is an object as described in the MIB. +For scalar objects +.Pq without indices +the final zero must be omitted. +For table entries a list of 1 or more indices must be added via +.Fa index +and +.Fa indexlen . +The list of indices must match the INDEX list on the ENTRY object in the MIB. +The total length of the OID, including indices, can't be more than +.Dv AGENTX_OID_MAX_LEN +and indexlen can't be more than +.Dv AGENTX_OID_INDEX_MAX_LEN . +If +.Fa implied +is set the final index must be of type OID or string and will omit the leading +length indicator. +This value must only be set if specified in the MIB. +.Fn getcb +will be called for each varbind in a GET, GETNEXT or GETBULK request that +matches the object. +.Ss HANDLING GET REQUESTS +A call to +.Fn getcb +must eventually result in a call to one of the following functions: +.Bl -tag -width agentx_varbind_counter32() +.It Fn agentx_varbind_integer +Set the return value to an uint32_t value. +.It Fn agentx_varbind_string +A C string wrapper around +.Fn agentx_varbind_nstring . +.It Fn agentx_varbind_nstring +Set the return value to an octetstring. +.It Fn agentx_varbind_printf +A printf wrapper around +.Fn agentx_varbind_nstring . +.It Fn agentx_varbind_null +Set the return value to null. +.It Fn agentx_varbind_oid +Set the return value to an OID value. +.It Fn agentx_varbind_object +An agentx_object wrapper around +.Fn agentx_varbind_oid . +.It Fn agentx_varbind_index +An agentx_index wrapper around +.Fn agentx_varbind_oid . +.It Fn agentx_varbind_ipaddress +Set the return value to ipaddress. +.It Fn agentx_varbind_counter32 +Set the return value to an uint32_t of type counter32. +.It Fn agentx_varbind_gauge32 +Set the return value to an uint32_t of type gauge32. +.It Fn agentx_varbind_timeticks +Set the return value to an uint32_t of type timeticks. +.It Fn agentx_varbind_opaque +Set the return value to an opaque value. +.It Fn agentx_varbind_counter64 +Set the return value to an uint64_t of type counter64. +.It Fn agentx_varbind_notfound +When the request is of type GET return an nosuchinstance error. +When the request is of type GETNEXT or GETBULK return an endofmibview error. +On endofmibview the next object is queried. +This function can only be called on objects that contain one or more *_dynamic +indices. +.It Fn agentx_varbind_error +Returns a GENERR error to the client. +.El +.Pp +For objects containing *_dynamic indices the following support functions are to +be used: +.Bl -tag -width Ds +.It Fn agentx_varbind_request +Returns whether the request is of type GET, GETNEXT or GETNEXTINCLUSIVE. +.It Fn agentx_varbind_get_index_integer +Retrieve a single uint32_t index value. +.It Fn agentx_varbind_get_index_string +Retrieve an octetstring index value. +.Fa slen +is the length of the string and +.Fa implied +indicates if the next value for this index should be length sorted before +alphabetically sorted. +.It Fn agentx_varbind_get_index_oid +Retrieve an oid index value. +.Fa oidlen +is the length of the oid and +.Fa implied +indicates if the next value for this index should be length sorted before +alphabetically sorted. +.It Fn agentx_varbind_get_index_ipaddress +Retrieve an ipaddress index value. +.It Fn agentx_varbind_set_index_integer +Sets a single uint32_t index value. +.It Fn agentx_varbind_set_index_string +A C string wrapper around +.Fn agentx_varbind_set_index_nstring . +.It Fn agentx_varbind_set_index_nstring +Set an octetstring index value. +.It Fn agentx_varbind_set_index_oid +Set an oid index value. +.It Fn agentx_varbind_set_index_object +A agentx_object wrapper around +.Fn agentx_varbind_set_index_oid . +.It Fn agentx_varbind_set_index_ipaddress +Set an ipaddress index value. +.El +.Pp +For these functions +.Fa sai +must be part of the object the request is performed on. +The function type must also match the data type of +.Fa sai . +.Pp +Other functions that can retrieve information from the agentx context are: +.Bl -tag -width Ds +.It Fn agentx_context_object_find +Find a agentx_object created inside agentx_context +.Fa sac +based on +.Fa oid +and +.Fa oidlen . +If +.Fa active +is set the object must be reachable from the agentx master, else NULL is +returned. +If +.Fa oid +can be an instance, find its parent object. +.It Fn agentx_context_object_nfind +Find the next agentx_object created inside agentx_context +.Fa sac +based on +.Fa oid +and +.Fa oidlen . +If +.Fa active +is set the object must be reachable from the agentx master, else NULL is +returned. +If +.Fa inclusive +is set the object returned may also exactly match +.Fa oid . +.It Fn agentx_context_uptime +Returns the sysuptime in seconds for +.Fa sac +in timeticks. +.El +.Sh SEE ALSO +.Xr snmp 1 , +.Xr snmpd 8 +.Sh STANDARDS +.Rs +.%A M. Daniele +.%A B. Wijnen +.%A M. Ellison, Ed. +.%A D. Francisco, Ed. +.%D January 2000 +.%R RFC 2741 +.%T Agent Extensibility (AgentX) Protocol Version 1 +.Re +.Pp +.Rs +.%A L. Heintz +.%A S. Gudur +.%A M. Ellison, Ed. +.%D January 2000 +.%R RFC 2742 +.%T Definitions of Managed Objects for Extensible SNMP Agents +.Re +.Sh HISTORY +The +.Nm agentx +API first appeared in +.Ox 6.8 . +.Sh AUTHORS +.An Martijn van Duren Aq Mt martijn@openbsd.org diff --git a/lib/libagentx/agentx.c b/lib/libagentx/agentx.c index 2e70a502f8a..6cafec2a04d 100644 --- a/lib/libagentx/agentx.c +++ b/lib/libagentx/agentx.c @@ -13,1347 +13,3991 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/socket.h> +#include <netinet/in.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <endian.h> #include <errno.h> -#include <inttypes.h> +#include <stdarg.h> #include <stdlib.h> -#include <stdint.h> #include <stdio.h> #include <string.h> #include <strings.h> +#include <time.h> #include <unistd.h> +#include "agentx_internal.h" #include "agentx.h" -#define AGENTX_PDU_HEADER 20 - -static int agentx_pdu_need(struct agentx *, size_t); -static int agentx_pdu_header(struct agentx *, - enum agentx_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t, - struct agentx_ostring *); -static uint32_t agentx_packetid(struct agentx *); -static uint32_t agentx_pdu_queue(struct agentx *); -static int agentx_pdu_add_uint16(struct agentx *, uint16_t); -static int agentx_pdu_add_uint32(struct agentx *, uint32_t); -static int agentx_pdu_add_uint64(struct agentx *, uint64_t); -static int agentx_pdu_add_oid(struct agentx *, struct agentx_oid *, int); -static int agentx_pdu_add_str(struct agentx *, struct agentx_ostring *); -static int agentx_pdu_add_varbindlist( struct agentx *, struct agentx_varbind *, - size_t); -static uint16_t agentx_pdutoh16(struct agentx_pdu_header *, uint8_t *); -static uint32_t agentx_pdutoh32(struct agentx_pdu_header *, uint8_t *); -static uint64_t agentx_pdutoh64(struct agentx_pdu_header *, uint8_t *); -static ssize_t agentx_pdutooid(struct agentx_pdu_header *, struct agentx_oid *, - uint8_t *, size_t); -static ssize_t agentx_pdutoostring(struct agentx_pdu_header *, - struct agentx_ostring *, uint8_t *, size_t); -static ssize_t agentx_pdutovarbind(struct agentx_pdu_header *, - struct agentx_varbind *, uint8_t *, size_t); +enum agentx_index_type { + AXI_TYPE_NEW, + AXI_TYPE_ANY, + AXI_TYPE_VALUE, + AXI_TYPE_DYNAMIC +}; + +#define AGENTX_CONTEXT_CTX(axc) (axc->axc_name_default ? NULL : \ + &(axc->axc_name)) + +struct agentx_agentcaps { + struct agentx_context *axa_axc; + struct ax_oid axa_oid; + struct ax_ostring axa_descr; + enum agentx_cstate axa_cstate; + enum agentx_dstate axa_dstate; + TAILQ_ENTRY(agentx_agentcaps) axa_axc_agentcaps; +}; + +struct agentx_region { + struct agentx_context *axr_axc; + struct ax_oid axr_oid; + uint8_t axr_timeout; + uint8_t axr_priority; + enum agentx_cstate axr_cstate; + enum agentx_dstate axr_dstate; + TAILQ_HEAD(, agentx_index) axr_indices; + TAILQ_HEAD(, agentx_object) axr_objects; + TAILQ_ENTRY(agentx_region) axr_axc_regions; +}; + +struct agentx_index { + struct agentx_region *axi_axr; + enum agentx_index_type axi_type; + struct ax_varbind axi_vb; + struct agentx_object **axi_object; + size_t axi_objectlen; + size_t axi_objectsize; + enum agentx_cstate axi_cstate; + enum agentx_dstate axi_dstate; + TAILQ_ENTRY(agentx_index) axi_axr_indices; +}; + +struct agentx_object { + struct agentx_region *axo_axr; + struct ax_oid axo_oid; + struct agentx_index *axo_index[AGENTX_OID_INDEX_MAX_LEN]; + size_t axo_indexlen; + int axo_implied; + uint8_t axo_timeout; + /* Prevent freeing object while in use by get and set requesets */ + uint32_t axo_lock; + void (*axo_get)(struct agentx_varbind *); + enum agentx_cstate axo_cstate; + enum agentx_dstate axo_dstate; + RB_ENTRY(agentx_object) axo_axc_objects; + TAILQ_ENTRY(agentx_object) axo_axr_objects; +}; + +struct agentx_varbind { + struct agentx_get *axv_axg; + struct agentx_object *axv_axo; + struct agentx_varbind_index { + struct agentx_index *axv_axi; + union ax_data axv_idata; + uint8_t axv_idatacomplete; + } axv_index[AGENTX_OID_INDEX_MAX_LEN]; + size_t axv_indexlen; + int axv_initialized; + int axv_include; + struct ax_varbind axv_vb; + struct ax_oid axv_start; + struct ax_oid axv_end; + enum ax_pdu_error axv_error; +}; + +#define AGENTX_GET_CTX(axg) (axg->axg_context_default ? NULL : \ + &(axg->axg_context)) +struct agentx_request { + uint32_t axr_packetid; + int (*axr_cb)(struct ax_pdu *, void *); + void *axr_cookie; + RB_ENTRY(agentx_request) axr_ax_requests; +}; + +static void agentx_start(struct agentx *); +static void agentx_finalize(struct agentx *, int); +static void agentx_wantwritenow(struct agentx *, int); +void (*agentx_wantwrite)(struct agentx *, int) = + agentx_wantwritenow; +static void agentx_reset(struct agentx *); +static void agentx_free_finalize(struct agentx *); +static int agentx_session_start(struct agentx_session *); +static int agentx_session_finalize(struct ax_pdu *, void *); +static int agentx_session_close(struct agentx_session *, + enum ax_close_reason); +static int agentx_session_close_finalize(struct ax_pdu *, void *); +static void agentx_session_free_finalize(struct agentx_session *); +static void agentx_session_reset(struct agentx_session *); +static void agentx_context_start(struct agentx_context *); +static void agentx_context_free_finalize(struct agentx_context *); +static void agentx_context_reset(struct agentx_context *); +static int agentx_agentcaps_start(struct agentx_agentcaps *); +static int agentx_agentcaps_finalize(struct ax_pdu *, void *); +static int agentx_agentcaps_close(struct agentx_agentcaps *); +static int agentx_agentcaps_close_finalize(struct ax_pdu *, void *); +static void agentx_agentcaps_free_finalize(struct agentx_agentcaps *); +static void agentx_agentcaps_reset(struct agentx_agentcaps *); +static int agentx_region_start(struct agentx_region *); +static int agentx_region_finalize(struct ax_pdu *, void *); +static int agentx_region_close(struct agentx_region *); +static int agentx_region_close_finalize(struct ax_pdu *, void *); +static void agentx_region_free_finalize(struct agentx_region *); +static void agentx_region_reset(struct agentx_region *); +static struct agentx_index *agentx_index(struct agentx_region *, + struct ax_varbind *, enum agentx_index_type); +static int agentx_index_start(struct agentx_index *); +static int agentx_index_finalize(struct ax_pdu *, void *); +static void agentx_index_free_finalize(struct agentx_index *); +static void agentx_index_reset(struct agentx_index *); +static int agentx_index_close(struct agentx_index *); +static int agentx_index_close_finalize(struct ax_pdu *, void *); +static int agentx_object_start(struct agentx_object *); +static int agentx_object_finalize(struct ax_pdu *, void *); +static int agentx_object_lock(struct agentx_object *); +static void agentx_object_unlock(struct agentx_object *); +static int agentx_object_close(struct agentx_object *); +static int agentx_object_close_finalize(struct ax_pdu *, void *); +static void agentx_object_free_finalize(struct agentx_object *); +static void agentx_object_reset(struct agentx_object *); +static int agentx_object_cmp(struct agentx_object *, + struct agentx_object *); +static void agentx_get_start(struct agentx_context *, + struct ax_pdu *); +static void agentx_get_finalize(struct agentx_get *); +static void agentx_get_free(struct agentx_get *); +static void agentx_varbind_start(struct agentx_varbind *); +static void agentx_varbind_finalize(struct agentx_varbind *); +static void agentx_varbind_nosuchobject(struct agentx_varbind *); +static void agentx_varbind_nosuchinstance(struct agentx_varbind *); +static void agentx_varbind_endofmibview(struct agentx_varbind *); +static void agentx_varbind_error_type(struct agentx_varbind *, + enum ax_pdu_error, int); +static int agentx_request(struct agentx *, uint32_t, + int (*)(struct ax_pdu *, void *), void *); +static int agentx_request_cmp(struct agentx_request *, + struct agentx_request *); +static int agentx_strcat(char **, const char *); + +RB_PROTOTYPE_STATIC(ax_requests, agentx_request, axr_ax_requests, + agentx_request_cmp) +RB_PROTOTYPE_STATIC(axc_objects, agentx_object, axo_axc_objects, + agentx_object_cmp) struct agentx * -agentx_new(int fd) +agentx(void (*nofd)(struct agentx *, void *, int), void *cookie) { struct agentx *ax; - if (fd == -1) { - errno = EINVAL; + if ((ax = calloc(1, sizeof(*ax))) == NULL) return NULL; + + ax->ax_nofd = nofd; + ax->ax_cookie = cookie; + ax->ax_fd = -1; + ax->ax_cstate = AX_CSTATE_CLOSE; + ax->ax_dstate = AX_DSTATE_OPEN; + TAILQ_INIT(&(ax->ax_sessions)); + TAILQ_INIT(&(ax->ax_getreqs)); + RB_INIT(&(ax->ax_requests)); + + agentx_start(ax); + + return ax; +} + +/* + * agentx_finalize is not a suitable name for a public API, + * but use it internally for consistency + */ +void +agentx_connect(struct agentx *ax, int fd) +{ + agentx_finalize(ax, fd); +} + +static void +agentx_start(struct agentx *ax) +{ +#ifdef AX_DEBUG + if (ax->ax_cstate != AX_CSTATE_CLOSE || + ax->ax_dstate != AX_DSTATE_OPEN) + agentx_log_ax_fatalx(ax, "%s: unexpected connect", __func__); +#endif + ax->ax_cstate = AX_CSTATE_WAITOPEN; + ax->ax_nofd(ax, ax->ax_cookie, 0); +} + +static void +agentx_finalize(struct agentx *ax, int fd) +{ + struct agentx_session *axs; + + if (ax->ax_cstate != AX_CSTATE_WAITOPEN) { +#ifdef AX_DEBUG + agentx_log_ax_fatalx(ax, "%s: agentx unexpected connect", + __func__); +#else + agentx_log_ax_warnx(ax, + "%s: agentx unexpected connect: ignoring", __func__); + return; +#endif + } + if ((ax->ax_ax = ax_new(fd)) == NULL) { + agentx_log_ax_warn(ax, "failed to initialize"); + close(fd); + agentx_reset(ax); + return; } - if ((ax = calloc(1, sizeof(*ax))) == NULL) - return NULL; + agentx_log_ax_info(ax, "new connection: %d", fd); + ax->ax_fd = fd; - if ((ax->ax_rbuf = malloc(512)) == NULL) - goto fail; - ax->ax_rbsize = 512; - ax->ax_byteorder = AGENTX_BYTE_ORDER_NATIVE; + ax->ax_cstate = AX_CSTATE_OPEN; - return ax; + TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) { + if (agentx_session_start(axs) == -1) + break; + } +} -fail: - free(ax); - return NULL; +static void +agentx_wantwritenow(struct agentx *ax, int fd) +{ + agentx_write(ax); +} + +static void +agentx_reset(struct agentx *ax) +{ + struct agentx_session *axs, *tsas; + struct agentx_request *axr; + struct agentx_get *axg; + + ax_free(ax->ax_ax); + ax->ax_ax = NULL; + ax->ax_fd = -1; + + ax->ax_cstate = AX_CSTATE_CLOSE; + + while ((axr = RB_MIN(ax_requests, &(ax->ax_requests))) != NULL) { + RB_REMOVE(ax_requests, &(ax->ax_requests), axr); + free(axr); + } + TAILQ_FOREACH_SAFE(axs, &(ax->ax_sessions), axs_ax_sessions, tsas) + agentx_session_reset(axs); + while (!TAILQ_EMPTY(&(ax->ax_getreqs))) { + axg = TAILQ_FIRST(&(ax->ax_getreqs)); + axg->axg_axc = NULL; + TAILQ_REMOVE(&(ax->ax_getreqs), axg, axg_ax_getreqs); + } + + if (ax->ax_dstate == AX_DSTATE_CLOSE) { + agentx_free_finalize(ax); + return; + } + + agentx_start(ax); } void agentx_free(struct agentx *ax) { + struct agentx_session *axs, *tsas; + if (ax == NULL) return; - close(ax->ax_fd); - free(ax->ax_rbuf); - free(ax->ax_wbuf); - free(ax->ax_packetids); + + if (ax->ax_dstate == AX_DSTATE_CLOSE) { +/* Malloc throws abort on invalid pointers as well */ + agentx_log_ax_fatalx(ax, "%s: double free", __func__); + } + ax->ax_dstate = AX_DSTATE_CLOSE; + + if (!TAILQ_EMPTY(&(ax->ax_sessions))) { + TAILQ_FOREACH_SAFE(axs, &(ax->ax_sessions), axs_ax_sessions, + tsas) { + if (axs->axs_dstate != AX_DSTATE_CLOSE) + agentx_session_free(axs); + } + } else + agentx_free_finalize(ax); +} + +static void +agentx_free_finalize(struct agentx *ax) +{ +#ifdef AX_DEBUG + if (ax->ax_dstate != AX_DSTATE_CLOSE) + agentx_log_ax_fatalx(ax, "%s: agentx not closing", + __func__); + if (!TAILQ_EMPTY(&(ax->ax_sessions))) + agentx_log_ax_fatalx(ax, "%s: agentx still has sessions", + __func__); + if (!RB_EMPTY(&(ax->ax_requests))) + agentx_log_ax_fatalx(ax, + "%s: agentx still has pending requests", __func__); +#endif + + ax_free(ax->ax_ax); + ax->ax_nofd(ax, ax->ax_cookie, 1); free(ax); } -struct agentx_pdu * -agentx_recv(struct agentx *ax) -{ - struct agentx_pdu *pdu; - struct agentx_pdu_header header; - struct agentx_pdu_response *response; - struct agentx_varbind *varbind; - struct agentx_pdu_searchrangelist *srl = NULL; - struct agentx_pdu_varbindlist *vbl; - struct agentx_searchrange *sr; - size_t rbsize, packetidx = 0, i, rawlen; - ssize_t nread; - uint8_t *u8; - uint8_t *rbuf; - int found; +struct agentx_session * +agentx_session(struct agentx *ax, uint32_t oid[], + size_t oidlen, const char *descr, uint8_t timeout) +{ + struct agentx_session *axs; + size_t i; - /* Only read a single packet at a time to make sure libevent triggers */ - if (ax->ax_rblen < AGENTX_PDU_HEADER) { - if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, - AGENTX_PDU_HEADER - ax->ax_rblen)) == 0) { - errno = ECONNRESET; - return NULL; - } - if (nread == -1) - return NULL; - ax->ax_rblen += nread; - if (ax->ax_rblen < AGENTX_PDU_HEADER) { - errno = EAGAIN; - return NULL; - } + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_ax_fatalx(ax, "%s: oidlen > %d", __func__, + AGENTX_OID_MAX_LEN); +#else + errno = EINVAL; + return NULL; +#endif } - u8 = ax->ax_rbuf; - header.aph_version = *u8++; - header.aph_type = *u8++; - header.aph_flags = *u8++; - u8++; - header.aph_sessionid = agentx_pdutoh32(&header, u8); - u8 += 4; - header.aph_transactionid = agentx_pdutoh32(&header, u8); - u8 += 4; - header.aph_packetid = agentx_pdutoh32(&header, u8); - u8 += 4; - header.aph_plength = agentx_pdutoh32(&header, u8); - - if (header.aph_version != 1) { - errno = EPROTO; + if ((axs = calloc(1, sizeof(*axs))) == NULL) + return NULL; + + axs->axs_ax = ax; + axs->axs_timeout = timeout; + for (i = 0; i < oidlen; i++) + axs->axs_oid.aoi_id[i] = oid[i]; + axs->axs_oid.aoi_idlen = oidlen; + axs->axs_descr.aos_string = (unsigned char *)strdup(descr); + if (axs->axs_descr.aos_string == NULL) { + free(axs); return NULL; } - if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) { - if (AGENTX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) { - rbsize = (((AGENTX_PDU_HEADER + header.aph_plength) - / 512) + 1) * 512; - if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize, - rbsize, sizeof(*rbuf))) == NULL) - return NULL; - ax->ax_rbsize = rbsize; - ax->ax_rbuf = rbuf; - } - nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, - header.aph_plength - (ax->ax_rblen - AGENTX_PDU_HEADER)); - if (nread == 0) - errno = ECONNRESET; - if (nread <= 0) - return NULL; - ax->ax_rblen += nread; - if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) { - errno = EAGAIN; + axs->axs_descr.aos_slen = strlen(descr); + axs->axs_cstate = AX_CSTATE_CLOSE; + axs->axs_dstate = AX_DSTATE_OPEN; + TAILQ_INIT(&(axs->axs_contexts)); + TAILQ_INSERT_HEAD(&(ax->ax_sessions), axs, axs_ax_sessions); + + if (ax->ax_cstate == AX_CSTATE_OPEN) + (void) agentx_session_start(axs); + + return axs; +} + +static int +agentx_session_start(struct agentx_session *axs) +{ + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (ax->ax_cstate != AX_CSTATE_OPEN || + axs->axs_cstate != AX_CSTATE_CLOSE || + axs->axs_dstate != AX_DSTATE_OPEN) + agentx_log_ax_fatalx(ax, "%s: unexpected session open", + __func__); +#endif + packetid = ax_open(ax->ax_ax, axs->axs_timeout, &(axs->axs_oid), + &(axs->axs_descr)); + if (packetid == 0) { + agentx_log_ax_warn(ax, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_OPEN)); + agentx_reset(ax); + return -1; + } + axs->axs_packetid = packetid; + agentx_log_ax_info(ax, "opening session"); + axs->axs_cstate = AX_CSTATE_WAITOPEN; + return agentx_request(ax, packetid, agentx_session_finalize, axs); +} + +static int +agentx_session_finalize(struct ax_pdu *pdu, void *cookie) +{ + struct agentx_session *axs = cookie; + struct agentx *ax = axs->axs_ax; + struct agentx_context *axc; + +#ifdef AX_DEBUG + if (axs->axs_cstate != AX_CSTATE_WAITOPEN) + agentx_log_ax_fatalx(ax, "%s: not expecting new session", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + agentx_log_ax_warnx(ax, "failed to open session: %s", + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + agentx_reset(ax); + return -1; + } + + axs->axs_id = pdu->ap_header.aph_sessionid; + axs->axs_cstate = AX_CSTATE_OPEN; + + if (axs->axs_dstate == AX_DSTATE_CLOSE) { + agentx_session_close(axs, AX_CLOSE_SHUTDOWN); + return 0; + } + + agentx_log_axs_info(axs, "open"); + + TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts) + agentx_context_start(axc); + return 0; +} + +static int +agentx_session_close(struct agentx_session *axs, + enum ax_close_reason reason) +{ + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (axs->axs_cstate != AX_CSTATE_OPEN) + agentx_log_ax_fatalx(ax, "%s: unexpected session close", + __func__); +#endif + if ((packetid = ax_close(ax->ax_ax, axs->axs_id, reason)) == 0) { + agentx_log_axs_warn(axs, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_CLOSE)); + agentx_reset(ax); + return -1; + } + + agentx_log_axs_info(axs, "closing session: %s", + ax_closereason2string(reason)); + + axs->axs_cstate = AX_CSTATE_WAITCLOSE; + return agentx_request(ax, packetid, agentx_session_close_finalize, + axs); +} + +static int +agentx_session_close_finalize(struct ax_pdu *pdu, void *cookie) +{ + struct agentx_session *axs = cookie; + struct agentx *ax = axs->axs_ax; + struct agentx_context *axc, *tsac; + +#ifdef AX_DEBUG + if (axs->axs_cstate != AX_CSTATE_WAITCLOSE) + agentx_log_axs_fatalx(axs, "%s: not expecting session close", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + agentx_log_axs_warnx(axs, "failed to close session: %s", + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + agentx_reset(ax); + return -1; + } + + axs->axs_cstate = AX_CSTATE_CLOSE; + + agentx_log_axs_info(axs, "closed"); + + TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, tsac) + agentx_context_reset(axc); + + if (axs->axs_dstate == AX_DSTATE_CLOSE) + agentx_session_free_finalize(axs); + else { + if (ax->ax_cstate == AX_CSTATE_OPEN) + if (agentx_session_start(axs) == -1) + return -1; + } + return 0; +} + +void +agentx_session_free(struct agentx_session *axs) +{ + struct agentx_context *axc, *tsac; + + if (axs == NULL) + return; + + if (axs->axs_dstate == AX_DSTATE_CLOSE) + agentx_log_axs_fatalx(axs, "%s: double free", __func__); + + axs->axs_dstate = AX_DSTATE_CLOSE; + + if (axs->axs_cstate == AX_CSTATE_OPEN) + (void) agentx_session_close(axs, AX_CLOSE_SHUTDOWN); + + TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, tsac) { + if (axc->axc_dstate != AX_DSTATE_CLOSE) + agentx_context_free(axc); + } + + if (axs->axs_cstate == AX_CSTATE_CLOSE) + agentx_session_free_finalize(axs); +} + +static void +agentx_session_free_finalize(struct agentx_session *axs) +{ + struct agentx *ax = axs->axs_ax; + +#ifdef AX_DEBUG + if (axs->axs_cstate != AX_CSTATE_CLOSE) + agentx_log_axs_fatalx(axs, "%s: free without closing", + __func__); + if (!TAILQ_EMPTY(&(axs->axs_contexts))) + agentx_log_axs_fatalx(axs, + "%s: agentx still has contexts", __func__); +#endif + + TAILQ_REMOVE(&(ax->ax_sessions), axs, axs_ax_sessions); + free(axs->axs_descr.aos_string); + free(axs); + + if (TAILQ_EMPTY(&(ax->ax_sessions)) && ax->ax_dstate == AX_DSTATE_CLOSE) + agentx_free_finalize(ax); +} + +static void +agentx_session_reset(struct agentx_session *axs) +{ + struct agentx_context *axc, *tsac; + + axs->axs_cstate = AX_CSTATE_CLOSE; + + TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, tsac) + agentx_context_reset(axc); + + if (axs->axs_dstate == AX_DSTATE_CLOSE) + agentx_session_free_finalize(axs); +} + +struct agentx_context * +agentx_context(struct agentx_session *axs, const char *name) +{ + struct agentx_context *axc; + + if (axs->axs_dstate == AX_DSTATE_CLOSE) + agentx_log_axs_fatalx(axs, "%s: use after free", __func__); + + if ((axc = calloc(1, sizeof(*axc))) == NULL) + return NULL; + + axc->axc_axs = axs; + axc->axc_name_default = (name == NULL); + if (name != NULL) { + axc->axc_name.aos_string = (unsigned char *)strdup(name); + if (axc->axc_name.aos_string == NULL) { + free(axc); return NULL; } + axc->axc_name.aos_slen = strlen(name); } + axc->axc_cstate = axs->axs_cstate == AX_CSTATE_OPEN ? + AX_CSTATE_OPEN : AX_CSTATE_CLOSE; + axc->axc_dstate = AX_DSTATE_OPEN; + TAILQ_INIT(&(axc->axc_agentcaps)); + TAILQ_INIT(&(axc->axc_regions)); + + TAILQ_INSERT_HEAD(&(axs->axs_contexts), axc, axc_axs_contexts); - if ((pdu = calloc(1, sizeof(*pdu))) == NULL) + return axc; +} + +static void +agentx_context_start(struct agentx_context *axc) +{ + struct agentx_agentcaps *axa; + struct agentx_region *axr; + +#ifdef AX_DEBUG + if (axc->axc_cstate != AX_CSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected context start", + __func__); +#endif + axc->axc_cstate = AX_CSTATE_OPEN; + + TAILQ_FOREACH(axa, &(axc->axc_agentcaps), axa_axc_agentcaps) { + if (agentx_agentcaps_start(axa) == -1) + return; + } + TAILQ_FOREACH(axr, &(axc->axc_regions), axr_axc_regions) { + if (agentx_region_start(axr) == -1) + return; + } +} + +uint32_t +agentx_context_uptime(struct agentx_context *axc) +{ + struct timespec cur, res; + + if (axc->axc_sysuptimespec.tv_sec == 0 && + axc->axc_sysuptimespec.tv_nsec == 0) + return 0; + + (void) clock_gettime(CLOCK_MONOTONIC, &cur); + + timespecsub(&cur, &(axc->axc_sysuptimespec), &res); + + return axc->axc_sysuptime + + (uint32_t) ((res.tv_sec * 100) + (res.tv_nsec / 10000000)); +} + +struct agentx_object * +agentx_context_object_find(struct agentx_context *axc, + const uint32_t oid[], size_t oidlen, int active, int instance) +{ + struct agentx_object *axo, axo_search; + size_t i; + + for (i = 0; i < oidlen; i++) + axo_search.axo_oid.aoi_id[i] = oid[i]; + axo_search.axo_oid.aoi_idlen = oidlen; + + axo = RB_FIND(axc_objects, &(axc->axc_objects), &axo_search); + while (axo == NULL && !instance && axo_search.axo_oid.aoi_idlen > 0) { + axo = RB_FIND(axc_objects, &(axc->axc_objects), &axo_search); + axo_search.axo_oid.aoi_idlen--; + } + if (active && axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN) return NULL; + return axo; +} - memcpy(&(pdu->ap_header), &header, sizeof(header)); - -#if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE) - { - char chars[4]; - int print = 1; - - fprintf(stderr, "received packet:\n"); - for (i = 0; i < pdu->ap_header.aph_plength + AGENTX_PDU_HEADER; - i++) { - fprintf(stderr, "%02hhx ", ax->ax_rbuf[i]); - chars[i % 4] = ax->ax_rbuf[i]; - if (!isprint(ax->ax_rbuf[i])) - print = 0; - if (i % 4 == 3) { - if (print) - fprintf(stderr, "%.4s", chars); - fprintf(stderr, "\n"); - print = 1; - } - } +struct agentx_object * +agentx_context_object_nfind(struct agentx_context *axc, + const uint32_t oid[], size_t oidlen, int active, int inclusive) +{ + struct agentx_object *axo, axo_search; + size_t i; + + for (i = 0; i < oidlen; i++) + axo_search.axo_oid.aoi_id[i] = oid[i]; + axo_search.axo_oid.aoi_idlen = oidlen; + + axo = RB_NFIND(axc_objects, &(axc->axc_objects), &axo_search); + if (!inclusive && axo != NULL && + ax_oid_cmp(&(axo_search.axo_oid), &(axo->axo_oid)) <= 0) { + axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo); } + + while (active && axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN) + axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo); + return axo; +} + +void +agentx_context_free(struct agentx_context *axc) +{ + struct agentx_agentcaps *axa, *tsaa; + struct agentx_region *axr, *tsar; + + if (axc == NULL) + return; + +#ifdef AX_DEBUG + if (axc->axc_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: double free", __func__); #endif + axc->axc_dstate = AX_DSTATE_CLOSE; - u8 = (ax->ax_rbuf) + AGENTX_PDU_HEADER; - rawlen = pdu->ap_header.aph_plength; - if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) { - nread = agentx_pdutoostring(&header, &(pdu->ap_context), u8, - rawlen); - if (nread == -1) - goto fail; - rawlen -= nread; - u8 += nread; + TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps, + tsaa) { + if (axa->axa_dstate != AX_DSTATE_CLOSE) + agentx_agentcaps_free(axa); } + TAILQ_FOREACH_SAFE(axr, &(axc->axc_regions), axr_axc_regions, tsar) { + if (axr->axr_dstate != AX_DSTATE_CLOSE) + agentx_region_free(axr); + } +} - switch (pdu->ap_header.aph_type) { - case AGENTX_PDU_TYPE_GETBULK: - if (rawlen < 4) { - errno = EPROTO; - goto fail; - } - pdu->ap_payload.ap_getbulk.ap_nonrep = - agentx_pdutoh16(&header, u8); - u8 += 2; - pdu->ap_payload.ap_getbulk.ap_maxrep = - agentx_pdutoh16(&header, u8); - u8 += 2; - srl = &(pdu->ap_payload.ap_getbulk.ap_srl); - rawlen -= 4; - /* FALLTHROUGH */ - case AGENTX_PDU_TYPE_GET: - case AGENTX_PDU_TYPE_GETNEXT: - if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_GETBULK) - srl = &(pdu->ap_payload.ap_srl); - while (rawlen > 0 ) { - srl->ap_nsr++; - sr = reallocarray(srl->ap_sr, srl->ap_nsr, sizeof(*sr)); - if (sr == NULL) - goto fail; - srl->ap_sr = sr; - sr += (srl->ap_nsr - 1); - if ((nread = agentx_pdutooid(&header, &(sr->asr_start), - u8, rawlen)) == -1) - goto fail; - rawlen -= nread; - u8 += nread; - if ((nread = agentx_pdutooid(&header, &(sr->asr_stop), - u8, rawlen)) == -1) - goto fail; - rawlen -= nread; - u8 += nread; - } - break; - case AGENTX_PDU_TYPE_TESTSET: - vbl = &(pdu->ap_payload.ap_vbl); - while (rawlen > 0) { - varbind = recallocarray(vbl->ap_varbind, - vbl->ap_nvarbind, vbl->ap_nvarbind + 1, - sizeof(*(vbl->ap_varbind))); - if (varbind == NULL) - goto fail; - vbl->ap_varbind = varbind; - nread = agentx_pdutovarbind(&header, - &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen); - if (nread == -1) - goto fail; - vbl->ap_nvarbind++; - u8 += nread; - rawlen -= nread; - } - break; - case AGENTX_PDU_TYPE_COMMITSET: - case AGENTX_PDU_TYPE_UNDOSET: - case AGENTX_PDU_TYPE_CLEANUPSET: - if (rawlen != 0) { - errno = EPROTO; - goto fail; - } - break; - case AGENTX_PDU_TYPE_RESPONSE: - if (ax->ax_packetids != NULL) { - found = 0; - for (i = 0; ax->ax_packetids[i] != 0; i++) { - if (ax->ax_packetids[i] == - pdu->ap_header.aph_packetid) { - packetidx = i; - found = 1; - } - } - if (found) { - ax->ax_packetids[packetidx] = - ax->ax_packetids[i - 1]; - ax->ax_packetids[i - 1] = 0; - } else { - errno = EPROTO; - goto fail; - } - } - if (rawlen < 8) { - errno = EPROTO; - goto fail; - } - response = &(pdu->ap_payload.ap_response); - response->ap_uptime = agentx_pdutoh32(&header, u8); - u8 += 4; - response->ap_error = agentx_pdutoh16(&header, u8); - u8 += 2; - response->ap_index = agentx_pdutoh16(&header, u8); - u8 += 2; - rawlen -= 8; - while (rawlen > 0) { - varbind = recallocarray(response->ap_varbindlist, - response->ap_nvarbind, response->ap_nvarbind + 1, - sizeof(*(response->ap_varbindlist))); - if (varbind == NULL) - goto fail; - response->ap_varbindlist = varbind; - nread = agentx_pdutovarbind(&header, - &(response->ap_varbindlist[response->ap_nvarbind]), - u8, rawlen); - if (nread == -1) - goto fail; - response->ap_nvarbind++; - u8 += nread; - rawlen -= nread; - } - break; - default: - pdu->ap_payload.ap_raw = malloc(pdu->ap_header.aph_plength); - if (pdu->ap_payload.ap_raw == NULL) - goto fail; - memcpy(pdu->ap_payload.ap_raw, ax->ax_rbuf + AGENTX_PDU_HEADER, - pdu->ap_header.aph_plength); - break; +static void +agentx_context_free_finalize(struct agentx_context *axc) +{ + struct agentx_session *axs = axc->axc_axs; + +#ifdef AX_DEBUG + if (axc->axc_dstate != AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected context free", + __func__); +#endif + if (!TAILQ_EMPTY(&(axc->axc_regions)) || + !TAILQ_EMPTY(&(axc->axc_agentcaps))) + return; + TAILQ_REMOVE(&(axs->axs_contexts), axc, axc_axs_contexts); + free(axc->axc_name.aos_string); + free(axc); +} + +static void +agentx_context_reset(struct agentx_context *axc) +{ + struct agentx_agentcaps *axa, *tsaa; + struct agentx_region *axr, *tsar; + + axc->axc_cstate = AX_CSTATE_CLOSE; + axc->axc_sysuptimespec.tv_sec = 0; + axc->axc_sysuptimespec.tv_nsec = 0; + + TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps, tsaa) + agentx_agentcaps_reset(axa); + TAILQ_FOREACH_SAFE(axr, &(axc->axc_regions), axr_axc_regions, tsar) + agentx_region_reset(axr); + + if (axc->axc_dstate == AX_DSTATE_CLOSE) + agentx_context_free_finalize(axc); +} + +struct agentx_agentcaps * +agentx_agentcaps(struct agentx_context *axc, uint32_t oid[], + size_t oidlen, const char *descr) +{ + struct agentx_agentcaps *axa; + size_t i; + + if (axc->axc_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: use after free", __func__); + + if ((axa = calloc(1, sizeof(*axa))) == NULL) + return NULL; + + axa->axa_axc = axc; + for (i = 0; i < oidlen; i++) + axa->axa_oid.aoi_id[i] = oid[i]; + axa->axa_oid.aoi_idlen = oidlen; + axa->axa_descr.aos_string = (unsigned char *)strdup(descr); + if (axa->axa_descr.aos_string == NULL) { + free(axa); + return NULL; } + axa->axa_descr.aos_slen = strlen(descr); + axa->axa_cstate = AX_CSTATE_CLOSE; + axa->axa_dstate = AX_DSTATE_OPEN; - ax->ax_rblen = 0; + TAILQ_INSERT_TAIL(&(axc->axc_agentcaps), axa, axa_axc_agentcaps); - return pdu; -fail: - agentx_pdu_free(pdu); - return NULL; + if (axc->axc_cstate == AX_CSTATE_OPEN) + agentx_agentcaps_start(axa); + + return axa; } static int -agentx_pdu_need(struct agentx *ax, size_t need) +agentx_agentcaps_start(struct agentx_agentcaps *axa) { - uint8_t *wbuf; - size_t wbsize; + struct agentx_context *axc = axa->axa_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (axc->axc_cstate != AX_CSTATE_OPEN || + axa->axa_cstate != AX_CSTATE_CLOSE || + axa->axa_dstate != AX_DSTATE_OPEN) + agentx_log_axc_fatalx(axc, + "%s: unexpected region registration", __func__); +#endif - if (ax->ax_wbtlen >= ax->ax_wbsize) { - wbsize = ((ax->ax_wbtlen / 512) + 1) * 512; - wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1); - if (wbuf == NULL) { - ax->ax_wbtlen = ax->ax_wblen; - return -1; - } - ax->ax_wbsize = wbsize; - ax->ax_wbuf = wbuf; + packetid = ax_addagentcaps(ax->ax_ax, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), &(axa->axa_oid), &(axa->axa_descr)); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_ADDAGENTCAPS)); + agentx_reset(ax); + return -1; + } + agentx_log_axc_info(axc, "agentcaps %s: opening", + ax_oid2string(&(axa->axa_oid))); + axa->axa_cstate = AX_CSTATE_WAITOPEN; + return agentx_request(ax, packetid, agentx_agentcaps_finalize, + axa); +} + +static int +agentx_agentcaps_finalize(struct ax_pdu *pdu, void *cookie) +{ + struct agentx_agentcaps *axa = cookie; + struct agentx_context *axc = axa->axa_axc; + +#ifdef AX_DEBUG + if (axa->axa_cstate != AX_CSTATE_WAITOPEN) + agentx_log_axc_fatalx(axc, + "%s: not expecting agentcaps open", __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + /* Agentcaps failing is nothing too serious */ + agentx_log_axc_warn(axc, "agentcaps %s: %s", + ax_oid2string(&(axa->axa_oid)), + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + axa->axa_cstate = AX_CSTATE_CLOSE; + return 0; } + axa->axa_cstate = AX_CSTATE_OPEN; + + agentx_log_axc_info(axc, "agentcaps %s: open", + ax_oid2string(&(axa->axa_oid))); + + if (axa->axa_dstate == AX_DSTATE_CLOSE) + agentx_agentcaps_close(axa); + return 0; } -ssize_t -agentx_send(struct agentx *ax) +static int +agentx_agentcaps_close(struct agentx_agentcaps *axa) +{ + struct agentx_context *axc = axa->axa_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (axa->axa_cstate != AX_CSTATE_OPEN) + agentx_log_axc_fatalx(axc, "%s: unexpected agentcaps close", + __func__); +#endif + + axa->axa_cstate = AX_CSTATE_WAITCLOSE; + if (axs->axs_cstate == AX_CSTATE_WAITCLOSE) + return 0; + + packetid = ax_removeagentcaps(ax->ax_ax, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), &(axa->axa_oid)); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_REMOVEAGENTCAPS)); + agentx_reset(ax); + return -1; + } + agentx_log_axc_info(axc, "agentcaps %s: closing", + ax_oid2string(&(axa->axa_oid))); + return agentx_request(ax, packetid, + agentx_agentcaps_close_finalize, axa); +} + +static int +agentx_agentcaps_close_finalize(struct ax_pdu *pdu, void *cookie) { - ssize_t nwrite; + struct agentx_agentcaps *axa = cookie; + struct agentx_context *axc = axa->axa_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + +#ifdef AX_DEBUG + if (axa->axa_cstate != AX_CSTATE_WAITCLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected agentcaps close", + __func__); +#endif - if (ax->ax_wblen != ax->ax_wbtlen) { - errno = EALREADY; + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + agentx_log_axc_warnx(axc, "agentcaps %s: %s", + ax_oid2string(&(axa->axa_oid)), + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + agentx_reset(ax); return -1; } - if (ax->ax_wblen == 0) + axa->axa_cstate = AX_CSTATE_CLOSE; + + agentx_log_axc_info(axc, "agentcaps %s: closed", + ax_oid2string(&(axa->axa_oid))); + + if (axa->axa_dstate == AX_DSTATE_CLOSE) { + agentx_agentcaps_free_finalize(axa); return 0; + } else { + if (axc->axc_cstate == AX_CSTATE_OPEN) { + if (agentx_agentcaps_start(axa) == -1) + return -1; + } + } + return 0; +} -#if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE) - { - size_t i; - char chars[4]; - int print = 1; - - fprintf(stderr, "sending packet:\n"); - for (i = 0; i < ax->ax_wblen; i++) { - fprintf(stderr, "%02hhx ", ax->ax_wbuf[i]); - chars[i % 4] = ax->ax_wbuf[i]; - if (!isprint(ax->ax_wbuf[i])) - print = 0; - if (i % 4 == 3) { - if (print) - fprintf(stderr, "%.4s", chars); - fprintf(stderr, "\n"); - print = 1; - } +void +agentx_agentcaps_free(struct agentx_agentcaps *axa) +{ + if (axa == NULL) + return; + + if (axa->axa_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axa->axa_axc, "%s: double free", + __func__); + + axa->axa_dstate = AX_DSTATE_CLOSE; + + if (axa->axa_cstate == AX_CSTATE_OPEN) { + if (agentx_agentcaps_close(axa) == -1) + return; + } + + if (axa->axa_cstate == AX_CSTATE_CLOSE) + agentx_agentcaps_free_finalize(axa); +} + +static void +agentx_agentcaps_free_finalize(struct agentx_agentcaps *axa) +{ + struct agentx_context *axc = axa->axa_axc; + +#ifdef AX_DEBUG + if (axa->axa_dstate != AX_DSTATE_CLOSE || + axa->axa_cstate != AX_CSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected free", __func__); +#endif + + TAILQ_REMOVE(&(axc->axc_agentcaps), axa, axa_axc_agentcaps); + free(axa->axa_descr.aos_string); + free(axa); + + if (axc->axc_dstate == AX_DSTATE_CLOSE) + agentx_context_free_finalize(axc); +} + +static void +agentx_agentcaps_reset(struct agentx_agentcaps *axa) +{ + axa->axa_cstate = AX_CSTATE_CLOSE; + + if (axa->axa_dstate == AX_DSTATE_CLOSE) + agentx_agentcaps_free_finalize(axa); +} + +struct agentx_region * +agentx_region(struct agentx_context *axc, uint32_t oid[], + size_t oidlen, uint8_t timeout) +{ + struct agentx_region *axr; + struct ax_oid tmpoid; + size_t i; + + if (axc->axc_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: use after free", __func__); + if (oidlen < 1) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axc, "%s: oidlen == 0", __func__); +#else + errno = EINVAL; + return NULL; +#endif + } + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axc, "%s: oidlen > %d", __func__, + AGENTX_OID_MAX_LEN); +#else + errno = EINVAL; + return NULL; +#endif + } + + for (i = 0; i < oidlen; i++) + tmpoid.aoi_id[i] = oid[i]; + tmpoid.aoi_idlen = oidlen; + TAILQ_FOREACH(axr, &(axc->axc_regions), axr_axc_regions) { + if (ax_oid_cmp(&(axr->axr_oid), &tmpoid) == 0) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axc, + "%s: duplicate region registration", __func__); +#else + errno = EINVAL; + return NULL; +#endif } } + + if ((axr = calloc(1, sizeof(*axr))) == NULL) + return NULL; + + axr->axr_axc = axc; + axr->axr_timeout = timeout; + axr->axr_priority = AX_PRIORITY_DEFAULT; + bcopy(&tmpoid, &(axr->axr_oid), sizeof(axr->axr_oid)); + axr->axr_cstate = AX_CSTATE_CLOSE; + axr->axr_dstate = AX_DSTATE_OPEN; + TAILQ_INIT(&(axr->axr_indices)); + TAILQ_INIT(&(axr->axr_objects)); + + TAILQ_INSERT_HEAD(&(axc->axc_regions), axr, axr_axc_regions); + + if (axc->axc_cstate == AX_CSTATE_OPEN) + (void) agentx_region_start(axr); + + return axr; +} + +static int +agentx_region_start(struct agentx_region *axr) +{ + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (axc->axc_cstate != AX_CSTATE_OPEN || + axr->axr_cstate != AX_CSTATE_CLOSE || + axr->axr_dstate != AX_DSTATE_OPEN) + agentx_log_axc_fatalx(axc, + "%s: unexpected region registration", __func__); #endif - if ((nwrite = send(ax->ax_fd, ax->ax_wbuf, ax->ax_wblen, - MSG_NOSIGNAL | MSG_DONTWAIT)) == -1) + packetid = ax_register(ax->ax_ax, 0, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), axr->axr_timeout, axr->axr_priority, + 0, &(axr->axr_oid), 0); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_REGISTER)); + agentx_reset(ax); return -1; + } + agentx_log_axc_info(axc, "region %s: opening", + ax_oid2string(&(axr->axr_oid))); + axr->axr_cstate = AX_CSTATE_WAITOPEN; + return agentx_request(ax, packetid, agentx_region_finalize, axr); +} + +static int +agentx_region_finalize(struct ax_pdu *pdu, void *cookie) +{ + struct agentx_region *axr = cookie; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct agentx_index *axi; + struct agentx_object *axo; + +#ifdef AX_DEBUG + if (axr->axr_cstate != AX_CSTATE_WAITOPEN) + agentx_log_axc_fatalx(axc, "%s: not expecting region open", + __func__); +#endif - memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite); - ax->ax_wblen -= nwrite; - ax->ax_wbtlen = ax->ax_wblen; + if (pdu->ap_payload.ap_response.ap_error == AX_PDU_ERROR_NOERROR) { + axr->axr_cstate = AX_CSTATE_OPEN; + agentx_log_axc_info(axc, "region %s: open", + ax_oid2string(&(axr->axr_oid))); + } else if (pdu->ap_payload.ap_response.ap_error == + AX_PDU_ERROR_DUPLICATEREGISTRATION) { + axr->axr_cstate = AX_CSTATE_CLOSE; + /* Try at lower priority: first come first serve */ + if ((++axr->axr_priority) != 0) { + agentx_log_axc_warnx(axc, "region %s: duplicate, " + "reducing priority", + ax_oid2string(&(axr->axr_oid))); + return agentx_region_start(axr); + } + agentx_log_axc_info(axc, "region %s: duplicate, can't " + "reduce priority, ignoring", + ax_oid2string(&(axr->axr_oid))); + } else if (pdu->ap_payload.ap_response.ap_error == + AX_PDU_ERROR_REQUESTDENIED) { + axr->axr_cstate = AX_CSTATE_CLOSE; + agentx_log_axc_warnx(axc, "region %s: %s", + ax_oid2string(&(axr->axr_oid)), + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + /* + * If we can't register a region, related objects are useless. + * But no need to retry. + */ + return 0; + } else { + agentx_log_axc_info(axc, "region %s: %s", + ax_oid2string(&(axr->axr_oid)), + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + agentx_reset(ax); + return -1; + } - return ax->ax_wblen; + if (axr->axr_dstate == AX_DSTATE_CLOSE) { + if (agentx_region_close(axr) == -1) + return -1; + } else { + TAILQ_FOREACH(axi, &(axr->axr_indices), axi_axr_indices) { + if (agentx_index_start(axi) == -1) + return -1; + } + TAILQ_FOREACH(axo, &(axr->axr_objects), axo_axr_objects) { + if (agentx_object_start(axo) == -1) + return -1; + } + } + return 0; } -uint32_t -agentx_open(struct agentx *ax, uint8_t timeout, struct agentx_oid *oid, - struct agentx_ostring *descr) +static int +agentx_region_close(struct agentx_region *axr) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_OPEN, 0, 0, 0, 0, - NULL) == -1) - return 0; - agentx_pdu_need(ax, 4); - ax->ax_wbuf[ax->ax_wbtlen++] = timeout; - memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); - ax->ax_wbtlen += 3; - if (agentx_pdu_add_oid(ax, oid, 0) == -1) - return 0; - if (agentx_pdu_add_str(ax, descr) == -1) + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (axr->axr_cstate != AX_CSTATE_OPEN) + agentx_log_axc_fatalx(axc, "%s: unexpected region close", + __func__); +#endif + + axr->axr_cstate = AX_CSTATE_WAITCLOSE; + if (axs->axs_cstate == AX_CSTATE_WAITCLOSE) return 0; - return agentx_pdu_queue(ax); + packetid = ax_unregister(ax->ax_ax, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), axr->axr_priority, 0, &(axr->axr_oid), + 0); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_UNREGISTER)); + agentx_reset(ax); + return -1; + } + agentx_log_axc_info(axc, "region %s: closing", + ax_oid2string(&(axr->axr_oid))); + return agentx_request(ax, packetid, agentx_region_close_finalize, + axr); } -uint32_t -agentx_close(struct agentx *ax, uint32_t sessionid, - enum agentx_close_reason reason) +static int +agentx_region_close_finalize(struct ax_pdu *pdu, void *cookie) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0, - NULL) == -1) - return 0; + struct agentx_region *axr = cookie; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + +#ifdef AX_DEBUG + if (axr->axr_cstate != AX_CSTATE_WAITCLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected region close", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + agentx_log_axc_warnx(axc, "closing %s: %s", + ax_oid2string(&(axr->axr_oid)), + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + agentx_reset(ax); + return -1; + } - if (agentx_pdu_need(ax, 4) == -1) + axr->axr_priority = AX_PRIORITY_DEFAULT; + axr->axr_cstate = AX_CSTATE_CLOSE; + + agentx_log_axc_info(axc, "region %s: closed", + ax_oid2string(&(axr->axr_oid))); + + if (axr->axr_dstate == AX_DSTATE_CLOSE) { + agentx_region_free_finalize(axr); return 0; - ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason; - memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); - ax->ax_wbtlen += 3; + } else { + if (axc->axc_cstate == AX_CSTATE_OPEN) { + if (agentx_region_start(axr) == -1) + return -1; + } + } + return 0; +} - return agentx_pdu_queue(ax); +void +agentx_region_free(struct agentx_region *axr) +{ + struct agentx_index *axi, *tsai; + struct agentx_object *axo, *tsao; + + if (axr == NULL) + return; + + if (axr->axr_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axr->axr_axc, "%s: double free", + __func__); + + axr->axr_dstate = AX_DSTATE_CLOSE; + + TAILQ_FOREACH_SAFE(axi, &(axr->axr_indices), axi_axr_indices, tsai) { + if (axi->axi_dstate != AX_DSTATE_CLOSE) + agentx_index_free(axi); + } + + TAILQ_FOREACH_SAFE(axo, &(axr->axr_objects), axo_axr_objects, tsao) { + if (axo->axo_dstate != AX_DSTATE_CLOSE) + agentx_object_free(axo); + } + + if (axr->axr_cstate == AX_CSTATE_OPEN) { + if (agentx_region_close(axr) == -1) + return; + } + + if (axr->axr_cstate == AX_CSTATE_CLOSE) + agentx_region_free_finalize(axr); } -uint32_t -agentx_indexallocate(struct agentx *ax, uint8_t flags, uint32_t sessionid, - struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb) +static void +agentx_region_free_finalize(struct agentx_region *axr) { - if (flags & ~(AGENTX_PDU_FLAG_NEW_INDEX | AGENTX_PDU_FLAG_ANY_INDEX)) { + struct agentx_context *axc = axr->axr_axc; + +#ifdef AX_DEBUG + if (axr->axr_dstate != AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected free", __func__); +#endif + + if (!TAILQ_EMPTY(&(axr->axr_indices)) || + !TAILQ_EMPTY(&(axr->axr_objects))) + return; + + if (axr->axr_cstate != AX_CSTATE_CLOSE) + return; + + TAILQ_REMOVE(&(axc->axc_regions), axr, axr_axc_regions); + free(axr); + + if (axc->axc_dstate == AX_DSTATE_CLOSE) + agentx_context_free_finalize(axc); +} + +static void +agentx_region_reset(struct agentx_region *axr) +{ + struct agentx_index *axi, *tsai; + struct agentx_object *axo, *tsao; + + axr->axr_cstate = AX_CSTATE_CLOSE; + axr->axr_priority = AX_PRIORITY_DEFAULT; + + TAILQ_FOREACH_SAFE(axi, &(axr->axr_indices), axi_axr_indices, tsai) + agentx_index_reset(axi); + TAILQ_FOREACH_SAFE(axo, &(axr->axr_objects), axo_axr_objects, tsao) + agentx_object_reset(axo); + + if (axr->axr_dstate == AX_DSTATE_CLOSE) + agentx_region_free_finalize(axr); +} + +struct agentx_index * +agentx_index_integer_new(struct agentx_region *axr, uint32_t oid[], + size_t oidlen) +{ + struct ax_varbind vb; + size_t i; + + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); errno = EINVAL; - return 0; + return NULL; +#endif } - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXALLOCATE, flags, - sessionid, 0, 0, context) == -1) - return 0; + vb.avb_type = AX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_uint32 = 0; - if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1) - return 0; + return agentx_index(axr, &vb, AXI_TYPE_NEW); +} - return agentx_pdu_queue(ax); +struct agentx_index * +agentx_index_integer_any(struct agentx_region *axr, uint32_t oid[], + size_t oidlen) +{ + struct ax_varbind vb; + size_t i; + + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_uint32 = 0; + + return agentx_index(axr, &vb, AXI_TYPE_ANY); } -uint32_t -agentx_indexdeallocate(struct agentx *ax, uint32_t sessionid, - struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb) +struct agentx_index * +agentx_index_integer_value(struct agentx_region *axr, uint32_t oid[], + size_t oidlen, uint32_t value) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXDEALLOCATE, 0, - sessionid, 0, 0, context) == -1) - return 0; + struct ax_varbind vb; + size_t i; - if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1) - return 0; + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_uint32 = value; - return agentx_pdu_queue(ax); + return agentx_index(axr, &vb, AXI_TYPE_VALUE); } -uint32_t -agentx_addagentcaps(struct agentx *ax, uint32_t sessionid, - struct agentx_ostring *context, struct agentx_oid *id, - struct agentx_ostring *descr) +struct agentx_index * +agentx_index_integer_dynamic(struct agentx_region *axr, uint32_t oid[], + size_t oidlen) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_ADDAGENTCAPS, 0, - sessionid, 0, 0, context) == -1) - return 0; - if (agentx_pdu_add_oid(ax, id, 0) == -1) - return 0; - if (agentx_pdu_add_str(ax, descr) == -1) - return 0; + struct ax_varbind vb; + size_t i; + + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; - return agentx_pdu_queue(ax); + return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC); } -uint32_t -agentx_removeagentcaps(struct agentx *ax, uint32_t sessionid, - struct agentx_ostring *context, struct agentx_oid *id) +struct agentx_index * +agentx_index_string_dynamic(struct agentx_region *axr, uint32_t oid[], + size_t oidlen) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REMOVEAGENTCAPS, 0, - sessionid, 0, 0, context) == -1) - return 0; - if (agentx_pdu_add_oid(ax, id, 0) == -1) - return 0; + struct ax_varbind vb; + size_t i; - return agentx_pdu_queue(ax); + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AX_DATA_TYPE_OCTETSTRING; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_ostring.aos_slen = 0; + vb.avb_data.avb_ostring.aos_string = NULL; + return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC); } -uint32_t -agentx_register(struct agentx *ax, uint8_t flags, uint32_t sessionid, - struct agentx_ostring *context, uint8_t timeout, uint8_t priority, - uint8_t range_subid, struct agentx_oid *subtree, uint32_t upperbound) +struct agentx_index * +agentx_index_nstring_dynamic(struct agentx_region *axr, uint32_t oid[], + size_t oidlen, size_t vlen) { - if (flags & ~(AGENTX_PDU_FLAG_INSTANCE_REGISTRATION)) { + struct ax_varbind vb; + size_t i; + + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); errno = EINVAL; - return 0; + return NULL; +#endif + } + if (vlen == 0 || vlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid string " + "length: %zu\n", __func__, vlen); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: invalid string " + "length: %zu\n", __func__, vlen); + errno = EINVAL; + return NULL; +#endif } - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REGISTER, flags, - sessionid, 0, 0, context) == -1) - return 0; + vb.avb_type = AX_DATA_TYPE_OCTETSTRING; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_ostring.aos_slen = vlen; + vb.avb_data.avb_ostring.aos_string = NULL; - if (agentx_pdu_need(ax, 4) == -1) - return 0; - ax->ax_wbuf[ax->ax_wbtlen++] = timeout; - ax->ax_wbuf[ax->ax_wbtlen++] = priority; - ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; - ax->ax_wbuf[ax->ax_wbtlen++] = 0; - if (agentx_pdu_add_oid(ax, subtree, 0) == -1) - return 0; - if (range_subid != 0) { - if (agentx_pdu_add_uint32(ax, upperbound) == -1) - return 0; + return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC); +} + +struct agentx_index * +agentx_index_oid_dynamic(struct agentx_region *axr, uint32_t oid[], + size_t oidlen) +{ + struct ax_varbind vb; + size_t i; + + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif } - return agentx_pdu_queue(ax); + vb.avb_type = AX_DATA_TYPE_OID; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_oid.aoi_idlen = 0; + + return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC); } -uint32_t -agentx_unregister(struct agentx *ax, uint32_t sessionid, - struct agentx_ostring *context, uint8_t priority, uint8_t range_subid, - struct agentx_oid *subtree, uint32_t upperbound) +struct agentx_index * +agentx_index_noid_dynamic(struct agentx_region *axr, uint32_t oid[], + size_t oidlen, size_t vlen) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_UNREGISTER, 0, - sessionid, 0, 0, context) == -1) - return 0; + struct ax_varbind vb; + size_t i; - if (agentx_pdu_need(ax, 4) == -1) - return 0; - ax->ax_wbuf[ax->ax_wbtlen++] = 0; - ax->ax_wbuf[ax->ax_wbtlen++] = priority; - ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; - ax->ax_wbuf[ax->ax_wbtlen++] = 0; - if (agentx_pdu_add_oid(ax, subtree, 0) == -1) - return 0; - if (range_subid != 0) { - if (agentx_pdu_add_uint32(ax, upperbound) == -1) - return 0; + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif } + if (vlen == 0 || vlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid string " + "length: %zu\n", __func__, vlen); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: invalid string " + "length: %zu\n", __func__, vlen); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AX_DATA_TYPE_OID; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_oid.aoi_idlen = vlen; - return agentx_pdu_queue(ax); + return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC); } -int -agentx_response(struct agentx *ax, uint32_t sessionid, uint32_t transactionid, - uint32_t packetid, struct agentx_ostring *context, uint32_t sysuptime, - uint16_t error, uint16_t index, struct agentx_varbind *vblist, size_t nvb) +struct agentx_index * +agentx_index_ipaddress_dynamic(struct agentx_region *axr, uint32_t oid[], + size_t oidlen) { - if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_RESPONSE, 0, sessionid, - transactionid, packetid, context) == -1) - return -1; + struct ax_varbind vb; + size_t i; + + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AX_DATA_TYPE_IPADDRESS; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_data.avb_ostring.aos_string = NULL; + vb.avb_oid.aoi_idlen = oidlen; + + return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC); +} + +static struct agentx_index * +agentx_index(struct agentx_region *axr, struct ax_varbind *vb, + enum agentx_index_type type) +{ + struct agentx_index *axi; + + if (axr->axr_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axr->axr_axc, "%s: use after free", + __func__); + if (ax_oid_cmp(&(axr->axr_oid), &(vb->avb_oid)) != -2) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oid is not child " + "of region %s", __func__, + ax_oid2string(&(vb->avb_oid))); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oid is not child of " + "region %s", __func__, ax_oid2string(&(vb->avb_oid))); + errno = EINVAL; + return NULL; +#endif + } - if (agentx_pdu_add_uint32(ax, sysuptime) == -1 || - agentx_pdu_add_uint16(ax, error) == -1 || - agentx_pdu_add_uint16(ax, index) == -1) + if ((axi = calloc(1, sizeof(*axi))) == NULL) + return NULL; + + axi->axi_axr = axr; + axi->axi_type = type; + bcopy(vb, &(axi->axi_vb), sizeof(*vb)); + axi->axi_cstate = AX_CSTATE_CLOSE; + axi->axi_dstate = AX_DSTATE_OPEN; + TAILQ_INSERT_HEAD(&(axr->axr_indices), axi, axi_axr_indices); + + if (axr->axr_cstate == AX_CSTATE_OPEN) + agentx_index_start(axi); + + return axi; +} + +static int +agentx_index_start(struct agentx_index *axi) +{ + struct agentx_region *axr = axi->axi_axr; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + int flags = 0; + +#ifdef AX_DEBUG + if (axr->axr_cstate != AX_CSTATE_OPEN || + axi->axi_cstate != AX_CSTATE_CLOSE || + axi->axi_dstate != AX_DSTATE_OPEN) + agentx_log_axc_fatalx(axc, "%s: unexpected index allocation", + __func__); +#endif + + axi->axi_cstate = AX_CSTATE_WAITOPEN; + + if (axi->axi_type == AXI_TYPE_NEW) + flags = AX_PDU_FLAG_NEW_INDEX; + else if (axi->axi_type == AXI_TYPE_ANY) + flags = AX_PDU_FLAG_ANY_INDEX; + else if (axi->axi_type == AXI_TYPE_DYNAMIC) { + agentx_index_finalize(NULL, axi); + return 0; + } + + /* We might be able to bundle, but if we fail we'd have to reorganise */ + packetid = ax_indexallocate(ax->ax_ax, flags, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), &(axi->axi_vb), 1); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_INDEXDEALLOCATE)); + agentx_reset(ax); return -1; + } + if (axi->axi_type == AXI_TYPE_VALUE) + agentx_log_axc_info(axc, "index %s: allocating '%u'", + ax_oid2string(&(axi->axi_vb.avb_oid)), + axi->axi_vb.avb_data.avb_uint32); + else if (axi->axi_type == AXI_TYPE_ANY) + agentx_log_axc_info(axc, "index %s: allocating any index", + ax_oid2string(&(axi->axi_vb.avb_oid))); + else if (axi->axi_type == AXI_TYPE_NEW) + agentx_log_axc_info(axc, "index %s: allocating new index", + ax_oid2string(&(axi->axi_vb.avb_oid))); + + return agentx_request(ax, packetid, agentx_index_finalize, axi); +} + +static int +agentx_index_finalize(struct ax_pdu *pdu, void *cookie) +{ + struct agentx_index *axi = cookie; + struct agentx_region *axr = axi->axi_axr; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct ax_pdu_response *resp; + size_t i; + +#ifdef AX_DEBUG + if (axi->axi_cstate != AX_CSTATE_WAITOPEN) + agentx_log_axc_fatalx(axc, + "%s: not expecting index allocate", __func__); +#endif + if (axi->axi_type == AXI_TYPE_DYNAMIC) { + axi->axi_cstate = AX_CSTATE_OPEN; + return 0; + } - if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1) + resp = &(pdu->ap_payload.ap_response); + if (resp->ap_error != AX_PDU_ERROR_NOERROR) { + axi->axi_cstate = AX_CSTATE_CLOSE; + agentx_log_axc_warnx(axc, "index %s: %s", + ax_oid2string(&(axr->axr_oid)), + ax_error2string(resp->ap_error)); + return 0; + } + axi->axi_cstate = AX_CSTATE_OPEN; + if (resp->ap_nvarbind != 1) { + agentx_log_axc_warnx(axc, "index %s: unexpected number of " + "indices", ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); return -1; - if (agentx_pdu_queue(ax) == 0) + } + if (resp->ap_varbindlist[0].avb_type != axi->axi_vb.avb_type) { + agentx_log_axc_warnx(axc, "index %s: unexpected index type", + ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); return -1; + } + if (ax_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), + &(axi->axi_vb.avb_oid)) != 0) { + agentx_log_axc_warnx(axc, "index %s: unexpected oid", + ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); + return -1; + } + + switch (axi->axi_vb.avb_type) { + case AX_DATA_TYPE_INTEGER: + if (axi->axi_type == AXI_TYPE_NEW || + axi->axi_type == AXI_TYPE_ANY) + axi->axi_vb.avb_data.avb_uint32 = + resp->ap_varbindlist[0].avb_data.avb_uint32; + else if (axi->axi_vb.avb_data.avb_uint32 != + resp->ap_varbindlist[0].avb_data.avb_uint32) { + agentx_log_axc_warnx(axc, "index %s: unexpected " + "index value", ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); + return -1; + } + agentx_log_axc_info(axc, "index %s: allocated '%u'", + ax_oid2string(&(axi->axi_vb.avb_oid)), + axi->axi_vb.avb_data.avb_uint32); + break; + default: + agentx_log_axc_fatalx(axc, "%s: Unsupported index type", + __func__); + } + + if (axi->axi_dstate == AX_DSTATE_CLOSE) + return agentx_index_close(axi); + + /* TODO Make use of range_subid register */ + for (i = 0; i < axi->axi_objectlen; i++) { + if (axi->axi_object[i]->axo_dstate == AX_DSTATE_OPEN) { + if (agentx_object_start(axi->axi_object[i]) == -1) + return -1; + } + } return 0; } void -agentx_pdu_free(struct agentx_pdu *pdu) +agentx_index_free(struct agentx_index *axi) { size_t i; - struct agentx_pdu_response *response; + struct agentx_object *axo; - if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) - free(pdu->ap_context.aos_string); + if (axi == NULL) + return; - switch (pdu->ap_header.aph_type) { - case AGENTX_PDU_TYPE_GET: - case AGENTX_PDU_TYPE_GETNEXT: - case AGENTX_PDU_TYPE_GETBULK: - free(pdu->ap_payload.ap_srl.ap_sr); - break; - case AGENTX_PDU_TYPE_RESPONSE: - response = &(pdu->ap_payload.ap_response); - for (i = 0; i < response->ap_nvarbind; i++) - agentx_varbind_free(&(response->ap_varbindlist[i])); - free(response->ap_varbindlist); - break; - default: - free(pdu->ap_payload.ap_raw); - break; + if (axi->axi_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axi->axi_axr->axr_axc, + "%s: double free", __func__); + + /* TODO Do a range_subid unregister before freeing */ + for (i = 0; i < axi->axi_objectlen; i++) { + axo = axi->axi_object[i]; + if (axo->axo_dstate != AX_DSTATE_CLOSE) { + agentx_object_free(axo); + if (axi->axi_object[i] != axo) + i--; + } } - free(pdu); + + axi->axi_dstate = AX_DSTATE_CLOSE; + + if (axi->axi_cstate == AX_CSTATE_OPEN) + (void) agentx_index_close(axi); + else if (axi->axi_cstate == AX_CSTATE_CLOSE) + agentx_index_free_finalize(axi); } -void -agentx_varbind_free(struct agentx_varbind *varbind) +static void +agentx_index_free_finalize(struct agentx_index *axi) { - switch (varbind->avb_type) { - case AGENTX_DATA_TYPE_OCTETSTRING: - case AGENTX_DATA_TYPE_IPADDRESS: - case AGENTX_DATA_TYPE_OPAQUE: - free(varbind->avb_data.avb_ostring.aos_string); - break; - default: - break; + struct agentx_region *axr = axi->axi_axr; + +#ifdef AX_DEBUG + if (axi->axi_dstate != AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axr->axr_axc, "%s: unexpected free", + __func__); + if (axi->axi_cstate != AX_CSTATE_CLOSE) + agentx_log_axc_fatalx(axr->axr_axc, + "%s: free without deallocating", __func__); +#endif + + if (axi->axi_objectlen != 0) + return; + + TAILQ_REMOVE(&(axr->axr_indices), axi, axi_axr_indices); + ax_varbind_free(&(axi->axi_vb)); + free(axi->axi_object); + free(axi); + if (axr->axr_dstate == AX_DSTATE_CLOSE) + agentx_region_free_finalize(axr); +} + +static void +agentx_index_reset(struct agentx_index *axi) +{ + axi->axi_cstate = AX_CSTATE_CLOSE; + + if (axi->axi_dstate == AX_DSTATE_CLOSE) + agentx_index_free_finalize(axi); +} + +static int +agentx_index_close(struct agentx_index *axi) +{ + struct agentx_region *axr = axi->axi_axr; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + uint32_t packetid; + +#ifdef AX_DEBUG + if (axi->axi_cstate != AX_CSTATE_OPEN) + agentx_log_axc_fatalx(axc, + "%s: unexpected index deallocation", __func__); +#endif + + axi->axi_cstate = AX_CSTATE_WAITCLOSE; + if (axs->axs_cstate == AX_CSTATE_WAITCLOSE) + return 0; + + /* We might be able to bundle, but if we fail we'd have to reorganise */ + packetid = ax_indexdeallocate(ax->ax_ax, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), &(axi->axi_vb), 1); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_INDEXDEALLOCATE)); + agentx_reset(ax); + return -1; } + agentx_log_axc_info(axc, "index %s: deallocating", + ax_oid2string(&(axi->axi_vb.avb_oid))); + return agentx_request(ax, packetid, agentx_index_close_finalize, + axi); } -const char * -agentx_error2string(enum agentx_pdu_error error) -{ - static char buffer[64]; - switch (error) { - case AGENTX_PDU_ERROR_NOERROR: - return "No error"; - case AGENTX_PDU_ERROR_GENERR: - return "Generic error"; - case AGENTX_PDU_ERROR_NOACCESS: - return "No access"; - case AGENTX_PDU_ERROR_WRONGTYPE: - return "Wrong type"; - case AGENTX_PDU_ERROR_WRONGLENGTH: - return "Wrong length"; - case AGENTX_PDU_ERROR_WRONGENCODING: - return "Wrong encoding"; - case AGENTX_PDU_ERROR_WRONGVALUE: - return "Wrong value"; - case AGENTX_PDU_ERROR_NOCREATION: - return "No creation"; - case AGENTX_PDU_ERROR_INCONSISTENTVALUE: - return "Inconsistent value"; - case AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE: - return "Resource unavailable"; - case AGENTX_PDU_ERROR_COMMITFAILED: - return "Commit failed"; - case AGENTX_PDU_ERROR_UNDOFAILED: - return "Undo failed"; - case AGENTX_PDU_ERROR_NOTWRITABLE: - return "Not writable"; - case AGENTX_PDU_ERROR_INCONSISTENTNAME: - return "Inconsistent name"; - case AGENTX_PDU_ERROR_OPENFAILED: - return "Open Failed"; - case AGENTX_PDU_ERROR_NOTOPEN: - return "Not open"; - case AGENTX_PDU_ERROR_INDEXWRONGTYPE: - return "Index wrong type"; - case AGENTX_PDU_ERROR_INDEXALREADYALLOCATED: - return "Index already allocated"; - case AGENTX_PDU_ERROR_INDEXNONEAVAILABLE: - return "Index none available"; - case AGENTX_PDU_ERROR_INDEXNOTALLOCATED: - return "Index not allocated"; - case AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT: - return "Unsupported context"; - case AGENTX_PDU_ERROR_DUPLICATEREGISTRATION: - return "Duplicate registration"; - case AGENTX_PDU_ERROR_UNKNOWNREGISTRATION: - return "Unkown registration"; - case AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS: - return "Unknown agent capabilities"; - case AGENTX_PDU_ERROR_PARSEERROR: - return "Parse error"; - case AGENTX_PDU_ERROR_REQUESTDENIED: - return "Request denied"; - case AGENTX_PDU_ERROR_PROCESSINGERROR: - return "Processing error"; - } - snprintf(buffer, sizeof(buffer), "Unknown error: %d", error); - return buffer; -} - -const char * -agentx_pdutype2string(enum agentx_pdu_type type) -{ - static char buffer[64]; - switch(type) { - case AGENTX_PDU_TYPE_OPEN: - return "agentx-Open-PDU"; - case AGENTX_PDU_TYPE_CLOSE: - return "agentx-Close-PDU"; - case AGENTX_PDU_TYPE_REGISTER: - return "agentx-Register-PDU"; - case AGENTX_PDU_TYPE_UNREGISTER: - return "agentx-Unregister-PDU"; - case AGENTX_PDU_TYPE_GET: - return "agentx-Get-PDU"; - case AGENTX_PDU_TYPE_GETNEXT: - return "agentx-GetNext-PDU"; - case AGENTX_PDU_TYPE_GETBULK: - return "agentx-GetBulk-PDU"; - case AGENTX_PDU_TYPE_TESTSET: - return "agentx-TestSet-PDU"; - case AGENTX_PDU_TYPE_COMMITSET: - return "agentx-CommitSet-PDU"; - case AGENTX_PDU_TYPE_UNDOSET: - return "agentx-UndoSet-PDU"; - case AGENTX_PDU_TYPE_CLEANUPSET: - return "agentx-CleanupSet-PDU"; - case AGENTX_PDU_TYPE_NOTIFY: - return "agentx-Notify-PDU"; - case AGENTX_PDU_TYPE_PING: - return "agentx-Ping-PDU"; - case AGENTX_PDU_TYPE_INDEXALLOCATE: - return "agentx-IndexAllocate-PDU"; - case AGENTX_PDU_TYPE_INDEXDEALLOCATE: - return "agentx-IndexDeallocate-PDU"; - case AGENTX_PDU_TYPE_ADDAGENTCAPS: - return "agentx-AddAgentCaps-PDU"; - case AGENTX_PDU_TYPE_REMOVEAGENTCAPS: - return "agentx-RemoveAgentCaps-PDU"; - case AGENTX_PDU_TYPE_RESPONSE: - return "agentx-Response-PDU"; - } - snprintf(buffer, sizeof(buffer), "Unknown type: %d", type); - return buffer; -} - -const char * -agentx_closereason2string(enum agentx_close_reason reason) -{ - static char buffer[64]; - - switch (reason) { - case AGENTX_CLOSE_OTHER: - return "Undefined reason"; - case AGENTX_CLOSEN_PARSEERROR: - return "Too many AgentX parse errors from peer"; - case AGENTX_CLOSE_PROTOCOLERROR: - return "Too many AgentX protocol errors from peer"; - case AGENTX_CLOSE_TIMEOUTS: - return "Too many timeouts waiting for peer"; - case AGENTX_CLOSE_SHUTDOWN: - return "shutting down"; - case AGENTX_CLOSE_BYMANAGER: - return "Manager shuts down"; - } - snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason); - return buffer; -} - -const char * -agentx_oid2string(struct agentx_oid *oid) -{ - return agentx_oidrange2string(oid, 0, 0); -} - -const char * -agentx_oidrange2string(struct agentx_oid *oid, uint8_t range_subid, - uint32_t upperbound) -{ - static char buf[1024]; - char *p; - size_t i, rest; - int ret; - - rest = sizeof(buf); - p = buf; - for (i = 0; i < oid->aoi_idlen; i++) { - if (range_subid != 0 && range_subid - 1 == (uint8_t)i) - ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i], - upperbound); - else - ret = snprintf(p, rest, ".%u", oid->aoi_id[i]); - if ((size_t) ret >= rest) { - snprintf(buf, sizeof(buf), "Couldn't parse oid"); - return buf; +static int +agentx_index_close_finalize(struct ax_pdu *pdu, void *cookie) +{ + struct agentx_index *axi = cookie; + struct agentx_region *axr = axi->axi_axr; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct ax_pdu_response *resp = &(pdu->ap_payload.ap_response); + +#ifdef AX_DEBUG + if (axi->axi_cstate != AX_CSTATE_WAITCLOSE) + agentx_log_axc_fatalx(axc, "%s: unexpected indexdeallocate", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + agentx_log_axc_warnx(axc, + "index %s: couldn't deallocate: %s", + ax_oid2string(&(axi->axi_vb.avb_oid)), + ax_error2string(resp->ap_error)); + agentx_reset(ax); + return -1; + } + + if (resp->ap_nvarbind != 1) { + agentx_log_axc_warnx(axc, + "index %s: unexpected number of indices", + ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); + return -1; + } + if (resp->ap_varbindlist[0].avb_type != axi->axi_vb.avb_type) { + agentx_log_axc_warnx(axc, "index %s: unexpected index type", + ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); + return -1; + } + if (ax_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), + &(axi->axi_vb.avb_oid)) != 0) { + agentx_log_axc_warnx(axc, "index %s: unexpected oid", + ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); + return -1; + } + switch (axi->axi_vb.avb_type) { + case AX_DATA_TYPE_INTEGER: + if (axi->axi_vb.avb_data.avb_uint32 != + resp->ap_varbindlist[0].avb_data.avb_uint32) { + agentx_log_axc_warnx(axc, + "index %s: unexpected index value", + ax_oid2string(&(axr->axr_oid))); + agentx_reset(ax); + return -1; } - p += ret; - rest -= (size_t) ret; + break; + default: + agentx_log_axc_fatalx(axc, "%s: Unsupported index type", + __func__); + } + + axi->axi_cstate = AX_CSTATE_CLOSE; + + agentx_log_axc_info(axc, "index %s: deallocated", + ax_oid2string(&(axi->axi_vb.avb_oid))); + + if (axi->axi_dstate == AX_DSTATE_CLOSE) { + agentx_index_free_finalize(axi); + } else if (axr->axr_cstate == AX_CSTATE_OPEN) { + if (agentx_index_start(axi) == -1) + return -1; } - return buf; + return 0; } -const char * -agentx_varbind2string(struct agentx_varbind *vb) +struct agentx_object * +agentx_object(struct agentx_region *axr, uint32_t oid[], size_t oidlen, + struct agentx_index *axi[], size_t axilen, int implied, + void (*get)(struct agentx_varbind *)) { - static char buf[1024]; - char tmpbuf[1024]; - size_t i, bufleft; - int ishex = 0; - char *p; - int ret; + struct agentx_object *axo, **tsao, axo_search; + struct agentx_index *lsai; + int ready = 1; + size_t i, j; + + if (axr->axr_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axr->axr_axc, "%s: use after free", + __func__); + if (oidlen < 1) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen == 0", + __func__); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen == 0", + __func__); + errno = EINVAL; + return NULL; +#endif + } + if (oidlen > AGENTX_OID_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: oidlen > %d", + __func__, AGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + if (axilen > AGENTX_OID_INDEX_MAX_LEN) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: indexlen > %d", + __func__, AGENTX_OID_INDEX_MAX_LEN); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: indexlen > %d", + __func__, AGENTX_OID_INDEX_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } - switch (vb->avb_type) { - case AGENTX_DATA_TYPE_INTEGER: - snprintf(buf, sizeof(buf), "%s: (int)%u", - agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); - break; - case AGENTX_DATA_TYPE_OCTETSTRING: - for (i = 0; - i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) { - if (!isprint(vb->avb_data.avb_ostring.aos_string[i])) - ishex = 1; + for (i = 0; i < oidlen; i++) + axo_search.axo_oid.aoi_id[i] = oid[i]; + axo_search.axo_oid.aoi_idlen = oidlen; + + do { + if (RB_FIND(axc_objects, &(axr->axr_axc->axc_objects), + &axo_search) != NULL) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid " + "parent child object relationship", __func__); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: invalid " + "parent child object relationship", __func__); + errno = EINVAL; + return NULL; +#endif } - if (ishex) { - p = tmpbuf; - bufleft = sizeof(tmpbuf); - for (i = 0; - i < vb->avb_data.avb_ostring.aos_slen; i++) { - ret = snprintf(p, bufleft, " %02hhX", - vb->avb_data.avb_ostring.aos_string[i]); - if (ret >= (int) bufleft) { - p = strrchr(p, ' '); - strlcpy(p, "...", 4); - break; - } - p += 3; - bufleft -= 3; + axo_search.axo_oid.aoi_idlen--; + } while (axo_search.axo_oid.aoi_idlen > 0); + axo_search.axo_oid.aoi_idlen = oidlen; + axo = RB_NFIND(axc_objects, &(axr->axr_axc->axc_objects), &axo_search); + if (axo != NULL && + ax_oid_cmp(&(axo->axo_oid), &(axo_search.axo_oid)) == 2) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid parent " + "child object relationship", __func__); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: invalid parent " + "child object relationship", __func__); + errno = EINVAL; + return NULL; +#endif + } + if (implied == 1) { + lsai = axi[axilen - 1]; + if (lsai->axi_vb.avb_type == AX_DATA_TYPE_OCTETSTRING) { + if (lsai->axi_vb.avb_data.avb_ostring.aos_slen != 0) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, + "%s: implied can only be used on strings " + "of dynamic length", __func__); +#else + agentx_log_axc_warnx(axr->axr_axc, + "%s: implied can only be used on strings " + "of dynamic length", __func__); + errno = EINVAL; + return NULL; +#endif } - ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s", - agentx_oid2string(&(vb->avb_oid)), tmpbuf); - if (ret >= (int) sizeof(buf)) { - p = strrchr(buf, ' '); - strlcpy(p, "...", 4); + } else if (lsai->axi_vb.avb_type == AX_DATA_TYPE_OID) { + if (lsai->axi_vb.avb_data.avb_oid.aoi_idlen != 0) { +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, + "%s: implied can only be used on oids of " + "dynamic length", __func__); +#else + agentx_log_axc_warnx(axr->axr_axc, + "%s: implied can only be used on oids of " + "dynamic length", __func__); + errno = EINVAL; + return NULL; +#endif } } else { - ret = snprintf(buf, sizeof(buf), "%s: (string)", - agentx_oid2string(&(vb->avb_oid))); - if (ret >= (int) sizeof(buf)) { - snprintf(buf, sizeof(buf), "<too large OID>: " - "(string)<too large string>"); - break; - } - p = buf + ret; - bufleft = (int) sizeof(buf) - ret; - if (snprintf(p, bufleft, "%.*s", - vb->avb_data.avb_ostring.aos_slen, - vb->avb_data.avb_ostring.aos_string) >= - (int) bufleft) { - p = buf + sizeof(buf) - 4; - strlcpy(p, "...", 4); - } - } - break; - case AGENTX_DATA_TYPE_NULL: - snprintf(buf, sizeof(buf), "%s: <null>", - agentx_oid2string(&(vb->avb_oid))); - break; - case AGENTX_DATA_TYPE_OID: - strlcpy(tmpbuf, - agentx_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf)); - snprintf(buf, sizeof(buf), "%s: (oid)%s", - agentx_oid2string(&(vb->avb_oid)), tmpbuf); - break; - case AGENTX_DATA_TYPE_IPADDRESS: - if (vb->avb_data.avb_ostring.aos_slen != 4) { - snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>", - agentx_oid2string(&(vb->avb_oid))); - break; +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axr->axr_axc, "%s: implied " + "can only be set on oid and string indices", + __func__); +#else + agentx_log_axc_warnx(axr->axr_axc, "%s: implied can " + "only be set on oid and string indices", __func__); + errno = EINVAL; + return NULL; +#endif } - if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string, - tmpbuf, sizeof(tmpbuf)) == NULL) { - snprintf(buf, sizeof(buf), "%s: (ipaddress)" - "<unparseable>: %s", - agentx_oid2string(&(vb->avb_oid)), - strerror(errno)); - break; + } + + ready = axr->axr_cstate == AX_CSTATE_OPEN; + if ((axo = calloc(1, sizeof(*axo))) == NULL) + return NULL; + axo->axo_axr = axr; + bcopy(&(axo_search.axo_oid), &(axo->axo_oid), sizeof(axo->axo_oid)); + for (i = 0; i < axilen; i++) { + axo->axo_index[i] = axi[i]; + if (axi[i]->axi_objectlen == axi[i]->axi_objectsize) { + tsao = recallocarray(axi[i]->axi_object, + axi[i]->axi_objectlen, axi[i]->axi_objectlen + 1, + sizeof(*axi[i]->axi_object)); + if (tsao == NULL) { + free(axo); + return NULL; + } + axi[i]->axi_object = tsao; + axi[i]->axi_objectsize = axi[i]->axi_objectlen + 1; } - snprintf(buf, sizeof(buf), "%s: (ipaddress)%s", - agentx_oid2string(&(vb->avb_oid)), tmpbuf); - break; - case AGENTX_DATA_TYPE_COUNTER32: - snprintf(buf, sizeof(buf), "%s: (counter32)%u", - agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); - break; - case AGENTX_DATA_TYPE_GAUGE32: - snprintf(buf, sizeof(buf), "%s: (gauge32)%u", - agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); - break; - case AGENTX_DATA_TYPE_TIMETICKS: - snprintf(buf, sizeof(buf), "%s: (timeticks)%u", - agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); - break; - case AGENTX_DATA_TYPE_OPAQUE: - p = tmpbuf; - bufleft = sizeof(tmpbuf); - for (i = 0; - i < vb->avb_data.avb_ostring.aos_slen; i++) { - ret = snprintf(p, bufleft, " %02hhX", - vb->avb_data.avb_ostring.aos_string[i]); - if (ret >= (int) bufleft) { - p = strrchr(p, ' '); - strlcpy(p, "...", 4); + for (j = 0; j < axi[i]->axi_objectlen; j++) { + if (ax_oid_cmp(&(axo->axo_oid), + &(axi[i]->axi_object[j]->axo_oid)) < 0) { + memmove(&(axi[i]->axi_object[j + 1]), + &(axi[i]->axi_object[j]), + sizeof(*(axi[i]->axi_object)) * + (axi[i]->axi_objectlen - j)); break; } - p += 3; - bufleft -= 3; } - ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s", - agentx_oid2string(&(vb->avb_oid)), tmpbuf); - if (ret >= (int) sizeof(buf)) { - p = strrchr(buf, ' '); - strlcpy(p, "...", 4); + axi[i]->axi_object[j] = axo; + axi[i]->axi_objectlen++; + if (axi[i]->axi_cstate != AX_CSTATE_OPEN) + ready = 0; + } + axo->axo_indexlen = axilen; + axo->axo_implied = implied; + axo->axo_timeout = 0; + axo->axo_lock = 0; + axo->axo_get = get; + axo->axo_cstate = AX_CSTATE_CLOSE; + axo->axo_dstate = AX_DSTATE_OPEN; + + TAILQ_INSERT_TAIL(&(axr->axr_objects), axo, axo_axr_objects); + RB_INSERT(axc_objects, &(axr->axr_axc->axc_objects), axo); + + if (ready) + agentx_object_start(axo); + + return axo; +} + +static int +agentx_object_start(struct agentx_object *axo) +{ + struct agentx_region *axr = axo->axo_axr; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct ax_oid oid; + char oids[1024]; + size_t i; + int needregister = 0; + uint32_t packetid; + uint8_t flags = AX_PDU_FLAG_INSTANCE_REGISTRATION; + +#ifdef AX_DEBUG + if (axr->axr_cstate != AX_CSTATE_OPEN || + axo->axo_cstate != AX_CSTATE_CLOSE || + axo->axo_dstate != AX_DSTATE_OPEN) + agentx_log_axc_fatalx(axc, + "%s: unexpected object registration", __func__); +#endif + + if (axo->axo_timeout != 0) + needregister = 1; + for (i = 0; i < axo->axo_indexlen; i++) { + if (axo->axo_index[i]->axi_cstate != AX_CSTATE_OPEN) + return 0; + if (axo->axo_index[i]->axi_type != AXI_TYPE_DYNAMIC) + needregister = 1; + } + if (!needregister) { + axo->axo_cstate = AX_CSTATE_WAITOPEN; + agentx_object_finalize(NULL, axo); + return 0; + } + + bcopy(&(axo->axo_oid), &(oid), sizeof(oid)); + for (i = 0; i < axo->axo_indexlen; i++) { + if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) { + flags = 0; + break; } - break; - case AGENTX_DATA_TYPE_COUNTER64: - snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64, - agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64); - break; - case AGENTX_DATA_TYPE_NOSUCHOBJECT: - snprintf(buf, sizeof(buf), "%s: <noSuchObject>", - agentx_oid2string(&(vb->avb_oid))); - break; - case AGENTX_DATA_TYPE_NOSUCHINSTANCE: - snprintf(buf, sizeof(buf), "%s: <noSuchInstance>", - agentx_oid2string(&(vb->avb_oid))); - break; - case AGENTX_DATA_TYPE_ENDOFMIBVIEW: - snprintf(buf, sizeof(buf), "%s: <endOfMibView>", - agentx_oid2string(&(vb->avb_oid))); - break; +#ifdef AX_DEBUG + if (axo->axo_index[i]->axi_vb.avb_type != + AX_DATA_TYPE_INTEGER) + agentx_log_axc_fatalx(axc, + "%s: Unsupported allocated index type", __func__); +#endif + oid.aoi_id[oid.aoi_idlen++] = + axo->axo_index[i]->axi_vb.avb_data.avb_uint32; + } + packetid = ax_register(ax->ax_ax, flags, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), axo->axo_timeout, + AX_PRIORITY_DEFAULT, 0, &oid, 0); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_REGISTER)); + agentx_reset(ax); + return -1; } - return buf; + strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids)); + agentx_log_axc_info(axc, "object %s (%s %s): opening", + oids, flags ? "instance" : "region", ax_oid2string(&(oid))); + axo->axo_cstate = AX_CSTATE_WAITOPEN; + return agentx_request(ax, packetid, agentx_object_finalize, axo); } -int -agentx_oid_cmp(struct agentx_oid *o1, struct agentx_oid *o2) +static int +agentx_object_finalize(struct ax_pdu *pdu, void *cookie) { - size_t i, min; + struct agentx_object *axo = cookie; + struct agentx_context *axc = axo->axo_axr->axr_axc; + struct ax_oid oid; + char oids[1024]; + size_t i; + uint8_t flags = 1; + +#ifdef AX_DEBUG + if (axo->axo_cstate != AX_CSTATE_WAITOPEN) + agentx_log_axc_fatalx(axc, "%s: not expecting object open", + __func__); +#endif + + if (pdu == NULL) { + axo->axo_cstate = AX_CSTATE_OPEN; + return 0; + } + + bcopy(&(axo->axo_oid), &oid, sizeof(oid)); + for (i = 0; i < axo->axo_indexlen; i++) { + if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AX_DEBUG + if (axo->axo_index[i]->axi_vb.avb_type != + AX_DATA_TYPE_INTEGER) + agentx_log_axc_fatalx(axc, + "%s: Unsupported allocated index type", __func__); +#endif + + oid.aoi_id[oid.aoi_idlen++] = + axo->axo_index[i]->axi_vb.avb_data.avb_uint32; + } + strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids)); + + /* + * We should only be here for table objects with registered indices. + * If we fail here something is misconfigured and the admin should fix + * it. + */ + if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) { + axo->axo_cstate = AX_CSTATE_CLOSE; + agentx_log_axc_info(axc, "object %s (%s %s): %s", + oids, flags ? "instance" : "region", ax_oid2string(&oid), + ax_error2string(pdu->ap_payload.ap_response.ap_error)); + if (axo->axo_dstate == AX_DSTATE_CLOSE) + return agentx_object_close_finalize(NULL, axo); + return 0; + } + axo->axo_cstate = AX_CSTATE_OPEN; + agentx_log_axc_info(axc, "object %s (%s %s): open", oids, + flags ? "instance" : "region", ax_oid2string(&oid)); + + if (axo->axo_dstate == AX_DSTATE_CLOSE) + return agentx_object_close(axo); - min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen; - for (i = 0; i < min; i++) { - if (o1->aoi_id[i] < o2->aoi_id[i]) - return -1; - if (o1->aoi_id[i] > o2->aoi_id[i]) - return 1; - } - /* o1 is parent of o2 */ - if (o1->aoi_idlen < o2->aoi_idlen) - return -2; - /* o1 is child of o2 */ - if (o1->aoi_idlen > o2->aoi_idlen) - return 2; return 0; } -int -agentx_oid_add(struct agentx_oid *oid, uint32_t value) +static int +agentx_object_lock(struct agentx_object *axo) { - if (oid->aoi_idlen == AGENTX_OID_MAX_LEN) + if (axo->axo_lock == UINT32_MAX) { + agentx_log_axc_warnx(axo->axo_axr->axr_axc, + "%s: axo_lock == %u", __func__, UINT32_MAX); return -1; - oid->aoi_id[oid->aoi_idlen++] = value; + } + axo->axo_lock++; return 0; } -static uint32_t -agentx_pdu_queue(struct agentx *ax) +static void +agentx_object_unlock(struct agentx_object *axo) { - struct agentx_pdu_header header; - uint32_t packetid, plength; - size_t wbtlen = ax->ax_wbtlen; +#ifdef AX_DEBUG + if (axo->axo_lock == 0) + agentx_log_axc_fatalx(axo->axo_axr->axr_axc, + "%s: axo_lock == 0", __func__); +#endif + axo->axo_lock--; + if (axo->axo_lock == 0 && axo->axo_dstate == AX_DSTATE_CLOSE && + axo->axo_cstate == AX_CSTATE_CLOSE) + agentx_object_free_finalize(axo); +} - header.aph_flags = ax->ax_byteorder == AGENTX_BYTE_ORDER_BE ? - AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER : 0; - packetid = agentx_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12])); - plength = (ax->ax_wbtlen - ax->ax_wblen) - AGENTX_PDU_HEADER; - ax->ax_wbtlen = ax->ax_wblen + 16; - (void)agentx_pdu_add_uint32(ax, plength); +static int +agentx_object_close(struct agentx_object *axo) +{ + struct agentx_context *axc = axo->axo_axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct ax_oid oid; + char oids[1024]; + size_t i; + int needclose = 0; + uint32_t packetid; + uint8_t flags = 1; + +#ifdef AX_DEBUG + if (axo->axo_cstate != AX_CSTATE_OPEN) + agentx_log_axc_fatalx(axc, "%s: unexpected object close", + __func__); +#endif - ax->ax_wblen = ax->ax_wbtlen = wbtlen; + for (i = 0; i < axo->axo_indexlen; i++) { +#ifdef AX_DEBUG + if (axo->axo_index[i]->axi_cstate != AX_CSTATE_OPEN) + agentx_log_axc_fatalx(axc, + "%s: Object open while index closed", __func__); +#endif + if (axo->axo_index[i]->axi_type != AXI_TYPE_DYNAMIC) + needclose = 1; + } + axo->axo_cstate = AX_CSTATE_WAITCLOSE; + if (axs->axs_cstate == AX_CSTATE_WAITCLOSE) + return 0; + if (!needclose) { + agentx_object_close_finalize(NULL, axo); + return 0; + } - return packetid; + bcopy(&(axo->axo_oid), &(oid), sizeof(oid)); + for (i = 0; i < axo->axo_indexlen; i++) { + if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AX_DEBUG + if (axo->axo_index[i]->axi_vb.avb_type != + AX_DATA_TYPE_INTEGER) + agentx_log_axc_fatalx(axc, + "%s: Unsupported allocated index type", __func__); +#endif + oid.aoi_id[oid.aoi_idlen++] = + axo->axo_index[i]->axi_vb.avb_data.avb_uint32; + } + packetid = ax_unregister(ax->ax_ax, axs->axs_id, + AGENTX_CONTEXT_CTX(axc), AX_PRIORITY_DEFAULT, 0, &oid, 0); + if (packetid == 0) { + agentx_log_axc_warn(axc, "couldn't generate %s", + ax_pdutype2string(AX_PDU_TYPE_UNREGISTER)); + agentx_reset(ax); + return -1; + } + strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids)); + agentx_log_axc_info(axc, "object %s (%s %s): closing", + oids, flags ? "instance" : "region", ax_oid2string(&(oid))); + return agentx_request(ax, packetid, agentx_object_close_finalize, + axo); } static int -agentx_pdu_header(struct agentx *ax, enum agentx_pdu_type type, uint8_t flags, - uint32_t sessionid, uint32_t transactionid, uint32_t packetid, - struct agentx_ostring *context) +agentx_object_close_finalize(struct ax_pdu *pdu, void *cookie) { - if (ax->ax_wblen != ax->ax_wbtlen) { - errno = EALREADY; - return -1; - } + struct agentx_object *axo = cookie; + struct agentx_region *axr = axo->axo_axr; + struct agentx_context *axc = axr->axr_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct ax_oid oid; + char oids[1024]; + uint8_t flags = 1; + size_t i; - ax->ax_wbtlen = ax->ax_wblen; - if (agentx_pdu_need(ax, 4) == -1) - return -1; - ax->ax_wbuf[ax->ax_wbtlen++] = 1; - ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t) type; - if (context != NULL) - flags |= AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT; - if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) - flags |= AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER; - ax->ax_wbuf[ax->ax_wbtlen++] = flags; - ax->ax_wbuf[ax->ax_wbtlen++] = 0; - if (packetid == 0) - packetid = agentx_packetid(ax); - if (agentx_pdu_add_uint32(ax, sessionid) == -1 || - agentx_pdu_add_uint32(ax, transactionid) == -1 || - agentx_pdu_add_uint32(ax, packetid) == -1 || - agentx_pdu_need(ax, 4) == -1) - return -1; - ax->ax_wbtlen += 4; - if (context != NULL) { - if (agentx_pdu_add_str(ax, context) == -1) +#ifdef AX_DEBUG + if (axo->axo_cstate != AX_CSTATE_WAITCLOSE) + agentx_log_axc_fatalx(axc, + "%s: unexpected object unregister", __func__); +#endif + + if (pdu != NULL) { + bcopy(&(axo->axo_oid), &(oid), sizeof(oid)); + for (i = 0; i < axo->axo_indexlen; i++) { + if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AX_DEBUG + if (axo->axo_index[i]->axi_vb.avb_type != + AX_DATA_TYPE_INTEGER) + agentx_log_axc_fatalx(axc, + "%s: Unsupported allocated index type", + __func__); +#endif + oid.aoi_id[oid.aoi_idlen++] = + axo->axo_index[i]->axi_vb.avb_data.avb_uint32; + } + strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids)); + if (pdu->ap_payload.ap_response.ap_error != + AX_PDU_ERROR_NOERROR) { + agentx_log_axc_warnx(axc, + "closing object %s (%s %s): %s", oids, + flags ? "instance" : "region", + ax_oid2string(&oid), ax_error2string( + pdu->ap_payload.ap_response.ap_error)); + agentx_reset(ax); return -1; + } + agentx_log_axc_info(axc, "object %s (%s %s): closed", oids, + flags ? "instance" : "region", ax_oid2string(&oid)); + } + + if (axo->axo_dstate == AX_DSTATE_CLOSE) + agentx_object_free_finalize(axo); + else { + if (axr->axr_cstate == AX_CSTATE_OPEN) + if (agentx_object_start(axo) == -1) + return -1; } return 0; } -static uint32_t -agentx_packetid(struct agentx *ax) +void +agentx_object_free(struct agentx_object *axo) +{ + if (axo == NULL) + return; + + if (axo->axo_dstate == AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axo->axo_axr->axr_axc, + "%s: double free", __func__); + + axo->axo_dstate = AX_DSTATE_CLOSE; + + if (axo->axo_cstate == AX_CSTATE_OPEN) { + if (agentx_object_close(axo) == -1) + return; + } + if (axo->axo_cstate == AX_CSTATE_CLOSE) + agentx_object_free_finalize(axo); +} + +static void +agentx_object_free_finalize(struct agentx_object *axo) { - uint32_t packetid, *packetids; - size_t npackets = 0, i; +#ifdef AX_DEBUG + struct agentx *ax = axo->axo_axr->axr_axc->axc_axs->axs_ax; +#endif + size_t i, j; int found; - if (ax->ax_packetids != NULL) { - for (npackets = 0; ax->ax_packetids[npackets] != 0; npackets++) - continue; +#ifdef AX_DEBUG + if (axo->axo_dstate != AX_DSTATE_CLOSE) + agentx_log_axc_fatalx(axo->axo_axr->axr_axc, + "%s: unexpected free", __func__); +#endif + + if (axo->axo_lock != 0) { +#ifdef AX_DEBUG + if (TAILQ_EMPTY(&(ax->ax_getreqs))) + agentx_log_axc_fatalx(axo->axo_axr->axr_axc, + "%s: %s axo_lock == %u", __func__, + ax_oid2string(&(axo->axo_oid)), axo->axo_lock); +#endif + return; } - if (ax->ax_packetidsize == 0 || npackets == ax->ax_packetidsize - 1) { - packetids = recallocarray(ax->ax_packetids, ax->ax_packetidsize, - ax->ax_packetidsize + 25, sizeof(*packetids)); - if (packetids == NULL) + + RB_REMOVE(axc_objects, &(axo->axo_axr->axr_axc->axc_objects), axo); + TAILQ_REMOVE(&(axo->axo_axr->axr_objects), axo, axo_axr_objects); + + for (i = 0; i < axo->axo_indexlen; i++) { + found = 0; + for (j = 0; j < axo->axo_index[i]->axi_objectlen; j++) { + if (axo->axo_index[i]->axi_object[j] == axo) + found = 1; + if (found && j + 1 != axo->axo_index[i]->axi_objectlen) + axo->axo_index[i]->axi_object[j] = + axo->axo_index[i]->axi_object[j + 1]; + } +#ifdef AX_DEBUG + if (!found) + agentx_log_axc_fatalx(axo->axo_axr->axr_axc, + "%s: object not found in index", __func__); +#endif + axo->axo_index[i]->axi_objectlen--; + if (axo->axo_index[i]->axi_dstate == AX_DSTATE_CLOSE && + axo->axo_index[i]->axi_cstate == AX_CSTATE_CLOSE) + agentx_index_free_finalize(axo->axo_index[i]); + } + + free(axo); +} + +static void +agentx_object_reset(struct agentx_object *axo) +{ + axo->axo_cstate = AX_CSTATE_CLOSE; + + if (axo->axo_dstate == AX_DSTATE_CLOSE) + agentx_object_free_finalize(axo); +} + +static int +agentx_object_cmp(struct agentx_object *o1, struct agentx_object *o2) +{ + return ax_oid_cmp(&(o1->axo_oid), &(o2->axo_oid)); +} + +static int +agentx_object_implied(struct agentx_object *axo, + struct agentx_index *axi) +{ + size_t i = 0; + + for (i = 0; i < axo->axo_indexlen; i++) { + if (axo->axo_index[i] == axi) { + if (axi->axi_vb.avb_data.avb_ostring.aos_slen != 0) + return 1; + else if (i == axo->axo_indexlen - 1) + return axo->axo_implied; return 0; - ax->ax_packetidsize += 25; - ax->ax_packetids = packetids; + } } +#ifdef AX_DEBUG + agentx_log_axc_fatalx(axo->axo_axr->axr_axc, "%s: unsupported index", + __func__); +#endif + return 0; +} + +static void +agentx_get_start(struct agentx_context *axc, struct ax_pdu *pdu) +{ + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + struct agentx_get *axg, tsag; + struct ax_pdu_searchrangelist *srl; + char *logmsg = NULL; + size_t i, j; + int fail = 0; + + if ((axg = calloc(1, sizeof(*axg))) == NULL) { + tsag.axg_sessionid = pdu->ap_header.aph_sessionid; + tsag.axg_transactionid = pdu->ap_header.aph_transactionid; + tsag.axg_packetid = pdu->ap_header.aph_packetid; + tsag.axg_context_default = axc->axc_name_default; + tsag.axg_fd = axc->axc_axs->axs_ax->ax_fd; + agentx_log_axg_warn(&tsag, "Couldn't parse request"); + agentx_reset(ax); + return; + } + + axg->axg_sessionid = pdu->ap_header.aph_sessionid; + axg->axg_transactionid = pdu->ap_header.aph_transactionid; + axg->axg_packetid = pdu->ap_header.aph_packetid; + axg->axg_context_default = axc->axc_name_default; + axg->axg_fd = axc->axc_axs->axs_ax->ax_fd; + if (!axc->axc_name_default) { + axg->axg_context.aos_string = + (unsigned char *)strdup((char *)axc->axc_name.aos_string); + if (axg->axg_context.aos_string == NULL) { + agentx_log_axg_warn(axg, "Couldn't parse request"); + free(axg); + agentx_reset(ax); + return; + } + } + axg->axg_context.aos_slen = axc->axc_name.aos_slen; + axg->axg_type = pdu->ap_header.aph_type; + axg->axg_axc = axc; + TAILQ_INSERT_TAIL(&(ax->ax_getreqs), axg, axg_ax_getreqs); + if (axg->axg_type == AX_PDU_TYPE_GET || + axg->axg_type == AX_PDU_TYPE_GETNEXT) { + srl = &(pdu->ap_payload.ap_srl); + axg->axg_nvarbind = srl->ap_nsr; + } else { + axg->axg_nonrep = pdu->ap_payload.ap_getbulk.ap_nonrep; + axg->axg_maxrep = pdu->ap_payload.ap_getbulk.ap_maxrep; + srl = &(pdu->ap_payload.ap_getbulk.ap_srl); + axg->axg_nvarbind = ((srl->ap_nsr - axg->axg_nonrep) * + axg->axg_maxrep) + axg->axg_nonrep; + } + + if ((axg->axg_varbind = calloc(axg->axg_nvarbind, + sizeof(*(axg->axg_varbind)))) == NULL) { + agentx_log_axg_warn(axg, "Couldn't parse request"); + agentx_get_free(axg); + agentx_reset(ax); + return; + } + + /* XXX net-snmp doesn't use getbulk, so untested */ + /* Two loops: varbind after needs to be initialized */ + for (i = 0; i < srl->ap_nsr; i++) { + if (i < axg->axg_nonrep || + axg->axg_type != AX_PDU_TYPE_GETBULK) + j = i; + else if (axg->axg_maxrep == 0) + break; + else + j = (axg->axg_maxrep * i) + axg->axg_nonrep; + bcopy(&(srl->ap_sr[i].asr_start), + &(axg->axg_varbind[j].axv_vb.avb_oid), + sizeof(srl->ap_sr[i].asr_start)); + bcopy(&(srl->ap_sr[i].asr_start), + &(axg->axg_varbind[j].axv_start), + sizeof(srl->ap_sr[i].asr_start)); + bcopy(&(srl->ap_sr[i].asr_stop), + &(axg->axg_varbind[j].axv_end), + sizeof(srl->ap_sr[i].asr_stop)); + axg->axg_varbind[j].axv_initialized = 1; + axg->axg_varbind[j].axv_axg = axg; + axg->axg_varbind[j].axv_include = + srl->ap_sr[i].asr_start.aoi_include; + if (j == 0) + fail |= agentx_strcat(&logmsg, " {"); + else + fail |= agentx_strcat(&logmsg, ",{"); + fail |= agentx_strcat(&logmsg, + ax_oid2string(&(srl->ap_sr[i].asr_start))); + if (srl->ap_sr[i].asr_start.aoi_include) + fail |= agentx_strcat(&logmsg, " (inclusive)"); + if (srl->ap_sr[i].asr_stop.aoi_idlen != 0) { + fail |= agentx_strcat(&logmsg, " - "); + fail |= agentx_strcat(&logmsg, + ax_oid2string(&(srl->ap_sr[i].asr_stop))); + } + fail |= agentx_strcat(&logmsg, "}"); + if (fail) { + agentx_log_axg_warn(axg, "Couldn't parse request"); + free(logmsg); + agentx_get_free(axg); + agentx_reset(ax); + return; + } + } + + agentx_log_axg_debug(axg, "%s:%s", + ax_pdutype2string(axg->axg_type), logmsg); + free(logmsg); + + for (i = 0; i < srl->ap_nsr; i++) { + if (i < axg->axg_nonrep || + axg->axg_type != AX_PDU_TYPE_GETBULK) + j = i; + else if (axg->axg_maxrep == 0) + break; + else + j = (axg->axg_maxrep * i) + axg->axg_nonrep; + agentx_varbind_start(&(axg->axg_varbind[j])); + } +} + +static void +agentx_get_finalize(struct agentx_get *axg) +{ + struct agentx_context *axc = axg->axg_axc; + struct agentx_session *axs = axc->axc_axs; + struct agentx *ax = axs->axs_ax; + size_t i, j, nvarbind = 0; + uint16_t error = 0, index = 0; + struct ax_varbind *vbl; + char *logmsg = NULL; + int fail = 0; + + for (i = 0; i < axg->axg_nvarbind; i++) { + if (axg->axg_varbind[i].axv_initialized) { + if (axg->axg_varbind[i].axv_vb.avb_type == 0) + return; + nvarbind++; + } + } + + if (axg->axg_axc == NULL) { + agentx_get_free(axg); + return; + } + + if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) { + agentx_log_axg_warn(axg, "Couldn't parse request"); + agentx_get_free(axg); + agentx_reset(ax); + return; + } + for (i = 0, j = 0; i < axg->axg_nvarbind; i++) { + if (axg->axg_varbind[i].axv_initialized) { + memcpy(&(vbl[j]), &(axg->axg_varbind[i].axv_vb), + sizeof(*vbl)); + if (error == 0 && axg->axg_varbind[i].axv_error != + AX_PDU_ERROR_NOERROR) { + error = axg->axg_varbind[i].axv_error; + index = j + 1; + } + if (j == 0) + fail |= agentx_strcat(&logmsg, " {"); + else + fail |= agentx_strcat(&logmsg, ",{"); + fail |= agentx_strcat(&logmsg, + ax_varbind2string(&(vbl[j]))); + if (axg->axg_varbind[i].axv_error != + AX_PDU_ERROR_NOERROR) { + fail |= agentx_strcat(&logmsg, "("); + fail |= agentx_strcat(&logmsg, + ax_error2string( + axg->axg_varbind[i].axv_error)); + fail |= agentx_strcat(&logmsg, ")"); + } + fail |= agentx_strcat(&logmsg, "}"); + if (fail) { + agentx_log_axg_warn(axg, + "Couldn't parse request"); + free(logmsg); + agentx_get_free(axg); + return; + } + j++; + } + } + agentx_log_axg_debug(axg, "response:%s", logmsg); + free(logmsg); + + if (ax_response(ax->ax_ax, axs->axs_id, axg->axg_transactionid, + axg->axg_packetid, AGENTX_CONTEXT_CTX(axc), 0, error, index, + vbl, nvarbind) == -1) { + agentx_log_axg_warn(axg, "Couldn't parse request"); + agentx_reset(ax); + } else + agentx_wantwrite(ax, ax->ax_fd); + free(vbl); + agentx_get_free(axg); +} + +void +agentx_get_free(struct agentx_get *axg) +{ + struct agentx_varbind *axv; + struct agentx_object *axo; + struct agentx *ax = axg->axg_axc->axc_axs->axs_ax; + struct agentx_varbind_index *index; + size_t i, j; + + if (axg->axg_axc != NULL) + TAILQ_REMOVE(&(ax->ax_getreqs), axg, axg_ax_getreqs); + + for (i = 0; i < axg->axg_nvarbind; i++) { + axv = &(axg->axg_varbind[i]); + for (j = 0; axv->axv_axo != NULL && + j < axv->axv_axo->axo_indexlen; j++) { + axo = axv->axv_axo; + index = &(axv->axv_index[j]); + if (axo->axo_index[j]->axi_vb.avb_type == + AX_DATA_TYPE_OCTETSTRING || + axo->axo_index[j]->axi_vb.avb_type == + AX_DATA_TYPE_IPADDRESS) + free(index->axv_idata.avb_ostring.aos_string); + } + ax_varbind_free(&(axg->axg_varbind[i].axv_vb)); + } + + free(axg->axg_context.aos_string); + free(axg->axg_varbind); + free(axg); +} + +static void +agentx_varbind_start(struct agentx_varbind *axv) +{ + struct agentx_get *axg = axv->axv_axg; + struct agentx_context *axc = axg->axg_axc; + struct agentx_object *axo, axo_search; + struct agentx_varbind_index *index; + struct agentx_index *axi; + struct ax_oid *oid; + union ax_data *data; + struct in_addr *ipaddress; + unsigned char *ipbytes; + size_t i, j, k; + int overflow = 0, dynamic; + +#ifdef AX_DEBUG + if (!axv->axv_initialized) + agentx_log_axg_fatalx(axv->axv_axg, + "%s: axv_initialized not set", __func__); +#endif + + bcopy(&(axv->axv_vb.avb_oid), &(axo_search.axo_oid), + sizeof(axo_search.axo_oid)); + do { - found = 0; - packetid = arc4random(); - for (i = 0; ax->ax_packetids[i] != 0; i++) { - if (ax->ax_packetids[i] == packetid) { - found = 1; + axo = RB_FIND(axc_objects, &(axc->axc_objects), &axo_search); + if (axo_search.axo_oid.aoi_idlen > 0) + axo_search.axo_oid.aoi_idlen--; + } while (axo == NULL && axo_search.axo_oid.aoi_idlen > 0); + if (axo == NULL || axo->axo_cstate != AX_CSTATE_OPEN) { + axv->axv_include = 1; + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) { + agentx_varbind_nosuchobject(axv); + return; + } + bcopy(&(axv->axv_vb.avb_oid), &(axo_search.axo_oid), + sizeof(axo_search.axo_oid)); + axo = RB_NFIND(axc_objects, &(axc->axc_objects), &axo_search); +getnext: + while (axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN) + axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo); + if (axo == NULL) { + agentx_varbind_endofmibview(axv); + return; + } + bcopy(&(axo->axo_oid), &(axv->axv_vb.avb_oid), + sizeof(axo->axo_oid)); + } + axv->axv_axo = axo; + axv->axv_indexlen = axo->axo_indexlen; + if (agentx_object_lock(axo) == -1) { + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + + oid = &(axv->axv_vb.avb_oid); + if (axo->axo_indexlen == 0) { + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) { + if (oid->aoi_idlen != axo->axo_oid.aoi_idlen + 1 || + oid->aoi_id[oid->aoi_idlen - 1] != 0) { + agentx_varbind_nosuchinstance(axv); + return; + } + } else { + if (oid->aoi_idlen == axo->axo_oid.aoi_idlen) { + oid->aoi_id[oid->aoi_idlen++] = 0; + axv->axv_include = 1; + } else { + axv->axv_axo = NULL; + agentx_object_unlock(axo); + axo = RB_NEXT(axc_objects, &(axc->axc_objects), + axo); + goto getnext; + } + } + } + j = axo->axo_oid.aoi_idlen; +/* + * We can't trust what the client gives us, so sometimes we need to map it to + * index type. + * - AX_PDU_TYPE_GET: we always return AX_DATA_TYPE_NOSUCHINSTANCE + * - AX_PDU_TYPE_GETNEXT: + * - Missing OID digits to match indices will result in the indices to be NUL- + * initialized and the request type will be set to + * AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE + * - An overflow can happen on AX_DATA_TYPE_OCTETSTRING and + * AX_DATA_TYPE_IPADDRESS. This results in request type being set to + * AGENTX_REQUEST_TYPE_GETNEXT and will set the index to its maximum + * value: + * - AX_DATA_TYPE_INTEGER: UINT32_MAX + * - AX_DATA_TYPE_OCTETSTRING: aos_slen = UINT32_MAX and + * aos_string = NULL + * - AX_DATA_TYPE_OID: aoi_idlen = UINT32_MAX and aoi_id[x] = UINT32_MAX + * - AX_DATA_TYPE_IPADDRESS: 255.255.255.255 + */ + for (dynamic = 0, i = 0; i < axo->axo_indexlen; i++) { + index = &(axv->axv_index[i]); + index->axv_axi = axo->axo_index[i]; + data = &(index->axv_idata); + if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) + dynamic = 1; + if (j >= axv->axv_vb.avb_oid.aoi_idlen && !overflow && + axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) + continue; + switch (axo->axo_index[i]->axi_vb.avb_type) { + case AX_DATA_TYPE_INTEGER: +/* Dynamic index: normal copy paste */ + if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) { + data->avb_uint32 = overflow ? + UINT32_MAX : axv->axv_vb.avb_oid.aoi_id[j]; + j++; + index->axv_idatacomplete = 1; break; } + axi = axo->axo_index[i]; +/* With a GET-request we need an exact match */ + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) { + if (axi->axi_vb.avb_data.avb_uint32 != + axv->axv_vb.avb_oid.aoi_id[j]) { + agentx_varbind_nosuchinstance(axv); + return; + } + index->axv_idatacomplete = 1; + j++; + break; + } +/* A higher value automatically moves us to the next value */ + if (overflow || + axv->axv_vb.avb_oid.aoi_id[j] > + axi->axi_vb.avb_data.avb_uint32) { +/* If we're !dynamic up until now the rest of the oid doesn't matter */ + if (!dynamic) { + agentx_varbind_endofmibview(axv); + return; + } +/* + * Else we just pick the max value and make sure we don't return + * AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE + */ + data->avb_uint32 = UINT32_MAX; + index->axv_idatacomplete = 1; + overflow = 1; + j++; + break; +/* + * A lower value automatically moves to the set value and counts as a short oid + */ + } else if (axv->axv_vb.avb_oid.aoi_id[j] < + axi->axi_vb.avb_data.avb_uint32) { + data->avb_uint32 = + axi->axi_vb.avb_data.avb_uint32; + j = axv->axv_vb.avb_oid.aoi_idlen; + break; + } +/* Normal match, except we already matched overflow at higher value */ + data->avb_uint32 = axv->axv_vb.avb_oid.aoi_id[j]; + j++; + index->axv_idatacomplete = 1; + break; + case AX_DATA_TYPE_OCTETSTRING: + if (!agentx_object_implied(axo, index->axv_axi)) { + if (overflow || axv->axv_vb.avb_oid.aoi_id[j] > + AGENTX_OID_MAX_LEN - + axv->axv_vb.avb_oid.aoi_idlen) { + overflow = 1; + data->avb_ostring.aos_slen = UINT32_MAX; + index->axv_idatacomplete = 1; + continue; + } + data->avb_ostring.aos_slen = + axv->axv_vb.avb_oid.aoi_id[j++]; + } else { + if (overflow) { + data->avb_ostring.aos_slen = UINT32_MAX; + index->axv_idatacomplete = 1; + continue; + } + data->avb_ostring.aos_slen = + axv->axv_vb.avb_oid.aoi_idlen - j; + } + data->avb_ostring.aos_string = + calloc(data->avb_ostring.aos_slen + 1, 1); + if (data->avb_ostring.aos_string == NULL) { + agentx_log_axg_warn(axg, + "Failed to bind string index"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + for (k = 0; k < data->avb_ostring.aos_slen; k++, j++) { + if (!overflow && + j == axv->axv_vb.avb_oid.aoi_idlen) + break; + + if (axv->axv_vb.avb_oid.aoi_id[j] > 255) + overflow = 1; + + data->avb_ostring.aos_string[k] = overflow ? + 0xff : axv->axv_vb.avb_oid.aoi_id[j]; + } + if (k == data->avb_ostring.aos_slen) + index->axv_idatacomplete = 1; + break; + case AX_DATA_TYPE_OID: + if (!agentx_object_implied(axo, index->axv_axi)) { + if (overflow || axv->axv_vb.avb_oid.aoi_id[j] > + AGENTX_OID_MAX_LEN - + axv->axv_vb.avb_oid.aoi_idlen) { + overflow = 1; + data->avb_oid.aoi_idlen = UINT32_MAX; + index->axv_idatacomplete = 1; + continue; + } + data->avb_oid.aoi_idlen = + axv->axv_vb.avb_oid.aoi_id[j++]; + } else { + if (overflow) { + data->avb_oid.aoi_idlen = UINT32_MAX; + index->axv_idatacomplete = 1; + continue; + } + data->avb_oid.aoi_idlen = + axv->axv_vb.avb_oid.aoi_idlen - j; + } + for (k = 0; k < data->avb_oid.aoi_idlen; k++, j++) { + if (!overflow && + j == axv->axv_vb.avb_oid.aoi_idlen) { + data->avb_oid.aoi_id[k] = 0; + continue; + } + data->avb_oid.aoi_id[k] = overflow ? + UINT32_MAX : axv->axv_vb.avb_oid.aoi_id[j]; + } + if (j <= axv->axv_vb.avb_oid.aoi_idlen) + index->axv_idatacomplete = 1; + break; + case AX_DATA_TYPE_IPADDRESS: + ipaddress = calloc(1, sizeof(*ipaddress)); + if (ipaddress == NULL) { + agentx_log_axg_warn(axg, + "Failed to bind ipaddress index"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + ipbytes = (unsigned char *)ipaddress; + for (k = 0; k < 4; k++, j++) { + if (!overflow && + j == axv->axv_vb.avb_oid.aoi_idlen) + break; + + if (axv->axv_vb.avb_oid.aoi_id[j] > 255) + overflow = 1; + + ipbytes[k] = overflow ? 255 : + axv->axv_vb.avb_oid.aoi_id[j]; + } + if (j <= axv->axv_vb.avb_oid.aoi_idlen) + index->axv_idatacomplete = 1; + data->avb_ostring.aos_slen = sizeof(*ipaddress); + data->avb_ostring.aos_string = + (unsigned char *)ipaddress; + break; + default: +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axg, + "%s: unexpected index type", __func__); +#else + agentx_log_axg_warnx(axg, + "%s: unexpected index type", __func__); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; +#endif + } + } + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) { + if ((axo->axo_indexlen > 0 && + !axv->axv_index[axo->axo_indexlen - 1].axv_idatacomplete) || + j != axv->axv_vb.avb_oid.aoi_idlen || overflow) { + agentx_varbind_nosuchinstance(axv); + return; } - } while (packetid == 0 || found); - ax->ax_packetids[npackets] = packetid; + } + + if (overflow || j > axv->axv_vb.avb_oid.aoi_idlen) + axv->axv_include = 0; + +/* + * AGENTX_REQUEST_TYPE_GETNEXT request can !dynamic objects can just move to + * the next object + */ + if (agentx_varbind_request(axv) == AGENTX_REQUEST_TYPE_GETNEXT && + !dynamic) { + agentx_varbind_endofmibview(axv); + return; + } - return packetid; + axo->axo_get(axv); } -static int -agentx_pdu_add_uint16(struct agentx *ax, uint16_t value) +void +agentx_varbind_integer(struct agentx_varbind *axv, uint32_t value) { - if (agentx_pdu_need(ax, sizeof(value)) == -1) - return -1; + axv->axv_vb.avb_type = AX_DATA_TYPE_INTEGER; + axv->axv_vb.avb_data.avb_uint32 = value; - if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) - value = htobe16(value); - else - value = htole16(value); - memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); - ax->ax_wbtlen += sizeof(value); - return 0; + agentx_varbind_finalize(axv); } -static int -agentx_pdu_add_uint32(struct agentx *ax, uint32_t value) +void +agentx_varbind_string(struct agentx_varbind *axv, const char *value) { - if (agentx_pdu_need(ax, sizeof(value)) == -1) - return -1; + agentx_varbind_nstring(axv, (const unsigned char *)value, + strlen(value)); +} - if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) - value = htobe32(value); - else - value = htole32(value); - memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); - ax->ax_wbtlen += sizeof(value); - return 0; +void +agentx_varbind_nstring(struct agentx_varbind *axv, + const unsigned char *value, size_t slen) +{ + axv->axv_vb.avb_data.avb_ostring.aos_string = malloc(slen); + if (axv->axv_vb.avb_data.avb_ostring.aos_string == NULL) { + agentx_log_axg_warn(axv->axv_axg, "Couldn't bind string"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + axv->axv_vb.avb_type = AX_DATA_TYPE_OCTETSTRING; + memcpy(axv->axv_vb.avb_data.avb_ostring.aos_string, value, slen); + axv->axv_vb.avb_data.avb_ostring.aos_slen = slen; + + agentx_varbind_finalize(axv); } -static int -agentx_pdu_add_uint64(struct agentx *ax, uint64_t value) +void +agentx_varbind_printf(struct agentx_varbind *axv, const char *fmt, ...) { - if (agentx_pdu_need(ax, sizeof(value)) == -1) - return -1; + va_list ap; + int r; + + axv->axv_vb.avb_type = AX_DATA_TYPE_OCTETSTRING; + va_start(ap, fmt); + r = vasprintf((char **)&(axv->axv_vb.avb_data.avb_ostring.aos_string), + fmt, ap); + va_end(ap); + if (r == -1) { + axv->axv_vb.avb_data.avb_ostring.aos_string = NULL; + agentx_log_axg_warn(axv->axv_axg, "Couldn't bind string"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + axv->axv_vb.avb_data.avb_ostring.aos_slen = r; - if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) - value = htobe64(value); - else - value = htole64(value); - memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); - ax->ax_wbtlen += sizeof(value); - return 0; + agentx_varbind_finalize(axv); } +void +agentx_varbind_null(struct agentx_varbind *axv) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_NULL; -static int -agentx_pdu_add_oid(struct agentx *ax, struct agentx_oid *oid, int include) + agentx_varbind_finalize(axv); +} + +void +agentx_varbind_oid(struct agentx_varbind *axv, const uint32_t oid[], + size_t oidlen) { - static struct agentx_oid nulloid = {0}; - uint8_t prefix = 0, n_subid, i = 0; + size_t i; - n_subid = oid->aoi_idlen; + axv->axv_vb.avb_type = AX_DATA_TYPE_OID; - if (oid == NULL) - oid = &nulloid; + for (i = 0; i < oidlen; i++) + axv->axv_vb.avb_data.avb_oid.aoi_id[i] = oid[i]; + axv->axv_vb.avb_data.avb_oid.aoi_idlen = oidlen; - if (oid->aoi_idlen > 4 && - oid->aoi_id[0] == 1 && oid->aoi_id[1] == 3 && - oid->aoi_id[2] == 6 && oid->aoi_id[3] == 1 && - oid->aoi_id[4] <= UINT8_MAX) { - prefix = oid->aoi_id[4]; - i = 5; + agentx_varbind_finalize(axv); +} + +void +agentx_varbind_object(struct agentx_varbind *axv, + struct agentx_object *axo) +{ + agentx_varbind_oid(axv, axo->axo_oid.aoi_id, + axo->axo_oid.aoi_idlen); +} + +void +agentx_varbind_index(struct agentx_varbind *axv, + struct agentx_index *axi) +{ + agentx_varbind_oid(axv, axi->axi_vb.avb_oid.aoi_id, + axi->axi_vb.avb_oid.aoi_idlen); +} + + +void +agentx_varbind_ipaddress(struct agentx_varbind *axv, + const struct in_addr *value) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_IPADDRESS; + axv->axv_vb.avb_data.avb_ostring.aos_string = malloc(4); + if (axv->axv_vb.avb_data.avb_ostring.aos_string == NULL) { + agentx_log_axg_warn(axv->axv_axg, "Couldn't bind ipaddress"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; } + memcpy(axv->axv_vb.avb_data.avb_ostring.aos_string, value, 4); + axv->axv_vb.avb_data.avb_ostring.aos_slen = 4; - if (agentx_pdu_need(ax, 4) == -1) - return -1; - ax->ax_wbuf[ax->ax_wbtlen++] = n_subid - i; - ax->ax_wbuf[ax->ax_wbtlen++] = prefix; - ax->ax_wbuf[ax->ax_wbtlen++] = !!include; - ax->ax_wbuf[ax->ax_wbtlen++] = 0; + agentx_varbind_finalize(axv); +} - for (; i < n_subid; i++) { - if (agentx_pdu_add_uint32(ax, oid->aoi_id[i]) == -1) - return -1; +void +agentx_varbind_counter32(struct agentx_varbind *axv, uint32_t value) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_COUNTER32; + axv->axv_vb.avb_data.avb_uint32 = value; + + agentx_varbind_finalize(axv); +} + +void +agentx_varbind_gauge32(struct agentx_varbind *axv, uint32_t value) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_GAUGE32; + axv->axv_vb.avb_data.avb_uint32 = value; + + agentx_varbind_finalize(axv); +} + +void +agentx_varbind_timeticks(struct agentx_varbind *axv, uint32_t value) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_TIMETICKS; + axv->axv_vb.avb_data.avb_uint32 = value; + + agentx_varbind_finalize(axv); +} + +void +agentx_varbind_opaque(struct agentx_varbind *axv, const char *string, + size_t strlen) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_OPAQUE; + axv->axv_vb.avb_data.avb_ostring.aos_string = malloc(strlen); + if (axv->axv_vb.avb_data.avb_ostring.aos_string == NULL) { + agentx_log_axg_warn(axv->axv_axg, "Couldn't bind opaque"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 1); + return; } + memcpy(axv->axv_vb.avb_data.avb_ostring.aos_string, string, strlen); + axv->axv_vb.avb_data.avb_ostring.aos_slen = strlen; - return 0; + agentx_varbind_finalize(axv); } -static int -agentx_pdu_add_str(struct agentx *ax, struct agentx_ostring *str) +void +agentx_varbind_counter64(struct agentx_varbind *axv, uint64_t value) { - size_t length, zeroes; + axv->axv_vb.avb_type = AX_DATA_TYPE_COUNTER64; + axv->axv_vb.avb_data.avb_uint64 = value; - if (agentx_pdu_add_uint32(ax, str->aos_slen) == -1) - return -1; + agentx_varbind_finalize(axv); +} - if ((zeroes = (4 - (str->aos_slen % 4))) == 4) - zeroes = 0; - length = str->aos_slen + zeroes; - if (agentx_pdu_need(ax, length) == -1) - return -1; +void +agentx_varbind_notfound(struct agentx_varbind *axv) +{ + if (axv->axv_indexlen == 0) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "%s invalid call", + __func__); +#else + agentx_log_axg_warnx(axv->axv_axg, "%s invalid call", + __func__); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 1); +#endif + } else if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) + agentx_varbind_nosuchinstance(axv); + else + agentx_varbind_endofmibview(axv); +} - memcpy(&(ax->ax_wbuf[ax->ax_wbtlen]), str->aos_string, str->aos_slen); - ax->ax_wbtlen += str->aos_slen; - memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, zeroes); - ax->ax_wbtlen += zeroes; - return 0; +void +agentx_varbind_error(struct agentx_varbind *axv) +{ + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 1); } -static int -agentx_pdu_add_varbindlist(struct agentx *ax, - struct agentx_varbind *vblist, size_t nvb) +static void +agentx_varbind_error_type(struct agentx_varbind *axv, + enum ax_pdu_error error, int done) { - size_t i; - uint16_t temp; + if (axv->axv_error == AX_PDU_ERROR_NOERROR) { + axv->axv_error = error; + } - for (i = 0; i < nvb; i++) { - temp = (uint16_t) vblist[i].avb_type; - if (agentx_pdu_add_uint16(ax, temp) == -1 || - agentx_pdu_need(ax, 2)) - return -1; - memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2); - ax->ax_wbtlen += 2; - if (agentx_pdu_add_oid(ax, &(vblist[i].avb_oid), 0) == -1) - return -1; - switch (vblist[i].avb_type) { - case AGENTX_DATA_TYPE_INTEGER: - case AGENTX_DATA_TYPE_COUNTER32: - case AGENTX_DATA_TYPE_GAUGE32: - case AGENTX_DATA_TYPE_TIMETICKS: - if (agentx_pdu_add_uint32(ax, - vblist[i].avb_data.avb_uint32) == -1) - return -1; - break; - case AGENTX_DATA_TYPE_COUNTER64: - if (agentx_pdu_add_uint64(ax, - vblist[i].avb_data.avb_uint64) == -1) - return -1; + if (done) { + axv->axv_vb.avb_type = AX_DATA_TYPE_NULL; + + agentx_varbind_finalize(axv); + } +} + +static void +agentx_varbind_finalize(struct agentx_varbind *axv) +{ + struct agentx_get *axg = axv->axv_axg; + struct ax_oid oid; + union ax_data *data; + size_t i, j; + int cmp; + + if (axv->axv_error != AX_PDU_ERROR_NOERROR) { + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + goto done; + } + bcopy(&(axv->axv_axo->axo_oid), &oid, sizeof(oid)); + if (axv->axv_indexlen == 0) + ax_oid_add(&oid, 0); + for (i = 0; i < axv->axv_indexlen; i++) { + data = &(axv->axv_index[i].axv_idata); + switch (axv->axv_index[i].axv_axi->axi_vb.avb_type) { + case AX_DATA_TYPE_INTEGER: + if (ax_oid_add(&oid, data->avb_uint32) == -1) + goto fail; break; - case AGENTX_DATA_TYPE_OCTETSTRING: - case AGENTX_DATA_TYPE_IPADDRESS: - case AGENTX_DATA_TYPE_OPAQUE: - if (agentx_pdu_add_str(ax, - &(vblist[i].avb_data.avb_ostring)) == -1) - return -1; + case AX_DATA_TYPE_OCTETSTRING: + if (!agentx_object_implied(axv->axv_axo, + axv->axv_index[i].axv_axi)) { + if (ax_oid_add(&oid, + data->avb_ostring.aos_slen) == -1) + goto fail; + } + for (j = 0; j < data->avb_ostring.aos_slen; j++) { + if (ax_oid_add(&oid, + (uint8_t)data->avb_ostring.aos_string[j]) == + -1) + goto fail; + } break; - case AGENTX_DATA_TYPE_OID: - if (agentx_pdu_add_oid(ax, - &(vblist[i].avb_data.avb_oid), 1) == -1) - return -1; + case AX_DATA_TYPE_OID: + if (!agentx_object_implied(axv->axv_axo, + axv->axv_index[i].axv_axi)) { + if (ax_oid_add(&oid, + data->avb_oid.aoi_idlen) == -1) + goto fail; + } + for (j = 0; j < data->avb_oid.aoi_idlen; j++) { + if (ax_oid_add(&oid, + data->avb_oid.aoi_id[j]) == -1) + goto fail; + } break; - case AGENTX_DATA_TYPE_NULL: - case AGENTX_DATA_TYPE_NOSUCHOBJECT: - case AGENTX_DATA_TYPE_NOSUCHINSTANCE: - case AGENTX_DATA_TYPE_ENDOFMIBVIEW: + case AX_DATA_TYPE_IPADDRESS: + for (j = 0; j < 4; j++) { + if (ax_oid_add(&oid, + data->avb_ostring.aos_string == NULL ? 0 : + (uint8_t)data->avb_ostring.aos_string[j]) == + -1) + goto fail; + } break; default: - errno = EINVAL; - return -1; +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axg, + "%s: unsupported index type", __func__); +#else + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + axv->axv_error = AX_PDU_ERROR_PROCESSINGERROR; + agentx_object_unlock(axv->axv_axo); + agentx_get_finalize(axv->axv_axg); + return; +#endif } } + cmp = ax_oid_cmp(&(axv->axv_vb.avb_oid), &oid); + if ((agentx_varbind_request(axv) == AGENTX_REQUEST_TYPE_GETNEXT && + cmp >= 0) || cmp > 0) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axg, "indices not incremented"); +#else + agentx_log_axg_warnx(axg, "indices not incremented"); + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + axv->axv_error = AX_PDU_ERROR_GENERR; +#endif + } else + bcopy(&oid, &(axv->axv_vb.avb_oid), sizeof(oid)); +done: + agentx_object_unlock(axv->axv_axo); + agentx_get_finalize(axv->axv_axg); + return; + +fail: + agentx_log_axg_warnx(axg, "oid too large"); + bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid), + sizeof(axv->axv_start)); + axv->axv_error = AX_PDU_ERROR_GENERR; + agentx_object_unlock(axv->axv_axo); + agentx_get_finalize(axv->axv_axg); +} + +static void +agentx_varbind_nosuchobject(struct agentx_varbind *axv) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_NOSUCHOBJECT; + + if (axv->axv_axo != NULL) + agentx_object_unlock(axv->axv_axo); + agentx_get_finalize(axv->axv_axg); +} + +static void +agentx_varbind_nosuchinstance(struct agentx_varbind *axv) +{ + axv->axv_vb.avb_type = AX_DATA_TYPE_NOSUCHINSTANCE; + + if (axv->axv_axo != NULL) + agentx_object_unlock(axv->axv_axo); + agentx_get_finalize(axv->axv_axg); +} + +static void +agentx_varbind_endofmibview(struct agentx_varbind *axv) +{ + struct agentx_object *axo; + struct ax_varbind *vb; + struct agentx_varbind_index *index; + size_t i; + +#ifdef AX_DEBUG + if (axv->axv_axg->axg_type != AX_PDU_TYPE_GETNEXT && + axv->axv_axg->axg_type != AX_PDU_TYPE_GETBULK) + agentx_log_axg_fatalx(axv->axv_axg, + "%s: invalid request type", __func__); +#endif + + if (axv->axv_axo != NULL && + (axo = RB_NEXT(axc_objects, &(axc->axc_objects), + axv->axv_axo)) != NULL && + ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) < 0) { + bcopy(&(axo->axo_oid), &(axv->axv_vb.avb_oid), + sizeof(axo->axo_oid)); + axv->axv_include = 1; + for (i = 0; i < axv->axv_indexlen; i++) { + index = &(axv->axv_index[i]); + vb = &(index->axv_axi->axi_vb); + if (vb->avb_type == AX_DATA_TYPE_OCTETSTRING || + vb->avb_type == AX_DATA_TYPE_IPADDRESS) + free(index->axv_idata.avb_ostring.aos_string); + } + bzero(&(axv->axv_index), sizeof(axv->axv_index)); + agentx_object_unlock(axv->axv_axo); + agentx_varbind_start(axv); + return; + } + + axv->axv_vb.avb_type = AX_DATA_TYPE_ENDOFMIBVIEW; + + if (axv->axv_axo != NULL) + agentx_object_unlock(axv->axv_axo); + agentx_get_finalize(axv->axv_axg); +} + +enum agentx_request_type +agentx_varbind_request(struct agentx_varbind *axv) +{ + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) + return AGENTX_REQUEST_TYPE_GET; + if (axv->axv_include || + (axv->axv_indexlen > 0 && + !axv->axv_index[axv->axv_indexlen - 1].axv_idatacomplete)) + return AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE; + return AGENTX_REQUEST_TYPE_GETNEXT; +} + +struct agentx_object * +agentx_varbind_get_object(struct agentx_varbind *axv) +{ + return axv->axv_axo; +} + +uint32_t +agentx_varbind_get_index_integer(struct agentx_varbind *axv, + struct agentx_index *axi) +{ + size_t i; + + if (axi->axi_vb.avb_type != AX_DATA_TYPE_INTEGER) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return 0; +#endif + } + + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) + return axv->axv_index[i].axv_idata.avb_uint32; + } +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); return 0; +#endif } -static uint16_t -agentx_pdutoh16(struct agentx_pdu_header *header, uint8_t *buf) +const unsigned char * +agentx_varbind_get_index_string(struct agentx_varbind *axv, + struct agentx_index *axi, size_t *slen, int *implied) { - uint16_t value; + struct agentx_varbind_index *index; + size_t i; + + if (axi->axi_vb.avb_type != AX_DATA_TYPE_OCTETSTRING) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + *slen = 0; + *implied = 0; + return NULL; +#endif + } - memcpy(&value, buf, sizeof(value)); - if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER) - return be16toh(value); - return le16toh(value); + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + index = &(axv->axv_index[i]); + *slen = index->axv_idata.avb_ostring.aos_slen; + *implied = agentx_object_implied(axv->axv_axo, axi); + return index->axv_idata.avb_ostring.aos_string; + } + } + +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + *slen = 0; + *implied = 0; + return NULL; +#endif } -static uint32_t -agentx_pdutoh32(struct agentx_pdu_header *header, uint8_t *buf) +const uint32_t * +agentx_varbind_get_index_oid(struct agentx_varbind *axv, + struct agentx_index *axi, size_t *oidlen, int *implied) { - uint32_t value; + struct agentx_varbind_index *index; + size_t i; - memcpy(&value, buf, sizeof(value)); - if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER) - return be32toh(value); - return le32toh(value); + if (axi->axi_vb.avb_type != AX_DATA_TYPE_OID) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + *oidlen = 0; + *implied = 0; + return NULL; +#endif + } + + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + index = &(axv->axv_index[i]); + *oidlen = index->axv_idata.avb_oid.aoi_idlen; + *implied = agentx_object_implied(axv->axv_axo, axi); + return index->axv_idata.avb_oid.aoi_id; + } + } + +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + *oidlen = 0; + *implied = 0; + return NULL; +#endif } -static uint64_t -agentx_pdutoh64(struct agentx_pdu_header *header, uint8_t *buf) +const struct in_addr * +agentx_varbind_get_index_ipaddress(struct agentx_varbind *axv, + struct agentx_index *axi) { - uint64_t value; + static struct in_addr nuladdr = {0}; + struct agentx_varbind_index *index; + size_t i; - memcpy(&value, buf, sizeof(value)); - if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER) - return be64toh(value); - return le64toh(value); + if (axi->axi_vb.avb_type != AX_DATA_TYPE_IPADDRESS) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return NULL; +#endif + } + + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + index = &(axv->axv_index[i]); + if (index->axv_idata.avb_ostring.aos_string == NULL) + return &nuladdr; + return (struct in_addr *) + index->axv_idata.avb_ostring.aos_string; + } + } + +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return NULL; +#endif } -static ssize_t -agentx_pdutooid(struct agentx_pdu_header *header, struct agentx_oid *oid, - uint8_t *buf, size_t rawlen) +void +agentx_varbind_set_index_integer(struct agentx_varbind *axv, + struct agentx_index *axi, uint32_t value) { - size_t i = 0; - ssize_t nread; - - if (rawlen < 4) - goto fail; - rawlen -= 4; - nread = 4; - oid->aoi_idlen = *buf++; - if (rawlen < (oid->aoi_idlen * 4)) - goto fail; - nread += oid->aoi_idlen * 4; - if (*buf != 0) { - oid->aoi_id[0] = 1; - oid->aoi_id[1] = 3; - oid->aoi_id[2] = 6; - oid->aoi_id[3] = 1; - oid->aoi_id[4] = *buf; - oid->aoi_idlen += 5; - i = 5; - } - buf++; - oid->aoi_include = *buf; - for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) - oid->aoi_id[i] = agentx_pdutoh32(header, buf); - - return nread; + size_t i; -fail: - errno = EPROTO; - return -1; + if (axi->axi_vb.avb_type != AX_DATA_TYPE_INTEGER) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET && + axv->axv_index[i].axv_idata.avb_uint32 != value) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, + "can't change index on GET"); +#else + agentx_log_axg_warnx(axv->axv_axg, + "can't change index on GET"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + axv->axv_index[i].axv_idata.avb_uint32 = value; + return; + } + } +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); +#endif } -static ssize_t -agentx_pdutoostring(struct agentx_pdu_header *header, - struct agentx_ostring *ostring, uint8_t *buf, size_t rawlen) +void +agentx_varbind_set_index_string(struct agentx_varbind *axv, + struct agentx_index *axi, const char *value) { - ssize_t nread; + agentx_varbind_set_index_nstring(axv, axi, + (const unsigned char *)value, strlen(value)); +} - if (rawlen < 4) - goto fail; +void +agentx_varbind_set_index_nstring(struct agentx_varbind *axv, + struct agentx_index *axi, const unsigned char *value, size_t slen) +{ + struct ax_ostring *curvalue; + unsigned char *nstring; + size_t i; - ostring->aos_slen = agentx_pdutoh32(header, buf); - rawlen -= 4; - buf += 4; - if (ostring->aos_slen > rawlen) - goto fail; - if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL) - return -1; - memcpy(ostring->aos_string, buf, ostring->aos_slen); - ostring->aos_string[ostring->aos_slen] = '\0'; + if (axi->axi_vb.avb_type != AX_DATA_TYPE_OCTETSTRING) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return; +#endif + } - nread = 4 + ostring->aos_slen; - if (ostring->aos_slen % 4 != 0) - nread += 4 - (ostring->aos_slen % 4); + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + if (axi->axi_vb.avb_data.avb_ostring.aos_slen != 0 && + axi->axi_vb.avb_data.avb_ostring.aos_slen != slen) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, + "invalid string length on explicit length " + "string"); +#else + agentx_log_axg_warnx(axv->axv_axg, + "invalid string length on explicit length " + "string"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + curvalue = &(axv->axv_index[i].axv_idata.avb_ostring); + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET && + (curvalue->aos_slen != slen || + memcmp(curvalue->aos_string, value, slen) != 0)) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, + "can't change index on GET"); +#else + agentx_log_axg_warnx(axv->axv_axg, + "can't change index on GET"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + if ((nstring = recallocarray(curvalue->aos_string, + curvalue->aos_slen + 1, slen + 1, 1)) == NULL) { + agentx_log_axg_warn(axv->axv_axg, + "Failed to bind string index"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 0); + return; + } + curvalue->aos_string = nstring; + memcpy(nstring, value, slen); + curvalue->aos_slen = slen; + return; + } + } +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); +#endif +} - return nread; +void +agentx_varbind_set_index_oid(struct agentx_varbind *axv, + struct agentx_index *axi, const uint32_t *value, size_t oidlen) +{ + struct ax_oid *curvalue, oid; + size_t i; -fail: - errno = EPROTO; - return -1; + if (axi->axi_vb.avb_type != AX_DATA_TYPE_OID) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + if (axi->axi_vb.avb_data.avb_oid.aoi_idlen != 0 && + axi->axi_vb.avb_data.avb_oid.aoi_idlen != oidlen) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, + "invalid oid length on explicit length " + "oid"); +#else + agentx_log_axg_warnx(axv->axv_axg, + "invalid oid length on explicit length " + "oid"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + curvalue = &(axv->axv_index[i].axv_idata.avb_oid); + for (i = 0; i < oidlen; i++) + oid.aoi_id[i] = value[i]; + oid.aoi_idlen = oidlen; + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET && + ax_oid_cmp(&oid, curvalue) != 0) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, + "can't change index on GET"); +#else + agentx_log_axg_warnx(axv->axv_axg, + "can't change index on GET"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + for (i = 0; i < oidlen; i++) + curvalue->aoi_id[i] = value[i]; + curvalue->aoi_idlen = oidlen; + return; + } + } +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); +#endif +} + +void +agentx_varbind_set_index_object(struct agentx_varbind *axv, + struct agentx_index *axi, struct agentx_object *axo) +{ + agentx_varbind_set_index_oid(axv, axi, axo->axo_oid.aoi_id, + axo->axo_oid.aoi_idlen); } -static ssize_t -agentx_pdutovarbind(struct agentx_pdu_header *header, - struct agentx_varbind *varbind, uint8_t *buf, size_t rawlen) +void +agentx_varbind_set_index_ipaddress(struct agentx_varbind *axv, + struct agentx_index *axi, const struct in_addr *addr) { - ssize_t nread, rread = 4; + struct ax_ostring *curvalue; + size_t i; - if (rawlen == 0) - return 0; + if (axi->axi_vb.avb_type != AX_DATA_TYPE_IPADDRESS) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index type"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index type"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); + return; +#endif + } - if (rawlen < 8) - goto fail; - varbind->avb_type = agentx_pdutoh16(header, buf); + for (i = 0; i < axv->axv_indexlen; i++) { + if (axv->axv_index[i].axv_axi == axi) { + curvalue = &(axv->axv_index[i].axv_idata.avb_ostring); + if (curvalue->aos_string == NULL) + curvalue->aos_string = calloc(1, sizeof(*addr)); + if (curvalue->aos_string == NULL) { + agentx_log_axg_warn(axv->axv_axg, + "Failed to bind ipaddress index"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_PROCESSINGERROR, 0); + return; + } + if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET && + memcmp(addr, curvalue->aos_string, + sizeof(*addr)) != 0) { +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, + "can't change index on GET"); +#else + agentx_log_axg_warnx(axv->axv_axg, + "can't change index on GET"); + agentx_varbind_error_type(axv, + AX_PDU_ERROR_GENERR, 0); + return; +#endif + } + bcopy(addr, curvalue->aos_string, sizeof(*addr)); + return; + } + } +#ifdef AX_DEBUG + agentx_log_axg_fatalx(axv->axv_axg, "invalid index"); +#else + agentx_log_axg_warnx(axv->axv_axg, "invalid index"); + agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0); +#endif +} + +static int +agentx_request(struct agentx *ax, uint32_t packetid, + int (*cb)(struct ax_pdu *, void *), void *cookie) +{ + struct agentx_request *axr; - buf += 4; - rawlen -= 4; - nread = agentx_pdutooid(header, &(varbind->avb_oid), buf, rawlen); - if (nread == -1) +#ifdef AX_DEBUG + if (ax->ax_ax->ax_wblen == 0) + agentx_log_ax_fatalx(ax, "%s: no data to be written", + __func__); +#endif + + if ((axr = calloc(1, sizeof(*axr))) == NULL) { + agentx_log_ax_warn(ax, "couldn't create request context"); + agentx_reset(ax); return -1; - rread += nread; - buf += nread; - rawlen -= nread; - - switch(varbind->avb_type) { - case AGENTX_DATA_TYPE_INTEGER: - case AGENTX_DATA_TYPE_COUNTER32: - case AGENTX_DATA_TYPE_GAUGE32: - case AGENTX_DATA_TYPE_TIMETICKS: - if (rawlen < 4) - goto fail; - varbind->avb_data.avb_uint32 = agentx_pdutoh32(header, buf); - return rread + 4; - case AGENTX_DATA_TYPE_COUNTER64: - if (rawlen < 8) - goto fail; - varbind->avb_data.avb_uint64 = agentx_pdutoh64(header, buf); - return rread + 8; - case AGENTX_DATA_TYPE_OCTETSTRING: - case AGENTX_DATA_TYPE_IPADDRESS: - case AGENTX_DATA_TYPE_OPAQUE: - nread = agentx_pdutoostring(header, - &(varbind->avb_data.avb_ostring), buf, rawlen); - if (nread == -1) - return -1; - return nread + rread; - case AGENTX_DATA_TYPE_OID: - nread = agentx_pdutooid(header, &(varbind->avb_data.avb_oid), - buf, rawlen); - if (nread == -1) + } + + axr->axr_packetid = packetid; + axr->axr_cb = cb; + axr->axr_cookie = cookie; + if (RB_INSERT(ax_requests, &(ax->ax_requests), axr) != NULL) { +#ifdef AX_DEBUG + agentx_log_ax_fatalx(ax, "%s: duplicate packetid", __func__); +#else + agentx_log_ax_warnx(ax, "%s: duplicate packetid", __func__); + free(axr); + agentx_reset(ax); + return -1; +#endif + } + + agentx_wantwrite(ax, ax->ax_fd); + return 0; +} + +static int +agentx_request_cmp(struct agentx_request *r1, + struct agentx_request *r2) +{ + return r1->axr_packetid < r2->axr_packetid ? -1 : + r1->axr_packetid > r2->axr_packetid; +} + +static int +agentx_strcat(char **dst, const char *src) +{ + char *tmp; + size_t dstlen = 0, buflen = 0, srclen, nbuflen; + + if (*dst != NULL) { + dstlen = strlen(*dst); + buflen = ((dstlen / 512) + 1) * 512; + } + + srclen = strlen(src); + if (*dst == NULL || dstlen + srclen > buflen) { + nbuflen = (((dstlen + srclen) / 512) + 1) * 512; + tmp = recallocarray(*dst, buflen, nbuflen, sizeof(*tmp)); + if (tmp == NULL) return -1; - return nread + rread; - case AGENTX_DATA_TYPE_NULL: - case AGENTX_DATA_TYPE_NOSUCHOBJECT: - case AGENTX_DATA_TYPE_NOSUCHINSTANCE: - case AGENTX_DATA_TYPE_ENDOFMIBVIEW: - return rread; + *dst = tmp; + buflen = nbuflen; } -fail: - errno = EPROTO; - return -1; + (void)strlcat(*dst, src, buflen); + return 0; } + +void +agentx_read(struct agentx *ax) +{ + struct agentx_session *axs; + struct agentx_context *axc; + struct agentx_request axr_search, *axr; + struct ax_pdu *pdu; + int error; + + if ((pdu = ax_recv(ax->ax_ax)) == NULL) { + if (errno == EAGAIN) + return; + agentx_log_ax_warn(ax, "lost connection"); + agentx_reset(ax); + return; + } + + TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) { + if (axs->axs_id == pdu->ap_header.aph_sessionid) + break; + if (axs->axs_cstate == AX_CSTATE_WAITOPEN && + axs->axs_packetid == pdu->ap_header.aph_packetid) + break; + } + if (axs == NULL) { + agentx_log_ax_warnx(ax, "received unexpected session: %d", + pdu->ap_header.aph_sessionid); + ax_pdu_free(pdu); + agentx_reset(ax); + return; + } + TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts) { + if ((pdu->ap_header.aph_flags & + AX_PDU_FLAG_NON_DEFAULT_CONTEXT) == 0 && + axc->axc_name_default == 1) + break; + if (pdu->ap_header.aph_flags & + AX_PDU_FLAG_NON_DEFAULT_CONTEXT && + axc->axc_name_default == 0 && + pdu->ap_context.aos_slen == axc->axc_name.aos_slen && + memcmp(pdu->ap_context.aos_string, + axc->axc_name.aos_string, axc->axc_name.aos_slen) == 0) + break; + } + if (pdu->ap_header.aph_type != AX_PDU_TYPE_RESPONSE) { + if (axc == NULL) { + agentx_log_ax_warnx(ax, "%s: invalid context", + pdu->ap_context.aos_string); + ax_pdu_free(pdu); + agentx_reset(ax); + return; + } + } + + switch (pdu->ap_header.aph_type) { + case AX_PDU_TYPE_GET: + case AX_PDU_TYPE_GETNEXT: + case AX_PDU_TYPE_GETBULK: + agentx_get_start(axc, pdu); + break; + /* Add stubs for set functions */ + case AX_PDU_TYPE_TESTSET: + case AX_PDU_TYPE_COMMITSET: + case AX_PDU_TYPE_UNDOSET: + if (pdu->ap_header.aph_type == AX_PDU_TYPE_TESTSET) + error = AX_PDU_ERROR_NOTWRITABLE; + else if (pdu->ap_header.aph_type == AX_PDU_TYPE_COMMITSET) + error = AX_PDU_ERROR_COMMITFAILED; + else + error = AX_PDU_ERROR_UNDOFAILED; + + agentx_log_axc_debug(axc, "unsupported call: %s", + ax_pdutype2string(pdu->ap_header.aph_type)); + if (ax_response(ax->ax_ax, axs->axs_id, + pdu->ap_header.aph_transactionid, + pdu->ap_header.aph_packetid, + axc == NULL ? NULL : AGENTX_CONTEXT_CTX(axc), + 0, error, 1, NULL, 0) == -1) + agentx_log_axc_warn(axc, + "transaction: %u packetid: %u: failed to send " + "reply", pdu->ap_header.aph_transactionid, + pdu->ap_header.aph_packetid); + if (ax->ax_ax->ax_wblen > 0) + agentx_wantwrite(ax, ax->ax_fd); + break; + case AX_PDU_TYPE_CLEANUPSET: + agentx_log_ax_debug(ax, "unsupported call: %s", + ax_pdutype2string(pdu->ap_header.aph_type)); + break; + case AX_PDU_TYPE_RESPONSE: + axr_search.axr_packetid = pdu->ap_header.aph_packetid; + axr = RB_FIND(ax_requests, &(ax->ax_requests), &axr_search); + if (axr == NULL) { + if (axc == NULL) + agentx_log_ax_warnx(ax, "received " + "response on non-request"); + else + agentx_log_axc_warnx(axc, "received " + "response on non-request"); + break; + } + if (axc != NULL && pdu->ap_payload.ap_response.ap_error == 0) { + axc->axc_sysuptime = + pdu->ap_payload.ap_response.ap_uptime; + (void) clock_gettime(CLOCK_MONOTONIC, + &(axc->axc_sysuptimespec)); + } + RB_REMOVE(ax_requests, &(ax->ax_requests), axr); + (void) axr->axr_cb(pdu, axr->axr_cookie); + free(axr); + break; + default: + if (axc == NULL) + agentx_log_ax_warnx(ax, "unsupported call: %s", + ax_pdutype2string(pdu->ap_header.aph_type)); + else + agentx_log_axc_warnx(axc, "unsupported call: %s", + ax_pdutype2string(pdu->ap_header.aph_type)); + agentx_reset(ax); + break; + } + ax_pdu_free(pdu); +} + +void +agentx_write(struct agentx *ax) +{ + ssize_t send; + + if ((send = ax_send(ax->ax_ax)) == -1) { + if (errno == EAGAIN) { + agentx_wantwrite(ax, ax->ax_fd); + return; + } + agentx_log_ax_warn(ax, "lost connection"); + agentx_reset(ax); + return; + } + if (send > 0) + agentx_wantwrite(ax, ax->ax_fd); +} + +RB_GENERATE_STATIC(ax_requests, agentx_request, axr_ax_requests, + agentx_request_cmp) +RB_GENERATE_STATIC(axc_objects, agentx_object, axo_axc_objects, + agentx_object_cmp) diff --git a/lib/libagentx/agentx.h b/lib/libagentx/agentx.h index 2300c79117a..fb634fcd803 100644 --- a/lib/libagentx/agentx.h +++ b/lib/libagentx/agentx.h @@ -14,219 +14,134 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <stdint.h> - -#define AGENTX_PDU_FLAG_INSTANCE_REGISTRATION (1 << 0) -#define AGENTX_PDU_FLAG_NEW_INDEX (1 << 1) -#define AGENTX_PDU_FLAG_ANY_INDEX (1 << 2) -#define AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT (1 << 3) -#define AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER (1 << 4) - -#define AGENTX_PRIORITY_DEFAULT 127 - -enum agentx_byte_order { - AGENTX_BYTE_ORDER_BE, - AGENTX_BYTE_ORDER_LE -}; - -#if BYTE_ORDER == BIG_ENDIAN -#define AGENTX_BYTE_ORDER_NATIVE AGENTX_BYTE_ORDER_BE -#else -#define AGENTX_BYTE_ORDER_NATIVE AGENTX_BYTE_ORDER_LE -#endif - -enum agentx_pdu_type { - AGENTX_PDU_TYPE_OPEN = 1, - AGENTX_PDU_TYPE_CLOSE = 2, - AGENTX_PDU_TYPE_REGISTER = 3, - AGENTX_PDU_TYPE_UNREGISTER = 4, - AGENTX_PDU_TYPE_GET = 5, - AGENTX_PDU_TYPE_GETNEXT = 6, - AGENTX_PDU_TYPE_GETBULK = 7, - AGENTX_PDU_TYPE_TESTSET = 8, - AGENTX_PDU_TYPE_COMMITSET = 9, - AGENTX_PDU_TYPE_UNDOSET = 10, - AGENTX_PDU_TYPE_CLEANUPSET = 11, - AGENTX_PDU_TYPE_NOTIFY = 12, - AGENTX_PDU_TYPE_PING = 13, - AGENTX_PDU_TYPE_INDEXALLOCATE = 14, - AGENTX_PDU_TYPE_INDEXDEALLOCATE = 15, - AGENTX_PDU_TYPE_ADDAGENTCAPS = 16, - AGENTX_PDU_TYPE_REMOVEAGENTCAPS = 17, - AGENTX_PDU_TYPE_RESPONSE = 18 -}; +#include <netinet/in.h> -enum agentx_pdu_error { - AGENTX_PDU_ERROR_NOERROR = 0, - AGENTX_PDU_ERROR_GENERR = 5, - AGENTX_PDU_ERROR_NOACCESS = 6, - AGENTX_PDU_ERROR_WRONGTYPE = 7, - AGENTX_PDU_ERROR_WRONGLENGTH = 8, - AGENTX_PDU_ERROR_WRONGENCODING = 9, - AGENTX_PDU_ERROR_WRONGVALUE = 10, - AGENTX_PDU_ERROR_NOCREATION = 11, - AGENTX_PDU_ERROR_INCONSISTENTVALUE = 12, - AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE = 13, - AGENTX_PDU_ERROR_COMMITFAILED = 14, - AGENTX_PDU_ERROR_UNDOFAILED = 15, - AGENTX_PDU_ERROR_NOTWRITABLE = 17, - AGENTX_PDU_ERROR_INCONSISTENTNAME = 18, - AGENTX_PDU_ERROR_OPENFAILED = 256, - AGENTX_PDU_ERROR_NOTOPEN = 257, - AGENTX_PDU_ERROR_INDEXWRONGTYPE = 258, - AGENTX_PDU_ERROR_INDEXALREADYALLOCATED = 259, - AGENTX_PDU_ERROR_INDEXNONEAVAILABLE = 260, - AGENTX_PDU_ERROR_INDEXNOTALLOCATED = 261, - AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT = 262, - AGENTX_PDU_ERROR_DUPLICATEREGISTRATION = 263, - AGENTX_PDU_ERROR_UNKNOWNREGISTRATION = 264, - AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS = 265, - AGENTX_PDU_ERROR_PARSEERROR = 266, - AGENTX_PDU_ERROR_REQUESTDENIED = 267, - AGENTX_PDU_ERROR_PROCESSINGERROR = 268 -}; - -enum agentx_data_type { - AGENTX_DATA_TYPE_INTEGER = 2, - AGENTX_DATA_TYPE_OCTETSTRING = 4, - AGENTX_DATA_TYPE_NULL = 5, - AGENTX_DATA_TYPE_OID = 6, - AGENTX_DATA_TYPE_IPADDRESS = 64, - AGENTX_DATA_TYPE_COUNTER32 = 65, - AGENTX_DATA_TYPE_GAUGE32 = 66, - AGENTX_DATA_TYPE_TIMETICKS = 67, - AGENTX_DATA_TYPE_OPAQUE = 68, - AGENTX_DATA_TYPE_COUNTER64 = 70, - AGENTX_DATA_TYPE_NOSUCHOBJECT = 128, - AGENTX_DATA_TYPE_NOSUCHINSTANCE = 129, - AGENTX_DATA_TYPE_ENDOFMIBVIEW = 130 -}; - -enum agentx_close_reason { - AGENTX_CLOSE_OTHER = 1, - AGENTX_CLOSEN_PARSEERROR = 2, - AGENTX_CLOSE_PROTOCOLERROR = 3, - AGENTX_CLOSE_TIMEOUTS = 4, - AGENTX_CLOSE_SHUTDOWN = 5, - AGENTX_CLOSE_BYMANAGER = 6 -}; - -struct agentx { - int ax_fd; - enum agentx_byte_order ax_byteorder; - uint8_t *ax_rbuf; - size_t ax_rblen; - size_t ax_rbsize; - uint8_t *ax_wbuf; - size_t ax_wblen; - size_t ax_wbtlen; - size_t ax_wbsize; - uint32_t *ax_packetids; - size_t ax_packetidsize; +#include <stdint.h> +#include <stddef.h> + +struct agentx; +struct agentx_session; +struct agentx_context; +struct agentx_agentcaps; +struct agentx_region; +struct agentx_index; +struct agentx_object; +struct agentx_varbind; + +enum agentx_request_type { + AGENTX_REQUEST_TYPE_GET, + AGENTX_REQUEST_TYPE_GETNEXT, + AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE }; -#ifndef AGENTX_PRIMITIVE -#define AGENTX_PRIMITIVE - +#define AGENTX_MASTER_PATH "/var/agentx/master" #define AGENTX_OID_MAX_LEN 128 - -struct agentx_oid { - uint8_t aoi_include; - uint32_t aoi_id[AGENTX_OID_MAX_LEN]; - size_t aoi_idlen; -}; - -struct agentx_ostring { - unsigned char *aos_string; - uint32_t aos_slen; -}; -#endif - -struct agentx_searchrange { - struct agentx_oid asr_start; - struct agentx_oid asr_stop; -}; - -struct agentx_pdu_header { - uint8_t aph_version; - uint8_t aph_type; - uint8_t aph_flags; - uint8_t aph_reserved; - uint32_t aph_sessionid; - uint32_t aph_transactionid; - uint32_t aph_packetid; - uint32_t aph_plength; -}; - -struct agentx_varbind { - enum agentx_data_type avb_type; - struct agentx_oid avb_oid; - union agentx_data { - uint32_t avb_uint32; - uint64_t avb_uint64; - struct agentx_ostring avb_ostring; - struct agentx_oid avb_oid; - } avb_data; -}; - -struct agentx_pdu { - struct agentx_pdu_header ap_header; - struct agentx_ostring ap_context; - union { - struct agentx_pdu_searchrangelist { - size_t ap_nsr; - struct agentx_searchrange *ap_sr; - } ap_srl; - struct agentx_pdu_getbulk { - uint16_t ap_nonrep; - uint16_t ap_maxrep; - struct agentx_pdu_searchrangelist ap_srl; - } ap_getbulk; - struct agentx_pdu_varbindlist { - struct agentx_varbind *ap_varbind; - size_t ap_nvarbind; - } ap_vbl; - struct agentx_pdu_response { - uint32_t ap_uptime; - enum agentx_pdu_error ap_error; - uint16_t ap_index; - struct agentx_varbind *ap_varbindlist; - size_t ap_nvarbind; - } ap_response; - void *ap_raw; - } ap_payload; -}; - -struct agentx *agentx_new(int); +#define AGENTX_OID_INDEX_MAX_LEN 10 +#define AGENTX_MIB2 1, 3, 6, 1, 2, 1 +#define AGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1 +#define AGENTX_OID(...) (uint32_t []) { __VA_ARGS__ }, \ + (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) + +extern void (*agentx_log_fatal)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +extern void (*agentx_log_warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +extern void (*agentx_log_info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +extern void (*agentx_log_debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +struct agentx *agentx(void (*)(struct agentx *, void *, int), void *); +void agentx_connect(struct agentx *, int); +void agentx_read(struct agentx *); +void agentx_write(struct agentx *); +extern void (*agentx_wantwrite)(struct agentx *, int); void agentx_free(struct agentx *); -struct agentx_pdu *agentx_recv(struct agentx *); -ssize_t agentx_send(struct agentx *); -uint32_t agentx_open(struct agentx *, uint8_t, struct agentx_oid *, - struct agentx_ostring *); -uint32_t agentx_close(struct agentx *, uint32_t, enum agentx_close_reason); -uint32_t agentx_indexallocate(struct agentx *, uint8_t, uint32_t, - struct agentx_ostring *, struct agentx_varbind *, size_t); -uint32_t agentx_indexdeallocate(struct agentx *, uint32_t, - struct agentx_ostring *, struct agentx_varbind *, size_t); -uint32_t agentx_addagentcaps(struct agentx *, uint32_t, struct agentx_ostring *, - struct agentx_oid *, struct agentx_ostring *); -uint32_t agentx_removeagentcaps(struct agentx *, uint32_t, - struct agentx_ostring *, struct agentx_oid *); -uint32_t agentx_register(struct agentx *, uint8_t, uint32_t, - struct agentx_ostring *, uint8_t, uint8_t, uint8_t, struct agentx_oid *, - uint32_t); -uint32_t agentx_unregister(struct agentx *, uint32_t, struct agentx_ostring *, - uint8_t, uint8_t, struct agentx_oid *, uint32_t); -int agentx_response(struct agentx *, uint32_t, uint32_t, uint32_t, - struct agentx_ostring *, uint32_t, uint16_t, uint16_t, - struct agentx_varbind *, size_t); -void agentx_pdu_free(struct agentx_pdu *); -void agentx_varbind_free(struct agentx_varbind *); -const char *agentx_error2string(enum agentx_pdu_error); -const char *agentx_pdutype2string(enum agentx_pdu_type); -const char *agentx_oid2string(struct agentx_oid *); -const char *agentx_oidrange2string(struct agentx_oid *, uint8_t, uint32_t); -const char *agentx_varbind2string(struct agentx_varbind *); -const char *agentx_closereason2string(enum agentx_close_reason); -int agentx_oid_cmp(struct agentx_oid *, struct agentx_oid *); -int agentx_oid_add(struct agentx_oid *, uint32_t); +struct agentx_session *agentx_session(struct agentx *, + uint32_t[], size_t, const char *, uint8_t); +void agentx_session_free(struct agentx_session *); +struct agentx_context *agentx_context(struct agentx_session *, + const char *); +struct agentx_object *agentx_context_object_find( + struct agentx_context *, const uint32_t[], size_t, int, int); +struct agentx_object *agentx_context_object_nfind( + struct agentx_context *, const uint32_t[], size_t, int, int); +uint32_t agentx_context_uptime(struct agentx_context *); +void agentx_context_free(struct agentx_context *); +struct agentx_agentcaps *agentx_agentcaps(struct agentx_context *, + uint32_t[], size_t, const char *); +void agentx_agentcaps_free(struct agentx_agentcaps *); +struct agentx_region *agentx_region(struct agentx_context *, + uint32_t[], size_t, uint8_t); +void agentx_region_free(struct agentx_region *); +struct agentx_index *agentx_index_integer_new(struct agentx_region *, + uint32_t[], size_t); +struct agentx_index *agentx_index_integer_any(struct agentx_region *, + uint32_t[], size_t); +struct agentx_index *agentx_index_integer_value(struct agentx_region *, + uint32_t[], size_t, uint32_t); +struct agentx_index *agentx_index_integer_dynamic( + struct agentx_region *, uint32_t[], size_t); +struct agentx_index *agentx_index_string_dynamic( + struct agentx_region *, uint32_t[], size_t); +struct agentx_index *agentx_index_nstring_dynamic( + struct agentx_region *, uint32_t[], size_t, size_t); +struct agentx_index *agentx_index_oid_dynamic(struct agentx_region *, + uint32_t[], size_t); +struct agentx_index *agentx_index_noid_dynamic(struct agentx_region *, + uint32_t[], size_t, size_t); +struct agentx_index *agentx_index_ipaddress_dynamic( + struct agentx_region *, uint32_t[], size_t); +void agentx_index_free(struct agentx_index *); +struct agentx_object *agentx_object(struct agentx_region *, uint32_t[], + size_t, struct agentx_index *[], size_t, int, + void (*)(struct agentx_varbind *)); +void agentx_object_free(struct agentx_object *); + +void agentx_varbind_integer(struct agentx_varbind *, uint32_t); +void agentx_varbind_string(struct agentx_varbind *, const char *); +void agentx_varbind_nstring(struct agentx_varbind *, + const unsigned char *, size_t); +void agentx_varbind_printf(struct agentx_varbind *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_varbind_null(struct agentx_varbind *); +void agentx_varbind_oid(struct agentx_varbind *, const uint32_t[], + size_t); +void agentx_varbind_object(struct agentx_varbind *, + struct agentx_object *); +void agentx_varbind_index(struct agentx_varbind *, + struct agentx_index *); +void agentx_varbind_ipaddress(struct agentx_varbind *, + const struct in_addr *); +void agentx_varbind_counter32(struct agentx_varbind *, uint32_t); +void agentx_varbind_gauge32(struct agentx_varbind *, uint32_t); +void agentx_varbind_timeticks(struct agentx_varbind *, uint32_t); +void agentx_varbind_opaque(struct agentx_varbind *, const char *, size_t); +void agentx_varbind_counter64(struct agentx_varbind *, uint64_t); +void agentx_varbind_notfound(struct agentx_varbind *); +void agentx_varbind_error(struct agentx_varbind *); + +enum agentx_request_type agentx_varbind_request( + struct agentx_varbind *); +struct agentx_object * + agentx_varbind_get_object(struct agentx_varbind *); +uint32_t agentx_varbind_get_index_integer(struct agentx_varbind *, + struct agentx_index *); +const unsigned char *agentx_varbind_get_index_string( + struct agentx_varbind *, struct agentx_index *, size_t *, int *); +const uint32_t *agentx_varbind_get_index_oid(struct agentx_varbind *, + struct agentx_index *, size_t *, int *); +const struct in_addr *agentx_varbind_get_index_ipaddress( + struct agentx_varbind *, struct agentx_index *); +void agentx_varbind_set_index_integer(struct agentx_varbind *, + struct agentx_index *, uint32_t); +void agentx_varbind_set_index_string(struct agentx_varbind *, + struct agentx_index *, const char *); +void agentx_varbind_set_index_nstring(struct agentx_varbind *, + struct agentx_index *, const unsigned char *, size_t); +void agentx_varbind_set_index_oid(struct agentx_varbind *, + struct agentx_index *, const uint32_t *, size_t); +void agentx_varbind_set_index_object(struct agentx_varbind *, + struct agentx_index *, struct agentx_object *); +void agentx_varbind_set_index_ipaddress(struct agentx_varbind *, + struct agentx_index *, const struct in_addr *); diff --git a/lib/libagentx/agentx_internal.h b/lib/libagentx/agentx_internal.h new file mode 100644 index 00000000000..f8d7a8ce2d9 --- /dev/null +++ b/lib/libagentx/agentx_internal.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/tree.h> + +#include "ax.h" + +enum agentx_cstate { /* Current state */ + AX_CSTATE_CLOSE, /* Closed */ + AX_CSTATE_WAITOPEN, /* Connection requested */ + AX_CSTATE_OPEN, /* Open */ + AX_CSTATE_WAITCLOSE /* Close requested */ +}; + +enum agentx_dstate { /* Desired state */ + AX_DSTATE_OPEN, /* Open */ + AX_DSTATE_CLOSE /* Close/free */ +}; + +struct agentx { + void (*ax_nofd)(struct agentx *, void *, int); + void *ax_cookie; + int ax_fd; + enum agentx_cstate ax_cstate; + enum agentx_dstate ax_dstate; + struct ax *ax_ax; + TAILQ_HEAD(, agentx_session) ax_sessions; + TAILQ_HEAD(, agentx_get) ax_getreqs; + RB_HEAD(ax_requests, agentx_request) ax_requests; +}; + +struct agentx_session { + struct agentx *axs_ax; + uint32_t axs_id; + uint32_t axs_timeout; + struct ax_oid axs_oid; + struct ax_ostring axs_descr; + enum agentx_cstate axs_cstate; + enum agentx_dstate axs_dstate; + uint32_t axs_packetid; + TAILQ_HEAD(, agentx_context) axs_contexts; + TAILQ_ENTRY(agentx_session) axs_ax_sessions; +}; + +struct agentx_context { + struct agentx_session *axc_axs; + int axc_name_default; + struct ax_ostring axc_name; + uint32_t axc_sysuptime; + struct timespec axc_sysuptimespec; + enum agentx_cstate axc_cstate; + enum agentx_dstate axc_dstate; + TAILQ_HEAD(, agentx_agentcaps) axc_agentcaps; + TAILQ_HEAD(, agentx_region) axc_regions; + RB_HEAD(axc_objects, agentx_object) axc_objects; + TAILQ_ENTRY(agentx_context) axc_axs_contexts; +}; + +struct agentx_get { + struct agentx_context *axg_axc; + int axg_fd; /* Only used for logging */ + uint32_t axg_sessionid; + uint32_t axg_transactionid; + uint32_t axg_packetid; + int axg_context_default; + struct ax_ostring axg_context; + enum ax_pdu_type axg_type; + uint16_t axg_nonrep; + uint16_t axg_maxrep; + size_t axg_nvarbind; + struct agentx_varbind *axg_varbind; + TAILQ_ENTRY(agentx_get) axg_ax_getreqs; +}; + +__dead void agentx_log_ax_fatalx(struct agentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_ax_warn(struct agentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_ax_warnx(struct agentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_ax_info(struct agentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_ax_debug(struct agentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead void agentx_log_axs_fatalx(struct agentx_session *, const char *, + ...) __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axs_warnx(struct agentx_session *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axs_warn(struct agentx_session *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axs_info(struct agentx_session *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead void agentx_log_axc_fatalx(struct agentx_context *, const char *, + ...) __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axc_warnx(struct agentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axc_warn(struct agentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axc_info(struct agentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axc_debug(struct agentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead void agentx_log_axg_fatalx(struct agentx_get *, const char *, + ...) __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axg_warnx(struct agentx_get *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axg_warn(struct agentx_get *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void agentx_log_axg_debug(struct agentx_get *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); diff --git a/lib/libagentx/agentx_log.c b/lib/libagentx/agentx_log.c new file mode 100644 index 00000000000..fd01b626bcd --- /dev/null +++ b/lib/libagentx/agentx_log.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "agentx_internal.h" + +#define AGENTX_CONTEXT_NAME(axc) (axc->axc_name_default ? "<default>" : \ + (char *)axc->axc_name.aos_string) +#define AGENTX_GET_CTXNAME(axg) (axg->axg_context_default ? "<default>" : \ + (char *)axg->axg_context.aos_string) + +enum agentx_log_type { + AGENTX_LOG_TYPE_FATAL, + AGENTX_LOG_TYPE_WARN, + AGENTX_LOG_TYPE_INFO, + AGENTX_LOG_TYPE_DEBUG +}; + +void (*agentx_log_fatal)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; +void (*agentx_log_warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; +void (*agentx_log_info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; +void (*agentx_log_debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; + + +static void +agentx_log_do(enum agentx_log_type, const char *, va_list, int, + struct agentx *, struct agentx_session *, struct agentx_context *, + struct agentx_get *); + +void +agentx_log_ax_fatalx(struct agentx *ax, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_FATAL, fmt, ap, 0, ax, NULL, NULL, + NULL); + va_end(ap); + abort(); +} + +void +agentx_log_ax_warn(struct agentx *ax, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 1, ax, NULL, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_ax_warnx(struct agentx *ax, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 0, ax, NULL, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_ax_info(struct agentx *ax, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_INFO, fmt, ap, 0, ax, NULL, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_ax_debug(struct agentx *ax, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, ax, NULL, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_axs_fatalx(struct agentx_session *axs, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, axs, NULL, + NULL); + va_end(ap); + abort(); +} + +void +agentx_log_axs_warnx(struct agentx_session *axs, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, axs, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_axs_warn(struct agentx_session *axs, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, axs, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_axs_info(struct agentx_session *axs, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, axs, NULL, + NULL); + va_end(ap); +} + +void +agentx_log_axc_fatalx(struct agentx_context *axc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, axc, + NULL); + va_end(ap); + abort(); +} + +void +agentx_log_axc_warnx(struct agentx_context *axc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, axc, + NULL); + va_end(ap); +} + +void +agentx_log_axc_warn(struct agentx_context *axc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, axc, + NULL); + va_end(ap); +} + +void +agentx_log_axc_info(struct agentx_context *axc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, NULL, axc, + NULL); + va_end(ap); +} + +void +agentx_log_axc_debug(struct agentx_context *axc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, axc, + NULL); + va_end(ap); +} + +void +agentx_log_axg_fatalx(struct agentx_get *axg, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, NULL, + axg); + va_end(ap); + abort(); +} + +void +agentx_log_axg_warnx(struct agentx_get *axg, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, NULL, + axg); + va_end(ap); +} + +void +agentx_log_axg_warn(struct agentx_get *axg, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, NULL, + axg); + va_end(ap); +} + +void +agentx_log_axg_debug(struct agentx_get *axg, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + agentx_log_do(AGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, NULL, + axg); + va_end(ap); +} + +static void +agentx_log_do(enum agentx_log_type type, const char *fmt, va_list ap, + int useerrno, struct agentx *ax, struct agentx_session *axs, + struct agentx_context *axc, struct agentx_get *axg) +{ + void (*agentx_log)(const char *, ...); + char buf[1500]; + + if (type == AGENTX_LOG_TYPE_FATAL) + agentx_log = agentx_log_fatal; + else if (type == AGENTX_LOG_TYPE_WARN) + agentx_log = agentx_log_warn; + else if (type == AGENTX_LOG_TYPE_INFO) + agentx_log = agentx_log_info; + else + agentx_log = agentx_log_debug; + if (agentx_log == NULL) + return; + + vsnprintf(buf, sizeof(buf), fmt, ap); + + if (axg != NULL) { + if (useerrno) + agentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: " + "%s: %s", axg->axg_fd, axg->axg_sessionid, + AGENTX_GET_CTXNAME(axg), axg->axg_transactionid, + axg->axg_packetid, buf, strerror(errno)); + else + agentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: " + "%s", axg->axg_fd, axg->axg_sessionid, + AGENTX_GET_CTXNAME(axg), axg->axg_transactionid, + axg->axg_packetid, buf); + } else if (axc != NULL) { + axs = axc->axc_axs; + ax = axs->axs_ax; + if (useerrno) + agentx_log("[fd:%d sess:%u ctx:%s]: %s: %s", + ax->ax_fd, axs->axs_id, AGENTX_CONTEXT_NAME(axc), + buf, strerror(errno)); + else + agentx_log("[fd:%d sess:%u ctx:%s]: %s", ax->ax_fd, + axs->axs_id, AGENTX_CONTEXT_NAME(axc), buf); + } else if (axs != NULL) { + ax = axs->axs_ax; + if (useerrno) + agentx_log("[fd:%d sess:%u]: %s: %s", ax->ax_fd, + axs->axs_id, buf, strerror(errno)); + else + agentx_log("[fd:%d sess:%u]: %s", ax->ax_fd, + axs->axs_id, buf); + } else if (ax->ax_fd == -1) { + if (useerrno) + agentx_log("%s: %s", buf, strerror(errno)); + else + agentx_log("%s", buf); + } else { + if (useerrno) + agentx_log("[fd:%d]: %s: %s", ax->ax_fd, buf, + strerror(errno)); + else + agentx_log("[fd:%d]: %s", ax->ax_fd, buf); + } +} diff --git a/lib/libagentx/ax.c b/lib/libagentx/ax.c new file mode 100644 index 00000000000..8a287040d7b --- /dev/null +++ b/lib/libagentx/ax.c @@ -0,0 +1,1359 @@ +/* + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/socket.h> + +#include <arpa/inet.h> + +#include <ctype.h> +#include <endian.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include "ax.h" + +#define AX_PDU_HEADER 20 + +static int ax_pdu_need(struct ax *, size_t); +static int ax_pdu_header(struct ax *, + enum ax_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t, + struct ax_ostring *); +static uint32_t ax_packetid(struct ax *); +static uint32_t ax_pdu_queue(struct ax *); +static int ax_pdu_add_uint16(struct ax *, uint16_t); +static int ax_pdu_add_uint32(struct ax *, uint32_t); +static int ax_pdu_add_uint64(struct ax *, uint64_t); +static int ax_pdu_add_oid(struct ax *, struct ax_oid *, int); +static int ax_pdu_add_str(struct ax *, struct ax_ostring *); +static int ax_pdu_add_varbindlist( struct ax *, struct ax_varbind *, + size_t); +static uint16_t ax_pdutoh16(struct ax_pdu_header *, uint8_t *); +static uint32_t ax_pdutoh32(struct ax_pdu_header *, uint8_t *); +static uint64_t ax_pdutoh64(struct ax_pdu_header *, uint8_t *); +static ssize_t ax_pdutooid(struct ax_pdu_header *, struct ax_oid *, + uint8_t *, size_t); +static ssize_t ax_pdutoostring(struct ax_pdu_header *, + struct ax_ostring *, uint8_t *, size_t); +static ssize_t ax_pdutovarbind(struct ax_pdu_header *, + struct ax_varbind *, uint8_t *, size_t); + +struct ax * +ax_new(int fd) +{ + struct ax *ax; + + if (fd == -1) { + errno = EINVAL; + return NULL; + } + + if ((ax = calloc(1, sizeof(*ax))) == NULL) + return NULL; + ax->ax_fd = fd; + if ((ax->ax_rbuf = malloc(512)) == NULL) + goto fail; + ax->ax_rbsize = 512; + ax->ax_byteorder = AX_BYTE_ORDER_NATIVE; + + return ax; + +fail: + free(ax); + return NULL; +} + +void +ax_free(struct ax *ax) +{ + if (ax == NULL) + return; + close(ax->ax_fd); + free(ax->ax_rbuf); + free(ax->ax_wbuf); + free(ax->ax_packetids); + free(ax); +} + +struct ax_pdu * +ax_recv(struct ax *ax) +{ + struct ax_pdu *pdu; + struct ax_pdu_header header; + struct ax_pdu_response *response; + struct ax_varbind *varbind; + struct ax_pdu_searchrangelist *srl = NULL; + struct ax_pdu_varbindlist *vbl; + struct ax_searchrange *sr; + size_t rbsize, packetidx = 0, i, rawlen; + ssize_t nread; + uint8_t *u8; + uint8_t *rbuf; + int found; + + /* Only read a single packet at a time to make sure libevent triggers */ + if (ax->ax_rblen < AX_PDU_HEADER) { + if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, + AX_PDU_HEADER - ax->ax_rblen)) == 0) { + errno = ECONNRESET; + return NULL; + } + if (nread == -1) + return NULL; + ax->ax_rblen += nread; + if (ax->ax_rblen < AX_PDU_HEADER) { + errno = EAGAIN; + return NULL; + } + } + u8 = ax->ax_rbuf; + header.aph_version = *u8++; + header.aph_type = *u8++; + header.aph_flags = *u8++; + u8++; + header.aph_sessionid = ax_pdutoh32(&header, u8); + u8 += 4; + header.aph_transactionid = ax_pdutoh32(&header, u8); + u8 += 4; + header.aph_packetid = ax_pdutoh32(&header, u8); + u8 += 4; + header.aph_plength = ax_pdutoh32(&header, u8); + + if (header.aph_version != 1) { + errno = EPROTO; + return NULL; + } + if (ax->ax_rblen < AX_PDU_HEADER + header.aph_plength) { + if (AX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) { + rbsize = (((AX_PDU_HEADER + header.aph_plength) + / 512) + 1) * 512; + if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize, + rbsize, sizeof(*rbuf))) == NULL) + return NULL; + ax->ax_rbsize = rbsize; + ax->ax_rbuf = rbuf; + } + nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, + header.aph_plength - (ax->ax_rblen - AX_PDU_HEADER)); + if (nread == 0) + errno = ECONNRESET; + if (nread <= 0) + return NULL; + ax->ax_rblen += nread; + if (ax->ax_rblen < AX_PDU_HEADER + header.aph_plength) { + errno = EAGAIN; + return NULL; + } + } + + if ((pdu = calloc(1, sizeof(*pdu))) == NULL) + return NULL; + + memcpy(&(pdu->ap_header), &header, sizeof(header)); + +#if defined(AX_DEBUG) && defined(AX_DEBUG_VERBOSE) + { + char chars[4]; + int print = 1; + + fprintf(stderr, "received packet:\n"); + for (i = 0; i < pdu->ap_header.aph_plength + AX_PDU_HEADER; + i++) { + fprintf(stderr, "%02hhx ", ax->ax_rbuf[i]); + chars[i % 4] = ax->ax_rbuf[i]; + if (!isprint(ax->ax_rbuf[i])) + print = 0; + if (i % 4 == 3) { + if (print) + fprintf(stderr, "%.4s", chars); + fprintf(stderr, "\n"); + print = 1; + } + } + } +#endif + + u8 = (ax->ax_rbuf) + AX_PDU_HEADER; + rawlen = pdu->ap_header.aph_plength; + if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) { + nread = ax_pdutoostring(&header, &(pdu->ap_context), u8, + rawlen); + if (nread == -1) + goto fail; + rawlen -= nread; + u8 += nread; + } + + switch (pdu->ap_header.aph_type) { + case AX_PDU_TYPE_GETBULK: + if (rawlen < 4) { + errno = EPROTO; + goto fail; + } + pdu->ap_payload.ap_getbulk.ap_nonrep = + ax_pdutoh16(&header, u8); + u8 += 2; + pdu->ap_payload.ap_getbulk.ap_maxrep = + ax_pdutoh16(&header, u8); + u8 += 2; + srl = &(pdu->ap_payload.ap_getbulk.ap_srl); + rawlen -= 4; + /* FALLTHROUGH */ + case AX_PDU_TYPE_GET: + case AX_PDU_TYPE_GETNEXT: + if (pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK) + srl = &(pdu->ap_payload.ap_srl); + while (rawlen > 0 ) { + srl->ap_nsr++; + sr = reallocarray(srl->ap_sr, srl->ap_nsr, sizeof(*sr)); + if (sr == NULL) + goto fail; + srl->ap_sr = sr; + sr += (srl->ap_nsr - 1); + if ((nread = ax_pdutooid(&header, &(sr->asr_start), + u8, rawlen)) == -1) + goto fail; + rawlen -= nread; + u8 += nread; + if ((nread = ax_pdutooid(&header, &(sr->asr_stop), + u8, rawlen)) == -1) + goto fail; + rawlen -= nread; + u8 += nread; + } + break; + case AX_PDU_TYPE_TESTSET: + vbl = &(pdu->ap_payload.ap_vbl); + while (rawlen > 0) { + varbind = recallocarray(vbl->ap_varbind, + vbl->ap_nvarbind, vbl->ap_nvarbind + 1, + sizeof(*(vbl->ap_varbind))); + if (varbind == NULL) + goto fail; + vbl->ap_varbind = varbind; + nread = ax_pdutovarbind(&header, + &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen); + if (nread == -1) + goto fail; + vbl->ap_nvarbind++; + u8 += nread; + rawlen -= nread; + } + break; + case AX_PDU_TYPE_COMMITSET: + case AX_PDU_TYPE_UNDOSET: + case AX_PDU_TYPE_CLEANUPSET: + if (rawlen != 0) { + errno = EPROTO; + goto fail; + } + break; + case AX_PDU_TYPE_RESPONSE: + if (ax->ax_packetids != NULL) { + found = 0; + for (i = 0; ax->ax_packetids[i] != 0; i++) { + if (ax->ax_packetids[i] == + pdu->ap_header.aph_packetid) { + packetidx = i; + found = 1; + } + } + if (found) { + ax->ax_packetids[packetidx] = + ax->ax_packetids[i - 1]; + ax->ax_packetids[i - 1] = 0; + } else { + errno = EPROTO; + goto fail; + } + } + if (rawlen < 8) { + errno = EPROTO; + goto fail; + } + response = &(pdu->ap_payload.ap_response); + response->ap_uptime = ax_pdutoh32(&header, u8); + u8 += 4; + response->ap_error = ax_pdutoh16(&header, u8); + u8 += 2; + response->ap_index = ax_pdutoh16(&header, u8); + u8 += 2; + rawlen -= 8; + while (rawlen > 0) { + varbind = recallocarray(response->ap_varbindlist, + response->ap_nvarbind, response->ap_nvarbind + 1, + sizeof(*(response->ap_varbindlist))); + if (varbind == NULL) + goto fail; + response->ap_varbindlist = varbind; + nread = ax_pdutovarbind(&header, + &(response->ap_varbindlist[response->ap_nvarbind]), + u8, rawlen); + if (nread == -1) + goto fail; + response->ap_nvarbind++; + u8 += nread; + rawlen -= nread; + } + break; + default: + pdu->ap_payload.ap_raw = malloc(pdu->ap_header.aph_plength); + if (pdu->ap_payload.ap_raw == NULL) + goto fail; + memcpy(pdu->ap_payload.ap_raw, ax->ax_rbuf + AX_PDU_HEADER, + pdu->ap_header.aph_plength); + break; + } + + ax->ax_rblen = 0; + + return pdu; +fail: + ax_pdu_free(pdu); + return NULL; +} + +static int +ax_pdu_need(struct ax *ax, size_t need) +{ + uint8_t *wbuf; + size_t wbsize; + + if (ax->ax_wbtlen >= ax->ax_wbsize) { + wbsize = ((ax->ax_wbtlen / 512) + 1) * 512; + wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1); + if (wbuf == NULL) { + ax->ax_wbtlen = ax->ax_wblen; + return -1; + } + ax->ax_wbsize = wbsize; + ax->ax_wbuf = wbuf; + } + + return 0; +} + +ssize_t +ax_send(struct ax *ax) +{ + ssize_t nwrite; + + if (ax->ax_wblen != ax->ax_wbtlen) { + errno = EALREADY; + return -1; + } + + if (ax->ax_wblen == 0) + return 0; + +#if defined(AX_DEBUG) && defined(AX_DEBUG_VERBOSE) + { + size_t i; + char chars[4]; + int print = 1; + + fprintf(stderr, "sending packet:\n"); + for (i = 0; i < ax->ax_wblen; i++) { + fprintf(stderr, "%02hhx ", ax->ax_wbuf[i]); + chars[i % 4] = ax->ax_wbuf[i]; + if (!isprint(ax->ax_wbuf[i])) + print = 0; + if (i % 4 == 3) { + if (print) + fprintf(stderr, "%.4s", chars); + fprintf(stderr, "\n"); + print = 1; + } + } + } +#endif + + if ((nwrite = send(ax->ax_fd, ax->ax_wbuf, ax->ax_wblen, + MSG_NOSIGNAL | MSG_DONTWAIT)) == -1) + return -1; + + memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite); + ax->ax_wblen -= nwrite; + ax->ax_wbtlen = ax->ax_wblen; + + return ax->ax_wblen; +} + +uint32_t +ax_open(struct ax *ax, uint8_t timeout, struct ax_oid *oid, + struct ax_ostring *descr) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_OPEN, 0, 0, 0, 0, + NULL) == -1) + return 0; + ax_pdu_need(ax, 4); + ax->ax_wbuf[ax->ax_wbtlen++] = timeout; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); + ax->ax_wbtlen += 3; + if (ax_pdu_add_oid(ax, oid, 0) == -1) + return 0; + if (ax_pdu_add_str(ax, descr) == -1) + return 0; + + return ax_pdu_queue(ax); +} + +uint32_t +ax_close(struct ax *ax, uint32_t sessionid, + enum ax_close_reason reason) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0, + NULL) == -1) + return 0; + + if (ax_pdu_need(ax, 4) == -1) + return 0; + ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); + ax->ax_wbtlen += 3; + + return ax_pdu_queue(ax); +} + +uint32_t +ax_indexallocate(struct ax *ax, uint8_t flags, uint32_t sessionid, + struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb) +{ + if (flags & ~(AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)) { + errno = EINVAL; + return 0; + } + + if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXALLOCATE, flags, + sessionid, 0, 0, context) == -1) + return 0; + + if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) + return 0; + + return ax_pdu_queue(ax); +} + +uint32_t +ax_indexdeallocate(struct ax *ax, uint32_t sessionid, + struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXDEALLOCATE, 0, + sessionid, 0, 0, context) == -1) + return 0; + + if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) + return 0; + + return ax_pdu_queue(ax); +} + +uint32_t +ax_addagentcaps(struct ax *ax, uint32_t sessionid, + struct ax_ostring *context, struct ax_oid *id, + struct ax_ostring *descr) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_ADDAGENTCAPS, 0, + sessionid, 0, 0, context) == -1) + return 0; + if (ax_pdu_add_oid(ax, id, 0) == -1) + return 0; + if (ax_pdu_add_str(ax, descr) == -1) + return 0; + + return ax_pdu_queue(ax); +} + +uint32_t +ax_removeagentcaps(struct ax *ax, uint32_t sessionid, + struct ax_ostring *context, struct ax_oid *id) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_REMOVEAGENTCAPS, 0, + sessionid, 0, 0, context) == -1) + return 0; + if (ax_pdu_add_oid(ax, id, 0) == -1) + return 0; + + return ax_pdu_queue(ax); + +} + +uint32_t +ax_register(struct ax *ax, uint8_t flags, uint32_t sessionid, + struct ax_ostring *context, uint8_t timeout, uint8_t priority, + uint8_t range_subid, struct ax_oid *subtree, uint32_t upperbound) +{ + if (flags & ~(AX_PDU_FLAG_INSTANCE_REGISTRATION)) { + errno = EINVAL; + return 0; + } + + if (ax_pdu_header(ax, AX_PDU_TYPE_REGISTER, flags, + sessionid, 0, 0, context) == -1) + return 0; + + if (ax_pdu_need(ax, 4) == -1) + return 0; + ax->ax_wbuf[ax->ax_wbtlen++] = timeout; + ax->ax_wbuf[ax->ax_wbtlen++] = priority; + ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + if (ax_pdu_add_oid(ax, subtree, 0) == -1) + return 0; + if (range_subid != 0) { + if (ax_pdu_add_uint32(ax, upperbound) == -1) + return 0; + } + + return ax_pdu_queue(ax); +} + +uint32_t +ax_unregister(struct ax *ax, uint32_t sessionid, + struct ax_ostring *context, uint8_t priority, uint8_t range_subid, + struct ax_oid *subtree, uint32_t upperbound) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_UNREGISTER, 0, + sessionid, 0, 0, context) == -1) + return 0; + + if (ax_pdu_need(ax, 4) == -1) + return 0; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + ax->ax_wbuf[ax->ax_wbtlen++] = priority; + ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + if (ax_pdu_add_oid(ax, subtree, 0) == -1) + return 0; + if (range_subid != 0) { + if (ax_pdu_add_uint32(ax, upperbound) == -1) + return 0; + } + + return ax_pdu_queue(ax); +} + +int +ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid, + uint32_t packetid, struct ax_ostring *context, uint32_t sysuptime, + uint16_t error, uint16_t index, struct ax_varbind *vblist, size_t nvb) +{ + if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid, + transactionid, packetid, context) == -1) + return -1; + + if (ax_pdu_add_uint32(ax, sysuptime) == -1 || + ax_pdu_add_uint16(ax, error) == -1 || + ax_pdu_add_uint16(ax, index) == -1) + return -1; + + if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) + return -1; + if (ax_pdu_queue(ax) == 0) + return -1; + return 0; +} + +void +ax_pdu_free(struct ax_pdu *pdu) +{ + size_t i; + struct ax_pdu_response *response; + + if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) + free(pdu->ap_context.aos_string); + + switch (pdu->ap_header.aph_type) { + case AX_PDU_TYPE_GET: + case AX_PDU_TYPE_GETNEXT: + case AX_PDU_TYPE_GETBULK: + free(pdu->ap_payload.ap_srl.ap_sr); + break; + case AX_PDU_TYPE_RESPONSE: + response = &(pdu->ap_payload.ap_response); + for (i = 0; i < response->ap_nvarbind; i++) + ax_varbind_free(&(response->ap_varbindlist[i])); + free(response->ap_varbindlist); + break; + default: + free(pdu->ap_payload.ap_raw); + break; + } + free(pdu); +} + +void +ax_varbind_free(struct ax_varbind *varbind) +{ + switch (varbind->avb_type) { + case AX_DATA_TYPE_OCTETSTRING: + case AX_DATA_TYPE_IPADDRESS: + case AX_DATA_TYPE_OPAQUE: + free(varbind->avb_data.avb_ostring.aos_string); + break; + default: + break; + } +} + +const char * +ax_error2string(enum ax_pdu_error error) +{ + static char buffer[64]; + switch (error) { + case AX_PDU_ERROR_NOERROR: + return "No error"; + case AX_PDU_ERROR_GENERR: + return "Generic error"; + case AX_PDU_ERROR_NOACCESS: + return "No access"; + case AX_PDU_ERROR_WRONGTYPE: + return "Wrong type"; + case AX_PDU_ERROR_WRONGLENGTH: + return "Wrong length"; + case AX_PDU_ERROR_WRONGENCODING: + return "Wrong encoding"; + case AX_PDU_ERROR_WRONGVALUE: + return "Wrong value"; + case AX_PDU_ERROR_NOCREATION: + return "No creation"; + case AX_PDU_ERROR_INCONSISTENTVALUE: + return "Inconsistent value"; + case AX_PDU_ERROR_RESOURCEUNAVAILABLE: + return "Resource unavailable"; + case AX_PDU_ERROR_COMMITFAILED: + return "Commit failed"; + case AX_PDU_ERROR_UNDOFAILED: + return "Undo failed"; + case AX_PDU_ERROR_NOTWRITABLE: + return "Not writable"; + case AX_PDU_ERROR_INCONSISTENTNAME: + return "Inconsistent name"; + case AX_PDU_ERROR_OPENFAILED: + return "Open Failed"; + case AX_PDU_ERROR_NOTOPEN: + return "Not open"; + case AX_PDU_ERROR_INDEXWRONGTYPE: + return "Index wrong type"; + case AX_PDU_ERROR_INDEXALREADYALLOCATED: + return "Index already allocated"; + case AX_PDU_ERROR_INDEXNONEAVAILABLE: + return "Index none available"; + case AX_PDU_ERROR_INDEXNOTALLOCATED: + return "Index not allocated"; + case AX_PDU_ERROR_UNSUPPORTEDCONETXT: + return "Unsupported context"; + case AX_PDU_ERROR_DUPLICATEREGISTRATION: + return "Duplicate registration"; + case AX_PDU_ERROR_UNKNOWNREGISTRATION: + return "Unkown registration"; + case AX_PDU_ERROR_UNKNOWNAGENTCAPS: + return "Unknown agent capabilities"; + case AX_PDU_ERROR_PARSEERROR: + return "Parse error"; + case AX_PDU_ERROR_REQUESTDENIED: + return "Request denied"; + case AX_PDU_ERROR_PROCESSINGERROR: + return "Processing error"; + } + snprintf(buffer, sizeof(buffer), "Unknown error: %d", error); + return buffer; +} + +const char * +ax_pdutype2string(enum ax_pdu_type type) +{ + static char buffer[64]; + switch(type) { + case AX_PDU_TYPE_OPEN: + return "agentx-Open-PDU"; + case AX_PDU_TYPE_CLOSE: + return "agentx-Close-PDU"; + case AX_PDU_TYPE_REGISTER: + return "agentx-Register-PDU"; + case AX_PDU_TYPE_UNREGISTER: + return "agentx-Unregister-PDU"; + case AX_PDU_TYPE_GET: + return "agentx-Get-PDU"; + case AX_PDU_TYPE_GETNEXT: + return "agentx-GetNext-PDU"; + case AX_PDU_TYPE_GETBULK: + return "agentx-GetBulk-PDU"; + case AX_PDU_TYPE_TESTSET: + return "agentx-TestSet-PDU"; + case AX_PDU_TYPE_COMMITSET: + return "agentx-CommitSet-PDU"; + case AX_PDU_TYPE_UNDOSET: + return "agentx-UndoSet-PDU"; + case AX_PDU_TYPE_CLEANUPSET: + return "agentx-CleanupSet-PDU"; + case AX_PDU_TYPE_NOTIFY: + return "agentx-Notify-PDU"; + case AX_PDU_TYPE_PING: + return "agentx-Ping-PDU"; + case AX_PDU_TYPE_INDEXALLOCATE: + return "agentx-IndexAllocate-PDU"; + case AX_PDU_TYPE_INDEXDEALLOCATE: + return "agentx-IndexDeallocate-PDU"; + case AX_PDU_TYPE_ADDAGENTCAPS: + return "agentx-AddAgentCaps-PDU"; + case AX_PDU_TYPE_REMOVEAGENTCAPS: + return "agentx-RemoveAgentCaps-PDU"; + case AX_PDU_TYPE_RESPONSE: + return "agentx-Response-PDU"; + } + snprintf(buffer, sizeof(buffer), "Unknown type: %d", type); + return buffer; +} + +const char * +ax_closereason2string(enum ax_close_reason reason) +{ + static char buffer[64]; + + switch (reason) { + case AX_CLOSE_OTHER: + return "Undefined reason"; + case AX_CLOSEN_PARSEERROR: + return "Too many AgentX parse errors from peer"; + case AX_CLOSE_PROTOCOLERROR: + return "Too many AgentX protocol errors from peer"; + case AX_CLOSE_TIMEOUTS: + return "Too many timeouts waiting for peer"; + case AX_CLOSE_SHUTDOWN: + return "shutting down"; + case AX_CLOSE_BYMANAGER: + return "Manager shuts down"; + } + snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason); + return buffer; +} + +const char * +ax_oid2string(struct ax_oid *oid) +{ + return ax_oidrange2string(oid, 0, 0); +} + +const char * +ax_oidrange2string(struct ax_oid *oid, uint8_t range_subid, + uint32_t upperbound) +{ + static char buf[1024]; + char *p; + size_t i, rest; + int ret; + + rest = sizeof(buf); + p = buf; + for (i = 0; i < oid->aoi_idlen; i++) { + if (range_subid != 0 && range_subid - 1 == (uint8_t)i) + ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i], + upperbound); + else + ret = snprintf(p, rest, ".%u", oid->aoi_id[i]); + if ((size_t) ret >= rest) { + snprintf(buf, sizeof(buf), "Couldn't parse oid"); + return buf; + } + p += ret; + rest -= (size_t) ret; + } + return buf; +} + +const char * +ax_varbind2string(struct ax_varbind *vb) +{ + static char buf[1024]; + char tmpbuf[1024]; + size_t i, bufleft; + int ishex = 0; + char *p; + int ret; + + switch (vb->avb_type) { + case AX_DATA_TYPE_INTEGER: + snprintf(buf, sizeof(buf), "%s: (int)%u", + ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AX_DATA_TYPE_OCTETSTRING: + for (i = 0; + i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) { + if (!isprint(vb->avb_data.avb_ostring.aos_string[i])) + ishex = 1; + } + if (ishex) { + p = tmpbuf; + bufleft = sizeof(tmpbuf); + for (i = 0; + i < vb->avb_data.avb_ostring.aos_slen; i++) { + ret = snprintf(p, bufleft, " %02hhX", + vb->avb_data.avb_ostring.aos_string[i]); + if (ret >= (int) bufleft) { + p = strrchr(p, ' '); + strlcpy(p, "...", 4); + break; + } + p += 3; + bufleft -= 3; + } + ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s", + ax_oid2string(&(vb->avb_oid)), tmpbuf); + if (ret >= (int) sizeof(buf)) { + p = strrchr(buf, ' '); + strlcpy(p, "...", 4); + } + } else { + ret = snprintf(buf, sizeof(buf), "%s: (string)", + ax_oid2string(&(vb->avb_oid))); + if (ret >= (int) sizeof(buf)) { + snprintf(buf, sizeof(buf), "<too large OID>: " + "(string)<too large string>"); + break; + } + p = buf + ret; + bufleft = (int) sizeof(buf) - ret; + if (snprintf(p, bufleft, "%.*s", + vb->avb_data.avb_ostring.aos_slen, + vb->avb_data.avb_ostring.aos_string) >= + (int) bufleft) { + p = buf + sizeof(buf) - 4; + strlcpy(p, "...", 4); + } + } + break; + case AX_DATA_TYPE_NULL: + snprintf(buf, sizeof(buf), "%s: <null>", + ax_oid2string(&(vb->avb_oid))); + break; + case AX_DATA_TYPE_OID: + strlcpy(tmpbuf, + ax_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf)); + snprintf(buf, sizeof(buf), "%s: (oid)%s", + ax_oid2string(&(vb->avb_oid)), tmpbuf); + break; + case AX_DATA_TYPE_IPADDRESS: + if (vb->avb_data.avb_ostring.aos_slen != 4) { + snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>", + ax_oid2string(&(vb->avb_oid))); + break; + } + if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string, + tmpbuf, sizeof(tmpbuf)) == NULL) { + snprintf(buf, sizeof(buf), "%s: (ipaddress)" + "<unparseable>: %s", + ax_oid2string(&(vb->avb_oid)), + strerror(errno)); + break; + } + snprintf(buf, sizeof(buf), "%s: (ipaddress)%s", + ax_oid2string(&(vb->avb_oid)), tmpbuf); + break; + case AX_DATA_TYPE_COUNTER32: + snprintf(buf, sizeof(buf), "%s: (counter32)%u", + ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AX_DATA_TYPE_GAUGE32: + snprintf(buf, sizeof(buf), "%s: (gauge32)%u", + ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AX_DATA_TYPE_TIMETICKS: + snprintf(buf, sizeof(buf), "%s: (timeticks)%u", + ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AX_DATA_TYPE_OPAQUE: + p = tmpbuf; + bufleft = sizeof(tmpbuf); + for (i = 0; + i < vb->avb_data.avb_ostring.aos_slen; i++) { + ret = snprintf(p, bufleft, " %02hhX", + vb->avb_data.avb_ostring.aos_string[i]); + if (ret >= (int) bufleft) { + p = strrchr(p, ' '); + strlcpy(p, "...", 4); + break; + } + p += 3; + bufleft -= 3; + } + ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s", + ax_oid2string(&(vb->avb_oid)), tmpbuf); + if (ret >= (int) sizeof(buf)) { + p = strrchr(buf, ' '); + strlcpy(p, "...", 4); + } + break; + case AX_DATA_TYPE_COUNTER64: + snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64, + ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64); + break; + case AX_DATA_TYPE_NOSUCHOBJECT: + snprintf(buf, sizeof(buf), "%s: <noSuchObject>", + ax_oid2string(&(vb->avb_oid))); + break; + case AX_DATA_TYPE_NOSUCHINSTANCE: + snprintf(buf, sizeof(buf), "%s: <noSuchInstance>", + ax_oid2string(&(vb->avb_oid))); + break; + case AX_DATA_TYPE_ENDOFMIBVIEW: + snprintf(buf, sizeof(buf), "%s: <endOfMibView>", + ax_oid2string(&(vb->avb_oid))); + break; + } + return buf; +} + +int +ax_oid_cmp(struct ax_oid *o1, struct ax_oid *o2) +{ + size_t i, min; + + min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen; + for (i = 0; i < min; i++) { + if (o1->aoi_id[i] < o2->aoi_id[i]) + return -1; + if (o1->aoi_id[i] > o2->aoi_id[i]) + return 1; + } + /* o1 is parent of o2 */ + if (o1->aoi_idlen < o2->aoi_idlen) + return -2; + /* o1 is child of o2 */ + if (o1->aoi_idlen > o2->aoi_idlen) + return 2; + return 0; +} + +int +ax_oid_add(struct ax_oid *oid, uint32_t value) +{ + if (oid->aoi_idlen == AX_OID_MAX_LEN) + return -1; + oid->aoi_id[oid->aoi_idlen++] = value; + return 0; +} + +static uint32_t +ax_pdu_queue(struct ax *ax) +{ + struct ax_pdu_header header; + uint32_t packetid, plength; + size_t wbtlen = ax->ax_wbtlen; + + header.aph_flags = ax->ax_byteorder == AX_BYTE_ORDER_BE ? + AX_PDU_FLAG_NETWORK_BYTE_ORDER : 0; + packetid = ax_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12])); + plength = (ax->ax_wbtlen - ax->ax_wblen) - AX_PDU_HEADER; + ax->ax_wbtlen = ax->ax_wblen + 16; + (void)ax_pdu_add_uint32(ax, plength); + + ax->ax_wblen = ax->ax_wbtlen = wbtlen; + + return packetid; +} + +static int +ax_pdu_header(struct ax *ax, enum ax_pdu_type type, uint8_t flags, + uint32_t sessionid, uint32_t transactionid, uint32_t packetid, + struct ax_ostring *context) +{ + if (ax->ax_wblen != ax->ax_wbtlen) { + errno = EALREADY; + return -1; + } + + ax->ax_wbtlen = ax->ax_wblen; + if (ax_pdu_need(ax, 4) == -1) + return -1; + ax->ax_wbuf[ax->ax_wbtlen++] = 1; + ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t) type; + if (context != NULL) + flags |= AX_PDU_FLAG_NON_DEFAULT_CONTEXT; + if (ax->ax_byteorder == AX_BYTE_ORDER_BE) + flags |= AX_PDU_FLAG_NETWORK_BYTE_ORDER; + ax->ax_wbuf[ax->ax_wbtlen++] = flags; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + if (packetid == 0) + packetid = ax_packetid(ax); + if (ax_pdu_add_uint32(ax, sessionid) == -1 || + ax_pdu_add_uint32(ax, transactionid) == -1 || + ax_pdu_add_uint32(ax, packetid) == -1 || + ax_pdu_need(ax, 4) == -1) + return -1; + ax->ax_wbtlen += 4; + if (context != NULL) { + if (ax_pdu_add_str(ax, context) == -1) + return -1; + } + + return 0; +} + +static uint32_t +ax_packetid(struct ax *ax) +{ + uint32_t packetid, *packetids; + size_t npackets = 0, i; + int found; + + if (ax->ax_packetids != NULL) { + for (npackets = 0; ax->ax_packetids[npackets] != 0; npackets++) + continue; + } + if (ax->ax_packetidsize == 0 || npackets == ax->ax_packetidsize - 1) { + packetids = recallocarray(ax->ax_packetids, ax->ax_packetidsize, + ax->ax_packetidsize + 25, sizeof(*packetids)); + if (packetids == NULL) + return 0; + ax->ax_packetidsize += 25; + ax->ax_packetids = packetids; + } + do { + found = 0; + packetid = arc4random(); + for (i = 0; ax->ax_packetids[i] != 0; i++) { + if (ax->ax_packetids[i] == packetid) { + found = 1; + break; + } + } + } while (packetid == 0 || found); + ax->ax_packetids[npackets] = packetid; + + return packetid; +} + +static int +ax_pdu_add_uint16(struct ax *ax, uint16_t value) +{ + if (ax_pdu_need(ax, sizeof(value)) == -1) + return -1; + + if (ax->ax_byteorder == AX_BYTE_ORDER_BE) + value = htobe16(value); + else + value = htole16(value); + memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); + ax->ax_wbtlen += sizeof(value); + return 0; +} + +static int +ax_pdu_add_uint32(struct ax *ax, uint32_t value) +{ + if (ax_pdu_need(ax, sizeof(value)) == -1) + return -1; + + if (ax->ax_byteorder == AX_BYTE_ORDER_BE) + value = htobe32(value); + else + value = htole32(value); + memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); + ax->ax_wbtlen += sizeof(value); + return 0; +} + +static int +ax_pdu_add_uint64(struct ax *ax, uint64_t value) +{ + if (ax_pdu_need(ax, sizeof(value)) == -1) + return -1; + + if (ax->ax_byteorder == AX_BYTE_ORDER_BE) + value = htobe64(value); + else + value = htole64(value); + memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); + ax->ax_wbtlen += sizeof(value); + return 0; +} + + +static int +ax_pdu_add_oid(struct ax *ax, struct ax_oid *oid, int include) +{ + static struct ax_oid nulloid = {0}; + uint8_t prefix = 0, n_subid, i = 0; + + n_subid = oid->aoi_idlen; + + if (oid == NULL) + oid = &nulloid; + + if (oid->aoi_idlen > 4 && + oid->aoi_id[0] == 1 && oid->aoi_id[1] == 3 && + oid->aoi_id[2] == 6 && oid->aoi_id[3] == 1 && + oid->aoi_id[4] <= UINT8_MAX) { + prefix = oid->aoi_id[4]; + i = 5; + } + + if (ax_pdu_need(ax, 4) == -1) + return -1; + ax->ax_wbuf[ax->ax_wbtlen++] = n_subid - i; + ax->ax_wbuf[ax->ax_wbtlen++] = prefix; + ax->ax_wbuf[ax->ax_wbtlen++] = !!include; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + + for (; i < n_subid; i++) { + if (ax_pdu_add_uint32(ax, oid->aoi_id[i]) == -1) + return -1; + } + + return 0; +} + +static int +ax_pdu_add_str(struct ax *ax, struct ax_ostring *str) +{ + size_t length, zeroes; + + if (ax_pdu_add_uint32(ax, str->aos_slen) == -1) + return -1; + + if ((zeroes = (4 - (str->aos_slen % 4))) == 4) + zeroes = 0; + length = str->aos_slen + zeroes; + if (ax_pdu_need(ax, length) == -1) + return -1; + + memcpy(&(ax->ax_wbuf[ax->ax_wbtlen]), str->aos_string, str->aos_slen); + ax->ax_wbtlen += str->aos_slen; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, zeroes); + ax->ax_wbtlen += zeroes; + return 0; +} + +static int +ax_pdu_add_varbindlist(struct ax *ax, + struct ax_varbind *vblist, size_t nvb) +{ + size_t i; + uint16_t temp; + + for (i = 0; i < nvb; i++) { + temp = (uint16_t) vblist[i].avb_type; + if (ax_pdu_add_uint16(ax, temp) == -1 || + ax_pdu_need(ax, 2)) + return -1; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2); + ax->ax_wbtlen += 2; + if (ax_pdu_add_oid(ax, &(vblist[i].avb_oid), 0) == -1) + return -1; + switch (vblist[i].avb_type) { + case AX_DATA_TYPE_INTEGER: + case AX_DATA_TYPE_COUNTER32: + case AX_DATA_TYPE_GAUGE32: + case AX_DATA_TYPE_TIMETICKS: + if (ax_pdu_add_uint32(ax, + vblist[i].avb_data.avb_uint32) == -1) + return -1; + break; + case AX_DATA_TYPE_COUNTER64: + if (ax_pdu_add_uint64(ax, + vblist[i].avb_data.avb_uint64) == -1) + return -1; + break; + case AX_DATA_TYPE_OCTETSTRING: + case AX_DATA_TYPE_IPADDRESS: + case AX_DATA_TYPE_OPAQUE: + if (ax_pdu_add_str(ax, + &(vblist[i].avb_data.avb_ostring)) == -1) + return -1; + break; + case AX_DATA_TYPE_OID: + if (ax_pdu_add_oid(ax, + &(vblist[i].avb_data.avb_oid), 1) == -1) + return -1; + break; + case AX_DATA_TYPE_NULL: + case AX_DATA_TYPE_NOSUCHOBJECT: + case AX_DATA_TYPE_NOSUCHINSTANCE: + case AX_DATA_TYPE_ENDOFMIBVIEW: + break; + default: + errno = EINVAL; + return -1; + } + } + return 0; +} + +static uint16_t +ax_pdutoh16(struct ax_pdu_header *header, uint8_t *buf) +{ + uint16_t value; + + memcpy(&value, buf, sizeof(value)); + if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) + return be16toh(value); + return le16toh(value); +} + +static uint32_t +ax_pdutoh32(struct ax_pdu_header *header, uint8_t *buf) +{ + uint32_t value; + + memcpy(&value, buf, sizeof(value)); + if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) + return be32toh(value); + return le32toh(value); +} + +static uint64_t +ax_pdutoh64(struct ax_pdu_header *header, uint8_t *buf) +{ + uint64_t value; + + memcpy(&value, buf, sizeof(value)); + if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) + return be64toh(value); + return le64toh(value); +} + +static ssize_t +ax_pdutooid(struct ax_pdu_header *header, struct ax_oid *oid, + uint8_t *buf, size_t rawlen) +{ + size_t i = 0; + ssize_t nread; + + if (rawlen < 4) + goto fail; + rawlen -= 4; + nread = 4; + oid->aoi_idlen = *buf++; + if (rawlen < (oid->aoi_idlen * 4)) + goto fail; + nread += oid->aoi_idlen * 4; + if (*buf != 0) { + oid->aoi_id[0] = 1; + oid->aoi_id[1] = 3; + oid->aoi_id[2] = 6; + oid->aoi_id[3] = 1; + oid->aoi_id[4] = *buf; + oid->aoi_idlen += 5; + i = 5; + } + buf++; + oid->aoi_include = *buf; + for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) + oid->aoi_id[i] = ax_pdutoh32(header, buf); + + return nread; + +fail: + errno = EPROTO; + return -1; +} + +static ssize_t +ax_pdutoostring(struct ax_pdu_header *header, + struct ax_ostring *ostring, uint8_t *buf, size_t rawlen) +{ + ssize_t nread; + + if (rawlen < 4) + goto fail; + + ostring->aos_slen = ax_pdutoh32(header, buf); + rawlen -= 4; + buf += 4; + if (ostring->aos_slen > rawlen) + goto fail; + if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL) + return -1; + memcpy(ostring->aos_string, buf, ostring->aos_slen); + ostring->aos_string[ostring->aos_slen] = '\0'; + + nread = 4 + ostring->aos_slen; + if (ostring->aos_slen % 4 != 0) + nread += 4 - (ostring->aos_slen % 4); + + return nread; + +fail: + errno = EPROTO; + return -1; +} + +static ssize_t +ax_pdutovarbind(struct ax_pdu_header *header, + struct ax_varbind *varbind, uint8_t *buf, size_t rawlen) +{ + ssize_t nread, rread = 4; + + if (rawlen == 0) + return 0; + + if (rawlen < 8) + goto fail; + varbind->avb_type = ax_pdutoh16(header, buf); + + buf += 4; + rawlen -= 4; + nread = ax_pdutooid(header, &(varbind->avb_oid), buf, rawlen); + if (nread == -1) + return -1; + rread += nread; + buf += nread; + rawlen -= nread; + + switch(varbind->avb_type) { + case AX_DATA_TYPE_INTEGER: + case AX_DATA_TYPE_COUNTER32: + case AX_DATA_TYPE_GAUGE32: + case AX_DATA_TYPE_TIMETICKS: + if (rawlen < 4) + goto fail; + varbind->avb_data.avb_uint32 = ax_pdutoh32(header, buf); + return rread + 4; + case AX_DATA_TYPE_COUNTER64: + if (rawlen < 8) + goto fail; + varbind->avb_data.avb_uint64 = ax_pdutoh64(header, buf); + return rread + 8; + case AX_DATA_TYPE_OCTETSTRING: + case AX_DATA_TYPE_IPADDRESS: + case AX_DATA_TYPE_OPAQUE: + nread = ax_pdutoostring(header, + &(varbind->avb_data.avb_ostring), buf, rawlen); + if (nread == -1) + return -1; + return nread + rread; + case AX_DATA_TYPE_OID: + nread = ax_pdutooid(header, &(varbind->avb_data.avb_oid), + buf, rawlen); + if (nread == -1) + return -1; + return nread + rread; + case AX_DATA_TYPE_NULL: + case AX_DATA_TYPE_NOSUCHOBJECT: + case AX_DATA_TYPE_NOSUCHINSTANCE: + case AX_DATA_TYPE_ENDOFMIBVIEW: + return rread; + } + +fail: + errno = EPROTO; + return -1; +} diff --git a/lib/libagentx/ax.h b/lib/libagentx/ax.h new file mode 100644 index 00000000000..863507bc0b6 --- /dev/null +++ b/lib/libagentx/ax.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdint.h> + +#define AX_PDU_FLAG_INSTANCE_REGISTRATION (1 << 0) +#define AX_PDU_FLAG_NEW_INDEX (1 << 1) +#define AX_PDU_FLAG_ANY_INDEX (1 << 2) +#define AX_PDU_FLAG_NON_DEFAULT_CONTEXT (1 << 3) +#define AX_PDU_FLAG_NETWORK_BYTE_ORDER (1 << 4) + +#define AX_PRIORITY_DEFAULT 127 + +enum ax_byte_order { + AX_BYTE_ORDER_BE, + AX_BYTE_ORDER_LE +}; + +#if BYTE_ORDER == BIG_ENDIAN +#define AX_BYTE_ORDER_NATIVE AX_BYTE_ORDER_BE +#else +#define AX_BYTE_ORDER_NATIVE AX_BYTE_ORDER_LE +#endif + +enum ax_pdu_type { + AX_PDU_TYPE_OPEN = 1, + AX_PDU_TYPE_CLOSE = 2, + AX_PDU_TYPE_REGISTER = 3, + AX_PDU_TYPE_UNREGISTER = 4, + AX_PDU_TYPE_GET = 5, + AX_PDU_TYPE_GETNEXT = 6, + AX_PDU_TYPE_GETBULK = 7, + AX_PDU_TYPE_TESTSET = 8, + AX_PDU_TYPE_COMMITSET = 9, + AX_PDU_TYPE_UNDOSET = 10, + AX_PDU_TYPE_CLEANUPSET = 11, + AX_PDU_TYPE_NOTIFY = 12, + AX_PDU_TYPE_PING = 13, + AX_PDU_TYPE_INDEXALLOCATE = 14, + AX_PDU_TYPE_INDEXDEALLOCATE = 15, + AX_PDU_TYPE_ADDAGENTCAPS = 16, + AX_PDU_TYPE_REMOVEAGENTCAPS = 17, + AX_PDU_TYPE_RESPONSE = 18 +}; + +enum ax_pdu_error { + AX_PDU_ERROR_NOERROR = 0, + AX_PDU_ERROR_GENERR = 5, + AX_PDU_ERROR_NOACCESS = 6, + AX_PDU_ERROR_WRONGTYPE = 7, + AX_PDU_ERROR_WRONGLENGTH = 8, + AX_PDU_ERROR_WRONGENCODING = 9, + AX_PDU_ERROR_WRONGVALUE = 10, + AX_PDU_ERROR_NOCREATION = 11, + AX_PDU_ERROR_INCONSISTENTVALUE = 12, + AX_PDU_ERROR_RESOURCEUNAVAILABLE = 13, + AX_PDU_ERROR_COMMITFAILED = 14, + AX_PDU_ERROR_UNDOFAILED = 15, + AX_PDU_ERROR_NOTWRITABLE = 17, + AX_PDU_ERROR_INCONSISTENTNAME = 18, + AX_PDU_ERROR_OPENFAILED = 256, + AX_PDU_ERROR_NOTOPEN = 257, + AX_PDU_ERROR_INDEXWRONGTYPE = 258, + AX_PDU_ERROR_INDEXALREADYALLOCATED = 259, + AX_PDU_ERROR_INDEXNONEAVAILABLE = 260, + AX_PDU_ERROR_INDEXNOTALLOCATED = 261, + AX_PDU_ERROR_UNSUPPORTEDCONETXT = 262, + AX_PDU_ERROR_DUPLICATEREGISTRATION = 263, + AX_PDU_ERROR_UNKNOWNREGISTRATION = 264, + AX_PDU_ERROR_UNKNOWNAGENTCAPS = 265, + AX_PDU_ERROR_PARSEERROR = 266, + AX_PDU_ERROR_REQUESTDENIED = 267, + AX_PDU_ERROR_PROCESSINGERROR = 268 +}; + +enum ax_data_type { + AX_DATA_TYPE_INTEGER = 2, + AX_DATA_TYPE_OCTETSTRING = 4, + AX_DATA_TYPE_NULL = 5, + AX_DATA_TYPE_OID = 6, + AX_DATA_TYPE_IPADDRESS = 64, + AX_DATA_TYPE_COUNTER32 = 65, + AX_DATA_TYPE_GAUGE32 = 66, + AX_DATA_TYPE_TIMETICKS = 67, + AX_DATA_TYPE_OPAQUE = 68, + AX_DATA_TYPE_COUNTER64 = 70, + AX_DATA_TYPE_NOSUCHOBJECT = 128, + AX_DATA_TYPE_NOSUCHINSTANCE = 129, + AX_DATA_TYPE_ENDOFMIBVIEW = 130 +}; + +enum ax_close_reason { + AX_CLOSE_OTHER = 1, + AX_CLOSEN_PARSEERROR = 2, + AX_CLOSE_PROTOCOLERROR = 3, + AX_CLOSE_TIMEOUTS = 4, + AX_CLOSE_SHUTDOWN = 5, + AX_CLOSE_BYMANAGER = 6 +}; + +struct ax { + int ax_fd; + enum ax_byte_order ax_byteorder; + uint8_t *ax_rbuf; + size_t ax_rblen; + size_t ax_rbsize; + uint8_t *ax_wbuf; + size_t ax_wblen; + size_t ax_wbtlen; + size_t ax_wbsize; + uint32_t *ax_packetids; + size_t ax_packetidsize; +}; + +#ifndef AX_PRIMITIVE +#define AX_PRIMITIVE + +#define AX_OID_MAX_LEN 128 + +struct ax_oid { + uint8_t aoi_include; + uint32_t aoi_id[AX_OID_MAX_LEN]; + size_t aoi_idlen; +}; + +struct ax_ostring { + unsigned char *aos_string; + uint32_t aos_slen; +}; +#endif + +struct ax_searchrange { + struct ax_oid asr_start; + struct ax_oid asr_stop; +}; + +struct ax_pdu_header { + uint8_t aph_version; + uint8_t aph_type; + uint8_t aph_flags; + uint8_t aph_reserved; + uint32_t aph_sessionid; + uint32_t aph_transactionid; + uint32_t aph_packetid; + uint32_t aph_plength; +}; + +struct ax_varbind { + enum ax_data_type avb_type; + struct ax_oid avb_oid; + union ax_data { + uint32_t avb_uint32; + uint64_t avb_uint64; + struct ax_ostring avb_ostring; + struct ax_oid avb_oid; + } avb_data; +}; + +struct ax_pdu { + struct ax_pdu_header ap_header; + struct ax_ostring ap_context; + union { + struct ax_pdu_searchrangelist { + size_t ap_nsr; + struct ax_searchrange *ap_sr; + } ap_srl; + struct ax_pdu_getbulk { + uint16_t ap_nonrep; + uint16_t ap_maxrep; + struct ax_pdu_searchrangelist ap_srl; + } ap_getbulk; + struct ax_pdu_varbindlist { + struct ax_varbind *ap_varbind; + size_t ap_nvarbind; + } ap_vbl; + struct ax_pdu_response { + uint32_t ap_uptime; + enum ax_pdu_error ap_error; + uint16_t ap_index; + struct ax_varbind *ap_varbindlist; + size_t ap_nvarbind; + } ap_response; + void *ap_raw; + } ap_payload; +}; + +struct ax *ax_new(int); +void ax_free(struct ax *); +struct ax_pdu *ax_recv(struct ax *); +ssize_t ax_send(struct ax *); +uint32_t ax_open(struct ax *, uint8_t, struct ax_oid *, + struct ax_ostring *); +uint32_t ax_close(struct ax *, uint32_t, enum ax_close_reason); +uint32_t ax_indexallocate(struct ax *, uint8_t, uint32_t, + struct ax_ostring *, struct ax_varbind *, size_t); +uint32_t ax_indexdeallocate(struct ax *, uint32_t, + struct ax_ostring *, struct ax_varbind *, size_t); +uint32_t ax_addagentcaps(struct ax *, uint32_t, struct ax_ostring *, + struct ax_oid *, struct ax_ostring *); +uint32_t ax_removeagentcaps(struct ax *, uint32_t, + struct ax_ostring *, struct ax_oid *); +uint32_t ax_register(struct ax *, uint8_t, uint32_t, + struct ax_ostring *, uint8_t, uint8_t, uint8_t, struct ax_oid *, + uint32_t); +uint32_t ax_unregister(struct ax *, uint32_t, struct ax_ostring *, + uint8_t, uint8_t, struct ax_oid *, uint32_t); +int ax_response(struct ax *, uint32_t, uint32_t, uint32_t, + struct ax_ostring *, uint32_t, uint16_t, uint16_t, + struct ax_varbind *, size_t); +void ax_pdu_free(struct ax_pdu *); +void ax_varbind_free(struct ax_varbind *); +const char *ax_error2string(enum ax_pdu_error); +const char *ax_pdutype2string(enum ax_pdu_type); +const char *ax_oid2string(struct ax_oid *); +const char *ax_oidrange2string(struct ax_oid *, uint8_t, uint32_t); +const char *ax_varbind2string(struct ax_varbind *); +const char *ax_closereason2string(enum ax_close_reason); +int ax_oid_cmp(struct ax_oid *, struct ax_oid *); +int ax_oid_add(struct ax_oid *, uint32_t); diff --git a/lib/libagentx/shlib_version b/lib/libagentx/shlib_version index 97c9f92d6b8..1edea46de91 100644 --- a/lib/libagentx/shlib_version +++ b/lib/libagentx/shlib_version @@ -1,2 +1,2 @@ -major=0 +major=1 minor=0 diff --git a/lib/libagentx/subagentx.3 b/lib/libagentx/subagentx.3 deleted file mode 100644 index d283ff198e8..00000000000 --- a/lib/libagentx/subagentx.3 +++ /dev/null @@ -1,645 +0,0 @@ -.\" $OpenBSD: subagentx.3,v 1.4 2020/10/18 06:26:17 bentley Exp $ -.\" -.\" Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: October 18 2020 $ -.Dt SUBAGENTX 3 -.Os -.Sh NAME -.Nm subagentx_log_fatal , -.Nm subagentx_log_warn , -.Nm subagentx_log_info , -.Nm subagentx_log_debug , -.Nm subagentx , -.Nm subagentx_connect , -.Nm subagentx_read , -.Nm subagentx_write , -.Nm subagentx_wantwrite , -.Nm subagentx_free , -.Nm subagentx_session , -.Nm subagentx_session_free , -.Nm subagentx_context , -.Nm subagentx_context_object_find , -.Nm subagentx_context_object_nfind , -.Nm subagentx_context_uptime , -.Nm subagentx_context_free , -.Nm subagentx_region , -.Nm subagentx_region_free , -.Nm subagentx_agentcaps , -.Nm subagentx_agentcaps_free , -.Nm subagentx_index_integer_new , -.Nm subagentx_index_integer_any , -.Nm subagentx_index_integer_value , -.Nm subagentx_index_integer_dynamic , -.Nm subagentx_index_string_dynamic , -.Nm subagentx_index_nstring_dynamic , -.Nm subagentx_index_oid_dynamic , -.Nm subagentx_index_noid_dynamic , -.Nm subagentx_index_ipaddress_dynamic , -.Nm subagentx_index_free , -.Nm subagentx_object , -.Nm subagentx_object_free , -.Nm subagentx_varbind_integer , -.Nm subagentx_varbind_string , -.Nm subagentx_varbind_nstring , -.Nm subagentx_varbind_printf , -.Nm subagentx_varbind_null , -.Nm subagentx_varbind_oid , -.Nm subagentx_varbind_object , -.Nm subagentx_varbind_index , -.Nm subagentx_varbind_ipaddress , -.Nm subagentx_varbind_counter32 , -.Nm subagentx_varbind_gauge32 , -.Nm subagentx_varbind_timeticks , -.Nm subagentx_varbind_opaque , -.Nm subagentx_varbind_counter64 , -.Nm subagentx_varbind_notfound , -.Nm subagentx_varbind_error , -.Nm subagentx_varbind_request , -.Nm subagentx_varbind_get_index_integer , -.Nm subagentx_varbind_get_index_string , -.Nm subagentx_varbind_get_index_oid , -.Nm subagentx_varbind_get_index_ipaddress , -.Nm subagentx_varbind_set_index_integer , -.Nm subagentx_varbind_set_index_string , -.Nm subagentx_varbind_set_index_nstring , -.Nm subagentx_varbind_set_index_oid , -.Nm subagentx_varbind_set_index_object , -.Nm subagentx_varbind_set_index_ipaddress -.Nd manage an interface to an agentx master -.Sh SYNOPSIS -.In subagentx.h -.Ft extern void -.Fn (*subagentx_log_fatal) "const char *fmt" ... -.Ft extern void -.Fn (*subagentx_log_warn) "const char *fmt" ... -.Ft extern void -.Fn (*subagentx_log_info) "const char *fmt" ... -.Ft extern void -.Fn (*subagentx_log_debug) "const char *fmt" ... -.Ft struct subagentx * -.Fn subagentx "void (*nofd)(struct subagentx *, void *, int)" "void *cookie" -.Ft void -.Fn subagentx_connect "struct subagentx *sa" "int fd" -.Ft void -.Fn subagentx_read "struct subagentx *sa" -.Ft void -.Fn subagentx_write "struct subagentx *sa" -.Ft extern void -.Fn (*subagentx_wantwrite) "struct subagentx *sa" "int fd" -.Ft void -.Fn subagentx_free "struct subagentx *sa" -.Ft struct subagentx_session * -.Fo subagentx_session -.Fa "struct subagentx *sa" "uint32_t oid[]" "size_t oidlen" -.Fa "const char *descr" "uint8_t timeout" -.Fc -.Ft void -.Fn subagentx_session_free "struct subagentx_session *sas" -.Ft struct subagentx_context * -.Fn subagentx_context "struct subagentx_session *sas" "const char *name" -.Ft struct subagentx_object * -.Fo subagentx_context_object_find -.Fa "struct subagentx_context *sac" "const uint32_t oid[]" "size_t oidlen" -.Fa "int active" "int instance" -.Fc -.Ft struct subagentx_object * -.Fo subagentx_context_object_nfind -.Fa "struct subagentx_context *" "const uint32_t oid[]" "size_t oidlen" -.Fa "int active" "int inclusive" -.Fc -.Ft uint32_t -.Fn subagentx_context_uptime "struct subagentx_context *sac" -.Ft void -.Fn subagentx_context_free "struct subagentx_context *sac" -.Ft struct subagentx_agentcaps * -.Fo subagentx_agentcaps -.Fa "struct subagentx_context *sac" "uint32_t oid[]" "size_t oidlen" -.Fa "const char *descr" -.Fc -.Ft void -.Fn subagentx_agentcaps_free "struct subagentx_agentcaps *saa" -.Ft struct subagentx_region * -.Fo subagentx_region -.Fa "struct subagentx_context *sac" "uint32_t oid[]" -.Fa "size_t oidlen" "uint8_t timeout" -.Fc -.Ft void -.Fn subagentx_region_free "struct subagentx_region *sar" -.Ft struct subagentx_index * -.Fo subagentx_index_integer_new -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_integer_any -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_integer_value -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fa "uint32_t value" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_integer_dynamic -.Fa "struct subagentx_region *sar" "uint32_t oid[] "size_t oidlen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_string_dynamic -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_nstring_dynamic -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fa "size_t slen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_oid_dynamic -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_noid_dynamic -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fa "size_t vlen" -.Fc -.Ft struct subagentx_index * -.Fo subagentx_index_ipaddress_dynamic -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fc -.Ft void -.Fn subagentx_index_free "struct subagentx_index *sai" -.Ft struct subagentx_object * -.Fo subagentx_object -.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen" -.Fa "struct subagentx_index *index[]" "size_t indexlen" "int implied" -.Fa "void (*getcb)(struct subagentx_varbind *)" -.Fc -.Ft void -.Fn subagentx_object_free "struct subagentx_object *sao" -.Ft void -.Fn subagentx_varbind_integer "struct subagentx_varbind *sav" "uint32_t value" -.Ft void -.Fn subagentx_varbind_string "struct subagentx_varbind *sav" "const char *value" -.Ft void -.Fo subagentx_varbind_nstring -.Fa "struct subagentx_varbind *sav" "const char *value" "size_t slen" -.Fc -.Ft void -.Fo subagentx_varbind_printf -.Fa "struct subagentx_varbind *sav" "const char *fmt" ... -.Fc -.Ft void -.Fn subagentx_varbind_null "struct subagentx_varbind *sav" -.Ft void -.Fo subagentx_varbind_oid -.Fa "struct subagentx_varbind *sav" "const uint32_t oid[]" "size_t oidlen" -.Fc -.Ft void -.Fo subagentx_varbind_object -.Fa "struct subagentx_varbind *sav" "struct subagentx_object *sao" -.Fc -.Ft void -.Fo subagentx_varbind_index -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fc -.Ft void -.Fo subagentx_varbind_ipaddress -.Fa "struct subagentx_varbind *sav" "const struct in_addr *addr" -.Fc -.Ft void -.Fn subagentx_varbind_counter32 "struct subagentx_varbind *sav" "uint32_t value" -.Ft void -.Fn subagentx_varbind_gauge32 "struct subagentx_varbind *sav" "uint32_t value" -.Ft void -.Fo subagentx_varbind_timeticks -.Fa "struct subagentx_varbind *sav" "uint32_t value" -.Fc -.Ft void -.Fo subagentx_varbind_opaque -.Fa "struct subagentx_varbind *sav" "const char *value" "size_t slen" -.Fc -.Ft void -.Fn subagentx_varbind_counter64 "struct subagentx_varbind *sav" "uint64_t value" -.Ft void -.Fn subagentx_varbind_notfound "struct subagentx_varbind *sav" -.Ft void -.Fn subagentx_varbind_error "struct subagentx_varbind *sav" -.Ft enum subagentx_request_type -.Fn subagentx_varbind_request "struct subagentx_varbind *sav" -.Ft uint32_t -.Fo subagentx_varbind_get_index_integer -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fc -.Ft const unsigned char * -.Fo subagentx_varbind_get_index_string -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" "size_t *slen" -.Fa "int *implied" -.Fc -.Ft const uint32_t * -.Fo subagentx_varbind_get_index_oid -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "size_t *oidlen" "int *implied" -.Fc -.Ft const struct in_addr * -.Fo subagentx_varbind_get_index_ipaddress -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fc -.Ft void -.Fo subagentx_varbind_set_index_integer -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "uint32_t value" -.Fc -.Ft void -.Fo subagentx_varbind_set_index_string -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "const unsigned char *value" -.Fc -.Ft void -.Fo subagentx_varbind_set_index_nstring -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "const unsigned char *value" "size_t slen" -.Fc -.Ft void -.Fo subagentx_varbind_set_index_oid -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "const uint32_t *oid" "size_t oidlen" -.Fc -.Ft void -.Fo subagentx_varbind_set_index_object -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "struct subagentx_object *sao" -.Fc -.Ft void -.Fo subagentx_varbind_set_index_ipaddress -.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" -.Fa "const struct in_addr *addr" -.Fc -.Bd -literal -enum subagentx_request_type { - SUBAGENTX_REQUEST_TYPE_GET, - SUBAGENTX_REQUEST_TYPE_GETNEXT, - SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE -}; -.Ed -.Fd #define SUBAGENTX_AGENTX_MASTER \(dq/var/agentx/master\(dq -.Fd #define SUBAGENTX_OID_MAX_LEN 128 -.Fd #define SUBAGENTX_OID_INDEX_MAX_LEN 10 -.Fd #define SUBAGENTX_OID(...) -.Fd #define SUBAGENTX_MIB2 1, 3, 6, 1, 2, 1 -.Fd #define SUBAGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1 -.Sh DESCRIPTION -The -.Nm subagentx -functions allow an application to describe their MIB layout and provide an -.Fa fd -based interface to control the internal agentx state. -.Nm subagentx -is not thread safe. -.Ss DESCRIBING THE MIB -.Nm subagentx -is a framework to abstract away the agentx protocol from the application. -For the framework to report information to the administrator, the -.Fn subagentx_log_fatal , -.Fn subagentx_log_warn , -.Fn subagentx_log_info -and -.Fn subagentx_log_debug -functions must be set. -.Pp -When -.Fa sa -is created by -.Fn subagentx -or when -.Fa sa -detects that there is no connection to the agentx master it calls out to -.Fa nofd -with itself, -.Fa cookie -and an integer -.Fa close -as arguments. -If -.Fa close -is not set -.Fn nofd -is expected to set up a new -.Fa fd -to the agentx master. -This one can usually be found at -.Dv SUBAGENTX_AGENTX_MASTER . -This -.Fa fd -can be returned to -.Fa sa -at any moment via -.Fn subagentx_connect , -but must always be done as a result of a call to -.Fn nofd . -Once -.Fn subagentx_connect -has been called the application is responsible for retrieving data when available -on -.Fa fd -by calling -.Fn subagentx_read . -If nonblocking writes are desirable the -.Fn subagentx_wantwrite -pointer can be set to an application function and will be called as soon as -there's data available to be written out. -Once -.Fa fd -is ready for write the function -.Fn subagentx_write -should be called. -.Pp -.Fa sa -can be freed via -.Fn subagentx_free . -It will close all active sessions and free all derived objects. -Once freed no new objects can be derived from the freed objects. -Once all sessions are closed it will call out to -.Fn nofd -with -.Fa close -set, indicating that the application can clean up any context related to -.Fa sa . -.Pp -On top of the -.Fa sa -connection a -.Vt subagentx_session -must be set up. -Normally there's only a single session per -.Fa sa . -The -.Fa timeout -argument specifies the maximum time in seconds the master should wait for a -reply before determining we're gone. -If set to 0 the agentx master determines the timeout. -The -.Fa oid -and -.Fa oidlen -combination identifies the subagent and will be visible through the -agentxSessionObjectID object on the agentx master. -The -.Fa descr -is a short displaystring description of the agent and will be visiable through -the agentxSessionDescr object on the agentx master. -.Pp -The -.Vt subagentx_context -is the SNMPv3 context in which the objects operate and is built on top of -subagentx_session -.Fa sas . -If the default context is requested -.Fa name -must be NULL. -.Pp -.Fn subagentx_agentcaps -registers an entry in the agentx master's sysORTable. -The -.Fa oid , -.Fa oidlen -combination should point to an AGENT-CAPABILITIES object which describes the -capabilities of the subagent. -.Fa descr -should be a textual description of the capabilities. -If no AGENT-CAPABILITIES object is defined this function can be omitted. -.Pp -A -.Vt subagentx_region -indicates a region inside the object-tree for which get- and set-requests will -be queried. -If the OID has already been claimed by another subagent it will try to claim it -on a lower priority. -The -.Fa timeout -parameter overrules its -.Vt subagentx_session -counterpart. -.Pp -For objects in a table one or more -.Ft subagentx_index -elements must be supplied. -.Fn subagentx_index_integer_new , -.Fn subagentx_index_integer_any -and -.Fn subagentx_index_integer_value -register an integer index at the agentx master. -Of these -.Fn subagentx_index_integer_new -registers a new, previously unused, index; -.Fn subagentx_index_integer_any -registers the first available index; -and -.Fn subagentx_index_integer_value -tries to register a specific value. -If the registration of an index fails an error will be logged and all objects -using it will remain disabled. -The OID where the index should be registered is documented by the MIB. -These registered indices are usually used for tables where multiple subagents -are registered. -.Pp -For dynamic indices the subagentx_index_*_dynamic functions can be used, based -on the data type of the object. -The data type should match the data type in the MIB at the -.Fa oid -object. -Indices of data type string or oid with a fixed length should be created via -.Fn subagentx_index_nstring_dynamic -and -.Fn subagentx_index_noid_dynamic -respectively. -.Pp -.Vt subagentx_object -is an object as described in the MIB. -For scalar objects -.Pq without indices -the final zero must be omitted. -For table entries a list of 1 or more indices must be added via -.Fa index -and -.Fa indexlen . -The list of indices must match the INDEX list on the ENTRY object in the MIB. -The total length of the OID, including indices, can't be more than -.Dv SUBAGENTX_OID_MAX_LEN -and indexlen can't be more than -.Dv SUBAGENTX_OID_INDEX_MAX_LEN . -If -.Fa implied -is set the final index must be of type OID or string and will omit the leading -length indicator. -This value must only be set if specified in the MIB. -.Fn getcb -will be called for each varbind in a GET, GETNEXT or GETBULK request that -matches the object. -.Ss HANDLING GET REQUESTS -A call to -.Fn getcb -must eventually result in a call to one of the following functions: -.Bl -tag -width subagentx_varbind_counter32() -.It Fn subagentx_varbind_integer -Set the return value to an uint32_t value. -.It Fn subagentx_varbind_string -A C string wrapper around -.Fn subagentx_varbind_nstring . -.It Fn subagentx_varbind_nstring -Set the return value to an octetstring. -.It Fn subagentx_varbind_printf -A printf wrapper around -.Fn subagentx_varbind_nstring . -.It Fn subagentx_varbind_null -Set the return value to null. -.It Fn subagentx_varbind_oid -Set the return value to an OID value. -.It Fn subagentx_varbind_object -An subagentx_object wrapper around -.Fn subagentx_varbind_oid . -.It Fn subagentx_varbind_index -An subagentx_index wrapper around -.Fn subagentx_varbind_oid . -.It Fn subagentx_varbind_ipaddress -Set the return value to ipaddress. -.It Fn subagentx_varbind_counter32 -Set the return value to an uint32_t of type counter32. -.It Fn subagentx_varbind_gauge32 -Set the return value to an uint32_t of type gauge32. -.It Fn subagentx_varbind_timeticks -Set the return value to an uint32_t of type timeticks. -.It Fn subagentx_varbind_opaque -Set the return value to an opaque value. -.It Fn subagentx_varbind_counter64 -Set the return value to an uint64_t of type counter64. -.It Fn subagentx_varbind_notfound -When the request is of type GET return an nosuchinstance error. -When the request is of type GETNEXT or GETBULK return an endofmibview error. -On endofmibview the next object is queried. -This function can only be called on objects that contain one or more *_dynamic -indices. -.It Fn subagentx_varbind_error -Returns a GENERR error to the client. -.El -.Pp -For objects containing *_dynamic indices the following support functions are to -be used: -.Bl -tag -width Ds -.It Fn subagentx_varbind_request -Returns whether the request is of type GET, GETNEXT or GETNEXTINCLUSIVE. -.It Fn subagentx_varbind_get_index_integer -Retrieve a single uint32_t index value. -.It Fn subagentx_varbind_get_index_string -Retrieve an octetstring index value. -.Fa slen -is the length of the string and -.Fa implied -indicates if the next value for this index should be length sorted before -alphabetically sorted. -.It Fn subagentx_varbind_get_index_oid -Retrieve an oid index value. -.Fa oidlen -is the length of the oid and -.Fa implied -indicates if the next value for this index should be length sorted before -alphabetically sorted. -.It Fn subagentx_varbind_get_index_ipaddress -Retrieve an ipaddress index value. -.It Fn subagentx_varbind_set_index_integer -Sets a single uint32_t index value. -.It Fn subagentx_varbind_set_index_string -A C string wrapper around -.Fn subagentx_varbind_set_index_nstring . -.It Fn subagentx_varbind_set_index_nstring -Set an octetstring index value. -.It Fn subagentx_varbind_set_index_oid -Set an oid index value. -.It Fn subagentx_varbind_set_index_object -A subagentx_object wrapper around -.Fn subagentx_varbind_set_index_oid . -.It Fn subagentx_varbind_set_index_ipaddress -Set an ipaddress index value. -.El -.Pp -For these functions -.Fa sai -must be part of the object the request is performed on. -The function type must also match the data type of -.Fa sai . -.Pp -Other functions that can retrieve information from the agentx context are: -.Bl -tag -width Ds -.It Fn subagentx_context_object_find -Find a subagentx_object created inside subagentx_context -.Fa sac -based on -.Fa oid -and -.Fa oidlen . -If -.Fa active -is set the object must be reachable from the agentx master, else NULL is -returned. -If -.Fa oid -can be an instance, find its parent object. -.It Fn subagentx_context_object_nfind -Find the next subagentx_object created inside subagentx_context -.Fa sac -based on -.Fa oid -and -.Fa oidlen . -If -.Fa active -is set the object must be reachable from the agentx master, else NULL is -returned. -If -.Fa inclusive -is set the object returned may also exactly match -.Fa oid . -.It Fn subagentx_context_uptime -Returns the sysuptime in seconds for -.Fa sac -in timeticks. -.El -.Sh SEE ALSO -.Xr snmp 1 , -.Xr snmpd 8 -.Sh STANDARDS -.Rs -.%A M. Daniele -.%A B. Wijnen -.%A M. Ellison, Ed. -.%A D. Francisco, Ed. -.%D January 2000 -.%R RFC 2741 -.%T Agent Extensibility (AgentX) Protocol Version 1 -.Re -.Pp -.Rs -.%A L. Heintz -.%A S. Gudur -.%A M. Ellison, Ed. -.%D January 2000 -.%R RFC 2742 -.%T Definitions of Managed Objects for Extensible SNMP Agents -.Re -.Sh HISTORY -The -.Nm subagentx -API first appeared in -.Ox 6.8 . -.Sh AUTHORS -.An Martijn van Duren Aq Mt martijn@openbsd.org diff --git a/lib/libagentx/subagentx.c b/lib/libagentx/subagentx.c deleted file mode 100644 index 4d34b28304d..00000000000 --- a/lib/libagentx/subagentx.c +++ /dev/null @@ -1,4003 +0,0 @@ -/* - * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include <netinet/in.h> - -#include <errno.h> -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <time.h> -#include <unistd.h> - -#include "subagentx_internal.h" -#include "subagentx.h" - -enum subagentx_index_type { - SAI_TYPE_NEW, - SAI_TYPE_ANY, - SAI_TYPE_VALUE, - SAI_TYPE_DYNAMIC -}; - -#define SUBAGENTX_CONTEXT_CTX(sac) (sac->sac_name_default ? NULL : \ - &(sac->sac_name)) - -struct subagentx_agentcaps { - struct subagentx_context *saa_sac; - struct agentx_oid saa_oid; - struct agentx_ostring saa_descr; - enum subagentx_cstate saa_cstate; - enum subagentx_dstate saa_dstate; - TAILQ_ENTRY(subagentx_agentcaps) saa_sac_agentcaps; -}; - -struct subagentx_region { - struct subagentx_context *sar_sac; - struct agentx_oid sar_oid; - uint8_t sar_timeout; - uint8_t sar_priority; - enum subagentx_cstate sar_cstate; - enum subagentx_dstate sar_dstate; - TAILQ_HEAD(, subagentx_index) sar_indices; - TAILQ_HEAD(, subagentx_object) sar_objects; - TAILQ_ENTRY(subagentx_region) sar_sac_regions; -}; - -struct subagentx_index { - struct subagentx_region *sai_sar; - enum subagentx_index_type sai_type; - struct agentx_varbind sai_vb; - struct subagentx_object **sai_object; - size_t sai_objectlen; - size_t sai_objectsize; - enum subagentx_cstate sai_cstate; - enum subagentx_dstate sai_dstate; - TAILQ_ENTRY(subagentx_index) sai_sar_indices; -}; - -struct subagentx_object { - struct subagentx_region *sao_sar; - struct agentx_oid sao_oid; - struct subagentx_index *sao_index[SUBAGENTX_OID_INDEX_MAX_LEN]; - size_t sao_indexlen; - int sao_implied; - uint8_t sao_timeout; - /* Prevent freeing object while in use by get and set requesets */ - uint32_t sao_lock; - void (*sao_get)(struct subagentx_varbind *); - enum subagentx_cstate sao_cstate; - enum subagentx_dstate sao_dstate; - RB_ENTRY(subagentx_object) sao_sac_objects; - TAILQ_ENTRY(subagentx_object) sao_sar_objects; -}; - -struct subagentx_varbind { - struct subagentx_get *sav_sag; - struct subagentx_object *sav_sao; - struct subagentx_varbind_index { - struct subagentx_index *sav_sai; - union agentx_data sav_idata; - uint8_t sav_idatacomplete; - } sav_index[SUBAGENTX_OID_INDEX_MAX_LEN]; - size_t sav_indexlen; - int sav_initialized; - int sav_include; - struct agentx_varbind sav_vb; - struct agentx_oid sav_start; - struct agentx_oid sav_end; - enum agentx_pdu_error sav_error; -}; - -#define SUBAGENTX_GET_CTX(sag) (sag->sag_context_default ? NULL : \ - &(sag->sag_context)) -struct subagentx_request { - uint32_t sar_packetid; - int (*sar_cb)(struct agentx_pdu *, void *); - void *sar_cookie; - RB_ENTRY(subagentx_request) sar_sa_requests; -}; - -static void subagentx_start(struct subagentx *); -static void subagentx_finalize(struct subagentx *, int); -static void subagentx_wantwritenow(struct subagentx *, int); -void (*subagentx_wantwrite)(struct subagentx *, int) = - subagentx_wantwritenow; -static void subagentx_reset(struct subagentx *); -static void subagentx_free_finalize(struct subagentx *); -static int subagentx_session_start(struct subagentx_session *); -static int subagentx_session_finalize(struct agentx_pdu *, void *); -static int subagentx_session_close(struct subagentx_session *, - enum agentx_close_reason); -static int subagentx_session_close_finalize(struct agentx_pdu *, void *); -static void subagentx_session_free_finalize(struct subagentx_session *); -static void subagentx_session_reset(struct subagentx_session *); -static void subagentx_context_start(struct subagentx_context *); -static void subagentx_context_free_finalize(struct subagentx_context *); -static void subagentx_context_reset(struct subagentx_context *); -static int subagentx_agentcaps_start(struct subagentx_agentcaps *); -static int subagentx_agentcaps_finalize(struct agentx_pdu *, void *); -static int subagentx_agentcaps_close(struct subagentx_agentcaps *); -static int subagentx_agentcaps_close_finalize(struct agentx_pdu *, void *); -static void subagentx_agentcaps_free_finalize(struct subagentx_agentcaps *); -static void subagentx_agentcaps_reset(struct subagentx_agentcaps *); -static int subagentx_region_start(struct subagentx_region *); -static int subagentx_region_finalize(struct agentx_pdu *, void *); -static int subagentx_region_close(struct subagentx_region *); -static int subagentx_region_close_finalize(struct agentx_pdu *, void *); -static void subagentx_region_free_finalize(struct subagentx_region *); -static void subagentx_region_reset(struct subagentx_region *); -static struct subagentx_index *subagentx_index(struct subagentx_region *, - struct agentx_varbind *, enum subagentx_index_type); -static int subagentx_index_start(struct subagentx_index *); -static int subagentx_index_finalize(struct agentx_pdu *, void *); -static void subagentx_index_free_finalize(struct subagentx_index *); -static void subagentx_index_reset(struct subagentx_index *); -static int subagentx_index_close(struct subagentx_index *); -static int subagentx_index_close_finalize(struct agentx_pdu *, void *); -static int subagentx_object_start(struct subagentx_object *); -static int subagentx_object_finalize(struct agentx_pdu *, void *); -static int subagentx_object_lock(struct subagentx_object *); -static void subagentx_object_unlock(struct subagentx_object *); -static int subagentx_object_close(struct subagentx_object *); -static int subagentx_object_close_finalize(struct agentx_pdu *, void *); -static void subagentx_object_free_finalize(struct subagentx_object *); -static void subagentx_object_reset(struct subagentx_object *); -static int subagentx_object_cmp(struct subagentx_object *, - struct subagentx_object *); -static void subagentx_get_start(struct subagentx_context *, - struct agentx_pdu *); -static void subagentx_get_finalize(struct subagentx_get *); -static void subagentx_get_free(struct subagentx_get *); -static void subagentx_varbind_start(struct subagentx_varbind *); -static void subagentx_varbind_finalize(struct subagentx_varbind *); -static void subagentx_varbind_nosuchobject(struct subagentx_varbind *); -static void subagentx_varbind_nosuchinstance(struct subagentx_varbind *); -static void subagentx_varbind_endofmibview(struct subagentx_varbind *); -static void subagentx_varbind_error_type(struct subagentx_varbind *, - enum agentx_pdu_error, int); -static int subagentx_request(struct subagentx *, uint32_t, - int (*)(struct agentx_pdu *, void *), void *); -static int subagentx_request_cmp(struct subagentx_request *, - struct subagentx_request *); -static int subagentx_strcat(char **, const char *); - -RB_PROTOTYPE_STATIC(sa_requests, subagentx_request, sar_sa_requests, - subagentx_request_cmp) -RB_PROTOTYPE_STATIC(sac_objects, subagentx_object, sao_sac_objects, - subagentx_object_cmp) - -struct subagentx * -subagentx(void (*nofd)(struct subagentx *, void *, int), void *cookie) -{ - struct subagentx *sa; - - if ((sa = calloc(1, sizeof(*sa))) == NULL) - return NULL; - - sa->sa_nofd = nofd; - sa->sa_cookie = cookie; - sa->sa_fd = -1; - sa->sa_cstate = SA_CSTATE_CLOSE; - sa->sa_dstate = SA_DSTATE_OPEN; - TAILQ_INIT(&(sa->sa_sessions)); - TAILQ_INIT(&(sa->sa_getreqs)); - RB_INIT(&(sa->sa_requests)); - - subagentx_start(sa); - - return sa; -} - -/* - * subagentx_finalize is not a suitable name for a public API, - * but use it internally for consistency - */ -void -subagentx_connect(struct subagentx *sa, int fd) -{ - subagentx_finalize(sa, fd); -} - -static void -subagentx_start(struct subagentx *sa) -{ -#ifdef AGENTX_DEBUG - if (sa->sa_cstate != SA_CSTATE_CLOSE || - sa->sa_dstate != SA_DSTATE_OPEN) - subagentx_log_sa_fatalx(sa, "%s: unexpected connect", __func__); -#endif - sa->sa_cstate = SA_CSTATE_WAITOPEN; - sa->sa_nofd(sa, sa->sa_cookie, 0); -} - -static void -subagentx_finalize(struct subagentx *sa, int fd) -{ - struct subagentx_session *sas; - - if (sa->sa_cstate != SA_CSTATE_WAITOPEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sa_fatalx(sa, "%s: subagentx unexpected connect", - __func__); -#else - subagentx_log_sa_warnx(sa, - "%s: subagentx unexpected connect: ignoring", __func__); - return; -#endif - } - if ((sa->sa_ax = agentx_new(fd)) == NULL) { - subagentx_log_sa_warn(sa, "failed to initialize"); - close(fd); - subagentx_reset(sa); - return; - } - - subagentx_log_sa_info(sa, "new connection: %d", fd); - - sa->sa_fd = fd; - sa->sa_cstate = SA_CSTATE_OPEN; - - TAILQ_FOREACH(sas, &(sa->sa_sessions), sas_sa_sessions) { - if (subagentx_session_start(sas) == -1) - break; - } -} - -static void -subagentx_wantwritenow(struct subagentx *sa, int fd) -{ - subagentx_write(sa); -} - -static void -subagentx_reset(struct subagentx *sa) -{ - struct subagentx_session *sas, *tsas; - struct subagentx_request *sar; - struct subagentx_get *sag; - - agentx_free(sa->sa_ax); - sa->sa_ax = NULL; - sa->sa_fd = -1; - - sa->sa_cstate = SA_CSTATE_CLOSE; - - while ((sar = RB_MIN(sa_requests, &(sa->sa_requests))) != NULL) { - RB_REMOVE(sa_requests, &(sa->sa_requests), sar); - free(sar); - } - TAILQ_FOREACH_SAFE(sas, &(sa->sa_sessions), sas_sa_sessions, tsas) - subagentx_session_reset(sas); - while (!TAILQ_EMPTY(&(sa->sa_getreqs))) { - sag = TAILQ_FIRST(&(sa->sa_getreqs)); - sag->sag_sac = NULL; - TAILQ_REMOVE(&(sa->sa_getreqs), sag, sag_sa_getreqs); - } - - if (sa->sa_dstate == SA_DSTATE_CLOSE) { - subagentx_free_finalize(sa); - return; - } - - subagentx_start(sa); -} - -void -subagentx_free(struct subagentx *sa) -{ - struct subagentx_session *sas, *tsas; - - if (sa == NULL) - return; - - if (sa->sa_dstate == SA_DSTATE_CLOSE) { -/* Malloc throws abort on invalid pointers as well */ - subagentx_log_sa_fatalx(sa, "%s: double free", __func__); - } - sa->sa_dstate = SA_DSTATE_CLOSE; - - if (!TAILQ_EMPTY(&(sa->sa_sessions))) { - TAILQ_FOREACH_SAFE(sas, &(sa->sa_sessions), sas_sa_sessions, - tsas) { - if (sas->sas_dstate != SA_DSTATE_CLOSE) - subagentx_session_free(sas); - } - } else - subagentx_free_finalize(sa); -} - -static void -subagentx_free_finalize(struct subagentx *sa) -{ -#ifdef AGENTX_DEBUG - if (sa->sa_dstate != SA_DSTATE_CLOSE) - subagentx_log_sa_fatalx(sa, "%s: subagentx not closing", - __func__); - if (!TAILQ_EMPTY(&(sa->sa_sessions))) - subagentx_log_sa_fatalx(sa, "%s: subagentx still has sessions", - __func__); - if (!RB_EMPTY(&(sa->sa_requests))) - subagentx_log_sa_fatalx(sa, - "%s: subagentx still has pending requests", __func__); -#endif - - agentx_free(sa->sa_ax); - sa->sa_nofd(sa, sa->sa_cookie, 1); - free(sa); -} - -struct subagentx_session * -subagentx_session(struct subagentx *sa, uint32_t oid[], - size_t oidlen, const char *descr, uint8_t timeout) -{ - struct subagentx_session *sas; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sa_fatalx(sa, "%s: oidlen > %d", __func__, - SUBAGENTX_OID_MAX_LEN); -#else - errno = EINVAL; - return NULL; -#endif - } - if ((sas = calloc(1, sizeof(*sas))) == NULL) - return NULL; - - sas->sas_sa = sa; - sas->sas_timeout = timeout; - for (i = 0; i < oidlen; i++) - sas->sas_oid.aoi_id[i] = oid[i]; - sas->sas_oid.aoi_idlen = oidlen; - sas->sas_descr.aos_string = (unsigned char *)strdup(descr); - if (sas->sas_descr.aos_string == NULL) { - free(sas); - return NULL; - } - sas->sas_descr.aos_slen = strlen(descr); - sas->sas_cstate = SA_CSTATE_CLOSE; - sas->sas_dstate = SA_DSTATE_OPEN; - TAILQ_INIT(&(sas->sas_contexts)); - TAILQ_INSERT_HEAD(&(sa->sa_sessions), sas, sas_sa_sessions); - - if (sa->sa_cstate == SA_CSTATE_OPEN) - (void) subagentx_session_start(sas); - - return sas; -} - -static int -subagentx_session_start(struct subagentx_session *sas) -{ - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (sa->sa_cstate != SA_CSTATE_OPEN || - sas->sas_cstate != SA_CSTATE_CLOSE || - sas->sas_dstate != SA_DSTATE_OPEN) - subagentx_log_sa_fatalx(sa, "%s: unexpected session open", - __func__); -#endif - packetid = agentx_open(sa->sa_ax, sas->sas_timeout, &(sas->sas_oid), - &(sas->sas_descr)); - if (packetid == 0) { - subagentx_log_sa_warn(sa, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_OPEN)); - subagentx_reset(sa); - return -1; - } - sas->sas_packetid = packetid; - subagentx_log_sa_info(sa, "opening session"); - sas->sas_cstate = SA_CSTATE_WAITOPEN; - return subagentx_request(sa, packetid, subagentx_session_finalize, sas); -} - -static int -subagentx_session_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_session *sas = cookie; - struct subagentx *sa = sas->sas_sa; - struct subagentx_context *sac; - -#ifdef AGENTX_DEBUG - if (sas->sas_cstate != SA_CSTATE_WAITOPEN) - subagentx_log_sa_fatalx(sa, "%s: not expecting new session", - __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - subagentx_log_sa_warnx(sa, "failed to open session: %s", - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - subagentx_reset(sa); - return -1; - } - - sas->sas_id = pdu->ap_header.aph_sessionid; - sas->sas_cstate = SA_CSTATE_OPEN; - - if (sas->sas_dstate == SA_DSTATE_CLOSE) { - subagentx_session_close(sas, AGENTX_CLOSE_SHUTDOWN); - return 0; - } - - subagentx_log_sas_info(sas, "open"); - - TAILQ_FOREACH(sac, &(sas->sas_contexts), sac_sas_contexts) - subagentx_context_start(sac); - return 0; -} - -static int -subagentx_session_close(struct subagentx_session *sas, - enum agentx_close_reason reason) -{ - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (sas->sas_cstate != SA_CSTATE_OPEN) - subagentx_log_sa_fatalx(sa, "%s: unexpected session close", - __func__); -#endif - if ((packetid = agentx_close(sa->sa_ax, sas->sas_id, reason)) == 0) { - subagentx_log_sas_warn(sas, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_CLOSE)); - subagentx_reset(sa); - return -1; - } - - subagentx_log_sas_info(sas, "closing session: %s", - agentx_closereason2string(reason)); - - sas->sas_cstate = SA_CSTATE_WAITCLOSE; - return subagentx_request(sa, packetid, subagentx_session_close_finalize, - sas); -} - -static int -subagentx_session_close_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_session *sas = cookie; - struct subagentx *sa = sas->sas_sa; - struct subagentx_context *sac, *tsac; - -#ifdef AGENTX_DEBUG - if (sas->sas_cstate != SA_CSTATE_WAITCLOSE) - subagentx_log_sas_fatalx(sas, "%s: not expecting session close", - __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - subagentx_log_sas_warnx(sas, "failed to close session: %s", - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - subagentx_reset(sa); - return -1; - } - - sas->sas_cstate = SA_CSTATE_CLOSE; - - subagentx_log_sas_info(sas, "closed"); - - TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) - subagentx_context_reset(sac); - - if (sas->sas_dstate == SA_DSTATE_CLOSE) - subagentx_session_free_finalize(sas); - else { - if (sa->sa_cstate == SA_CSTATE_OPEN) - if (subagentx_session_start(sas) == -1) - return -1; - } - return 0; -} - -void -subagentx_session_free(struct subagentx_session *sas) -{ - struct subagentx_context *sac, *tsac; - - if (sas == NULL) - return; - - if (sas->sas_dstate == SA_DSTATE_CLOSE) - subagentx_log_sas_fatalx(sas, "%s: double free", __func__); - - sas->sas_dstate = SA_DSTATE_CLOSE; - - if (sas->sas_cstate == SA_CSTATE_OPEN) - (void) subagentx_session_close(sas, AGENTX_CLOSE_SHUTDOWN); - - TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) { - if (sac->sac_dstate != SA_DSTATE_CLOSE) - subagentx_context_free(sac); - } - - if (sas->sas_cstate == SA_CSTATE_CLOSE) - subagentx_session_free_finalize(sas); -} - -static void -subagentx_session_free_finalize(struct subagentx_session *sas) -{ - struct subagentx *sa = sas->sas_sa; - -#ifdef AGENTX_DEBUG - if (sas->sas_cstate != SA_CSTATE_CLOSE) - subagentx_log_sas_fatalx(sas, "%s: free without closing", - __func__); - if (!TAILQ_EMPTY(&(sas->sas_contexts))) - subagentx_log_sas_fatalx(sas, - "%s: subagentx still has contexts", __func__); -#endif - - TAILQ_REMOVE(&(sa->sa_sessions), sas, sas_sa_sessions); - free(sas->sas_descr.aos_string); - free(sas); - - if (TAILQ_EMPTY(&(sa->sa_sessions)) && sa->sa_dstate == SA_DSTATE_CLOSE) - subagentx_free_finalize(sa); -} - -static void -subagentx_session_reset(struct subagentx_session *sas) -{ - struct subagentx_context *sac, *tsac; - - sas->sas_cstate = SA_CSTATE_CLOSE; - - TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) - subagentx_context_reset(sac); - - if (sas->sas_dstate == SA_DSTATE_CLOSE) - subagentx_session_free_finalize(sas); -} - -struct subagentx_context * -subagentx_context(struct subagentx_session *sas, const char *name) -{ - struct subagentx_context *sac; - - if (sas->sas_dstate == SA_DSTATE_CLOSE) - subagentx_log_sas_fatalx(sas, "%s: use after free", __func__); - - if ((sac = calloc(1, sizeof(*sac))) == NULL) - return NULL; - - sac->sac_sas = sas; - sac->sac_name_default = (name == NULL); - if (name != NULL) { - sac->sac_name.aos_string = (unsigned char *)strdup(name); - if (sac->sac_name.aos_string == NULL) { - free(sac); - return NULL; - } - sac->sac_name.aos_slen = strlen(name); - } - sac->sac_cstate = sas->sas_cstate == SA_CSTATE_OPEN ? - SA_CSTATE_OPEN : SA_CSTATE_CLOSE; - sac->sac_dstate = SA_DSTATE_OPEN; - TAILQ_INIT(&(sac->sac_agentcaps)); - TAILQ_INIT(&(sac->sac_regions)); - - TAILQ_INSERT_HEAD(&(sas->sas_contexts), sac, sac_sas_contexts); - - return sac; -} - -static void -subagentx_context_start(struct subagentx_context *sac) -{ - struct subagentx_agentcaps *saa; - struct subagentx_region *sar; - -#ifdef AGENTX_DEBUG - if (sac->sac_cstate != SA_CSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected context start", - __func__); -#endif - sac->sac_cstate = SA_CSTATE_OPEN; - - TAILQ_FOREACH(saa, &(sac->sac_agentcaps), saa_sac_agentcaps) { - if (subagentx_agentcaps_start(saa) == -1) - return; - } - TAILQ_FOREACH(sar, &(sac->sac_regions), sar_sac_regions) { - if (subagentx_region_start(sar) == -1) - return; - } -} - -uint32_t -subagentx_context_uptime(struct subagentx_context *sac) -{ - struct timespec cur, res; - - if (sac->sac_sysuptimespec.tv_sec == 0 && - sac->sac_sysuptimespec.tv_nsec == 0) - return 0; - - (void) clock_gettime(CLOCK_MONOTONIC, &cur); - - timespecsub(&cur, &(sac->sac_sysuptimespec), &res); - - return sac->sac_sysuptime + - (uint32_t) ((res.tv_sec * 100) + (res.tv_nsec / 10000000)); -} - -struct subagentx_object * -subagentx_context_object_find(struct subagentx_context *sac, - const uint32_t oid[], size_t oidlen, int active, int instance) -{ - struct subagentx_object *sao, sao_search; - size_t i; - - for (i = 0; i < oidlen; i++) - sao_search.sao_oid.aoi_id[i] = oid[i]; - sao_search.sao_oid.aoi_idlen = oidlen; - - sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search); - while (sao == NULL && !instance && sao_search.sao_oid.aoi_idlen > 0) { - sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search); - sao_search.sao_oid.aoi_idlen--; - } - if (active && sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN) - return NULL; - return sao; -} - -struct subagentx_object * -subagentx_context_object_nfind(struct subagentx_context *sac, - const uint32_t oid[], size_t oidlen, int active, int inclusive) -{ - struct subagentx_object *sao, sao_search; - size_t i; - - for (i = 0; i < oidlen; i++) - sao_search.sao_oid.aoi_id[i] = oid[i]; - sao_search.sao_oid.aoi_idlen = oidlen; - - sao = RB_NFIND(sac_objects, &(sac->sac_objects), &sao_search); - if (!inclusive && sao != NULL && - agentx_oid_cmp(&(sao_search.sao_oid), &(sao->sao_oid)) <= 0) { - sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao); - } - - while (active && sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN) - sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao); - return sao; -} - -void -subagentx_context_free(struct subagentx_context *sac) -{ - struct subagentx_agentcaps *saa, *tsaa; - struct subagentx_region *sar, *tsar; - - if (sac == NULL) - return; - -#ifdef AGENTX_DEBUG - if (sac->sac_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: double free", __func__); -#endif - sac->sac_dstate = SA_DSTATE_CLOSE; - - TAILQ_FOREACH_SAFE(saa, &(sac->sac_agentcaps), saa_sac_agentcaps, - tsaa) { - if (saa->saa_dstate != SA_DSTATE_CLOSE) - subagentx_agentcaps_free(saa); - } - TAILQ_FOREACH_SAFE(sar, &(sac->sac_regions), sar_sac_regions, tsar) { - if (sar->sar_dstate != SA_DSTATE_CLOSE) - subagentx_region_free(sar); - } -} - -static void -subagentx_context_free_finalize(struct subagentx_context *sac) -{ - struct subagentx_session *sas = sac->sac_sas; - -#ifdef AGENTX_DEBUG - if (sac->sac_dstate != SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected context free", - __func__); -#endif - if (!TAILQ_EMPTY(&(sac->sac_regions)) || - !TAILQ_EMPTY(&(sac->sac_agentcaps))) - return; - TAILQ_REMOVE(&(sas->sas_contexts), sac, sac_sas_contexts); - free(sac->sac_name.aos_string); - free(sac); -} - -static void -subagentx_context_reset(struct subagentx_context *sac) -{ - struct subagentx_agentcaps *saa, *tsaa; - struct subagentx_region *sar, *tsar; - - sac->sac_cstate = SA_CSTATE_CLOSE; - sac->sac_sysuptimespec.tv_sec = 0; - sac->sac_sysuptimespec.tv_nsec = 0; - - TAILQ_FOREACH_SAFE(saa, &(sac->sac_agentcaps), saa_sac_agentcaps, tsaa) - subagentx_agentcaps_reset(saa); - TAILQ_FOREACH_SAFE(sar, &(sac->sac_regions), sar_sac_regions, tsar) - subagentx_region_reset(sar); - - if (sac->sac_dstate == SA_DSTATE_CLOSE) - subagentx_context_free_finalize(sac); -} - -struct subagentx_agentcaps * -subagentx_agentcaps(struct subagentx_context *sac, uint32_t oid[], - size_t oidlen, const char *descr) -{ - struct subagentx_agentcaps *saa; - size_t i; - - if (sac->sac_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: use after free", __func__); - - if ((saa = calloc(1, sizeof(*saa))) == NULL) - return NULL; - - saa->saa_sac = sac; - for (i = 0; i < oidlen; i++) - saa->saa_oid.aoi_id[i] = oid[i]; - saa->saa_oid.aoi_idlen = oidlen; - saa->saa_descr.aos_string = (unsigned char *)strdup(descr); - if (saa->saa_descr.aos_string == NULL) { - free(saa); - return NULL; - } - saa->saa_descr.aos_slen = strlen(descr); - saa->saa_cstate = SA_CSTATE_CLOSE; - saa->saa_dstate = SA_DSTATE_OPEN; - - TAILQ_INSERT_TAIL(&(sac->sac_agentcaps), saa, saa_sac_agentcaps); - - if (sac->sac_cstate == SA_CSTATE_OPEN) - subagentx_agentcaps_start(saa); - - return saa; -} - -static int -subagentx_agentcaps_start(struct subagentx_agentcaps *saa) -{ - struct subagentx_context *sac = saa->saa_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (sac->sac_cstate != SA_CSTATE_OPEN || - saa->saa_cstate != SA_CSTATE_CLOSE || - saa->saa_dstate != SA_DSTATE_OPEN) - subagentx_log_sac_fatalx(sac, - "%s: unexpected region registration", __func__); -#endif - - packetid = agentx_addagentcaps(sa->sa_ax, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), &(saa->saa_oid), &(saa->saa_descr)); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_ADDAGENTCAPS)); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "agentcaps %s: opening", - agentx_oid2string(&(saa->saa_oid))); - saa->saa_cstate = SA_CSTATE_WAITOPEN; - return subagentx_request(sa, packetid, subagentx_agentcaps_finalize, - saa); -} - -static int -subagentx_agentcaps_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_agentcaps *saa = cookie; - struct subagentx_context *sac = saa->saa_sac; - -#ifdef AGENTX_DEBUG - if (saa->saa_cstate != SA_CSTATE_WAITOPEN) - subagentx_log_sac_fatalx(sac, - "%s: not expecting agentcaps open", __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - /* Agentcaps failing is nothing too serious */ - subagentx_log_sac_warn(sac, "agentcaps %s: %s", - agentx_oid2string(&(saa->saa_oid)), - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - saa->saa_cstate = SA_CSTATE_CLOSE; - return 0; - } - - saa->saa_cstate = SA_CSTATE_OPEN; - - subagentx_log_sac_info(sac, "agentcaps %s: open", - agentx_oid2string(&(saa->saa_oid))); - - if (saa->saa_dstate == SA_DSTATE_CLOSE) - subagentx_agentcaps_close(saa); - - return 0; -} - -static int -subagentx_agentcaps_close(struct subagentx_agentcaps *saa) -{ - struct subagentx_context *sac = saa->saa_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (saa->saa_cstate != SA_CSTATE_OPEN) - subagentx_log_sac_fatalx(sac, "%s: unexpected agentcaps close", - __func__); -#endif - - saa->saa_cstate = SA_CSTATE_WAITCLOSE; - if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) - return 0; - - packetid = agentx_removeagentcaps(sa->sa_ax, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), &(saa->saa_oid)); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_REMOVEAGENTCAPS)); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "agentcaps %s: closing", - agentx_oid2string(&(saa->saa_oid))); - return subagentx_request(sa, packetid, - subagentx_agentcaps_close_finalize, saa); -} - -static int -subagentx_agentcaps_close_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_agentcaps *saa = cookie; - struct subagentx_context *sac = saa->saa_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - -#ifdef AGENTX_DEBUG - if (saa->saa_cstate != SA_CSTATE_WAITCLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected agentcaps close", - __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - subagentx_log_sac_warnx(sac, "agentcaps %s: %s", - agentx_oid2string(&(saa->saa_oid)), - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - subagentx_reset(sa); - return -1; - } - - saa->saa_cstate = SA_CSTATE_CLOSE; - - subagentx_log_sac_info(sac, "agentcaps %s: closed", - agentx_oid2string(&(saa->saa_oid))); - - if (saa->saa_dstate == SA_DSTATE_CLOSE) { - subagentx_agentcaps_free_finalize(saa); - return 0; - } else { - if (sac->sac_cstate == SA_CSTATE_OPEN) { - if (subagentx_agentcaps_start(saa) == -1) - return -1; - } - } - return 0; -} - -void -subagentx_agentcaps_free(struct subagentx_agentcaps *saa) -{ - if (saa == NULL) - return; - - if (saa->saa_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(saa->saa_sac, "%s: double free", - __func__); - - saa->saa_dstate = SA_DSTATE_CLOSE; - - if (saa->saa_cstate == SA_CSTATE_OPEN) { - if (subagentx_agentcaps_close(saa) == -1) - return; - } - - if (saa->saa_cstate == SA_CSTATE_CLOSE) - subagentx_agentcaps_free_finalize(saa); -} - -static void -subagentx_agentcaps_free_finalize(struct subagentx_agentcaps *saa) -{ - struct subagentx_context *sac = saa->saa_sac; - -#ifdef AGENTX_DEBUG - if (saa->saa_dstate != SA_DSTATE_CLOSE || - saa->saa_cstate != SA_CSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected free", __func__); -#endif - - TAILQ_REMOVE(&(sac->sac_agentcaps), saa, saa_sac_agentcaps); - free(saa->saa_descr.aos_string); - free(saa); - - if (sac->sac_dstate == SA_DSTATE_CLOSE) - subagentx_context_free_finalize(sac); -} - -static void -subagentx_agentcaps_reset(struct subagentx_agentcaps *saa) -{ - saa->saa_cstate = SA_CSTATE_CLOSE; - - if (saa->saa_dstate == SA_DSTATE_CLOSE) - subagentx_agentcaps_free_finalize(saa); -} - -struct subagentx_region * -subagentx_region(struct subagentx_context *sac, uint32_t oid[], - size_t oidlen, uint8_t timeout) -{ - struct subagentx_region *sar; - struct agentx_oid tmpoid; - size_t i; - - if (sac->sac_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: use after free", __func__); - if (oidlen < 1) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sac, "%s: oidlen == 0", __func__); -#else - errno = EINVAL; - return NULL; -#endif - } - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sac, "%s: oidlen > %d", __func__, - SUBAGENTX_OID_MAX_LEN); -#else - errno = EINVAL; - return NULL; -#endif - } - - for (i = 0; i < oidlen; i++) - tmpoid.aoi_id[i] = oid[i]; - tmpoid.aoi_idlen = oidlen; - TAILQ_FOREACH(sar, &(sac->sac_regions), sar_sac_regions) { - if (agentx_oid_cmp(&(sar->sar_oid), &tmpoid) == 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sac, - "%s: duplicate region registration", __func__); -#else - errno = EINVAL; - return NULL; -#endif - } - } - - if ((sar = calloc(1, sizeof(*sar))) == NULL) - return NULL; - - sar->sar_sac = sac; - sar->sar_timeout = timeout; - sar->sar_priority = AGENTX_PRIORITY_DEFAULT; - bcopy(&tmpoid, &(sar->sar_oid), sizeof(sar->sar_oid)); - sar->sar_cstate = SA_CSTATE_CLOSE; - sar->sar_dstate = SA_DSTATE_OPEN; - TAILQ_INIT(&(sar->sar_indices)); - TAILQ_INIT(&(sar->sar_objects)); - - TAILQ_INSERT_HEAD(&(sac->sac_regions), sar, sar_sac_regions); - - if (sac->sac_cstate == SA_CSTATE_OPEN) - (void) subagentx_region_start(sar); - - return sar; -} - -static int -subagentx_region_start(struct subagentx_region *sar) -{ - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (sac->sac_cstate != SA_CSTATE_OPEN || - sar->sar_cstate != SA_CSTATE_CLOSE || - sar->sar_dstate != SA_DSTATE_OPEN) - subagentx_log_sac_fatalx(sac, - "%s: unexpected region registration", __func__); -#endif - - packetid = agentx_register(sa->sa_ax, 0, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), sar->sar_timeout, sar->sar_priority, - 0, &(sar->sar_oid), 0); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_REGISTER)); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "region %s: opening", - agentx_oid2string(&(sar->sar_oid))); - sar->sar_cstate = SA_CSTATE_WAITOPEN; - return subagentx_request(sa, packetid, subagentx_region_finalize, sar); -} - -static int -subagentx_region_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_region *sar = cookie; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct subagentx_index *sai; - struct subagentx_object *sao; - -#ifdef AGENTX_DEBUG - if (sar->sar_cstate != SA_CSTATE_WAITOPEN) - subagentx_log_sac_fatalx(sac, "%s: not expecting region open", - __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error == AGENTX_PDU_ERROR_NOERROR) { - sar->sar_cstate = SA_CSTATE_OPEN; - subagentx_log_sac_info(sac, "region %s: open", - agentx_oid2string(&(sar->sar_oid))); - } else if (pdu->ap_payload.ap_response.ap_error == - AGENTX_PDU_ERROR_DUPLICATEREGISTRATION) { - sar->sar_cstate = SA_CSTATE_CLOSE; - /* Try at lower priority: first come first serve */ - if ((++sar->sar_priority) != 0) { - subagentx_log_sac_warnx(sac, "region %s: duplicate, " - "reducing priority", - agentx_oid2string(&(sar->sar_oid))); - return subagentx_region_start(sar); - } - subagentx_log_sac_info(sac, "region %s: duplicate, can't " - "reduce priority, ignoring", - agentx_oid2string(&(sar->sar_oid))); - } else if (pdu->ap_payload.ap_response.ap_error == - AGENTX_PDU_ERROR_REQUESTDENIED) { - sar->sar_cstate = SA_CSTATE_CLOSE; - subagentx_log_sac_warnx(sac, "region %s: %s", - agentx_oid2string(&(sar->sar_oid)), - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - /* - * If we can't register a region, related objects are useless. - * But no need to retry. - */ - return 0; - } else { - subagentx_log_sac_info(sac, "region %s: %s", - agentx_oid2string(&(sar->sar_oid)), - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - subagentx_reset(sa); - return -1; - } - - if (sar->sar_dstate == SA_DSTATE_CLOSE) { - if (subagentx_region_close(sar) == -1) - return -1; - } else { - TAILQ_FOREACH(sai, &(sar->sar_indices), sai_sar_indices) { - if (subagentx_index_start(sai) == -1) - return -1; - } - TAILQ_FOREACH(sao, &(sar->sar_objects), sao_sar_objects) { - if (subagentx_object_start(sao) == -1) - return -1; - } - } - return 0; -} - -static int -subagentx_region_close(struct subagentx_region *sar) -{ - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (sar->sar_cstate != SA_CSTATE_OPEN) - subagentx_log_sac_fatalx(sac, "%s: unexpected region close", - __func__); -#endif - - sar->sar_cstate = SA_CSTATE_WAITCLOSE; - if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) - return 0; - - packetid = agentx_unregister(sa->sa_ax, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), sar->sar_priority, 0, &(sar->sar_oid), - 0); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_UNREGISTER)); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "region %s: closing", - agentx_oid2string(&(sar->sar_oid))); - return subagentx_request(sa, packetid, subagentx_region_close_finalize, - sar); -} - -static int -subagentx_region_close_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_region *sar = cookie; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - -#ifdef AGENTX_DEBUG - if (sar->sar_cstate != SA_CSTATE_WAITCLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected region close", - __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - subagentx_log_sac_warnx(sac, "closing %s: %s", - agentx_oid2string(&(sar->sar_oid)), - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - subagentx_reset(sa); - return -1; - } - - sar->sar_priority = AGENTX_PRIORITY_DEFAULT; - sar->sar_cstate = SA_CSTATE_CLOSE; - - subagentx_log_sac_info(sac, "region %s: closed", - agentx_oid2string(&(sar->sar_oid))); - - if (sar->sar_dstate == SA_DSTATE_CLOSE) { - subagentx_region_free_finalize(sar); - return 0; - } else { - if (sac->sac_cstate == SA_CSTATE_OPEN) { - if (subagentx_region_start(sar) == -1) - return -1; - } - } - return 0; -} - -void -subagentx_region_free(struct subagentx_region *sar) -{ - struct subagentx_index *sai, *tsai; - struct subagentx_object *sao, *tsao; - - if (sar == NULL) - return; - - if (sar->sar_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sar->sar_sac, "%s: double free", - __func__); - - sar->sar_dstate = SA_DSTATE_CLOSE; - - TAILQ_FOREACH_SAFE(sai, &(sar->sar_indices), sai_sar_indices, tsai) { - if (sai->sai_dstate != SA_DSTATE_CLOSE) - subagentx_index_free(sai); - } - - TAILQ_FOREACH_SAFE(sao, &(sar->sar_objects), sao_sar_objects, tsao) { - if (sao->sao_dstate != SA_DSTATE_CLOSE) - subagentx_object_free(sao); - } - - if (sar->sar_cstate == SA_CSTATE_OPEN) { - if (subagentx_region_close(sar) == -1) - return; - } - - if (sar->sar_cstate == SA_CSTATE_CLOSE) - subagentx_region_free_finalize(sar); -} - -static void -subagentx_region_free_finalize(struct subagentx_region *sar) -{ - struct subagentx_context *sac = sar->sar_sac; - -#ifdef AGENTX_DEBUG - if (sar->sar_dstate != SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected free", __func__); -#endif - - if (!TAILQ_EMPTY(&(sar->sar_indices)) || - !TAILQ_EMPTY(&(sar->sar_objects))) - return; - - if (sar->sar_cstate != SA_CSTATE_CLOSE) - return; - - TAILQ_REMOVE(&(sac->sac_regions), sar, sar_sac_regions); - free(sar); - - if (sac->sac_dstate == SA_DSTATE_CLOSE) - subagentx_context_free_finalize(sac); -} - -static void -subagentx_region_reset(struct subagentx_region *sar) -{ - struct subagentx_index *sai, *tsai; - struct subagentx_object *sao, *tsao; - - sar->sar_cstate = SA_CSTATE_CLOSE; - sar->sar_priority = AGENTX_PRIORITY_DEFAULT; - - TAILQ_FOREACH_SAFE(sai, &(sar->sar_indices), sai_sar_indices, tsai) - subagentx_index_reset(sai); - TAILQ_FOREACH_SAFE(sao, &(sar->sar_objects), sao_sar_objects, tsao) - subagentx_object_reset(sao); - - if (sar->sar_dstate == SA_DSTATE_CLOSE) - subagentx_region_free_finalize(sar); -} - -struct subagentx_index * -subagentx_index_integer_new(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_INTEGER; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_uint32 = 0; - - return subagentx_index(sar, &vb, SAI_TYPE_NEW); -} - -struct subagentx_index * -subagentx_index_integer_any(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_INTEGER; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_uint32 = 0; - - return subagentx_index(sar, &vb, SAI_TYPE_ANY); -} - -struct subagentx_index * -subagentx_index_integer_value(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen, uint32_t value) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_INTEGER; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_uint32 = value; - - return subagentx_index(sar, &vb, SAI_TYPE_VALUE); -} - -struct subagentx_index * -subagentx_index_integer_dynamic(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_INTEGER; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - - return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); -} - -struct subagentx_index * -subagentx_index_string_dynamic(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_ostring.aos_slen = 0; - vb.avb_data.avb_ostring.aos_string = NULL; - - return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); -} - -struct subagentx_index * -subagentx_index_nstring_dynamic(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen, size_t vlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - if (vlen == 0 || vlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid string " - "length: %zu\n", __func__, vlen); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid string " - "length: %zu\n", __func__, vlen); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_ostring.aos_slen = vlen; - vb.avb_data.avb_ostring.aos_string = NULL; - - return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); -} - -struct subagentx_index * -subagentx_index_oid_dynamic(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_OID; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_oid.aoi_idlen = 0; - - return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); -} - -struct subagentx_index * -subagentx_index_noid_dynamic(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen, size_t vlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - if (vlen == 0 || vlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid string " - "length: %zu\n", __func__, vlen); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid string " - "length: %zu\n", __func__, vlen); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_OID; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_oid.aoi_idlen = oidlen; - vb.avb_data.avb_oid.aoi_idlen = vlen; - - return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); -} - -struct subagentx_index * -subagentx_index_ipaddress_dynamic(struct subagentx_region *sar, uint32_t oid[], - size_t oidlen) -{ - struct agentx_varbind vb; - size_t i; - - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - vb.avb_type = AGENTX_DATA_TYPE_IPADDRESS; - for (i = 0; i < oidlen; i++) - vb.avb_oid.aoi_id[i] = oid[i]; - vb.avb_data.avb_ostring.aos_string = NULL; - vb.avb_oid.aoi_idlen = oidlen; - - return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); -} - -static struct subagentx_index * -subagentx_index(struct subagentx_region *sar, struct agentx_varbind *vb, - enum subagentx_index_type type) -{ - struct subagentx_index *sai; - - if (sar->sar_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sar->sar_sac, "%s: use after free", - __func__); - if (agentx_oid_cmp(&(sar->sar_oid), &(vb->avb_oid)) != -2) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oid is not child " - "of region %s", __func__, - agentx_oid2string(&(vb->avb_oid))); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oid is not child of " - "region %s", __func__, agentx_oid2string(&(vb->avb_oid))); - errno = EINVAL; - return NULL; -#endif - } - - if ((sai = calloc(1, sizeof(*sai))) == NULL) - return NULL; - - sai->sai_sar = sar; - sai->sai_type = type; - bcopy(vb, &(sai->sai_vb), sizeof(*vb)); - sai->sai_cstate = SA_CSTATE_CLOSE; - sai->sai_dstate = SA_DSTATE_OPEN; - TAILQ_INSERT_HEAD(&(sar->sar_indices), sai, sai_sar_indices); - - if (sar->sar_cstate == SA_CSTATE_OPEN) - subagentx_index_start(sai); - - return sai; -} - -static int -subagentx_index_start(struct subagentx_index *sai) -{ - struct subagentx_region *sar = sai->sai_sar; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - int flags = 0; - -#ifdef AGENTX_DEBUG - if (sar->sar_cstate != SA_CSTATE_OPEN || - sai->sai_cstate != SA_CSTATE_CLOSE || - sai->sai_dstate != SA_DSTATE_OPEN) - subagentx_log_sac_fatalx(sac, "%s: unexpected index allocation", - __func__); -#endif - - sai->sai_cstate = SA_CSTATE_WAITOPEN; - - if (sai->sai_type == SAI_TYPE_NEW) - flags = AGENTX_PDU_FLAG_NEW_INDEX; - else if (sai->sai_type == SAI_TYPE_ANY) - flags = AGENTX_PDU_FLAG_ANY_INDEX; - else if (sai->sai_type == SAI_TYPE_DYNAMIC) { - subagentx_index_finalize(NULL, sai); - return 0; - } - - /* We might be able to bundle, but if we fail we'd have to reorganise */ - packetid = agentx_indexallocate(sa->sa_ax, flags, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), &(sai->sai_vb), 1); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_INDEXDEALLOCATE)); - subagentx_reset(sa); - return -1; - } - if (sai->sai_type == SAI_TYPE_VALUE) - subagentx_log_sac_info(sac, "index %s: allocating '%u'", - agentx_oid2string(&(sai->sai_vb.avb_oid)), - sai->sai_vb.avb_data.avb_uint32); - else if (sai->sai_type == SAI_TYPE_ANY) - subagentx_log_sac_info(sac, "index %s: allocating any index", - agentx_oid2string(&(sai->sai_vb.avb_oid))); - else if (sai->sai_type == SAI_TYPE_NEW) - subagentx_log_sac_info(sac, "index %s: allocating new index", - agentx_oid2string(&(sai->sai_vb.avb_oid))); - - return subagentx_request(sa, packetid, subagentx_index_finalize, sai); -} - -static int -subagentx_index_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_index *sai = cookie; - struct subagentx_region *sar = sai->sai_sar; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct agentx_pdu_response *resp; - size_t i; - -#ifdef AGENTX_DEBUG - if (sai->sai_cstate != SA_CSTATE_WAITOPEN) - subagentx_log_sac_fatalx(sac, - "%s: not expecting index allocate", __func__); -#endif - if (sai->sai_type == SAI_TYPE_DYNAMIC) { - sai->sai_cstate = SA_CSTATE_OPEN; - return 0; - } - - resp = &(pdu->ap_payload.ap_response); - if (resp->ap_error != AGENTX_PDU_ERROR_NOERROR) { - sai->sai_cstate = SA_CSTATE_CLOSE; - subagentx_log_sac_warnx(sac, "index %s: %s", - agentx_oid2string(&(sar->sar_oid)), - agentx_error2string(resp->ap_error)); - return 0; - } - sai->sai_cstate = SA_CSTATE_OPEN; - if (resp->ap_nvarbind != 1) { - subagentx_log_sac_warnx(sac, "index %s: unexpected number of " - "indices", agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - if (resp->ap_varbindlist[0].avb_type != sai->sai_vb.avb_type) { - subagentx_log_sac_warnx(sac, "index %s: unexpected index type", - agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - if (agentx_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), - &(sai->sai_vb.avb_oid)) != 0) { - subagentx_log_sac_warnx(sac, "index %s: unexpected oid", - agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - - switch (sai->sai_vb.avb_type) { - case AGENTX_DATA_TYPE_INTEGER: - if (sai->sai_type == SAI_TYPE_NEW || - sai->sai_type == SAI_TYPE_ANY) - sai->sai_vb.avb_data.avb_uint32 = - resp->ap_varbindlist[0].avb_data.avb_uint32; - else if (sai->sai_vb.avb_data.avb_uint32 != - resp->ap_varbindlist[0].avb_data.avb_uint32) { - subagentx_log_sac_warnx(sac, "index %s: unexpected " - "index value", agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "index %s: allocated '%u'", - agentx_oid2string(&(sai->sai_vb.avb_oid)), - sai->sai_vb.avb_data.avb_uint32); - break; - default: - subagentx_log_sac_fatalx(sac, "%s: Unsupported index type", - __func__); - } - - if (sai->sai_dstate == SA_DSTATE_CLOSE) - return subagentx_index_close(sai); - - /* TODO Make use of range_subid register */ - for (i = 0; i < sai->sai_objectlen; i++) { - if (sai->sai_object[i]->sao_dstate == SA_DSTATE_OPEN) { - if (subagentx_object_start(sai->sai_object[i]) == -1) - return -1; - } - } - return 0; -} - -void -subagentx_index_free(struct subagentx_index *sai) -{ - size_t i; - struct subagentx_object *sao; - - if (sai == NULL) - return; - - if (sai->sai_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sai->sai_sar->sar_sac, - "%s: double free", __func__); - - /* TODO Do a range_subid unregister before freeing */ - for (i = 0; i < sai->sai_objectlen; i++) { - sao = sai->sai_object[i]; - if (sao->sao_dstate != SA_DSTATE_CLOSE) { - subagentx_object_free(sao); - if (sai->sai_object[i] != sao) - i--; - } - } - - sai->sai_dstate = SA_DSTATE_CLOSE; - - if (sai->sai_cstate == SA_CSTATE_OPEN) - (void) subagentx_index_close(sai); - else if (sai->sai_cstate == SA_CSTATE_CLOSE) - subagentx_index_free_finalize(sai); -} - -static void -subagentx_index_free_finalize(struct subagentx_index *sai) -{ - struct subagentx_region *sar = sai->sai_sar; - -#ifdef AGENTX_DEBUG - if (sai->sai_dstate != SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sar->sar_sac, "%s: unexpected free", - __func__); - if (sai->sai_cstate != SA_CSTATE_CLOSE) - subagentx_log_sac_fatalx(sar->sar_sac, - "%s: free without deallocating", __func__); -#endif - - if (sai->sai_objectlen != 0) - return; - - TAILQ_REMOVE(&(sar->sar_indices), sai, sai_sar_indices); - agentx_varbind_free(&(sai->sai_vb)); - free(sai->sai_object); - free(sai); - if (sar->sar_dstate == SA_DSTATE_CLOSE) - subagentx_region_free_finalize(sar); -} - -static void -subagentx_index_reset(struct subagentx_index *sai) -{ - sai->sai_cstate = SA_CSTATE_CLOSE; - - if (sai->sai_dstate == SA_DSTATE_CLOSE) - subagentx_index_free_finalize(sai); -} - -static int -subagentx_index_close(struct subagentx_index *sai) -{ - struct subagentx_region *sar = sai->sai_sar; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - uint32_t packetid; - -#ifdef AGENTX_DEBUG - if (sai->sai_cstate != SA_CSTATE_OPEN) - subagentx_log_sac_fatalx(sac, - "%s: unexpected index deallocation", __func__); -#endif - - sai->sai_cstate = SA_CSTATE_WAITCLOSE; - if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) - return 0; - - /* We might be able to bundle, but if we fail we'd have to reorganise */ - packetid = agentx_indexdeallocate(sa->sa_ax, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), &(sai->sai_vb), 1); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_INDEXDEALLOCATE)); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "index %s: deallocating", - agentx_oid2string(&(sai->sai_vb.avb_oid))); - return subagentx_request(sa, packetid, subagentx_index_close_finalize, - sai); -} - -static int -subagentx_index_close_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_index *sai = cookie; - struct subagentx_region *sar = sai->sai_sar; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct agentx_pdu_response *resp = &(pdu->ap_payload.ap_response); - -#ifdef AGENTX_DEBUG - if (sai->sai_cstate != SA_CSTATE_WAITCLOSE) - subagentx_log_sac_fatalx(sac, "%s: unexpected indexdeallocate", - __func__); -#endif - - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - subagentx_log_sac_warnx(sac, - "index %s: couldn't deallocate: %s", - agentx_oid2string(&(sai->sai_vb.avb_oid)), - agentx_error2string(resp->ap_error)); - subagentx_reset(sa); - return -1; - } - - if (resp->ap_nvarbind != 1) { - subagentx_log_sac_warnx(sac, - "index %s: unexpected number of indices", - agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - if (resp->ap_varbindlist[0].avb_type != sai->sai_vb.avb_type) { - subagentx_log_sac_warnx(sac, "index %s: unexpected index type", - agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - if (agentx_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), - &(sai->sai_vb.avb_oid)) != 0) { - subagentx_log_sac_warnx(sac, "index %s: unexpected oid", - agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - switch (sai->sai_vb.avb_type) { - case AGENTX_DATA_TYPE_INTEGER: - if (sai->sai_vb.avb_data.avb_uint32 != - resp->ap_varbindlist[0].avb_data.avb_uint32) { - subagentx_log_sac_warnx(sac, - "index %s: unexpected index value", - agentx_oid2string(&(sar->sar_oid))); - subagentx_reset(sa); - return -1; - } - break; - default: - subagentx_log_sac_fatalx(sac, "%s: Unsupported index type", - __func__); - } - - sai->sai_cstate = SA_CSTATE_CLOSE; - - subagentx_log_sac_info(sac, "index %s: deallocated", - agentx_oid2string(&(sai->sai_vb.avb_oid))); - - if (sai->sai_dstate == SA_DSTATE_CLOSE) { - subagentx_index_free_finalize(sai); - } else if (sar->sar_cstate == SA_CSTATE_OPEN) { - if (subagentx_index_start(sai) == -1) - return -1; - } - return 0; -} - -struct subagentx_object * -subagentx_object(struct subagentx_region *sar, uint32_t oid[], size_t oidlen, - struct subagentx_index *sai[], size_t sailen, int implied, - void (*get)(struct subagentx_varbind *)) -{ - struct subagentx_object *sao, **tsao, sao_search; - struct subagentx_index *lsai; - int ready = 1; - size_t i, j; - - if (sar->sar_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sar->sar_sac, "%s: use after free", - __func__); - if (oidlen < 1) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen == 0", - __func__); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen == 0", - __func__); - errno = EINVAL; - return NULL; -#endif - } - if (oidlen > SUBAGENTX_OID_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", - __func__, SUBAGENTX_OID_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - if (sailen > SUBAGENTX_OID_INDEX_MAX_LEN) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: indexlen > %d", - __func__, SUBAGENTX_OID_INDEX_MAX_LEN); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: indexlen > %d", - __func__, SUBAGENTX_OID_INDEX_MAX_LEN); - errno = EINVAL; - return NULL; -#endif - } - - for (i = 0; i < oidlen; i++) - sao_search.sao_oid.aoi_id[i] = oid[i]; - sao_search.sao_oid.aoi_idlen = oidlen; - - do { - if (RB_FIND(sac_objects, &(sar->sar_sac->sac_objects), - &sao_search) != NULL) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid " - "parent child object relationship", __func__); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid " - "parent child object relationship", __func__); - errno = EINVAL; - return NULL; -#endif - } - sao_search.sao_oid.aoi_idlen--; - } while (sao_search.sao_oid.aoi_idlen > 0); - sao_search.sao_oid.aoi_idlen = oidlen; - sao = RB_NFIND(sac_objects, &(sar->sar_sac->sac_objects), &sao_search); - if (sao != NULL && - agentx_oid_cmp(&(sao->sao_oid), &(sao_search.sao_oid)) == 2) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid parent " - "child object relationship", __func__); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid parent " - "child object relationship", __func__); - errno = EINVAL; - return NULL; -#endif - } - if (implied == 1) { - lsai = sai[sailen - 1]; - if (lsai->sai_vb.avb_type == AGENTX_DATA_TYPE_OCTETSTRING) { - if (lsai->sai_vb.avb_data.avb_ostring.aos_slen != 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, - "%s: implied can only be used on strings " - "of dynamic length", __func__); -#else - subagentx_log_sac_warnx(sar->sar_sac, - "%s: implied can only be used on strings " - "of dynamic length", __func__); - errno = EINVAL; - return NULL; -#endif - } - } else if (lsai->sai_vb.avb_type == AGENTX_DATA_TYPE_OID) { - if (lsai->sai_vb.avb_data.avb_oid.aoi_idlen != 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, - "%s: implied can only be used on oids of " - "dynamic length", __func__); -#else - subagentx_log_sac_warnx(sar->sar_sac, - "%s: implied can only be used on oids of " - "dynamic length", __func__); - errno = EINVAL; - return NULL; -#endif - } - } else { -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sar->sar_sac, "%s: implied " - "can only be set on oid and string indices", - __func__); -#else - subagentx_log_sac_warnx(sar->sar_sac, "%s: implied can " - "only be set on oid and string indices", __func__); - errno = EINVAL; - return NULL; -#endif - } - } - - ready = sar->sar_cstate == SA_CSTATE_OPEN; - if ((sao = calloc(1, sizeof(*sao))) == NULL) - return NULL; - sao->sao_sar = sar; - bcopy(&(sao_search.sao_oid), &(sao->sao_oid), sizeof(sao->sao_oid)); - for (i = 0; i < sailen; i++) { - sao->sao_index[i] = sai[i]; - if (sai[i]->sai_objectlen == sai[i]->sai_objectsize) { - tsao = recallocarray(sai[i]->sai_object, - sai[i]->sai_objectlen, sai[i]->sai_objectlen + 1, - sizeof(*sai[i]->sai_object)); - if (tsao == NULL) { - free(sao); - return NULL; - } - sai[i]->sai_object = tsao; - sai[i]->sai_objectsize = sai[i]->sai_objectlen + 1; - } - for (j = 0; j < sai[i]->sai_objectlen; j++) { - if (agentx_oid_cmp(&(sao->sao_oid), - &(sai[i]->sai_object[j]->sao_oid)) < 0) { - memmove(&(sai[i]->sai_object[j + 1]), - &(sai[i]->sai_object[j]), - sizeof(*(sai[i]->sai_object)) * - (sai[i]->sai_objectlen - j)); - break; - } - } - sai[i]->sai_object[j] = sao; - sai[i]->sai_objectlen++; - if (sai[i]->sai_cstate != SA_CSTATE_OPEN) - ready = 0; - } - sao->sao_indexlen = sailen; - sao->sao_implied = implied; - sao->sao_timeout = 0; - sao->sao_lock = 0; - sao->sao_get = get; - sao->sao_cstate = SA_CSTATE_CLOSE; - sao->sao_dstate = SA_DSTATE_OPEN; - - TAILQ_INSERT_TAIL(&(sar->sar_objects), sao, sao_sar_objects); - RB_INSERT(sac_objects, &(sar->sar_sac->sac_objects), sao); - - if (ready) - subagentx_object_start(sao); - - return sao; -} - -static int -subagentx_object_start(struct subagentx_object *sao) -{ - struct subagentx_region *sar = sao->sao_sar; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct agentx_oid oid; - char oids[1024]; - size_t i; - int needregister = 0; - uint32_t packetid; - uint8_t flags = AGENTX_PDU_FLAG_INSTANCE_REGISTRATION; - -#ifdef AGENTX_DEBUG - if (sar->sar_cstate != SA_CSTATE_OPEN || - sao->sao_cstate != SA_CSTATE_CLOSE || - sao->sao_dstate != SA_DSTATE_OPEN) - subagentx_log_sac_fatalx(sac, - "%s: unexpected object registration", __func__); -#endif - - if (sao->sao_timeout != 0) - needregister = 1; - for (i = 0; i < sao->sao_indexlen; i++) { - if (sao->sao_index[i]->sai_cstate != SA_CSTATE_OPEN) - return 0; - if (sao->sao_index[i]->sai_type != SAI_TYPE_DYNAMIC) - needregister = 1; - } - if (!needregister) { - sao->sao_cstate = SA_CSTATE_WAITOPEN; - subagentx_object_finalize(NULL, sao); - return 0; - } - - bcopy(&(sao->sao_oid), &(oid), sizeof(oid)); - for (i = 0; i < sao->sao_indexlen; i++) { - if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { - flags = 0; - break; - } -#ifdef AGENTX_DEBUG - if (sao->sao_index[i]->sai_vb.avb_type != - AGENTX_DATA_TYPE_INTEGER) - subagentx_log_sac_fatalx(sac, - "%s: Unsupported allocated index type", __func__); -#endif - oid.aoi_id[oid.aoi_idlen++] = - sao->sao_index[i]->sai_vb.avb_data.avb_uint32; - } - packetid = agentx_register(sa->sa_ax, flags, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), sao->sao_timeout, - AGENTX_PRIORITY_DEFAULT, 0, &oid, 0); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_REGISTER)); - subagentx_reset(sa); - return -1; - } - strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); - subagentx_log_sac_info(sac, "object %s (%s %s): opening", - oids, flags ? "instance" : "region", agentx_oid2string(&(oid))); - sao->sao_cstate = SA_CSTATE_WAITOPEN; - return subagentx_request(sa, packetid, subagentx_object_finalize, sao); -} - -static int -subagentx_object_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_object *sao = cookie; - struct subagentx_context *sac = sao->sao_sar->sar_sac; - struct agentx_oid oid; - char oids[1024]; - size_t i; - uint8_t flags = 1; - -#ifdef AGENTX_DEBUG - if (sao->sao_cstate != SA_CSTATE_WAITOPEN) - subagentx_log_sac_fatalx(sac, "%s: not expecting object open", - __func__); -#endif - - if (pdu == NULL) { - sao->sao_cstate = SA_CSTATE_OPEN; - return 0; - } - - bcopy(&(sao->sao_oid), &oid, sizeof(oid)); - for (i = 0; i < sao->sao_indexlen; i++) { - if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { - flags = 0; - break; - } -#ifdef AGENTX_DEBUG - if (sao->sao_index[i]->sai_vb.avb_type != - AGENTX_DATA_TYPE_INTEGER) - subagentx_log_sac_fatalx(sac, - "%s: Unsupported allocated index type", __func__); -#endif - - oid.aoi_id[oid.aoi_idlen++] = - sao->sao_index[i]->sai_vb.avb_data.avb_uint32; - } - strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); - - /* - * We should only be here for table objects with registered indices. - * If we fail here something is misconfigured and the admin should fix - * it. - */ - if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { - sao->sao_cstate = SA_CSTATE_CLOSE; - subagentx_log_sac_info(sac, "object %s (%s %s): %s", - oids, flags ? "instance" : "region", agentx_oid2string(&oid), - agentx_error2string(pdu->ap_payload.ap_response.ap_error)); - if (sao->sao_dstate == SA_DSTATE_CLOSE) - return subagentx_object_close_finalize(NULL, sao); - return 0; - } - sao->sao_cstate = SA_CSTATE_OPEN; - subagentx_log_sac_info(sac, "object %s (%s %s): open", oids, - flags ? "instance" : "region", agentx_oid2string(&oid)); - - if (sao->sao_dstate == SA_DSTATE_CLOSE) - return subagentx_object_close(sao); - - return 0; -} - -static int -subagentx_object_lock(struct subagentx_object *sao) -{ - if (sao->sao_lock == UINT32_MAX) { - subagentx_log_sac_warnx(sao->sao_sar->sar_sac, - "%s: sao_lock == %u", __func__, UINT32_MAX); - return -1; - } - sao->sao_lock++; - return 0; -} - -static void -subagentx_object_unlock(struct subagentx_object *sao) -{ -#ifdef AGENTX_DEBUG - if (sao->sao_lock == 0) - subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, - "%s: sao_lock == 0", __func__); -#endif - sao->sao_lock--; - if (sao->sao_lock == 0 && sao->sao_dstate == SA_DSTATE_CLOSE && - sao->sao_cstate == SA_CSTATE_CLOSE) - subagentx_object_free_finalize(sao); -} - -static int -subagentx_object_close(struct subagentx_object *sao) -{ - struct subagentx_context *sac = sao->sao_sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct agentx_oid oid; - char oids[1024]; - size_t i; - int needclose = 0; - uint32_t packetid; - uint8_t flags = 1; - -#ifdef AGENTX_DEBUG - if (sao->sao_cstate != SA_CSTATE_OPEN) - subagentx_log_sac_fatalx(sac, "%s: unexpected object close", - __func__); -#endif - - for (i = 0; i < sao->sao_indexlen; i++) { -#ifdef AGENTX_DEBUG - if (sao->sao_index[i]->sai_cstate != SA_CSTATE_OPEN) - subagentx_log_sac_fatalx(sac, - "%s: Object open while index closed", __func__); -#endif - if (sao->sao_index[i]->sai_type != SAI_TYPE_DYNAMIC) - needclose = 1; - } - sao->sao_cstate = SA_CSTATE_WAITCLOSE; - if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) - return 0; - if (!needclose) { - subagentx_object_close_finalize(NULL, sao); - return 0; - } - - bcopy(&(sao->sao_oid), &(oid), sizeof(oid)); - for (i = 0; i < sao->sao_indexlen; i++) { - if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { - flags = 0; - break; - } -#ifdef AGENTX_DEBUG - if (sao->sao_index[i]->sai_vb.avb_type != - AGENTX_DATA_TYPE_INTEGER) - subagentx_log_sac_fatalx(sac, - "%s: Unsupported allocated index type", __func__); -#endif - oid.aoi_id[oid.aoi_idlen++] = - sao->sao_index[i]->sai_vb.avb_data.avb_uint32; - } - packetid = agentx_unregister(sa->sa_ax, sas->sas_id, - SUBAGENTX_CONTEXT_CTX(sac), AGENTX_PRIORITY_DEFAULT, 0, &oid, 0); - if (packetid == 0) { - subagentx_log_sac_warn(sac, "couldn't generate %s", - agentx_pdutype2string(AGENTX_PDU_TYPE_UNREGISTER)); - subagentx_reset(sa); - return -1; - } - strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); - subagentx_log_sac_info(sac, "object %s (%s %s): closing", - oids, flags ? "instance" : "region", agentx_oid2string(&(oid))); - return subagentx_request(sa, packetid, subagentx_object_close_finalize, - sao); -} - -static int -subagentx_object_close_finalize(struct agentx_pdu *pdu, void *cookie) -{ - struct subagentx_object *sao = cookie; - struct subagentx_region *sar = sao->sao_sar; - struct subagentx_context *sac = sar->sar_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct agentx_oid oid; - char oids[1024]; - uint8_t flags = 1; - size_t i; - -#ifdef AGENTX_DEBUG - if (sao->sao_cstate != SA_CSTATE_WAITCLOSE) - subagentx_log_sac_fatalx(sac, - "%s: unexpected object unregister", __func__); -#endif - - if (pdu != NULL) { - bcopy(&(sao->sao_oid), &(oid), sizeof(oid)); - for (i = 0; i < sao->sao_indexlen; i++) { - if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { - flags = 0; - break; - } -#ifdef AGENTX_DEBUG - if (sao->sao_index[i]->sai_vb.avb_type != - AGENTX_DATA_TYPE_INTEGER) - subagentx_log_sac_fatalx(sac, - "%s: Unsupported allocated index type", - __func__); -#endif - oid.aoi_id[oid.aoi_idlen++] = - sao->sao_index[i]->sai_vb.avb_data.avb_uint32; - } - strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); - if (pdu->ap_payload.ap_response.ap_error != - AGENTX_PDU_ERROR_NOERROR) { - subagentx_log_sac_warnx(sac, - "closing object %s (%s %s): %s", oids, - flags ? "instance" : "region", - agentx_oid2string(&oid), agentx_error2string( - pdu->ap_payload.ap_response.ap_error)); - subagentx_reset(sa); - return -1; - } - subagentx_log_sac_info(sac, "object %s (%s %s): closed", oids, - flags ? "instance" : "region", agentx_oid2string(&oid)); - } - - if (sao->sao_dstate == SA_DSTATE_CLOSE) - subagentx_object_free_finalize(sao); - else { - if (sar->sar_cstate == SA_CSTATE_OPEN) - if (subagentx_object_start(sao) == -1) - return -1; - } - - return 0; -} - -void -subagentx_object_free(struct subagentx_object *sao) -{ - if (sao == NULL) - return; - - if (sao->sao_dstate == SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, - "%s: double free", __func__); - - sao->sao_dstate = SA_DSTATE_CLOSE; - - if (sao->sao_cstate == SA_CSTATE_OPEN) { - if (subagentx_object_close(sao) == -1) - return; - } - if (sao->sao_cstate == SA_CSTATE_CLOSE) - subagentx_object_free_finalize(sao); -} - -static void -subagentx_object_free_finalize(struct subagentx_object *sao) -{ -#ifdef AGENTX_DEBUG - struct subagentx *sa = sao->sao_sar->sar_sac->sac_sas->sas_sa; -#endif - size_t i, j; - int found; - -#ifdef AGENTX_DEBUG - if (sao->sao_dstate != SA_DSTATE_CLOSE) - subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, - "%s: unexpected free", __func__); -#endif - - if (sao->sao_lock != 0) { -#ifdef AGENTX_DEBUG - if (TAILQ_EMPTY(&(sa->sa_getreqs))) - subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, - "%s: %s sao_lock == %u", __func__, - agentx_oid2string(&(sao->sao_oid)), sao->sao_lock); -#endif - return; - } - - RB_REMOVE(sac_objects, &(sao->sao_sar->sar_sac->sac_objects), sao); - TAILQ_REMOVE(&(sao->sao_sar->sar_objects), sao, sao_sar_objects); - - for (i = 0; i < sao->sao_indexlen; i++) { - found = 0; - for (j = 0; j < sao->sao_index[i]->sai_objectlen; j++) { - if (sao->sao_index[i]->sai_object[j] == sao) - found = 1; - if (found && j + 1 != sao->sao_index[i]->sai_objectlen) - sao->sao_index[i]->sai_object[j] = - sao->sao_index[i]->sai_object[j + 1]; - } -#ifdef AGENTX_DEBUG - if (!found) - subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, - "%s: object not found in index", __func__); -#endif - sao->sao_index[i]->sai_objectlen--; - if (sao->sao_index[i]->sai_dstate == SA_DSTATE_CLOSE && - sao->sao_index[i]->sai_cstate == SA_CSTATE_CLOSE) - subagentx_index_free_finalize(sao->sao_index[i]); - } - - free(sao); -} - -static void -subagentx_object_reset(struct subagentx_object *sao) -{ - sao->sao_cstate = SA_CSTATE_CLOSE; - - if (sao->sao_dstate == SA_DSTATE_CLOSE) - subagentx_object_free_finalize(sao); -} - -static int -subagentx_object_cmp(struct subagentx_object *o1, struct subagentx_object *o2) -{ - return agentx_oid_cmp(&(o1->sao_oid), &(o2->sao_oid)); -} - -static int -subagentx_object_implied(struct subagentx_object *sao, - struct subagentx_index *sai) -{ - size_t i = 0; - - for (i = 0; i < sao->sao_indexlen; i++) { - if (sao->sao_index[i] == sai) { - if (sai->sai_vb.avb_data.avb_ostring.aos_slen != 0) - return 1; - else if (i == sao->sao_indexlen - 1) - return sao->sao_implied; - return 0; - } - } -#ifdef AGENTX_DEBUG - subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, "%s: unsupported index", - __func__); -#endif - return 0; -} - -static void -subagentx_get_start(struct subagentx_context *sac, struct agentx_pdu *pdu) -{ - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - struct subagentx_get *sag, tsag; - struct agentx_pdu_searchrangelist *srl; - char *logmsg = NULL; - size_t i, j; - int fail = 0; - - if ((sag = calloc(1, sizeof(*sag))) == NULL) { - tsag.sag_sessionid = pdu->ap_header.aph_sessionid; - tsag.sag_transactionid = pdu->ap_header.aph_transactionid; - tsag.sag_packetid = pdu->ap_header.aph_packetid; - tsag.sag_context_default = sac->sac_name_default; - tsag.sag_fd = sac->sac_sas->sas_sa->sa_fd; - subagentx_log_sag_warn(&tsag, "Couldn't parse request"); - subagentx_reset(sa); - return; - } - - sag->sag_sessionid = pdu->ap_header.aph_sessionid; - sag->sag_transactionid = pdu->ap_header.aph_transactionid; - sag->sag_packetid = pdu->ap_header.aph_packetid; - sag->sag_context_default = sac->sac_name_default; - sag->sag_fd = sac->sac_sas->sas_sa->sa_fd; - if (!sac->sac_name_default) { - sag->sag_context.aos_string = - (unsigned char *)strdup((char *)sac->sac_name.aos_string); - if (sag->sag_context.aos_string == NULL) { - subagentx_log_sag_warn(sag, "Couldn't parse request"); - free(sag); - subagentx_reset(sa); - return; - } - } - sag->sag_context.aos_slen = sac->sac_name.aos_slen; - sag->sag_type = pdu->ap_header.aph_type; - sag->sag_sac = sac; - TAILQ_INSERT_TAIL(&(sa->sa_getreqs), sag, sag_sa_getreqs); - if (sag->sag_type == AGENTX_PDU_TYPE_GET || - sag->sag_type == AGENTX_PDU_TYPE_GETNEXT) { - srl = &(pdu->ap_payload.ap_srl); - sag->sag_nvarbind = srl->ap_nsr; - } else { - sag->sag_nonrep = pdu->ap_payload.ap_getbulk.ap_nonrep; - sag->sag_maxrep = pdu->ap_payload.ap_getbulk.ap_maxrep; - srl = &(pdu->ap_payload.ap_getbulk.ap_srl); - sag->sag_nvarbind = ((srl->ap_nsr - sag->sag_nonrep) * - sag->sag_maxrep) + sag->sag_nonrep; - } - - if ((sag->sag_varbind = calloc(sag->sag_nvarbind, - sizeof(*(sag->sag_varbind)))) == NULL) { - subagentx_log_sag_warn(sag, "Couldn't parse request"); - subagentx_get_free(sag); - subagentx_reset(sa); - return; - } - - /* XXX net-snmp doesn't use getbulk, so untested */ - /* Two loops: varbind after needs to be initialized */ - for (i = 0; i < srl->ap_nsr; i++) { - if (i < sag->sag_nonrep || - sag->sag_type != AGENTX_PDU_TYPE_GETBULK) - j = i; - else if (sag->sag_maxrep == 0) - break; - else - j = (sag->sag_maxrep * i) + sag->sag_nonrep; - bcopy(&(srl->ap_sr[i].asr_start), - &(sag->sag_varbind[j].sav_vb.avb_oid), - sizeof(srl->ap_sr[i].asr_start)); - bcopy(&(srl->ap_sr[i].asr_start), - &(sag->sag_varbind[j].sav_start), - sizeof(srl->ap_sr[i].asr_start)); - bcopy(&(srl->ap_sr[i].asr_stop), - &(sag->sag_varbind[j].sav_end), - sizeof(srl->ap_sr[i].asr_stop)); - sag->sag_varbind[j].sav_initialized = 1; - sag->sag_varbind[j].sav_sag = sag; - sag->sag_varbind[j].sav_include = - srl->ap_sr[i].asr_start.aoi_include; - if (j == 0) - fail |= subagentx_strcat(&logmsg, " {"); - else - fail |= subagentx_strcat(&logmsg, ",{"); - fail |= subagentx_strcat(&logmsg, - agentx_oid2string(&(srl->ap_sr[i].asr_start))); - if (srl->ap_sr[i].asr_start.aoi_include) - fail |= subagentx_strcat(&logmsg, " (inclusive)"); - if (srl->ap_sr[i].asr_stop.aoi_idlen != 0) { - fail |= subagentx_strcat(&logmsg, " - "); - fail |= subagentx_strcat(&logmsg, - agentx_oid2string(&(srl->ap_sr[i].asr_stop))); - } - fail |= subagentx_strcat(&logmsg, "}"); - if (fail) { - subagentx_log_sag_warn(sag, "Couldn't parse request"); - free(logmsg); - subagentx_get_free(sag); - subagentx_reset(sa); - return; - } - } - - subagentx_log_sag_debug(sag, "%s:%s", - agentx_pdutype2string(sag->sag_type), logmsg); - free(logmsg); - - for (i = 0; i < srl->ap_nsr; i++) { - if (i < sag->sag_nonrep || - sag->sag_type != AGENTX_PDU_TYPE_GETBULK) - j = i; - else if (sag->sag_maxrep == 0) - break; - else - j = (sag->sag_maxrep * i) + sag->sag_nonrep; - subagentx_varbind_start(&(sag->sag_varbind[j])); - } -} - -static void -subagentx_get_finalize(struct subagentx_get *sag) -{ - struct subagentx_context *sac = sag->sag_sac; - struct subagentx_session *sas = sac->sac_sas; - struct subagentx *sa = sas->sas_sa; - size_t i, j, nvarbind = 0; - uint16_t error = 0, index = 0; - struct agentx_varbind *vbl; - char *logmsg = NULL; - int fail = 0; - - for (i = 0; i < sag->sag_nvarbind; i++) { - if (sag->sag_varbind[i].sav_initialized) { - if (sag->sag_varbind[i].sav_vb.avb_type == 0) - return; - nvarbind++; - } - } - - if (sag->sag_sac == NULL) { - subagentx_get_free(sag); - return; - } - - if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) { - subagentx_log_sag_warn(sag, "Couldn't parse request"); - subagentx_get_free(sag); - subagentx_reset(sa); - return; - } - for (i = 0, j = 0; i < sag->sag_nvarbind; i++) { - if (sag->sag_varbind[i].sav_initialized) { - memcpy(&(vbl[j]), &(sag->sag_varbind[i].sav_vb), - sizeof(*vbl)); - if (error == 0 && sag->sag_varbind[i].sav_error != - AGENTX_PDU_ERROR_NOERROR) { - error = sag->sag_varbind[i].sav_error; - index = j + 1; - } - if (j == 0) - fail |= subagentx_strcat(&logmsg, " {"); - else - fail |= subagentx_strcat(&logmsg, ",{"); - fail |= subagentx_strcat(&logmsg, - agentx_varbind2string(&(vbl[j]))); - if (sag->sag_varbind[i].sav_error != - AGENTX_PDU_ERROR_NOERROR) { - fail |= subagentx_strcat(&logmsg, "("); - fail |= subagentx_strcat(&logmsg, - agentx_error2string( - sag->sag_varbind[i].sav_error)); - fail |= subagentx_strcat(&logmsg, ")"); - } - fail |= subagentx_strcat(&logmsg, "}"); - if (fail) { - subagentx_log_sag_warn(sag, - "Couldn't parse request"); - free(logmsg); - subagentx_get_free(sag); - return; - } - j++; - } - } - subagentx_log_sag_debug(sag, "response:%s", logmsg); - free(logmsg); - - if (agentx_response(sa->sa_ax, sas->sas_id, sag->sag_transactionid, - sag->sag_packetid, SUBAGENTX_CONTEXT_CTX(sac), 0, error, index, - vbl, nvarbind) == -1) { - subagentx_log_sag_warn(sag, "Couldn't parse request"); - subagentx_reset(sa); - } else - subagentx_wantwrite(sa, sa->sa_fd); - free(vbl); - subagentx_get_free(sag); -} - -void -subagentx_get_free(struct subagentx_get *sag) -{ - struct subagentx_varbind *sav; - struct subagentx_object *sao; - struct subagentx *sa = sag->sag_sac->sac_sas->sas_sa; - struct subagentx_varbind_index *index; - size_t i, j; - - if (sag->sag_sac != NULL) - TAILQ_REMOVE(&(sa->sa_getreqs), sag, sag_sa_getreqs); - - for (i = 0; i < sag->sag_nvarbind; i++) { - sav = &(sag->sag_varbind[i]); - for (j = 0; sav->sav_sao != NULL && - j < sav->sav_sao->sao_indexlen; j++) { - sao = sav->sav_sao; - index = &(sav->sav_index[j]); - if (sao->sao_index[j]->sai_vb.avb_type == - AGENTX_DATA_TYPE_OCTETSTRING || - sao->sao_index[j]->sai_vb.avb_type == - AGENTX_DATA_TYPE_IPADDRESS) - free(index->sav_idata.avb_ostring.aos_string); - } - agentx_varbind_free(&(sag->sag_varbind[i].sav_vb)); - } - - free(sag->sag_context.aos_string); - free(sag->sag_varbind); - free(sag); -} - -static void -subagentx_varbind_start(struct subagentx_varbind *sav) -{ - struct subagentx_get *sag = sav->sav_sag; - struct subagentx_context *sac = sag->sag_sac; - struct subagentx_object *sao, sao_search; - struct subagentx_varbind_index *index; - struct subagentx_index *sai; - struct agentx_oid *oid; - union agentx_data *data; - struct in_addr *ipaddress; - unsigned char *ipbytes; - size_t i, j, k; - int overflow = 0, dynamic; - -#ifdef AGENTX_DEBUG - if (!sav->sav_initialized) - subagentx_log_sag_fatalx(sav->sav_sag, - "%s: sav_initialized not set", __func__); -#endif - - bcopy(&(sav->sav_vb.avb_oid), &(sao_search.sao_oid), - sizeof(sao_search.sao_oid)); - - do { - sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search); - if (sao_search.sao_oid.aoi_idlen > 0) - sao_search.sao_oid.aoi_idlen--; - } while (sao == NULL && sao_search.sao_oid.aoi_idlen > 0); - if (sao == NULL || sao->sao_cstate != SA_CSTATE_OPEN) { - sav->sav_include = 1; - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { - subagentx_varbind_nosuchobject(sav); - return; - } - bcopy(&(sav->sav_vb.avb_oid), &(sao_search.sao_oid), - sizeof(sao_search.sao_oid)); - sao = RB_NFIND(sac_objects, &(sac->sac_objects), &sao_search); -getnext: - while (sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN) - sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao); - if (sao == NULL) { - subagentx_varbind_endofmibview(sav); - return; - } - bcopy(&(sao->sao_oid), &(sav->sav_vb.avb_oid), - sizeof(sao->sao_oid)); - } - sav->sav_sao = sao; - sav->sav_indexlen = sao->sao_indexlen; - if (subagentx_object_lock(sao) == -1) { - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - - oid = &(sav->sav_vb.avb_oid); - if (sao->sao_indexlen == 0) { - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { - if (oid->aoi_idlen != sao->sao_oid.aoi_idlen + 1 || - oid->aoi_id[oid->aoi_idlen - 1] != 0) { - subagentx_varbind_nosuchinstance(sav); - return; - } - } else { - if (oid->aoi_idlen == sao->sao_oid.aoi_idlen) { - oid->aoi_id[oid->aoi_idlen++] = 0; - sav->sav_include = 1; - } else { - sav->sav_sao = NULL; - subagentx_object_unlock(sao); - sao = RB_NEXT(sac_objects, &(sac->sac_objects), - sao); - goto getnext; - } - } - } - j = sao->sao_oid.aoi_idlen; -/* - * We can't trust what the client gives us, so sometimes we need to map it to - * index type. - * - AGENTX_PDU_TYPE_GET: we always return AGENTX_DATA_TYPE_NOSUCHINSTANCE - * - AGENTX_PDU_TYPE_GETNEXT: - * - Missing OID digits to match indices will result in the indices to be NUL- - * initialized and the request type will be set to - * SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE - * - An overflow can happen on AGENTX_DATA_TYPE_OCTETSTRING and - * AGENTX_DATA_TYPE_IPADDRESS. This results in request type being set to - * SUBAGENTX_REQUEST_TYPE_GETNEXT and will set the index to its maximum - * value: - * - AGENTX_DATA_TYPE_INTEGER: UINT32_MAX - * - AGENTX_DATA_TYPE_OCTETSTRING: aos_slen = UINT32_MAX and - * aos_string = NULL - * - AGENTX_DATA_TYPE_OID: aoi_idlen = UINT32_MAX and aoi_id[x] = UINT32_MAX - * - AGENTX_DATA_TYPE_IPADDRESS: 255.255.255.255 - */ - for (dynamic = 0, i = 0; i < sao->sao_indexlen; i++) { - index = &(sav->sav_index[i]); - index->sav_sai = sao->sao_index[i]; - data = &(index->sav_idata); - if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) - dynamic = 1; - if (j >= sav->sav_vb.avb_oid.aoi_idlen && !overflow && - sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) - continue; - switch (sao->sao_index[i]->sai_vb.avb_type) { - case AGENTX_DATA_TYPE_INTEGER: -/* Dynamic index: normal copy paste */ - if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { - data->avb_uint32 = overflow ? - UINT32_MAX : sav->sav_vb.avb_oid.aoi_id[j]; - j++; - index->sav_idatacomplete = 1; - break; - } - sai = sao->sao_index[i]; -/* With a GET-request we need an exact match */ - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { - if (sai->sai_vb.avb_data.avb_uint32 != - sav->sav_vb.avb_oid.aoi_id[j]) { - subagentx_varbind_nosuchinstance(sav); - return; - } - index->sav_idatacomplete = 1; - j++; - break; - } -/* A higher value automatically moves us to the next value */ - if (overflow || - sav->sav_vb.avb_oid.aoi_id[j] > - sai->sai_vb.avb_data.avb_uint32) { -/* If we're !dynamic up until now the rest of the oid doesn't matter */ - if (!dynamic) { - subagentx_varbind_endofmibview(sav); - return; - } -/* - * Else we just pick the max value and make sure we don't return - * SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE - */ - data->avb_uint32 = UINT32_MAX; - index->sav_idatacomplete = 1; - overflow = 1; - j++; - break; -/* - * A lower value automatically moves to the set value and counts as a short oid - */ - } else if (sav->sav_vb.avb_oid.aoi_id[j] < - sai->sai_vb.avb_data.avb_uint32) { - data->avb_uint32 = - sai->sai_vb.avb_data.avb_uint32; - j = sav->sav_vb.avb_oid.aoi_idlen; - break; - } -/* Normal match, except we already matched overflow at higher value */ - data->avb_uint32 = sav->sav_vb.avb_oid.aoi_id[j]; - j++; - index->sav_idatacomplete = 1; - break; - case AGENTX_DATA_TYPE_OCTETSTRING: - if (!subagentx_object_implied(sao, index->sav_sai)) { - if (overflow || sav->sav_vb.avb_oid.aoi_id[j] > - SUBAGENTX_OID_MAX_LEN - - sav->sav_vb.avb_oid.aoi_idlen) { - overflow = 1; - data->avb_ostring.aos_slen = UINT32_MAX; - index->sav_idatacomplete = 1; - continue; - } - data->avb_ostring.aos_slen = - sav->sav_vb.avb_oid.aoi_id[j++]; - } else { - if (overflow) { - data->avb_ostring.aos_slen = UINT32_MAX; - index->sav_idatacomplete = 1; - continue; - } - data->avb_ostring.aos_slen = - sav->sav_vb.avb_oid.aoi_idlen - j; - } - data->avb_ostring.aos_string = - calloc(data->avb_ostring.aos_slen + 1, 1); - if (data->avb_ostring.aos_string == NULL) { - subagentx_log_sag_warn(sag, - "Failed to bind string index"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - for (k = 0; k < data->avb_ostring.aos_slen; k++, j++) { - if (!overflow && - j == sav->sav_vb.avb_oid.aoi_idlen) - break; - - if (sav->sav_vb.avb_oid.aoi_id[j] > 255) - overflow = 1; - - data->avb_ostring.aos_string[k] = overflow ? - 0xff : sav->sav_vb.avb_oid.aoi_id[j]; - } - if (k == data->avb_ostring.aos_slen) - index->sav_idatacomplete = 1; - break; - case AGENTX_DATA_TYPE_OID: - if (!subagentx_object_implied(sao, index->sav_sai)) { - if (overflow || sav->sav_vb.avb_oid.aoi_id[j] > - SUBAGENTX_OID_MAX_LEN - - sav->sav_vb.avb_oid.aoi_idlen) { - overflow = 1; - data->avb_oid.aoi_idlen = UINT32_MAX; - index->sav_idatacomplete = 1; - continue; - } - data->avb_oid.aoi_idlen = - sav->sav_vb.avb_oid.aoi_id[j++]; - } else { - if (overflow) { - data->avb_oid.aoi_idlen = UINT32_MAX; - index->sav_idatacomplete = 1; - continue; - } - data->avb_oid.aoi_idlen = - sav->sav_vb.avb_oid.aoi_idlen - j; - } - for (k = 0; k < data->avb_oid.aoi_idlen; k++, j++) { - if (!overflow && - j == sav->sav_vb.avb_oid.aoi_idlen) { - data->avb_oid.aoi_id[k] = 0; - continue; - } - data->avb_oid.aoi_id[k] = overflow ? - UINT32_MAX : sav->sav_vb.avb_oid.aoi_id[j]; - } - if (j <= sav->sav_vb.avb_oid.aoi_idlen) - index->sav_idatacomplete = 1; - break; - case AGENTX_DATA_TYPE_IPADDRESS: - ipaddress = calloc(1, sizeof(*ipaddress)); - if (ipaddress == NULL) { - subagentx_log_sag_warn(sag, - "Failed to bind ipaddress index"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - ipbytes = (unsigned char *)ipaddress; - for (k = 0; k < 4; k++, j++) { - if (!overflow && - j == sav->sav_vb.avb_oid.aoi_idlen) - break; - - if (sav->sav_vb.avb_oid.aoi_id[j] > 255) - overflow = 1; - - ipbytes[k] = overflow ? 255 : - sav->sav_vb.avb_oid.aoi_id[j]; - } - if (j <= sav->sav_vb.avb_oid.aoi_idlen) - index->sav_idatacomplete = 1; - data->avb_ostring.aos_slen = sizeof(*ipaddress); - data->avb_ostring.aos_string = - (unsigned char *)ipaddress; - break; - default: -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sag, - "%s: unexpected index type", __func__); -#else - subagentx_log_sag_warnx(sag, - "%s: unexpected index type", __func__); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; -#endif - } - } - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { - if ((sao->sao_indexlen > 0 && - !sav->sav_index[sao->sao_indexlen - 1].sav_idatacomplete) || - j != sav->sav_vb.avb_oid.aoi_idlen || overflow) { - subagentx_varbind_nosuchinstance(sav); - return; - } - } - - if (overflow || j > sav->sav_vb.avb_oid.aoi_idlen) - sav->sav_include = 0; - -/* - * SUBAGENTX_REQUEST_TYPE_GETNEXT request can !dynamic objects can just move to - * the next object - */ - if (subagentx_varbind_request(sav) == SUBAGENTX_REQUEST_TYPE_GETNEXT && - !dynamic) { - subagentx_varbind_endofmibview(sav); - return; - } - - sao->sao_get(sav); -} - -void -subagentx_varbind_integer(struct subagentx_varbind *sav, uint32_t value) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_INTEGER; - sav->sav_vb.avb_data.avb_uint32 = value; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_string(struct subagentx_varbind *sav, const char *value) -{ - subagentx_varbind_nstring(sav, (const unsigned char *)value, - strlen(value)); -} - -void -subagentx_varbind_nstring(struct subagentx_varbind *sav, - const unsigned char *value, size_t slen) -{ - sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(slen); - if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) { - subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind string"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; - memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, value, slen); - sav->sav_vb.avb_data.avb_ostring.aos_slen = slen; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_printf(struct subagentx_varbind *sav, const char *fmt, ...) -{ - va_list ap; - int r; - - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; - va_start(ap, fmt); - r = vasprintf((char **)&(sav->sav_vb.avb_data.avb_ostring.aos_string), - fmt, ap); - va_end(ap); - if (r == -1) { - sav->sav_vb.avb_data.avb_ostring.aos_string = NULL; - subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind string"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - sav->sav_vb.avb_data.avb_ostring.aos_slen = r; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_null(struct subagentx_varbind *sav) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NULL; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_oid(struct subagentx_varbind *sav, const uint32_t oid[], - size_t oidlen) -{ - size_t i; - - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OID; - - for (i = 0; i < oidlen; i++) - sav->sav_vb.avb_data.avb_oid.aoi_id[i] = oid[i]; - sav->sav_vb.avb_data.avb_oid.aoi_idlen = oidlen; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_object(struct subagentx_varbind *sav, - struct subagentx_object *sao) -{ - subagentx_varbind_oid(sav, sao->sao_oid.aoi_id, - sao->sao_oid.aoi_idlen); -} - -void -subagentx_varbind_index(struct subagentx_varbind *sav, - struct subagentx_index *sai) -{ - subagentx_varbind_oid(sav, sai->sai_vb.avb_oid.aoi_id, - sai->sai_vb.avb_oid.aoi_idlen); -} - - -void -subagentx_varbind_ipaddress(struct subagentx_varbind *sav, - const struct in_addr *value) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_IPADDRESS; - sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(4); - if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) { - subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind ipaddress"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, value, 4); - sav->sav_vb.avb_data.avb_ostring.aos_slen = 4; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_counter32(struct subagentx_varbind *sav, uint32_t value) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_COUNTER32; - sav->sav_vb.avb_data.avb_uint32 = value; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_gauge32(struct subagentx_varbind *sav, uint32_t value) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_GAUGE32; - sav->sav_vb.avb_data.avb_uint32 = value; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_timeticks(struct subagentx_varbind *sav, uint32_t value) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_TIMETICKS; - sav->sav_vb.avb_data.avb_uint32 = value; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_opaque(struct subagentx_varbind *sav, const char *string, - size_t strlen) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OPAQUE; - sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(strlen); - if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) { - subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind opaque"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 1); - return; - } - memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, string, strlen); - sav->sav_vb.avb_data.avb_ostring.aos_slen = strlen; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_counter64(struct subagentx_varbind *sav, uint64_t value) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_COUNTER64; - sav->sav_vb.avb_data.avb_uint64 = value; - - subagentx_varbind_finalize(sav); -} - -void -subagentx_varbind_notfound(struct subagentx_varbind *sav) -{ - if (sav->sav_indexlen == 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "%s invalid call", - __func__); -#else - subagentx_log_sag_warnx(sav->sav_sag, "%s invalid call", - __func__); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 1); -#endif - } else if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) - subagentx_varbind_nosuchinstance(sav); - else - subagentx_varbind_endofmibview(sav); -} - -void -subagentx_varbind_error(struct subagentx_varbind *sav) -{ - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 1); -} - -static void -subagentx_varbind_error_type(struct subagentx_varbind *sav, - enum agentx_pdu_error error, int done) -{ - if (sav->sav_error == AGENTX_PDU_ERROR_NOERROR) { - sav->sav_error = error; - } - - if (done) { - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NULL; - - subagentx_varbind_finalize(sav); - } -} - -static void -subagentx_varbind_finalize(struct subagentx_varbind *sav) -{ - struct subagentx_get *sag = sav->sav_sag; - struct agentx_oid oid; - union agentx_data *data; - size_t i, j; - int cmp; - - if (sav->sav_error != AGENTX_PDU_ERROR_NOERROR) { - bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), - sizeof(sav->sav_start)); - goto done; - } - bcopy(&(sav->sav_sao->sao_oid), &oid, sizeof(oid)); - if (sav->sav_indexlen == 0) - agentx_oid_add(&oid, 0); - for (i = 0; i < sav->sav_indexlen; i++) { - data = &(sav->sav_index[i].sav_idata); - switch (sav->sav_index[i].sav_sai->sai_vb.avb_type) { - case AGENTX_DATA_TYPE_INTEGER: - if (agentx_oid_add(&oid, data->avb_uint32) == -1) - goto fail; - break; - case AGENTX_DATA_TYPE_OCTETSTRING: - if (!subagentx_object_implied(sav->sav_sao, - sav->sav_index[i].sav_sai)) { - if (agentx_oid_add(&oid, - data->avb_ostring.aos_slen) == -1) - goto fail; - } - for (j = 0; j < data->avb_ostring.aos_slen; j++) { - if (agentx_oid_add(&oid, - (uint8_t)data->avb_ostring.aos_string[j]) == - -1) - goto fail; - } - break; - case AGENTX_DATA_TYPE_OID: - if (!subagentx_object_implied(sav->sav_sao, - sav->sav_index[i].sav_sai)) { - if (agentx_oid_add(&oid, - data->avb_oid.aoi_idlen) == -1) - goto fail; - } - for (j = 0; j < data->avb_oid.aoi_idlen; j++) { - if (agentx_oid_add(&oid, - data->avb_oid.aoi_id[j]) == -1) - goto fail; - } - break; - case AGENTX_DATA_TYPE_IPADDRESS: - for (j = 0; j < 4; j++) { - if (agentx_oid_add(&oid, - data->avb_ostring.aos_string == NULL ? 0 : - (uint8_t)data->avb_ostring.aos_string[j]) == - -1) - goto fail; - } - break; - default: -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sag, - "%s: unsupported index type", __func__); -#else - bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), - sizeof(sav->sav_start)); - sav->sav_error = AGENTX_PDU_ERROR_PROCESSINGERROR; - subagentx_object_unlock(sav->sav_sao); - subagentx_get_finalize(sav->sav_sag); - return; -#endif - } - } - cmp = agentx_oid_cmp(&(sav->sav_vb.avb_oid), &oid); - if ((subagentx_varbind_request(sav) == SUBAGENTX_REQUEST_TYPE_GETNEXT && - cmp >= 0) || cmp > 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sag, "indices not incremented"); -#else - subagentx_log_sag_warnx(sag, "indices not incremented"); - bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), - sizeof(sav->sav_start)); - sav->sav_error = AGENTX_PDU_ERROR_GENERR; -#endif - } else - bcopy(&oid, &(sav->sav_vb.avb_oid), sizeof(oid)); -done: - subagentx_object_unlock(sav->sav_sao); - subagentx_get_finalize(sav->sav_sag); - return; - -fail: - subagentx_log_sag_warnx(sag, "oid too large"); - bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), - sizeof(sav->sav_start)); - sav->sav_error = AGENTX_PDU_ERROR_GENERR; - subagentx_object_unlock(sav->sav_sao); - subagentx_get_finalize(sav->sav_sag); -} - -static void -subagentx_varbind_nosuchobject(struct subagentx_varbind *sav) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NOSUCHOBJECT; - - if (sav->sav_sao != NULL) - subagentx_object_unlock(sav->sav_sao); - subagentx_get_finalize(sav->sav_sag); -} - -static void -subagentx_varbind_nosuchinstance(struct subagentx_varbind *sav) -{ - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NOSUCHINSTANCE; - - if (sav->sav_sao != NULL) - subagentx_object_unlock(sav->sav_sao); - subagentx_get_finalize(sav->sav_sag); -} - -static void -subagentx_varbind_endofmibview(struct subagentx_varbind *sav) -{ - struct subagentx_object *sao; - struct agentx_varbind *vb; - struct subagentx_varbind_index *index; - size_t i; - -#ifdef AGENTX_DEBUG - if (sav->sav_sag->sag_type != AGENTX_PDU_TYPE_GETNEXT && - sav->sav_sag->sag_type != AGENTX_PDU_TYPE_GETBULK) - subagentx_log_sag_fatalx(sav->sav_sag, - "%s: invalid request type", __func__); -#endif - - if (sav->sav_sao != NULL && - (sao = RB_NEXT(sac_objects, &(sac->sac_objects), - sav->sav_sao)) != NULL && - agentx_oid_cmp(&(sao->sao_oid), &(sav->sav_end)) < 0) { - bcopy(&(sao->sao_oid), &(sav->sav_vb.avb_oid), - sizeof(sao->sao_oid)); - sav->sav_include = 1; - for (i = 0; i < sav->sav_indexlen; i++) { - index = &(sav->sav_index[i]); - vb = &(index->sav_sai->sai_vb); - if (vb->avb_type == AGENTX_DATA_TYPE_OCTETSTRING || - vb->avb_type == AGENTX_DATA_TYPE_IPADDRESS) - free(index->sav_idata.avb_ostring.aos_string); - } - bzero(&(sav->sav_index), sizeof(sav->sav_index)); - subagentx_object_unlock(sav->sav_sao); - subagentx_varbind_start(sav); - return; - } - - sav->sav_vb.avb_type = AGENTX_DATA_TYPE_ENDOFMIBVIEW; - - if (sav->sav_sao != NULL) - subagentx_object_unlock(sav->sav_sao); - subagentx_get_finalize(sav->sav_sag); -} - -enum subagentx_request_type -subagentx_varbind_request(struct subagentx_varbind *sav) -{ - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) - return SUBAGENTX_REQUEST_TYPE_GET; - if (sav->sav_include || - (sav->sav_indexlen > 0 && - !sav->sav_index[sav->sav_indexlen - 1].sav_idatacomplete)) - return SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE; - return SUBAGENTX_REQUEST_TYPE_GETNEXT; -} - -struct subagentx_object * -subagentx_varbind_get_object(struct subagentx_varbind *sav) -{ - return sav->sav_sao; -} - -uint32_t -subagentx_varbind_get_index_integer(struct subagentx_varbind *sav, - struct subagentx_index *sai) -{ - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_INTEGER) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return 0; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) - return sav->sav_index[i].sav_idata.avb_uint32; - } -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return 0; -#endif -} - -const unsigned char * -subagentx_varbind_get_index_string(struct subagentx_varbind *sav, - struct subagentx_index *sai, size_t *slen, int *implied) -{ - struct subagentx_varbind_index *index; - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OCTETSTRING) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - *slen = 0; - *implied = 0; - return NULL; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - index = &(sav->sav_index[i]); - *slen = index->sav_idata.avb_ostring.aos_slen; - *implied = subagentx_object_implied(sav->sav_sao, sai); - return index->sav_idata.avb_ostring.aos_string; - } - } - -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - *slen = 0; - *implied = 0; - return NULL; -#endif -} - -const uint32_t * -subagentx_varbind_get_index_oid(struct subagentx_varbind *sav, - struct subagentx_index *sai, size_t *oidlen, int *implied) -{ - struct subagentx_varbind_index *index; - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OID) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - *oidlen = 0; - *implied = 0; - return NULL; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - index = &(sav->sav_index[i]); - *oidlen = index->sav_idata.avb_oid.aoi_idlen; - *implied = subagentx_object_implied(sav->sav_sao, sai); - return index->sav_idata.avb_oid.aoi_id; - } - } - -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - *oidlen = 0; - *implied = 0; - return NULL; -#endif -} - -const struct in_addr * -subagentx_varbind_get_index_ipaddress(struct subagentx_varbind *sav, - struct subagentx_index *sai) -{ - static struct in_addr nuladdr = {0}; - struct subagentx_varbind_index *index; - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_IPADDRESS) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return NULL; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - index = &(sav->sav_index[i]); - if (index->sav_idata.avb_ostring.aos_string == NULL) - return &nuladdr; - return (struct in_addr *) - index->sav_idata.avb_ostring.aos_string; - } - } - -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return NULL; -#endif -} - -void -subagentx_varbind_set_index_integer(struct subagentx_varbind *sav, - struct subagentx_index *sai, uint32_t value) -{ - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_INTEGER) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && - sav->sav_index[i].sav_idata.avb_uint32 != value) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, - "can't change index on GET"); -#else - subagentx_log_sag_warnx(sav->sav_sag, - "can't change index on GET"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - sav->sav_index[i].sav_idata.avb_uint32 = value; - return; - } - } -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); -#endif -} - -void -subagentx_varbind_set_index_string(struct subagentx_varbind *sav, - struct subagentx_index *sai, const char *value) -{ - subagentx_varbind_set_index_nstring(sav, sai, - (const unsigned char *)value, strlen(value)); -} - -void -subagentx_varbind_set_index_nstring(struct subagentx_varbind *sav, - struct subagentx_index *sai, const unsigned char *value, size_t slen) -{ - struct agentx_ostring *curvalue; - unsigned char *nstring; - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OCTETSTRING) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - if (sai->sai_vb.avb_data.avb_ostring.aos_slen != 0 && - sai->sai_vb.avb_data.avb_ostring.aos_slen != slen) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, - "invalid string length on explicit length " - "string"); -#else - subagentx_log_sag_warnx(sav->sav_sag, - "invalid string length on explicit length " - "string"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - curvalue = &(sav->sav_index[i].sav_idata.avb_ostring); - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && - (curvalue->aos_slen != slen || - memcmp(curvalue->aos_string, value, slen) != 0)) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, - "can't change index on GET"); -#else - subagentx_log_sag_warnx(sav->sav_sag, - "can't change index on GET"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - if ((nstring = recallocarray(curvalue->aos_string, - curvalue->aos_slen + 1, slen + 1, 1)) == NULL) { - subagentx_log_sag_warn(sav->sav_sag, - "Failed to bind string index"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 0); - return; - } - curvalue->aos_string = nstring; - memcpy(nstring, value, slen); - curvalue->aos_slen = slen; - return; - } - } -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); -#endif -} - -void -subagentx_varbind_set_index_oid(struct subagentx_varbind *sav, - struct subagentx_index *sai, const uint32_t *value, size_t oidlen) -{ - struct agentx_oid *curvalue, oid; - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OID) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - if (sai->sai_vb.avb_data.avb_oid.aoi_idlen != 0 && - sai->sai_vb.avb_data.avb_oid.aoi_idlen != oidlen) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, - "invalid oid length on explicit length " - "oid"); -#else - subagentx_log_sag_warnx(sav->sav_sag, - "invalid oid length on explicit length " - "oid"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - curvalue = &(sav->sav_index[i].sav_idata.avb_oid); - for (i = 0; i < oidlen; i++) - oid.aoi_id[i] = value[i]; - oid.aoi_idlen = oidlen; - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && - agentx_oid_cmp(&oid, curvalue) != 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, - "can't change index on GET"); -#else - subagentx_log_sag_warnx(sav->sav_sag, - "can't change index on GET"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - for (i = 0; i < oidlen; i++) - curvalue->aoi_id[i] = value[i]; - curvalue->aoi_idlen = oidlen; - return; - } - } -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); -#endif -} - -void -subagentx_varbind_set_index_object(struct subagentx_varbind *sav, - struct subagentx_index *sai, struct subagentx_object *sao) -{ - subagentx_varbind_set_index_oid(sav, sai, sao->sao_oid.aoi_id, - sao->sao_oid.aoi_idlen); -} - -void -subagentx_varbind_set_index_ipaddress(struct subagentx_varbind *sav, - struct subagentx_index *sai, const struct in_addr *addr) -{ - struct agentx_ostring *curvalue; - size_t i; - - if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_IPADDRESS) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - - for (i = 0; i < sav->sav_indexlen; i++) { - if (sav->sav_index[i].sav_sai == sai) { - curvalue = &(sav->sav_index[i].sav_idata.avb_ostring); - if (curvalue->aos_string == NULL) - curvalue->aos_string = calloc(1, sizeof(*addr)); - if (curvalue->aos_string == NULL) { - subagentx_log_sag_warn(sav->sav_sag, - "Failed to bind ipaddress index"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_PROCESSINGERROR, 0); - return; - } - if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && - memcmp(addr, curvalue->aos_string, - sizeof(*addr)) != 0) { -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, - "can't change index on GET"); -#else - subagentx_log_sag_warnx(sav->sav_sag, - "can't change index on GET"); - subagentx_varbind_error_type(sav, - AGENTX_PDU_ERROR_GENERR, 0); - return; -#endif - } - bcopy(addr, curvalue->aos_string, sizeof(*addr)); - return; - } - } -#ifdef AGENTX_DEBUG - subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); -#else - subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); - subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); -#endif -} - -static int -subagentx_request(struct subagentx *sa, uint32_t packetid, - int (*cb)(struct agentx_pdu *, void *), void *cookie) -{ - struct subagentx_request *sar; - -#ifdef AGENTX_DEBUG - if (sa->sa_ax->ax_wblen == 0) - subagentx_log_sa_fatalx(sa, "%s: no data to be written", - __func__); -#endif - - if ((sar = calloc(1, sizeof(*sar))) == NULL) { - subagentx_log_sa_warn(sa, "couldn't create request context"); - subagentx_reset(sa); - return -1; - } - - sar->sar_packetid = packetid; - sar->sar_cb = cb; - sar->sar_cookie = cookie; - if (RB_INSERT(sa_requests, &(sa->sa_requests), sar) != NULL) { -#ifdef AGENTX_DEBUG - subagentx_log_sa_fatalx(sa, "%s: duplicate packetid", __func__); -#else - subagentx_log_sa_warnx(sa, "%s: duplicate packetid", __func__); - free(sar); - subagentx_reset(sa); - return -1; -#endif - } - - subagentx_wantwrite(sa, sa->sa_fd); - return 0; -} - -static int -subagentx_request_cmp(struct subagentx_request *r1, - struct subagentx_request *r2) -{ - return r1->sar_packetid < r2->sar_packetid ? -1 : - r1->sar_packetid > r2->sar_packetid; -} - -static int -subagentx_strcat(char **dst, const char *src) -{ - char *tmp; - size_t dstlen = 0, buflen = 0, srclen, nbuflen; - - if (*dst != NULL) { - dstlen = strlen(*dst); - buflen = ((dstlen / 512) + 1) * 512; - } - - srclen = strlen(src); - if (*dst == NULL || dstlen + srclen > buflen) { - nbuflen = (((dstlen + srclen) / 512) + 1) * 512; - tmp = recallocarray(*dst, buflen, nbuflen, sizeof(*tmp)); - if (tmp == NULL) - return -1; - *dst = tmp; - buflen = nbuflen; - } - - (void)strlcat(*dst, src, buflen); - return 0; -} - -void -subagentx_read(struct subagentx *sa) -{ - struct subagentx_session *sas; - struct subagentx_context *sac; - struct subagentx_request sar_search, *sar; - struct agentx_pdu *pdu; - int error; - - if ((pdu = agentx_recv(sa->sa_ax)) == NULL) { - if (errno == EAGAIN) - return; - subagentx_log_sa_warn(sa, "lost connection"); - subagentx_reset(sa); - return; - } - - TAILQ_FOREACH(sas, &(sa->sa_sessions), sas_sa_sessions) { - if (sas->sas_id == pdu->ap_header.aph_sessionid) - break; - if (sas->sas_cstate == SA_CSTATE_WAITOPEN && - sas->sas_packetid == pdu->ap_header.aph_packetid) - break; - } - if (sas == NULL) { - subagentx_log_sa_warnx(sa, "received unexpected session: %d", - pdu->ap_header.aph_sessionid); - agentx_pdu_free(pdu); - subagentx_reset(sa); - return; - } - TAILQ_FOREACH(sac, &(sas->sas_contexts), sac_sas_contexts) { - if ((pdu->ap_header.aph_flags & - AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) == 0 && - sac->sac_name_default == 1) - break; - if (pdu->ap_header.aph_flags & - AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT && - sac->sac_name_default == 0 && - pdu->ap_context.aos_slen == sac->sac_name.aos_slen && - memcmp(pdu->ap_context.aos_string, - sac->sac_name.aos_string, sac->sac_name.aos_slen) == 0) - break; - } - if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_RESPONSE) { - if (sac == NULL) { - subagentx_log_sa_warnx(sa, "%s: invalid context", - pdu->ap_context.aos_string); - agentx_pdu_free(pdu); - subagentx_reset(sa); - return; - } - } - - switch (pdu->ap_header.aph_type) { - case AGENTX_PDU_TYPE_GET: - case AGENTX_PDU_TYPE_GETNEXT: - case AGENTX_PDU_TYPE_GETBULK: - subagentx_get_start(sac, pdu); - break; - /* Add stubs for set functions */ - case AGENTX_PDU_TYPE_TESTSET: - case AGENTX_PDU_TYPE_COMMITSET: - case AGENTX_PDU_TYPE_UNDOSET: - if (pdu->ap_header.aph_type == AGENTX_PDU_TYPE_TESTSET) - error = AGENTX_PDU_ERROR_NOTWRITABLE; - else if (pdu->ap_header.aph_type == AGENTX_PDU_TYPE_COMMITSET) - error = AGENTX_PDU_ERROR_COMMITFAILED; - else - error = AGENTX_PDU_ERROR_UNDOFAILED; - - subagentx_log_sac_debug(sac, "unsupported call: %s", - agentx_pdutype2string(pdu->ap_header.aph_type)); - if (agentx_response(sa->sa_ax, sas->sas_id, - pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid, - sac == NULL ? NULL : SUBAGENTX_CONTEXT_CTX(sac), - 0, error, 1, NULL, 0) == -1) - subagentx_log_sac_warn(sac, - "transaction: %u packetid: %u: failed to send " - "reply", pdu->ap_header.aph_transactionid, - pdu->ap_header.aph_packetid); - if (sa->sa_ax->ax_wblen > 0) - subagentx_wantwrite(sa, sa->sa_fd); - break; - case AGENTX_PDU_TYPE_CLEANUPSET: - subagentx_log_sa_debug(sa, "unsupported call: %s", - agentx_pdutype2string(pdu->ap_header.aph_type)); - break; - case AGENTX_PDU_TYPE_RESPONSE: - sar_search.sar_packetid = pdu->ap_header.aph_packetid; - sar = RB_FIND(sa_requests, &(sa->sa_requests), &sar_search); - if (sar == NULL) { - if (sac == NULL) - subagentx_log_sa_warnx(sa, "received " - "response on non-request"); - else - subagentx_log_sac_warnx(sac, "received " - "response on non-request"); - break; - } - if (sac != NULL && pdu->ap_payload.ap_response.ap_error == 0) { - sac->sac_sysuptime = - pdu->ap_payload.ap_response.ap_uptime; - (void) clock_gettime(CLOCK_MONOTONIC, - &(sac->sac_sysuptimespec)); - } - RB_REMOVE(sa_requests, &(sa->sa_requests), sar); - (void) sar->sar_cb(pdu, sar->sar_cookie); - free(sar); - break; - default: - if (sac == NULL) - subagentx_log_sa_warnx(sa, "unsupported call: %s", - agentx_pdutype2string(pdu->ap_header.aph_type)); - else - subagentx_log_sac_warnx(sac, "unsupported call: %s", - agentx_pdutype2string(pdu->ap_header.aph_type)); - subagentx_reset(sa); - break; - } - agentx_pdu_free(pdu); -} - -void -subagentx_write(struct subagentx *sa) -{ - ssize_t send; - - if ((send = agentx_send(sa->sa_ax)) == -1) { - if (errno == EAGAIN) { - subagentx_wantwrite(sa, sa->sa_fd); - return; - } - subagentx_log_sa_warn(sa, "lost connection"); - subagentx_reset(sa); - return; - } - if (send > 0) - subagentx_wantwrite(sa, sa->sa_fd); -} - -RB_GENERATE_STATIC(sa_requests, subagentx_request, sar_sa_requests, - subagentx_request_cmp) -RB_GENERATE_STATIC(sac_objects, subagentx_object, sao_sac_objects, - subagentx_object_cmp) diff --git a/lib/libagentx/subagentx.h b/lib/libagentx/subagentx.h deleted file mode 100644 index 418b86a78c1..00000000000 --- a/lib/libagentx/subagentx.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <netinet/in.h> - -#include <stdint.h> -#include <stddef.h> - -struct subagentx; -struct subagentx_session; -struct subagentx_context; -struct subagentx_agentcaps; -struct subagentx_region; -struct subagentx_index; -struct subagentx_object; -struct subagentx_varbind; - -enum subagentx_request_type { - SUBAGENTX_REQUEST_TYPE_GET, - SUBAGENTX_REQUEST_TYPE_GETNEXT, - SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE -}; - -#define SUBAGENTX_AGENTX_MASTER "/var/agentx/master" -#define SUBAGENTX_OID_MAX_LEN 128 -#define SUBAGENTX_OID_INDEX_MAX_LEN 10 -#define SUBAGENTX_MIB2 1, 3, 6, 1, 2, 1 -#define SUBAGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1 -#define SUBAGENTX_OID(...) (uint32_t []) { __VA_ARGS__ }, \ - (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) - -extern void (*subagentx_log_fatal)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -extern void (*subagentx_log_warn)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -extern void (*subagentx_log_info)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); -extern void (*subagentx_log_debug)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))); - -struct subagentx *subagentx(void (*)(struct subagentx *, void *, int), void *); -void subagentx_connect(struct subagentx *, int); -void subagentx_read(struct subagentx *); -void subagentx_write(struct subagentx *); -extern void (*subagentx_wantwrite)(struct subagentx *, int); -void subagentx_free(struct subagentx *); -struct subagentx_session *subagentx_session(struct subagentx *, - uint32_t[], size_t, const char *, uint8_t); -void subagentx_session_free(struct subagentx_session *); -struct subagentx_context *subagentx_context(struct subagentx_session *, - const char *); -struct subagentx_object *subagentx_context_object_find( - struct subagentx_context *, const uint32_t[], size_t, int, int); -struct subagentx_object *subagentx_context_object_nfind( - struct subagentx_context *, const uint32_t[], size_t, int, int); -uint32_t subagentx_context_uptime(struct subagentx_context *); -void subagentx_context_free(struct subagentx_context *); -struct subagentx_agentcaps *subagentx_agentcaps(struct subagentx_context *, - uint32_t[], size_t, const char *); -void subagentx_agentcaps_free(struct subagentx_agentcaps *); -struct subagentx_region *subagentx_region(struct subagentx_context *, - uint32_t[], size_t, uint8_t); -void subagentx_region_free(struct subagentx_region *); -struct subagentx_index *subagentx_index_integer_new(struct subagentx_region *, - uint32_t[], size_t); -struct subagentx_index *subagentx_index_integer_any(struct subagentx_region *, - uint32_t[], size_t); -struct subagentx_index *subagentx_index_integer_value(struct subagentx_region *, - uint32_t[], size_t, uint32_t); -struct subagentx_index *subagentx_index_integer_dynamic( - struct subagentx_region *, uint32_t[], size_t); -struct subagentx_index *subagentx_index_string_dynamic( - struct subagentx_region *, uint32_t[], size_t); -struct subagentx_index *subagentx_index_nstring_dynamic( - struct subagentx_region *, uint32_t[], size_t, size_t); -struct subagentx_index *subagentx_index_oid_dynamic(struct subagentx_region *, - uint32_t[], size_t); -struct subagentx_index *subagentx_index_noid_dynamic(struct subagentx_region *, - uint32_t[], size_t, size_t); -struct subagentx_index *subagentx_index_ipaddress_dynamic( - struct subagentx_region *, uint32_t[], size_t); -void subagentx_index_free(struct subagentx_index *); -struct subagentx_object *subagentx_object(struct subagentx_region *, uint32_t[], - size_t, struct subagentx_index *[], size_t, int, - void (*)(struct subagentx_varbind *)); -void subagentx_object_free(struct subagentx_object *); - -void subagentx_varbind_integer(struct subagentx_varbind *, uint32_t); -void subagentx_varbind_string(struct subagentx_varbind *, const char *); -void subagentx_varbind_nstring(struct subagentx_varbind *, - const unsigned char *, size_t); -void subagentx_varbind_printf(struct subagentx_varbind *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_varbind_null(struct subagentx_varbind *); -void subagentx_varbind_oid(struct subagentx_varbind *, const uint32_t[], - size_t); -void subagentx_varbind_object(struct subagentx_varbind *, - struct subagentx_object *); -void subagentx_varbind_index(struct subagentx_varbind *, - struct subagentx_index *); -void subagentx_varbind_ipaddress(struct subagentx_varbind *, - const struct in_addr *); -void subagentx_varbind_counter32(struct subagentx_varbind *, uint32_t); -void subagentx_varbind_gauge32(struct subagentx_varbind *, uint32_t); -void subagentx_varbind_timeticks(struct subagentx_varbind *, uint32_t); -void subagentx_varbind_opaque(struct subagentx_varbind *, const char *, size_t); -void subagentx_varbind_counter64(struct subagentx_varbind *, uint64_t); -void subagentx_varbind_notfound(struct subagentx_varbind *); -void subagentx_varbind_error(struct subagentx_varbind *); - -enum subagentx_request_type subagentx_varbind_request( - struct subagentx_varbind *); -struct subagentx_object * - subagentx_varbind_get_object(struct subagentx_varbind *); -uint32_t subagentx_varbind_get_index_integer(struct subagentx_varbind *, - struct subagentx_index *); -const unsigned char *subagentx_varbind_get_index_string( - struct subagentx_varbind *, struct subagentx_index *, size_t *, int *); -const uint32_t *subagentx_varbind_get_index_oid(struct subagentx_varbind *, - struct subagentx_index *, size_t *, int *); -const struct in_addr *subagentx_varbind_get_index_ipaddress( - struct subagentx_varbind *, struct subagentx_index *); -void subagentx_varbind_set_index_integer(struct subagentx_varbind *, - struct subagentx_index *, uint32_t); -void subagentx_varbind_set_index_string(struct subagentx_varbind *, - struct subagentx_index *, const char *); -void subagentx_varbind_set_index_nstring(struct subagentx_varbind *, - struct subagentx_index *, const unsigned char *, size_t); -void subagentx_varbind_set_index_oid(struct subagentx_varbind *, - struct subagentx_index *, const uint32_t *, size_t); -void subagentx_varbind_set_index_object(struct subagentx_varbind *, - struct subagentx_index *, struct subagentx_object *); -void subagentx_varbind_set_index_ipaddress(struct subagentx_varbind *, - struct subagentx_index *, const struct in_addr *); diff --git a/lib/libagentx/subagentx_internal.h b/lib/libagentx/subagentx_internal.h deleted file mode 100644 index 4dafcf373a4..00000000000 --- a/lib/libagentx/subagentx_internal.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include <sys/queue.h> -#include <sys/time.h> -#include <sys/tree.h> - -#include "agentx.h" - -enum subagentx_cstate { /* Current state */ - SA_CSTATE_CLOSE, /* Closed */ - SA_CSTATE_WAITOPEN, /* Connection requested */ - SA_CSTATE_OPEN, /* Open */ - SA_CSTATE_WAITCLOSE /* Close requested */ -}; - -enum subagentx_dstate { /* Desired state */ - SA_DSTATE_OPEN, /* Open */ - SA_DSTATE_CLOSE /* Close/free */ -}; - -struct subagentx { - void (*sa_nofd)(struct subagentx *, void *, int); - void *sa_cookie; - int sa_fd; - enum subagentx_cstate sa_cstate; - enum subagentx_dstate sa_dstate; - struct agentx *sa_ax; - TAILQ_HEAD(, subagentx_session) sa_sessions; - TAILQ_HEAD(, subagentx_get) sa_getreqs; - RB_HEAD(sa_requests, subagentx_request) sa_requests; -}; - -struct subagentx_session { - struct subagentx *sas_sa; - uint32_t sas_id; - uint32_t sas_timeout; - struct agentx_oid sas_oid; - struct agentx_ostring sas_descr; - enum subagentx_cstate sas_cstate; - enum subagentx_dstate sas_dstate; - uint32_t sas_packetid; - TAILQ_HEAD(, subagentx_context) sas_contexts; - TAILQ_ENTRY(subagentx_session) sas_sa_sessions; -}; - -struct subagentx_context { - struct subagentx_session *sac_sas; - int sac_name_default; - struct agentx_ostring sac_name; - uint32_t sac_sysuptime; - struct timespec sac_sysuptimespec; - enum subagentx_cstate sac_cstate; - enum subagentx_dstate sac_dstate; - TAILQ_HEAD(, subagentx_agentcaps) sac_agentcaps; - TAILQ_HEAD(, subagentx_region) sac_regions; - RB_HEAD(sac_objects, subagentx_object) sac_objects; - TAILQ_ENTRY(subagentx_context) sac_sas_contexts; -}; - -struct subagentx_get { - struct subagentx_context *sag_sac; - int sag_fd; /* Only used for logging */ - uint32_t sag_sessionid; - uint32_t sag_transactionid; - uint32_t sag_packetid; - int sag_context_default; - struct agentx_ostring sag_context; - enum agentx_pdu_type sag_type; - uint16_t sag_nonrep; - uint16_t sag_maxrep; - size_t sag_nvarbind; - struct subagentx_varbind *sag_varbind; - TAILQ_ENTRY(subagentx_get) sag_sa_getreqs; -}; - -__dead void subagentx_log_sa_fatalx(struct subagentx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sa_warn(struct subagentx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sa_warnx(struct subagentx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sa_info(struct subagentx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sa_debug(struct subagentx *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -__dead void subagentx_log_sas_fatalx(struct subagentx_session *, const char *, - ...) __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sas_warnx(struct subagentx_session *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sas_warn(struct subagentx_session *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sas_info(struct subagentx_session *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -__dead void subagentx_log_sac_fatalx(struct subagentx_context *, const char *, - ...) __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sac_warnx(struct subagentx_context *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sac_warn(struct subagentx_context *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sac_info(struct subagentx_context *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sac_debug(struct subagentx_context *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -__dead void subagentx_log_sag_fatalx(struct subagentx_get *, const char *, - ...) __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sag_warnx(struct subagentx_get *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sag_warn(struct subagentx_get *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); -void subagentx_log_sag_debug(struct subagentx_get *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); diff --git a/lib/libagentx/subagentx_log.c b/lib/libagentx/subagentx_log.c deleted file mode 100644 index d9dcca8e856..00000000000 --- a/lib/libagentx/subagentx_log.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "subagentx_internal.h" - -#define SUBAGENTX_CONTEXT_NAME(sac) (sac->sac_name_default ? "<default>" : \ - (char *)sac->sac_name.aos_string) -#define SUBAGENTX_GET_CTXNAME(sag) (sag->sag_context_default ? "<default>" : \ - (char *)sag->sag_context.aos_string) - -enum subagentx_log_type { - SUBAGENTX_LOG_TYPE_FATAL, - SUBAGENTX_LOG_TYPE_WARN, - SUBAGENTX_LOG_TYPE_INFO, - SUBAGENTX_LOG_TYPE_DEBUG -}; - -void (*subagentx_log_fatal)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))) = NULL; -void (*subagentx_log_warn)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))) = NULL; -void (*subagentx_log_info)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))) = NULL; -void (*subagentx_log_debug)(const char *, ...) - __attribute__((__format__ (printf, 1, 2))) = NULL; - - -static void -subagentx_log_do(enum subagentx_log_type, const char *, va_list, int, - struct subagentx *, struct subagentx_session *, struct subagentx_context *, - struct subagentx_get *); - -void -subagentx_log_sa_fatalx(struct subagentx *sa, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, sa, NULL, NULL, - NULL); - va_end(ap); - abort(); -} - -void -subagentx_log_sa_warn(struct subagentx *sa, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, sa, NULL, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sa_warnx(struct subagentx *sa, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, sa, NULL, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sa_info(struct subagentx *sa, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, sa, NULL, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sa_debug(struct subagentx *sa, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, sa, NULL, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sas_fatalx(struct subagentx_session *sas, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, sas, NULL, - NULL); - va_end(ap); - abort(); -} - -void -subagentx_log_sas_warnx(struct subagentx_session *sas, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, sas, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sas_warn(struct subagentx_session *sas, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, sas, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sas_info(struct subagentx_session *sas, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, sas, NULL, - NULL); - va_end(ap); -} - -void -subagentx_log_sac_fatalx(struct subagentx_context *sac, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, sac, - NULL); - va_end(ap); - abort(); -} - -void -subagentx_log_sac_warnx(struct subagentx_context *sac, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, sac, - NULL); - va_end(ap); -} - -void -subagentx_log_sac_warn(struct subagentx_context *sac, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, sac, - NULL); - va_end(ap); -} - -void -subagentx_log_sac_info(struct subagentx_context *sac, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, NULL, sac, - NULL); - va_end(ap); -} - -void -subagentx_log_sac_debug(struct subagentx_context *sac, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, sac, - NULL); - va_end(ap); -} - -void -subagentx_log_sag_fatalx(struct subagentx_get *sag, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, NULL, - sag); - va_end(ap); - abort(); -} - -void -subagentx_log_sag_warnx(struct subagentx_get *sag, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, NULL, - sag); - va_end(ap); -} - -void -subagentx_log_sag_warn(struct subagentx_get *sag, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, NULL, - sag); - va_end(ap); -} - -void -subagentx_log_sag_debug(struct subagentx_get *sag, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, NULL, - sag); - va_end(ap); -} - -static void -subagentx_log_do(enum subagentx_log_type type, const char *fmt, va_list ap, - int useerrno, struct subagentx *sa, struct subagentx_session *sas, - struct subagentx_context *sac, struct subagentx_get *sag) -{ - void (*subagentx_log)(const char *, ...); - char buf[1500]; - - if (type == SUBAGENTX_LOG_TYPE_FATAL) - subagentx_log = subagentx_log_fatal; - else if (type == SUBAGENTX_LOG_TYPE_WARN) - subagentx_log = subagentx_log_warn; - else if (type == SUBAGENTX_LOG_TYPE_INFO) - subagentx_log = subagentx_log_info; - else - subagentx_log = subagentx_log_debug; - if (subagentx_log == NULL) - return; - - vsnprintf(buf, sizeof(buf), fmt, ap); - - if (sag != NULL) { - if (useerrno) - subagentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: " - "%s: %s", sag->sag_fd, sag->sag_sessionid, - SUBAGENTX_GET_CTXNAME(sag), sag->sag_transactionid, - sag->sag_packetid, buf, strerror(errno)); - else - subagentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: " - "%s", sag->sag_fd, sag->sag_sessionid, - SUBAGENTX_GET_CTXNAME(sag), sag->sag_transactionid, - sag->sag_packetid, buf); - } else if (sac != NULL) { - sas = sac->sac_sas; - sa = sas->sas_sa; - if (useerrno) - subagentx_log("[fd:%d sess:%u ctx:%s]: %s: %s", - sa->sa_fd, sas->sas_id, SUBAGENTX_CONTEXT_NAME(sac), - buf, strerror(errno)); - else - subagentx_log("[fd:%d sess:%u ctx:%s]: %s", sa->sa_fd, - sas->sas_id, SUBAGENTX_CONTEXT_NAME(sac), buf); - } else if (sas != NULL) { - sa = sas->sas_sa; - if (useerrno) - subagentx_log("[fd:%d sess:%u]: %s: %s", sa->sa_fd, - sas->sas_id, buf, strerror(errno)); - else - subagentx_log("[fd:%d sess:%u]: %s", sa->sa_fd, - sas->sas_id, buf); - } else if (sa->sa_fd == -1) { - if (useerrno) - subagentx_log("%s: %s", buf, strerror(errno)); - else - subagentx_log("%s", buf); - } else { - if (useerrno) - subagentx_log("[fd:%d]: %s: %s", sa->sa_fd, buf, - strerror(errno)); - else - subagentx_log("[fd:%d]: %s", sa->sa_fd, buf); - } -} |