Skip to content

Commit 43e8e75

Browse files
committed
Add CPython-style error handling to Python API calls in LDAPraise_for_message
- Ensure pointers are set to NULL when freed. - Free all non-NULL pointers at the end. - Use goto to jump to the cleanup. Note that Python API sets the current exception on failure. If something else than C API fails, PyErr_NoMemory or PyErr_SetObject is called before returning NULL. I recommend reviewing the diff with whitespace changes hidden (`-w` in Git CLI).
1 parent d9ded15 commit 43e8e75

File tree

1 file changed

+161
-103
lines changed

1 file changed

+161
-103
lines changed

Modules/constants.c

Lines changed: 161 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -50,139 +50,197 @@ LDAPerr(int errnum)
5050
PyObject *
5151
LDAPraise_for_message(LDAP *l, LDAPMessage *m)
5252
{
53+
int myerrno, errnum, opt_errnum, res, msgid = -1, msgtype = 0;
54+
PyObject *errobj = NULL;
55+
PyObject *info = NULL;
56+
PyObject *str = NULL;
57+
PyObject *pyerrno = NULL;
58+
PyObject *pyresult = NULL;
59+
PyObject *pyctrls = NULL;
60+
char *matched = NULL;
61+
char *error = NULL;
62+
char **refs = NULL;
63+
LDAPControl **serverctrls = NULL;
64+
5365
if (l == NULL) {
5466
PyErr_SetFromErrno(LDAPexception_class);
5567
ldap_msgfree(m);
5668
return NULL;
5769
}
58-
else {
59-
int myerrno, errnum, opt_errnum, msgid = -1, msgtype = 0;
60-
PyObject *errobj;
61-
PyObject *info;
62-
PyObject *str;
63-
PyObject *pyerrno;
64-
PyObject *pyresult;
65-
PyObject *pyctrls = NULL;
66-
char *matched = NULL, *error = NULL, **refs = NULL;
67-
LDAPControl **serverctrls = NULL;
68-
69-
/* at first save errno for later use before it gets overwritten by another call */
70-
myerrno = errno;
71-
72-
if (m != NULL) {
73-
msgid = ldap_msgid(m);
74-
msgtype = ldap_msgtype(m);
75-
ldap_parse_result(l, m, &errnum, &matched, &error, &refs,
76-
&serverctrls, 1);
77-
}
7870

79-
if (msgtype <= 0) {
80-
opt_errnum = ldap_get_option(l, LDAP_OPT_ERROR_NUMBER, &errnum);
81-
if (opt_errnum != LDAP_OPT_SUCCESS)
82-
errnum = opt_errnum;
71+
/* at first save errno for later use before it gets overwritten by another call */
72+
myerrno = errno;
8373

74+
if (m != NULL) {
75+
msgid = ldap_msgid(m);
76+
msgtype = ldap_msgtype(m);
77+
ldap_parse_result(l, m, &errnum, &matched, &error, &refs,
78+
&serverctrls, 1);
79+
}
80+
81+
if (msgtype <= 0) {
82+
opt_errnum = ldap_get_option(l, LDAP_OPT_ERROR_NUMBER, &errnum);
83+
if (opt_errnum != LDAP_OPT_SUCCESS) {
84+
errnum = opt_errnum;
8485
if (errnum == LDAP_NO_MEMORY) {
85-
return PyErr_NoMemory();
86+
PyErr_NoMemory();
87+
goto cleanup;
8688
}
87-
88-
ldap_get_option(l, LDAP_OPT_MATCHED_DN, &matched);
89-
ldap_get_option(l, LDAP_OPT_ERROR_STRING, &error);
9089
}
9190

92-
if (errnum >= LDAP_ERROR_MIN && errnum <= LDAP_ERROR_MAX &&
93-
errobjects[errnum + LDAP_ERROR_OFFSET] != NULL) {
94-
errobj = errobjects[errnum + LDAP_ERROR_OFFSET];
91+
ldap_get_option(l, LDAP_OPT_MATCHED_DN, &matched);
92+
ldap_get_option(l, LDAP_OPT_ERROR_STRING, &error);
93+
}
94+
95+
if (errnum >= LDAP_ERROR_MIN && errnum <= LDAP_ERROR_MAX) {
96+
// Borrowed reference
97+
errobj = errobjects[errnum + LDAP_ERROR_OFFSET];
98+
}
99+
if (errobj == NULL) {
100+
// Borrowed reference
101+
errobj = LDAPexception_class;
102+
}
103+
104+
info = PyDict_New();
105+
if (info == NULL) {
106+
goto cleanup;
107+
}
108+
109+
if (msgtype > 0) {
110+
pyresult = PyInt_FromLong(msgtype);
111+
if (!pyresult) {
112+
goto cleanup;
95113
}
96-
else {
97-
errobj = LDAPexception_class;
114+
res = PyDict_SetItemString(info, "msgtype", pyresult);
115+
if (res) {
116+
goto cleanup;
98117
}
118+
Py_CLEAR(pyresult);
119+
}
99120

100-
info = PyDict_New();
101-
if (info == NULL) {
102-
ldap_memfree(matched);
103-
ldap_memfree(error);
104-
ldap_memvfree((void **)refs);
105-
ldap_controls_free(serverctrls);
106-
return NULL;
121+
if (msgid >= 0) {
122+
pyresult = PyInt_FromLong(msgid);
123+
if (!pyresult) {
124+
goto cleanup;
107125
}
108-
109-
if (msgtype > 0) {
110-
pyresult = PyInt_FromLong(msgtype);
111-
if (pyresult)
112-
PyDict_SetItemString(info, "msgtype", pyresult);
113-
Py_XDECREF(pyresult);
126+
res = PyDict_SetItemString(info, "msgid", pyresult);
127+
if (res) {
128+
goto cleanup;
114129
}
130+
Py_CLEAR(pyresult);
131+
}
115132

116-
if (msgid >= 0) {
117-
pyresult = PyInt_FromLong(msgid);
118-
if (pyresult)
119-
PyDict_SetItemString(info, "msgid", pyresult);
120-
Py_XDECREF(pyresult);
121-
}
133+
pyresult = PyInt_FromLong(errnum);
134+
if (!pyresult) {
135+
goto cleanup;
136+
}
137+
res = PyDict_SetItemString(info, "result", pyresult);
138+
if (res) {
139+
goto cleanup;
140+
}
141+
Py_CLEAR(pyresult);
142+
143+
str = PyUnicode_FromString(ldap_err2string(errnum));
144+
if (!str) {
145+
goto cleanup;
146+
}
147+
res = PyDict_SetItemString(info, "desc", str);
148+
if (res) {
149+
goto cleanup;
150+
}
151+
Py_CLEAR(str);
122152

123-
pyresult = PyInt_FromLong(errnum);
124-
if (pyresult)
125-
PyDict_SetItemString(info, "result", pyresult);
126-
Py_XDECREF(pyresult);
127-
128-
str = PyUnicode_FromString(ldap_err2string(errnum));
129-
if (str)
130-
PyDict_SetItemString(info, "desc", str);
131-
Py_XDECREF(str);
132-
133-
if (myerrno != 0) {
134-
pyerrno = PyInt_FromLong(myerrno);
135-
if (pyerrno)
136-
PyDict_SetItemString(info, "errno", pyerrno);
137-
Py_XDECREF(pyerrno);
153+
if (myerrno != 0) {
154+
pyerrno = PyInt_FromLong(myerrno);
155+
if (!pyerrno) {
156+
goto cleanup;
138157
}
158+
res = PyDict_SetItemString(info, "errno", pyerrno);
159+
if (res) {
160+
goto cleanup;
161+
}
162+
Py_CLEAR(pyerrno);
163+
}
139164

140-
if (!(pyctrls = LDAPControls_to_List(serverctrls))) {
141-
int err = LDAP_NO_MEMORY;
165+
if (!(pyctrls = LDAPControls_to_List(serverctrls))) {
166+
int err = LDAP_NO_MEMORY;
167+
ldap_set_option(l, LDAP_OPT_ERROR_NUMBER, &err);
142168

143-
ldap_set_option(l, LDAP_OPT_ERROR_NUMBER, &err);
144-
ldap_memfree(matched);
145-
ldap_memfree(error);
146-
ldap_memvfree((void **)refs);
147-
ldap_controls_free(serverctrls);
148-
return PyErr_NoMemory();
149-
}
150-
ldap_controls_free(serverctrls);
151-
PyDict_SetItemString(info, "ctrls", pyctrls);
152-
Py_XDECREF(pyctrls);
153-
154-
if (matched != NULL) {
155-
if (*matched != '\0') {
156-
str = PyUnicode_FromString(matched);
157-
if (str)
158-
PyDict_SetItemString(info, "matched", str);
159-
Py_XDECREF(str);
169+
PyErr_NoMemory();
170+
goto cleanup;
171+
}
172+
ldap_controls_free(serverctrls);
173+
serverctrls = NULL;
174+
res = PyDict_SetItemString(info, "ctrls", pyctrls);
175+
if (res) {
176+
goto cleanup;
177+
}
178+
Py_CLEAR(pyctrls);
179+
180+
if (matched != NULL) {
181+
if (*matched != '\0') {
182+
str = PyUnicode_FromString(matched);
183+
if (!str) {
184+
goto cleanup;
185+
}
186+
res = PyDict_SetItemString(info, "matched", str);
187+
if (res) {
188+
goto cleanup;
160189
}
161-
ldap_memfree(matched);
190+
Py_CLEAR(str);
162191
}
192+
ldap_memfree(matched);
193+
matched = NULL;
194+
}
163195

164-
if (errnum == LDAP_REFERRAL && refs != NULL && refs[0] != NULL) {
165-
/* Keep old behaviour, overshadow error message */
166-
char err[1024];
196+
if (errnum == LDAP_REFERRAL && refs != NULL && refs[0] != NULL) {
197+
/* Keep old behaviour, overshadow error message */
198+
char err[1024];
167199

168-
snprintf(err, sizeof(err), "Referral:\n%s", refs[0]);
169-
str = PyUnicode_FromString(err);
170-
PyDict_SetItemString(info, "info", str);
171-
Py_XDECREF(str);
200+
snprintf(err, sizeof(err), "Referral:\n%s", refs[0]);
201+
str = PyUnicode_FromString(err);
202+
if (!str) {
203+
goto cleanup;
204+
}
205+
res = PyDict_SetItemString(info, "info", str);
206+
if (res) {
207+
goto cleanup;
208+
}
209+
Py_CLEAR(str);
210+
}
211+
else if (error != NULL && *error != '\0') {
212+
str = PyUnicode_FromString(error);
213+
if (!str) {
214+
goto cleanup;
172215
}
173-
else if (error != NULL && *error != '\0') {
174-
str = PyUnicode_FromString(error);
175-
if (str)
176-
PyDict_SetItemString(info, "info", str);
177-
Py_XDECREF(str);
216+
res = PyDict_SetItemString(info, "info", str);
217+
if (res) {
218+
goto cleanup;
178219
}
220+
Py_CLEAR(str);
221+
}
179222

180-
PyErr_SetObject(errobj, info);
181-
Py_DECREF(info);
182-
ldap_memvfree((void **)refs);
223+
PyErr_SetObject(errobj, info);
224+
225+
cleanup:
226+
if (matched) {
227+
ldap_memfree(matched);
228+
}
229+
if (error) {
183230
ldap_memfree(error);
184-
return NULL;
185231
}
232+
if (refs) {
233+
ldap_memvfree((void **)refs);
234+
}
235+
if (serverctrls) {
236+
ldap_controls_free(serverctrls);
237+
}
238+
Py_XDECREF(pyresult);
239+
Py_XDECREF(pyerrno);
240+
Py_XDECREF(str);
241+
Py_XDECREF(info);
242+
Py_XDECREF(pyctrls);
243+
return NULL;
186244
}
187245

188246
PyObject *

0 commit comments

Comments
 (0)