sendrecvmsg.c 13 KB

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