PageRenderTime 2259ms CodeModel.GetById 72ms RepoModel.GetById 7ms app.codeStats 0ms

/src/c/tcp.d

https://gitlab.com/jlarocco/ecl
D | 414 lines | 304 code | 42 blank | 68 comment | 41 complexity | c0e1f5c30cf3459fffa9347ce7785220 MD5 | raw file
Possible License(s): LGPL-2.0, JSON
  1. /* -*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*- */
  2. /* vim: set filetype=c tabstop=8 shiftwidth=4 expandtab: */
  3. /* tcp.c -- stream interface to TCP */
  4. /*
  5. Copyright (c) 1990, Giuseppe Attardi.
  6. Copyright (c) 2001, Juan Jose Garcia Ripoll.
  7. ECL is free software; you can redistribute it and/or modify it
  8. under the terms of the GNU General Library Public License as published
  9. by the Free Software Foundation; either version 2 of the License, or
  10. (at your option) any later version.
  11. See file '../Copyright' for full details.
  12. */
  13. #include <ecl/ecl.h>
  14. #include <stdio.h>
  15. #include <sys/types.h>
  16. #include <errno.h>
  17. #if defined(ECL_MS_WINDOWS_HOST)
  18. #include <winsock.h>
  19. #else
  20. extern int errno;
  21. #include <sys/types.h>
  22. #include <sys/socket.h>
  23. #include <sys/un.h>
  24. #include <netinet/in.h>
  25. #include <arpa/inet.h>
  26. #include <netdb.h>
  27. #include <unistd.h>
  28. #endif
  29. #include <string.h>
  30. #if defined(ECL_MS_WINDOWS_HOST)
  31. #include <io.h>
  32. #else
  33. #include <sys/ioctl.h>
  34. #endif
  35. /* Maximum length for a unix socket pathname */
  36. #define UNIX_MAX_PATH 107
  37. #if defined(ECL_MS_WINDOWS_HOST)
  38. WSADATA wsadata;
  39. int wsock_initialized = 0;
  40. #define INIT_TCP \
  41. if ( !wsock_initialized ) \
  42. { \
  43. if ( WSAStartup( MAKEWORD( 2, 2 ), &wsadata ) != NO_ERROR ) \
  44. FEerror( "Unable to initialize Windows socket library.", 0 ); \
  45. else \
  46. wsock_initialized = 1; \
  47. }
  48. #else
  49. #define INIT_TCP
  50. #endif
  51. void
  52. ecl_tcp_close_all(void)
  53. {
  54. #if defined(ECL_MS_WINDOWS_HOST)
  55. if ( wsock_initialized )
  56. {
  57. WSACleanup();
  58. wsock_initialized = 0;
  59. }
  60. #endif
  61. }
  62. /***********************************************************************
  63. * Client side
  64. **********************************************************************/
  65. /*
  66. * Attempts to connect to server, given host and port. Returns file
  67. * descriptor (network socket) or 0 if connection fails.
  68. */
  69. static
  70. int connect_to_server(char *host, int port)
  71. {
  72. struct sockaddr_in inaddr; /* INET socket address. */
  73. struct sockaddr *addr; /* address to connect to */
  74. struct hostent *host_ptr;
  75. int addrlen; /* length of address */
  76. #if !defined(ECL_MS_WINDOWS_HOST)
  77. extern char *getenv();
  78. extern struct hostent *gethostbyname();
  79. #endif
  80. int fd; /* Network socket */
  81. INIT_TCP
  82. /* Get the statistics on the specified host. */
  83. if ((inaddr.sin_addr.s_addr = inet_addr(host)) == -1) {
  84. if ((host_ptr = gethostbyname(host)) == NULL) {
  85. /* No such host! */
  86. errno = EINVAL;
  87. return(0);
  88. }
  89. /* Check the address type for an internet host. */
  90. if (host_ptr->h_addrtype != AF_INET) {
  91. /* Not an Internet host! */
  92. #if defined(ECL_MS_WINDOWS_HOST)
  93. errno = WSAEPROTOTYPE;
  94. #else
  95. errno = EPROTOTYPE;
  96. #endif
  97. return(0);
  98. }
  99. /* Set up the socket data. */
  100. inaddr.sin_family = host_ptr->h_addrtype;
  101. memcpy((char *)&inaddr.sin_addr, (char *)host_ptr->h_addr,
  102. sizeof(inaddr.sin_addr));
  103. }
  104. else
  105. inaddr.sin_family = AF_INET;
  106. addr = (struct sockaddr *) &inaddr;
  107. addrlen = sizeof (struct sockaddr_in);
  108. inaddr.sin_port = port;
  109. inaddr.sin_port = htons(inaddr.sin_port);
  110. /*
  111. * Open the network connection.
  112. */
  113. if ((fd = socket((int) addr->sa_family, SOCK_STREAM, 0)) < 0)
  114. return(0); /* errno set by system call. */
  115. ecl_disable_interrupts();
  116. #ifdef TCP_NODELAY
  117. /* make sure to turn off TCP coalescence */
  118. #if defined(ECL_MS_WINDOWS_HOST)
  119. { char mi;
  120. setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof (char));
  121. }
  122. #else
  123. { int mi;
  124. setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof (int));
  125. }
  126. #endif
  127. #endif
  128. if (connect(fd, addr, addrlen) == -1) {
  129. #if defined(ECL_MS_WINDOWS_HOST)
  130. closesocket(fd);
  131. #else
  132. (void) close (fd);
  133. #endif
  134. fd = 0;
  135. }
  136. ecl_enable_interrupts();
  137. return(fd);
  138. }
  139. /***********************************************************************
  140. * Server side
  141. **********************************************************************/
  142. /*
  143. * Creates a server port. Returns file
  144. * descriptor (network socket) or 0 if connection fails.
  145. */
  146. int
  147. create_server_port(int port)
  148. {
  149. struct sockaddr_in inaddr; /* INET socket address. */
  150. struct sockaddr *addr; /* address to connect to */
  151. int addrlen; /* length of address */
  152. int request, conn; /* Network socket */
  153. INIT_TCP
  154. /*
  155. * Open the network connection.
  156. */
  157. if ((request = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  158. return(0); /* errno set by system call. */
  159. }
  160. #ifdef SO_REUSEADDR
  161. /* Necesary to restart the server without a reboot */
  162. #if defined(ECL_MS_WINDOWS_HOST)
  163. {
  164. char one = 1;
  165. setsockopt(request, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(char));
  166. }
  167. #else
  168. {
  169. int one = 1;
  170. setsockopt(request, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
  171. }
  172. #endif
  173. #endif /* SO_REUSEADDR */
  174. #ifdef TCP_NODELAY
  175. /* make sure to turn off TCP coalescence */
  176. #if defined(ECL_MS_WINDOWS_HOST)
  177. { char mi;
  178. setsockopt(request, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof (char));
  179. }
  180. #else
  181. { int mi;
  182. setsockopt(request, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof (int));
  183. }
  184. #endif
  185. #endif
  186. /* Set up the socket data. */
  187. memset((char *)&inaddr, 0, sizeof(inaddr));
  188. inaddr.sin_family = AF_INET;
  189. #if defined(ECL_MS_WINDOWS_HOST)
  190. inaddr.sin_port = htons((unsigned short)port);
  191. #else
  192. inaddr.sin_port = htons(port);
  193. #endif
  194. inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  195. if (bind(request, (struct sockaddr *)&inaddr, sizeof (inaddr)))
  196. FElibc_error("Binding TCP socket", 0);
  197. if (listen(request, 1))
  198. FElibc_error("TCP listening", 0);
  199. #if 0 && defined(ECL_THREADS)
  200. /* Don't make this file-descriptor non-blocking
  201. * just block on it before we attempt to accept from it
  202. * Think _hard_ about moving this out of here, into somewhere sane
  203. * and creating an 'accepting' stream type, which is bound to a port
  204. * on reading returns streams
  205. */
  206. {
  207. FILE *fp; /* need to use FILE *'s rather than fd... *sigh* */
  208. if ((fp = fdopen(request, "r")) == (FILE *)0)
  209. printf("fdopen didn't work on accept fd!\n"); fflush(stdout);
  210. fcntl(request, F_SETFL, O_NONBLOCK);
  211. clearerr(fp);
  212. loop: errno = 0;
  213. if ((conn = accept(request, (struct sockaddr *)NULL, (int *)NULL)) < 0)
  214. if (errno) {
  215. lwpblockon(active, fp, PD_INPUT);
  216. clearerr(fp);
  217. goto loop;
  218. } else {
  219. fclose(fp);
  220. FElibc_error("Accepting requests", 0);
  221. }
  222. fclose(fp);
  223. }
  224. #else
  225. if ((conn = accept(request, (struct sockaddr *)NULL, NULL)) < 0)
  226. FElibc_error("Accepting requests", 0);
  227. #endif /* THREADS */
  228. return(conn);
  229. }
  230. /***********************************************************************
  231. * Public interface to lisp environment
  232. **********************************************************************/
  233. /*
  234. @open-client-stream --
  235. To test this function, try:
  236. (setq s (si:open-client-stream "host" 13))
  237. (read-line s)
  238. "Wed Jun 22 19:44:36 METDST 1994"
  239. */
  240. cl_object
  241. si_open_client_stream(cl_object host, cl_object port)
  242. {
  243. int fd, p; /* file descriptor */
  244. cl_object stream;
  245. /* Ensure "host" is a string that we can pass to a C function */
  246. host = si_copy_to_simple_base_string(host);
  247. if (ecl_unlikely(!ECL_FIXNUMP(port) ||
  248. ecl_fixnum_minusp(port) ||
  249. ecl_fixnum_greater(port,ecl_make_fixnum(65536)))) {
  250. FEwrong_type_nth_arg(@[si::open-client-stream], 2, port,
  251. ecl_read_from_cstring("(INTEGER 0 65535)"));
  252. }
  253. p = ecl_fixnum(port);
  254. if (host->base_string.fillp > BUFSIZ - 1)
  255. FEerror("~S is a too long file name.", 1, host);
  256. ecl_disable_interrupts();
  257. fd = connect_to_server((char*)host->base_string.self, ecl_fixnum(port));
  258. ecl_enable_interrupts();
  259. if (fd == 0)
  260. @(return ECL_NIL)
  261. #if defined(ECL_MS_WINDOWS_HOST)
  262. stream = ecl_make_stream_from_fd(host, fd, ecl_smm_io_wsock, 8, 0, ECL_NIL);
  263. #else
  264. stream = ecl_make_stream_from_fd(host, fd, ecl_smm_io, 8, 0, ECL_NIL);
  265. #endif
  266. @(return stream)
  267. }
  268. cl_object
  269. si_open_server_stream(cl_object port)
  270. {
  271. int fd; /* file descriptor */
  272. cl_index p;
  273. if (ecl_unlikely(!ECL_FIXNUMP(port) ||
  274. ecl_fixnum_minusp(port) ||
  275. ecl_fixnum_greater(port,ecl_make_fixnum(65536)))) {
  276. FEwrong_type_only_arg(@[si::open-client-stream], port,
  277. ecl_read_from_cstring("(INTEGER 0 65535)"));
  278. }
  279. p = ecl_fixnum(port);
  280. ecl_disable_interrupts();
  281. fd = create_server_port(p);
  282. ecl_enable_interrupts();
  283. @(return ((fd == 0)? ECL_NIL : ecl_make_stream_from_fd(ECL_NIL, fd, ecl_smm_io, 8, 0, ECL_NIL)))
  284. }
  285. /************************************************************
  286. * Unix sockets *
  287. ************************************************************/
  288. cl_object
  289. si_open_unix_socket_stream(cl_object path)
  290. {
  291. #if defined(ECL_MS_WINDOWS_HOST)
  292. FEerror("UNIX socket not supported under Win32 platform", 0);
  293. #else
  294. int fd; /* file descriptor */
  295. struct sockaddr_un addr;
  296. if (ecl_unlikely(ecl_t_of(path) != t_base_string))
  297. FEwrong_type_nth_arg(@[si::open-unix-socket-stream], 1, path,
  298. @[string]);
  299. if (path->base_string.fillp > UNIX_MAX_PATH-1)
  300. FEerror("~S is a too long file name.", 1, path);
  301. fd = socket(PF_UNIX, SOCK_STREAM, 0);
  302. if (fd < 0) {
  303. FElibc_error("Unable to create unix socket", 0);
  304. @(return ECL_NIL)
  305. }
  306. memcpy(addr.sun_path, path->base_string.self, path->base_string.fillp);
  307. addr.sun_path[path->base_string.fillp] = 0;
  308. addr.sun_family = AF_UNIX;
  309. if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  310. close(fd);
  311. FElibc_error("Unable to connect to unix socket ~A", 1, path);
  312. @(return ECL_NIL)
  313. }
  314. @(return ecl_make_stream_from_fd(path, fd, ecl_smm_io, 8, 0, ECL_NIL))
  315. #endif
  316. }
  317. /************************************************************
  318. * Hostname resolution *
  319. ************************************************************/
  320. cl_object
  321. si_lookup_host_entry(cl_object host_or_address)
  322. {
  323. struct hostent *he;
  324. unsigned long l;
  325. char address[4];
  326. cl_object name, aliases, addresses;
  327. int i;
  328. INIT_TCP
  329. switch (ecl_t_of(host_or_address)) {
  330. #ifdef ECL_UNICODE
  331. case t_string:
  332. #endif
  333. case t_base_string:
  334. host_or_address = si_copy_to_simple_base_string(host_or_address);
  335. he = gethostbyname((char*)host_or_address->base_string.self);
  336. break;
  337. case t_fixnum:
  338. l = ecl_fixnum(host_or_address);
  339. goto addr;
  340. case t_bignum:
  341. l = _ecl_big_to_ulong(host_or_address);
  342. addr: address[0] = l & 0xFF;
  343. address[1] = (l >> 8) & 0xFF;
  344. address[2] = (l >> 16) & 0xFF;
  345. address[3] = (l >> 24) & 0xFF;
  346. he = gethostbyaddr(&address, 4, AF_INET);
  347. break;
  348. default:
  349. FEerror("LOOKUP-HOST-ENTRY: Number or string expected, got ~S",
  350. 1, host_or_address);
  351. }
  352. if (he == NULL)
  353. @(return ECL_NIL ECL_NIL ECL_NIL)
  354. name = make_base_string_copy(he->h_name);
  355. aliases = ECL_NIL;
  356. for (i = 0; he->h_aliases[i] != 0; i++)
  357. aliases = CONS(make_base_string_copy(he->h_aliases[i]), aliases);
  358. addresses = ECL_NIL;
  359. for (i = 0; he->h_addr_list[i]; i++) {
  360. unsigned long *s = (unsigned long*)(he->h_addr_list[i]);
  361. l = *s;
  362. addresses = CONS(ecl_make_integer(l), addresses);
  363. }
  364. @(return name aliases addresses)
  365. }