You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

478 lines
12 KiB

  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP HTML Embedded Scripting Language Version 3.0 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-1999 PHP Development Team (See Credits file) |
  6. +----------------------------------------------------------------------+
  7. | This program is free software; you can redistribute it and/or modify |
  8. | it under the terms of one of the following licenses: |
  9. | |
  10. | A) the GNU General Public License as published by the Free Software |
  11. | Foundation; either version 2 of the License, or (at your option) |
  12. | any later version. |
  13. | |
  14. | B) the PHP License as published by the PHP Development Team and |
  15. | included in the distribution in the file: LICENSE |
  16. | |
  17. | This program is distributed in the hope that it will be useful, |
  18. | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  19. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  20. | GNU General Public License for more details. |
  21. | |
  22. | You should have received a copy of both licenses referred to here. |
  23. | If you did not, or have any questions about PHP licensing, please |
  24. | contact core@php.net. |
  25. +----------------------------------------------------------------------+
  26. | Authors: Paul Panotzki - Bunyip Information Systems |
  27. | Jim Winstead (jimw@php.net) |
  28. +----------------------------------------------------------------------+
  29. */
  30. /* $Id$ */
  31. #ifdef THREAD_SAFE
  32. #include "tls.h"
  33. #endif
  34. #include "php.h"
  35. #include <stdlib.h>
  36. #if HAVE_UNISTD_H
  37. #include <unistd.h>
  38. #endif
  39. #include <sys/types.h>
  40. #if HAVE_SYS_SOCKET_H
  41. #include <sys/socket.h>
  42. #endif
  43. #ifdef WIN32
  44. #include <winsock.h>
  45. #else
  46. #include <netinet/in.h>
  47. #include <netdb.h>
  48. #include <arpa/inet.h>
  49. #endif
  50. #ifdef WIN32
  51. #undef AF_UNIX
  52. #endif
  53. #if defined(AF_UNIX)
  54. #include <sys/un.h>
  55. #endif
  56. #include <string.h>
  57. #include <errno.h>
  58. #include "base64.h"
  59. #include "file.h"
  60. #include "functions/post.h"
  61. #include "url.h"
  62. #include "fsock.h"
  63. #ifndef THREAD_SAFE
  64. extern int le_fp;
  65. #endif
  66. #define FREE_SOCK if(socketd >= 0) close(socketd); efree(sock); if (key) efree(key)
  67. #if WIN32|WINNT
  68. #define EWOULDBLOCK WSAEWOULDBLOCK
  69. #else
  70. #include "build-defs.h"
  71. #endif
  72. static unsigned char third_and_fourth_args_force_ref[] = { 4, BYREF_NONE, BYREF_NONE, BYREF_FORCE, BYREF_FORCE };
  73. function_entry fsock_functions[] = {
  74. PHP_FE(fsockopen, third_and_fourth_args_force_ref)
  75. PHP_FE(pfsockopen, third_and_fourth_args_force_ref)
  76. {NULL, NULL, NULL}
  77. };
  78. struct php3i_sockbuf {
  79. int socket;
  80. char *readbuf;
  81. size_t readbuflen;
  82. size_t readpos;
  83. size_t writepos;
  84. struct php3i_sockbuf *next;
  85. };
  86. static struct php3i_sockbuf *phpsockbuf;
  87. static int php3_minit_fsock(INIT_FUNC_ARGS);
  88. static int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS);
  89. static int php3_rshutdown_fsock(SHUTDOWN_FUNC_ARGS);
  90. php3_module_entry fsock_module_entry = {
  91. "Socket functions", fsock_functions, php3_minit_fsock, php3_mshutdown_fsock, NULL, php3_rshutdown_fsock, NULL, STANDARD_MODULE_PROPERTIES
  92. };
  93. #ifndef THREAD_SAFE
  94. static HashTable ht_keys;
  95. static HashTable ht_socks;
  96. #endif
  97. /* {{{ lookup_hostname */
  98. /*
  99. * Converts a host name to an IP address.
  100. */
  101. int lookup_hostname(const char *addr, struct in_addr *in)
  102. {
  103. struct hostent *host_info;
  104. if(!inet_aton(addr, in)) {
  105. host_info = gethostbyname(addr);
  106. if (host_info == 0) {
  107. /* Error: unknown host */
  108. return -1;
  109. }
  110. *in = *((struct in_addr *) host_info->h_addr);
  111. }
  112. return 0;
  113. }
  114. /* }}} */
  115. /* {{{ _php3_is_persistent_sock */
  116. int _php3_is_persistent_sock(int sock)
  117. {
  118. char *key;
  119. if (_php3_hash_find(&ht_socks, (char *) &sock, sizeof(sock),
  120. (void **) &key) == SUCCESS) {
  121. return 1;
  122. }
  123. return 0;
  124. }
  125. /* }}} */
  126. /* {{{ _php3_fsockopen() */
  127. /*
  128. This function takes an optional third argument which should be
  129. passed by reference. The error code from the connect call is written
  130. to this variable.
  131. */
  132. static void _php3_fsockopen(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
  133. pval *args[4];
  134. int *sock=emalloc(sizeof(int));
  135. int *sockp;
  136. int id, arg_count=ARG_COUNT(ht);
  137. int socketd = -1;
  138. unsigned short portno;
  139. char *key = NULL;
  140. TLS_VARS;
  141. if (arg_count > 4 || arg_count < 2 || getParametersArray(ht,arg_count,args)==FAILURE) {
  142. FREE_SOCK;
  143. WRONG_PARAM_COUNT;
  144. }
  145. switch(arg_count) {
  146. case 4:
  147. if(!ParameterPassedByReference(ht,4)) {
  148. php3_error(E_WARNING,"error string argument to fsockopen not passed by reference");
  149. }
  150. pval_copy_constructor(args[3]);
  151. args[3]->value.str.val = empty_string;
  152. args[3]->value.str.len = 0;
  153. args[3]->type = IS_STRING;
  154. /* fall-through */
  155. case 3:
  156. if(!ParameterPassedByReference(ht,3)) {
  157. php3_error(E_WARNING,"error argument to fsockopen not passed by reference");
  158. }
  159. args[2]->type = IS_LONG;
  160. args[2]->value.lval = 0;
  161. break;
  162. }
  163. convert_to_string(args[0]);
  164. convert_to_long(args[1]);
  165. portno = (unsigned short) args[1]->value.lval;
  166. key = emalloc(args[0]->value.str.len + 10);
  167. sprintf(key, "%s:%d", args[0]->value.str.val, portno);
  168. if (persistent && _php3_hash_find(&ht_keys, key, strlen(key) + 1,
  169. (void *) &sockp) == SUCCESS) {
  170. efree(key);
  171. *sock = *sockp;
  172. RETURN_LONG(php3_list_insert(sock, GLOBAL(wsa_fp)));
  173. }
  174. if (portno) {
  175. struct sockaddr_in server;
  176. memset(&server, 0, sizeof(server));
  177. socketd = socket(AF_INET, SOCK_STREAM, 0);
  178. if (socketd == SOCK_ERR) {
  179. FREE_SOCK;
  180. RETURN_FALSE;
  181. }
  182. server.sin_family = AF_INET;
  183. if (lookup_hostname(args[0]->value.str.val, &server.sin_addr)) {
  184. FREE_SOCK;
  185. RETURN_FALSE;
  186. }
  187. server.sin_port = htons(portno);
  188. if (connect(socketd, (struct sockaddr *)&server, sizeof(server)) == SOCK_CONN_ERR) {
  189. FREE_SOCK;
  190. if(arg_count>2) args[2]->value.lval = errno;
  191. if(arg_count>3) {
  192. args[3]->value.str.val = estrdup(strerror(errno));
  193. args[3]->value.str.len = strlen(args[3]->value.str.val);
  194. }
  195. RETURN_FALSE;
  196. }
  197. #if defined(AF_UNIX)
  198. } else {
  199. /* Unix domain socket. s->strval is socket name. */
  200. struct sockaddr_un unix_addr;
  201. socketd = socket(AF_UNIX,SOCK_STREAM,0);
  202. if (socketd == SOCK_ERR) {
  203. FREE_SOCK;
  204. RETURN_FALSE;
  205. }
  206. memset(&unix_addr,(char)0,sizeof(unix_addr));
  207. unix_addr.sun_family = AF_UNIX;
  208. strcpy(unix_addr.sun_path, args[0]->value.str.val);
  209. if (connect(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr)) == SOCK_CONN_ERR) {
  210. FREE_SOCK;
  211. if(arg_count>2) args[2]->value.lval = errno;
  212. if(arg_count>3) {
  213. args[3]->value.str.val = estrdup(strerror(errno));
  214. args[3]->value.str.len = strlen(args[3]->value.str.val);
  215. }
  216. RETURN_FALSE;
  217. }
  218. #endif /* AF_UNIX */
  219. }
  220. #if 0
  221. if ((fp = fdopen (socketd, "r+")) == NULL){
  222. RETURN_LONG(-6); /* FIXME */
  223. }
  224. #ifdef HAVE_SETVBUF
  225. if ((setvbuf(fp, NULL, _IONBF, 0)) != 0){
  226. RETURN_LONG(-7); /* FIXME */
  227. }
  228. #endif
  229. #endif
  230. *sock=socketd;
  231. if (persistent) {
  232. _php3_hash_update(&ht_keys, key, strlen(key) + 1,
  233. sock, sizeof(*sock), NULL);
  234. _php3_hash_update(&ht_socks, (char *) sock, sizeof(*sock),
  235. key, strlen(key) + 1, NULL);
  236. }
  237. if(key) efree(key);
  238. id = php3_list_insert(sock,GLOBAL(wsa_fp));
  239. RETURN_LONG(id);
  240. }
  241. /* }}} */
  242. /* {{{ proto int fsockopen(string hostname, int port [, int errno [, string errstr]])
  243. Open Internet or Unix domain socket connection */
  244. PHP_FUNCTION(fsockopen)
  245. {
  246. _php3_fsockopen(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  247. }
  248. /* }}} */
  249. /* {{{ proto int pfsockopen(string hostname, int port [, int errno [, string errstr]])
  250. Open persistent Internet or Unix domain socket connection */
  251. PHP_FUNCTION(pfsockopen)
  252. {
  253. _php3_fsockopen(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  254. }
  255. /* }}} */
  256. /* Known issues with the socket buffering code:
  257. * - does not work reliably with persistent sockets yet
  258. * (buffered data is not persistent)
  259. * - php3_fopen_url_wrapper() is still doing single-byte lookahead/read
  260. */
  261. /* {{{ _php3_sock_fgets() */
  262. int _php3_sock_fgets(char *buf, int maxlen, int socket)
  263. {
  264. struct php3i_sockbuf *sockbuf = NULL, *tmpsockbuf;
  265. int bytesread, toread, len, buflen, count = 0;
  266. char *nl;
  267. tmpsockbuf = phpsockbuf;
  268. while (tmpsockbuf) {
  269. if (tmpsockbuf->socket == socket) {
  270. sockbuf = tmpsockbuf;
  271. break;
  272. }
  273. tmpsockbuf = tmpsockbuf->next;
  274. }
  275. if (sockbuf) {
  276. toread = sockbuf->writepos - sockbuf->readpos;
  277. if (toread > maxlen) {
  278. toread = maxlen;
  279. }
  280. if ((nl = memchr(sockbuf->readbuf + sockbuf->readpos, '\n', toread)) != NULL) {
  281. toread = (nl - (sockbuf->readbuf + sockbuf->readpos)) + 1;
  282. }
  283. memcpy(buf, sockbuf->readbuf + sockbuf->readpos, toread);
  284. sockbuf->readpos += toread;
  285. count += toread;
  286. buf += toread;
  287. if (sockbuf->readpos >= sockbuf->writepos) {
  288. sockbuf->readpos = sockbuf->writepos = 0;
  289. }
  290. if (nl != NULL) {
  291. /* if a newline was found, skip the recv() loop */
  292. goto sock_fgets_exit;
  293. }
  294. }
  295. nl = NULL;
  296. buflen = 0;
  297. while (count < maxlen && nl == NULL) {
  298. toread = maxlen - count;
  299. bytesread = recv(socket, buf, toread, 0);
  300. if (bytesread <= 0) {
  301. break;
  302. }
  303. if ((nl = memchr(buf, '\n', bytesread)) != NULL) {
  304. len = (nl - buf) + 1;
  305. count += len;
  306. buf += len;
  307. if (len < bytesread) {
  308. buflen = bytesread - len;
  309. break;
  310. }
  311. } else {
  312. count += bytesread;
  313. buf += bytesread;
  314. }
  315. }
  316. if (buflen > 0) { /* there was data after the "\n" ... */
  317. if (sockbuf == NULL) {
  318. sockbuf = emalloc(sizeof(struct php3i_sockbuf));
  319. sockbuf->socket = socket;
  320. sockbuf->readbuf = emalloc(maxlen);
  321. sockbuf->readbuflen = maxlen;
  322. sockbuf->readpos = sockbuf->writepos = 0;
  323. sockbuf->next = phpsockbuf;
  324. phpsockbuf = sockbuf;
  325. } else {
  326. uint needlen = sockbuf->writepos + buflen;
  327. if (needlen > sockbuf->readbuflen) {
  328. sockbuf->readbuflen += maxlen;
  329. sockbuf->readbuf = erealloc(sockbuf->readbuf, sockbuf->readbuflen);
  330. }
  331. }
  332. memcpy(sockbuf->readbuf + sockbuf->writepos, buf, buflen);
  333. sockbuf->writepos += buflen;
  334. }
  335. sock_fgets_exit:
  336. *buf = '\0';
  337. return count;
  338. }
  339. /* }}} */
  340. /* {{{ _php3_sock_fread() */
  341. int _php3_sock_fread(char *buf, int maxlen, int socket)
  342. {
  343. struct php3i_sockbuf *sockbuf = phpsockbuf;
  344. int bytesread, toread, count = 0;
  345. while (sockbuf) {
  346. if (sockbuf->socket == socket) {
  347. toread = sockbuf->writepos - sockbuf->readpos;
  348. if (toread > maxlen) {
  349. toread = maxlen;
  350. }
  351. memcpy(buf, sockbuf->readbuf + sockbuf->readpos, toread);
  352. sockbuf->readpos += toread;
  353. count += toread;
  354. buf += toread;
  355. break;
  356. }
  357. sockbuf = sockbuf->next;
  358. }
  359. while (count < maxlen) {
  360. toread = maxlen - count;
  361. bytesread = recv(socket, buf, toread, 0);
  362. if (bytesread <= 0) {
  363. break;
  364. }
  365. count += bytesread;
  366. buf += bytesread;
  367. }
  368. *buf = '\0';
  369. return count;
  370. }
  371. /* }}} */
  372. /* {{{ module start/shutdown functions */
  373. /* {{{ _php3_sock_destroy */
  374. #ifndef THREAD_SAFE
  375. static void _php3_sock_destroy(void *data)
  376. {
  377. int *sock = (int *) data;
  378. close(*sock);
  379. }
  380. #endif
  381. /* }}} */
  382. /* {{{ php3_minit_fsock */
  383. static int php3_minit_fsock(INIT_FUNC_ARGS)
  384. {
  385. #ifndef THREAD_SAFE
  386. _php3_hash_init(&ht_keys, 0, NULL, NULL, 1);
  387. _php3_hash_init(&ht_socks, 0, NULL, _php3_sock_destroy, 1);
  388. #endif
  389. return SUCCESS;
  390. }
  391. /* }}} */
  392. /* {{{ php3_mshutdown_fsock */
  393. static int php3_mshutdown_fsock(SHUTDOWN_FUNC_ARGS)
  394. {
  395. #ifndef THREAD_SAFE
  396. _php3_hash_destroy(&ht_socks);
  397. _php3_hash_destroy(&ht_keys);
  398. #endif
  399. return SUCCESS;
  400. }
  401. /* }}} */
  402. /* {{{ php3_rshutdown_fsock() */
  403. static int php3_rshutdown_fsock(SHUTDOWN_FUNC_ARGS)
  404. {
  405. struct php3i_sockbuf *sockbuf = phpsockbuf, *this;
  406. while (sockbuf) {
  407. this = sockbuf;
  408. sockbuf = this->next;
  409. efree(this->readbuf);
  410. efree(this);
  411. }
  412. phpsockbuf = NULL;
  413. return SUCCESS;
  414. }
  415. /* }}} */
  416. /* }}} */
  417. /*
  418. * Local variables:
  419. * tab-width: 4
  420. * c-basic-offset: 4
  421. * End:
  422. */