transports.c 14 KB

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