Skip to content

Commit 718a4ff

Browse files
committed
Fix scalar values in SNMP set requests
1 parent dc8ded6 commit 718a4ff

4 files changed

Lines changed: 130 additions & 6 deletions

File tree

netsnmp/client_intf.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,16 +1045,36 @@ py_netsnmp_attr_set_bytes(PyObject *obj, char *attr_name,
10451045
}
10461046

10471047
static int
1048-
py_netsnmp_attr_bytes(PyObject *obj, char * attr_name, char **val,
1049-
Py_ssize_t *len)
1048+
py_netsnmp_attr_value(PyObject *obj, char *attr_name, char **val,
1049+
Py_ssize_t *len, PyObject **value_obj)
10501050
{
10511051
*val = NULL;
1052+
*value_obj = NULL;
10521053
if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) {
10531054
PyObject *attr = PyObject_GetAttrString(obj, attr_name);
10541055
if (attr) {
1055-
int retval;
1056-
retval = PyBytes_AsStringAndSize(attr, val, len);
1057-
Py_DECREF(attr);
1056+
int retval = -1;
1057+
1058+
if (PyBytes_Check(attr)) {
1059+
retval = PyBytes_AsStringAndSize(attr, val, len);
1060+
} else {
1061+
PyObject *str_attr = PyObject_Str(attr);
1062+
Py_DECREF(attr);
1063+
attr = str_attr;
1064+
if (attr) {
1065+
const char *str_val = PyUnicode_AsUTF8AndSize(attr, len);
1066+
if (str_val) {
1067+
*val = (char *)str_val;
1068+
retval = 0;
1069+
}
1070+
}
1071+
}
1072+
1073+
if (retval == 0) {
1074+
*value_obj = attr;
1075+
} else {
1076+
Py_XDECREF(attr);
1077+
}
10581078
return retval;
10591079
}
10601080
}
@@ -2594,6 +2614,7 @@ netsnmp_set(PyObject *self, PyObject *args)
25942614
char err_str[STR_BUF_SIZE];
25952615
char *tmpstr;
25962616
Py_ssize_t tmplen;
2617+
PyObject *value_obj = NULL;
25972618

25982619
oid_arr = calloc(MAX_OID_LEN, sizeof(oid));
25992620

@@ -2654,7 +2675,8 @@ netsnmp_set(PyObject *self, PyObject *args)
26542675
}
26552676
}
26562677

2657-
if (py_netsnmp_attr_bytes(varbind, "val", &val, &tmplen) < 0) {
2678+
if (py_netsnmp_attr_value(varbind, "val", &val, &tmplen,
2679+
&value_obj) < 0) {
26582680
snmp_free_pdu(pdu);
26592681
goto done;
26602682
}
@@ -2675,6 +2697,7 @@ netsnmp_set(PyObject *self, PyObject *args)
26752697
len = (int)tmplen;
26762698
status = __add_var_val_str(pdu, oid_arr, oid_arr_len,
26772699
(char *) tmp_val_str, len, type);
2700+
Py_CLEAR(value_obj);
26782701

26792702
if (verbose && status == FAILURE)
26802703
printf("error: set: adding variable/value to PDU");
@@ -2706,6 +2729,7 @@ netsnmp_set(PyObject *self, PyObject *args)
27062729
ret = Py_BuildValue("i",0); /* fail, return False */
27072730
}
27082731
done:
2732+
Py_XDECREF(value_obj);
27092733
Py_XDECREF(varbind);
27102734
SAFE_FREE(oid_arr);
27112735
if (PyErr_Occurred())

netsnmp/tests/system/run.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ python3 setup.py build_ext --inplace
2020
SNMPD_CONFIG="$RUNTIME_DIR/snmpd.conf"
2121
cat "$ROOT/netsnmp/tests/system/snmpd.conf" >"$SNMPD_CONFIG"
2222
printf '\npersistentDir %s\n' "$RUNTIME_DIR" >>"$SNMPD_CONFIG"
23+
printf 'pass_persist .1.3.6.1.4.1.8072.9999.9999 %s %s\n' \
24+
"$(command -v python3)" \
25+
"$ROOT/netsnmp/tests/system/set_test_agent.py" >>"$SNMPD_CONFIG"
2326

2427
snmpd -f -Lo -C \
2528
-c "$SNMPD_CONFIG" \
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import sys
2+
3+
4+
ROOT = '.1.3.6.1.4.1.8072.9999.9999'
5+
INTEGER_OID = ROOT + '.1.0'
6+
VALUES = {
7+
INTEGER_OID: ('integer', '42'),
8+
}
9+
10+
11+
def respond(*lines):
12+
print(*lines, sep='\n', flush=True)
13+
14+
15+
for command in sys.stdin:
16+
command = command.rstrip('\n')
17+
if command == 'PING':
18+
respond('PONG')
19+
elif command == 'get':
20+
oid = sys.stdin.readline().rstrip('\n')
21+
value = VALUES.get(oid)
22+
if value is None:
23+
respond('NONE')
24+
else:
25+
respond(oid, value[0], value[1])
26+
elif command == 'set':
27+
oid = sys.stdin.readline().rstrip('\n')
28+
value_type, value = sys.stdin.readline().rstrip('\n').split(' ', 1)
29+
if oid not in VALUES:
30+
respond('not-writable')
31+
else:
32+
VALUES[oid] = (value_type, value)
33+
respond('DONE')
34+
else:
35+
respond('NONE')

netsnmp/tests/system/test_set.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import socket
12
import unittest
23

34
import netsnmp
@@ -9,7 +10,68 @@
910
)
1011

1112

13+
TEST_INTEGER = '.1.3.6.1.4.1.8072.9999.9999.1'
14+
15+
1216
class SetTests(unittest.TestCase):
17+
def test_accepts_canonical_scalar_values(self):
18+
read_session = netsnmp.Session(**READ_ARGS)
19+
write_session = netsnmp.Session(**WRITE_ARGS)
20+
values = (
21+
(73, b'73'),
22+
('74', b'74'),
23+
(b'75', b'75'),
24+
)
25+
26+
for value, expected in values:
27+
with self.subTest(value=value):
28+
varbind = netsnmp.Varbind(
29+
TEST_INTEGER, '0', value, 'INTEGER')
30+
self.assertEqual(
31+
write_session.set(netsnmp.VarList(varbind)), 1)
32+
self.assertEqual(
33+
read_session.get(netsnmp.VarList(
34+
netsnmp.Varbind(TEST_INTEGER, '0'))),
35+
(expected,))
36+
37+
def test_accepts_text_octet_string_values(self):
38+
read_session = netsnmp.Session(**READ_ARGS)
39+
write_session = netsnmp.Session(**WRITE_ARGS)
40+
41+
for value, expected in (
42+
('text-value', b'text-value'),
43+
(b'bytes-value', b'bytes-value')):
44+
with self.subTest(value=value):
45+
varbind = netsnmp.Varbind(
46+
SYS_LOCATION, '0', value, 'OCTETSTR')
47+
self.assertEqual(
48+
write_session.set(netsnmp.VarList(varbind)), 1)
49+
self.assertEqual(
50+
read_session.get(netsnmp.VarList(
51+
netsnmp.Varbind(SYS_LOCATION, '0'))),
52+
(expected,))
53+
54+
def test_accepts_binary_octet_string(self):
55+
value = b'\x00\xff'
56+
57+
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as listener:
58+
listener.bind(('127.0.0.1', 0))
59+
listener.settimeout(1)
60+
port = listener.getsockname()[1]
61+
session = netsnmp.Session(
62+
Version=2,
63+
DestHost='127.0.0.1:{}'.format(port),
64+
Community='public',
65+
Timeout=1000,
66+
Retries=0,
67+
)
68+
varbind = netsnmp.Varbind(
69+
SYS_LOCATION, '0', value, 'OCTETSTR')
70+
71+
self.assertEqual(session.set(netsnmp.VarList(varbind)), 0)
72+
request, _ = listener.recvfrom(65535)
73+
self.assertIn(value, request)
74+
1375
def test_convenience_function(self):
1476
value = b'convenience-api'
1577
result = netsnmp.snmpset(

0 commit comments

Comments
 (0)