Line | Count | Source |
1 | | /*************************************************************************** |
2 | | * _ _ ____ _ |
3 | | * Project ___| | | | _ \| | |
4 | | * / __| | | | |_) | | |
5 | | * | (__| |_| | _ <| |___ |
6 | | * \___|\___/|_| \_\_____| |
7 | | * |
8 | | * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | | * |
10 | | * This software is licensed as described in the file COPYING, which |
11 | | * you should have received as part of this distribution. The terms |
12 | | * are also available at https://curl.se/docs/copyright.html. |
13 | | * |
14 | | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | | * copies of the Software, and permit persons to whom the Software is |
16 | | * furnished to do so, under the terms of the COPYING file. |
17 | | * |
18 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | | * KIND, either express or implied. |
20 | | * |
21 | | * SPDX-License-Identifier: curl |
22 | | * |
23 | | ***************************************************************************/ |
24 | | #include "curl_setup.h" |
25 | | |
26 | | #include "urldata.h" |
27 | | #include "api.h" |
28 | | #include "curl_threads.h" |
29 | | #include "multiif.h" |
30 | | #include "vtls/vtls_scache.h" |
31 | | |
32 | | typedef char mapi_fn_ids_fit_uint8[ |
33 | | (CURL_MAPI_FN_LAST <= CURL_CBAPI_FN_START) ? 1 : -1]; |
34 | | typedef char cbapi_fn_ids_fit_uint8[ |
35 | | ((CURL_CBAPI_FN_LAST - 1) <= UINT8_MAX) ? 1 : -1]; |
36 | | typedef char mapi_depth_fits_uint8[ |
37 | | (CURL_MAPI_MAX_RECURSION <= UINT8_MAX) ? 1 : -1]; |
38 | | |
39 | | struct Curl_eapi_fn_props { |
40 | | Curl_eapi_fn fn; |
41 | | uint8_t data_is_killed; /* easy handle is killed in call */ |
42 | | uint8_t recurse; /* may be called when another call is in progress */ |
43 | | uint8_t no_event_cb; /* may not be called during a multi event callback */ |
44 | | uint8_t no_scache_lock; /* may not be called with easy's vtls_scache |
45 | | locked by current thread */ |
46 | | }; |
47 | | |
48 | | static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = { |
49 | | /* function kill rec !ev !scach */ |
50 | | { CURL_EAPI_FN_easy_cleanup, 1, 0, 0, 0 }, |
51 | | { CURL_EAPI_FN_easy_duphandle, 0, 1, 0, 0 }, |
52 | | { CURL_EAPI_FN_easy_getinfo, 0, 1, 0, 0 }, |
53 | | { CURL_EAPI_FN_easy_header, 0, 1, 0, 0 }, |
54 | | { CURL_EAPI_FN_easy_nextheader, 0, 1, 0, 0 }, |
55 | | { CURL_EAPI_FN_easy_pause, 0, 1, 1, 0 }, |
56 | | { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0, 1 }, |
57 | | { CURL_EAPI_FN_easy_perform, 0, 0, 0, 1 }, |
58 | | { CURL_EAPI_FN_easy_recv, 0, 0, 0, 1 }, |
59 | | { CURL_EAPI_FN_easy_reset, 0, 0, 0, 0 }, |
60 | | { CURL_EAPI_FN_easy_send, 0, 0, 0, 1 }, |
61 | | { CURL_EAPI_FN_easy_setopt, 0, 1, 0, 0 }, |
62 | | { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0, 1 }, |
63 | | { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0, 1 }, |
64 | | { CURL_EAPI_FN_easy_upkeep, 0, 0, 0, 1 }, |
65 | | { CURL_EAPI_FN_ws_recv, 0, 1, 0, 1 }, |
66 | | { CURL_EAPI_FN_ws_send, 0, 1, 0, 1 }, |
67 | | { CURL_EAPI_FN_ws_start_frame, 0, 1, 0, 0 }, |
68 | | }; |
69 | | |
70 | | struct Curl_mapi_fn_props { |
71 | | Curl_mapi_fn fn; |
72 | | uint8_t multi_is_killed; /* multi handle is killed during call */ |
73 | | uint8_t recurse; /* may be called when another call is in progress */ |
74 | | uint8_t allow_ntfy_cb; /* may be called during a notify callback */ |
75 | | uint8_t no_scache_lock; /* may not be called with multi's vtls_scache |
76 | | locked by current thread */ |
77 | | }; |
78 | | |
79 | | static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = { |
80 | | /* function kill rec ntfy !scach */ |
81 | | { CURL_MAPI_FN_multi_add_handle, 0, 0, 1, 0 }, |
82 | | { CURL_MAPI_FN_multi_assign, 0, 1, 1, 0 }, |
83 | | { CURL_MAPI_FN_multi_cleanup, 1, 0, 0, 1 }, |
84 | | { CURL_MAPI_FN_multi_fdset, 0, 0, 1, 0 }, |
85 | | { CURL_MAPI_FN_multi_get_handles, 0, 1, 1, 0 }, |
86 | | { CURL_MAPI_FN_multi_get_offt, 0, 1, 1, 0 }, |
87 | | { CURL_MAPI_FN_multi_info_read, 0, 1, 1, 0 }, |
88 | | { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1, 0 }, |
89 | | { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1, 0 }, |
90 | | { CURL_MAPI_FN_multi_perform, 0, 0, 0, 1 }, |
91 | | { CURL_MAPI_FN_multi_poll, 0, 0, 1, 0 }, |
92 | | { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1, 0 }, |
93 | | { CURL_MAPI_FN_multi_setopt, 0, 0, 1, 0 }, |
94 | | { CURL_MAPI_FN_multi_socket_action, 0, 0, 0, 1 }, |
95 | | { CURL_MAPI_FN_multi_socket_all, 0, 0, 0, 1 }, |
96 | | { CURL_MAPI_FN_multi_socket, 0, 0, 0, 1 }, |
97 | | { CURL_MAPI_FN_multi_timeout, 0, 0, 1, 1 }, |
98 | | { CURL_MAPI_FN_multi_wait, 0, 0, 1, 0 }, |
99 | | { CURL_MAPI_FN_multi_waitfds, 0, 0, 1, 0 }, |
100 | | }; |
101 | | |
102 | | struct Curl_cbapi_fn_props { |
103 | | Curl_cbapi_fn fn; |
104 | | uint8_t is_event_cb; /* is a multi event processing callback */ |
105 | | }; |
106 | | |
107 | | static const struct Curl_cbapi_fn_props |
108 | | cbapi_fn_props[CURL_CBAPI_FN_LAST - CURL_CBAPI_FN_START] = { |
109 | | { CURL_CBAPI_FN_easy_chunk_bgn, 0 }, |
110 | | { CURL_CBAPI_FN_easy_chunk_end, 0 }, |
111 | | { CURL_CBAPI_FN_easy_closesocket, 0 }, |
112 | | { CURL_CBAPI_FN_easy_cr_in_read, 0 }, |
113 | | { CURL_CBAPI_FN_easy_cr_in_resume_from, 0 }, |
114 | | { CURL_CBAPI_FN_easy_cw_out_cb, 0 }, |
115 | | { CURL_CBAPI_FN_easy_fdebug, 0 }, |
116 | | { CURL_CBAPI_FN_easy_fnmatch_data, 0 }, |
117 | | { CURL_CBAPI_FN_easy_fopensocket, 0 }, |
118 | | { CURL_CBAPI_FN_easy_fprereq, 0 }, |
119 | | { CURL_CBAPI_FN_easy_fprogress, 0 }, |
120 | | { CURL_CBAPI_FN_easy_fread_func, 0 }, |
121 | | { CURL_CBAPI_FN_easy_fsockopt, 0 }, |
122 | | { CURL_CBAPI_FN_easy_fsslctx, 0 }, |
123 | | { CURL_CBAPI_FN_easy_fwrite_rtp, 0 }, |
124 | | { CURL_CBAPI_FN_easy_fxferinfo, 0 }, |
125 | | { CURL_CBAPI_FN_easy_ioctl_func, 0 }, |
126 | | { CURL_CBAPI_FN_easy_resolver_start, 0 }, |
127 | | { CURL_CBAPI_FN_easy_seek_func, 0 }, |
128 | | { CURL_CBAPI_FN_easy_ssh_hostkeyfunc, 0 }, |
129 | | { CURL_CBAPI_FN_easy_ssh_keyfunc, 0 }, |
130 | | { CURL_CBAPI_FN_easy_trailer_callback, 0 }, |
131 | | |
132 | | { CURL_CBAPI_FN_multi_ntfy_cb, 0 }, |
133 | | { CURL_CBAPI_FN_multi_push_cb, 0 }, |
134 | | { CURL_CBAPI_FN_multi_socket_cb, 1 }, |
135 | | { CURL_CBAPI_FN_multi_timer_cb, 1 }, |
136 | | }; |
137 | | |
138 | | static bool eapi_in_event_cb(struct Curl_easy *data) |
139 | 0 | { |
140 | 0 | struct Curl_multi *multi = data->multi; |
141 | 0 | if(multi && multi->callstack.count) { |
142 | 0 | size_t i; |
143 | 0 | for(i = 0; i < multi->callstack.count; ++i) { |
144 | 0 | if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) { |
145 | 0 | uint8_t fn = multi->callstack.calls[i]; |
146 | 0 | if((fn < CURL_CBAPI_FN_LAST) && |
147 | 0 | cbapi_fn_props[fn - CURL_CBAPI_FN_START].is_event_cb) |
148 | 0 | return TRUE; |
149 | 0 | } |
150 | 0 | } |
151 | 0 | } |
152 | 0 | return FALSE; |
153 | 0 | } |
154 | | |
155 | | static bool mapi_in_ntfy_cb(struct Curl_multi *multi) |
156 | 0 | { |
157 | 0 | if(multi && multi->callstack.count) { |
158 | 0 | size_t i; |
159 | 0 | for(i = 0; i < multi->callstack.count; ++i) { |
160 | 0 | if(multi->callstack.calls[i] == CURL_CBAPI_FN_multi_ntfy_cb) |
161 | 0 | return TRUE; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | return FALSE; |
165 | 0 | } |
166 | | |
167 | | bool Curl_api_multi_is_in_callback(struct Curl_multi *multi) |
168 | 0 | { |
169 | 0 | if(multi && multi->callstack.count) { |
170 | 0 | size_t i; |
171 | 0 | for(i = 0; i < multi->callstack.count; ++i) { |
172 | 0 | if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) |
173 | 0 | return TRUE; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | return FALSE; |
177 | 0 | } |
178 | | |
179 | | bool Curl_api_is_in_callback(struct Curl_easy *data) |
180 | 0 | { |
181 | 0 | if(data && data->multi) { |
182 | 0 | return Curl_api_multi_is_in_callback(data->multi); |
183 | 0 | } |
184 | 0 | return FALSE; |
185 | 0 | } |
186 | | |
187 | | bool Curl_eapi_enter(struct Curl_eapi_guard *guard, |
188 | | CURL *curl, |
189 | | Curl_eapi_fn fn, |
190 | | CURLcode *presult) |
191 | 0 | { |
192 | 0 | struct Curl_easy *data = curl; |
193 | 0 | const struct Curl_eapi_fn_props *fn_props; |
194 | 0 | CURLcode result = CURLE_OK; |
195 | |
|
196 | 0 | guard->depth = 0; |
197 | | |
198 | | /* Verify that we got an easy handle we can work with. */ |
199 | 0 | if(!GOOD_EASY_HANDLE(data)) { |
200 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
201 | 0 | goto out; |
202 | 0 | } |
203 | | /* verify that is either not added to a multi handle OR has a |
204 | | * GOOD multi handle that knows `data` for `data->mid`. */ |
205 | 0 | if(data->mid != UINT32_MAX) { |
206 | 0 | if(GOOD_MULTI_HANDLE(data->multi)) { |
207 | 0 | if(!Curl_multi_knows_easy(data->multi, data)) { |
208 | | /* But multi does not know it, something is fishy, better deny call */ |
209 | 0 | DEBUGASSERT(0); |
210 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
211 | 0 | goto out; |
212 | 0 | } |
213 | 0 | } |
214 | 0 | else { |
215 | 0 | DEBUGASSERT(0); /* data needs to have a GOOD multi handle */ |
216 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
217 | 0 | goto out; |
218 | 0 | } |
219 | 0 | } |
220 | 0 | else if(data->multi) { |
221 | 0 | DEBUGASSERT(0); /* data should not have a multi handle */ |
222 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
223 | 0 | goto out; |
224 | 0 | } |
225 | | /* verify that the call `fn` we're about to enter is known |
226 | | * and check call properties to be admitting. */ |
227 | 0 | if(fn >= CURL_EAPI_FN_LAST) { |
228 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
229 | 0 | goto out; |
230 | 0 | } |
231 | 0 | fn_props = &eapi_fn_props[fn]; |
232 | 0 | DEBUGASSERT(fn_props->fn == fn); |
233 | 0 | if(!fn_props->recurse) { |
234 | 0 | if(data->callstack.count) { |
235 | 0 | #ifdef CURLVERBOSE |
236 | 0 | DEBUGF(curl_mfprintf(stderr, |
237 | 0 | "EAPI guard: calling %hu with call to %u ongoing\n", (uint16_t)fn, |
238 | 0 | data->callstack.calls[data->callstack.count-1])); |
239 | 0 | #endif |
240 | 0 | result = CURLE_RECURSIVE_API_CALL; |
241 | 0 | goto out; |
242 | 0 | } |
243 | 0 | if(data->multi && data->multi->callstack.count) { |
244 | 0 | #ifdef CURLVERBOSE |
245 | |
|
246 | 0 | DEBUGF(curl_mfprintf(stderr, |
247 | 0 | "EAPI guard: calling %hu with multi call to %u ongoing\n", |
248 | 0 | (uint16_t)fn, |
249 | 0 | data->multi->callstack.calls[data->multi->callstack.count-1])); |
250 | 0 | #endif |
251 | 0 | result = CURLE_RECURSIVE_API_CALL; |
252 | 0 | goto out; |
253 | 0 | } |
254 | 0 | } |
255 | | |
256 | 0 | if(fn_props->no_event_cb && eapi_in_event_cb(data)) { |
257 | | /* Not allowed to be invoked while an event cb is ongoing */ |
258 | 0 | #ifdef CURLVERBOSE |
259 | 0 | DEBUGF(curl_mfprintf(stderr, |
260 | 0 | "EAPI guard: calling %hu while event callback ongoing\n", |
261 | 0 | (uint16_t)fn)); |
262 | 0 | #endif |
263 | 0 | result = CURLE_RECURSIVE_API_CALL; |
264 | 0 | goto out; |
265 | 0 | } |
266 | | |
267 | 0 | #if defined(USE_SSL) && defined(USE_MUTEX) |
268 | 0 | if(fn_props->no_scache_lock && |
269 | 0 | Curl_ssl_scache_is_locked_by_current_thread(data)) { |
270 | 0 | #ifdef CURLVERBOSE |
271 | 0 | DEBUGF(curl_mfprintf(stderr, |
272 | 0 | "EAPI guard: calling %hu while vtls_scache is locked by " |
273 | 0 | "current thread\n", (uint16_t)fn)); |
274 | 0 | #endif |
275 | 0 | result = CURLE_RECURSIVE_API_CALL; |
276 | 0 | goto out; |
277 | 0 | } |
278 | 0 | #endif |
279 | | |
280 | | /* all fine, add to data's callstack */ |
281 | 0 | if(data->callstack.count >= CURL_EAPI_MAX_RECURSION) { |
282 | 0 | result = CURLE_RECURSIVE_API_CALL; |
283 | 0 | goto out; |
284 | 0 | } |
285 | 0 | data->callstack.calls[data->callstack.count] = (uint16_t)fn; |
286 | 0 | ++data->callstack.count; |
287 | 0 | guard->depth = data->callstack.count; |
288 | 0 | guard->data = fn_props->data_is_killed ? NULL : data; |
289 | |
|
290 | 0 | out: |
291 | 0 | if(presult) |
292 | 0 | *presult = result; |
293 | 0 | return guard->depth > 0; |
294 | 0 | } |
295 | | |
296 | | void Curl_eapi_leave(struct Curl_eapi_guard *guard) |
297 | 0 | { |
298 | 0 | if(guard->depth) { |
299 | | /* guard->data is set when handle is supposed to stay alive during call */ |
300 | 0 | if(guard->data && GOOD_EASY_HANDLE(guard->data)) { |
301 | 0 | if(guard->depth > guard->data->callstack.count) { |
302 | 0 | DEBUGASSERT(0); /* something very wrong */ |
303 | 0 | } |
304 | 0 | else { |
305 | 0 | if(guard->depth < guard->data->callstack.count) { |
306 | 0 | DEBUGASSERT(0); /* someone forgot to clean up */ |
307 | 0 | } |
308 | | /* reset to depth the guard was entered in */ |
309 | 0 | guard->data->callstack.count = (uint16_t)(guard->depth - 1); |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | CURLHcode Curl_eapi_hcode(CURLcode result) |
316 | 0 | { |
317 | 0 | switch(result) { |
318 | 0 | case CURLE_OK: |
319 | 0 | return CURLHE_OK; |
320 | 0 | case CURLE_BAD_FUNCTION_ARGUMENT: |
321 | 0 | return CURLHE_BAD_ARGUMENT; |
322 | 0 | case CURLE_OUT_OF_MEMORY: |
323 | 0 | return CURLHE_OUT_OF_MEMORY; |
324 | 0 | case CURLE_NOT_BUILT_IN: |
325 | 0 | return CURLHE_NOT_BUILT_IN; |
326 | 0 | default: |
327 | | /* Unfortunately, we cannot convert RECURSIVE_API_CALL, |
328 | | * but since the header API is reentrant, this should not happen. */ |
329 | 0 | return CURLHE_BAD_ARGUMENT; |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | | bool Curl_mapi_enter(struct Curl_mapi_guard *guard, |
334 | | CURLM *m, |
335 | | Curl_mapi_fn fn, |
336 | | CURLMcode *pmresult) |
337 | 0 | { |
338 | 0 | struct Curl_multi *multi = m; |
339 | 0 | const struct Curl_mapi_fn_props *fn_props; |
340 | 0 | CURLMcode mresult = CURLM_OK; |
341 | |
|
342 | 0 | guard->depth = 0; |
343 | | |
344 | | /* Verify that we got an easy handle we can work with. */ |
345 | 0 | if(!GOOD_MULTI_HANDLE(multi)) { |
346 | 0 | mresult = CURLM_BAD_HANDLE; |
347 | 0 | goto out; |
348 | 0 | } |
349 | 0 | if(fn >= CURL_MAPI_FN_LAST) { |
350 | 0 | mresult = CURLM_BAD_FUNCTION_ARGUMENT; |
351 | 0 | goto out; |
352 | 0 | } |
353 | 0 | fn_props = &mapi_fn_props[fn]; |
354 | 0 | DEBUGASSERT(fn_props->fn == fn); |
355 | 0 | if(fn_props->allow_ntfy_cb && mapi_in_ntfy_cb(multi)) { |
356 | | /* explicitly allowed, even though normal recursion may not */ |
357 | 0 | } |
358 | 0 | else if(!fn_props->recurse && multi->callstack.count) { |
359 | 0 | #ifdef CURLVERBOSE |
360 | 0 | DEBUGF(curl_mfprintf(stderr, |
361 | 0 | "MAPI guard: calling %hu with call to %u ongoing\n", (uint16_t)fn, |
362 | 0 | multi->callstack.calls[multi->callstack.count-1])); |
363 | 0 | #endif |
364 | 0 | mresult = CURLM_RECURSIVE_API_CALL; |
365 | 0 | goto out; |
366 | 0 | } |
367 | | |
368 | 0 | #if defined(USE_SSL) && defined(USE_MUTEX) |
369 | 0 | if(fn_props->no_scache_lock && multi->ssl_scache && |
370 | 0 | Curl_ssl_scache_is_locked_by_current_thread(multi->admin)) { |
371 | 0 | #ifdef CURLVERBOSE |
372 | 0 | DEBUGF(curl_mfprintf(stderr, |
373 | 0 | "MAPI guard: calling %hu while its vtls_scache is locked by " |
374 | 0 | "current thread\n", (uint16_t)fn)); |
375 | 0 | #endif |
376 | 0 | mresult = CURLM_RECURSIVE_API_CALL; |
377 | 0 | goto out; |
378 | 0 | } |
379 | 0 | #endif |
380 | | |
381 | | /* all fine, add to data's callstack */ |
382 | 0 | if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) { |
383 | 0 | mresult = CURLM_RECURSIVE_API_CALL; |
384 | 0 | goto out; |
385 | 0 | } |
386 | 0 | multi->callstack.calls[multi->callstack.count] = (uint8_t)fn; |
387 | 0 | ++multi->callstack.count; |
388 | 0 | guard->depth = multi->callstack.count; |
389 | 0 | guard->multi = fn_props->multi_is_killed ? NULL : multi; |
390 | |
|
391 | 0 | out: |
392 | 0 | if(pmresult) |
393 | 0 | *pmresult = mresult; |
394 | 0 | return guard->depth > 0; |
395 | 0 | } |
396 | | |
397 | | void Curl_mapi_leave(struct Curl_mapi_guard *guard) |
398 | 0 | { |
399 | 0 | if(guard->depth) { |
400 | | /* guard->data is set when handle is supposed to stay alive during call */ |
401 | 0 | if(guard->multi && GOOD_MULTI_HANDLE(guard->multi)) { |
402 | 0 | if(guard->depth > guard->multi->callstack.count) { |
403 | 0 | DEBUGASSERT(0); /* something very wrong */ |
404 | 0 | } |
405 | 0 | else { |
406 | 0 | if(guard->depth < guard->multi->callstack.count) { |
407 | 0 | DEBUGASSERT(0); /* someone forgot to clean up */ |
408 | 0 | } |
409 | | /* reset to depth the guard was entered in */ |
410 | 0 | guard->multi->callstack.count = (uint8_t)(guard->depth - 1); |
411 | 0 | } |
412 | 0 | } |
413 | 0 | } |
414 | 0 | } |
415 | | |
416 | | void Curl_cbapi_enter(struct Curl_mapi_guard *guard, |
417 | | struct Curl_easy *data, |
418 | | struct Curl_multi *multi, |
419 | | Curl_cbapi_fn fn) |
420 | 0 | { |
421 | 0 | guard->depth = 0; |
422 | |
|
423 | 0 | if(!multi) |
424 | 0 | multi = data ? data->multi : NULL; |
425 | | /* if not multi is involved here, just leave */ |
426 | 0 | if(!multi) |
427 | 0 | return; |
428 | | /* invalid callback specifier? */ |
429 | 0 | if((fn >= CURL_CBAPI_FN_LAST) || (fn < CURL_CBAPI_FN_START)) { |
430 | 0 | DEBUGASSERT(0); |
431 | 0 | return; |
432 | 0 | } |
433 | 0 | DEBUGASSERT(cbapi_fn_props[fn - CURL_CBAPI_FN_START].fn == fn); |
434 | 0 | if(multi->callstack.count) { |
435 | 0 | size_t i; |
436 | 0 | for(i = multi->callstack.count; i; --i) { |
437 | 0 | if(multi->callstack.calls[i - 1] == fn) { |
438 | | /* recursive invocation of the same callback */ |
439 | 0 | DEBUGASSERT(0); |
440 | 0 | return; |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | /* all fine, add to data's callstack */ |
446 | | /* if multi callstack already at max depth, leave */ |
447 | 0 | if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) |
448 | 0 | return; |
449 | 0 | multi->callstack.calls[multi->callstack.count] = (uint8_t)fn; |
450 | 0 | ++multi->callstack.count; |
451 | 0 | guard->depth = multi->callstack.count; |
452 | 0 | guard->multi = multi; |
453 | 0 | } |
454 | | |
455 | | void Curl_cbapi_leave(struct Curl_mapi_guard *guard) |
456 | 0 | { |
457 | 0 | Curl_mapi_leave(guard); |
458 | 0 | } |