Version 1.8.0.0
[socat.git] / xio-unix.c
blob42bb2a3331d7194fa1c75d19efa4ad93f5e9606d
1 /* source: xio-unix.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for opening addresses of UNIX socket type */
7 #include "xiosysincludes.h"
8 #include "xioopen.h"
10 #include "xio-socket.h"
11 #include "xio-listen.h"
12 #include "xio-unix.h"
13 #include "xio-named.h"
16 #if WITH_UNIX
18 /* to avoid unneccessary runtime if () conditionals when no abstract support is
19 compiled in (or at least to give optimizing compilers a good chance) we need
20 a constant that can be used in C expressions */
21 #if WITH_ABSTRACT_UNIXSOCKET
22 # define ABSTRACT 1
23 #else
24 # define ABSTRACT 0
25 #endif
27 static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
28 static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
29 static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
30 static int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
31 static int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
32 static int xioopen_unix_client(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc);
34 /* the first free parameter is 0 for "normal" unix domain sockets, or 1 for
35 abstract unix sockets (Linux); the second and third free parameter are
36 unsused */
37 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>") };
38 #if WITH_LISTEN
39 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>") };
40 #endif /* WITH_LISTEN */
41 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>") };
42 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>") };
43 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>") };
44 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>") };
45 #if WITH_ABSTRACT_UNIXSOCKET
46 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>") };
47 #if WITH_LISTEN
48 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>") };
49 #endif /* WITH_LISTEN */
50 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>") };
51 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>") };
52 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>") };
53 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>") };
54 #endif /* WITH_ABSTRACT_UNIXSOCKET */
56 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 };
57 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) };
60 /* fills the socket address struct and returns its effective length.
61 abstract is usually 0; != 0 generates an abstract socket address on Linux.
62 tight!=0 calculates the resulting length from the path length, not from the
63 structures length; this is more common (see option unix-tightsocklen)
64 the struct need not be initialized when calling this function.
66 socklen_t
67 xiosetunix(int pf,
68 struct sockaddr_un *saun,
69 const char *path,
70 bool abstract,
71 bool tight) {
72 size_t pathlen;
73 socklen_t len;
75 socket_un_init(saun);
76 #ifdef WITH_ABSTRACT_UNIXSOCKET
77 if (abstract) {
78 if ((pathlen = strlen(path)) >= sizeof(saun->sun_path)) {
79 Warn2("socket address "F_Zu" characters long, truncating to "F_Zu"",
80 pathlen+1, sizeof(saun->sun_path));
82 saun->sun_path[0] = '\0'; /* so it's abstract */
83 strncpy(saun->sun_path+1, path, sizeof(saun->sun_path)-1); /* ok */
84 if (tight) {
85 len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+
86 MIN(pathlen+1, sizeof(saun->sun_path));
87 #if HAVE_STRUCT_SOCKADDR_SALEN
88 saun->sun_len = len;
89 #endif
90 } else {
91 len = sizeof(struct sockaddr_un);
93 return len;
94 } else
95 #endif /* WITH_ABSTRACT_UNIXSOCKET */
97 if ((pathlen = strlen(path)) > sizeof(saun->sun_path)) {
98 Warn2("unix socket address "F_Zu" characters long, truncating to "F_Zu"",
99 pathlen, sizeof(saun->sun_path));
101 strncpy(saun->sun_path, path, sizeof(saun->sun_path)); /* ok */
102 if (tight) {
103 len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+
104 MIN(pathlen, sizeof(saun->sun_path));
105 #if HAVE_STRUCT_SOCKADDR_SALEN
106 saun->sun_len = len;
107 #endif
108 } else {
109 len = sizeof(struct sockaddr_un);
111 return len;
114 #if WITH_LISTEN
115 static int xioopen_unix_listen(
116 int argc,
117 const char *argv[],
118 struct opt *opts,
119 int xioflags,
120 xiofile_t *xxfd,
121 const struct addrdesc *addrdesc)
123 /* we expect the form: filename */
124 const char *name;
125 xiosingle_t *sfd = &xxfd->stream;
126 int pf = PF_UNIX;
127 int socktype = SOCK_STREAM;
128 int protocol = 0;
129 struct sockaddr_un us;
130 socklen_t uslen;
131 struct opt *opts0 = NULL;
132 pid_t pid = Getpid();
133 bool opt_unlink_early = false;
134 bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
135 int result;
137 if (argc != 2) {
138 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
139 return STAT_NORETRY;
141 name = argv[1];
143 sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
144 retropt_socket_pf(opts, &pf);
145 if (sfd->howtoend == END_UNSPEC)
146 sfd->howtoend = END_SHUTDOWN;
148 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
149 /* only for non abstract because abstract do not work in file system */
150 retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
151 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
154 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
155 applyopts(sfd, -1, opts, PH_INIT);
156 applyopts_named(name, opts, PH_EARLY); /* umask! */
157 applyopts_offset(sfd, opts);
158 applyopts(sfd, -1, opts, PH_EARLY);
160 uslen = xiosetunix(pf, &us, name, addrdesc->arg1/*abstract*/,
161 sfd->para.socket.un.tight);
163 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
164 if (opt_unlink_early) {
165 xio_unlink(name, E_ERROR);
166 } else {
167 struct stat buf;
168 if (Lstat(name, &buf) == 0) {
169 Error1("\"%s\" exists", name);
170 return STAT_RETRYLATER;
173 if (opt_unlink_close) {
174 if ((sfd->unlink_close = strdup(name)) == NULL) {
175 Error1("strdup(\"%s\"): out of memory", name);
177 sfd->opt_unlink_close = true;
180 /* trying to set user-early, perm-early etc. here is useless because
181 file system entry is available only past bind() call. */
184 opts0 = copyopts(opts, GROUP_ALL);
186 /* this may fork() */
187 if ((result =
188 xioopen_listen(sfd, xioflags,
189 (struct sockaddr *)&us, uslen,
190 opts, opts0, pf, socktype, protocol))
191 != 0)
192 return result;
194 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
195 if (opt_unlink_close) {
196 if (pid != Getpid()) {
197 /* in a child process - do not unlink-close here! */
198 sfd->opt_unlink_close = false;
203 return 0;
205 #endif /* WITH_LISTEN */
208 static int xioopen_unix_connect(
209 int argc,
210 const char *argv[],
211 struct opt *opts,
212 int xioflags,
213 xiofile_t *xxfd,
214 const struct addrdesc *addrdesc)
216 /* we expect the form: filename */
217 const char *name;
218 struct single *sfd = &xxfd->stream;
219 const struct opt *namedopt;
220 int pf = PF_UNIX;
221 int socktype = SOCK_STREAM;
222 int protocol = 0;
223 struct sockaddr_un them, us;
224 socklen_t themlen, uslen = sizeof(us);
225 bool needbind = false;
226 bool needtemp = false;
227 bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
228 bool dofork = false;
229 struct opt *opts0;
230 char infobuff[256];
231 int level;
232 char *opt_bind_tempname = NULL;
233 int result;
235 if (argc != 2) {
236 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
237 return STAT_NORETRY;
239 name = argv[1];
241 sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
242 retropt_socket_pf(opts, &pf);
243 if (sfd->howtoend == END_UNSPEC)
244 sfd->howtoend = END_SHUTDOWN;
245 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
246 applyopts(sfd, -1, opts, PH_INIT);
247 applyopts_offset(sfd, opts);
248 applyopts(sfd, -1, opts, PH_EARLY);
250 themlen = xiosetunix(pf, &them, name, addrdesc->arg1/*abstract*/,
251 sfd->para.socket.un.tight);
253 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
254 /* Only for non abstract because abstract do not work in file system */
255 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
258 if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
259 (addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight,
260 sfd->para.socket.ip.ai_flags)
261 == STAT_OK) {
262 needbind = true;
265 if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) {
266 if (needbind) {
267 Error("do not use both options bind and unix-bind-tempnam");
268 return -1;
270 needbind = true;
271 needtemp = true;
272 xiosetunix(pf, &us, opt_bind_tempname?opt_bind_tempname:"",
273 addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight);
274 if (opt_bind_tempname == NULL && !addrdesc->arg1/*abstract*/) {
275 us.sun_path[0] = 0x01; /* mark as non abstract */
279 if (!needbind &&
280 (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
281 Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
284 retropt_bool(opts, OPT_FORK, &dofork);
286 opts0 = copyopts(opts, GROUP_ALL);
288 Notice1("opening connection to %s",
289 sockaddr_info((struct sockaddr *)&them, themlen, infobuff, sizeof(infobuff)));
291 do { /* loop over retries and forks */
293 #if WITH_RETRY
294 if (sfd->forever || sfd->retry) {
295 level = E_INFO;
296 } else
297 #endif /* WITH_RETRY */
298 level = E_ERROR;
300 result =
301 _xioopen_connect(sfd,
302 needbind?(union sockaddr_union *)&us:NULL, uslen,
303 (struct sockaddr *)&them, themlen,
304 opts, pf, socktype, protocol, needtemp, level);
305 if (result != 0) {
306 char infobuff[256];
307 /* we caller must handle this */
308 Msg3(level, "connect(, %s, "F_socklen"): %s",
309 sockaddr_info((struct sockaddr *)&them, themlen, infobuff, sizeof(infobuff)),
310 themlen, strerror(errno));
312 switch (result) {
313 case STAT_OK: break;
314 #if WITH_RETRY
315 case STAT_RETRYLATER:
316 if (sfd->forever || sfd->retry) {
317 --sfd->retry;
318 if (result == STAT_RETRYLATER) {
319 Nanosleep(&sfd->intervall, NULL);
321 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
322 continue;
324 return STAT_NORETRY;
325 #endif /* WITH_RETRY */
326 default:
327 return result;
330 if (dofork) {
331 xiosetchilddied(); /* set SIGCHLD handler */
334 #if WITH_RETRY
335 if (dofork) {
336 pid_t pid;
337 int level = E_ERROR;
338 if (sfd->forever || sfd->retry) {
339 level = E_WARN; /* most users won't expect a problem here,
340 so Notice is too weak */
343 while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
344 --sfd->retry;
345 if (sfd->forever || sfd->retry) {
346 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
347 Nanosleep(&sfd->intervall, NULL); continue;
349 return STAT_RETRYLATER;
352 if (pid == 0) { /* child process */
353 break;
356 /* parent process */
357 Close(sfd->fd);
358 /* with and without retry */
359 Nanosleep(&sfd->intervall, NULL);
360 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
361 continue; /* with next socket() bind() connect() */
362 } else
363 #endif /* WITH_RETRY */
365 break;
367 } while (true);
369 if (opt_unlink_close && needbind) {
370 if ((sfd->unlink_close = strndup(us.sun_path, sizeof(us.sun_path))) == NULL) {
371 Error2("strndup(\"%s\", "F_Zu"): out of memory", name, sizeof(us.sun_path));
373 sfd->opt_unlink_close = true;
376 if ((result = _xio_openlate(sfd, opts)) < 0) {
377 return result;
379 return STAT_OK;
383 static int xioopen_unix_sendto(
384 int argc,
385 const char *argv[],
386 struct opt *opts,
387 int xioflags,
388 xiofile_t *xxfd,
389 const struct addrdesc *addrdesc)
391 /* we expect the form: filename */
392 const char *name;
393 xiosingle_t *sfd = &xxfd->stream;
394 const struct opt *namedopt;
395 int pf = PF_UNIX;
396 int socktype = SOCK_DGRAM;
397 int protocol = 0;
398 struct sockaddr_un us;
399 socklen_t uslen = sizeof(us);
400 bool needbind = false;
401 bool needtemp = false;
402 bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
403 char *opt_bind_tempname = NULL;
404 int result;
406 if (argc != 2) {
407 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
408 return STAT_NORETRY;
410 name = argv[1];
412 sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
413 retropt_socket_pf(opts, &pf);
414 if (sfd->howtoend == END_UNSPEC)
415 sfd->howtoend = END_SHUTDOWN;
416 applyopts_offset(sfd, opts);
418 sfd->salen = xiosetunix(pf, &sfd->peersa.un, name, addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight);
420 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
421 /* only for non abstract because abstract do not work in file system */
422 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
425 sfd->dtype = XIODATA_RECVFROM;
427 if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
428 (addrdesc->arg1/*abstract*/<<1)| sfd->para.socket.un.tight,
429 sfd->para.socket.ip.ai_flags)
430 == STAT_OK) {
431 needbind = true;
434 if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) {
435 if (needbind) {
436 Error("do not use both options bind and bind-tempnam");
437 return STAT_NORETRY;
439 needbind = true;
440 needtemp = true;
441 xiosetunix(pf, &us, opt_bind_tempname?opt_bind_tempname:"",
442 addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight);
445 if (!needbind &&
446 (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
447 Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
450 if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
451 applyopts(sfd, -1, opts, PH_INIT);
453 result =
454 _xioopen_dgram_sendto(needbind?(union sockaddr_union *)&us:NULL, uslen,
455 opts, xioflags, sfd, addrdesc->groups,
456 pf, socktype, protocol, needtemp);
457 if (result != 0) {
458 return result;
461 if (opt_unlink_close && needbind) {
462 if ((sfd->unlink_close = strndup(us.sun_path, sizeof(us.sun_path))) == NULL) {
463 Error2("strndup(\"%s\", "F_Zu"): out of memory", name, sizeof(us.sun_path));
465 sfd->opt_unlink_close = true;
467 return STAT_OK;
471 static
472 int xioopen_unix_recvfrom(
473 int argc,
474 const char *argv[],
475 struct opt *opts,
476 int xioflags,
477 xiofile_t *xxfd,
478 const struct addrdesc *addrdesc)
480 /* we expect the form: filename */
481 const char *name;
482 xiosingle_t *sfd = &xxfd->stream;
483 int pf = PF_UNIX;
484 int socktype = SOCK_DGRAM;
485 int protocol = 0;
486 struct sockaddr_un us;
487 socklen_t uslen;
488 bool needbind = true;
489 bool opt_unlink_early = false;
490 bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
492 if (argc != 2) {
493 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
494 return STAT_NORETRY;
496 name = argv[1];
498 sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
499 retropt_socket_pf(opts, &pf);
500 if (sfd->howtoend == END_UNSPEC)
501 sfd->howtoend = END_NONE;
502 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
503 applyopts(sfd, -1, opts, PH_INIT);
504 applyopts_named(name, opts, PH_EARLY); /* umask! */
505 applyopts_offset(sfd, opts);
507 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
508 /* only for non abstract because abstract do not work in file system */
509 retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
510 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
512 applyopts(sfd, -1, opts, PH_EARLY);
514 uslen = xiosetunix(pf, &us, name, addrdesc->arg1/*abstract*/,
515 sfd->para.socket.un.tight);
517 #if 0
518 if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
519 (addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight,
520 sfd->para.socket.ip.ai_flags)) {
521 == STAT_OK) {
523 #endif
525 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
526 if (opt_unlink_early) {
527 xio_unlink(name, E_ERROR);
528 } else {
529 struct stat buf;
530 if (Lstat(name, &buf) == 0) {
531 Error1("\"%s\" exists", name);
532 return STAT_RETRYLATER;
535 if (opt_unlink_close) {
536 if ((sfd->unlink_close = strdup(name)) == NULL) {
537 Error1("strdup(\"%s\"): out of memory", name);
539 sfd->opt_unlink_close = true;
542 /* trying to set user-early, perm-early etc. here is useless because
543 file system entry is available only past bind() call. */
545 applyopts_named(name, opts, PH_EARLY); /* umask! */
547 sfd->para.socket.la.soa.sa_family = pf;
549 sfd->dtype = XIODATA_RECVFROM_ONE;
551 /* this may fork */
552 return
553 _xioopen_dgram_recvfrom(sfd, xioflags,
554 needbind?(struct sockaddr *)&us:NULL, uslen,
555 opts, pf, socktype, protocol, E_ERROR);
559 static int xioopen_unix_recv(
560 int argc,
561 const char *argv[],
562 struct opt *opts,
563 int xioflags,
564 xiofile_t *xxfd,
565 const struct addrdesc *addrdesc)
567 /* we expect the form: filename */
568 const char *name;
569 xiosingle_t *sfd = &xxfd->stream;
570 int pf = PF_UNIX;
571 int socktype = SOCK_DGRAM;
572 int protocol = 0;
573 union sockaddr_union us;
574 socklen_t uslen;
575 bool opt_unlink_early = false;
576 bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1);
577 int result;
579 if (argc != 2) {
580 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
581 return STAT_NORETRY;
583 name = argv[1];
585 sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
586 retropt_socket_pf(opts, &pf);
587 if (sfd->howtoend == END_UNSPEC)
588 sfd->howtoend = END_SHUTDOWN;
589 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
590 applyopts(sfd, -1, opts, PH_INIT);
591 applyopts_named(name, opts, PH_EARLY); /* umask! */
592 applyopts_offset(sfd, opts);
594 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
595 /* only for non abstract because abstract do not work in file system */
596 retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
597 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
599 applyopts(sfd, -1, opts, PH_EARLY);
601 uslen = xiosetunix(pf, &us.un, name, addrdesc->arg1/*abstract*/,
602 sfd->para.socket.un.tight);
604 #if 0
605 if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
606 (addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight,
607 sfd->para.socket.ip.ai_flags)
608 == STAT_OK) {
610 #endif
612 if (!(ABSTRACT && addrdesc->arg1/*abstract*/)) {
613 if (opt_unlink_early) {
614 xio_unlink(name, E_ERROR);
615 } else {
616 struct stat buf;
617 if (Lstat(name, &buf) == 0) {
618 Error1("\"%s\" exists", name);
619 return STAT_RETRYLATER;
622 if (opt_unlink_close) {
623 if ((sfd->unlink_close = strdup(name)) == NULL) {
624 Error1("strdup(\"%s\"): out of memory", name);
626 sfd->opt_unlink_close = true;
629 applyopts_named(name, opts, PH_EARLY); /* umask! */
631 sfd->para.socket.la.soa.sa_family = pf;
633 sfd->dtype = XIODATA_RECV;
634 result = _xioopen_dgram_recv(sfd, xioflags, &us.soa, uslen,
635 opts, pf, socktype, protocol, E_ERROR);
636 return result;
640 /* generic UNIX socket client, tries connect, SEQPACKET, send(to) */
641 static int xioopen_unix_client(
642 int argc,
643 const char *argv[],
644 struct opt *opts,
645 int xioflags,
646 xiofile_t *xxfd,
647 const struct addrdesc *addrdesc)
649 /* we expect the form: filename */
650 if (argc != 2) {
651 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
652 return STAT_NORETRY;
655 return
656 _xioopen_unix_client(&xxfd->stream, xioflags, addrdesc->groups,
657 addrdesc->arg1/*abstract*/, opts, argv[1], addrdesc);
660 /* establishes communication with an existing UNIX type socket. supports stream
661 and datagram socket types: first tries to connect(), but when this fails it
662 falls back to sendto().
663 applies and consumes the following option:
664 PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND,
665 PH_CONNECTED, PH_LATE, ?PH_CONNECT
666 OFUNC_OFFSET,
667 OPT_PROTOCOL_FAMILY, OPT_UNIX_TIGHTSOCKLEN, OPT_UNLINK_CLOSE, OPT_BIND,
668 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK,
671 _xioopen_unix_client(
672 xiosingle_t *sfd,
673 int xioflags,
674 groups_t groups,
675 int abstract,
676 struct opt *opts,
677 const char *name,
678 const struct addrdesc *addrdesc)
680 const struct opt *namedopt;
681 int pf = PF_UNIX;
682 int socktype = 0; /* to be determined by server socket type */
683 int protocol = 0;
684 union sockaddr_union them, us;
685 socklen_t themlen, uslen = sizeof(us);
686 bool needbind = false;
687 bool needtemp = false;
688 bool opt_unlink_close = false;
689 char *opt_bind_tempname = NULL;
690 struct opt *opts0;
691 int result;
693 sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN;
694 retropt_socket_pf(opts, &pf);
695 if (sfd->howtoend == END_UNSPEC)
696 sfd->howtoend = END_SHUTDOWN;
697 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
698 applyopts(sfd, -1, opts, PH_INIT);
699 applyopts_offset(sfd, opts);
700 retropt_int(opts, OPT_SO_TYPE, &socktype);
701 retropt_int(opts, OPT_SO_PROTOTYPE, &protocol);
702 applyopts(sfd, -1, opts, PH_EARLY);
704 themlen = xiosetunix(pf, &them.un, name, abstract, sfd->para.socket.un.tight);
705 if (!(ABSTRACT && abstract)) {
706 /* only for non abstract because abstract do not work in file system */
707 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
710 if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
711 (abstract<<1)|sfd->para.socket.un.tight,
712 sfd->para.socket.ip.ai_flags)
713 != STAT_NOACTION) {
714 needbind = true;
717 if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) {
718 if (needbind) {
719 Error("do not use both options bind and unix-bind-tempname");
720 return STAT_NORETRY;
722 needbind = true;
723 needtemp = true;
726 if (!needbind &&
727 (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
728 Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
731 if (opt_unlink_close) {
732 if ((sfd->unlink_close = strdup(name)) == NULL) {
733 Error1("strdup(\"%s\"): out of memory", name);
735 sfd->opt_unlink_close = true;
738 /* save options, because we might have to start again */
739 opts0 = copyopts(opts, GROUP_ALL);
741 /* just a breakable block, helps to avoid goto */
742 do {
743 /* sfd->dtype = DATA_STREAM; // is default */
744 if (needtemp)
745 xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"",
746 abstract, sfd->para.socket.un.tight);
747 /* this function handles AF_UNIX with EPROTOTYPE specially for us */
748 if ((result =
749 _xioopen_connect(sfd,
750 needbind?&us:NULL, uslen,
751 &them.soa, themlen,
752 opts, pf, socktype?socktype:SOCK_STREAM, protocol,
753 needtemp, E_INFO)) == 0)
754 break;
755 if ((errno != EPROTOTYPE
756 #if WITH_ABSTRACT_UNIXSOCKET
757 && !(abstract && errno == ECONNREFUSED)
758 #endif
759 ) || socktype != 0)
760 break;
761 if (needbind)
762 xio_unlink(us.un.sun_path, E_ERROR);
763 dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
765 if (needtemp)
766 xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"",
767 abstract, sfd->para.socket.un.tight);
768 socktype = SOCK_SEQPACKET;
769 if ((result =
770 _xioopen_connect(sfd,
771 needbind?&us:NULL, uslen,
772 (struct sockaddr *)&them, themlen,
773 opts, pf, SOCK_SEQPACKET, protocol,
774 needtemp, E_INFO)) == 0)
775 break;
776 if (errno != EPROTOTYPE && errno != EPROTONOSUPPORT/*AIX*/
777 #if WITH_ABSTRACT_UNIXSOCKET
778 && !(abstract && errno == ECONNREFUSED)
779 #endif
781 break;
782 if (needbind)
783 xio_unlink(us.un.sun_path, E_ERROR);
784 dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
786 if (needtemp)
787 xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"",
788 abstract, sfd->para.socket.un.tight);
789 sfd->peersa = them;
790 sfd->salen = themlen;
791 if ((result =
792 _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
793 opts, xioflags, sfd, groups,
794 pf, SOCK_DGRAM, protocol, needtemp))
795 == 0) {
796 sfd->dtype = XIODATA_RECVFROM;
797 break;
799 } while (0);
801 if (result != 0) {
802 Error3("%s: %s: %s", addrdesc->defname, name, strerror(errno));
803 if (needbind)
804 xio_unlink(us.un.sun_path, E_ERROR);
805 return result;
808 if ((result = _xio_openlate(sfd, opts)) < 0) {
809 return result;
811 return 0;
815 /* returns information that can be used for constructing an environment
816 variable describing the socket address.
817 if idx is 0, this function writes "ADDR" into namebuff and the path into
818 valuebuff, and returns 0 (which means that no more info is there).
819 if idx is != 0, it returns -1
820 namelen and valuelen contain the max. allowed length of output chars in the
821 respective buffer.
822 on error this function returns -1.
825 xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen,
826 char *valuebuff, size_t valuelen,
827 struct sockaddr_un *sa, socklen_t salen, int ipproto) {
828 if (idx != 0) {
829 return -1;
831 strcpy(namebuff, "ADDR");
832 sockaddr_unix_info(sa, salen, valuebuff, valuelen);
833 return 0;
836 static const char tmpchars[] =
837 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
838 static size_t numchars = sizeof(tmpchars)-1;
840 /* Simplyfied version of tempnam(). Uses the current directory when pathx is
841 not absolute.
842 Returns a malloc()'ed string with a probably free name,
843 or NULL when an error occurred */
844 char *xio_tempnam(
845 const char *pathx,
846 bool donttry) /* for abstract, do not check if it exists */
848 int len;
849 char *X; /* begin of XXXXXX */
850 unsigned int i = TMP_MAX;
851 unsigned int r1, r2;
852 uint64_t v;
853 char *patht;
854 char readl[PATH_MAX];
855 int rlc;
857 if (pathx == NULL || pathx[0] == '\0')
858 pathx = "/tmp/socat-bind.XXXXXX";
860 len = strlen(pathx);
861 if (len < 6 || strstr(pathx, "XXXXXX") == NULL) {
862 Warn1("xio_tempnam(\"%s\"): path pattern is not valid", pathx);
863 errno = EINVAL;
864 return NULL;
866 patht = strdup(pathx);
867 if (patht == NULL) {
868 Error1("strdup("F_Zu"): out of memory", strlen(pathx));
869 return patht;
871 X = strstr(patht, "XXXXXX");
873 Debug1("xio_tempnam(\"%s\"): trying path names, suppressing stat() logs",
874 patht);
875 while (i > 0) {
876 r1 = random();
877 r2 = random();
878 v = r2*RAND_MAX + r1;
879 X[0] = tmpchars[v%numchars];
880 v /= numchars;
881 X[1] = tmpchars[v%numchars];
882 v /= numchars;
883 X[2] = tmpchars[v%numchars];
884 v /= numchars;
885 X[3] = tmpchars[v%numchars];
886 v /= numchars;
887 X[4] = tmpchars[v%numchars];
888 v /= numchars;
889 X[5] = tmpchars[v%numchars];
890 v /= numchars;
892 if (donttry)
893 return patht;
895 /* readlink() might be faster than lstat() */
896 rlc = readlink(patht, readl, sizeof(readl));
897 if (rlc < 0 && errno == ENOENT)
898 break;
900 --i;
903 if (i == 0) {
904 errno = EEXIST;
905 return NULL;
908 return patht;
911 #endif /* WITH_UNIX */