1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Wez Furlong <wez@thebrainroom.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: transports.c 290803 2009-11-16 10:36:27Z felipe $ */
20 :
21 : #include "php.h"
22 : #include "php_streams_int.h"
23 : #include "ext/standard/file.h"
24 :
25 : static HashTable xport_hash;
26 :
27 : PHPAPI HashTable *php_stream_xport_get_hash(void)
28 35326 : {
29 35326 : return &xport_hash;
30 : }
31 :
32 : PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
33 176362 : {
34 176362 : return zend_hash_update(&xport_hash, protocol, strlen(protocol) + 1, &factory, sizeof(factory), NULL);
35 : }
36 :
37 : PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
38 70660 : {
39 70660 : return zend_hash_del(&xport_hash, protocol, strlen(protocol) + 1);
40 : }
41 :
42 : #define ERR_REPORT(out_err, fmt, arg) \
43 : if (out_err) { spprintf(out_err, 0, fmt, arg); } \
44 : else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); }
45 :
46 : #define ERR_RETURN(out_err, local_err, fmt) \
47 : if (out_err) { *out_err = local_err; } \
48 : else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \
49 : if (local_err) { efree(local_err); local_err = NULL; } \
50 : }
51 :
52 : PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
53 : int flags, const char *persistent_id,
54 : struct timeval *timeout,
55 : php_stream_context *context,
56 : char **error_string,
57 : int *error_code
58 : STREAMS_DC TSRMLS_DC)
59 1800 : {
60 1800 : php_stream *stream = NULL;
61 1800 : php_stream_transport_factory *factory = NULL;
62 1800 : const char *p, *protocol = NULL;
63 1800 : int n = 0, failed = 0;
64 1800 : char *error_text = NULL;
65 1800 : struct timeval default_timeout = { 0, 0 };
66 :
67 1800 : default_timeout.tv_sec = FG(default_socket_timeout);
68 :
69 1800 : if (timeout == NULL) {
70 1530 : timeout = &default_timeout;
71 : }
72 :
73 : /* check for a cached persistent socket */
74 1800 : if (persistent_id) {
75 993 : switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) {
76 : case PHP_STREAM_PERSISTENT_SUCCESS:
77 : /* use a 0 second timeout when checking if the socket
78 : * has already died */
79 0 : if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
80 0 : return stream;
81 : }
82 : /* dead - kill it */
83 0 : php_stream_pclose(stream);
84 0 : stream = NULL;
85 :
86 : /* fall through */
87 :
88 : case PHP_STREAM_PERSISTENT_FAILURE:
89 : default:
90 : /* failed; get a new one */
91 : ;
92 : }
93 : }
94 :
95 8875 : for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
96 7075 : n++;
97 : }
98 :
99 3593 : if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
100 1793 : protocol = name;
101 1793 : name = p + 3;
102 1793 : namelen -= n + 3;
103 : } else {
104 7 : protocol = "tcp";
105 7 : n = 3;
106 : }
107 :
108 1800 : if (protocol) {
109 1800 : char *tmp = estrndup(protocol, n);
110 1800 : if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) {
111 : char wrapper_name[32];
112 :
113 1 : if (n >= sizeof(wrapper_name))
114 0 : n = sizeof(wrapper_name) - 1;
115 1 : PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
116 :
117 1 : ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
118 : wrapper_name);
119 :
120 1 : efree(tmp);
121 1 : return NULL;
122 : }
123 1799 : efree(tmp);
124 : }
125 :
126 1799 : if (factory == NULL) {
127 : /* should never happen */
128 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
129 0 : return NULL;
130 : }
131 :
132 1799 : stream = (*factory)(protocol, n,
133 : (char*)name, namelen, persistent_id, options, flags, timeout,
134 : context STREAMS_REL_CC TSRMLS_CC);
135 :
136 1799 : if (stream) {
137 1799 : php_stream_context_set(stream, context);
138 :
139 1799 : if ((flags & STREAM_XPORT_SERVER) == 0) {
140 : /* client */
141 :
142 1731 : if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
143 1731 : if (-1 == php_stream_xport_connect(stream, name, namelen,
144 : flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
145 : timeout, &error_text, error_code TSRMLS_CC)) {
146 :
147 19 : ERR_RETURN(error_string, error_text, "connect() failed: %s");
148 :
149 19 : failed = 1;
150 : }
151 : }
152 :
153 : } else {
154 : /* server */
155 68 : if (flags & STREAM_XPORT_BIND) {
156 68 : if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
157 0 : ERR_RETURN(error_string, error_text, "bind() failed: %s");
158 0 : failed = 1;
159 68 : } else if (flags & STREAM_XPORT_LISTEN) {
160 64 : if (0 != php_stream_xport_listen(stream, 5, &error_text TSRMLS_CC)) {
161 0 : ERR_RETURN(error_string, error_text, "listen() failed: %s");
162 0 : failed = 1;
163 : }
164 : }
165 : }
166 : }
167 : }
168 :
169 1799 : if (failed) {
170 : /* failure means that they don't get a stream to play with */
171 19 : if (persistent_id) {
172 2 : php_stream_pclose(stream);
173 : } else {
174 17 : php_stream_close(stream);
175 : }
176 19 : stream = NULL;
177 : }
178 :
179 1799 : return stream;
180 : }
181 :
182 : /* Bind the stream to a local address */
183 : PHPAPI int php_stream_xport_bind(php_stream *stream,
184 : const char *name, long namelen,
185 : char **error_text
186 : TSRMLS_DC)
187 68 : {
188 : php_stream_xport_param param;
189 : int ret;
190 :
191 68 : memset(¶m, 0, sizeof(param));
192 68 : param.op = STREAM_XPORT_OP_BIND;
193 68 : param.inputs.name = (char*)name;
194 68 : param.inputs.namelen = namelen;
195 68 : param.want_errortext = error_text ? 1 : 0;
196 :
197 68 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
198 :
199 68 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
200 68 : if (error_text) {
201 68 : *error_text = param.outputs.error_text;
202 : }
203 :
204 68 : return param.outputs.returncode;
205 : }
206 :
207 0 : return ret;
208 : }
209 :
210 : /* Connect to a remote address */
211 : PHPAPI int php_stream_xport_connect(php_stream *stream,
212 : const char *name, long namelen,
213 : int asynchronous,
214 : struct timeval *timeout,
215 : char **error_text,
216 : int *error_code
217 : TSRMLS_DC)
218 1731 : {
219 : php_stream_xport_param param;
220 : int ret;
221 :
222 1731 : memset(¶m, 0, sizeof(param));
223 1731 : param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
224 1731 : param.inputs.name = (char*)name;
225 1731 : param.inputs.namelen = namelen;
226 1731 : param.inputs.timeout = timeout;
227 :
228 1731 : param.want_errortext = error_text ? 1 : 0;
229 :
230 1731 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
231 :
232 1731 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
233 1731 : if (error_text) {
234 1731 : *error_text = param.outputs.error_text;
235 : }
236 1731 : if (error_code) {
237 1730 : *error_code = param.outputs.error_code;
238 : }
239 1731 : return param.outputs.returncode;
240 : }
241 :
242 0 : return ret;
243 :
244 : }
245 :
246 : /* Prepare to listen */
247 : PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
248 64 : {
249 : php_stream_xport_param param;
250 : int ret;
251 :
252 64 : memset(¶m, 0, sizeof(param));
253 64 : param.op = STREAM_XPORT_OP_LISTEN;
254 64 : param.inputs.backlog = backlog;
255 64 : param.want_errortext = error_text ? 1 : 0;
256 :
257 64 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
258 :
259 64 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
260 64 : if (error_text) {
261 64 : *error_text = param.outputs.error_text;
262 : }
263 :
264 64 : return param.outputs.returncode;
265 : }
266 :
267 0 : return ret;
268 : }
269 :
270 : /* Get the next client and their address (as a string) */
271 : PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
272 : char **textaddr, int *textaddrlen,
273 : void **addr, socklen_t *addrlen,
274 : struct timeval *timeout,
275 : char **error_text
276 : TSRMLS_DC)
277 41 : {
278 : php_stream_xport_param param;
279 : int ret;
280 :
281 41 : memset(¶m, 0, sizeof(param));
282 :
283 41 : param.op = STREAM_XPORT_OP_ACCEPT;
284 41 : param.inputs.timeout = timeout;
285 41 : param.want_addr = addr ? 1 : 0;
286 41 : param.want_textaddr = textaddr ? 1 : 0;
287 41 : param.want_errortext = error_text ? 1 : 0;
288 :
289 41 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
290 :
291 41 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
292 41 : *client = param.outputs.client;
293 41 : if (addr) {
294 0 : *addr = param.outputs.addr;
295 0 : *addrlen = param.outputs.addrlen;
296 : }
297 41 : if (textaddr) {
298 0 : *textaddr = param.outputs.textaddr;
299 0 : *textaddrlen = param.outputs.textaddrlen;
300 : }
301 41 : if (error_text) {
302 41 : *error_text = param.outputs.error_text;
303 : }
304 :
305 41 : return param.outputs.returncode;
306 : }
307 0 : return ret;
308 : }
309 :
310 : PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
311 : char **textaddr, int *textaddrlen,
312 : void **addr, socklen_t *addrlen
313 : TSRMLS_DC)
314 0 : {
315 : php_stream_xport_param param;
316 : int ret;
317 :
318 0 : memset(¶m, 0, sizeof(param));
319 :
320 0 : param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
321 0 : param.want_addr = addr ? 1 : 0;
322 0 : param.want_textaddr = textaddr ? 1 : 0;
323 :
324 0 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
325 :
326 0 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
327 0 : if (addr) {
328 0 : *addr = param.outputs.addr;
329 0 : *addrlen = param.outputs.addrlen;
330 : }
331 0 : if (textaddr) {
332 0 : *textaddr = param.outputs.textaddr;
333 0 : *textaddrlen = param.outputs.textaddrlen;
334 : }
335 :
336 0 : return param.outputs.returncode;
337 : }
338 0 : return ret;
339 : }
340 :
341 : PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
342 5 : {
343 : php_stream_xport_crypto_param param;
344 : int ret;
345 :
346 5 : memset(¶m, 0, sizeof(param));
347 5 : param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
348 5 : param.inputs.method = crypto_method;
349 5 : param.inputs.session = session_stream;
350 :
351 5 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
352 :
353 5 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
354 5 : return param.outputs.returncode;
355 : }
356 :
357 0 : php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
358 :
359 0 : return ret;
360 : }
361 :
362 : PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
363 5 : {
364 : php_stream_xport_crypto_param param;
365 : int ret;
366 :
367 5 : memset(¶m, 0, sizeof(param));
368 5 : param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
369 5 : param.inputs.activate = activate;
370 :
371 5 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
372 :
373 5 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
374 5 : return param.outputs.returncode;
375 : }
376 :
377 0 : php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
378 :
379 0 : return ret;
380 : }
381 :
382 : /* Similar to recv() system call; read data from the stream, optionally
383 : * peeking, optionally retrieving OOB data */
384 : PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
385 : long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen
386 : TSRMLS_DC)
387 0 : {
388 : php_stream_xport_param param;
389 0 : int ret = 0;
390 0 : int recvd_len = 0;
391 : #if 0
392 : int oob;
393 :
394 : if (flags == 0 && addr == NULL) {
395 : return php_stream_read(stream, buf, buflen);
396 : }
397 :
398 : if (stream->readfilters.head) {
399 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
400 : return -1;
401 : }
402 :
403 : oob = (flags & STREAM_OOB) == STREAM_OOB;
404 :
405 : if (!oob && addr == NULL) {
406 : /* must be peeking at regular data; copy content from the buffer
407 : * first, then adjust the pointer/len before handing off to the
408 : * stream */
409 : recvd_len = stream->writepos - stream->readpos;
410 : if (recvd_len > buflen) {
411 : recvd_len = buflen;
412 : }
413 : if (recvd_len) {
414 : memcpy(buf, stream->readbuf, recvd_len);
415 : buf += recvd_len;
416 : buflen -= recvd_len;
417 : }
418 : /* if we filled their buffer, return */
419 : if (buflen == 0) {
420 : return recvd_len;
421 : }
422 : }
423 : #endif
424 :
425 : /* otherwise, we are going to bypass the buffer */
426 :
427 0 : memset(¶m, 0, sizeof(param));
428 :
429 0 : param.op = STREAM_XPORT_OP_RECV;
430 0 : param.want_addr = addr ? 1 : 0;
431 0 : param.want_textaddr = textaddr ? 1 : 0;
432 0 : param.inputs.buf = buf;
433 0 : param.inputs.buflen = buflen;
434 0 : param.inputs.flags = flags;
435 :
436 0 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
437 :
438 0 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
439 0 : if (addr) {
440 0 : *addr = param.outputs.addr;
441 0 : *addrlen = param.outputs.addrlen;
442 : }
443 0 : if (textaddr) {
444 0 : *textaddr = param.outputs.textaddr;
445 0 : *textaddrlen = param.outputs.textaddrlen;
446 : }
447 0 : return recvd_len + param.outputs.returncode;
448 : }
449 0 : return recvd_len ? recvd_len : -1;
450 : }
451 :
452 : /* Similar to send() system call; send data to the stream, optionally
453 : * sending it as OOB data */
454 : PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
455 : long flags, void *addr, socklen_t addrlen TSRMLS_DC)
456 0 : {
457 : php_stream_xport_param param;
458 0 : int ret = 0;
459 : int oob;
460 :
461 : #if 0
462 : if (flags == 0 && addr == NULL) {
463 : return php_stream_write(stream, buf, buflen);
464 : }
465 : #endif
466 :
467 0 : oob = (flags & STREAM_OOB) == STREAM_OOB;
468 :
469 0 : if ((oob || addr) && stream->writefilters.head) {
470 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
471 0 : return -1;
472 : }
473 :
474 0 : memset(¶m, 0, sizeof(param));
475 :
476 0 : param.op = STREAM_XPORT_OP_SEND;
477 0 : param.want_addr = addr ? 1 : 0;
478 0 : param.inputs.buf = (char*)buf;
479 0 : param.inputs.buflen = buflen;
480 0 : param.inputs.flags = flags;
481 0 : param.inputs.addr = addr;
482 0 : param.inputs.addrlen = addrlen;
483 :
484 0 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
485 :
486 0 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
487 0 : return param.outputs.returncode;
488 : }
489 0 : return -1;
490 : }
491 :
492 : /* Similar to shutdown() system call; shut down part of a full-duplex
493 : * connection */
494 : PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC)
495 3 : {
496 : php_stream_xport_param param;
497 3 : int ret = 0;
498 :
499 3 : memset(¶m, 0, sizeof(param));
500 :
501 3 : param.op = STREAM_XPORT_OP_SHUTDOWN;
502 3 : param.how = how;
503 :
504 3 : ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
505 :
506 3 : if (ret == PHP_STREAM_OPTION_RETURN_OK) {
507 3 : return param.outputs.returncode;
508 : }
509 0 : return -1;
510 : }
511 :
512 : /*
513 : * Local variables:
514 : * tab-width: 4
515 : * c-basic-offset: 4
516 : * End:
517 : * vim600: noet sw=4 ts=4 fdm=marker
518 : * vim<600: noet sw=4 ts=4
519 : */
|