sendrecvmsg.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2016 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. | Authors: Gustavo Lopes <cataphract@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include <php.h>
  19. #include "php_sockets.h"
  20. #include "sendrecvmsg.h"
  21. #include "conversions.h"
  22. #include <limits.h>
  23. #include <Zend/zend_llist.h>
  24. #ifdef ZTS
  25. #include <TSRM/TSRM.h>
  26. #endif
  27. #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
  28. #define DEFAULT_BUFF_SIZE 8192
  29. #define MAX_ARRAY_KEY_SIZE 128
  30. #ifdef PHP_WIN32
  31. #include "windows_common.h"
  32. #include <Mswsock.h>
  33. #define IPV6_RECVPKTINFO IPV6_PKTINFO
  34. #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
  35. #define msghdr _WSAMSG
  36. static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
  37. static __declspec(thread) LPFN_WSARECVMSG WSARecvMsg = NULL;
  38. inline ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
  39. {
  40. DWORD recvd = 0,
  41. bytesReturned;
  42. if (WSARecvMsg == NULL) {
  43. int res = WSAIoctl((SOCKET) sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
  44. &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
  45. &WSARecvMsg, sizeof(WSARecvMsg),
  46. &bytesReturned, NULL, NULL);
  47. if (res != 0) {
  48. return -1;
  49. }
  50. }
  51. msg->dwFlags = (DWORD)flags;
  52. return WSARecvMsg((SOCKET)sockfd, msg, &recvd, NULL, NULL) == 0
  53. ? (ssize_t)recvd
  54. : -1;
  55. }
  56. inline ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
  57. {
  58. DWORD sent = 0;
  59. return WSASendMsg((SOCKET)sockfd, (struct msghdr*)msg, (DWORD)flags, &sent, NULL, NULL) == 0
  60. ? (ssize_t)sent
  61. : -1;
  62. }
  63. #endif
  64. #define LONG_CHECK_VALID_INT(l) \
  65. do { \
  66. if ((l) < INT_MIN && (l) > INT_MAX) { \
  67. php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The value %ld does not fit inside " \
  68. "the boundaries of a native integer", (l)); \
  69. return; \
  70. } \
  71. } while (0)
  72. static struct {
  73. int initialized;
  74. HashTable ht;
  75. } ancillary_registry;
  76. #ifdef ZTS
  77. static MUTEX_T ancillary_mutex;
  78. #endif
  79. static void init_ancillary_registry(void)
  80. {
  81. ancillary_reg_entry entry;
  82. anc_reg_key key;
  83. ancillary_registry.initialized = 1;
  84. zend_hash_init(&ancillary_registry.ht, 32, NULL, NULL, 1);
  85. #define PUT_ENTRY(sizev, var_size, calc, from, to, level, type) \
  86. entry.size = sizev; \
  87. entry.var_el_size = var_size; \
  88. entry.calc_space = calc; \
  89. entry.from_array = from; \
  90. entry.to_array = to; \
  91. key.cmsg_level = level; \
  92. key.cmsg_type = type; \
  93. zend_hash_update(&ancillary_registry.ht, (char*)&key, sizeof(key), \
  94. (void*)&entry, sizeof(entry), NULL)
  95. #if defined(IPV6_PKTINFO) && HAVE_IPV6
  96. PUT_ENTRY(sizeof(struct in6_pktinfo), 0, 0, from_zval_write_in6_pktinfo,
  97. to_zval_read_in6_pktinfo, IPPROTO_IPV6, IPV6_PKTINFO);
  98. #endif
  99. #if defined(IPV6_HOPLIMIT) && HAVE_IPV6
  100. PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
  101. to_zval_read_int, IPPROTO_IPV6, IPV6_HOPLIMIT);
  102. #endif
  103. #if defined(IPV6_TCLASS) && HAVE_IPV6
  104. PUT_ENTRY(sizeof(int), 0, 0, from_zval_write_int,
  105. to_zval_read_int, IPPROTO_IPV6, IPV6_TCLASS);
  106. #endif
  107. #ifdef SO_PASSCRED
  108. PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred,
  109. to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS);
  110. #endif
  111. #ifdef SCM_RIGHTS
  112. PUT_ENTRY(0, sizeof(int), calculate_scm_rights_space, from_zval_write_fd_array,
  113. to_zval_read_fd_array, SOL_SOCKET, SCM_RIGHTS);
  114. #endif
  115. }
  116. static void destroy_ancillary_registry(void)
  117. {
  118. if (ancillary_registry.initialized) {
  119. zend_hash_destroy(&ancillary_registry.ht);
  120. ancillary_registry.initialized = 0;
  121. }
  122. }
  123. ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
  124. {
  125. anc_reg_key key = { cmsg_level, msg_type };
  126. ancillary_reg_entry *entry;
  127. #ifdef ZTS
  128. tsrm_mutex_lock(ancillary_mutex);
  129. #endif
  130. if (!ancillary_registry.initialized) {
  131. init_ancillary_registry();
  132. }
  133. #ifdef ZTS
  134. tsrm_mutex_unlock(ancillary_mutex);
  135. #endif
  136. if (zend_hash_find(&ancillary_registry.ht, (char*)&key, sizeof(key),
  137. (void**)&entry) == SUCCESS) {
  138. return entry;
  139. } else {
  140. return NULL;
  141. }
  142. }
  143. PHP_FUNCTION(socket_sendmsg)
  144. {
  145. zval *zsocket,
  146. *zmsg;
  147. long flags = 0;
  148. php_socket *php_sock;
  149. struct msghdr *msghdr;
  150. zend_llist *allocations;
  151. struct err_s err = {0};
  152. ssize_t res;
  153. /* zmsg should be passed by ref */
  154. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|l", &zsocket, &zmsg, &flags) == FAILURE) {
  155. return;
  156. }
  157. LONG_CHECK_VALID_INT(flags);
  158. ZEND_FETCH_RESOURCE(php_sock, php_socket *, &zsocket, -1,
  159. php_sockets_le_socket_name, php_sockets_le_socket());
  160. msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_send,
  161. sizeof(*msghdr), "msghdr", &allocations, &err);
  162. if (err.has_error) {
  163. err_msg_dispose(&err TSRMLS_CC);
  164. RETURN_FALSE;
  165. }
  166. res = sendmsg(php_sock->bsd_socket, msghdr, (int)flags);
  167. if (res != -1) {
  168. zend_llist_destroy(allocations);
  169. efree(allocations);
  170. RETURN_LONG((long)res);
  171. } else {
  172. PHP_SOCKET_ERROR(php_sock, "error in sendmsg", errno);
  173. RETURN_FALSE;
  174. }
  175. }
  176. PHP_FUNCTION(socket_recvmsg)
  177. {
  178. zval *zsocket,
  179. *zmsg;
  180. long flags = 0;
  181. php_socket *php_sock;
  182. ssize_t res;
  183. struct msghdr *msghdr;
  184. zend_llist *allocations;
  185. struct err_s err = {0};
  186. //ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
  187. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|l",
  188. &zsocket, &zmsg, &flags) == FAILURE) {
  189. return;
  190. }
  191. LONG_CHECK_VALID_INT(flags);
  192. ZEND_FETCH_RESOURCE(php_sock, php_socket *, &zsocket, -1,
  193. php_sockets_le_socket_name, php_sockets_le_socket());
  194. msghdr = from_zval_run_conversions(zmsg, php_sock, from_zval_write_msghdr_recv,
  195. sizeof(*msghdr), "msghdr", &allocations, &err);
  196. if (err.has_error) {
  197. err_msg_dispose(&err TSRMLS_CC);
  198. RETURN_FALSE;
  199. }
  200. res = recvmsg(php_sock->bsd_socket, msghdr, (int)flags);
  201. if (res != -1) {
  202. zval *zres;
  203. struct key_value kv[] = {
  204. {KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET), &res},
  205. {0}
  206. };
  207. zres = to_zval_run_conversions((char *)msghdr, to_zval_read_msghdr,
  208. "msghdr", kv, &err);
  209. /* we don;t need msghdr anymore; free it */
  210. msghdr = NULL;
  211. allocations_dispose(&allocations);
  212. zval_dtor(zmsg);
  213. if (!err.has_error) {
  214. ZVAL_COPY_VALUE(zmsg, zres);
  215. efree(zres); /* only shallow destruction */
  216. } else {
  217. err_msg_dispose(&err TSRMLS_CC);
  218. ZVAL_FALSE(zmsg);
  219. /* no need to destroy/free zres -- it's NULL in this circumstance */
  220. assert(zres == NULL);
  221. }
  222. } else {
  223. SOCKETS_G(last_error) = errno;
  224. php_error_docref(NULL TSRMLS_CC, E_WARNING, "error in recvmsg [%d]: %s",
  225. errno, sockets_strerror(errno TSRMLS_CC));
  226. RETURN_FALSE;
  227. }
  228. RETURN_LONG((long)res);
  229. }
  230. PHP_FUNCTION(socket_cmsg_space)
  231. {
  232. long level,
  233. type,
  234. n = 0;
  235. ancillary_reg_entry *entry;
  236. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|l",
  237. &level, &type, &n) == FAILURE) {
  238. return;
  239. }
  240. LONG_CHECK_VALID_INT(level);
  241. LONG_CHECK_VALID_INT(type);
  242. LONG_CHECK_VALID_INT(n);
  243. if (n < 0) {
  244. php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The third argument "
  245. "cannot be negative");
  246. return;
  247. }
  248. entry = get_ancillary_reg_entry(level, type);
  249. if (entry == NULL) {
  250. php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The pair level %ld/type %ld is "
  251. "not supported by PHP", level, type);
  252. return;
  253. }
  254. if (entry->var_el_size > 0 && n > (LONG_MAX - (long)entry->size -
  255. (long)CMSG_SPACE(0) - 15L) / entry->var_el_size) {
  256. /* the -15 is to account for any padding CMSG_SPACE may add after the data */
  257. php_error_docref0(NULL TSRMLS_CC, E_WARNING, "The value for the "
  258. "third argument (%ld) is too large", n);
  259. return;
  260. }
  261. RETURN_LONG((long)CMSG_SPACE(entry->size + n * entry->var_el_size));
  262. }
  263. #if HAVE_IPV6
  264. int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4 TSRMLS_DC)
  265. {
  266. struct err_s err = {0};
  267. zend_llist *allocations = NULL;
  268. void *opt_ptr;
  269. socklen_t optlen;
  270. int retval;
  271. assert(level == IPPROTO_IPV6);
  272. switch (optname) {
  273. #ifdef IPV6_PKTINFO
  274. case IPV6_PKTINFO:
  275. #ifdef PHP_WIN32
  276. if (Z_TYPE_PP(arg4) == IS_ARRAY) {
  277. php_error_docref0(NULL TSRMLS_CC, E_WARNING, "Windows does not "
  278. "support sticky IPV6_PKTINFO");
  279. return FAILURE;
  280. } else {
  281. /* windows has no IPV6_RECVPKTINFO, and uses IPV6_PKTINFO
  282. * for the same effect. We define IPV6_RECVPKTINFO to be
  283. * IPV6_PKTINFO, so assume the assume user used IPV6_RECVPKTINFO */
  284. return 1;
  285. }
  286. #endif
  287. opt_ptr = from_zval_run_conversions(*arg4, php_sock, from_zval_write_in6_pktinfo,
  288. sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
  289. if (err.has_error) {
  290. err_msg_dispose(&err TSRMLS_CC);
  291. return FAILURE;
  292. }
  293. optlen = sizeof(struct in6_pktinfo);
  294. goto dosockopt;
  295. #endif
  296. }
  297. /* we also support IPV6_TCLASS, but that can be handled by the default
  298. * integer optval handling in the caller */
  299. return 1;
  300. dosockopt:
  301. retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
  302. if (retval != 0) {
  303. PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
  304. }
  305. allocations_dispose(&allocations);
  306. return retval != 0 ? FAILURE : SUCCESS;
  307. }
  308. int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result TSRMLS_DC)
  309. {
  310. struct err_s err = {0};
  311. void *buffer;
  312. socklen_t size;
  313. int res;
  314. to_zval_read_field *reader;
  315. assert(level == IPPROTO_IPV6);
  316. switch (optname) {
  317. #ifdef IPV6_PKTINFO
  318. case IPV6_PKTINFO:
  319. size = sizeof(struct in6_pktinfo);
  320. reader = &to_zval_read_in6_pktinfo;
  321. break;
  322. #endif
  323. default:
  324. return 1;
  325. }
  326. buffer = ecalloc(1, size);
  327. res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
  328. if (res != 0) {
  329. PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
  330. } else {
  331. zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
  332. empty_key_value_list, &err);
  333. if (err.has_error) {
  334. err_msg_dispose(&err TSRMLS_CC);
  335. res = -1;
  336. } else {
  337. ZVAL_COPY_VALUE(result, zv);
  338. efree(zv);
  339. }
  340. }
  341. efree(buffer);
  342. return res == 0 ? SUCCESS : FAILURE;
  343. }
  344. #endif /* HAVE_IPV6 */
  345. void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
  346. {
  347. /* IPv6 ancillary data */
  348. #if defined(IPV6_RECVPKTINFO) && HAVE_IPV6
  349. REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_CS | CONST_PERSISTENT);
  350. REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_CS | CONST_PERSISTENT);
  351. #endif
  352. #if defined(IPV6_RECVHOPLIMIT) && HAVE_IPV6
  353. REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_CS | CONST_PERSISTENT);
  354. REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_CS | CONST_PERSISTENT);
  355. #endif
  356. /* would require some effort:
  357. REGISTER_LONG_CONSTANT("IPV6_RECVRTHDR", IPV6_RECVRTHDR, CONST_CS | CONST_PERSISTENT);
  358. REGISTER_LONG_CONSTANT("IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, CONST_CS | CONST_PERSISTENT);
  359. REGISTER_LONG_CONSTANT("IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, CONST_CS | CONST_PERSISTENT);
  360. */
  361. #if defined(IPV6_RECVTCLASS) && HAVE_IPV6
  362. REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_CS | CONST_PERSISTENT);
  363. REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_CS | CONST_PERSISTENT);
  364. #endif
  365. /*
  366. REGISTER_LONG_CONSTANT("IPV6_RTHDR", IPV6_RTHDR, CONST_CS | CONST_PERSISTENT);
  367. REGISTER_LONG_CONSTANT("IPV6_HOPOPTS", IPV6_HOPOPTS, CONST_CS | CONST_PERSISTENT);
  368. REGISTER_LONG_CONSTANT("IPV6_DSTOPTS", IPV6_DSTOPTS, CONST_CS | CONST_PERSISTENT);
  369. */
  370. #ifdef SCM_RIGHTS
  371. REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT);
  372. #endif
  373. #ifdef SO_PASSCRED
  374. REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT);
  375. REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT);
  376. #endif
  377. #ifdef ZTS
  378. ancillary_mutex = tsrm_mutex_alloc();
  379. #endif
  380. }
  381. void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS)
  382. {
  383. #ifdef ZTS
  384. tsrm_mutex_free(ancillary_mutex);
  385. #endif
  386. destroy_ancillary_registry();
  387. }