Commit e7bcbf1
committed
ext/curl: add socket callback options bridging to ext/sockets
Expose libcurl's CURLOPT_SOCKOPTFUNCTION, CURLOPT_OPENSOCKETFUNCTION and
CURLOPT_CLOSESOCKETFUNCTION, letting userland hook into socket creation,
configuration and teardown. These are useful for application security, in
particular SSRF protection (validating the resolved address before connecting)
and low-level socket hardening.
Following the existing curl_write_header / curl_prereqfunction bridge model, the
callbacks exchange ext/sockets Socket objects so they are fully usable in pure
PHP (socket_create/socket_bind, socket_set_option, socket_close):
- sockopt: fn(CurlHandle $ch, Socket $socket, int $purpose): int
returns CURL_SOCKOPT_OK / _ERROR / _ALREADY_CONNECTED
- opensocket: fn(CurlHandle $ch, int $purpose, array $address): Socket|false
$address = [family, socktype, protocol, ip, port];
returning false aborts the connection (CURL_SOCKET_BAD)
- closesocket: fn(CurlHandle $ch, Socket $socket): void
The dependency on ext/sockets is optional both at build and at runtime
(ZEND_MOD_OPTIONAL): the rest of ext/curl keeps working when sockets is not
loaded, and the three socket-callback options simply throw a clear Error when
invoked in that configuration. The Socket class entry is resolved lazily by
name at MINIT rather than referenced as a link-time symbol, so curl never
carries a hard symbol dependency on ext/sockets.
Notable details:
- Descriptors owned by libcurl are detached (bsd_socket = -1) before the
temporary Socket object is released, to avoid a double close. Socket
objects are created without calling socket_import_file_descriptor(), which
on Windows emits a spurious WSAEINVAL warning on a not-yet-connected
socket during the SOCKOPT phase.
- Pooled connections still alive at curl_easy_cleanup() would otherwise
invoke the userland CURLOPT_CLOSESOCKETFUNCTION callback during handle
destruction. Calling into PHP from there is unsafe (an exception thrown
from the callback would surface outside any try/catch). Setting the
option back to NULL on the easy handle is not enough — libcurl caches the
function pointer per connection. The close FCC is torn down before
curl_easy_cleanup() so the trampoline falls through to its native-close
fallback when libcurl invokes it.
- The same teardown is applied to every easy handle attached to a
CurlMultiHandle before curl_multi_cleanup() so the close callback never
fires from within the multi's destructor either.
- Stream-backed Sockets and already-closed Sockets returned from
CURLOPT_OPENSOCKETFUNCTION are refused with a TypeError: a stream-backed
Socket bypasses our bsd_socket detach (socket_free_obj() delegates close
to the backing php_stream), and an already-closed Socket would bury the
real cause under a generic CURLE_COULDNT_CONNECT.
- When the sockopt callback throws, the abort path returns
CURL_SOCKOPT_ERROR (matching opensocket / ssh_hostkey) so libcurl does
not connect with a half-configured socket.
- Setting an option to null restores libcurl's native default.
New constants (only defined when ext/curl is built with sockets headers
available, i.e. HAVE_SOCKETS): CURLOPT_SOCKOPTFUNCTION,
CURLOPT_OPENSOCKETFUNCTION, CURLOPT_CLOSESOCKETFUNCTION, CURL_SOCKOPT_OK,
CURL_SOCKOPT_ERROR, CURL_SOCKOPT_ALREADY_CONNECTED, CURLSOCKTYPE_IPCXN,
CURLSOCKTYPE_ACCEPT.1 parent dec19ed commit e7bcbf1
13 files changed
Lines changed: 988 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
218 | 218 | | |
219 | 219 | | |
220 | 220 | | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
221 | 225 | | |
222 | 226 | | |
223 | 227 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
250 | 250 | | |
251 | 251 | | |
252 | 252 | | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
253 | 263 | | |
254 | 264 | | |
255 | 265 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
78 | 85 | | |
79 | 86 | | |
80 | 87 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
28 | 34 | | |
29 | 35 | | |
30 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2347 | 2347 | | |
2348 | 2348 | | |
2349 | 2349 | | |
| 2350 | + | |
| 2351 | + | |
| 2352 | + | |
| 2353 | + | |
| 2354 | + | |
| 2355 | + | |
| 2356 | + | |
| 2357 | + | |
| 2358 | + | |
| 2359 | + | |
| 2360 | + | |
| 2361 | + | |
| 2362 | + | |
| 2363 | + | |
| 2364 | + | |
| 2365 | + | |
| 2366 | + | |
| 2367 | + | |
| 2368 | + | |
| 2369 | + | |
| 2370 | + | |
| 2371 | + | |
| 2372 | + | |
| 2373 | + | |
| 2374 | + | |
| 2375 | + | |
| 2376 | + | |
| 2377 | + | |
| 2378 | + | |
| 2379 | + | |
| 2380 | + | |
| 2381 | + | |
| 2382 | + | |
| 2383 | + | |
| 2384 | + | |
| 2385 | + | |
| 2386 | + | |
| 2387 | + | |
| 2388 | + | |
| 2389 | + | |
| 2390 | + | |
| 2391 | + | |
| 2392 | + | |
| 2393 | + | |
| 2394 | + | |
| 2395 | + | |
| 2396 | + | |
| 2397 | + | |
| 2398 | + | |
| 2399 | + | |
| 2400 | + | |
| 2401 | + | |
| 2402 | + | |
| 2403 | + | |
| 2404 | + | |
| 2405 | + | |
| 2406 | + | |
2350 | 2407 | | |
2351 | 2408 | | |
2352 | 2409 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
88 | 93 | | |
89 | 94 | | |
90 | 95 | | |
| |||
0 commit comments