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 */
4 
5 /* this file contains the source for opening addresses of UNIX socket type */
6 
7 #include "xiosysincludes.h"
8 #include "xioopen.h"
9 
10 #include "xio-socket.h"
11 #include "xio-listen.h"
12 #include "xio-unix.h"
13 #include "xio-named.h"
14 
15 
16 #if WITH_UNIX
17 
18 /* to avoid unneccessary "live" 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
26 
27 static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3);
28 static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3);
29 static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3);
30 static int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3);
31 static
32 int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
33 		      int xioflags, xiofile_t *xxfd, unsigned groups,
34 		      int abstract, int dummy2, int dummy3);
35 static
36 int xioopen_unix_client(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3);
37 
38 /* the first free parameter is 0 for "normal" unix domain sockets, or 1 for
39    abstract unix sockets (Linux); the second and third free parameter are
40    unsused */
41 const struct addrdesc xioaddr_unix_connect = { "unix-connect",  3, xioopen_unix_connect,  GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          0, 0, 0 HELP(":<filename>") };
42 #if WITH_LISTEN
43 const struct addrdesc xioaddr_unix_listen  = { "unix-listen",   3, xioopen_unix_listen,   GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_LISTEN|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<filename>") };
44 #endif /* WITH_LISTEN */
45 const struct addrdesc xioaddr_unix_sendto  = { "unix-sendto",   3, xioopen_unix_sendto,   GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          0, 0, 0 HELP(":<filename>") };
46 const struct addrdesc xioaddr_unix_recvfrom= { "unix-recvfrom", 3, xioopen_unix_recvfrom, GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY|GROUP_CHILD,              0, 0, 0 HELP(":<filename>") };
47 const struct addrdesc xioaddr_unix_recv    = { "unix-recv",     1, xioopen_unix_recv,     GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          0, 0, 0 HELP(":<filename>") };
48 const struct addrdesc xioaddr_unix_client  = { "unix-client",   3, xioopen_unix_client,   GROUP_FD|GROUP_NAMED|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          0, 0, 0 HELP(":<filename>") };
49 #if WITH_ABSTRACT_UNIXSOCKET
50 const struct addrdesc xioaddr_abstract_connect = { "abstract-connect",  3, xioopen_unix_connect,  GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          1, 0, 0 HELP(":<filename>") };
51 #if WITH_LISTEN
52 const struct addrdesc xioaddr_abstract_listen  = { "abstract-listen",   3, xioopen_unix_listen,   GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_LISTEN|GROUP_CHILD|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
53 #endif /* WITH_LISTEN */
54 const struct addrdesc xioaddr_abstract_sendto  = { "abstract-sendto",   3, xioopen_unix_sendto,   GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          1, 0, 0 HELP(":<filename>") };
55 const struct addrdesc xioaddr_abstract_recvfrom= { "abstract-recvfrom", 3, xioopen_unix_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY|GROUP_CHILD,              1, 0, 0 HELP(":<filename>") };
56 const struct addrdesc xioaddr_abstract_recv    = { "abstract-recv",     1, xioopen_unix_recv,     GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          1, 0, 0 HELP(":<filename>") };
57 const struct addrdesc xioaddr_abstract_client  = { "abstract-client",   3, xioopen_unix_client,   GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY,                          1, 0, 0 HELP(":<filename>") };
58 #endif /* WITH_ABSTRACT_UNIXSOCKET */
59 
60 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) };
61 
62 
63 /* fills the socket address struct and returns its effective length.
64    abstract is usually 0;  != 0 generates an abstract socket address on Linux.
65    tight!=0 calculates the resulting length from the path length, not from the
66    structures length; this is more common (see option unix-tightsocklen)
67    the struct need not be initialized when calling this function.
68 */
69 socklen_t
xiosetunix(int pf,struct sockaddr_un * saun,const char * path,bool abstract,bool tight)70 xiosetunix(int pf,
71 	   struct sockaddr_un *saun,
72 	   const char *path,
73 	   bool abstract,
74 	   bool tight) {
75    size_t pathlen;
76    socklen_t len;
77 
78    socket_un_init(saun);
79 #ifdef WITH_ABSTRACT_UNIXSOCKET
80    if (abstract) {
81       if ((pathlen = strlen(path)) >= sizeof(saun->sun_path)) {
82 	 Warn2("socket address "F_Zu" characters long, truncating to "F_Zu"",
83 	       pathlen+1, sizeof(saun->sun_path));
84       }
85       saun->sun_path[0] = '\0';	/* so it's abstract */
86       strncpy(saun->sun_path+1, path, sizeof(saun->sun_path)-1);	/* ok */
87       if (tight) {
88 	 len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+
89 	    MIN(pathlen+1, sizeof(saun->sun_path));
90 #if HAVE_STRUCT_SOCKADDR_SALEN
91 	 saun->sun_len = len;
92 #endif
93       } else {
94 	 len = sizeof(struct sockaddr_un);
95       }
96       return len;
97    }
98 #endif /* WITH_ABSTRACT_UNIXSOCKET */
99 
100    if ((pathlen = strlen(path)) > sizeof(saun->sun_path)) {
101       Warn2("unix socket address "F_Zu" characters long, truncating to "F_Zu"",
102 	    pathlen, sizeof(saun->sun_path));
103    }
104    strncpy(saun->sun_path, path, sizeof(saun->sun_path));	/* ok */
105    if (tight) {
106       len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+
107 	 MIN(pathlen, sizeof(saun->sun_path));
108 #if HAVE_STRUCT_SOCKADDR_SALEN
109       saun->sun_len = len;
110 #endif
111    } else {
112       len = sizeof(struct sockaddr_un);
113    }
114    return len;
115 }
116 
117 #if WITH_LISTEN
xioopen_unix_listen(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xxfd,unsigned groups,int abstract,int dummy2,int dummy3)118 static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) {
119    /* we expect the form: filename */
120    const char *name;
121    xiosingle_t *xfd = &xxfd->stream;
122    int pf = PF_UNIX;
123    int socktype = SOCK_STREAM;
124    int protocol = 0;
125    struct sockaddr_un us;
126    socklen_t uslen;
127    struct opt *opts0 = NULL;
128    pid_t pid = Getpid();
129    bool opt_unlink_early = false;
130    bool opt_unlink_close = true;
131    int result;
132 
133    if (argc != 2) {
134       Error2("%s: wrong number of parameters (%d instead of 1)",
135 	     argv[0], argc-1);
136       return STAT_NORETRY;
137    }
138    name = argv[1];
139 
140    xfd->para.socket.un.tight = true;
141    retropt_socket_pf(opts, &pf);
142    xfd->howtoend = END_SHUTDOWN;
143 
144    if (!(ABSTRACT && abstract)) {
145       /* only for non abstract because abstract do not work in file system */
146       retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
147       retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
148    }
149 
150    if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
151    applyopts(-1, opts, PH_INIT);
152    applyopts_named(name, opts, PH_EARLY);	/* umask! */
153    applyopts_offset(xfd, opts);
154    applyopts(-1, opts, PH_EARLY);
155 
156    uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight);
157 
158    if (!(ABSTRACT && abstract)) {
159       if (opt_unlink_early) {
160 	 if (Unlink(name) < 0) {
161 	    if (errno == ENOENT) {
162 	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
163 	    } else {
164 	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
165 	    }
166 	 }
167       } else {
168 	 struct stat buf;
169 	 if (Lstat(name, &buf) == 0) {
170 	    Error1("\"%s\" exists", name);
171 	    return STAT_RETRYLATER;
172 	 }
173       }
174       if (opt_unlink_close) {
175 	 if ((xfd->unlink_close = strdup(name)) == NULL) {
176 	    Error1("strdup(\"%s\"): out of memory", name);
177 	 }
178 	 xfd->opt_unlink_close = true;
179       }
180 
181       /* trying to set user-early, perm-early etc. here is useless because
182 	 file system entry is available only past bind() call. */
183    }
184 
185    opts0 = copyopts(opts, GROUP_ALL);
186 
187    /* this may fork() */
188    if ((result =
189 	xioopen_listen(xfd, xioflags,
190 		       (struct sockaddr *)&us, uslen,
191 		       opts, opts0, pf, socktype, protocol))
192        != 0)
193       return result;
194 
195    if (!(ABSTRACT && abstract)) {
196       if (opt_unlink_close) {
197 	 if (pid != Getpid()) {
198 	    /* in a child process - do not unlink-close here! */
199 	    xfd->opt_unlink_close = false;
200 	 }
201       }
202    }
203 
204    return 0;
205 }
206 #endif /* WITH_LISTEN */
207 
208 
xioopen_unix_connect(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xxfd,unsigned groups,int abstract,int dummy2,int dummy3)209 static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) {
210    /* we expect the form: filename */
211    const char *name;
212    struct single *xfd = &xxfd->stream;
213    const struct opt *namedopt;
214    int pf = PF_UNIX;
215    int socktype = SOCK_STREAM;
216    int protocol = 0;
217    struct sockaddr_un them, us;
218    socklen_t themlen, uslen = sizeof(us);
219    bool needbind = false;
220    bool opt_unlink_close = true;
221    int result;
222 
223    if (argc != 2) {
224       Error2("%s: wrong number of parameters (%d instead of 1)",
225 	     argv[0], argc-1);
226       return STAT_NORETRY;
227    }
228    name = argv[1];
229 
230    xfd->para.socket.un.tight = true;
231    retropt_socket_pf(opts, &pf);
232    xfd->howtoend = END_SHUTDOWN;
233    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return STAT_NORETRY;
234    applyopts(-1, opts, PH_INIT);
235    applyopts_offset(xfd, opts);
236    applyopts(-1, opts, PH_EARLY);
237 
238    themlen = xiosetunix(pf, &them, name, abstract, xfd->para.socket.un.tight);
239 
240    if (!(ABSTRACT && abstract)) {
241       /* only for non abstract because abstract do not work in file system */
242       retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
243    }
244    if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
245 		    (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
246       needbind = true;
247    }
248 
249    if (!needbind &&
250        (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
251       Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
252    }
253 
254    if (opt_unlink_close && needbind) {
255       if ((xfd->unlink_close = strdup(us.sun_path)) == NULL) {
256 	 Error1("strdup(\"%s\"): out of memory", name);
257       }
258       xfd->opt_unlink_close = true;
259    }
260 
261    if ((result =
262 	xioopen_connect(xfd,
263 			needbind?(union sockaddr_union *)&us:NULL, uslen,
264 			(struct sockaddr *)&them, themlen,
265 			opts, pf, socktype, protocol, false)) != 0) {
266       return result;
267    }
268    if ((result = _xio_openlate(xfd, opts)) < 0) {
269       return result;
270    }
271    return STAT_OK;
272 }
273 
274 
xioopen_unix_sendto(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xxfd,unsigned groups,int abstract,int dummy,int dummy3)275 static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy, int dummy3) {
276    /* we expect the form: filename */
277    const char *name;
278    xiosingle_t *xfd = &xxfd->stream;
279    const struct opt *namedopt;
280    int pf = PF_UNIX;
281    int socktype = SOCK_DGRAM;
282    int protocol = 0;
283    union sockaddr_union us;
284    socklen_t uslen = sizeof(us);
285    bool needbind = false;
286    bool opt_unlink_close = true;
287 
288    if (argc != 2) {
289       Error2("%s: wrong number of parameters (%d instead of 1)",
290 	     argv[0], argc-1);
291       return STAT_NORETRY;
292    }
293    name = argv[1];
294 
295    xfd->para.socket.un.tight = true;
296    retropt_socket_pf(opts, &pf);
297    xfd->howtoend = END_SHUTDOWN;
298    applyopts_offset(xfd, opts);
299 
300    xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, xfd->para.socket.un.tight);
301 
302    if (!(ABSTRACT && abstract)) {
303       /* only for non abstract because abstract do not work in file system */
304       retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
305    }
306 
307    xfd->dtype = XIODATA_RECVFROM;
308 
309    if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
310 		   (abstract<<1)| xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
311       needbind = true;
312    }
313 
314    if (!needbind &&
315        (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
316       Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
317    }
318 
319    if (opt_unlink_close && needbind) {
320       if ((xfd->unlink_close = strdup(us.un.sun_path)) == NULL) {
321 	 Error1("strdup(\"%s\"): out of memory", name);
322       }
323       xfd->opt_unlink_close = true;
324    }
325 
326    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
327    applyopts(-1, opts, PH_INIT);
328 
329    return
330       _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
331 			    opts, xioflags, xfd, groups,
332 			    pf, socktype, protocol);
333 }
334 
335 
336 static
xioopen_unix_recvfrom(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xxfd,unsigned groups,int abstract,int dummy2,int dummy3)337 int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
338 		     int xioflags, xiofile_t *xxfd, unsigned groups,
339 		     int abstract, int dummy2, int dummy3) {
340    /* we expect the form: filename */
341    const char *name;
342    xiosingle_t *xfd = &xxfd->stream;
343    int pf = PF_UNIX;
344    int socktype = SOCK_DGRAM;
345    int protocol = 0;
346    struct sockaddr_un us;
347    socklen_t uslen;
348    bool needbind = true;
349    bool opt_unlink_early = false;
350    bool opt_unlink_close = true;
351 
352    if (argc != 2) {
353       Error2("%s: wrong number of parameters (%d instead of 1)",
354 	     argv[0], argc-1);
355       return STAT_NORETRY;
356    }
357    name = argv[1];
358 
359    xfd->para.socket.un.tight = true;
360    retropt_socket_pf(opts, &pf);
361    xfd->howtoend = END_NONE;
362    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return STAT_NORETRY;
363    applyopts(-1, opts, PH_INIT);
364    applyopts_named(name, opts, PH_EARLY);       /* umask! */
365    applyopts_offset(xfd, opts);
366 
367    if (!(ABSTRACT && abstract)) {
368       /* only for non abstract because abstract do not work in file system */
369       retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
370       retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
371    }
372    applyopts(-1, opts, PH_EARLY);
373 
374    uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight);
375 
376 #if 0
377    if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
378 		    (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
379    }
380 #endif
381 
382    if (!(ABSTRACT && abstract)) {
383       if (opt_unlink_early) {
384 	 if (Unlink(name) < 0) {
385 	    if (errno == ENOENT) {
386 	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
387 	    } else {
388 	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
389 	    }
390 	 }
391       } else {
392 	 struct stat buf;
393 	 if (Lstat(name, &buf) == 0) {
394 	    Error1("\"%s\" exists", name);
395 	    return STAT_RETRYLATER;
396 	 }
397       }
398       if (opt_unlink_close) {
399 	 if ((xfd->unlink_close = strdup(name)) == NULL) {
400 	    Error1("strdup(\"%s\"): out of memory", name);
401 	 }
402 	 xfd->opt_unlink_close = true;
403       }
404 
405       /* trying to set user-early, perm-early etc. here is useless because
406 	 file system entry is available only past bind() call. */
407    }
408    applyopts_named(name, opts, PH_EARLY);	/* umask! */
409 
410    xfd->para.socket.la.soa.sa_family = pf;
411 
412    xfd->dtype = XIODATA_RECVFROM_ONE;
413 
414    /* this may fork */
415    return
416       _xioopen_dgram_recvfrom(xfd, xioflags,
417 			      needbind?(struct sockaddr *)&us:NULL, uslen,
418 			      opts, pf, socktype, protocol, E_ERROR);
419 }
420 
421 
422 static
xioopen_unix_recv(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xxfd,unsigned groups,int abstract,int dummy2,int dummy3)423 int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
424 		      int xioflags, xiofile_t *xxfd, unsigned groups,
425 		      int abstract, int dummy2, int dummy3) {
426    /* we expect the form: filename */
427    const char *name;
428    xiosingle_t *xfd = &xxfd->stream;
429    int pf = PF_UNIX;
430    int socktype = SOCK_DGRAM;
431    int protocol = 0;
432    union sockaddr_union us;
433    socklen_t uslen;
434    bool opt_unlink_early = false;
435    bool opt_unlink_close = true;
436    int result;
437 
438    if (argc != 2) {
439       Error2("%s: wrong number of parameters (%d instead of 1)",
440 	     argv[0], argc-1);
441       return STAT_NORETRY;
442    }
443    name = argv[1];
444 
445    xfd->para.socket.un.tight = true;
446    retropt_socket_pf(opts, &pf);
447    xfd->howtoend = END_SHUTDOWN;
448    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return STAT_NORETRY;
449    applyopts(-1, opts, PH_INIT);
450    applyopts_named(name, opts, PH_EARLY);       /* umask! */
451    applyopts_offset(xfd, opts);
452 
453    if (!(ABSTRACT && abstract)) {
454       /* only for non abstract because abstract do not work in file system */
455       retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
456       retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
457    }
458    applyopts(-1, opts, PH_EARLY);
459 
460    uslen = xiosetunix(pf, &us.un, name, abstract, xfd->para.socket.un.tight);
461 
462 #if 0
463    if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
464 		    (abstract<<1)|xfd->para.socket.un.tight, 0, 0)
465        == STAT_OK) {
466    }
467 #endif
468 
469    if (!(ABSTRACT && abstract)) {
470       if (opt_unlink_early) {
471 	 if (Unlink(name) < 0) {
472 	    if (errno == ENOENT) {
473 	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
474 	    } else {
475 	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
476 	    }
477 	 }
478       } else {
479 	 struct stat buf;
480 	 if (Lstat(name, &buf) == 0) {
481 	    Error1("\"%s\" exists", name);
482 	    return STAT_RETRYLATER;
483 	 }
484       }
485       if (opt_unlink_close) {
486 	 if ((xfd->unlink_close = strdup(name)) == NULL) {
487 	    Error1("strdup(\"%s\"): out of memory", name);
488 	 }
489 	 xfd->opt_unlink_close = true;
490       }
491    }
492    applyopts_named(name, opts, PH_EARLY);	/* umask! */
493 
494    xfd->para.socket.la.soa.sa_family = pf;
495 
496    xfd->dtype = XIODATA_RECV;
497    result = _xioopen_dgram_recv(xfd, xioflags, &us.soa, uslen,
498 				opts, pf, socktype, protocol, E_ERROR);
499    return result;
500 }
501 
502 
xioopen_unix_client(int argc,const char * argv[],struct opt * opts,int xioflags,xiofile_t * xxfd,unsigned groups,int abstract,int dummy2,int dummy3)503 static int xioopen_unix_client(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) {
504    /* we expect the form: filename */
505    if (argc != 2) {
506       Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
507    }
508 
509    return
510       _xioopen_unix_client(&xxfd->stream, xioflags, groups, abstract, opts,
511 			   argv[1]);
512 }
513 
514 /* establishes communication with an existing UNIX type socket. supports stream
515    and datagram socket types: first tries to connect(), but when this fails it
516    falls back to sendto().
517    applies and consumes the following option:
518    PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND,
519    PH_CONNECTED, PH_LATE, ?PH_CONNECT
520    OFUNC_OFFSET,
521    OPT_PROTOCOL_FAMILY, OPT_UNIX_TIGHTSOCKLEN, OPT_UNLINK_CLOSE, OPT_BIND,
522    OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK,
523 */
524 int
_xioopen_unix_client(xiosingle_t * xfd,int xioflags,unsigned groups,int abstract,struct opt * opts,const char * name)525 _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups,
526 		     int abstract, struct opt *opts, const char *name) {
527    const struct opt *namedopt;
528    int pf = PF_UNIX;
529    int socktype = 0;	/* to be determined by server socket type */
530    int protocol = 0;
531    union sockaddr_union them, us;
532    socklen_t themlen, uslen = sizeof(us);
533    bool needbind = false;
534    bool opt_unlink_close = false;
535    struct opt *opts0;
536    int result;
537 
538    xfd->para.socket.un.tight = true;
539    retropt_socket_pf(opts, &pf);
540    xfd->howtoend = END_SHUTDOWN;
541    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return STAT_NORETRY;
542    applyopts(-1, opts, PH_INIT);
543    applyopts_offset(xfd, opts);
544    retropt_int(opts, OPT_SO_TYPE, &socktype);
545    retropt_int(opts, OPT_SO_PROTOTYPE, &protocol);
546    applyopts(-1, opts, PH_EARLY);
547 
548    themlen = xiosetunix(pf, &them.un, name, abstract, xfd->para.socket.un.tight);
549    if (!(ABSTRACT && abstract)) {
550       /* only for non abstract because abstract do not work in file system */
551       retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
552    }
553    if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
554 		    (abstract<<1)|xfd->para.socket.un.tight, 0, 0)
555        != STAT_NOACTION) {
556       needbind = true;
557    }
558 
559    if (!needbind &&
560        (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) {
561       Error1("Option \"%s\" only with bind option", namedopt->desc->defname);
562    }
563 
564    if (opt_unlink_close) {
565       if ((xfd->unlink_close = strdup(name)) == NULL) {
566 	 Error1("strdup(\"%s\"): out of memory", name);
567       }
568       xfd->opt_unlink_close = true;
569    }
570 
571    /* save options, because we might have to start again */
572    opts0 = copyopts(opts, GROUP_ALL);
573 
574    /* just a breakable block, helps to avoid goto */
575    do {
576       /* xfd->dtype = DATA_STREAM; // is default */
577       /* this function handles AF_UNIX with EPROTOTYPE specially for us */
578       if ((result =
579 	xioopen_connect(xfd,
580 			needbind?&us:NULL, uslen,
581 			&them.soa, themlen,
582 			opts, pf, socktype?socktype:SOCK_STREAM, protocol,
583 			false)) == 0)
584 	 break;
585       if (errno != EPROTOTYPE || socktype != 0)
586 	 break;
587       if (needbind)
588 	 Unlink(us.un.sun_path);
589       dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
590 
591       socktype = SOCK_SEQPACKET;
592       if ((result =
593 	xioopen_connect(xfd,
594 			needbind?&us:NULL, uslen,
595 			(struct sockaddr *)&them, themlen,
596 			opts, pf, SOCK_SEQPACKET, protocol,
597 			false)) == 0)
598 	 break;
599       if (errno != EPROTOTYPE)
600 	 break;
601       if (needbind)
602 	 Unlink(us.un.sun_path);
603       dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;
604 
605       xfd->peersa = them;
606       xfd->salen = sizeof(struct sockaddr_un);
607       if ((result =
608 	      _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
609 				    opts, xioflags, xfd, groups,
610 				    pf, SOCK_DGRAM, protocol))
611 	  == 0) {
612 	 xfd->dtype = XIODATA_RECVFROM;
613 	 break;
614       }
615    } while (0);
616 
617    if (result != 0) {
618       if (needbind) {
619 	 Unlink(us.un.sun_path);
620       }
621       return result;
622    }
623 
624    if ((result = _xio_openlate(xfd, opts)) < 0) {
625       return result;
626    }
627    return 0;
628 }
629 
630 
631 /* returns information that can be used for constructing an environment
632    variable describing the socket address.
633    if idx is 0, this function writes "ADDR" into namebuff and the path into
634    valuebuff, and returns 0 (which means that no more info is there).
635    if idx is != 0, it returns -1
636    namelen and valuelen contain the max. allowed length of output chars in the
637    respective buffer.
638    on error this function returns -1.
639 */
640 int
xiosetsockaddrenv_unix(int idx,char * namebuff,size_t namelen,char * valuebuff,size_t valuelen,struct sockaddr_un * sa,socklen_t salen,int ipproto)641 xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen,
642 		       char *valuebuff, size_t valuelen,
643 		       struct sockaddr_un *sa, socklen_t salen, int ipproto) {
644    if (idx != 0) {
645       return -1;
646    }
647    strcpy(namebuff, "ADDR");
648    sockaddr_unix_info(sa, salen, valuebuff, valuelen);
649    return 0;
650 }
651 
652 #endif /* WITH_UNIX */
653