A more nuanced approach to Python warnings.
[elinks.git] / src / main / interlink.c
blobb535c022694023f6645acca2110b2fc76d5a09bf
1 /* Inter-instances internal communication socket interface */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
11 #ifdef HAVE_UNISTD_H
12 #include <unistd.h>
13 #endif
15 /* struct timeval */
16 #ifdef HAVE_SYS_TIME_H
17 #include <sys/time.h>
18 #endif
19 #ifdef HAVE_TIME_H
20 #include <time.h>
21 #endif
23 /* Blame BSD for position of this includes. */
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
26 #endif
27 #ifdef HAVE_SYS_SELECT_H
28 #include <sys/select.h>
29 #endif
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h> /* OS/2 needs this after sys/types.h */
32 #endif
34 #ifdef HAVE_SYS_UN_H
35 #include <sys/un.h>
36 #endif
38 #ifdef HAVE_ARPA_INET_H
39 #include <arpa/inet.h>
40 #endif
42 #include "elinks.h"
44 #include "config/home.h"
45 #include "intl/gettext/libintl.h"
46 #include "main/interlink.h"
47 #include "main/select.h"
48 #include "osdep/osdep.h"
49 #include "util/conv.h"
50 #include "util/error.h"
51 #include "util/memory.h"
52 #include "util/string.h"
54 /* Testing purpose. Do not remove. */
55 /* At some point zas added experimental support for interlinking
56 * ELinks' over a TCP socket. However, it was removed due to some
57 * unsettled security related questions. More info in:
59 * src/config/options.c (1.233 -> 1.234)
60 * src/network/interlink.c (1.54 -> 1.55)
62 * The log message:
64 * Experimental remote mode is now (almost) functionnal. To test define
65 * ELINKS_REMOTE in setup.h, compile elinks, add a second ip to your
66 * machine or use a second machine, then launch elinks -listen
67 * <your_first_ip> on one side, and elinks -remote <your_first_ip> on the
68 * second side. There is many things to improve, and it may not function as
69 * you hope it does, but feel free to provide enhancements. Note this
70 * feature may disappear soon if none is interested in. Please report
71 * issues/comments/ideas/bugs on #elinks.
74 /* Set to 1 to make ELinks use a tcp socket bound to loopback address with
75 * port defined by ELINKS_PORT (setup.h) for internal communications
76 * instead of a socket file, which is the preferred way. */
77 #if 0
78 #define CONFIG_TCP_INTERLINK
79 #endif
81 /* Common to both AF_UNIX and AF_INET stuff. */
82 struct socket_info {
83 struct sockaddr *addr;
84 int size;
85 int fd;
88 /* Accepted socket info */
89 static struct socket_info s_info_accept;
91 /* Listening socket info */
92 static struct socket_info s_info_listen;
94 /* Connect socket info */
95 static struct socket_info s_info_connect;
97 /* Type of address requested (for get_address()) */
98 enum addr_type {
99 ADDR_IP_CLIENT,
100 ADDR_IP_SERVER,
101 ADDR_ANY_SERVER,
105 #ifndef CONFIG_TCP_INTERLINK
107 /*** Unix file socket for internal communication. ***/
109 /* Compute socket file path and allocate space for it.
110 * It returns 0 on error (in this case, there's no need
111 * to free anything).
112 * It returns 1 on success. */
113 static int
114 get_sun_path(struct string *sun_path)
116 assert(sun_path);
117 if_assert_failed return 0;
119 if (!elinks_home) return 0;
121 if (!init_string(sun_path)) return 0;
123 add_to_string(sun_path, elinks_home);
124 add_to_string(sun_path, ELINKS_SOCK_NAME);
125 add_long_to_string(sun_path,
126 get_cmd_opt_int("session-ring"));
128 return 1;
131 /* @type is ignored here => always local. */
132 static int
133 get_address(struct socket_info *info, enum addr_type type)
135 struct sockaddr_un *addr = NULL;
136 int sun_path_freespace;
137 struct string path;
139 assert(info);
140 if_assert_failed return -1;
142 if (!get_sun_path(&path)) return -1;
144 /* Linux defines that as:
145 * #define UNIX_PATH_MAX 108
146 * struct sockaddr_un {
147 * sa_family_t sun_family;
148 * char sun_path[UNIX_PATH_MAX];
149 * };
151 * UNIX_PATH_MAX is not defined on all systems, so we'll use sizeof().
154 /* Following code may need to be changed if at some time the
155 * struct sockaddr_un mess is fixed. For now, i tried to make it
156 * sure and portable.
158 * Extract from glibc documentation:
159 * char sun_path[108]
160 * This is the file name to use.
161 * Incomplete: Why is 108 a magic number?
162 * RMS suggests making this a zero-length array and tweaking the example
163 * following to use alloca to allocate an appropriate amount of storage
164 * based on the length of the filename.
166 * But at this day (2003/06/18) it seems there's no implementation of such
167 * thing.
168 * If it was the case, then following code will always generate an error.
169 * --Zas
172 sun_path_freespace = sizeof(addr->sun_path) - (path.length + 1);
173 if (sun_path_freespace < 0) {
174 INTERNAL("Socket path name '%s' is too long: %d >= %zu",
175 path.source, path.length, sizeof(addr->sun_path));
176 goto free_and_error;
179 addr = mem_calloc(1, sizeof(*addr));
180 if (!addr) goto free_and_error;
182 memcpy(addr->sun_path, path.source, path.length); /* ending '\0' is done by calloc() */
183 done_string(&path);
185 addr->sun_family = AF_UNIX;
187 info->addr = (struct sockaddr *) addr;
188 /* The size of the address is the offset of the start of the filename,
189 * plus its length, plus one for the terminating null byte (well, this
190 * last byte may or not be needed it depends of....).
191 * Alternatively we can use SUN_LEN() macro but this one is not always
192 * defined nor always defined in the same way. --Zas */
193 info->size = sizeof(*addr) - sun_path_freespace;
195 return AF_UNIX;
197 free_and_error:
198 done_string(&path);
199 mem_free_if(addr);
201 return -1;
204 static int
205 alloc_address(struct socket_info *info)
207 struct sockaddr_un *sa;
209 assert(info);
210 if_assert_failed return 0;
212 /* calloc() is safer there. */
213 sa = mem_calloc(1, sizeof(*sa));
214 if (!sa) return 0;
216 info->addr = (struct sockaddr *) sa;
217 info->size = sizeof(*sa);
219 return 1;
222 static void
223 unlink_unix(struct sockaddr *addr)
225 assert(addr);
226 if_assert_failed return;
228 unlink(((struct sockaddr_un *) addr)->sun_path);
231 #define setsock_reuse_addr(fd)
234 #else /* CONFIG_TCP_INTERLINK */
236 /*** TCP socket for internal communication. ***/
237 /* FIXME: IPv6 support. */
239 /* These may not be defined in netinet/in.h on some systems. */
240 #ifndef INADDR_LOOPBACK
241 #define INADDR_LOOPBACK ((struct in_addr) 0x7f000001)
242 #endif
244 #ifndef IPPORT_USERRESERVED
245 #define IPPORT_USERRESERVED 5000
246 #endif
248 /* @type is not used for now, and is ignored, it will
249 * be used in remote mode feature. */
250 static int
251 get_address(struct socket_info *info, enum addr_type type)
253 struct sockaddr_in *sin;
254 uint16_t port;
256 assert(info);
257 if_assert_failed return -1;
259 /* Each ring is bind to ELINKS_PORT + ring number. */
260 port = ELINKS_PORT + get_cmd_opt_int("session-ring");
261 if (port < IPPORT_USERRESERVED)
262 return -1; /* Just in case of... */
264 sin = mem_calloc(1, sizeof(*sin));
265 if (!sin) return -1;
267 sin->sin_family = AF_INET;
268 sin->sin_port = htons(port);
269 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
271 info->addr = (struct sockaddr *) sin;
272 info->size = sizeof(*sin);
274 return PF_INET;
277 static int
278 alloc_address(struct socket_info *info)
280 struct sockaddr_in *sa;
282 assert(info);
283 if_assert_failed return 0;
285 /* calloc() is safer there. */
286 sa = mem_calloc(1, sizeof(*sa));
287 if (!sa) return 0;
289 info->addr = (struct sockaddr *) sa;
290 info->size = sizeof(*sa);
292 return 1;
295 #if defined(SOL_SOCKET) && defined(SO_REUSEADDR)
296 static void
297 setsock_reuse_addr(int fd)
299 int reuse_addr = 1;
301 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
302 (void *) &reuse_addr, sizeof(reuse_addr));
304 #else
305 #define setsock_reuse_addr(fd)
306 #endif
308 #define unlink_unix(s)
310 #endif /* CONFIG_TCP_INTERLINK */
312 /* Max. number of bind attempts. */
313 #define MAX_BIND_TRIES 3
314 /* Base delay in useconds between bind attempts.
315 * We will sleep that time on first attempt, then
316 * 2 * delay, then 3 * delay .... */
317 #define BIND_TRIES_DELAY 100000
318 /* Number of connections in listen backlog. */
319 #define LISTEN_BACKLOG 100
321 /* Max. number of connect attempts. */
322 #define MAX_CONNECT_TRIES 3
323 /* Base delay in useconds between connect attempts. */
324 #define CONNECT_TRIES_DELAY 50000
326 static void
327 report_af_unix_error(unsigned char *function, int error)
329 ERROR(gettext("The call to %s failed: %d (%s)"),
330 function, error, (unsigned char *) strerror(error));
333 /* Called when we receive a connection on listening socket. */
334 static void
335 af_unix_connection(struct socket_info *info)
337 int ns;
338 int l;
340 assert(info);
341 if_assert_failed return;
343 l = info->size;
345 memset(info->addr, 0, l);
346 ns = accept(info->fd, info->addr, &l);
347 if (ns < 0) {
348 report_af_unix_error("accept()", errno);
349 return;
352 init_term(ns, ns);
354 set_highpri();
357 /* usleep() is not portable, so we use this replacement.
358 * TODO: move it to somewhere. */
359 void
360 elinks_usleep(unsigned long useconds)
362 struct timeval delay;
363 fd_set dummy;
365 FD_ZERO(&dummy);
367 delay.tv_sec = 0;
368 delay.tv_usec = useconds;
369 select(0, &dummy, &dummy, &dummy, &delay);
372 /* Listen on socket for internal ELinks communication.
373 * Returns -1 on error
374 * or listened file descriptor on success. */
375 static int
376 bind_to_af_unix(void)
378 mode_t saved_mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
379 int attempts = 0;
380 int pf = get_address(&s_info_listen, ADDR_IP_SERVER);
382 if (pf == -1) goto free_and_error;
384 while (1) {
385 s_info_listen.fd = socket(pf, SOCK_STREAM, 0);
386 if (s_info_listen.fd == -1) {
387 report_af_unix_error("socket()", errno);
388 goto free_and_error;
391 setsock_reuse_addr(s_info_listen.fd);
393 if (bind(s_info_listen.fd, s_info_listen.addr, s_info_listen.size) >= 0)
394 break;
396 if (errno != EADDRINUSE)
397 report_af_unix_error("bind()", errno);
399 ++attempts;
401 if (attempts == MAX_BIND_TRIES)
402 unlink_unix(s_info_listen.addr);
404 if (attempts > MAX_BIND_TRIES)
405 goto free_and_error;
407 elinks_usleep(BIND_TRIES_DELAY * attempts);
408 close(s_info_listen.fd);
411 /* Listen and accept. */
412 if (!alloc_address(&s_info_accept))
413 goto free_and_error;
415 s_info_accept.fd = s_info_listen.fd;
417 if (listen(s_info_listen.fd, LISTEN_BACKLOG)) {
418 report_af_unix_error("listen()", errno);
419 goto free_and_error;
422 set_handlers(s_info_listen.fd, (void (*)(void *)) af_unix_connection,
423 NULL, NULL, &s_info_accept);
425 umask(saved_mask);
426 return s_info_listen.fd;
428 free_and_error:
429 done_interlink();
430 umask(saved_mask);
432 return -1;
435 /* Connect to an listening socket for internal ELinks communication.
436 * Returns -1 on error
437 * or file descriptor on success. */
438 static int
439 connect_to_af_unix(void)
441 int attempts = 0;
442 int pf = get_address(&s_info_connect, ADDR_IP_CLIENT);
444 while (pf != -1 && attempts++ < MAX_CONNECT_TRIES) {
445 int saved_errno;
447 s_info_connect.fd = socket(pf, SOCK_STREAM, 0);
448 if (s_info_connect.fd == -1) {
449 report_af_unix_error("socket()", errno);
450 break;
453 if (connect(s_info_connect.fd, s_info_connect.addr,
454 s_info_connect.size) >= 0)
455 return s_info_connect.fd;
457 saved_errno = errno;
458 close(s_info_connect.fd);
460 if (saved_errno != ECONNREFUSED && saved_errno != ENOENT) {
461 report_af_unix_error("connect()", errno);
462 break;
465 elinks_usleep(CONNECT_TRIES_DELAY * attempts);
468 mem_free_set(&s_info_connect.addr, NULL);
469 return -1;
472 static void safe_close(int *fd) {
473 if (*fd == -1) return;
474 close(*fd);
475 *fd = -1;
478 /* Free all allocated memory and close all descriptors if
479 * needed. */
480 void
481 done_interlink(void)
483 /* We test for addr != NULL since
484 * if it was not allocated then fd is not
485 * initialized and we don't want to close
486 * fd 0 ;). --Zas */
487 if (s_info_listen.addr) {
488 safe_close(&s_info_listen.fd);
489 unlink_unix(s_info_listen.addr);
490 mem_free(s_info_listen.addr);
491 s_info_listen.addr = NULL;
494 if (s_info_connect.addr) {
495 safe_close(&s_info_connect.fd);
496 mem_free(s_info_connect.addr);
497 s_info_connect.addr = NULL;
500 if (s_info_accept.addr) {
501 safe_close(&s_info_accept.fd);
502 mem_free(s_info_accept.addr);
503 s_info_accept.addr = NULL;
507 /* Initialize sockets for internal ELinks communication.
508 * If connect succeeds it returns file descriptor,
509 * else it tries to bind and listen on a socket, and
510 * return -1
513 init_interlink(void)
515 int fd = connect_to_af_unix();
517 if (fd != -1) return fd;
519 bind_to_af_unix();
520 return -1;
524 #undef MAX_BIND_TRIES
525 #undef BIND_TRIES_DELAY
526 #undef LISTEN_BACKLOG
527 #undef MAX_CONNECT_TRIES
528 #undef CONNECT_TRIES_DELAY