xp_socket.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Wez Furlong <wez@thebrainroom.com> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "php.h"
  17. #include "ext/standard/file.h"
  18. #include "streams/php_streams_int.h"
  19. #include "php_network.h"
  20. #if defined(PHP_WIN32) || defined(__riscos__)
  21. # undef AF_UNIX
  22. #endif
  23. #ifdef AF_UNIX
  24. #include <sys/un.h>
  25. #endif
  26. #ifndef MSG_DONTWAIT
  27. # define MSG_DONTWAIT 0
  28. #endif
  29. #ifndef MSG_PEEK
  30. # define MSG_PEEK 0
  31. #endif
  32. #ifdef PHP_WIN32
  33. /* send/recv family on windows expects int */
  34. # define XP_SOCK_BUF_SIZE(sz) (((sz) > INT_MAX) ? INT_MAX : (int)(sz))
  35. #else
  36. # define XP_SOCK_BUF_SIZE(sz) (sz)
  37. #endif
  38. const php_stream_ops php_stream_generic_socket_ops;
  39. PHPAPI const php_stream_ops php_stream_socket_ops;
  40. const php_stream_ops php_stream_udp_socket_ops;
  41. #ifdef AF_UNIX
  42. const php_stream_ops php_stream_unix_socket_ops;
  43. const php_stream_ops php_stream_unixdg_socket_ops;
  44. #endif
  45. static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam);
  46. /* {{{ Generic socket stream operations */
  47. static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t count)
  48. {
  49. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  50. ssize_t didwrite;
  51. struct timeval *ptimeout;
  52. if (!sock || sock->socket == -1) {
  53. return 0;
  54. }
  55. if (sock->timeout.tv_sec == -1)
  56. ptimeout = NULL;
  57. else
  58. ptimeout = &sock->timeout;
  59. retry:
  60. didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
  61. if (didwrite <= 0) {
  62. char *estr;
  63. int err = php_socket_errno();
  64. if (PHP_IS_TRANSIENT_ERROR(err)) {
  65. if (sock->is_blocked) {
  66. int retval;
  67. sock->timeout_event = 0;
  68. do {
  69. retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
  70. if (retval == 0) {
  71. sock->timeout_event = 1;
  72. break;
  73. }
  74. if (retval > 0) {
  75. /* writable now; retry */
  76. goto retry;
  77. }
  78. err = php_socket_errno();
  79. } while (err == EINTR);
  80. } else {
  81. /* EWOULDBLOCK/EAGAIN is not an error for a non-blocking stream.
  82. * Report zero byte write instead. */
  83. return 0;
  84. }
  85. }
  86. if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
  87. estr = php_socket_strerror(err, NULL, 0);
  88. php_error_docref(NULL, E_NOTICE,
  89. "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
  90. (zend_long)count, err, estr);
  91. efree(estr);
  92. }
  93. }
  94. if (didwrite > 0) {
  95. php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);
  96. }
  97. return didwrite;
  98. }
  99. static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock)
  100. {
  101. int retval;
  102. struct timeval *ptimeout;
  103. if (!sock || sock->socket == -1) {
  104. return;
  105. }
  106. sock->timeout_event = 0;
  107. if (sock->timeout.tv_sec == -1)
  108. ptimeout = NULL;
  109. else
  110. ptimeout = &sock->timeout;
  111. while(1) {
  112. retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
  113. if (retval == 0)
  114. sock->timeout_event = 1;
  115. if (retval >= 0)
  116. break;
  117. if (php_socket_errno() != EINTR)
  118. break;
  119. }
  120. }
  121. static ssize_t php_sockop_read(php_stream *stream, char *buf, size_t count)
  122. {
  123. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  124. ssize_t nr_bytes = 0;
  125. int err;
  126. if (!sock || sock->socket == -1) {
  127. return -1;
  128. }
  129. if (sock->is_blocked) {
  130. php_sock_stream_wait_for_data(stream, sock);
  131. if (sock->timeout_event)
  132. return -1;
  133. }
  134. nr_bytes = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0);
  135. err = php_socket_errno();
  136. if (nr_bytes < 0) {
  137. if (PHP_IS_TRANSIENT_ERROR(err)) {
  138. nr_bytes = 0;
  139. } else {
  140. stream->eof = 1;
  141. }
  142. } else if (nr_bytes == 0) {
  143. stream->eof = 1;
  144. }
  145. if (nr_bytes > 0) {
  146. php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);
  147. }
  148. return nr_bytes;
  149. }
  150. static int php_sockop_close(php_stream *stream, int close_handle)
  151. {
  152. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  153. #ifdef PHP_WIN32
  154. int n;
  155. #endif
  156. if (!sock) {
  157. return 0;
  158. }
  159. if (close_handle) {
  160. #ifdef PHP_WIN32
  161. if (sock->socket == -1)
  162. sock->socket = SOCK_ERR;
  163. #endif
  164. if (sock->socket != SOCK_ERR) {
  165. #ifdef PHP_WIN32
  166. /* prevent more data from coming in */
  167. shutdown(sock->socket, SHUT_RD);
  168. /* try to make sure that the OS sends all data before we close the connection.
  169. * Essentially, we are waiting for the socket to become writeable, which means
  170. * that all pending data has been sent.
  171. * We use a small timeout which should encourage the OS to send the data,
  172. * but at the same time avoid hanging indefinitely.
  173. * */
  174. do {
  175. n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
  176. } while (n == -1 && php_socket_errno() == EINTR);
  177. #endif
  178. closesocket(sock->socket);
  179. sock->socket = SOCK_ERR;
  180. }
  181. }
  182. pefree(sock, php_stream_is_persistent(stream));
  183. return 0;
  184. }
  185. static int php_sockop_flush(php_stream *stream)
  186. {
  187. #if 0
  188. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  189. return fsync(sock->socket);
  190. #endif
  191. return 0;
  192. }
  193. static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb)
  194. {
  195. #ifdef ZEND_WIN32
  196. return 0;
  197. #else
  198. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  199. return zend_fstat(sock->socket, &ssb->sb);
  200. #endif
  201. }
  202. static inline int sock_sendto(php_netstream_data_t *sock, const char *buf, size_t buflen, int flags,
  203. struct sockaddr *addr, socklen_t addrlen
  204. )
  205. {
  206. int ret;
  207. if (addr) {
  208. ret = sendto(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, addr, XP_SOCK_BUF_SIZE(addrlen));
  209. return (ret == SOCK_CONN_ERR) ? -1 : ret;
  210. }
  211. #ifdef PHP_WIN32
  212. return ((ret = send(sock->socket, buf, buflen > INT_MAX ? INT_MAX : (int)buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
  213. #else
  214. return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
  215. #endif
  216. }
  217. static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
  218. zend_string **textaddr,
  219. struct sockaddr **addr, socklen_t *addrlen
  220. )
  221. {
  222. int ret;
  223. int want_addr = textaddr || addr;
  224. if (want_addr) {
  225. php_sockaddr_storage sa;
  226. socklen_t sl = sizeof(sa);
  227. ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
  228. ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
  229. #ifdef PHP_WIN32
  230. /* POSIX discards excess bytes without signalling failure; emulate this on Windows */
  231. if (ret == -1 && WSAGetLastError() == WSAEMSGSIZE) {
  232. ret = buflen;
  233. }
  234. #endif
  235. if (sl) {
  236. php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
  237. textaddr, addr, addrlen);
  238. } else {
  239. if (textaddr) {
  240. *textaddr = ZSTR_EMPTY_ALLOC();
  241. }
  242. if (addr) {
  243. *addr = NULL;
  244. *addrlen = 0;
  245. }
  246. }
  247. } else {
  248. ret = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags);
  249. ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
  250. }
  251. return ret;
  252. }
  253. static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
  254. {
  255. int oldmode, flags;
  256. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  257. php_stream_xport_param *xparam;
  258. if (!sock) {
  259. return PHP_STREAM_OPTION_RETURN_NOTIMPL;
  260. }
  261. switch(option) {
  262. case PHP_STREAM_OPTION_CHECK_LIVENESS:
  263. {
  264. struct timeval tv;
  265. char buf;
  266. int alive = 1;
  267. if (value == -1) {
  268. if (sock->timeout.tv_sec == -1) {
  269. tv.tv_sec = FG(default_socket_timeout);
  270. tv.tv_usec = 0;
  271. } else {
  272. tv = sock->timeout;
  273. }
  274. } else {
  275. tv.tv_sec = value;
  276. tv.tv_usec = 0;
  277. }
  278. if (sock->socket == -1) {
  279. alive = 0;
  280. } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
  281. #ifdef PHP_WIN32
  282. int ret;
  283. #else
  284. ssize_t ret;
  285. #endif
  286. int err;
  287. ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK);
  288. err = php_socket_errno();
  289. if (0 == ret || /* the counterpart did properly shutdown*/
  290. (0 > ret && err != EWOULDBLOCK && err != EAGAIN && err != EMSGSIZE)) { /* there was an unrecoverable error */
  291. alive = 0;
  292. }
  293. }
  294. return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
  295. }
  296. case PHP_STREAM_OPTION_BLOCKING:
  297. oldmode = sock->is_blocked;
  298. if (SUCCESS == php_set_sock_blocking(sock->socket, value)) {
  299. sock->is_blocked = value;
  300. return oldmode;
  301. }
  302. return PHP_STREAM_OPTION_RETURN_ERR;
  303. case PHP_STREAM_OPTION_READ_TIMEOUT:
  304. sock->timeout = *(struct timeval*)ptrparam;
  305. sock->timeout_event = 0;
  306. return PHP_STREAM_OPTION_RETURN_OK;
  307. case PHP_STREAM_OPTION_META_DATA_API:
  308. add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
  309. add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
  310. add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
  311. return PHP_STREAM_OPTION_RETURN_OK;
  312. case PHP_STREAM_OPTION_XPORT_API:
  313. xparam = (php_stream_xport_param *)ptrparam;
  314. switch (xparam->op) {
  315. case STREAM_XPORT_OP_LISTEN:
  316. xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ? 0: -1;
  317. return PHP_STREAM_OPTION_RETURN_OK;
  318. case STREAM_XPORT_OP_GET_NAME:
  319. xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
  320. xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
  321. xparam->want_addr ? &xparam->outputs.addr : NULL,
  322. xparam->want_addr ? &xparam->outputs.addrlen : NULL
  323. );
  324. return PHP_STREAM_OPTION_RETURN_OK;
  325. case STREAM_XPORT_OP_GET_PEER_NAME:
  326. xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
  327. xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
  328. xparam->want_addr ? &xparam->outputs.addr : NULL,
  329. xparam->want_addr ? &xparam->outputs.addrlen : NULL
  330. );
  331. return PHP_STREAM_OPTION_RETURN_OK;
  332. case STREAM_XPORT_OP_SEND:
  333. flags = 0;
  334. if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
  335. flags |= MSG_OOB;
  336. }
  337. xparam->outputs.returncode = sock_sendto(sock,
  338. xparam->inputs.buf, xparam->inputs.buflen,
  339. flags,
  340. xparam->inputs.addr,
  341. xparam->inputs.addrlen);
  342. if (xparam->outputs.returncode == -1) {
  343. char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
  344. php_error_docref(NULL, E_WARNING,
  345. "%s\n", err);
  346. efree(err);
  347. }
  348. return PHP_STREAM_OPTION_RETURN_OK;
  349. case STREAM_XPORT_OP_RECV:
  350. flags = 0;
  351. if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
  352. flags |= MSG_OOB;
  353. }
  354. if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
  355. flags |= MSG_PEEK;
  356. }
  357. xparam->outputs.returncode = sock_recvfrom(sock,
  358. xparam->inputs.buf, xparam->inputs.buflen,
  359. flags,
  360. xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
  361. xparam->want_addr ? &xparam->outputs.addr : NULL,
  362. xparam->want_addr ? &xparam->outputs.addrlen : NULL
  363. );
  364. return PHP_STREAM_OPTION_RETURN_OK;
  365. #ifdef HAVE_SHUTDOWN
  366. # ifndef SHUT_RD
  367. # define SHUT_RD 0
  368. # endif
  369. # ifndef SHUT_WR
  370. # define SHUT_WR 1
  371. # endif
  372. # ifndef SHUT_RDWR
  373. # define SHUT_RDWR 2
  374. # endif
  375. case STREAM_XPORT_OP_SHUTDOWN: {
  376. static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
  377. xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
  378. return PHP_STREAM_OPTION_RETURN_OK;
  379. }
  380. #endif
  381. default:
  382. break;
  383. }
  384. }
  385. return PHP_STREAM_OPTION_RETURN_NOTIMPL;
  386. }
  387. static int php_sockop_cast(php_stream *stream, int castas, void **ret)
  388. {
  389. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  390. if (!sock) {
  391. return FAILURE;
  392. }
  393. switch(castas) {
  394. case PHP_STREAM_AS_STDIO:
  395. if (ret) {
  396. *(FILE**)ret = fdopen(sock->socket, stream->mode);
  397. if (*ret)
  398. return SUCCESS;
  399. return FAILURE;
  400. }
  401. return SUCCESS;
  402. case PHP_STREAM_AS_FD_FOR_SELECT:
  403. case PHP_STREAM_AS_FD:
  404. case PHP_STREAM_AS_SOCKETD:
  405. if (ret)
  406. *(php_socket_t *)ret = sock->socket;
  407. return SUCCESS;
  408. default:
  409. return FAILURE;
  410. }
  411. }
  412. /* }}} */
  413. /* These may look identical, but we need them this way so that
  414. * we can determine which type of socket we are dealing with
  415. * by inspecting stream->ops.
  416. * A "useful" side-effect is that the user's scripts can then
  417. * make similar decisions using stream_get_meta_data.
  418. * */
  419. const php_stream_ops php_stream_generic_socket_ops = {
  420. php_sockop_write, php_sockop_read,
  421. php_sockop_close, php_sockop_flush,
  422. "generic_socket",
  423. NULL, /* seek */
  424. php_sockop_cast,
  425. php_sockop_stat,
  426. php_sockop_set_option,
  427. };
  428. const php_stream_ops php_stream_socket_ops = {
  429. php_sockop_write, php_sockop_read,
  430. php_sockop_close, php_sockop_flush,
  431. "tcp_socket",
  432. NULL, /* seek */
  433. php_sockop_cast,
  434. php_sockop_stat,
  435. php_tcp_sockop_set_option,
  436. };
  437. const php_stream_ops php_stream_udp_socket_ops = {
  438. php_sockop_write, php_sockop_read,
  439. php_sockop_close, php_sockop_flush,
  440. "udp_socket",
  441. NULL, /* seek */
  442. php_sockop_cast,
  443. php_sockop_stat,
  444. php_tcp_sockop_set_option,
  445. };
  446. #ifdef AF_UNIX
  447. const php_stream_ops php_stream_unix_socket_ops = {
  448. php_sockop_write, php_sockop_read,
  449. php_sockop_close, php_sockop_flush,
  450. "unix_socket",
  451. NULL, /* seek */
  452. php_sockop_cast,
  453. php_sockop_stat,
  454. php_tcp_sockop_set_option,
  455. };
  456. const php_stream_ops php_stream_unixdg_socket_ops = {
  457. php_sockop_write, php_sockop_read,
  458. php_sockop_close, php_sockop_flush,
  459. "udg_socket",
  460. NULL, /* seek */
  461. php_sockop_cast,
  462. php_sockop_stat,
  463. php_tcp_sockop_set_option,
  464. };
  465. #endif
  466. /* network socket operations */
  467. #ifdef AF_UNIX
  468. static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr)
  469. {
  470. memset(unix_addr, 0, sizeof(*unix_addr));
  471. unix_addr->sun_family = AF_UNIX;
  472. /* we need to be binary safe on systems that support an abstract
  473. * namespace */
  474. if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
  475. /* On linux, when the path begins with a NUL byte we are
  476. * referring to an abstract namespace. In theory we should
  477. * allow an extra byte below, since we don't need the NULL.
  478. * BUT, to get into this branch of code, the name is too long,
  479. * so we don't care. */
  480. xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
  481. php_error_docref(NULL, E_NOTICE,
  482. "socket path exceeded the maximum allowed length of %lu bytes "
  483. "and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
  484. }
  485. memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
  486. return 1;
  487. }
  488. #endif
  489. static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err)
  490. {
  491. char *colon;
  492. char *host = NULL;
  493. #ifdef HAVE_IPV6
  494. char *p;
  495. if (*(str) == '[' && str_len > 1) {
  496. /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
  497. p = memchr(str + 1, ']', str_len - 2);
  498. if (!p || *(p + 1) != ':') {
  499. if (get_err) {
  500. *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
  501. }
  502. return NULL;
  503. }
  504. *portno = atoi(p + 2);
  505. return estrndup(str + 1, p - str - 1);
  506. }
  507. #endif
  508. if (str_len) {
  509. colon = memchr(str, ':', str_len - 1);
  510. } else {
  511. colon = NULL;
  512. }
  513. if (colon) {
  514. *portno = atoi(colon + 1);
  515. host = estrndup(str, colon - str);
  516. } else {
  517. if (get_err) {
  518. *err = strpprintf(0, "Failed to parse address \"%s\"", str);
  519. }
  520. return NULL;
  521. }
  522. return host;
  523. }
  524. static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno)
  525. {
  526. return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text);
  527. }
  528. static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
  529. php_stream_xport_param *xparam)
  530. {
  531. char *host = NULL;
  532. int portno, err;
  533. long sockopts = STREAM_SOCKOP_NONE;
  534. zval *tmpzval = NULL;
  535. #ifdef AF_UNIX
  536. if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
  537. struct sockaddr_un unix_addr;
  538. sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
  539. if (sock->socket == SOCK_ERR) {
  540. if (xparam->want_errortext) {
  541. xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s",
  542. stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
  543. strerror(errno));
  544. }
  545. return -1;
  546. }
  547. parse_unix_address(xparam, &unix_addr);
  548. return bind(sock->socket, (const struct sockaddr *)&unix_addr,
  549. (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen);
  550. }
  551. #endif
  552. host = parse_ip_address(xparam, &portno);
  553. if (host == NULL) {
  554. return -1;
  555. }
  556. #ifdef IPV6_V6ONLY
  557. if (PHP_STREAM_CONTEXT(stream)
  558. && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "ipv6_v6only")) != NULL
  559. && Z_TYPE_P(tmpzval) != IS_NULL
  560. ) {
  561. sockopts |= STREAM_SOCKOP_IPV6_V6ONLY;
  562. sockopts |= STREAM_SOCKOP_IPV6_V6ONLY_ENABLED * zend_is_true(tmpzval);
  563. }
  564. #endif
  565. #ifdef SO_REUSEPORT
  566. if (PHP_STREAM_CONTEXT(stream)
  567. && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_reuseport")) != NULL
  568. && zend_is_true(tmpzval)
  569. ) {
  570. sockopts |= STREAM_SOCKOP_SO_REUSEPORT;
  571. }
  572. #endif
  573. #ifdef SO_BROADCAST
  574. if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
  575. && PHP_STREAM_CONTEXT(stream)
  576. && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
  577. && zend_is_true(tmpzval)
  578. ) {
  579. sockopts |= STREAM_SOCKOP_SO_BROADCAST;
  580. }
  581. #endif
  582. sock->socket = php_network_bind_socket_to_local_addr(host, portno,
  583. stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
  584. sockopts,
  585. xparam->want_errortext ? &xparam->outputs.error_text : NULL,
  586. &err
  587. );
  588. if (host) {
  589. efree(host);
  590. }
  591. return sock->socket == -1 ? -1 : 0;
  592. }
  593. static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
  594. php_stream_xport_param *xparam)
  595. {
  596. char *host = NULL, *bindto = NULL;
  597. int portno, bindport = 0;
  598. int err = 0;
  599. int ret;
  600. zval *tmpzval = NULL;
  601. long sockopts = STREAM_SOCKOP_NONE;
  602. #ifdef AF_UNIX
  603. if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
  604. struct sockaddr_un unix_addr;
  605. sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
  606. if (sock->socket == SOCK_ERR) {
  607. if (xparam->want_errortext) {
  608. xparam->outputs.error_text = strpprintf(0, "Failed to create unix socket");
  609. }
  610. return -1;
  611. }
  612. parse_unix_address(xparam, &unix_addr);
  613. ret = php_network_connect_socket(sock->socket,
  614. (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
  615. xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
  616. xparam->want_errortext ? &xparam->outputs.error_text : NULL,
  617. &err);
  618. xparam->outputs.error_code = err;
  619. goto out;
  620. }
  621. #endif
  622. host = parse_ip_address(xparam, &portno);
  623. if (host == NULL) {
  624. return -1;
  625. }
  626. if (PHP_STREAM_CONTEXT(stream) && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != NULL) {
  627. if (Z_TYPE_P(tmpzval) != IS_STRING) {
  628. if (xparam->want_errortext) {
  629. xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string.");
  630. }
  631. efree(host);
  632. return -1;
  633. }
  634. bindto = parse_ip_address_ex(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);
  635. }
  636. #ifdef SO_BROADCAST
  637. if (stream->ops == &php_stream_udp_socket_ops /* SO_BROADCAST is only applicable for UDP */
  638. && PHP_STREAM_CONTEXT(stream)
  639. && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "so_broadcast")) != NULL
  640. && zend_is_true(tmpzval)
  641. ) {
  642. sockopts |= STREAM_SOCKOP_SO_BROADCAST;
  643. }
  644. #endif
  645. if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
  646. #ifdef AF_UNIX
  647. && stream->ops != &php_stream_unix_socket_ops
  648. && stream->ops != &php_stream_unixdg_socket_ops
  649. #endif
  650. && PHP_STREAM_CONTEXT(stream)
  651. && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
  652. && zend_is_true(tmpzval)
  653. ) {
  654. sockopts |= STREAM_SOCKOP_TCP_NODELAY;
  655. }
  656. /* Note: the test here for php_stream_udp_socket_ops is important, because we
  657. * want the default to be TCP sockets so that the openssl extension can
  658. * re-use this code. */
  659. sock->socket = php_network_connect_socket_to_host(host, portno,
  660. stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
  661. xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
  662. xparam->inputs.timeout,
  663. xparam->want_errortext ? &xparam->outputs.error_text : NULL,
  664. &err,
  665. bindto,
  666. bindport,
  667. sockopts
  668. );
  669. ret = sock->socket == -1 ? -1 : 0;
  670. xparam->outputs.error_code = err;
  671. if (host) {
  672. efree(host);
  673. }
  674. if (bindto) {
  675. efree(bindto);
  676. }
  677. #ifdef AF_UNIX
  678. out:
  679. #endif
  680. if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
  681. /* indicates pending connection */
  682. return 1;
  683. }
  684. return ret;
  685. }
  686. static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
  687. php_stream_xport_param *xparam STREAMS_DC)
  688. {
  689. int clisock;
  690. bool nodelay = 0;
  691. zval *tmpzval = NULL;
  692. xparam->outputs.client = NULL;
  693. if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
  694. (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
  695. zend_is_true(tmpzval)) {
  696. nodelay = 1;
  697. }
  698. clisock = php_network_accept_incoming(sock->socket,
  699. xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
  700. xparam->want_addr ? &xparam->outputs.addr : NULL,
  701. xparam->want_addr ? &xparam->outputs.addrlen : NULL,
  702. xparam->inputs.timeout,
  703. xparam->want_errortext ? &xparam->outputs.error_text : NULL,
  704. &xparam->outputs.error_code,
  705. nodelay);
  706. if (clisock >= 0) {
  707. php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
  708. memcpy(clisockdata, sock, sizeof(*clisockdata));
  709. clisockdata->socket = clisock;
  710. #ifdef __linux__
  711. /* O_NONBLOCK is not inherited on Linux */
  712. clisockdata->is_blocked = 1;
  713. #endif
  714. xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
  715. if (xparam->outputs.client) {
  716. xparam->outputs.client->ctx = stream->ctx;
  717. if (stream->ctx) {
  718. GC_ADDREF(stream->ctx);
  719. }
  720. }
  721. }
  722. return xparam->outputs.client == NULL ? -1 : 0;
  723. }
  724. static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam)
  725. {
  726. php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
  727. php_stream_xport_param *xparam;
  728. switch(option) {
  729. case PHP_STREAM_OPTION_XPORT_API:
  730. xparam = (php_stream_xport_param *)ptrparam;
  731. switch(xparam->op) {
  732. case STREAM_XPORT_OP_CONNECT:
  733. case STREAM_XPORT_OP_CONNECT_ASYNC:
  734. xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam);
  735. return PHP_STREAM_OPTION_RETURN_OK;
  736. case STREAM_XPORT_OP_BIND:
  737. xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam);
  738. return PHP_STREAM_OPTION_RETURN_OK;
  739. case STREAM_XPORT_OP_ACCEPT:
  740. xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC);
  741. return PHP_STREAM_OPTION_RETURN_OK;
  742. default:
  743. /* fall through */
  744. ;
  745. }
  746. }
  747. return php_sockop_set_option(stream, option, value, ptrparam);
  748. }
  749. PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, size_t protolen,
  750. const char *resourcename, size_t resourcenamelen,
  751. const char *persistent_id, int options, int flags,
  752. struct timeval *timeout,
  753. php_stream_context *context STREAMS_DC)
  754. {
  755. php_stream *stream = NULL;
  756. php_netstream_data_t *sock;
  757. const php_stream_ops *ops;
  758. /* which type of socket ? */
  759. if (strncmp(proto, "tcp", protolen) == 0) {
  760. ops = &php_stream_socket_ops;
  761. } else if (strncmp(proto, "udp", protolen) == 0) {
  762. ops = &php_stream_udp_socket_ops;
  763. }
  764. #ifdef AF_UNIX
  765. else if (strncmp(proto, "unix", protolen) == 0) {
  766. ops = &php_stream_unix_socket_ops;
  767. } else if (strncmp(proto, "udg", protolen) == 0) {
  768. ops = &php_stream_unixdg_socket_ops;
  769. }
  770. #endif
  771. else {
  772. /* should never happen */
  773. return NULL;
  774. }
  775. sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
  776. memset(sock, 0, sizeof(php_netstream_data_t));
  777. sock->is_blocked = 1;
  778. sock->timeout.tv_sec = FG(default_socket_timeout);
  779. sock->timeout.tv_usec = 0;
  780. /* we don't know the socket until we have determined if we are binding or
  781. * connecting */
  782. sock->socket = -1;
  783. stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
  784. if (stream == NULL) {
  785. pefree(sock, persistent_id ? 1 : 0);
  786. return NULL;
  787. }
  788. if (flags == 0) {
  789. return stream;
  790. }
  791. return stream;
  792. }