1 /*
2 **  OSSP sa - Socket Abstraction
3 **  Copyright (c) 2001-2005 Ralf S. Engelschall <rse@engelschall.com>
4 **  Copyright (c) 2001-2005 The OSSP Project <http://www.ossp.org/>
5 **  Copyright (c) 2001-2005 Cable & Wireless <http://www.cw.com/>
6 **
7 **  This file is part of OSSP sa, a socket abstraction library which
8 **  can be found at http://www.ossp.org/pkg/lib/sa/.
9 **
10 **  Permission to use, copy, modify, and distribute this software for
11 **  any purpose with or without fee is hereby granted, provided that
12 **  the above copyright notice and this permission notice appear in all
13 **  copies.
14 **
15 **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
16 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
19 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
22 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 **  SUCH DAMAGE.
27 **
28 **  sa.c: socket abstraction library
29 */
30 
31 /* include optional Autoconf header */
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 /* include system API headers */
37 #include <stdio.h>       /* for "s[n]printf()" */
38 #include <stdlib.h>      /* for "malloc()" & friends */
39 #include <stdarg.h>      /* for "va_XXX()" and "va_list" */
40 #include <string.h>      /* for "strXXX()" and "size_t" */
41 #include <sys/types.h>   /* for general prerequisites */
42 #include <ctype.h>       /* for "isXXX()" */
43 #include <errno.h>       /* for "EXXX" */
44 #include <fcntl.h>       /* for "F_XXX" and "O_XXX" */
45 #include <unistd.h>      /* for standard Unix stuff */
46 #include <netdb.h>       /* for "struct prototent" */
47 #include <sys/time.h>    /* for "struct timeval" */
48 #include <sys/un.h>      /* for "struct sockaddr_un" */
49 #include <netinet/in.h>  /* for "struct sockaddr_in[6]" */
50 #include <sys/socket.h>  /* for "PF_XXX", "AF_XXX", "SOCK_XXX" and "SHUT_XX" */
51 #include <arpa/inet.h>   /* for "inet_XtoX" */
52 
53 /* include own API header */
54 #include "sa.h"
55 
56 /* unique library identifier */
57 const char sa_id[] = "OSSP sa";
58 
59 /* support for OSSP ex based exception throwing */
60 #ifdef WITH_EX
61 #include "ex.h"
62 #define SA_RC(rv) \
63     (  (rv) != SA_OK && (ex_catching && !ex_shielding) \
64      ? (ex_throw(sa_id, NULL, (rv)), (rv)) : (rv) )
65 #else
66 #define SA_RC(rv) (rv)
67 #endif /* WITH_EX */
68 
69 /* boolean values */
70 #ifndef FALSE
71 #define FALSE (0)
72 #endif
73 #ifndef TRUE
74 #define TRUE  (!FALSE)
75 #endif
76 
77 /* backward compatibility for AF_LOCAL */
78 #if !defined(AF_LOCAL) && defined(AF_UNIX)
79 #define AF_LOCAL AF_UNIX
80 #endif
81 
82 /* backward compatibility for PF_XXX (still unused) */
83 #if !defined(PF_LOCAL) && defined(AF_LOCAL)
84 #define PF_LOCAL AF_LOCAL
85 #endif
86 #if !defined(PF_INET) && defined(AF_INET)
87 #define PF_INET AF_INET
88 #endif
89 #if !defined(PF_INET6) && defined(AF_INET6)
90 #define PF_INET6 AF_INET6
91 #endif
92 
93 /* backward compatibility for SHUT_XX. Some platforms (like brain-dead
94    OpenUNIX) define those only if _XOPEN_SOURCE is defined, but unfortunately
95    then fail in their other vendor includes due to internal inconsistencies. */
96 #if !defined(SHUT_RD)
97 #define SHUT_RD 0
98 #endif
99 #if !defined(SHUT_WR)
100 #define SHUT_WR 1
101 #endif
102 #if !defined(SHUT_RDWR)
103 #define SHUT_RDWR 2
104 #endif
105 
106 /* backward compatibility for ssize_t */
107 #if defined(HAVE_CONFIG_H) && !defined(HAVE_SSIZE_T)
108 #define ssize_t long
109 #endif
110 
111 /* backward compatibility for O_NONBLOCK */
112 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
113 #define O_NONBLOCK O_NDELAY
114 #endif
115 
116 /* system call structure declaration macros */
117 #define SA_SC_DECLARE_0(rc_t, fn) \
118     struct { \
119         union { void (*any)(void); \
120                 rc_t (*std)(void); \
121                 rc_t (*ctx)(void *); } fptr; \
122         void *fctx; \
123     } sc_##fn;
124 #define SA_SC_DECLARE_1(rc_t, fn, a1_t) \
125     struct { \
126         union { void (*any)(void); \
127                 rc_t (*std)(a1_t); \
128                 rc_t (*ctx)(void *, a1_t); } fptr; \
129         void *fctx; \
130     } sc_##fn;
131 #define SA_SC_DECLARE_2(rc_t, fn, a1_t, a2_t) \
132     struct { \
133         union { void (*any)(void); \
134                 rc_t (*std)(a1_t, a2_t); \
135                 rc_t (*ctx)(void *, a1_t, a2_t); } fptr; \
136         void *fctx; \
137     } sc_##fn;
138 #define SA_SC_DECLARE_3(rc_t, fn, a1_t, a2_t, a3_t) \
139     struct { \
140         union { void (*any)(void); \
141                 rc_t (*std)(a1_t, a2_t, a3_t); \
142                 rc_t (*ctx)(void *, a1_t, a2_t, a3_t); } fptr; \
143         void *fctx; \
144     } sc_##fn;
145 #define SA_SC_DECLARE_4(rc_t, fn, a1_t, a2_t, a3_t, a4_t) \
146     struct { \
147         union { void (*any)(void); \
148                 rc_t (*std)(a1_t, a2_t, a3_t, a4_t); \
149                 rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t); } fptr; \
150         void *fctx; \
151     } sc_##fn;
152 #define SA_SC_DECLARE_5(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t) \
153     struct { \
154         union { void (*any)(void); \
155                 rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t); \
156                 rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t); } fptr; \
157         void *fctx; \
158     } sc_##fn;
159 #define SA_SC_DECLARE_6(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t) \
160     struct { \
161         union { void (*any)(void); \
162                 rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); \
163                 rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); } fptr; \
164         void *fctx; \
165     } sc_##fn;
166 
167 /* system call structure assignment macro */
168 #define SA_SC_ASSIGN(sa, fn, ptr, ctx) \
169     do { \
170         (sa)->scSysCall.sc_##fn.fptr.any = (void (*)(void))(ptr); \
171         (sa)->scSysCall.sc_##fn.fctx = (ctx); \
172     } while (0)
173 
174 /* system call structure assignment macro */
175 #define SA_SC_COPY(sa1, sa2, fn) \
176     do { \
177         (sa1)->scSysCall.sc_##fn.fptr.any = (sa2)->scSysCall.sc_##fn.fptr.any; \
178         (sa1)->scSysCall.sc_##fn.fctx     = (sa2)->scSysCall.sc_##fn.fctx; \
179     } while (0)
180 
181 /* system call function call macros */
182 #define SA_SC_CALL_0(sa, fn) \
183     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
184      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx) \
185      : ((sa)->scSysCall.sc_##fn.fptr.std)(void) )
186 #define SA_SC_CALL_1(sa, fn, a1) \
187     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
188      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1) \
189      : ((sa)->scSysCall.sc_##fn.fptr.std)(a1) )
190 #define SA_SC_CALL_2(sa, fn, a1, a2) \
191     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
192      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2) \
193      : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2) )
194 #define SA_SC_CALL_3(sa, fn, a1, a2, a3) \
195     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
196      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3) \
197      : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3) )
198 #define SA_SC_CALL_4(sa, fn, a1, a2, a3, a4) \
199     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
200      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4) \
201      : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4) )
202 #define SA_SC_CALL_5(sa, fn, a1, a2, a3, a4, a5) \
203     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
204      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5) \
205      : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5) )
206 #define SA_SC_CALL_6(sa, fn, a1, a2, a3, a4, a5, a6) \
207     (   (sa)->scSysCall.sc_##fn.fctx != NULL \
208      ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5, a6) \
209      : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5, a6) )
210 
211 /* system call table */
212 typedef struct {
213     SA_SC_DECLARE_3(int,              connect,       int, const struct sockaddr *, socklen_t)
214     SA_SC_DECLARE_3(int,              accept,        int, struct sockaddr *, socklen_t *)
215     SA_SC_DECLARE_5(int,              select,        int, fd_set *, fd_set *, fd_set *, struct timeval *)
216     SA_SC_DECLARE_3(ssize_t,          read,          int, void *, size_t)
217     SA_SC_DECLARE_3(ssize_t,          write,         int, const void *, size_t)
218     SA_SC_DECLARE_6(ssize_t,          recvfrom,      int, void *, size_t, int, struct sockaddr *, socklen_t *)
219     SA_SC_DECLARE_6(ssize_t,          sendto,        int, const void *, size_t, int, const struct sockaddr *, socklen_t)
220 } sa_syscall_tab_t;
221 
222 /* socket option information */
223 typedef struct {
224     int todo;
225     int value;
226 } sa_optinfo_t;
227 
228 /* socket abstraction object */
229 struct sa_st {
230     sa_type_t        eType;            /* socket type (stream or datagram) */
231     int              fdSocket;         /* socket file descriptor */
232     struct timeval   tvTimeout[4];     /* timeout values (sec, usec) */
233     int              nReadLen;         /* read  buffer current length */
234     int              nReadSize;        /* read  buffer current size */
235     char            *cpReadBuf;        /* read  buffer memory chunk */
236     int              nWriteLen;        /* write buffer current length */
237     int              nWriteSize;       /* write buffer current size */
238     char            *cpWriteBuf;       /* write buffer memory chunk */
239     sa_syscall_tab_t scSysCall;        /* table of system calls */
240     sa_optinfo_t     optInfo[5];       /* option storage */
241 };
242 
243 /* socket address abstraction object */
244 struct sa_addr_st {
245     int              nFamily;          /* the socket family (AF_XXX) */
246     struct sockaddr *saBuf;            /* the "struct sockaddr_xx" actually */
247     socklen_t        slBuf;            /* the length of "struct sockaddr_xx" */
248 };
249 
250 /* handy struct timeval check */
251 #define SA_TVISZERO(tv) \
252     ((tv).tv_sec == 0 && (tv).tv_usec == 0)
253 
254 /* convert Internet address from presentation to network format */
255 #ifndef HAVE_GETADDRINFO
sa_inet_pton(int family,const char * strptr,void * addrptr)256 static int sa_inet_pton(int family, const char *strptr, void *addrptr)
257 {
258 #ifdef HAVE_INET_PTON
259     return inet_pton(family, strptr, addrptr);
260 #else
261     struct in_addr in_val;
262 
263     if (family == AF_INET) {
264 #if defined(HAVE_INET_ATON)
265         /* at least for IPv4 we can rely on the old inet_aton(3)
266            and for IPv6 inet_pton(3) would exist anyway */
267         if (inet_aton(strptr, &in_val) == 0)
268             return 0;
269         memcpy(addrptr, &in_val, sizeof(struct in_addr));
270         return 1;
271 #elif defined(HAVE_INET_ADDR)
272         /* at least for IPv4 try to rely on the even older inet_addr(3) */
273         memset(&in_val, '\0', sizeof(in_val));
274         if ((in_val.s_addr = inet_addr(strptr)) == ((in_addr_t)-1))
275             return 0;
276         memcpy(addrptr, &in_val, sizeof(struct in_addr));
277         return 1;
278 #endif
279     }
280     errno = EAFNOSUPPORT;
281     return 0;
282 #endif
283 }
284 #endif /* !HAVE_GETADDRINFO */
285 
286 /* convert Internet address from network to presentation format */
sa_inet_ntop(int family,const void * src,char * dst,size_t size)287 static const char *sa_inet_ntop(int family, const void *src, char *dst, size_t size)
288 {
289 #ifdef HAVE_INET_NTOP
290     return inet_ntop(family, src, dst, size);
291 #else
292 #ifdef HAVE_INET_NTOA
293     char *cp;
294     int n;
295 #endif
296 
297     if (family == AF_INET) {
298 #ifdef HAVE_INET_NTOA
299         /* at least for IPv4 we can rely on the old inet_ntoa(3)
300            and for IPv6 inet_ntop(3) would exist anyway */
301         if ((cp = inet_ntoa(*((struct in_addr *)src))) == NULL)
302             return NULL;
303         n = strlen(cp);
304         if (n > size-1)
305             n = size-1;
306         memcpy(dst, cp, n);
307         dst[n] = '\0';
308         return dst;
309 #endif
310     }
311     errno = EAFNOSUPPORT;
312     return NULL;
313 #endif
314 }
315 
316 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
sa_mvxprintf(int (* output)(void * ctx,const char * buffer,size_t bufsize),void * ctx,const char * format,va_list ap)317 static int sa_mvxprintf(int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, const char *format, va_list ap)
318 {
319     /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
320     char ibuf[((sizeof(int)*8)/3)+10];
321     char *cp;
322     char c;
323     int d;
324     int n;
325     int bytes;
326 
327     if (format == NULL)
328         return -1;
329     bytes = 0;
330     while (*format != '\0') {
331         if (*format == '%') {
332             c = *(format+1);
333             if (c == '%') {
334                 /* expand "%%" */
335                 cp = &c;
336                 n = (int)sizeof(char);
337             }
338             else if (c == 'c') {
339                 /* expand "%c" */
340                 c = (char)va_arg(ap, int);
341                 cp = &c;
342                 n = (int)sizeof(char);
343             }
344             else if (c == 's') {
345                 /* expand "%s" */
346                 if ((cp = (char *)va_arg(ap, char *)) == NULL)
347                     cp = "(null)";
348                 n = (int)strlen(cp);
349             }
350             else if (c == 'd') {
351                 /* expand "%d" */
352                 d = (int)va_arg(ap, int);
353 #ifdef HAVE_SNPRINTF
354                 snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
355 #else
356                 sprintf(ibuf, "%d", d);                /* implicitly secure */
357 #endif
358                 cp = ibuf;
359                 n = (int)strlen(cp);
360             }
361             else {
362                 /* any other "%X" */
363                 cp = (char *)format;
364                 n  = 2;
365             }
366             format += 2;
367         }
368         else {
369             /* plain text */
370             cp = (char *)format;
371             if ((format = strchr(cp, '%')) == NULL)
372                 format = strchr(cp, '\0');
373             n = (int)(format - cp);
374         }
375         /* perform output operation */
376         if (output != NULL)
377             if ((n = output(ctx, cp, (size_t)n)) == -1)
378                 break;
379         bytes += n;
380     }
381     return bytes;
382 }
383 
384 /* output callback function context for sa_mvsnprintf() */
385 typedef struct {
386     char *bufptr;
387     size_t buflen;
388 } sa_mvsnprintf_cb_t;
389 
390 /* output callback function for sa_mvsnprintf() */
sa_mvsnprintf_cb(void * _ctx,const char * buffer,size_t bufsize)391 static int sa_mvsnprintf_cb(void *_ctx, const char *buffer, size_t bufsize)
392 {
393     sa_mvsnprintf_cb_t *ctx = (sa_mvsnprintf_cb_t *)_ctx;
394 
395     if (bufsize > ctx->buflen)
396         return -1;
397     memcpy(ctx->bufptr, buffer, bufsize);
398     ctx->bufptr += bufsize;
399     ctx->buflen -= bufsize;
400     return (int)bufsize;
401 }
402 
403 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
sa_mvsnprintf(char * buffer,size_t bufsize,const char * format,va_list ap)404 static int sa_mvsnprintf(char *buffer, size_t bufsize, const char *format, va_list ap)
405 {
406     int n;
407     sa_mvsnprintf_cb_t ctx;
408 
409     if (format == NULL)
410         return -1;
411     if (buffer != NULL && bufsize == 0)
412         return -1;
413     if (buffer == NULL)
414         /* just determine output length */
415         n = sa_mvxprintf(NULL, NULL, format, ap);
416     else {
417         /* perform real output */
418         ctx.bufptr = buffer;
419         ctx.buflen = bufsize;
420         n = sa_mvxprintf(sa_mvsnprintf_cb, &ctx, format, ap);
421         if (n != -1 && ctx.buflen == 0)
422             n = -1;
423         if (n != -1)
424             *(ctx.bufptr) = '\0';
425     }
426     return n;
427 }
428 
429 /* minimal snprintf(3) variant which supports %{c,s,d} only */
sa_msnprintf(char * buffer,size_t bufsize,const char * format,...)430 static int sa_msnprintf(char *buffer, size_t bufsize, const char *format, ...)
431 {
432     int chars;
433     va_list ap;
434 
435     /* pass through to va_list based variant */
436     va_start(ap, format);
437     chars = sa_mvsnprintf(buffer, bufsize, format, ap);
438     va_end(ap);
439 
440     return chars;
441 }
442 
443 /* create address object */
sa_addr_create(sa_addr_t ** saap)444 sa_rc_t sa_addr_create(sa_addr_t **saap)
445 {
446     sa_addr_t *saa;
447 
448     /* argument sanity check(s) */
449     if (saap == NULL)
450         return SA_RC(SA_ERR_ARG);
451 
452     /* allocate and initialize new address object */
453     if ((saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL)
454         return SA_RC(SA_ERR_MEM);
455     saa->nFamily = 0;
456     saa->saBuf   = NULL;
457     saa->slBuf   = 0;
458 
459     /* pass object to caller */
460     *saap = saa;
461 
462     return SA_OK;
463 }
464 
465 /* destroy address object */
sa_addr_destroy(sa_addr_t * saa)466 sa_rc_t sa_addr_destroy(sa_addr_t *saa)
467 {
468     /* argument sanity check(s) */
469     if (saa == NULL)
470         return SA_RC(SA_ERR_ARG);
471 
472     /* free address objects and sub-object(s) */
473     if (saa->saBuf != NULL)
474         free(saa->saBuf);
475     free(saa);
476 
477     return SA_OK;
478 }
479 
480 /* import URI into address object */
sa_addr_u2a(sa_addr_t * saa,const char * uri,...)481 sa_rc_t sa_addr_u2a(sa_addr_t *saa, const char *uri, ...)
482 {
483     va_list ap;
484     int sf;
485     socklen_t sl;
486     struct sockaddr *sa;
487     struct sockaddr_un un;
488 #ifdef HAVE_GETADDRINFO
489     struct addrinfo ai_hints;
490     struct addrinfo *ai = NULL;
491     int err;
492 #else
493     struct sockaddr_in sa4;
494 #ifdef AF_INET6
495     struct sockaddr_in6 sa6;
496 #endif
497     struct hostent *he;
498 #endif
499     struct servent *se;
500     int bIPv6;
501     int bNumeric;
502     const char *cpHost;
503     const char *cpPort;
504     char *cpProto;
505     unsigned int nPort;
506     const char *cpPath;
507     char uribuf[1024];
508     char *cp;
509     int i;
510     size_t n;
511     int k;
512 
513     /* argument sanity check(s) */
514     if (saa == NULL || uri == NULL)
515         return SA_RC(SA_ERR_ARG);
516 
517     /* on-the-fly create or just take over URI */
518     va_start(ap, uri);
519     k = sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap);
520     va_end(ap);
521     if (k == -1)
522         return SA_RC(SA_ERR_MEM);
523 
524     /* initialize result variables */
525     sa = NULL;
526     sl = 0;
527     sf = 0;
528 
529     /* parse URI and resolve contents.
530        The following syntax is recognized:
531        - unix:<path>
532        - inet://<host>:<port>[#(tcp|udp)] */
533     uri = uribuf;
534     if (strncmp(uri, "unix:", 5) == 0) {
535         /* parse URI */
536         cpPath = uri+5;
537 
538         /* mandatory(!) socket address structure initialization */
539         memset(&un, 0, sizeof(un));
540 
541         /* fill-in socket address structure */
542         n = strlen(cpPath);
543         if (n == 0)
544             return SA_RC(SA_ERR_ARG);
545         if ((n+1) > sizeof(un.sun_path))
546             return SA_RC(SA_ERR_MEM);
547         if (cpPath[0] != '/') {
548             if (getcwd(un.sun_path, sizeof(un.sun_path)-(n+1)) == NULL)
549                 return SA_RC(SA_ERR_MEM);
550             cp = un.sun_path + strlen(un.sun_path);
551         }
552         else
553             cp = un.sun_path;
554         memcpy(cp, cpPath, n+1);
555         un.sun_family = AF_LOCAL;
556 
557         /* provide results */
558         sa = (struct sockaddr *)&un;
559         sl = (socklen_t)sizeof(un);
560         sf = AF_LOCAL;
561     }
562     else if (strncmp(uri, "inet://", 7) == 0) {
563         /* parse URI into host, port and protocol parts */
564         cpHost = uri+7;
565         bIPv6 = FALSE;
566         if (cpHost[0] == '[') {
567             /* IPv6 address (see RFC2732) */
568 #ifndef AF_INET6
569             return SA_RC(SA_ERR_IMP);
570 #else
571             bIPv6 = TRUE;
572             cpHost++;
573             if ((cp = strchr(cpHost, ']')) == NULL)
574                 return SA_RC(SA_ERR_ARG);
575             *cp++ = '\0';
576             if (*cp != ':')
577                 return SA_RC(SA_ERR_ARG);
578             cp++;
579 #endif
580         }
581         else {
582             /* IPv4 address or hostname */
583             if ((cp = strrchr(cpHost, ':')) == NULL)
584                 return SA_RC(SA_ERR_ARG);
585             *cp++ = '\0';
586         }
587         cpPort = cp;
588         cpProto = "tcp";
589         if ((cp = strchr(cpPort, '#')) != NULL) {
590             *cp++ = '\0';
591             cpProto = cp;
592         }
593 
594         /* resolve port */
595         nPort = 0;
596         bNumeric = 1;
597         for (i = 0; cpPort[i] != '\0'; i++) {
598             if (!isdigit((int)cpPort[i])) {
599                 bNumeric = 0;
600                 break;
601             }
602         }
603         if (bNumeric)
604             nPort = (unsigned int)atoi(cpPort);
605         else {
606             if ((se = getservbyname(cpPort, cpProto)) == NULL)
607                 return SA_RC(SA_ERR_SYS);
608             nPort = ntohs(se->s_port);
609         }
610 
611 #ifdef HAVE_GETADDRINFO
612         memset(&ai_hints, 0, sizeof(ai_hints));
613         ai_hints.ai_family = PF_UNSPEC;
614         if ((err = getaddrinfo(cpHost, NULL, &ai_hints, &ai)) != 0) {
615             if (err == EAI_MEMORY)
616                 return SA_RC(SA_ERR_MEM);
617             else if (err == EAI_SYSTEM)
618                 return SA_RC(SA_ERR_SYS);
619             else
620                 return SA_RC(SA_ERR_ARG);
621         }
622         sa = ai->ai_addr;
623         sl = ai->ai_addrlen;
624         sf = ai->ai_family;
625         if (sf == AF_INET)
626             ((struct sockaddr_in *)sa)->sin_port = htons(nPort);
627         else if (sf == AF_INET6)
628             ((struct sockaddr_in6 *)sa)->sin6_port = htons(nPort);
629         else
630             return SA_RC(SA_ERR_ARG);
631 #else /* !HAVE_GETADDRINFO */
632 
633         /* mandatory(!) socket address structure initialization */
634         memset(&sa4, 0, sizeof(sa4));
635 #ifdef AF_INET6
636         memset(&sa6, 0, sizeof(sa6));
637 #endif
638 
639         /* resolve host by trying to parse it as either directly an IPv4 or
640            IPv6 address or by resolving it to either an IPv4 or IPv6 address */
641         if (!bIPv6 && sa_inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) {
642             sa4.sin_family = AF_INET;
643             sa4.sin_port = htons(nPort);
644             sa = (struct sockaddr *)&sa4;
645             sl = (socklen_t)sizeof(sa4);
646             sf = AF_INET;
647         }
648 #ifdef AF_INET6
649         else if (bIPv6 && sa_inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) {
650             sa6.sin6_family = AF_INET6;
651             sa6.sin6_port = htons(nPort);
652             sa = (struct sockaddr *)&sa6;
653             sl = (socklen_t)sizeof(sa6);
654             sf = AF_INET6;
655         }
656 #endif
657         else if ((he = gethostbyname(cpHost)) != NULL) {
658             if (he->h_addrtype == AF_INET) {
659                 sa4.sin_family = AF_INET;
660                 sa4.sin_port = htons(nPort);
661                 memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0],
662                        sizeof(sa4.sin_addr.s_addr));
663                 sa = (struct sockaddr *)&sa4;
664                 sl = (socklen_t)sizeof(sa4);
665                 sf = AF_INET;
666             }
667 #ifdef AF_INET6
668             else if (he->h_addrtype == AF_INET6) {
669                 sa6.sin6_family = AF_INET6;
670                 sa6.sin6_port = htons(nPort);
671                 memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0],
672                        sizeof(sa6.sin6_addr.s6_addr));
673                 sa = (struct sockaddr *)&sa6;
674                 sl = (socklen_t)sizeof(sa6);
675                 sf = AF_INET6;
676             }
677 #endif
678             else
679                 return SA_RC(SA_ERR_ARG);
680         }
681         else
682             return SA_RC(SA_ERR_ARG);
683 #endif /* !HAVE_GETADDRINFO */
684     }
685     else
686         return SA_RC(SA_ERR_ARG);
687 
688     /* fill-in result address structure */
689     if (saa->saBuf != NULL)
690         free(saa->saBuf);
691     if ((saa->saBuf = (struct sockaddr *)malloc((size_t)sl)) == NULL)
692         return SA_RC(SA_ERR_MEM);
693     memcpy(saa->saBuf, sa, (size_t)sl);
694     saa->slBuf = sl;
695     saa->nFamily = (int)sf;
696 
697 #ifdef HAVE_GETADDRINFO
698     if (ai != NULL)
699         freeaddrinfo(ai);
700 #endif
701 
702     return SA_OK;
703 }
704 
705 /* import "struct sockaddr" into address object */
sa_addr_s2a(sa_addr_t * saa,const struct sockaddr * sabuf,socklen_t salen)706 sa_rc_t sa_addr_s2a(sa_addr_t *saa, const struct sockaddr *sabuf, socklen_t salen)
707 {
708     /* argument sanity check(s) */
709     if (saa == NULL || sabuf == NULL || salen == 0)
710         return SA_RC(SA_ERR_ARG);
711 
712     /* make sure we import only supported addresses */
713     if (!(   sabuf->sa_family == AF_LOCAL
714           || sabuf->sa_family == AF_INET
715 #ifdef AF_INET6
716           || sabuf->sa_family == AF_INET6
717 #endif
718          ))
719         return SA_RC(SA_ERR_USE);
720 
721     /* create result address structure */
722     if (saa->saBuf != NULL)
723         free(saa->saBuf);
724     if ((saa->saBuf = (struct sockaddr *)malloc((size_t)salen)) == NULL)
725         return SA_RC(SA_ERR_MEM);
726     memcpy(saa->saBuf, sabuf, (size_t)salen);
727     saa->slBuf = salen;
728 
729     /* remember family */
730     saa->nFamily = (int)(sabuf->sa_family);
731 
732     return SA_OK;
733 }
734 
735 /* export address object into URI */
sa_addr_a2u(const sa_addr_t * saa,char ** uri)736 sa_rc_t sa_addr_a2u(const sa_addr_t *saa, char **uri)
737 {
738     char uribuf[1024];
739     struct sockaddr_un *un;
740     struct sockaddr_in *sa4;
741 #ifdef AF_INET6
742     struct sockaddr_in6 *sa6;
743 #endif
744     char caHost[512];
745     unsigned int nPort;
746 
747     /* argument sanity check(s) */
748     if (saa == NULL || uri == NULL)
749         return SA_RC(SA_ERR_ARG);
750 
751     /* export object contents */
752     if (saa->nFamily == AF_LOCAL) {
753         un = (struct sockaddr_un *)((void *)saa->saBuf);
754         if (   (   saa->slBuf >= (socklen_t)(&(((struct sockaddr_un *)0)->sun_path[0]))
755                 && un->sun_path[0] == '\0')
756             || (size_t)(saa->slBuf) < sizeof(struct sockaddr_un)) {
757             /* in case the remote side of a Unix Domain socket was not
758                bound, a "struct sockaddr_un" can occur with a length less
759                than the expected one. Then there is actually no path at all.
760                This has been verified under FreeBSD, Linux and Solaris. */
761             if (sa_msnprintf(uribuf, sizeof(uribuf), "unix:/NOT-BOUND") == -1)
762                 return SA_RC(SA_ERR_FMT);
763         }
764         else {
765             if (sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path) == -1)
766                 return SA_RC(SA_ERR_FMT);
767         }
768     }
769     else if (saa->nFamily == AF_INET) {
770         sa4 = (struct sockaddr_in *)((void *)saa->saBuf);
771         if (sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)) == NULL)
772             return SA_RC(SA_ERR_NET);
773         nPort = ntohs(sa4->sin_port);
774         if (sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort) == -1)
775             return SA_RC(SA_ERR_FMT);
776     }
777 #ifdef AF_INET6
778     else if (saa->nFamily == AF_INET6) {
779         sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf);
780         if (sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)) == NULL)
781             return SA_RC(SA_ERR_NET);
782         nPort = ntohs(sa6->sin6_port);
783         if (sa_msnprintf(uribuf, sizeof(uribuf), "inet://[%s]:%d", caHost, nPort) == -1)
784             return SA_RC(SA_ERR_FMT);
785     }
786 #endif
787     else
788         return SA_RC(SA_ERR_INT);
789 
790     /* pass result to caller */
791     *uri = strdup(uribuf);
792 
793     return SA_OK;
794 }
795 
796 /* export address object into "struct sockaddr" */
sa_addr_a2s(const sa_addr_t * saa,struct sockaddr ** sabuf,socklen_t * salen)797 sa_rc_t sa_addr_a2s(const sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen)
798 {
799     /* argument sanity check(s) */
800     if (saa == NULL || sabuf == NULL || salen == 0)
801         return SA_RC(SA_ERR_ARG);
802 
803     /* export underlying address structure */
804     if ((*sabuf = (struct sockaddr *)malloc((size_t)saa->slBuf)) == NULL)
805         return SA_RC(SA_ERR_MEM);
806     memmove(*sabuf, saa->saBuf, (size_t)saa->slBuf);
807     *salen = saa->slBuf;
808 
809     return SA_OK;
810 }
811 
sa_addr_match(const sa_addr_t * saa1,const sa_addr_t * saa2,int prefixlen)812 sa_rc_t sa_addr_match(const sa_addr_t *saa1, const sa_addr_t *saa2, int prefixlen)
813 {
814     const unsigned char *ucp1, *ucp2;
815     unsigned int uc1, uc2, mask;
816     size_t l1, l2;
817     int nBytes;
818     int nBits;
819 #ifdef AF_INET6
820     int i;
821     const unsigned char *ucp0;
822 #endif
823     unsigned int np1, np2;
824     int bMatchPort;
825 
826     /* argument sanity check(s) */
827     if (saa1 == NULL || saa2 == NULL)
828         return SA_RC(SA_ERR_ARG);
829 
830     /* short circuiting for wildcard matching */
831     if (prefixlen == 0)
832         return SA_OK;
833 
834     /* determine address representation pointer and size */
835     if (saa1->nFamily == AF_LOCAL) {
836         np1  = 0;
837         np2  = 0;
838         ucp1 = (const unsigned char *)(((struct sockaddr_un *)saa1->saBuf)->sun_path);
839         ucp2 = (const unsigned char *)(((struct sockaddr_un *)saa2->saBuf)->sun_path);
840         l1 = strlen(((struct sockaddr_un *)saa1->saBuf)->sun_path) * 8;
841         l2 = strlen(((struct sockaddr_un *)saa2->saBuf)->sun_path) * 8;
842         if (prefixlen < 0) {
843             if (l1 != l2)
844                 return SA_RC(SA_ERR_MTC);
845             nBits = (int)l1;
846         }
847         else {
848             if ((int)l1 < prefixlen || (int)l2 < prefixlen)
849                 return SA_RC(SA_ERR_MTC);
850             nBits = prefixlen;
851         }
852     }
853 #ifdef AF_INET6
854     else if (   (saa1->nFamily == AF_INET  && saa2->nFamily == AF_INET6)
855              || (saa1->nFamily == AF_INET6 && saa2->nFamily == AF_INET )) {
856         /* special case of comparing a regular IPv4 address (1.2.3.4) with an
857            "IPv4-mapped IPv6 address" (::ffff:1.2.3.4). For details see RFC 2373. */
858         if (saa1->nFamily == AF_INET6) {
859             np1  = (unsigned int)(((struct sockaddr_in6 *)saa1->saBuf)->sin6_port);
860             np2  = (unsigned int)(((struct sockaddr_in  *)saa2->saBuf)->sin_port);
861             ucp1 = (const unsigned char *)&(((struct sockaddr_in6 *)saa1->saBuf)->sin6_addr);
862             ucp2 = (const unsigned char *)&(((struct sockaddr_in  *)saa2->saBuf)->sin_addr);
863             ucp0 = ucp1;
864             ucp1 += 12;
865         }
866         else {
867             np1  = (unsigned int)(((struct sockaddr_in  *)saa1->saBuf)->sin_port);
868             np2  = (unsigned int)(((struct sockaddr_in6 *)saa2->saBuf)->sin6_port);
869             ucp1 = (const unsigned char *)&(((struct sockaddr_in  *)saa1->saBuf)->sin_addr);
870             ucp2 = (const unsigned char *)&(((struct sockaddr_in6 *)saa2->saBuf)->sin6_addr);
871             ucp0 = ucp2;
872             ucp2 += 12;
873         }
874         for (i = 0; i < 10; i++)
875             if ((int)ucp0[i] != 0x00)
876                 return SA_RC(SA_ERR_MTC);
877         if (!((int)ucp0[10] == 0xFF && (int)ucp0[11] == 0xFF))
878             return SA_RC(SA_ERR_MTC);
879         nBits = 32;
880     }
881 #endif
882     else if (saa1->nFamily == AF_INET) {
883         np1  = (unsigned int)(((struct sockaddr_in *)saa1->saBuf)->sin_port);
884         np2  = (unsigned int)(((struct sockaddr_in *)saa2->saBuf)->sin_port);
885         ucp1 = (const unsigned char *)&(((struct sockaddr_in *)saa1->saBuf)->sin_addr);
886         ucp2 = (const unsigned char *)&(((struct sockaddr_in *)saa2->saBuf)->sin_addr);
887         nBits = 32;
888     }
889 #ifdef AF_INET6
890     else if (saa1->nFamily == AF_INET6) {
891         np1  = (unsigned int)(((struct sockaddr_in6 *)saa1->saBuf)->sin6_port);
892         np2  = (unsigned int)(((struct sockaddr_in6 *)saa2->saBuf)->sin6_port);
893         ucp1 = (const unsigned char *)&(((struct sockaddr_in6 *)saa1->saBuf)->sin6_addr);
894         ucp2 = (const unsigned char *)&(((struct sockaddr_in6 *)saa2->saBuf)->sin6_addr);
895         nBits = 128;
896     }
897 #endif
898     else
899         return SA_RC(SA_ERR_INT);
900 
901     /* make sure we do not compare than possible */
902     if (prefixlen > (nBits+1))
903         return SA_RC(SA_ERR_ARG);
904 
905     /* support equal matching (= all bits plus optionally port) */
906     bMatchPort = FALSE;
907     if (prefixlen < 0) {
908         if (prefixlen < -1)
909             bMatchPort = TRUE;
910         prefixlen = nBits;
911     }
912 
913     /* perform address representation comparison
914        (assumption guaranteed by API: network byte order is used) */
915     nBytes = (prefixlen / 8);
916     nBits  = (prefixlen % 8);
917     if (nBytes > 0) {
918         if (memcmp(ucp1, ucp2, (size_t)nBytes) != 0)
919             return SA_RC(SA_ERR_MTC);
920     }
921     if (nBits > 0) {
922         uc1 = (unsigned int)ucp1[nBytes];
923         uc2 = (unsigned int)ucp2[nBytes];
924         mask = ((unsigned int)0xFF << (8-nBits)) & (unsigned int)0xFF;
925         if ((uc1 & mask) != (uc2 & mask))
926             return SA_RC(SA_ERR_MTC);
927     }
928 
929     /* optionally perform additional port matching */
930     if (bMatchPort)
931         if (np1 != np2)
932             return SA_RC(SA_ERR_MTC);
933 
934     return SA_OK;
935 }
936 
937 /* set timeouts if timeouts or done in kernel */
sa_socket_settimeouts(const sa_t * sa)938 static sa_rc_t sa_socket_settimeouts(const sa_t *sa)
939 {
940     /* stop processing if socket is still not allocated */
941     if (sa->fdSocket == -1)
942         return SA_OK;
943 
944 #if defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)
945     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
946         if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_RCVTIMEO,
947                        (const void *)(&sa->tvTimeout[SA_TIMEOUT_READ]),
948                        (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_READ]))) < 0)
949             return SA_RC(SA_ERR_SYS);
950     }
951     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
952         if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO,
953                        (const void *)(&sa->tvTimeout[SA_TIMEOUT_WRITE]),
954                        (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE]))) < 0)
955             return SA_RC(SA_ERR_SYS);
956     }
957 #endif
958     return SA_OK;
959 }
960 
961 /* set socket options */
sa_socket_setoptions(sa_t * sa)962 static sa_rc_t sa_socket_setoptions(sa_t *sa)
963 {
964     int i;
965     sa_rc_t rv;
966 
967     /* stop processing if socket is still not allocated */
968     if (sa->fdSocket == -1)
969         return SA_OK;
970 
971     /* check for pending options */
972     rv = SA_OK;
973     for (i = 0; i < (int)(sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) {
974         if (sa->optInfo[i].todo) {
975             switch (i) {
976                 /* enable/disable Nagle's Algorithm (see RFC898) */
977                 case SA_OPTION_NAGLE: {
978 #if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
979                     int mode = sa->optInfo[i].value;
980                     if (setsockopt(sa->fdSocket, IPPROTO_TCP, TCP_NODELAY,
981                                    (const void *)&mode,
982                                    (socklen_t)sizeof(mode)) < 0)
983                         rv = SA_ERR_SYS;
984                     else
985                         sa->optInfo[i].todo = FALSE;
986 #endif
987                     break;
988                 }
989                 /* enable/disable linger close semantics */
990                 case SA_OPTION_LINGER: {
991 #if defined(SO_LINGER)
992                     struct linger linger;
993                     linger.l_onoff  = (sa->optInfo[i].value == 0 ? 0 : 1);
994                     linger.l_linger = (sa->optInfo[i].value <= 0 ? 0 : sa->optInfo[i].value);
995                     if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_LINGER,
996                                    (const void *)&linger,
997                                    (socklen_t)sizeof(struct linger)) < 0)
998                         rv = SA_ERR_SYS;
999                     else
1000                         sa->optInfo[i].todo = FALSE;
1001 #endif
1002                     break;
1003                 }
1004                 /* enable/disable reusability of binding to address */
1005                 case SA_OPTION_REUSEADDR: {
1006 #if defined(SO_REUSEADDR)
1007                     int mode = sa->optInfo[i].value;
1008                     if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_REUSEADDR,
1009                                    (const void *)&mode, (socklen_t)sizeof(mode)) < 0)
1010                         rv = SA_ERR_SYS;
1011                     else
1012                         sa->optInfo[i].todo = FALSE;
1013 #endif
1014                     break;
1015                 }
1016                 /* enable/disable reusability of binding to port */
1017                 case SA_OPTION_REUSEPORT: {
1018 #if defined(SO_REUSEPORT)
1019                     int mode = sa->optInfo[i].value;
1020                     if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_REUSEPORT,
1021                                    (const void *)&mode, (socklen_t)sizeof(mode)) < 0)
1022                         rv = SA_ERR_SYS;
1023                     else
1024                         sa->optInfo[i].todo = FALSE;
1025 #endif
1026                     break;
1027                 }
1028                 /* enable/disable non-blocking I/O mode */
1029                 case SA_OPTION_NONBLOCK: {
1030                     int mode = sa->optInfo[i].value;
1031                     int flags;
1032                     if ((flags = fcntl(sa->fdSocket, F_GETFL, 0)) < 0) {
1033                         rv = SA_ERR_SYS;
1034                         break;
1035                     }
1036                     if (mode == 0)
1037                         flags &= ~(O_NONBLOCK);
1038                     else
1039                         flags |= O_NONBLOCK;
1040                     if (fcntl(sa->fdSocket, F_SETFL, flags) < 0)
1041                         rv = SA_ERR_SYS;
1042                     else
1043                         sa->optInfo[i].todo = FALSE;
1044                     break;
1045                 }
1046                 default: /* NOTREACHED */ break;
1047             }
1048         }
1049     }
1050 
1051     return SA_RC(rv);
1052 }
1053 
1054 /* internal lazy/delayed initialization of underlying socket */
sa_socket_init(sa_t * sa,int nFamily)1055 static sa_rc_t sa_socket_init(sa_t *sa, int nFamily)
1056 {
1057     int nType;
1058     int nProto;
1059 #if !(defined(IPPROTO_TCP) && defined(IPPROTO_UDP))
1060     struct protoent *pe;
1061 #endif
1062     sa_rc_t rv;
1063 
1064     /* argument sanity check(s) */
1065     if (sa == NULL)
1066         return SA_RC(SA_ERR_ARG);
1067 
1068     /* only perform operation if socket still does not exist */
1069     if (sa->fdSocket != -1)
1070         return SA_RC(SA_ERR_USE);
1071 
1072     /* determine socket type */
1073     if (sa->eType == SA_TYPE_STREAM)
1074         nType = SOCK_STREAM;
1075     else if (sa->eType == SA_TYPE_DATAGRAM)
1076         nType = SOCK_DGRAM;
1077     else
1078         return SA_RC(SA_ERR_INT);
1079 
1080     /* determine socket protocol */
1081     if (nFamily == AF_LOCAL)
1082         nProto = 0;
1083 #ifdef AF_INET6
1084     else if (nFamily == AF_INET || nFamily == AF_INET6) {
1085 #else
1086     else if (nFamily == AF_INET) {
1087 #endif
1088 #if defined(IPPROTO_TCP) && defined(IPPROTO_UDP)
1089         if (nType == SOCK_STREAM)
1090             nProto = IPPROTO_TCP;
1091         else if (nType == SOCK_DGRAM)
1092             nProto = IPPROTO_UDP;
1093         else
1094             return SA_RC(SA_ERR_INT);
1095 #else
1096         if (nType == SOCK_STREAM)
1097             pe = getprotobyname("tcp");
1098         else if (nType == SOCK_DGRAM)
1099             pe = getprotobyname("udp");
1100         else
1101             return SA_RC(SA_ERR_INT);
1102         if (pe == NULL)
1103             return SA_RC(SA_ERR_SYS);
1104         nProto = pe->p_proto;
1105 #endif
1106     }
1107     else
1108         return SA_RC(SA_ERR_INT);
1109 
1110     /* create the underlying socket */
1111     if ((sa->fdSocket = socket(nFamily, nType, nProto)) == -1)
1112         return SA_RC(SA_ERR_SYS);
1113 
1114     /* optionally set kernel timeouts */
1115     if ((rv = sa_socket_settimeouts(sa)) != SA_OK)
1116         return SA_RC(rv);
1117 
1118     /* optionally configure changed options */
1119     if ((rv = sa_socket_setoptions(sa)) != SA_OK)
1120         return SA_RC(rv);
1121 
1122     return SA_OK;
1123 }
1124 
1125 /* internal destruction of underlying socket */
1126 static sa_rc_t sa_socket_kill(sa_t *sa)
1127 {
1128     /* argument sanity check(s) */
1129     if (sa == NULL)
1130         return SA_RC(SA_ERR_ARG);
1131 
1132     /* check context */
1133     if (sa->fdSocket == -1)
1134         return SA_RC(SA_ERR_USE);
1135 
1136     /* close socket */
1137     (void)close(sa->fdSocket);
1138     sa->fdSocket = -1;
1139 
1140     return SA_OK;
1141 }
1142 
1143 /* create abstract socket object */
1144 sa_rc_t sa_create(sa_t **sap)
1145 {
1146     sa_t *sa;
1147     int i;
1148 
1149     /* argument sanity check(s) */
1150     if (sap == NULL)
1151         return SA_RC(SA_ERR_ARG);
1152 
1153     /* allocate and initialize socket object */
1154     if ((sa = (sa_t *)malloc(sizeof(sa_t))) == NULL)
1155         return SA_RC(SA_ERR_MEM);
1156 
1157     /* init object attributes */
1158     sa->eType          = SA_TYPE_STREAM;
1159     sa->fdSocket       = -1;
1160     sa->nReadLen       = 0;
1161     sa->nReadSize      = 0;
1162     sa->cpReadBuf      = NULL;
1163     sa->nWriteLen      = 0;
1164     sa->nWriteSize     = 0;
1165     sa->cpWriteBuf     = NULL;
1166 
1167     /* init timeval object attributes */
1168     for (i = 0; i < (int)(sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
1169         sa->tvTimeout[i].tv_sec  = 0;
1170         sa->tvTimeout[i].tv_usec = 0;
1171     }
1172 
1173     /* init options object attributes */
1174     for (i = 0; i < (int)(sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) {
1175         sa->optInfo[i].todo  = FALSE;
1176         sa->optInfo[i].value = 0;
1177     }
1178 
1179     /* init syscall object attributes */
1180     SA_SC_ASSIGN(sa, connect,       connect,       NULL);
1181     SA_SC_ASSIGN(sa, accept,        accept,        NULL);
1182     SA_SC_ASSIGN(sa, select,        select,        NULL);
1183     SA_SC_ASSIGN(sa, read,          read,          NULL);
1184     SA_SC_ASSIGN(sa, write,         write,         NULL);
1185     SA_SC_ASSIGN(sa, recvfrom,      recvfrom,      NULL);
1186     SA_SC_ASSIGN(sa, sendto,        sendto,        NULL);
1187 
1188     /* pass object to caller */
1189     *sap = sa;
1190 
1191     return SA_OK;
1192 }
1193 
1194 /* destroy abstract socket object */
1195 sa_rc_t sa_destroy(sa_t *sa)
1196 {
1197     /* argument sanity check(s) */
1198     if (sa == NULL)
1199         return SA_RC(SA_ERR_ARG);
1200 
1201     /* kill underlying socket */
1202     (void)sa_socket_kill(sa);
1203 
1204     /* free object and sub-objects */
1205     if (sa->cpReadBuf != NULL)
1206         free(sa->cpReadBuf);
1207     if (sa->cpWriteBuf != NULL)
1208         free(sa->cpWriteBuf);
1209     free(sa);
1210 
1211     return SA_OK;
1212 }
1213 
1214 /* switch communication type of socket */
1215 sa_rc_t sa_type(sa_t *sa, sa_type_t type)
1216 {
1217     /* argument sanity check(s) */
1218     if (sa == NULL)
1219         return SA_RC(SA_ERR_ARG);
1220     if (!(type == SA_TYPE_STREAM || type == SA_TYPE_DATAGRAM))
1221         return SA_RC(SA_ERR_ARG);
1222 
1223     /* kill underlying socket if type changes */
1224     if (sa->eType != type)
1225         (void)sa_socket_kill(sa);
1226 
1227     /* set new type */
1228     sa->eType = type;
1229 
1230     return SA_OK;
1231 }
1232 
1233 /* configure I/O timeout */
1234 sa_rc_t sa_timeout(sa_t *sa, sa_timeout_t id, long sec, long usec)
1235 {
1236     int i;
1237     sa_rc_t rv;
1238 
1239     /* argument sanity check(s) */
1240     if (sa == NULL)
1241         return SA_RC(SA_ERR_ARG);
1242 
1243     if (id == SA_TIMEOUT_ALL) {
1244         for (i = 0; i < (int)(sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
1245             sa->tvTimeout[i].tv_sec  = sec;
1246             sa->tvTimeout[i].tv_usec = usec;
1247         }
1248     }
1249     else {
1250         sa->tvTimeout[id].tv_sec  = sec;
1251         sa->tvTimeout[id].tv_usec = usec;
1252     }
1253 
1254     /* try to already set timeouts */
1255     if ((rv = sa_socket_settimeouts(sa)) != SA_OK)
1256         return SA_RC(rv);
1257 
1258     return SA_OK;
1259 }
1260 
1261 /* configure I/O buffers */
1262 sa_rc_t sa_buffer(sa_t *sa, sa_buffer_t id, size_t size)
1263 {
1264     char *cp;
1265 
1266     /* argument sanity check(s) */
1267     if (sa == NULL)
1268         return SA_RC(SA_ERR_ARG);
1269 
1270     if (id == SA_BUFFER_READ) {
1271         /* configure read/incoming buffer */
1272         if (sa->nReadLen > (int)size)
1273             return SA_RC(SA_ERR_USE);
1274         if (size > 0) {
1275             if (sa->cpReadBuf == NULL)
1276                 cp = (char *)malloc(size);
1277             else
1278                 cp = (char *)realloc(sa->cpReadBuf, size);
1279             if (cp == NULL)
1280                 return SA_RC(SA_ERR_MEM);
1281             sa->cpReadBuf = cp;
1282             sa->nReadSize = (int)size;
1283         }
1284         else {
1285             if (sa->cpReadBuf != NULL)
1286                 free(sa->cpReadBuf);
1287             sa->cpReadBuf = NULL;
1288             sa->nReadSize = 0;
1289         }
1290     }
1291     else if (id == SA_BUFFER_WRITE) {
1292         /* configure write/outgoing buffer */
1293         if (sa->nWriteLen > (int)size)
1294             return SA_RC(SA_ERR_USE);
1295         if (size > 0) {
1296             if (sa->cpWriteBuf == NULL)
1297                 cp = (char *)malloc(size);
1298             else
1299                 cp = (char *)realloc(sa->cpWriteBuf, size);
1300             if (cp == NULL)
1301                 return SA_RC(SA_ERR_MEM);
1302             sa->cpWriteBuf = cp;
1303             sa->nWriteSize = (int)size;
1304         }
1305         else {
1306             if (sa->cpWriteBuf != NULL)
1307                 free(sa->cpWriteBuf);
1308             sa->cpWriteBuf = NULL;
1309             sa->nWriteSize = 0;
1310         }
1311     }
1312     else
1313         return SA_RC(SA_ERR_ARG);
1314 
1315     return SA_OK;
1316 }
1317 
1318 /* configure socket option */
1319 sa_rc_t sa_option(sa_t *sa, sa_option_t id, ...)
1320 {
1321     sa_rc_t rv;
1322     va_list ap;
1323 
1324     /* argument sanity check(s) */
1325     if (sa == NULL)
1326         return SA_RC(SA_ERR_ARG);
1327 
1328     /* process option */
1329     rv = SA_OK;
1330     va_start(ap, id);
1331     switch (id) {
1332         case SA_OPTION_NAGLE: {
1333 #if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
1334             int mode = ((int)va_arg(ap, int) ? 1 : 0);
1335             sa->optInfo[SA_OPTION_NAGLE].value = mode;
1336             sa->optInfo[SA_OPTION_NAGLE].todo = TRUE;
1337 #else
1338             rv = SA_ERR_IMP;
1339 #endif
1340             break;
1341         }
1342         case SA_OPTION_LINGER: {
1343 #if defined(SO_LINGER)
1344             int amount = (int)va_arg(ap, int);
1345             sa->optInfo[SA_OPTION_LINGER].value = amount;
1346             sa->optInfo[SA_OPTION_LINGER].todo = TRUE;
1347 #else
1348             rv = SA_ERR_IMP;
1349 #endif
1350             break;
1351         }
1352         case SA_OPTION_REUSEADDR:
1353         {
1354 #if defined(SO_REUSEADDR)
1355             int mode = ((int)va_arg(ap, int) ? 1 : 0);
1356             sa->optInfo[SA_OPTION_REUSEADDR].value = mode;
1357             sa->optInfo[SA_OPTION_REUSEADDR].todo = TRUE;
1358 #else
1359             rv = SA_ERR_IMP;
1360 #endif
1361             break;
1362         }
1363         case SA_OPTION_REUSEPORT:
1364         {
1365 #if defined(SO_REUSEPORT)
1366             int mode = ((int)va_arg(ap, int) ? 1 : 0);
1367             sa->optInfo[SA_OPTION_REUSEPORT].value = mode;
1368             sa->optInfo[SA_OPTION_REUSEPORT].todo = TRUE;
1369 #else
1370             rv = SA_ERR_IMP;
1371 #endif
1372             break;
1373         }
1374         case SA_OPTION_NONBLOCK: {
1375             int mode = (int)va_arg(ap, int);
1376             sa->optInfo[SA_OPTION_NONBLOCK].value = mode;
1377             sa->optInfo[SA_OPTION_NONBLOCK].todo = TRUE;
1378             break;
1379         }
1380         default: {
1381             rv = SA_ERR_ARG;
1382         }
1383     }
1384     va_end(ap);
1385 
1386     /* if an error already occured, stop here */
1387     if (rv != SA_OK)
1388         return SA_RC(rv);
1389 
1390     /* else already (re)set) options (if possible) */
1391     if ((rv = sa_socket_setoptions(sa)) != SA_OK)
1392         return SA_RC(rv);
1393 
1394     return SA_OK;
1395 }
1396 
1397 /* override system call */
1398 sa_rc_t sa_syscall(sa_t *sa, sa_syscall_t id, void (*fptr)(void), void *fctx)
1399 {
1400     sa_rc_t rv;
1401 
1402     /* argument sanity check(s) */
1403     if (sa == NULL || fptr == NULL)
1404         return SA_RC(SA_ERR_ARG);
1405 
1406     /* assign system call */
1407     rv = SA_OK;
1408     switch (id) {
1409         case SA_SYSCALL_CONNECT:       SA_SC_ASSIGN(sa, connect,       fptr, fctx); break;
1410         case SA_SYSCALL_ACCEPT:        SA_SC_ASSIGN(sa, accept,        fptr, fctx); break;
1411         case SA_SYSCALL_SELECT:        SA_SC_ASSIGN(sa, select,        fptr, fctx); break;
1412         case SA_SYSCALL_READ:          SA_SC_ASSIGN(sa, read,          fptr, fctx); break;
1413         case SA_SYSCALL_WRITE:         SA_SC_ASSIGN(sa, write,         fptr, fctx); break;
1414         case SA_SYSCALL_RECVFROM:      SA_SC_ASSIGN(sa, recvfrom,      fptr, fctx); break;
1415         case SA_SYSCALL_SENDTO:        SA_SC_ASSIGN(sa, sendto,        fptr, fctx); break;
1416         default: rv = SA_ERR_ARG;
1417     }
1418 
1419     return SA_RC(rv);
1420 }
1421 
1422 /* bind socket to a local address */
1423 sa_rc_t sa_bind(sa_t *sa, const sa_addr_t *laddr)
1424 {
1425     sa_rc_t rv;
1426     struct sockaddr_un *un;
1427 
1428     /* argument sanity check(s) */
1429     if (sa == NULL || laddr == NULL)
1430         return SA_RC(SA_ERR_ARG);
1431 
1432     /* lazy creation of underlying socket */
1433     if (sa->fdSocket == -1)
1434         if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK)
1435             return SA_RC(rv);
1436 
1437     /* remove a possibly existing old Unix Domain socket on filesystem */
1438     if (laddr->nFamily == AF_LOCAL) {
1439         un = (struct sockaddr_un *)((void *)laddr->saBuf);
1440         (void)unlink(un->sun_path);
1441     }
1442 
1443     /* perform bind operation on underlying socket */
1444     if (bind(sa->fdSocket, laddr->saBuf, laddr->slBuf) == -1)
1445         return SA_RC(SA_ERR_SYS);
1446 
1447     return SA_OK;
1448 }
1449 
1450 /* connect socket to a remote address */
1451 sa_rc_t sa_connect(sa_t *sa, const sa_addr_t *raddr)
1452 {
1453     int flags, n, error;
1454     fd_set rset, wset;
1455     socklen_t len;
1456     sa_rc_t rv;
1457     struct timeval *tv;
1458     struct timeval tv_buf;
1459 
1460     /* argument sanity check(s) */
1461     if (sa == NULL || raddr == NULL)
1462         return SA_RC(SA_ERR_ARG);
1463 
1464     /* connecting is only possible for stream communication */
1465     if (sa->eType != SA_TYPE_STREAM)
1466         return SA_RC(SA_ERR_USE);
1467 
1468     /* lazy creation of underlying socket */
1469     if (sa->fdSocket == -1)
1470         if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
1471             return SA_RC(rv);
1472 
1473     /* prepare return code decision */
1474     rv = SA_OK;
1475     error = 0;
1476 
1477     /* temporarily switch underlying socket to non-blocking mode
1478        (necessary under timeout-aware operation only) */
1479     flags = 0;
1480     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) {
1481         flags = fcntl(sa->fdSocket, F_GETFL, 0);
1482         (void)fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK);
1483     }
1484 
1485     /* perform the connect operation */
1486     if ((n = SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) {
1487         if (errno != EINTR && errno != EINPROGRESS) {
1488             /* we have to perform the following post-processing under
1489                EINPROGRESS anway, but actually also for EINTR according
1490                to Unix Network Programming, volume 1, section 5.9, W.
1491                Richard Stevens: "What we are doing [] is restarting
1492                the interrupted system call ourself. This is fine for
1493                accept, along with the functions such as read, write,
1494                select and open. But there is one function that we cannot
1495                restart ourself: connect. If this function returns EINTR,
1496                we cannot call it again, as doing so will return an
1497                immediate error. When connect is interrupted by a caught
1498                signal and is not automatically restarted, we must call
1499                select to wait for the connection to complete, as we
1500                describe in section 15.3." */
1501             error = errno;
1502             goto done;
1503         }
1504     }
1505 
1506     /* ok if connect completed immediately */
1507     if (n == 0)
1508         goto done;
1509 
1510     /* wait for read or write possibility */
1511     FD_ZERO(&rset);
1512     FD_ZERO(&wset);
1513     FD_SET(sa->fdSocket, &rset);
1514     FD_SET(sa->fdSocket, &wset);
1515     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) {
1516         memcpy(&tv_buf, &sa->tvTimeout[SA_TIMEOUT_CONNECT], sizeof(struct timeval));
1517         tv = &tv_buf;
1518     }
1519     else
1520         tv = NULL;
1521     do {
1522         n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &rset, &wset, (fd_set *)NULL, tv);
1523     } while (n == -1 && errno == EINTR);
1524 
1525     /* decide on return semantic */
1526     if (n < 0) {
1527         error = errno;
1528         goto done;
1529     }
1530     else if (n == 0) {
1531         (void)close(sa->fdSocket); /* stop TCP three-way handshake */
1532         sa->fdSocket = -1;
1533         rv = SA_ERR_TMT;
1534         goto done;
1535     }
1536 
1537     /* fetch pending error */
1538     len = (socklen_t)sizeof(error);
1539     if (getsockopt(sa->fdSocket, SOL_SOCKET, SO_ERROR, (void *)&error, &len) < 0)
1540         error = errno;
1541 
1542     done:
1543 
1544     /* reset socket flags
1545        (necessary under timeout-aware operation only) */
1546     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT]))
1547         (void)fcntl(sa->fdSocket, F_SETFL, flags);
1548 
1549     /* optionally set errno */
1550     if (error != 0) {
1551         (void)close(sa->fdSocket); /* just in case */
1552         sa->fdSocket = -1;
1553         errno = error;
1554         rv = SA_ERR_SYS;
1555     }
1556 
1557     return SA_RC(rv);
1558 }
1559 
1560 /* listen on socket for connections */
1561 sa_rc_t sa_listen(sa_t *sa, int backlog)
1562 {
1563     /* argument sanity check(s) */
1564     if (sa == NULL)
1565         return SA_RC(SA_ERR_ARG);
1566 
1567     /* listening is only possible for stream communication */
1568     if (sa->eType != SA_TYPE_STREAM)
1569         return SA_RC(SA_ERR_USE);
1570 
1571     /* at least sa_bind() has to be already performed */
1572     if (sa->fdSocket == -1)
1573         return SA_RC(SA_ERR_USE);
1574 
1575     /* perform listen operation on underlying socket */
1576     if (listen(sa->fdSocket, backlog) == -1)
1577         return SA_RC(SA_ERR_SYS);
1578 
1579     return SA_OK;
1580 }
1581 
1582 /* accept a connection on socket */
1583 sa_rc_t sa_accept(sa_t *sa, sa_addr_t **caddr, sa_t **csa)
1584 {
1585     sa_rc_t rv;
1586     int n;
1587     fd_set fds;
1588     union {
1589         struct sockaddr_un un;
1590         struct sockaddr_in sa4;
1591 #ifdef AF_INET6
1592         struct sockaddr_in6 sa6;
1593 #endif
1594     } sa_buf;
1595     socklen_t sa_size;
1596     struct timeval tv;
1597     int s;
1598     int i;
1599 
1600     /* argument sanity check(s) */
1601     if (sa == NULL || caddr == NULL || csa == NULL)
1602         return SA_RC(SA_ERR_ARG);
1603 
1604     /* accepting connections is only possible for stream communication */
1605     if (sa->eType != SA_TYPE_STREAM)
1606         return SA_RC(SA_ERR_USE);
1607 
1608     /* at least sa_listen() has to be already performed */
1609     if (sa->fdSocket == -1)
1610         return SA_RC(SA_ERR_USE);
1611 
1612     /* if timeout is enabled, perform a smart-blocking wait */
1613     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_ACCEPT])) {
1614         FD_ZERO(&fds);
1615         FD_SET(sa->fdSocket, &fds);
1616         memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_ACCEPT], sizeof(struct timeval));
1617         do {
1618             n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &tv);
1619         } while (n == -1 && errno == EINTR);
1620         if (n == 0)
1621             return SA_RC(SA_ERR_TMT);
1622         if (n <= 0)
1623             return SA_RC(SA_ERR_SYS);
1624     }
1625 
1626     /* perform accept operation on underlying socket */
1627     sa_size = (socklen_t)sizeof(sa_buf);
1628     do {
1629         s = SA_SC_CALL_3(sa, accept, sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size);
1630     } while (s == -1 && errno == EINTR);
1631     if (s == -1)
1632         return SA_RC(SA_ERR_SYS);
1633 
1634     /* create result address object */
1635     if ((rv = sa_addr_create(caddr)) != SA_OK)
1636         return SA_RC(rv);
1637     if ((rv = sa_addr_s2a(*caddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) {
1638         (void)sa_addr_destroy(*caddr);
1639         return SA_RC(rv);
1640     }
1641 
1642     /* create result socket object */
1643     if ((rv = sa_create(csa)) != SA_OK) {
1644         (void)sa_addr_destroy(*caddr);
1645         return SA_RC(rv);
1646     }
1647 
1648     /* fill-in child socket */
1649     (*csa)->fdSocket = s;
1650 
1651     /* copy-over original system calls */
1652     SA_SC_COPY((*csa), sa, connect);
1653     SA_SC_COPY((*csa), sa, accept);
1654     SA_SC_COPY((*csa), sa, select);
1655     SA_SC_COPY((*csa), sa, read);
1656     SA_SC_COPY((*csa), sa, write);
1657     SA_SC_COPY((*csa), sa, recvfrom);
1658     SA_SC_COPY((*csa), sa, sendto);
1659 
1660     /* copy-over original timeout values */
1661     for (i = 0; i < (int)(sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) {
1662         (*csa)->tvTimeout[i].tv_sec  = sa->tvTimeout[i].tv_sec;
1663         (*csa)->tvTimeout[i].tv_usec = sa->tvTimeout[i].tv_usec;
1664     }
1665 
1666     return SA_OK;
1667 }
1668 
1669 /* determine remote address */
1670 sa_rc_t sa_getremote(sa_t *sa, sa_addr_t **raddr)
1671 {
1672     sa_rc_t rv;
1673     union {
1674         struct sockaddr_in sa4;
1675 #ifdef AF_INET6
1676         struct sockaddr_in6 sa6;
1677 #endif
1678     } sa_buf;
1679     socklen_t sa_size;
1680 
1681     /* argument sanity check(s) */
1682     if (sa == NULL || raddr == NULL)
1683         return SA_RC(SA_ERR_ARG);
1684 
1685     /* peers exist only for stream communication */
1686     if (sa->eType != SA_TYPE_STREAM)
1687         return SA_RC(SA_ERR_USE);
1688 
1689     /* at least sa_connect() or sa_accept() has to be already performed */
1690     if (sa->fdSocket == -1)
1691         return SA_RC(SA_ERR_USE);
1692 
1693     /* determine remote address of underlying socket */
1694     sa_size = (socklen_t)sizeof(sa_buf);
1695     if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size) < 0)
1696         return SA_RC(SA_ERR_SYS);
1697 
1698     /* create result address object */
1699     if ((rv = sa_addr_create(raddr)) != SA_OK)
1700         return SA_RC(rv);
1701     if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) {
1702         (void)sa_addr_destroy(*raddr);
1703         return SA_RC(rv);
1704     }
1705 
1706     return SA_OK;
1707 }
1708 
1709 /* determine local address */
1710 sa_rc_t sa_getlocal(sa_t *sa, sa_addr_t **laddr)
1711 {
1712     sa_rc_t rv;
1713     union {
1714         struct sockaddr_in sa4;
1715 #ifdef AF_INET6
1716         struct sockaddr_in6 sa6;
1717 #endif
1718     } sa_buf;
1719     socklen_t sa_size;
1720 
1721     /* argument sanity check(s) */
1722     if (sa == NULL || laddr == NULL)
1723         return SA_RC(SA_ERR_ARG);
1724 
1725     /* at least sa_bind() has to be already performed */
1726     if (sa->fdSocket == -1)
1727         return SA_RC(SA_ERR_USE);
1728 
1729     /* determine local address of underlying socket */
1730     sa_size = (socklen_t)sizeof(sa_buf);
1731     if (getsockname(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size) < 0)
1732         return SA_RC(SA_ERR_SYS);
1733 
1734     /* create result address object */
1735     if ((rv = sa_addr_create(laddr)) != SA_OK)
1736         return SA_RC(rv);
1737     if ((rv = sa_addr_s2a(*laddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) {
1738         (void)sa_addr_destroy(*laddr);
1739         return SA_RC(rv);
1740     }
1741 
1742     return SA_OK;
1743 }
1744 
1745 /* peek at underlying socket */
1746 sa_rc_t sa_getfd(sa_t *sa, int *fd)
1747 {
1748     /* argument sanity check(s) */
1749     if (sa == NULL || fd == NULL)
1750         return SA_RC(SA_ERR_ARG);
1751 
1752     /* if still no socket exists, say this explicitly */
1753     if (sa->fdSocket == -1)
1754         return SA_RC(SA_ERR_USE);
1755 
1756     /* pass socket to caller */
1757     *fd = sa->fdSocket;
1758 
1759     return SA_OK;
1760 }
1761 
1762 /* internal raw read operation */
1763 static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen)
1764 {
1765     int rv;
1766 #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO))
1767     fd_set fds;
1768     struct timeval tv;
1769 #endif
1770 
1771     /* if timeout is enabled, perform explicit/smart blocking instead
1772        of implicitly/hard blocking in the read(2) system call */
1773 #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO))
1774     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
1775         FD_ZERO(&fds);
1776         FD_SET(sa->fdSocket, &fds);
1777         memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_READ], sizeof(struct timeval));
1778         do {
1779             rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &tv);
1780         } while (rv == -1 && errno == EINTR);
1781         if (rv == 0) {
1782             errno = ETIMEDOUT;
1783             return -1;
1784         }
1785     }
1786 #endif
1787 
1788     /* perform read operation on underlying socket */
1789     do {
1790         rv = (int)SA_SC_CALL_3(sa, read, sa->fdSocket, cpBuf, (size_t)nBufLen);
1791     } while (rv == -1 && errno == EINTR);
1792 
1793 #if defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)
1794     if (rv == -1 && errno == EWOULDBLOCK)
1795         errno = ETIMEDOUT;
1796 #endif
1797 
1798     return rv;
1799 }
1800 
1801 /* read data from socket */
1802 sa_rc_t sa_read(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
1803 {
1804     int n;
1805     sa_rc_t rv;
1806     int res;
1807 
1808     /* argument sanity check(s) */
1809     if (sa == NULL || cpBuf == NULL || nBufReq == 0)
1810         return SA_RC(SA_ERR_ARG);
1811 
1812     /* reading is only possible for stream communication */
1813     if (sa->eType != SA_TYPE_STREAM)
1814         return SA_RC(SA_ERR_USE);
1815 
1816     /* at least a connection has to exist */
1817     if (sa->fdSocket == -1)
1818         return SA_RC(SA_ERR_USE);
1819 
1820     /* perform read operation */
1821     rv = SA_OK;
1822     if (sa->nReadSize == 0) {
1823         /* user-space unbuffered I/O */
1824         if (sa->nWriteLen > 0)
1825             (void)sa_flush(sa);
1826         res = sa_read_raw(sa, cpBuf, (int)nBufReq);
1827         if (res == 0)
1828             rv = SA_ERR_EOF;
1829         else if (res < 0 && errno == ETIMEDOUT)
1830             rv = SA_ERR_TMT;
1831         else if (res < 0)
1832             rv = SA_ERR_SYS;
1833     }
1834     else {
1835         /* user-space buffered I/O */
1836         res = 0;
1837         for (;;) {
1838             if ((int)nBufReq <= sa->nReadLen) {
1839                 /* buffer holds enough data, so just use this */
1840                 memmove(cpBuf, sa->cpReadBuf, nBufReq);
1841                 memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq);
1842                 sa->nReadLen -= nBufReq;
1843                 res += nBufReq;
1844             }
1845             else {
1846                 if (sa->nReadLen > 0) {
1847                     /* fetch already existing buffer contents as a start */
1848                     memmove(cpBuf, sa->cpReadBuf, (size_t)sa->nReadLen);
1849                     nBufReq -= sa->nReadLen;
1850                     cpBuf   += sa->nReadLen;
1851                     res     += sa->nReadLen;
1852                     sa->nReadLen = 0;
1853                 }
1854                 if (sa->nWriteLen > 0)
1855                     (void)sa_flush(sa);
1856                 if ((int)nBufReq >= sa->nReadSize) {
1857                     /* buffer is too small at all, so read directly */
1858                     n = sa_read_raw(sa, cpBuf, (int)nBufReq);
1859                     if (n > 0)
1860                         res += n;
1861                     else if (n == 0)
1862                         rv = (res == 0 ? SA_ERR_EOF : SA_OK);
1863                     else if (n < 0 && errno == ETIMEDOUT)
1864                         rv = (res == 0 ? SA_ERR_TMT : SA_OK);
1865                     else if (n < 0)
1866                         rv = (res == 0 ? SA_ERR_SYS : SA_OK);
1867                 }
1868                 else {
1869                     /* fill buffer with new data */
1870                     n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize);
1871                     if (n < 0 && errno == ETIMEDOUT)
1872                         /* timeout on this read, but perhaps ok as a whole */
1873                         rv = (res == 0 ? SA_ERR_TMT : SA_OK);
1874                     else if (n < 0)
1875                         /* error on this read, but perhaps ok as a whole */
1876                         rv = (res == 0 ? SA_ERR_SYS : SA_OK);
1877                     else if (n == 0)
1878                         /* EOF on this read, but perhaps ok as a whole */
1879                         rv = (res == 0 ? SA_ERR_EOF : SA_OK);
1880                     else {
1881                         sa->nReadLen = n;
1882                         continue; /* REPEAT OPERATION! */
1883                     }
1884                 }
1885             }
1886             break;
1887         }
1888     }
1889 
1890     /* pass number of actually read bytes to caller */
1891     if (nBufRes != NULL)
1892         *nBufRes = (size_t)res;
1893 
1894     return SA_RC(rv);
1895 }
1896 
1897 /* read data from socket until [CR]LF (convenience function) */
1898 sa_rc_t sa_readln(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes)
1899 {
1900     char c;
1901     size_t n;
1902     size_t res;
1903     sa_rc_t rv;
1904 
1905     /* argument sanity check(s) */
1906     if (sa == NULL || cpBuf == NULL || nBufReq == 0)
1907         return SA_RC(SA_ERR_ARG);
1908 
1909     /* reading is only possible for stream communication */
1910     if (sa->eType != SA_TYPE_STREAM)
1911         return SA_RC(SA_ERR_USE);
1912 
1913     /* at least a connection has to exist */
1914     if (sa->fdSocket == -1)
1915         return SA_RC(SA_ERR_USE);
1916 
1917     /* we just perform a plain sa_read() per character, because if
1918        buffers are enabled, this is not as stupid as it looks at the first
1919        hand and if buffers are disabled, there is no better solution
1920        anyway. */
1921     rv = SA_OK;
1922     res = 0;
1923     while (res < (nBufReq-1)) {
1924         rv = sa_read(sa, &c, 1, &n);
1925         if (rv != SA_OK)
1926             break;
1927         if (n == 0)
1928             break;
1929         cpBuf[res++] = c;
1930         if (c == '\n')
1931             break;
1932     }
1933     cpBuf[res] = '\0';
1934 
1935     /* pass number of actually read characters to caller */
1936     if (nBufRes != NULL)
1937         *nBufRes = res;
1938 
1939     return SA_RC(rv);
1940 }
1941 
1942 /* internal raw write operation */
1943 static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen)
1944 {
1945     int rv;
1946 #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO))
1947     fd_set fds;
1948     struct timeval tv;
1949 #endif
1950 
1951     /* if timeout is enabled, perform explicit/smart blocking instead
1952        of implicitly/hard blocking in the write(2) system call */
1953 #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO))
1954     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
1955         FD_ZERO(&fds);
1956         FD_SET(sa->fdSocket, &fds);
1957         memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_WRITE], sizeof(struct timeval));
1958         do {
1959             rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, (fd_set *)NULL, &fds, (fd_set *)NULL, &tv);
1960         } while (rv == -1 && errno == EINTR);
1961         if (rv == 0) {
1962             errno = ETIMEDOUT;
1963             return -1;
1964         }
1965     }
1966 #endif
1967 
1968     /* perform write operation on underlying socket */
1969     do {
1970         rv = (int)SA_SC_CALL_3(sa, write, sa->fdSocket, cpBuf, (size_t)nBufLen);
1971     } while (rv == -1 && errno == EINTR);
1972 
1973 #if defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)
1974     if (rv == -1 && errno == EWOULDBLOCK)
1975         errno = ETIMEDOUT;
1976 #endif
1977 
1978     return rv;
1979 }
1980 
1981 /* write data to socket */
1982 sa_rc_t sa_write(sa_t *sa, const char *cpBuf, size_t nBufReq, size_t *nBufRes)
1983 {
1984     int n;
1985     int res;
1986     sa_rc_t rv;
1987 
1988     /* argument sanity check(s) */
1989     if (sa == NULL || cpBuf == NULL || nBufReq == 0)
1990         return SA_RC(SA_ERR_ARG);
1991 
1992     /* writing is only possible for stream communication */
1993     if (sa->eType != SA_TYPE_STREAM)
1994         return SA_RC(SA_ERR_USE);
1995 
1996     /* at least a connection has to exist */
1997     if (sa->fdSocket == -1)
1998         return SA_RC(SA_ERR_USE);
1999 
2000     rv = SA_OK;
2001     if (sa->nWriteSize == 0) {
2002         /* user-space unbuffered I/O */
2003         res = sa_write_raw(sa, cpBuf, (int)nBufReq);
2004         if (res < 0 && errno == ETIMEDOUT)
2005             rv = SA_ERR_TMT;
2006         else if (res < 0)
2007             rv = SA_ERR_SYS;
2008     }
2009     else {
2010         /* user-space buffered I/O */
2011         if ((int)nBufReq > (sa->nWriteSize - sa->nWriteLen)) {
2012             /* not enough space in buffer, so flush buffer first */
2013             (void)sa_flush(sa);
2014         }
2015         res = 0;
2016         if ((int)nBufReq >= sa->nWriteSize) {
2017             /* buffer too small at all, so write immediately */
2018             while (nBufReq > 0) {
2019                 n = sa_write_raw(sa, cpBuf, (int)nBufReq);
2020                 if (n < 0 && errno == ETIMEDOUT)
2021                     rv = (res == 0 ? SA_ERR_TMT : SA_OK);
2022                 else if (n < 0)
2023                     rv = (res == 0 ? SA_ERR_SYS : SA_OK);
2024                 if (n <= 0)
2025                     break;
2026                 nBufReq  -= n;
2027                 cpBuf    += n;
2028                 res      += n;
2029             }
2030         }
2031         else {
2032             /* (again) enough sprace in buffer, so store data */
2033             memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq);
2034             sa->nWriteLen += nBufReq;
2035             res = (int)nBufReq;
2036         }
2037     }
2038 
2039     /* pass number of actually written bytes to caller */
2040     if (nBufRes != NULL)
2041         *nBufRes = (size_t)res;
2042 
2043     return SA_RC(rv);
2044 }
2045 
2046 /* output callback function context for sa_writef() */
2047 typedef struct {
2048     sa_t *sa;
2049     sa_rc_t rv;
2050 } sa_writef_cb_t;
2051 
2052 /* output callback function for sa_writef() */
2053 static int sa_writef_cb(void *_ctx, const char *buffer, size_t bufsize)
2054 {
2055     size_t n;
2056     sa_writef_cb_t *ctx = (sa_writef_cb_t *)_ctx;
2057 
2058     if ((ctx->rv = sa_write(ctx->sa, buffer, bufsize, &n)) != SA_OK)
2059         return -1;
2060     return (int)n;
2061 }
2062 
2063 /* write formatted string to socket (convenience function) */
2064 sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...)
2065 {
2066     va_list ap;
2067     int n;
2068     sa_writef_cb_t ctx;
2069 
2070     /* argument sanity check(s) */
2071     if (sa == NULL || cpFmt == NULL)
2072         return SA_RC(SA_ERR_ARG);
2073 
2074     /* writing is only possible for stream communication */
2075     if (sa->eType != SA_TYPE_STREAM)
2076         return SA_RC(SA_ERR_USE);
2077 
2078     /* at least a connection has to exist */
2079     if (sa->fdSocket == -1)
2080         return SA_RC(SA_ERR_USE);
2081 
2082     /* format string into temporary buffer */
2083     va_start(ap, cpFmt);
2084     ctx.sa = sa;
2085     ctx.rv = SA_OK;
2086     n = sa_mvxprintf(sa_writef_cb, &ctx, cpFmt, ap);
2087     if (n == -1 && ctx.rv == SA_OK)
2088         ctx.rv = SA_ERR_FMT;
2089     va_end(ap);
2090 
2091     return ctx.rv;
2092 }
2093 
2094 /* flush write/outgoing I/O buffer */
2095 sa_rc_t sa_flush(sa_t *sa)
2096 {
2097     int n;
2098     sa_rc_t rv;
2099 
2100     /* argument sanity check(s) */
2101     if (sa == NULL)
2102         return SA_RC(SA_ERR_ARG);
2103 
2104     /* flushing is only possible for stream communication */
2105     if (sa->eType != SA_TYPE_STREAM)
2106         return SA_RC(SA_ERR_USE);
2107 
2108     /* at least a connection has to exist */
2109     if (sa->fdSocket == -1)
2110         return SA_RC(SA_ERR_USE);
2111 
2112     /* try to flush buffer */
2113     rv = SA_OK;
2114     if (sa->nWriteSize > 0) {
2115         while (sa->nWriteLen > 0) {
2116             n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen);
2117             if (n < 0 && errno == ETIMEDOUT)
2118                 rv = SA_ERR_TMT;
2119             else if (n < 0)
2120                 rv = SA_ERR_SYS;
2121             if (n <= 0)
2122                 break;
2123             memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, (size_t)(sa->nWriteLen-n));
2124             sa->nWriteLen -= n;
2125         }
2126         sa->nWriteLen = 0;
2127     }
2128     return SA_RC(rv);
2129 }
2130 
2131 /* shutdown a socket connection */
2132 sa_rc_t sa_shutdown(sa_t *sa, const char *flags)
2133 {
2134     int how;
2135 
2136     /* argument sanity check(s) */
2137     if (sa == NULL || flags == NULL)
2138         return SA_RC(SA_ERR_ARG);
2139 
2140     /* shutdown is only possible for stream communication */
2141     if (sa->eType != SA_TYPE_STREAM)
2142         return SA_RC(SA_ERR_USE);
2143 
2144     /* at least a connection has to exist */
2145     if (sa->fdSocket == -1)
2146         return SA_RC(SA_ERR_USE);
2147 
2148     /* determine flags for shutdown(2) */
2149     how = 0;
2150     if (strcmp(flags, "r") == 0)
2151         how = SHUT_RD;
2152     else if (strcmp(flags, "w") == 0)
2153         how = SHUT_WR;
2154     else if (strcmp(flags, "rw") == 0 || strcmp(flags, "wr") == 0)
2155         how = SHUT_RDWR;
2156     else
2157         return SA_RC(SA_ERR_ARG);
2158 
2159     /* flush write buffers */
2160     if ((how & SHUT_WR) || (how & SHUT_RDWR))
2161         (void)sa_flush(sa);
2162 
2163     /* perform shutdown operation on underlying socket */
2164     if (shutdown(sa->fdSocket, how) == -1)
2165         return SA_RC(SA_ERR_SYS);
2166 
2167     return SA_OK;
2168 }
2169 
2170 /* receive data via socket */
2171 sa_rc_t sa_recv(sa_t *sa, sa_addr_t **raddr, char *buf, size_t buflen, size_t *bufdone)
2172 {
2173     sa_rc_t rv;
2174     union {
2175         struct sockaddr_in sa4;
2176 #ifdef AF_INET6
2177         struct sockaddr_in6 sa6;
2178 #endif
2179     } sa_buf;
2180     socklen_t sa_size;
2181     ssize_t n;
2182     int k;
2183     fd_set fds;
2184     struct timeval tv;
2185 
2186     /* argument sanity check(s) */
2187     if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
2188         return SA_RC(SA_ERR_ARG);
2189 
2190     /* receiving is only possible for datagram communication */
2191     if (sa->eType != SA_TYPE_DATAGRAM)
2192         return SA_RC(SA_ERR_USE);
2193 
2194     /* at least a sa_bind() has to be performed */
2195     if (sa->fdSocket == -1)
2196         return SA_RC(SA_ERR_USE);
2197 
2198     /* if timeout is enabled, perform explicit/smart blocking instead
2199        of implicitly/hard blocking in the recvfrom(2) system call */
2200     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) {
2201         FD_ZERO(&fds);
2202         FD_SET(sa->fdSocket, &fds);
2203         memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_READ], sizeof(struct timeval));
2204         do {
2205             k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &tv);
2206         } while (k == -1 && errno == EINTR);
2207         if (k == 0)
2208             errno = ETIMEDOUT;
2209         if (k <= 0)
2210             return SA_RC(SA_ERR_SYS);
2211     }
2212 
2213     /* perform receive operation on underlying socket */
2214     sa_size = (socklen_t)sizeof(sa_buf);
2215     if ((n = SA_SC_CALL_6(sa, recvfrom, sa->fdSocket, buf, buflen, 0,
2216                           (struct sockaddr *)&sa_buf, &sa_size)) == -1)
2217         return SA_RC(SA_ERR_SYS);
2218 
2219     /* create result address object */
2220     if ((rv = sa_addr_create(raddr)) != SA_OK)
2221         return rv;
2222     if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) {
2223         (void)sa_addr_destroy(*raddr);
2224         return rv;
2225     }
2226 
2227     /* pass actual number of received bytes to caller */
2228     if (bufdone != NULL)
2229         *bufdone = (size_t)n;
2230 
2231     return SA_OK;
2232 }
2233 
2234 /* send data via socket */
2235 sa_rc_t sa_send(sa_t *sa, sa_addr_t *raddr, const char *buf, size_t buflen, size_t *bufdone)
2236 {
2237     ssize_t n;
2238     int k;
2239     fd_set fds;
2240     sa_rc_t rv;
2241     struct timeval tv;
2242 
2243     /* argument sanity check(s) */
2244     if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL)
2245         return SA_RC(SA_ERR_ARG);
2246 
2247     /* sending is only possible for datagram communication */
2248     if (sa->eType != SA_TYPE_DATAGRAM)
2249         return SA_RC(SA_ERR_USE);
2250 
2251     /* lazy creation of underlying socket */
2252     if (sa->fdSocket == -1)
2253         if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK)
2254             return rv;
2255 
2256     /* if timeout is enabled, perform explicit/smart blocking instead
2257        of implicitly/hard blocking in the sendto(2) system call */
2258     if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) {
2259         FD_ZERO(&fds);
2260         FD_SET(sa->fdSocket, &fds);
2261         memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_WRITE], sizeof(struct timeval));
2262         do {
2263             k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, (fd_set *)NULL, &fds, (fd_set *)NULL, &tv);
2264         } while (k == -1 && errno == EINTR);
2265         if (k == 0)
2266             errno = ETIMEDOUT;
2267         if (k <= 0)
2268             return SA_RC(SA_ERR_SYS);
2269     }
2270 
2271     /* perform send operation on underlying socket */
2272     if ((n = SA_SC_CALL_6(sa, sendto, sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1)
2273         return SA_RC(SA_ERR_SYS);
2274 
2275     /* pass actual number of sent bytes to caller */
2276     if (bufdone != NULL)
2277         *bufdone = (size_t)n;
2278 
2279     return SA_OK;
2280 }
2281 
2282 /* send formatted string to socket (convenience function) */
2283 sa_rc_t sa_sendf(sa_t *sa, sa_addr_t *raddr, const char *cpFmt, ...)
2284 {
2285     va_list ap;
2286     va_list apbak;
2287     int nBuf;
2288     char *cpBuf;
2289     sa_rc_t rv;
2290     char caBuf[1024];
2291 
2292     /* argument sanity check(s) */
2293     if (sa == NULL || raddr == NULL || cpFmt == NULL)
2294         return SA_RC(SA_ERR_ARG);
2295 
2296     /* format string into temporary buffer */
2297     va_start(ap, cpFmt);
2298     va_copy(apbak, ap);
2299     if ((nBuf = sa_mvsnprintf(NULL, 0, cpFmt, ap)) == -1)
2300         return SA_RC(SA_ERR_FMT);
2301     va_copy(ap, apbak);
2302     if ((nBuf+1) > (int)sizeof(caBuf)) {
2303         /* requires a larger buffer, so allocate dynamically */
2304         if ((cpBuf = (char *)malloc((size_t)(nBuf+1))) == NULL)
2305             return SA_RC(SA_ERR_MEM);
2306     }
2307     else {
2308         /* fits into small buffer, so allocate statically */
2309         cpBuf = caBuf;
2310     }
2311     rv = SA_OK;
2312     if (sa_mvsnprintf(cpBuf, (size_t)(nBuf+1), cpFmt, ap) == -1)
2313         rv = SA_ERR_FMT;
2314     va_end(ap);
2315 
2316     /* pass-through to sa_send() */
2317     if (rv == SA_OK)
2318         rv = sa_send(sa, raddr, cpBuf, (size_t)nBuf, NULL);
2319 
2320     /* cleanup dynamically allocated buffer */
2321     if ((nBuf+1) > (int)sizeof(caBuf))
2322         free(cpBuf);
2323 
2324     return rv;
2325 }
2326 
2327 /* return error string */
2328 char *sa_error(sa_rc_t rv)
2329 {
2330     char *sz;
2331 
2332     /* translate result value into corresponding string */
2333     if      (rv == SA_OK)      sz = "Everything Ok";
2334     else if (rv == SA_ERR_ARG) sz = "Invalid Argument";
2335     else if (rv == SA_ERR_USE) sz = "Invalid Use Or Context";
2336     else if (rv == SA_ERR_MEM) sz = "Not Enough Memory";
2337     else if (rv == SA_ERR_MTC) sz = "Matching Failed";
2338     else if (rv == SA_ERR_EOF) sz = "End Of Communication";
2339     else if (rv == SA_ERR_TMT) sz = "Communication Timeout";
2340     else if (rv == SA_ERR_SYS) sz = "Operating System Error";
2341     else if (rv == SA_ERR_NET) sz = "Networking Error";
2342     else if (rv == SA_ERR_FMT) sz = "Formatting Error";
2343     else if (rv == SA_ERR_IMP) sz = "Implementation Not Available";
2344     else if (rv == SA_ERR_INT) sz = "Internal Error";
2345     else                       sz = "Invalid Result Code";
2346     return sz;
2347 }
2348 
2349