Commit 54ee20d
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 9898293 commit 54ee20d
13 files changed
Lines changed: 995 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
28 | 33 | | |
29 | 34 | | |
30 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
171 | 171 | | |
172 | 172 | | |
173 | 173 | | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
174 | 186 | | |
175 | 187 | | |
176 | 188 | | |
| |||
| 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 | |
|---|---|---|---|
| |||
2327 | 2327 | | |
2328 | 2328 | | |
2329 | 2329 | | |
| 2330 | + | |
| 2331 | + | |
| 2332 | + | |
| 2333 | + | |
| 2334 | + | |
| 2335 | + | |
| 2336 | + | |
| 2337 | + | |
| 2338 | + | |
| 2339 | + | |
| 2340 | + | |
| 2341 | + | |
| 2342 | + | |
| 2343 | + | |
| 2344 | + | |
| 2345 | + | |
| 2346 | + | |
| 2347 | + | |
| 2348 | + | |
| 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 | + | |
2330 | 2387 | | |
2331 | 2388 | | |
2332 | 2389 | | |
| |||
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 | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
87 | 92 | | |
88 | 93 | | |
89 | 94 | | |
| |||
0 commit comments