312 lines
7 KiB
C
312 lines
7 KiB
C
/* af_unix.c
|
|
* (c) 2002 Mikulas Patocka
|
|
* This file is a part of the Links program, released under GPL
|
|
*/
|
|
|
|
#include "links.h"
|
|
|
|
#ifndef USE_AF_UNIX
|
|
|
|
int s_unix_fd = -1;
|
|
|
|
int bind_to_af_unix(unsigned char *name)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void af_unix_close(void)
|
|
{
|
|
}
|
|
|
|
#else
|
|
|
|
#ifdef OS2
|
|
#ifndef HAVE_SYS_UN_H
|
|
#define HAVE_SYS_UN_H
|
|
#endif
|
|
struct os2_sockaddr_un {
|
|
u_short sun_family;
|
|
char sun_path[108];
|
|
};
|
|
#define sockaddr_un os2_sockaddr_un
|
|
#ifndef AF_UNIX
|
|
#define AF_UNIX 1
|
|
#endif
|
|
#ifndef PF_UNIX
|
|
#define PF_UNIX 1
|
|
#endif
|
|
#elif defined(HAVE_SYS_UN_H)
|
|
#include <sys/un.h>
|
|
#endif
|
|
|
|
#if defined(__GNU__)
|
|
#define SOCKET_TIMEOUT_HACK
|
|
#endif
|
|
|
|
static void af_unix_connection(void *);
|
|
|
|
#define ADDR_SIZE 4096
|
|
|
|
union address {
|
|
struct sockaddr s;
|
|
#ifdef HAVE_SYS_UN_H
|
|
struct sockaddr_un suni;
|
|
#endif
|
|
struct sockaddr_in sin;
|
|
unsigned char buffer[ADDR_SIZE];
|
|
};
|
|
|
|
static union address s_unix;
|
|
static union address s_unix_acc;
|
|
|
|
static socklen_t s_unix_l;
|
|
int s_unix_fd = -1;
|
|
static int s_unix_master = 0;
|
|
|
|
|
|
#define S2C1_HANDSHAKE_LENGTH 6
|
|
#define C2S2_HANDSHAKE_LENGTH sizeof(struct links_handshake)
|
|
#define S2C3_HANDSHAKE_LENGTH sizeof(struct links_handshake)
|
|
|
|
static struct links_handshake {
|
|
unsigned char version[30];
|
|
unsigned char system_name[32];
|
|
unsigned char system_id;
|
|
unsigned char sizeof_long;
|
|
} links_handshake;
|
|
|
|
#define HANDSHAKE_WRITE(hndl, sz) \
|
|
if ((r = hard_write(hndl, (unsigned char *)&links_handshake, sz)) != (sz))
|
|
#define HANDSHAKE_READ(hndl, sz) \
|
|
if ((r = hard_read(hndl, (unsigned char *)&received_handshake, sz)) != (sz) || memcmp(&received_handshake, &links_handshake, sz))
|
|
|
|
|
|
#ifdef OS2
|
|
static inline int have_pf_unix(void)
|
|
{
|
|
s_unix_fd = c_socket(PF_UNIX, SOCK_STREAM, 0);
|
|
return s_unix_fd >= 0;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(HAVE_SYS_UN_H) || defined(OS2)
|
|
static inline int get_address_localhost(unsigned char *name)
|
|
{
|
|
unsigned short port;
|
|
if (!name) {
|
|
port = LINKS_PORT;
|
|
} else if (!strcmp(cast_const_char name, "pmshell")) {
|
|
port = LINKS_PORT + 2;
|
|
} else {
|
|
port = LINKS_PORT;
|
|
while (*name) {
|
|
port = 257 * port + (*name * 2);
|
|
name++;
|
|
}
|
|
port %= LINKS_G_PORT_LEN;
|
|
port += LINKS_G_PORT_START;
|
|
port &= ~1;
|
|
}
|
|
if (anonymous) {
|
|
port++;
|
|
}
|
|
memset(&s_unix, 0, sizeof s_unix);
|
|
s_unix.sin.sin_family = AF_INET;
|
|
s_unix.sin.sin_port = htons(port);
|
|
s_unix.sin.sin_addr.s_addr = htonl(0x7f000001);
|
|
s_unix_l = sizeof(struct sockaddr_in);
|
|
/*debug("get address %d", port);*/
|
|
return PF_INET;
|
|
}
|
|
#endif
|
|
|
|
static int get_address(unsigned char *name)
|
|
{
|
|
#ifdef HAVE_SYS_UN_H
|
|
unsigned char *path;
|
|
#ifdef OS2
|
|
if (!have_pf_unix()) {
|
|
return get_address_localhost(name);
|
|
}
|
|
path = stracpy(cast_uchar "\\socket\\links\\");
|
|
#else
|
|
if (!links_home) return -1;
|
|
path = stracpy(links_home);
|
|
#endif
|
|
add_to_strn(&path, cast_uchar LINKS_SOCK_NAME);
|
|
if (anonymous) {
|
|
add_to_strn(&path, cast_uchar LINKS_ANONYMOUS_SOCK_SUFFIX);
|
|
}
|
|
if (name) {
|
|
unsigned char *n = stracpy(name);
|
|
check_filename(&n);
|
|
add_to_strn(&path, cast_uchar "-");
|
|
add_to_strn(&path, n);
|
|
mem_free(n);
|
|
}
|
|
#ifdef OS2
|
|
s_unix_l = sizeof(struct sockaddr_un);
|
|
#else
|
|
s_unix_l = (socklen_t)((unsigned char *)&s_unix.suni.sun_path - (unsigned char *)&s_unix.suni + strlen(cast_const_char path) + 1);
|
|
#endif
|
|
if (strlen(cast_const_char path) > sizeof(union address) || (size_t)s_unix_l > sizeof(union address)) {
|
|
mem_free(path);
|
|
return -1;
|
|
}
|
|
memset(&s_unix, 0, sizeof s_unix);
|
|
s_unix.suni.sun_family = AF_UNIX;
|
|
strcpy(cast_char s_unix.suni.sun_path, cast_const_char path);
|
|
mem_free(path);
|
|
return PF_UNIX;
|
|
#else
|
|
return get_address_localhost(name);
|
|
#endif
|
|
}
|
|
|
|
static void unlink_unix(void)
|
|
{
|
|
#if defined(HAVE_SYS_UN_H) && !defined(OS2)
|
|
if (s_unix.s.sa_family == AF_UNIX) {
|
|
int rs;
|
|
/*debug("unlink: %s", s_unix.suni.sun_path);*/
|
|
EINTRLOOP(rs, unlink(s_unix.suni.sun_path));
|
|
if (rs) {
|
|
/*perror("unlink");*/
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int bind_to_af_unix(unsigned char *name)
|
|
{
|
|
int u = 0;
|
|
int a1 = 1;
|
|
int cnt = 0;
|
|
int af;
|
|
int r;
|
|
int rs;
|
|
struct links_handshake received_handshake;
|
|
memset(&links_handshake, 0, sizeof links_handshake);
|
|
safe_strncpy(links_handshake.version, cast_uchar("Links " VERSION_STRING), sizeof links_handshake.version);
|
|
safe_strncpy(links_handshake.system_name, system_name, sizeof links_handshake.system_name);
|
|
links_handshake.system_id = SYSTEM_ID;
|
|
links_handshake.sizeof_long = sizeof(long);
|
|
if ((af = get_address(name)) == -1) {
|
|
close_socket(&s_unix_fd);
|
|
return -1;
|
|
}
|
|
again:
|
|
if (s_unix_fd == -1) {
|
|
s_unix_fd = c_socket(af, SOCK_STREAM, 0);
|
|
if (s_unix_fd == -1) return -1;
|
|
}
|
|
#if defined(SOL_SOCKET) && defined(SO_REUSEADDR)
|
|
EINTRLOOP(rs, setsockopt(s_unix_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&a1, sizeof a1));
|
|
#endif
|
|
EINTRLOOP(rs, bind(s_unix_fd, &s_unix.s, s_unix_l));
|
|
if (rs) {
|
|
/*debug("bind: %d, %s", errno, strerror(errno));*/
|
|
if (af == PF_INET && errno == EADDRNOTAVAIL) {
|
|
/* do not try to connect if the user has not configured loopback interface */
|
|
EINTRLOOP(rs, close(s_unix_fd));
|
|
return -1;
|
|
}
|
|
EINTRLOOP(rs, close(s_unix_fd));
|
|
s_unix_fd = c_socket(af, SOCK_STREAM, 0);
|
|
if (s_unix_fd == -1) return -1;
|
|
#if defined(SOL_SOCKET) && defined(SO_REUSEADDR)
|
|
EINTRLOOP(rs, setsockopt(s_unix_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&a1, sizeof a1));
|
|
#endif
|
|
EINTRLOOP(rs, connect(s_unix_fd, &s_unix.s, s_unix_l));
|
|
if (rs) {
|
|
retry:
|
|
/*debug("connect: %d, %s", errno, strerror(errno));*/
|
|
if (++cnt < MAX_BIND_TRIES) {
|
|
portable_sleep(100);
|
|
EINTRLOOP(rs, close(s_unix_fd));
|
|
s_unix_fd = -1;
|
|
goto again;
|
|
}
|
|
#ifdef SOCKET_TIMEOUT_HACK
|
|
retry_unlink:
|
|
#endif
|
|
EINTRLOOP(rs, close(s_unix_fd));
|
|
s_unix_fd = -1;
|
|
if (!u) {
|
|
unlink_unix();
|
|
u = 1;
|
|
goto again;
|
|
}
|
|
return -1;
|
|
}
|
|
#ifdef SOCKET_TIMEOUT_HACK
|
|
if (!can_read_timeout(s_unix_fd, AF_UNIX_SOCKET_TIMEOUT))
|
|
goto retry_unlink;
|
|
#endif
|
|
HANDSHAKE_READ(s_unix_fd, S2C1_HANDSHAKE_LENGTH) {
|
|
if (r != S2C1_HANDSHAKE_LENGTH) goto retry;
|
|
goto close_and_fail;
|
|
}
|
|
HANDSHAKE_WRITE(s_unix_fd, C2S2_HANDSHAKE_LENGTH)
|
|
goto close_and_fail;
|
|
HANDSHAKE_READ(s_unix_fd, S2C3_HANDSHAKE_LENGTH)
|
|
goto close_and_fail;
|
|
return s_unix_fd;
|
|
}
|
|
EINTRLOOP(rs, listen(s_unix_fd, 100));
|
|
if (rs) {
|
|
error("ERROR: listen failed: %d", errno);
|
|
close_and_fail:
|
|
EINTRLOOP(rs, close(s_unix_fd));
|
|
s_unix_fd = -1;
|
|
return -1;
|
|
}
|
|
s_unix_master = 1;
|
|
set_handlers(s_unix_fd, af_unix_connection, NULL, NULL);
|
|
return -1;
|
|
}
|
|
|
|
static void af_unix_connection(void *xxx)
|
|
{
|
|
socklen_t l = s_unix_l;
|
|
int ns;
|
|
int r;
|
|
int rs;
|
|
struct links_handshake received_handshake;
|
|
memset(&s_unix_acc, 0, sizeof s_unix_acc);
|
|
ns = c_accept(s_unix_fd, &s_unix_acc.s, &l);
|
|
if (ns == -1) return;
|
|
HANDSHAKE_WRITE(ns, S2C1_HANDSHAKE_LENGTH) {
|
|
EINTRLOOP(rs, close(ns));
|
|
return;
|
|
}
|
|
HANDSHAKE_READ(ns, C2S2_HANDSHAKE_LENGTH) {
|
|
portable_sleep(100); /* workaround for a race in previous Links version */
|
|
EINTRLOOP(rs, close(ns));
|
|
return;
|
|
}
|
|
HANDSHAKE_WRITE(ns, S2C3_HANDSHAKE_LENGTH) {
|
|
EINTRLOOP(rs, close(ns));
|
|
return;
|
|
}
|
|
if (!F) {
|
|
init_term(ns, ns, win_func);
|
|
set_highpri();
|
|
}
|
|
#ifdef G
|
|
else {
|
|
gfx_connection(ns);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void af_unix_close(void)
|
|
{
|
|
close_socket(&s_unix_fd);
|
|
if (s_unix_master) {
|
|
unlink_unix();
|
|
s_unix_master = 0;
|
|
}
|
|
}
|
|
|
|
#endif
|