Return to transports.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / streams |
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.4 ! misho 5: | Copyright (c) 1997-2014 The PHP Group |
1.1 misho 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:
1.1.1.2 misho 19: /* $Id$ */
1.1 misho 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: {
29: return &xport_hash;
30: }
31:
32: PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
33: {
34: 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: {
39: 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: {
60: php_stream *stream = NULL;
61: php_stream_transport_factory *factory = NULL;
62: const char *p, *protocol = NULL;
63: int n = 0, failed = 0;
64: char *error_text = NULL;
65: struct timeval default_timeout = { 0, 0 };
66:
67: default_timeout.tv_sec = FG(default_socket_timeout);
68:
69: if (timeout == NULL) {
70: timeout = &default_timeout;
71: }
72:
73: /* check for a cached persistent socket */
74: if (persistent_id) {
75: 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: if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
80: return stream;
81: }
82: /* dead - kill it */
83: php_stream_pclose(stream);
84: 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: for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
96: n++;
97: }
98:
99: if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
100: protocol = name;
101: name = p + 3;
102: namelen -= n + 3;
103: } else {
104: protocol = "tcp";
105: n = 3;
106: }
107:
108: if (protocol) {
109: char *tmp = estrndup(protocol, n);
110: if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) {
111: char wrapper_name[32];
112:
113: if (n >= sizeof(wrapper_name))
114: n = sizeof(wrapper_name) - 1;
115: PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
116:
117: 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: efree(tmp);
121: return NULL;
122: }
123: efree(tmp);
124: }
125:
126: if (factory == NULL) {
127: /* should never happen */
128: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
129: return NULL;
130: }
131:
132: stream = (*factory)(protocol, n,
133: (char*)name, namelen, persistent_id, options, flags, timeout,
134: context STREAMS_REL_CC TSRMLS_CC);
135:
136: if (stream) {
137: php_stream_context_set(stream, context);
138:
139: if ((flags & STREAM_XPORT_SERVER) == 0) {
140: /* client */
141:
142: if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
143: 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: ERR_RETURN(error_string, error_text, "connect() failed: %s");
148:
149: failed = 1;
150: }
151: }
152:
153: } else {
154: /* server */
155: if (flags & STREAM_XPORT_BIND) {
156: if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
157: ERR_RETURN(error_string, error_text, "bind() failed: %s");
158: failed = 1;
159: } else if (flags & STREAM_XPORT_LISTEN) {
160: zval **zbacklog = NULL;
161: int backlog = 32;
162:
163: if (stream->context && php_stream_context_get_option(stream->context, "socket", "backlog", &zbacklog) == SUCCESS) {
164: zval *ztmp = *zbacklog;
165:
166: convert_to_long_ex(&ztmp);
167: backlog = Z_LVAL_P(ztmp);
168: if (ztmp != *zbacklog) {
169: zval_ptr_dtor(&ztmp);
170: }
171: }
172:
173: if (0 != php_stream_xport_listen(stream, backlog, &error_text TSRMLS_CC)) {
174: ERR_RETURN(error_string, error_text, "listen() failed: %s");
175: failed = 1;
176: }
177: }
178: }
179: }
180: }
181:
182: if (failed) {
183: /* failure means that they don't get a stream to play with */
184: if (persistent_id) {
185: php_stream_pclose(stream);
186: } else {
187: php_stream_close(stream);
188: }
189: stream = NULL;
190: }
191:
192: return stream;
193: }
194:
195: /* Bind the stream to a local address */
196: PHPAPI int php_stream_xport_bind(php_stream *stream,
197: const char *name, long namelen,
198: char **error_text
199: TSRMLS_DC)
200: {
201: php_stream_xport_param param;
202: int ret;
203:
204: memset(¶m, 0, sizeof(param));
205: param.op = STREAM_XPORT_OP_BIND;
206: param.inputs.name = (char*)name;
207: param.inputs.namelen = namelen;
208: param.want_errortext = error_text ? 1 : 0;
209:
210: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
211:
212: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
213: if (error_text) {
214: *error_text = param.outputs.error_text;
215: }
216:
217: return param.outputs.returncode;
218: }
219:
220: return ret;
221: }
222:
223: /* Connect to a remote address */
224: PHPAPI int php_stream_xport_connect(php_stream *stream,
225: const char *name, long namelen,
226: int asynchronous,
227: struct timeval *timeout,
228: char **error_text,
229: int *error_code
230: TSRMLS_DC)
231: {
232: php_stream_xport_param param;
233: int ret;
234:
235: memset(¶m, 0, sizeof(param));
236: param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
237: param.inputs.name = (char*)name;
238: param.inputs.namelen = namelen;
239: param.inputs.timeout = timeout;
240:
241: param.want_errortext = error_text ? 1 : 0;
242:
243: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
244:
245: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
246: if (error_text) {
247: *error_text = param.outputs.error_text;
248: }
249: if (error_code) {
250: *error_code = param.outputs.error_code;
251: }
252: return param.outputs.returncode;
253: }
254:
255: return ret;
256:
257: }
258:
259: /* Prepare to listen */
260: PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
261: {
262: php_stream_xport_param param;
263: int ret;
264:
265: memset(¶m, 0, sizeof(param));
266: param.op = STREAM_XPORT_OP_LISTEN;
267: param.inputs.backlog = backlog;
268: param.want_errortext = error_text ? 1 : 0;
269:
270: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
271:
272: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
273: if (error_text) {
274: *error_text = param.outputs.error_text;
275: }
276:
277: return param.outputs.returncode;
278: }
279:
280: return ret;
281: }
282:
283: /* Get the next client and their address (as a string) */
284: PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
285: char **textaddr, int *textaddrlen,
286: void **addr, socklen_t *addrlen,
287: struct timeval *timeout,
288: char **error_text
289: TSRMLS_DC)
290: {
291: php_stream_xport_param param;
292: int ret;
293:
294: memset(¶m, 0, sizeof(param));
295:
296: param.op = STREAM_XPORT_OP_ACCEPT;
297: param.inputs.timeout = timeout;
298: param.want_addr = addr ? 1 : 0;
299: param.want_textaddr = textaddr ? 1 : 0;
300: param.want_errortext = error_text ? 1 : 0;
301:
302: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
303:
304: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
305: *client = param.outputs.client;
306: if (addr) {
307: *addr = param.outputs.addr;
308: *addrlen = param.outputs.addrlen;
309: }
310: if (textaddr) {
311: *textaddr = param.outputs.textaddr;
312: *textaddrlen = param.outputs.textaddrlen;
313: }
314: if (error_text) {
315: *error_text = param.outputs.error_text;
316: }
317:
318: return param.outputs.returncode;
319: }
320: return ret;
321: }
322:
323: PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
324: char **textaddr, int *textaddrlen,
325: void **addr, socklen_t *addrlen
326: TSRMLS_DC)
327: {
328: php_stream_xport_param param;
329: int ret;
330:
331: memset(¶m, 0, sizeof(param));
332:
333: param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
334: param.want_addr = addr ? 1 : 0;
335: param.want_textaddr = textaddr ? 1 : 0;
336:
337: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
338:
339: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
340: if (addr) {
341: *addr = param.outputs.addr;
342: *addrlen = param.outputs.addrlen;
343: }
344: if (textaddr) {
345: *textaddr = param.outputs.textaddr;
346: *textaddrlen = param.outputs.textaddrlen;
347: }
348:
349: return param.outputs.returncode;
350: }
351: return ret;
352: }
353:
354: PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
355: {
356: php_stream_xport_crypto_param param;
357: int ret;
358:
359: memset(¶m, 0, sizeof(param));
360: param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
361: param.inputs.method = crypto_method;
362: param.inputs.session = session_stream;
363:
364: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
365:
366: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
367: return param.outputs.returncode;
368: }
369:
370: php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
371:
372: return ret;
373: }
374:
375: PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
376: {
377: php_stream_xport_crypto_param param;
378: int ret;
379:
380: memset(¶m, 0, sizeof(param));
381: param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
382: param.inputs.activate = activate;
383:
384: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m);
385:
386: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
387: return param.outputs.returncode;
388: }
389:
390: php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
391:
392: return ret;
393: }
394:
395: /* Similar to recv() system call; read data from the stream, optionally
396: * peeking, optionally retrieving OOB data */
397: PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
398: long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen
399: TSRMLS_DC)
400: {
401: php_stream_xport_param param;
402: int ret = 0;
403: int recvd_len = 0;
404: #if 0
405: int oob;
406:
407: if (flags == 0 && addr == NULL) {
408: return php_stream_read(stream, buf, buflen);
409: }
410:
411: if (stream->readfilters.head) {
412: php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
413: return -1;
414: }
415:
416: oob = (flags & STREAM_OOB) == STREAM_OOB;
417:
418: if (!oob && addr == NULL) {
419: /* must be peeking at regular data; copy content from the buffer
420: * first, then adjust the pointer/len before handing off to the
421: * stream */
422: recvd_len = stream->writepos - stream->readpos;
423: if (recvd_len > buflen) {
424: recvd_len = buflen;
425: }
426: if (recvd_len) {
427: memcpy(buf, stream->readbuf, recvd_len);
428: buf += recvd_len;
429: buflen -= recvd_len;
430: }
431: /* if we filled their buffer, return */
432: if (buflen == 0) {
433: return recvd_len;
434: }
435: }
436: #endif
437:
438: /* otherwise, we are going to bypass the buffer */
439:
440: memset(¶m, 0, sizeof(param));
441:
442: param.op = STREAM_XPORT_OP_RECV;
443: param.want_addr = addr ? 1 : 0;
444: param.want_textaddr = textaddr ? 1 : 0;
445: param.inputs.buf = buf;
446: param.inputs.buflen = buflen;
447: param.inputs.flags = flags;
448:
449: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
450:
451: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
452: if (addr) {
453: *addr = param.outputs.addr;
454: *addrlen = param.outputs.addrlen;
455: }
456: if (textaddr) {
457: *textaddr = param.outputs.textaddr;
458: *textaddrlen = param.outputs.textaddrlen;
459: }
460: return recvd_len + param.outputs.returncode;
461: }
462: return recvd_len ? recvd_len : -1;
463: }
464:
465: /* Similar to send() system call; send data to the stream, optionally
466: * sending it as OOB data */
467: PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
468: long flags, void *addr, socklen_t addrlen TSRMLS_DC)
469: {
470: php_stream_xport_param param;
471: int ret = 0;
472: int oob;
473:
474: #if 0
475: if (flags == 0 && addr == NULL) {
476: return php_stream_write(stream, buf, buflen);
477: }
478: #endif
479:
480: oob = (flags & STREAM_OOB) == STREAM_OOB;
481:
482: if ((oob || addr) && stream->writefilters.head) {
483: php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
484: return -1;
485: }
486:
487: memset(¶m, 0, sizeof(param));
488:
489: param.op = STREAM_XPORT_OP_SEND;
490: param.want_addr = addr ? 1 : 0;
491: param.inputs.buf = (char*)buf;
492: param.inputs.buflen = buflen;
493: param.inputs.flags = flags;
494: param.inputs.addr = addr;
495: param.inputs.addrlen = addrlen;
496:
497: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
498:
499: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
500: return param.outputs.returncode;
501: }
502: return -1;
503: }
504:
505: /* Similar to shutdown() system call; shut down part of a full-duplex
506: * connection */
507: PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC)
508: {
509: php_stream_xport_param param;
510: int ret = 0;
511:
512: memset(¶m, 0, sizeof(param));
513:
514: param.op = STREAM_XPORT_OP_SHUTDOWN;
515: param.how = how;
516:
517: ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m);
518:
519: if (ret == PHP_STREAM_OPTION_RETURN_OK) {
520: return param.outputs.returncode;
521: }
522: return -1;
523: }
524:
525: /*
526: * Local variables:
527: * tab-width: 4
528: * c-basic-offset: 4
529: * End:
530: * vim600: noet sw=4 ts=4 fdm=marker
531: * vim<600: noet sw=4 ts=4
532: */