xp_socket.c 25 KB

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