PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/main/streams/transports.c

http://php52-backports.googlecode.com/
C | 519 lines | 371 code | 97 blank | 51 comment | 97 complexity | 7d561256e35c57a8b75ec27f580b629c MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2010 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. /* $Id: transports.c 293036 2010-01-03 09:23:27Z sebastian $ */
  19. #include "php.h"
  20. #include "php_streams_int.h"
  21. #include "ext/standard/file.h"
  22. static HashTable xport_hash;
  23. PHPAPI HashTable *php_stream_xport_get_hash(void)
  24. {
  25. return &xport_hash;
  26. }
  27. PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC)
  28. {
  29. return zend_hash_update(&xport_hash, protocol, strlen(protocol) + 1, &factory, sizeof(factory), NULL);
  30. }
  31. PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC)
  32. {
  33. return zend_hash_del(&xport_hash, protocol, strlen(protocol) + 1);
  34. }
  35. #define ERR_REPORT(out_err, fmt, arg) \
  36. if (out_err) { spprintf(out_err, 0, fmt, arg); } \
  37. else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); }
  38. #define ERR_RETURN(out_err, local_err, fmt) \
  39. if (out_err) { *out_err = local_err; } \
  40. else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \
  41. if (local_err) { efree(local_err); local_err = NULL; } \
  42. }
  43. PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options,
  44. int flags, const char *persistent_id,
  45. struct timeval *timeout,
  46. php_stream_context *context,
  47. char **error_string,
  48. int *error_code
  49. STREAMS_DC TSRMLS_DC)
  50. {
  51. php_stream *stream = NULL;
  52. php_stream_transport_factory *factory = NULL;
  53. const char *p, *protocol = NULL;
  54. int n = 0, failed = 0;
  55. char *error_text = NULL;
  56. struct timeval default_timeout = { 0, 0 };
  57. default_timeout.tv_sec = FG(default_socket_timeout);
  58. if (timeout == NULL) {
  59. timeout = &default_timeout;
  60. }
  61. /* check for a cached persistent socket */
  62. if (persistent_id) {
  63. switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) {
  64. case PHP_STREAM_PERSISTENT_SUCCESS:
  65. /* use a 0 second timeout when checking if the socket
  66. * has already died */
  67. if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) {
  68. return stream;
  69. }
  70. /* dead - kill it */
  71. php_stream_pclose(stream);
  72. stream = NULL;
  73. /* fall through */
  74. case PHP_STREAM_PERSISTENT_FAILURE:
  75. default:
  76. /* failed; get a new one */
  77. ;
  78. }
  79. }
  80. for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
  81. n++;
  82. }
  83. if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
  84. protocol = name;
  85. name = p + 3;
  86. namelen -= n + 3;
  87. } else {
  88. protocol = "tcp";
  89. n = 3;
  90. }
  91. if (protocol) {
  92. char *tmp = estrndup(protocol, n);
  93. if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) {
  94. char wrapper_name[32];
  95. if (n >= sizeof(wrapper_name))
  96. n = sizeof(wrapper_name) - 1;
  97. PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
  98. ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?",
  99. wrapper_name);
  100. efree(tmp);
  101. return NULL;
  102. }
  103. efree(tmp);
  104. }
  105. if (factory == NULL) {
  106. /* should never happen */
  107. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?");
  108. return NULL;
  109. }
  110. stream = (*factory)(protocol, n,
  111. (char*)name, namelen, persistent_id, options, flags, timeout,
  112. context STREAMS_REL_CC TSRMLS_CC);
  113. if (stream) {
  114. stream->context = context;
  115. if ((flags & STREAM_XPORT_SERVER) == 0) {
  116. /* client */
  117. if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
  118. if (-1 == php_stream_xport_connect(stream, name, namelen,
  119. flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
  120. timeout, &error_text, error_code TSRMLS_CC)) {
  121. ERR_RETURN(error_string, error_text, "connect() failed: %s");
  122. failed = 1;
  123. }
  124. }
  125. } else {
  126. /* server */
  127. if (flags & STREAM_XPORT_BIND) {
  128. if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) {
  129. ERR_RETURN(error_string, error_text, "bind() failed: %s");
  130. failed = 1;
  131. } else if (flags & STREAM_XPORT_LISTEN) {
  132. if (0 != php_stream_xport_listen(stream, 5, &error_text TSRMLS_CC)) {
  133. ERR_RETURN(error_string, error_text, "listen() failed: %s");
  134. failed = 1;
  135. }
  136. }
  137. }
  138. }
  139. }
  140. if (failed) {
  141. /* failure means that they don't get a stream to play with */
  142. if (persistent_id) {
  143. php_stream_pclose(stream);
  144. } else {
  145. php_stream_close(stream);
  146. }
  147. stream = NULL;
  148. }
  149. return stream;
  150. }
  151. /* Bind the stream to a local address */
  152. PHPAPI int php_stream_xport_bind(php_stream *stream,
  153. const char *name, long namelen,
  154. char **error_text
  155. TSRMLS_DC)
  156. {
  157. php_stream_xport_param param;
  158. int ret;
  159. memset(&param, 0, sizeof(param));
  160. param.op = STREAM_XPORT_OP_BIND;
  161. param.inputs.name = (char*)name;
  162. param.inputs.namelen = namelen;
  163. param.want_errortext = error_text ? 1 : 0;
  164. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  165. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  166. if (error_text) {
  167. *error_text = param.outputs.error_text;
  168. }
  169. return param.outputs.returncode;
  170. }
  171. return ret;
  172. }
  173. /* Connect to a remote address */
  174. PHPAPI int php_stream_xport_connect(php_stream *stream,
  175. const char *name, long namelen,
  176. int asynchronous,
  177. struct timeval *timeout,
  178. char **error_text,
  179. int *error_code
  180. TSRMLS_DC)
  181. {
  182. php_stream_xport_param param;
  183. int ret;
  184. memset(&param, 0, sizeof(param));
  185. param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT;
  186. param.inputs.name = (char*)name;
  187. param.inputs.namelen = namelen;
  188. param.inputs.timeout = timeout;
  189. param.want_errortext = error_text ? 1 : 0;
  190. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  191. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  192. if (error_text) {
  193. *error_text = param.outputs.error_text;
  194. }
  195. if (error_code) {
  196. *error_code = param.outputs.error_code;
  197. }
  198. return param.outputs.returncode;
  199. }
  200. return ret;
  201. }
  202. /* Prepare to listen */
  203. PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC)
  204. {
  205. php_stream_xport_param param;
  206. int ret;
  207. memset(&param, 0, sizeof(param));
  208. param.op = STREAM_XPORT_OP_LISTEN;
  209. param.inputs.backlog = backlog;
  210. param.want_errortext = error_text ? 1 : 0;
  211. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  212. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  213. if (error_text) {
  214. *error_text = param.outputs.error_text;
  215. }
  216. return param.outputs.returncode;
  217. }
  218. return ret;
  219. }
  220. /* Get the next client and their address (as a string) */
  221. PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client,
  222. char **textaddr, int *textaddrlen,
  223. void **addr, socklen_t *addrlen,
  224. struct timeval *timeout,
  225. char **error_text
  226. TSRMLS_DC)
  227. {
  228. php_stream_xport_param param;
  229. int ret;
  230. memset(&param, 0, sizeof(param));
  231. param.op = STREAM_XPORT_OP_ACCEPT;
  232. param.inputs.timeout = timeout;
  233. param.want_addr = addr ? 1 : 0;
  234. param.want_textaddr = textaddr ? 1 : 0;
  235. param.want_errortext = error_text ? 1 : 0;
  236. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  237. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  238. *client = param.outputs.client;
  239. if (addr) {
  240. *addr = param.outputs.addr;
  241. *addrlen = param.outputs.addrlen;
  242. }
  243. if (textaddr) {
  244. *textaddr = param.outputs.textaddr;
  245. *textaddrlen = param.outputs.textaddrlen;
  246. }
  247. if (error_text) {
  248. *error_text = param.outputs.error_text;
  249. }
  250. return param.outputs.returncode;
  251. }
  252. return ret;
  253. }
  254. PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer,
  255. char **textaddr, int *textaddrlen,
  256. void **addr, socklen_t *addrlen
  257. TSRMLS_DC)
  258. {
  259. php_stream_xport_param param;
  260. int ret;
  261. memset(&param, 0, sizeof(param));
  262. param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME;
  263. param.want_addr = addr ? 1 : 0;
  264. param.want_textaddr = textaddr ? 1 : 0;
  265. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  266. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  267. if (addr) {
  268. *addr = param.outputs.addr;
  269. *addrlen = param.outputs.addrlen;
  270. }
  271. if (textaddr) {
  272. *textaddr = param.outputs.textaddr;
  273. *textaddrlen = param.outputs.textaddrlen;
  274. }
  275. return param.outputs.returncode;
  276. }
  277. return ret;
  278. }
  279. PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC)
  280. {
  281. php_stream_xport_crypto_param param;
  282. int ret;
  283. memset(&param, 0, sizeof(param));
  284. param.op = STREAM_XPORT_CRYPTO_OP_SETUP;
  285. param.inputs.method = crypto_method;
  286. param.inputs.session = session_stream;
  287. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
  288. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  289. return param.outputs.returncode;
  290. }
  291. php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
  292. return ret;
  293. }
  294. PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC)
  295. {
  296. php_stream_xport_crypto_param param;
  297. int ret;
  298. memset(&param, 0, sizeof(param));
  299. param.op = STREAM_XPORT_CRYPTO_OP_ENABLE;
  300. param.inputs.activate = activate;
  301. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);
  302. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  303. return param.outputs.returncode;
  304. }
  305. php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto");
  306. return ret;
  307. }
  308. /* Similar to recv() system call; read data from the stream, optionally
  309. * peeking, optionally retrieving OOB data */
  310. PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen,
  311. long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen
  312. TSRMLS_DC)
  313. {
  314. php_stream_xport_param param;
  315. int ret = 0;
  316. int recvd_len = 0;
  317. #if 0
  318. int oob;
  319. if (flags == 0 && addr == NULL) {
  320. return php_stream_read(stream, buf, buflen);
  321. }
  322. if (stream->readfilters.head) {
  323. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream");
  324. return -1;
  325. }
  326. oob = (flags & STREAM_OOB) == STREAM_OOB;
  327. if (!oob && addr == NULL) {
  328. /* must be peeking at regular data; copy content from the buffer
  329. * first, then adjust the pointer/len before handing off to the
  330. * stream */
  331. recvd_len = stream->writepos - stream->readpos;
  332. if (recvd_len > buflen) {
  333. recvd_len = buflen;
  334. }
  335. if (recvd_len) {
  336. memcpy(buf, stream->readbuf, recvd_len);
  337. buf += recvd_len;
  338. buflen -= recvd_len;
  339. }
  340. /* if we filled their buffer, return */
  341. if (buflen == 0) {
  342. return recvd_len;
  343. }
  344. }
  345. #endif
  346. /* otherwise, we are going to bypass the buffer */
  347. memset(&param, 0, sizeof(param));
  348. param.op = STREAM_XPORT_OP_RECV;
  349. param.want_addr = addr ? 1 : 0;
  350. param.want_textaddr = textaddr ? 1 : 0;
  351. param.inputs.buf = buf;
  352. param.inputs.buflen = buflen;
  353. param.inputs.flags = flags;
  354. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  355. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  356. if (addr) {
  357. *addr = param.outputs.addr;
  358. *addrlen = param.outputs.addrlen;
  359. }
  360. if (textaddr) {
  361. *textaddr = param.outputs.textaddr;
  362. *textaddrlen = param.outputs.textaddrlen;
  363. }
  364. return recvd_len + param.outputs.returncode;
  365. }
  366. return recvd_len ? recvd_len : -1;
  367. }
  368. /* Similar to send() system call; send data to the stream, optionally
  369. * sending it as OOB data */
  370. PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen,
  371. long flags, void *addr, socklen_t addrlen TSRMLS_DC)
  372. {
  373. php_stream_xport_param param;
  374. int ret = 0;
  375. int oob;
  376. #if 0
  377. if (flags == 0 && addr == NULL) {
  378. return php_stream_write(stream, buf, buflen);
  379. }
  380. #endif
  381. oob = (flags & STREAM_OOB) == STREAM_OOB;
  382. if ((oob || addr) && stream->writefilters.head) {
  383. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream");
  384. return -1;
  385. }
  386. memset(&param, 0, sizeof(param));
  387. param.op = STREAM_XPORT_OP_SEND;
  388. param.want_addr = addr ? 1 : 0;
  389. param.inputs.buf = (char*)buf;
  390. param.inputs.buflen = buflen;
  391. param.inputs.flags = flags;
  392. param.inputs.addr = addr;
  393. param.inputs.addrlen = addrlen;
  394. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  395. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  396. return param.outputs.returncode;
  397. }
  398. return -1;
  399. }
  400. /* Similar to shutdown() system call; shut down part of a full-duplex
  401. * connection */
  402. PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC)
  403. {
  404. php_stream_xport_param param;
  405. int ret = 0;
  406. memset(&param, 0, sizeof(param));
  407. param.op = STREAM_XPORT_OP_SHUTDOWN;
  408. param.how = how;
  409. ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, &param);
  410. if (ret == PHP_STREAM_OPTION_RETURN_OK) {
  411. return param.outputs.returncode;
  412. }
  413. return -1;
  414. }
  415. /*
  416. * Local variables:
  417. * tab-width: 4
  418. * c-basic-offset: 4
  419. * End:
  420. * vim600: noet sw=4 ts=4 fdm=marker
  421. * vim<600: noet sw=4 ts=4
  422. */