mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 23:42:34 +00:00
905 lines
28 KiB
C
905 lines
28 KiB
C
/* source: xio-unix.c */
|
|
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
/* this file contains the source for opening addresses of UNIX socket type */
|
|
|
|
#include "xiosysincludes.h"
|
|
#include "xioopen.h"
|
|
|
|
#include "xio-socket.h"
|
|
#include "xio-listen.h"
|
|
#include "xio-unix.h"
|
|
#include "xio-named.h"
|
|
|
|
|
|
#if WITH_UNIX
|
|
|
|
/* to avoid unneccessary runtime if () conditionals when no abstract support is
|
|
compiled in (or at least to give optimizing compilers a good chance) we need
|
|
a constant that can be used in C expressions */
|
|
#if WITH_ABSTRACT_UNIXSOCKET
|
|
# define ABSTRACT 1
|
|
#else
|
|
# define ABSTRACT 0
|
|
#endif
|
|
|
|
static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
static int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
static int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
static int xioopen_unix_client(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
|
|
|
|
/* the first free parameter is 0 for "normal" unix domain sockets, or 1 for
|
|
abstract unix sockets (Linux); the second and third free parameter are
|
|
unsused */
|
|
const struct addrdesc xioaddr_unix_connect = { "UNIX-CONNECT", 1+XIO_RDWR, xioopen_unix_connect, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 0, 0, 0 HELP(":<filename>") };
|
|
#if WITH_LISTEN
|
|
const struct addrdesc xioaddr_unix_listen = { "UNIX-LISTEN", 1+XIO_RDWR, xioopen_unix_listen, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_LISTEN|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<filename>") };
|
|
#endif /* WITH_LISTEN */
|
|
const struct addrdesc xioaddr_unix_sendto = { "UNIX-SENDTO", 1+XIO_RDWR, xioopen_unix_sendto, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 0, 0, 0 HELP(":<filename>") };
|
|
const struct addrdesc xioaddr_unix_recvfrom= { "UNIX-RECVFROM", 1+XIO_RDWR, xioopen_unix_recvfrom, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY|GROUP_CHILD, 0, 0, 0 HELP(":<filename>") };
|
|
const struct addrdesc xioaddr_unix_recv = { "UNIX-RECV", 1+XIO_RDONLY, xioopen_unix_recv, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 0, 0, 0 HELP(":<filename>") };
|
|
const struct addrdesc xioaddr_unix_client = { "UNIX-CLIENT", 1+XIO_RDWR, xioopen_unix_client, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 0, 0, 0 HELP(":<filename>") };
|
|
#if WITH_ABSTRACT_UNIXSOCKET
|
|
const struct addrdesc xioaddr_abstract_connect = { "ABSTRACT-CONNECT", 1+XIO_RDWR, xioopen_unix_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
|
|
#if WITH_LISTEN
|
|
const struct addrdesc xioaddr_abstract_listen = { "ABSTRACT-LISTEN", 1+XIO_RDWR, xioopen_unix_listen, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_LISTEN|GROUP_CHILD|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
|
|
#endif /* WITH_LISTEN */
|
|
const struct addrdesc xioaddr_abstract_sendto = { "ABSTRACT-SENDTO", 1+XIO_RDWR, xioopen_unix_sendto, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
|
|
const struct addrdesc xioaddr_abstract_recvfrom= { "ABSTRACT-RECVFROM", 1+XIO_RDWR, xioopen_unix_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY|GROUP_CHILD, 1, 0, 0 HELP(":<filename>") };
|
|
const struct addrdesc xioaddr_abstract_recv = { "ABSTRACT-RECV", 1+XIO_RDONLY, xioopen_unix_recv, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
|
|
const struct addrdesc xioaddr_abstract_client = { "ABSTRACT-CLIENT", 1+XIO_RDWR, xioopen_unix_client, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
|
|
#endif /* WITH_ABSTRACT_UNIXSOCKET */
|
|
|
|
const struct optdesc xioopt_unix_bind_tempname = { "unix-bind-tempname", "bind-tempname", OPT_UNIX_BIND_TEMPNAME, GROUP_SOCK_UNIX, PH_PREOPEN, TYPE_STRING_NULL, OFUNC_SPEC };
|
|
const struct optdesc xioopt_unix_tightsocklen = { "unix-tightsocklen", "tightsocklen", OPT_UNIX_TIGHTSOCKLEN, GROUP_SOCK_UNIX, PH_PREBIND, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.un.tight), XIO_SIZEOF(para.socket.un.tight) };
|
|
|
|
|
|
/* fills the socket address struct and returns its effective length.
|
|
abstract is usually 0; != 0 generates an abstract socket address on Linux.
|
|
tight!=0 calculates the resulting length from the path length, not from the
|
|
structures length; this is more common (see option unix-tightsocklen)
|
|
the struct need not be initialized when calling this function.
|
|
*/
|
|
socklen_t
|
|
xiosetunix(int pf,
|
|
struct sockaddr_un *saun,
|
|
const char *path,
|
|
bool abstract,
|
|
bool tight) {
|
|
size_t pathlen;
|
|
socklen_t len;
|
|
|
|
socket_un_init(saun);
|
|
#ifdef WITH_ABSTRACT_UNIXSOCKET
|
|
if (abstract) {
|
|
if ((pathlen = strlen(path)) >= sizeof(saun->sun_path)) {
|
|
Warn2("socket address "F_Zu" characters long, truncating to "F_Zu"",
|
|
pathlen+1, sizeof(saun->sun_path));
|
|
}
|
|
saun->sun_path[0] = '\0'; /* so it's abstract */
|
|
strncpy(saun->sun_path+1, path, sizeof(saun->sun_path)-1); /* ok */
|
|
if (tight) {
|
|
len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+
|
|
MIN(pathlen+1, sizeof(saun->sun_path));
|
|
#if HAVE_STRUCT_SOCKADDR_SALEN
|
|
saun->sun_len = len;
|
|
#endif
|
|
} else {
|
|
len = sizeof(struct sockaddr_un);
|
|
}
|
|
return len;
|
|
} else
|
|
#endif /* WITH_ABSTRACT_UNIXSOCKET */
|
|
|
|
if ((pathlen = strlen(path)) > sizeof(saun->sun_path)) {
|
|
Warn2("unix socket address "F_Zu" characters long, truncating to "F_Zu"",
|
|
pathlen, sizeof(saun->sun_path));
|
|
}
|
|
strncpy(saun->sun_path, path, sizeof(saun->sun_path)); /* ok */
|
|
if (tight) {
|
|
len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+
|
|
MIN(pathlen, sizeof(saun->sun_path));
|
|
#if HAVE_STRUCT_SOCKADDR_SALEN
|
|
saun->sun_len = len;
|
|
#endif
|
|
} else {
|
|
len = sizeof(struct sockaddr_un);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
#if WITH_LISTEN
|
|
static int xioopen_unix_listen(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
/* we expect the form: filename */
|
|
const char *name;
|
|
xiosingle_t *sfd = &xxfd->stream;
|
|
int pf = PF_UNIX;
|
|
int socktype = SOCK_STREAM;
|
|
int protocol = 0;
|
|
struct sockaddr_un us;
|
|
socklen_t uslen;
|
|
struct opt *opts0 = NULL;
|
|
pid_t pid = Getpid();
|
|
bool opt_unlink_early = false;
|
|
bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
|
|
int result;
|
|
|
|
if (argc != 2) {
|
|
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
name = argv[1];
|
|
|
|
sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
|
|
retropt_socket_pf(opts, &pf);
|
|
sfd->howtoend = END_SHUTDOWN;
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
/* only for non abstract because abstract do not work in file system */
|
|
retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
|
|
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
|
|
}
|
|
|
|
if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
|
|
applyopts(sfd, -1, opts, PH_INIT);
|
|
applyopts_named(name, opts, PH_EARLY); /* umask! */
|
|
applyopts_offset(sfd, opts);
|
|
applyopts(sfd, -1, opts, PH_EARLY);
|
|
|
|
uslen = xiosetunix(pf, &us, name, addrdesc->arg1/*abstract*/,
|
|
sfd->para.socket.un.tight);
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
if (opt_unlink_early) {
|
|
xio_unlink(name, E_ERROR);
|
|
} else {
|
|
struct stat buf;
|
|
if (Lstat(name, &buf) == 0) {
|
|
Error1("\"%s\" exists", name);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
}
|
|
if (opt_unlink_close) {
|
|
if ((sfd->unlink_close = strdup(name)) == NULL) {
|
|
Error1("strdup(\"%s\"): out of memory", name);
|
|
}
|
|
sfd->opt_unlink_close = true;
|
|
}
|
|
|
|
/* trying to set user-early, perm-early etc. here is useless because
|
|
file system entry is available only past bind() call. */
|
|
}
|
|
|
|
opts0 = copyopts(opts, GROUP_ALL);
|
|
|
|
/* this may fork() */
|
|
if ((result =
|
|
xioopen_listen(sfd, xioflags,
|
|
(struct sockaddr *)&us, uslen,
|
|
opts, opts0, pf, socktype, protocol))
|
|
!= 0)
|
|
return result;
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
if (opt_unlink_close) {
|
|
if (pid != Getpid()) {
|
|
/* in a child process - do not unlink-close here! */
|
|
sfd->opt_unlink_close = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WITH_LISTEN */
|
|
|
|
|
|
static int xioopen_unix_connect(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
/* we expect the form: filename */
|
|
const char *name;
|
|
struct single *sfd = &xxfd->stream;
|
|
const struct opt *namedopt;
|
|
int pf = PF_UNIX;
|
|
int socktype = SOCK_STREAM;
|
|
int protocol = 0;
|
|
struct sockaddr_un them, us;
|
|
socklen_t themlen, uslen = sizeof(us);
|
|
bool needbind = false;
|
|
bool needtemp = false;
|
|
bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
|
|
bool dofork = false;
|
|
struct opt *opts0;
|
|
char infobuff[256];
|
|
int level;
|
|
char *opt_bind_tempname = NULL;
|
|
int result;
|
|
|
|
if (argc != 2) {
|
|
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
name = argv[1];
|
|
|
|
sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
|
|
retropt_socket_pf(opts, &pf);
|
|
sfd->howtoend = END_SHUTDOWN;
|
|
if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
|
|
applyopts(sfd, -1, opts, PH_INIT);
|
|
applyopts_offset(sfd, opts);
|
|
applyopts(sfd, -1, opts, PH_EARLY);
|
|
|
|
themlen = xiosetunix(pf, &them, name, addrdesc->arg1/*abstract*/,
|
|
sfd->para.socket.un.tight);
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
/* Only for non abstract because abstract do not work in file system */
|
|
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
|
|
}
|
|
|
|
if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
|
|
(addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight,
|
|
sfd->para.socket.ip.ai_flags)
|
|
== STAT_OK) {
|
|
needbind = true;
|
|
}
|
|
|
|
if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) {
|
|
if (needbind) {
|
|
Error("do not use both options bind and unix-bind-tempnam");
|
|
return -1;
|
|
}
|
|
needbind = true;
|
|
needtemp = true;
|
|
xiosetunix(pf, &us, opt_bind_tempname?opt_bind_tempname:"",
|
|
addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight);
|
|
if (opt_bind_tempname == NULL && !addrdesc->arg1/*abstract*/) {
|
|
us.sun_path[0] = 0x01; /* mark as non abstract */
|
|
}
|
|
}
|
|
|
|
if (!needbind &&
|
|
(namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
|
|
Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
|
|
}
|
|
|
|
retropt_bool(opts, OPT_FORK, &dofork);
|
|
|
|
opts0 = copyopts(opts, GROUP_ALL);
|
|
|
|
Notice1("opening connection to %s",
|
|
sockaddr_info((struct sockaddr *)&them, themlen, infobuff, sizeof(infobuff)));
|
|
|
|
do { /* loop over retries and forks */
|
|
|
|
#if WITH_RETRY
|
|
if (sfd->forever || sfd->retry) {
|
|
level = E_INFO;
|
|
} else
|
|
#endif /* WITH_RETRY */
|
|
level = E_ERROR;
|
|
|
|
result =
|
|
_xioopen_connect(sfd,
|
|
needbind?(union sockaddr_union *)&us:NULL, uslen,
|
|
(struct sockaddr *)&them, themlen,
|
|
opts, pf, socktype, protocol, needtemp, level);
|
|
if (result != 0) {
|
|
char infobuff[256];
|
|
/* we caller must handle this */
|
|
Msg3(level, "connect(, %s, "F_socklen"): %s",
|
|
sockaddr_info((struct sockaddr *)&them, themlen, infobuff, sizeof(infobuff)),
|
|
themlen, strerror(errno));
|
|
}
|
|
switch (result) {
|
|
case STAT_OK: break;
|
|
#if WITH_RETRY
|
|
case STAT_RETRYLATER:
|
|
if (sfd->forever || sfd->retry) {
|
|
--sfd->retry;
|
|
if (result == STAT_RETRYLATER) {
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
}
|
|
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
|
|
continue;
|
|
}
|
|
return STAT_NORETRY;
|
|
#endif /* WITH_RETRY */
|
|
default:
|
|
return result;
|
|
}
|
|
|
|
if (dofork) {
|
|
xiosetchilddied(); /* set SIGCHLD handler */
|
|
}
|
|
|
|
#if WITH_RETRY
|
|
if (dofork) {
|
|
pid_t pid;
|
|
int level = E_ERROR;
|
|
if (sfd->forever || sfd->retry) {
|
|
level = E_WARN; /* most users won't expect a problem here,
|
|
so Notice is too weak */
|
|
}
|
|
|
|
while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
|
|
--sfd->retry;
|
|
if (sfd->forever || sfd->retry) {
|
|
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
|
|
Nanosleep(&sfd->intervall, NULL); continue;
|
|
}
|
|
return STAT_RETRYLATER;
|
|
}
|
|
|
|
if (pid == 0) { /* child process */
|
|
break;
|
|
}
|
|
|
|
/* parent process */
|
|
Close(sfd->fd);
|
|
/* with and without retry */
|
|
Nanosleep(&sfd->intervall, NULL);
|
|
dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
|
|
continue; /* with next socket() bind() connect() */
|
|
} else
|
|
#endif /* WITH_RETRY */
|
|
{
|
|
break;
|
|
}
|
|
} while (true);
|
|
|
|
if (opt_unlink_close && needbind) {
|
|
if ((sfd->unlink_close = strndup(us.sun_path, sizeof(us.sun_path))) == NULL) {
|
|
Error2("strndup(\"%s\", "F_Zu"): out of memory", name, sizeof(us.sun_path));
|
|
}
|
|
sfd->opt_unlink_close = true;
|
|
}
|
|
|
|
if ((result = _xio_openlate(sfd, opts)) < 0) {
|
|
return result;
|
|
}
|
|
return STAT_OK;
|
|
}
|
|
|
|
|
|
static int xioopen_unix_sendto(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
/* we expect the form: filename */
|
|
const char *name;
|
|
xiosingle_t *sfd = &xxfd->stream;
|
|
const struct opt *namedopt;
|
|
int pf = PF_UNIX;
|
|
int socktype = SOCK_DGRAM;
|
|
int protocol = 0;
|
|
struct sockaddr_un us;
|
|
socklen_t uslen = sizeof(us);
|
|
bool needbind = false;
|
|
bool needtemp = false;
|
|
bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
|
|
char *opt_bind_tempname = NULL;
|
|
int result;
|
|
|
|
if (argc != 2) {
|
|
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
name = argv[1];
|
|
|
|
sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
|
|
retropt_socket_pf(opts, &pf);
|
|
sfd->howtoend = END_SHUTDOWN;
|
|
applyopts_offset(sfd, opts);
|
|
|
|
sfd->salen = xiosetunix(pf, &sfd->peersa.un, name, addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight);
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
/* only for non abstract because abstract do not work in file system */
|
|
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
|
|
}
|
|
|
|
sfd->dtype = XIODATA_RECVFROM;
|
|
|
|
if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
|
|
(addrdesc->arg1/*abstract*/<<1)| sfd->para.socket.un.tight,
|
|
sfd->para.socket.ip.ai_flags)
|
|
== STAT_OK) {
|
|
needbind = true;
|
|
}
|
|
|
|
if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) {
|
|
if (needbind) {
|
|
Error("do not use both options bind and bind-tempnam");
|
|
return STAT_NORETRY;
|
|
}
|
|
needbind = true;
|
|
needtemp = true;
|
|
xiosetunix(pf, &us, opt_bind_tempname?opt_bind_tempname:"",
|
|
addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight);
|
|
}
|
|
|
|
if (!needbind &&
|
|
(namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
|
|
Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
|
|
}
|
|
|
|
if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
|
|
applyopts(sfd, -1, opts, PH_INIT);
|
|
|
|
result =
|
|
_xioopen_dgram_sendto(needbind?(union sockaddr_union *)&us:NULL, uslen,
|
|
opts, xioflags, sfd, addrdesc->groups,
|
|
pf, socktype, protocol, needtemp);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
|
|
if (opt_unlink_close && needbind) {
|
|
if ((sfd->unlink_close = strndup(us.sun_path, sizeof(us.sun_path))) == NULL) {
|
|
Error2("strndup(\"%s\", "F_Zu"): out of memory", name, sizeof(us.sun_path));
|
|
}
|
|
sfd->opt_unlink_close = true;
|
|
}
|
|
return STAT_OK;
|
|
}
|
|
|
|
|
|
static
|
|
int xioopen_unix_recvfrom(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
/* we expect the form: filename */
|
|
const char *name;
|
|
xiosingle_t *sfd = &xxfd->stream;
|
|
int pf = PF_UNIX;
|
|
int socktype = SOCK_DGRAM;
|
|
int protocol = 0;
|
|
struct sockaddr_un us;
|
|
socklen_t uslen;
|
|
bool needbind = true;
|
|
bool opt_unlink_early = false;
|
|
bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
|
|
|
|
if (argc != 2) {
|
|
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
name = argv[1];
|
|
|
|
sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
|
|
retropt_socket_pf(opts, &pf);
|
|
sfd->howtoend = END_NONE;
|
|
if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
|
|
applyopts(sfd, -1, opts, PH_INIT);
|
|
applyopts_named(name, opts, PH_EARLY); /* umask! */
|
|
applyopts_offset(sfd, opts);
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
/* only for non abstract because abstract do not work in file system */
|
|
retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
|
|
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
|
|
}
|
|
applyopts(sfd, -1, opts, PH_EARLY);
|
|
|
|
uslen = xiosetunix(pf, &us, name, addrdesc->arg1/*abstract*/,
|
|
sfd->para.socket.un.tight);
|
|
|
|
#if 0
|
|
if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
|
|
(addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight,
|
|
sfd->para.socket.ip.ai_flags)) {
|
|
== STAT_OK) {
|
|
}
|
|
#endif
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
if (opt_unlink_early) {
|
|
xio_unlink(name, E_ERROR);
|
|
} else {
|
|
struct stat buf;
|
|
if (Lstat(name, &buf) == 0) {
|
|
Error1("\"%s\" exists", name);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
}
|
|
if (opt_unlink_close) {
|
|
if ((sfd->unlink_close = strdup(name)) == NULL) {
|
|
Error1("strdup(\"%s\"): out of memory", name);
|
|
}
|
|
sfd->opt_unlink_close = true;
|
|
}
|
|
|
|
/* trying to set user-early, perm-early etc. here is useless because
|
|
file system entry is available only past bind() call. */
|
|
}
|
|
applyopts_named(name, opts, PH_EARLY); /* umask! */
|
|
|
|
sfd->para.socket.la.soa.sa_family = pf;
|
|
|
|
sfd->dtype = XIODATA_RECVFROM_ONE;
|
|
|
|
/* this may fork */
|
|
return
|
|
_xioopen_dgram_recvfrom(sfd, xioflags,
|
|
needbind?(struct sockaddr *)&us:NULL, uslen,
|
|
opts, pf, socktype, protocol, E_ERROR);
|
|
}
|
|
|
|
|
|
static int xioopen_unix_recv(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
/* we expect the form: filename */
|
|
const char *name;
|
|
xiosingle_t *sfd = &xxfd->stream;
|
|
int pf = PF_UNIX;
|
|
int socktype = SOCK_DGRAM;
|
|
int protocol = 0;
|
|
union sockaddr_union us;
|
|
socklen_t uslen;
|
|
bool opt_unlink_early = false;
|
|
bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
|
|
int result;
|
|
|
|
if (argc != 2) {
|
|
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
name = argv[1];
|
|
|
|
sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
|
|
retropt_socket_pf(opts, &pf);
|
|
sfd->howtoend = END_SHUTDOWN;
|
|
if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
|
|
applyopts(sfd, -1, opts, PH_INIT);
|
|
applyopts_named(name, opts, PH_EARLY); /* umask! */
|
|
applyopts_offset(sfd, opts);
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
/* only for non abstract because abstract do not work in file system */
|
|
retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
|
|
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
|
|
}
|
|
applyopts(sfd, -1, opts, PH_EARLY);
|
|
|
|
uslen = xiosetunix(pf, &us.un, name, addrdesc->arg1/*abstract*/,
|
|
sfd->para.socket.un.tight);
|
|
|
|
#if 0
|
|
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
|
|
(addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight,
|
|
sfd->para.socket.ip.ai_flags)
|
|
== STAT_OK) {
|
|
}
|
|
#endif
|
|
|
|
if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
|
|
if (opt_unlink_early) {
|
|
xio_unlink(name, E_ERROR);
|
|
} else {
|
|
struct stat buf;
|
|
if (Lstat(name, &buf) == 0) {
|
|
Error1("\"%s\" exists", name);
|
|
return STAT_RETRYLATER;
|
|
}
|
|
}
|
|
if (opt_unlink_close) {
|
|
if ((sfd->unlink_close = strdup(name)) == NULL) {
|
|
Error1("strdup(\"%s\"): out of memory", name);
|
|
}
|
|
sfd->opt_unlink_close = true;
|
|
}
|
|
}
|
|
applyopts_named(name, opts, PH_EARLY); /* umask! */
|
|
|
|
sfd->para.socket.la.soa.sa_family = pf;
|
|
|
|
sfd->dtype = XIODATA_RECV;
|
|
result = _xioopen_dgram_recv(sfd, xioflags, &us.soa, uslen,
|
|
opts, pf, socktype, protocol, E_ERROR);
|
|
return result;
|
|
}
|
|
|
|
|
|
/* generic UNIX socket client, tries connect, SEQPACKET, send(to) */
|
|
static int xioopen_unix_client(
|
|
int argc,
|
|
const char *argv[],
|
|
struct opt *opts,
|
|
int xioflags,
|
|
xiofile_t *xxfd,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
/* we expect the form: filename */
|
|
if (argc != 2) {
|
|
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
|
|
return STAT_NORETRY;
|
|
}
|
|
|
|
return
|
|
_xioopen_unix_client(&xxfd->stream, xioflags, addrdesc->groups,
|
|
addrdesc->arg1/*abstract*/, opts, argv[1], addrdesc);
|
|
}
|
|
|
|
/* establishes communication with an existing UNIX type socket. supports stream
|
|
and datagram socket types: first tries to connect(), but when this fails it
|
|
falls back to sendto().
|
|
applies and consumes the following option:
|
|
PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND,
|
|
PH_CONNECTED, PH_LATE, ?PH_CONNECT
|
|
OFUNC_OFFSET,
|
|
OPT_PROTOCOL_FAMILY, OPT_UNIX_TIGHTSOCKLEN, OPT_UNLINK_CLOSE, OPT_BIND,
|
|
OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK,
|
|
*/
|
|
int
|
|
_xioopen_unix_client(
|
|
xiosingle_t *sfd,
|
|
int xioflags,
|
|
groups_t groups,
|
|
int abstract,
|
|
struct opt *opts,
|
|
const char *name,
|
|
const struct addrdesc *addrdesc)
|
|
{
|
|
const struct opt *namedopt;
|
|
int pf = PF_UNIX;
|
|
int socktype = 0; /* to be determined by server socket type */
|
|
int protocol = 0;
|
|
union sockaddr_union them, us;
|
|
socklen_t themlen, uslen = sizeof(us);
|
|
bool needbind = false;
|
|
bool needtemp = false;
|
|
bool opt_unlink_close = false;
|
|
char *opt_bind_tempname = NULL;
|
|
struct opt *opts0;
|
|
int result;
|
|
|
|
sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
|
|
retropt_socket_pf(opts, &pf);
|
|
sfd->howtoend = END_SHUTDOWN;
|
|
if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
|
|
applyopts(sfd, -1, opts, PH_INIT);
|
|
applyopts_offset(sfd, opts);
|
|
retropt_int(opts, OPT_SO_TYPE, &socktype);
|
|
retropt_int(opts, OPT_SO_PROTOTYPE, &protocol);
|
|
applyopts(sfd, -1, opts, PH_EARLY);
|
|
|
|
themlen = xiosetunix(pf, &them.un, name, abstract, sfd->para.socket.un.tight);
|
|
if (!(ABSTRACT && abstract)) {
|
|
/* only for non abstract because abstract do not work in file system */
|
|
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
|
|
}
|
|
|
|
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
|
|
(abstract<<1)|sfd->para.socket.un.tight,
|
|
sfd->para.socket.ip.ai_flags)
|
|
!= STAT_NOACTION) {
|
|
needbind = true;
|
|
}
|
|
|
|
if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) {
|
|
if (needbind) {
|
|
Error("do not use both options bind and unix-bind-tempname");
|
|
return STAT_NORETRY;
|
|
}
|
|
needbind = true;
|
|
needtemp = true;
|
|
}
|
|
|
|
if (!needbind &&
|
|
(namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
|
|
Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
|
|
}
|
|
|
|
if (opt_unlink_close) {
|
|
if ((sfd->unlink_close = strdup(name)) == NULL) {
|
|
Error1("strdup(\"%s\"): out of memory", name);
|
|
}
|
|
sfd->opt_unlink_close = true;
|
|
}
|
|
|
|
/* save options, because we might have to start again */
|
|
opts0 = copyopts(opts, GROUP_ALL);
|
|
|
|
/* just a breakable block, helps to avoid goto */
|
|
do {
|
|
/* sfd->dtype = DATA_STREAM; // is default */
|
|
if (needtemp)
|
|
xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"",
|
|
abstract, sfd->para.socket.un.tight);
|
|
/* this function handles AF_UNIX with EPROTOTYPE specially for us */
|
|
if ((result =
|
|
_xioopen_connect(sfd,
|
|
needbind?&us:NULL, uslen,
|
|
&them.soa, themlen,
|
|
opts, pf, socktype?socktype:SOCK_STREAM, protocol,
|
|
needtemp, E_INFO)) == 0)
|
|
break;
|
|
if ((errno != EPROTOTYPE
|
|
#if WITH_ABSTRACT_UNIXSOCKET
|
|
&& !(abstract && errno == ECONNREFUSED)
|
|
#endif
|
|
) || socktype != 0)
|
|
break;
|
|
if (needbind)
|
|
xio_unlink(us.un.sun_path, E_ERROR);
|
|
dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
|
|
|
|
if (needtemp)
|
|
xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"",
|
|
abstract, sfd->para.socket.un.tight);
|
|
socktype = SOCK_SEQPACKET;
|
|
if ((result =
|
|
_xioopen_connect(sfd,
|
|
needbind?&us:NULL, uslen,
|
|
(struct sockaddr *)&them, themlen,
|
|
opts, pf, SOCK_SEQPACKET, protocol,
|
|
needtemp, E_INFO)) == 0)
|
|
break;
|
|
if (errno != EPROTOTYPE && errno != EPROTONOSUPPORT/*AIX*/
|
|
#if WITH_ABSTRACT_UNIXSOCKET
|
|
&& !(abstract && errno == ECONNREFUSED)
|
|
#endif
|
|
)
|
|
break;
|
|
if (needbind)
|
|
xio_unlink(us.un.sun_path, E_ERROR);
|
|
dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
|
|
|
|
if (needtemp)
|
|
xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"",
|
|
abstract, sfd->para.socket.un.tight);
|
|
sfd->peersa = them;
|
|
sfd->salen = themlen;
|
|
if ((result =
|
|
_xioopen_dgram_sendto(needbind?&us:NULL, uslen,
|
|
opts, xioflags, sfd, groups,
|
|
pf, SOCK_DGRAM, protocol, needtemp))
|
|
== 0) {
|
|
sfd->dtype = XIODATA_RECVFROM;
|
|
break;
|
|
}
|
|
} while (0);
|
|
|
|
if (result != 0) {
|
|
Error3("%s: %s: %s", addrdesc->defname, name, strerror(errno));
|
|
if (needbind)
|
|
xio_unlink(us.un.sun_path, E_ERROR);
|
|
return result;
|
|
}
|
|
|
|
if ((result = _xio_openlate(sfd, opts)) < 0) {
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* returns information that can be used for constructing an environment
|
|
variable describing the socket address.
|
|
if idx is 0, this function writes "ADDR" into namebuff and the path into
|
|
valuebuff, and returns 0 (which means that no more info is there).
|
|
if idx is != 0, it returns -1
|
|
namelen and valuelen contain the max. allowed length of output chars in the
|
|
respective buffer.
|
|
on error this function returns -1.
|
|
*/
|
|
int
|
|
xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen,
|
|
char *valuebuff, size_t valuelen,
|
|
struct sockaddr_un *sa, socklen_t salen, int ipproto) {
|
|
if (idx != 0) {
|
|
return -1;
|
|
}
|
|
strcpy(namebuff, "ADDR");
|
|
sockaddr_unix_info(sa, salen, valuebuff, valuelen);
|
|
return 0;
|
|
}
|
|
|
|
static const char tmpchars[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
static size_t numchars = sizeof(tmpchars)-1;
|
|
|
|
/* Simplyfied version of tempnam(). Uses the current directory when pathx is
|
|
not absolute.
|
|
Returns a malloc()'ed string with a probably free name,
|
|
or NULL when an error occurred */
|
|
char *xio_tempnam(
|
|
const char *pathx,
|
|
bool donttry) /* for abstract, do not check if it exists */
|
|
{
|
|
int len;
|
|
char *X; /* begin of XXXXXX */
|
|
unsigned int i = TMP_MAX;
|
|
unsigned int r1, r2;
|
|
uint64_t v;
|
|
char *patht;
|
|
char readl[PATH_MAX];
|
|
int rlc;
|
|
|
|
if (pathx == NULL || pathx[0] == '\0')
|
|
pathx = "/tmp/socat-bind.XXXXXX";
|
|
|
|
len = strlen(pathx);
|
|
if (len < 6 || strstr(pathx, "XXXXXX") == NULL) {
|
|
Warn1("xio_tempnam(\"%s\"): path pattern is not valid", pathx);
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
patht = strdup(pathx);
|
|
if (patht == NULL) {
|
|
Error1("strdup("F_Zu"): out of memory", strlen(pathx));
|
|
return patht;
|
|
}
|
|
X = strstr(patht, "XXXXXX");
|
|
|
|
Debug1("xio_tempnam(\"%s\"): trying path names, suppressing stat() logs",
|
|
patht);
|
|
while (i > 0) {
|
|
r1 = random();
|
|
r2 = random();
|
|
v = r2*RAND_MAX + r1;
|
|
X[0] = tmpchars[v%numchars];
|
|
v /= numchars;
|
|
X[1] = tmpchars[v%numchars];
|
|
v /= numchars;
|
|
X[2] = tmpchars[v%numchars];
|
|
v /= numchars;
|
|
X[3] = tmpchars[v%numchars];
|
|
v /= numchars;
|
|
X[4] = tmpchars[v%numchars];
|
|
v /= numchars;
|
|
X[5] = tmpchars[v%numchars];
|
|
v /= numchars;
|
|
|
|
if (donttry)
|
|
return patht;
|
|
|
|
/* readlink() might be faster than lstat() */
|
|
rlc = readlink(patht, readl, sizeof(readl));
|
|
if (rlc < 0 && errno == ENOENT)
|
|
break;
|
|
|
|
--i;
|
|
}
|
|
|
|
if (i == 0) {
|
|
errno = EEXIST;
|
|
return NULL;
|
|
}
|
|
|
|
return patht;
|
|
}
|
|
|
|
#endif /* WITH_UNIX */
|