head 1.92; access; symbols LMTP2NNTP_1_4_1:1.91 LMTP2NNTP_1_4_0:1.91 SIO_0_9_3:1.91 SA_1_2_6:1.91 SA_1_2_5:1.90 SA_1_2_4:1.89 SA_1_2_3:1.88 LMTP2NNTP_1_3_0:1.85 LMTP2NNTP_1_3b2:1.85 SA_1_2_2:1.85 LMTP2NNTP_1_3b1:1.83 SA_1_2_1:1.83 LMTP2NNTP_1_3a3:1.82 LMTP2NNTP_1_3a2:1.82 LMTP2NNTP_1_3a1:1.82 SA_1_2_0:1.82 SA_1_1_0:1.75 SIO_0_9_2:1.74 SIO_0_9_1:1.74 LMTP2NNTP_1_2_0:1.74 LMTP2NNTP_1_2b4:1.74 LMTP2NNTP_1_2b3:1.74 LMTP2NNTP_1_2b2:1.74 LMTP2NNTP_1_2b1:1.73 LMTP2NNTP_1_2a8:1.73 SIO_0_9_0:1.73 LMTP2NNTP_1_2a7:1.73 SA_1_0_5:1.73 SA_1_0_4:1.73 SA_1_0_3:1.73 SA_1_0_2:1.72 SA_1_0_1:1.70 SA_1_0_0:1.67 SA_0_9_3:1.65 SA_0_9_2:1.59 LMTP2NNTP_1_2a6:1.58 LMTP2NNTP_1_2a5:1.58 SA_0_9_1:1.58 LMTP2NNTP_1_2a4:1.57 LMTP2NNTP_1_2a3:1.55 SA_0_9_0:1.55 LMTP2NNTP_1_2a1:1.39 LMTP2NNTP_1_1_1:1.39 LMTP2NNTP_1_1_0:1.39 LMTP2NNTP_1_1b4:1.39 LMTP2NNTP_1_1b3:1.39 LMTP2NNTP_1_1b2:1.33 LMTP2NNTP_1_1b1:1.33 SA_0_0_0:1.1.1.1 VENDOR:1.1.1; locks; strict; comment @ * @; 1.92 date 2006.06.10.10.00.17; author rse; state Exp; branches; next 1.91; commitid JpcJR7gM5uNpwrAr; 1.91 date 2005.10.03.08.16.56; author rse; state Exp; branches; next 1.90; 1.90 date 2005.03.29.19.24.29; author rse; state Exp; branches; next 1.89; 1.89 date 2005.01.29.08.17.32; author rse; state Exp; branches; next 1.88; 1.88 date 2005.01.24.15.10.09; author rse; state Exp; branches; next 1.87; 1.87 date 2004.10.03.14.49.37; author rse; state Exp; branches; next 1.86; 1.86 date 2004.10.03.14.00.23; author rse; state Exp; branches; next 1.85; 1.85 date 2004.06.26.11.38.20; author rse; state Exp; branches; next 1.84; 1.84 date 2004.06.26.11.16.02; author rse; state Exp; branches; next 1.83; 1.83 date 2004.06.11.10.30.34; author rse; state Exp; branches; next 1.82; 1.82 date 2004.04.02.18.49.42; author rse; state Exp; branches; next 1.81; 1.81 date 2004.04.02.18.47.39; author rse; state Exp; branches; next 1.80; 1.80 date 2004.04.02.18.21.07; author rse; state Exp; branches; next 1.79; 1.79 date 2004.04.02.12.26.59; author thl; state Exp; branches; next 1.78; 1.78 date 2004.03.26.16.57.14; author thl; state Exp; branches; next 1.77; 1.77 date 2004.03.26.16.05.59; author thl; state Exp; branches; next 1.76; 1.76 date 2004.02.17.09.21.05; author thl; state Exp; branches; next 1.75; 1.75 date 2003.11.10.18.52.35; author rse; state Exp; branches; next 1.74; 1.74 date 2003.02.07.20.47.51; author rse; state Exp; branches; next 1.73; 1.73 date 2003.01.06.13.11.23; author rse; state Exp; branches; next 1.72; 1.72 date 2002.11.28.15.42.29; author rse; state Exp; branches; next 1.71; 1.71 date 2002.11.14.08.54.00; author rse; state Exp; branches; next 1.70; 1.70 date 2002.11.07.12.44.12; author rse; state Exp; branches; next 1.69; 1.69 date 2002.11.07.12.17.06; author rse; state Exp; branches; next 1.68; 1.68 date 2002.11.07.06.24.08; author rse; state Exp; branches; next 1.67; 1.67 date 2002.10.31.07.20.10; author rse; state Exp; branches; next 1.66; 1.66 date 2002.10.30.20.22.06; author rse; state Exp; branches; next 1.65; 1.65 date 2002.10.30.19.09.35; author rse; state Exp; branches; next 1.64; 1.64 date 2002.10.30.09.21.52; author rse; state Exp; branches; next 1.63; 1.63 date 2002.10.30.08.42.16; author rse; state Exp; branches; next 1.62; 1.62 date 2002.10.26.15.45.32; author rse; state Exp; branches; next 1.61; 1.61 date 2002.10.26.11.11.05; author rse; state Exp; branches; next 1.60; 1.60 date 2002.10.25.15.27.29; author rse; state Exp; branches; next 1.59; 1.59 date 2002.10.11.15.27.39; author rse; state Exp; branches; next 1.58; 1.58 date 2002.03.15.10.47.36; author rse; state Exp; branches; next 1.57; 1.57 date 2002.03.07.11.51.54; author rse; state Exp; branches; next 1.56; 1.56 date 2002.03.07.11.23.33; author rse; state Exp; branches; next 1.55; 1.55 date 2002.01.30.16.43.00; author rse; state Exp; branches; next 1.54; 1.54 date 2002.01.29.12.48.28; author rse; state Exp; branches; next 1.53; 1.53 date 2002.01.27.20.43.21; author rse; state Exp; branches; next 1.52; 1.52 date 2002.01.27.20.42.29; author rse; state Exp; branches; next 1.51; 1.51 date 2002.01.02.13.53.11; author rse; state Exp; branches; next 1.50; 1.50 date 2002.01.02.13.52.38; author rse; state Exp; branches; next 1.49; 1.49 date 2002.01.02.13.44.48; author rse; state Exp; branches; next 1.48; 1.48 date 2002.01.02.13.35.36; author rse; state Exp; branches; next 1.47; 1.47 date 2002.01.02.13.27.11; author rse; state Exp; branches; next 1.46; 1.46 date 2002.01.02.13.21.07; author rse; state Exp; branches; next 1.45; 1.45 date 2002.01.02.13.20.11; author rse; state Exp; branches; next 1.44; 1.44 date 2002.01.02.13.08.48; author rse; state Exp; branches; next 1.43; 1.43 date 2002.01.02.13.08.13; author rse; state Exp; branches; next 1.42; 1.42 date 2002.01.02.12.43.50; author rse; state Exp; branches; next 1.41; 1.41 date 2002.01.02.12.33.10; author rse; state Exp; branches; next 1.40; 1.40 date 2002.01.02.12.31.19; author rse; state Exp; branches; next 1.39; 1.39 date 2001.10.31.20.42.42; author rse; state Exp; branches; next 1.38; 1.38 date 2001.10.31.12.41.53; author rse; state Exp; branches; next 1.37; 1.37 date 2001.10.31.11.45.35; author rse; state Exp; branches; next 1.36; 1.36 date 2001.10.24.12.11.31; author rse; state Exp; branches; next 1.35; 1.35 date 2001.10.24.11.43.28; author rse; state Exp; branches; next 1.34; 1.34 date 2001.10.24.11.23.17; author rse; state Exp; branches; next 1.33; 1.33 date 2001.10.11.15.20.30; author rse; state Exp; branches; next 1.32; 1.32 date 2001.10.11.15.12.19; author rse; state Exp; branches; next 1.31; 1.31 date 2001.10.11.14.36.29; author rse; state Exp; branches; next 1.30; 1.30 date 2001.10.10.19.32.29; author rse; state Exp; branches; next 1.29; 1.29 date 2001.10.10.15.40.00; author rse; state Exp; branches; next 1.28; 1.28 date 2001.10.10.09.21.27; author rse; state Exp; branches; next 1.27; 1.27 date 2001.10.10.09.05.18; author rse; state Exp; branches; next 1.26; 1.26 date 2001.10.10.07.42.26; author rse; state Exp; branches; next 1.25; 1.25 date 2001.10.09.20.19.58; author rse; state Exp; branches; next 1.24; 1.24 date 2001.10.09.19.58.46; author rse; state Exp; branches; next 1.23; 1.23 date 2001.10.09.18.55.22; author rse; state Exp; branches; next 1.22; 1.22 date 2001.10.09.18.38.29; author rse; state Exp; branches; next 1.21; 1.21 date 2001.10.09.18.37.18; author rse; state Exp; branches; next 1.20; 1.20 date 2001.10.09.18.36.17; author rse; state Exp; branches; next 1.19; 1.19 date 2001.10.09.18.34.12; author rse; state Exp; branches; next 1.18; 1.18 date 2001.10.09.18.26.12; author rse; state Exp; branches; next 1.17; 1.17 date 2001.10.09.14.58.22; author rse; state Exp; branches; next 1.16; 1.16 date 2001.10.09.14.05.55; author rse; state Exp; branches; next 1.15; 1.15 date 2001.10.09.13.19.25; author rse; state Exp; branches; next 1.14; 1.14 date 2001.10.08.15.15.25; author rse; state Exp; branches; next 1.13; 1.13 date 2001.10.08.13.37.08; author rse; state Exp; branches; next 1.12; 1.12 date 2001.10.08.10.03.54; author rse; state Exp; branches; next 1.11; 1.11 date 2001.10.07.15.21.17; author rse; state Exp; branches; next 1.10; 1.10 date 2001.10.07.14.16.47; author rse; state Exp; branches; next 1.9; 1.9 date 2001.10.06.16.01.48; author rse; state Exp; branches; next 1.8; 1.8 date 2001.10.05.20.52.15; author rse; state Exp; branches; next 1.7; 1.7 date 2001.10.05.13.19.30; author rse; state Exp; branches; next 1.6; 1.6 date 2001.10.05.13.17.36; author rse; state Exp; branches; next 1.5; 1.5 date 2001.10.05.11.48.49; author rse; state Exp; branches; next 1.4; 1.4 date 2001.10.05.11.40.22; author rse; state Exp; branches; next 1.3; 1.3 date 2001.10.04.11.25.44; author rse; state Exp; branches; next 1.2; 1.2 date 2001.10.03.19.40.31; author rse; state Exp; branches; next 1.1; 1.1 date 2001.10.02.13.27.44; author rse; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2001.10.02.13.27.44; author rse; state Exp; branches; next ; desc @@ 1.92 log @Adjusted all copyright messages for year 2006 @ text @/* ** OSSP sa - Socket Abstraction ** Copyright (c) 2001-2006 Ralf S. Engelschall ** Copyright (c) 2001-2006 The OSSP Project ** Copyright (c) 2001-2005 Cable & Wireless ** ** This file is part of OSSP sa, a socket abstraction library which ** can be found at http://www.ossp.org/pkg/lib/sa/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** sa.c: socket abstraction library */ /* include optional Autoconf header */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* include system API headers */ #include /* for "s[n]printf()" */ #include /* for "malloc()" & friends */ #include /* for "va_XXX()" and "va_list" */ #include /* for "strXXX()" and "size_t" */ #include /* for general prerequisites */ #include /* for "isXXX()" */ #include /* for "EXXX" */ #include /* for "F_XXX" and "O_XXX" */ #include /* for standard Unix stuff */ #include /* for "struct prototent" */ #include /* for "struct timeval" */ #include /* for "struct sockaddr_un" */ #include /* for "struct sockaddr_in[6]" */ #include /* for "PF_XXX", "AF_XXX", "SOCK_XXX" and "SHUT_XX" */ #include /* for "inet_XtoX" */ /* include own API header */ #include "sa.h" /* unique library identifier */ const char sa_id[] = "OSSP sa"; /* support for OSSP ex based exception throwing */ #ifdef WITH_EX #include "ex.h" #define SA_RC(rv) \ ( (rv) != SA_OK && (ex_catching && !ex_shielding) \ ? (ex_throw(sa_id, NULL, (rv)), (rv)) : (rv) ) #else #define SA_RC(rv) (rv) #endif /* WITH_EX */ /* boolean values */ #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (!FALSE) #endif /* backward compatibility for AF_LOCAL */ #if !defined(AF_LOCAL) && defined(AF_UNIX) #define AF_LOCAL AF_UNIX #endif /* backward compatibility for PF_XXX (still unused) */ #if !defined(PF_LOCAL) && defined(AF_LOCAL) #define PF_LOCAL AF_LOCAL #endif #if !defined(PF_INET) && defined(AF_INET) #define PF_INET AF_INET #endif #if !defined(PF_INET6) && defined(AF_INET6) #define PF_INET6 AF_INET6 #endif /* backward compatibility for SHUT_XX. Some platforms (like brain-dead OpenUNIX) define those only if _XOPEN_SOURCE is defined, but unfortunately then fail in their other vendor includes due to internal inconsistencies. */ #if !defined(SHUT_RD) #define SHUT_RD 0 #endif #if !defined(SHUT_WR) #define SHUT_WR 1 #endif #if !defined(SHUT_RDWR) #define SHUT_RDWR 2 #endif /* backward compatibility for ssize_t */ #if defined(HAVE_CONFIG_H) && !defined(HAVE_SSIZE_T) #define ssize_t long #endif /* backward compatibility for O_NONBLOCK */ #if !defined(O_NONBLOCK) && defined(O_NDELAY) #define O_NONBLOCK O_NDELAY #endif /* system call structure declaration macros */ #define SA_SC_DECLARE_0(rc_t, fn) \ struct { \ union { void (*any)(void); \ rc_t (*std)(void); \ rc_t (*ctx)(void *); } fptr; \ void *fctx; \ } sc_##fn; #define SA_SC_DECLARE_1(rc_t, fn, a1_t) \ struct { \ union { void (*any)(void); \ rc_t (*std)(a1_t); \ rc_t (*ctx)(void *, a1_t); } fptr; \ void *fctx; \ } sc_##fn; #define SA_SC_DECLARE_2(rc_t, fn, a1_t, a2_t) \ struct { \ union { void (*any)(void); \ rc_t (*std)(a1_t, a2_t); \ rc_t (*ctx)(void *, a1_t, a2_t); } fptr; \ void *fctx; \ } sc_##fn; #define SA_SC_DECLARE_3(rc_t, fn, a1_t, a2_t, a3_t) \ struct { \ union { void (*any)(void); \ rc_t (*std)(a1_t, a2_t, a3_t); \ rc_t (*ctx)(void *, a1_t, a2_t, a3_t); } fptr; \ void *fctx; \ } sc_##fn; #define SA_SC_DECLARE_4(rc_t, fn, a1_t, a2_t, a3_t, a4_t) \ struct { \ union { void (*any)(void); \ rc_t (*std)(a1_t, a2_t, a3_t, a4_t); \ rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t); } fptr; \ void *fctx; \ } sc_##fn; #define SA_SC_DECLARE_5(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t) \ struct { \ union { void (*any)(void); \ rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t); \ rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t); } fptr; \ void *fctx; \ } sc_##fn; #define SA_SC_DECLARE_6(rc_t, fn, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t) \ struct { \ union { void (*any)(void); \ rc_t (*std)(a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); \ rc_t (*ctx)(void *, a1_t, a2_t, a3_t, a4_t, a5_t, a6_t); } fptr; \ void *fctx; \ } sc_##fn; /* system call structure assignment macro */ #define SA_SC_ASSIGN(sa, fn, ptr, ctx) \ do { \ (sa)->scSysCall.sc_##fn.fptr.any = (void (*)(void))(ptr); \ (sa)->scSysCall.sc_##fn.fctx = (ctx); \ } while (0) /* system call structure assignment macro */ #define SA_SC_COPY(sa1, sa2, fn) \ do { \ (sa1)->scSysCall.sc_##fn.fptr.any = (sa2)->scSysCall.sc_##fn.fptr.any; \ (sa1)->scSysCall.sc_##fn.fctx = (sa2)->scSysCall.sc_##fn.fctx; \ } while (0) /* system call function call macros */ #define SA_SC_CALL_0(sa, fn) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(void) ) #define SA_SC_CALL_1(sa, fn, a1) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1) ) #define SA_SC_CALL_2(sa, fn, a1, a2) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2) ) #define SA_SC_CALL_3(sa, fn, a1, a2, a3) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3) ) #define SA_SC_CALL_4(sa, fn, a1, a2, a3, a4) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4) ) #define SA_SC_CALL_5(sa, fn, a1, a2, a3, a4, a5) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5) ) #define SA_SC_CALL_6(sa, fn, a1, a2, a3, a4, a5, a6) \ ( (sa)->scSysCall.sc_##fn.fctx != NULL \ ? ((sa)->scSysCall.sc_##fn.fptr.ctx)((sa)->scSysCall.sc_##fn.fctx, a1, a2, a3, a4, a5, a6) \ : ((sa)->scSysCall.sc_##fn.fptr.std)(a1, a2, a3, a4, a5, a6) ) /* system call table */ typedef struct { SA_SC_DECLARE_3(int, connect, int, const struct sockaddr *, socklen_t) SA_SC_DECLARE_3(int, accept, int, struct sockaddr *, socklen_t *) SA_SC_DECLARE_5(int, select, int, fd_set *, fd_set *, fd_set *, struct timeval *) SA_SC_DECLARE_3(ssize_t, read, int, void *, size_t) SA_SC_DECLARE_3(ssize_t, write, int, const void *, size_t) SA_SC_DECLARE_6(ssize_t, recvfrom, int, void *, size_t, int, struct sockaddr *, socklen_t *) SA_SC_DECLARE_6(ssize_t, sendto, int, const void *, size_t, int, const struct sockaddr *, socklen_t) } sa_syscall_tab_t; /* socket option information */ typedef struct { int todo; int value; } sa_optinfo_t; /* socket abstraction object */ struct sa_st { sa_type_t eType; /* socket type (stream or datagram) */ int fdSocket; /* socket file descriptor */ struct timeval tvTimeout[4]; /* timeout values (sec, usec) */ int nReadLen; /* read buffer current length */ int nReadSize; /* read buffer current size */ char *cpReadBuf; /* read buffer memory chunk */ int nWriteLen; /* write buffer current length */ int nWriteSize; /* write buffer current size */ char *cpWriteBuf; /* write buffer memory chunk */ sa_syscall_tab_t scSysCall; /* table of system calls */ sa_optinfo_t optInfo[5]; /* option storage */ }; /* socket address abstraction object */ struct sa_addr_st { int nFamily; /* the socket family (AF_XXX) */ struct sockaddr *saBuf; /* the "struct sockaddr_xx" actually */ socklen_t slBuf; /* the length of "struct sockaddr_xx" */ }; /* handy struct timeval check */ #define SA_TVISZERO(tv) \ ((tv).tv_sec == 0 && (tv).tv_usec == 0) /* convert Internet address from presentation to network format */ #ifndef HAVE_GETADDRINFO static int sa_inet_pton(int family, const char *strptr, void *addrptr) { #ifdef HAVE_INET_PTON return inet_pton(family, strptr, addrptr); #else struct in_addr in_val; if (family == AF_INET) { #if defined(HAVE_INET_ATON) /* at least for IPv4 we can rely on the old inet_aton(3) and for IPv6 inet_pton(3) would exist anyway */ if (inet_aton(strptr, &in_val) == 0) return 0; memcpy(addrptr, &in_val, sizeof(struct in_addr)); return 1; #elif defined(HAVE_INET_ADDR) /* at least for IPv4 try to rely on the even older inet_addr(3) */ memset(&in_val, '\0', sizeof(in_val)); if ((in_val.s_addr = inet_addr(strptr)) == ((in_addr_t)-1)) return 0; memcpy(addrptr, &in_val, sizeof(struct in_addr)); return 1; #endif } errno = EAFNOSUPPORT; return 0; #endif } #endif /* !HAVE_GETADDRINFO */ /* convert Internet address from network to presentation format */ static const char *sa_inet_ntop(int family, const void *src, char *dst, size_t size) { #ifdef HAVE_INET_NTOP return inet_ntop(family, src, dst, size); #else #ifdef HAVE_INET_NTOA char *cp; int n; #endif if (family == AF_INET) { #ifdef HAVE_INET_NTOA /* at least for IPv4 we can rely on the old inet_ntoa(3) and for IPv6 inet_ntop(3) would exist anyway */ if ((cp = inet_ntoa(*((struct in_addr *)src))) == NULL) return NULL; n = strlen(cp); if (n > size-1) n = size-1; memcpy(dst, cp, n); dst[n] = '\0'; return dst; #endif } errno = EAFNOSUPPORT; return NULL; #endif } /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */ static int sa_mvxprintf(int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, const char *format, va_list ap) { /* sufficient integer buffer: x log_10(2) + safety */ char ibuf[((sizeof(int)*8)/3)+10]; char *cp; char c; int d; int n; int bytes; if (format == NULL) return -1; bytes = 0; while (*format != '\0') { if (*format == '%') { c = *(format+1); if (c == '%') { /* expand "%%" */ cp = &c; n = (int)sizeof(char); } else if (c == 'c') { /* expand "%c" */ c = (char)va_arg(ap, int); cp = &c; n = (int)sizeof(char); } else if (c == 's') { /* expand "%s" */ if ((cp = (char *)va_arg(ap, char *)) == NULL) cp = "(null)"; n = (int)strlen(cp); } else if (c == 'd') { /* expand "%d" */ d = (int)va_arg(ap, int); #ifdef HAVE_SNPRINTF snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */ #else sprintf(ibuf, "%d", d); /* implicitly secure */ #endif cp = ibuf; n = (int)strlen(cp); } else { /* any other "%X" */ cp = (char *)format; n = 2; } format += 2; } else { /* plain text */ cp = (char *)format; if ((format = strchr(cp, '%')) == NULL) format = strchr(cp, '\0'); n = (int)(format - cp); } /* perform output operation */ if (output != NULL) if ((n = output(ctx, cp, (size_t)n)) == -1) break; bytes += n; } return bytes; } /* output callback function context for sa_mvsnprintf() */ typedef struct { char *bufptr; size_t buflen; } sa_mvsnprintf_cb_t; /* output callback function for sa_mvsnprintf() */ static int sa_mvsnprintf_cb(void *_ctx, const char *buffer, size_t bufsize) { sa_mvsnprintf_cb_t *ctx = (sa_mvsnprintf_cb_t *)_ctx; if (bufsize > ctx->buflen) return -1; memcpy(ctx->bufptr, buffer, bufsize); ctx->bufptr += bufsize; ctx->buflen -= bufsize; return (int)bufsize; } /* minimal vsnprintf(3) variant which supports %{c,s,d} only */ static int sa_mvsnprintf(char *buffer, size_t bufsize, const char *format, va_list ap) { int n; sa_mvsnprintf_cb_t ctx; if (format == NULL) return -1; if (buffer != NULL && bufsize == 0) return -1; if (buffer == NULL) /* just determine output length */ n = sa_mvxprintf(NULL, NULL, format, ap); else { /* perform real output */ ctx.bufptr = buffer; ctx.buflen = bufsize; n = sa_mvxprintf(sa_mvsnprintf_cb, &ctx, format, ap); if (n != -1 && ctx.buflen == 0) n = -1; if (n != -1) *(ctx.bufptr) = '\0'; } return n; } /* minimal snprintf(3) variant which supports %{c,s,d} only */ static int sa_msnprintf(char *buffer, size_t bufsize, const char *format, ...) { int chars; va_list ap; /* pass through to va_list based variant */ va_start(ap, format); chars = sa_mvsnprintf(buffer, bufsize, format, ap); va_end(ap); return chars; } /* create address object */ sa_rc_t sa_addr_create(sa_addr_t **saap) { sa_addr_t *saa; /* argument sanity check(s) */ if (saap == NULL) return SA_RC(SA_ERR_ARG); /* allocate and initialize new address object */ if ((saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL) return SA_RC(SA_ERR_MEM); saa->nFamily = 0; saa->saBuf = NULL; saa->slBuf = 0; /* pass object to caller */ *saap = saa; return SA_OK; } /* destroy address object */ sa_rc_t sa_addr_destroy(sa_addr_t *saa) { /* argument sanity check(s) */ if (saa == NULL) return SA_RC(SA_ERR_ARG); /* free address objects and sub-object(s) */ if (saa->saBuf != NULL) free(saa->saBuf); free(saa); return SA_OK; } /* import URI into address object */ sa_rc_t sa_addr_u2a(sa_addr_t *saa, const char *uri, ...) { va_list ap; int sf; socklen_t sl; struct sockaddr *sa; struct sockaddr_un un; #ifdef HAVE_GETADDRINFO struct addrinfo ai_hints; struct addrinfo *ai = NULL; int err; #else struct sockaddr_in sa4; #ifdef AF_INET6 struct sockaddr_in6 sa6; #endif struct hostent *he; #endif struct servent *se; int bIPv6; int bNumeric; const char *cpHost; const char *cpPort; char *cpProto; unsigned int nPort; const char *cpPath; char uribuf[1024]; char *cp; int i; size_t n; int k; /* argument sanity check(s) */ if (saa == NULL || uri == NULL) return SA_RC(SA_ERR_ARG); /* on-the-fly create or just take over URI */ va_start(ap, uri); k = sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap); va_end(ap); if (k == -1) return SA_RC(SA_ERR_MEM); /* initialize result variables */ sa = NULL; sl = 0; sf = 0; /* parse URI and resolve contents. The following syntax is recognized: - unix: - inet://:[#(tcp|udp)] */ uri = uribuf; if (strncmp(uri, "unix:", 5) == 0) { /* parse URI */ cpPath = uri+5; /* mandatory(!) socket address structure initialization */ memset(&un, 0, sizeof(un)); /* fill-in socket address structure */ n = strlen(cpPath); if (n == 0) return SA_RC(SA_ERR_ARG); if ((n+1) > sizeof(un.sun_path)) return SA_RC(SA_ERR_MEM); if (cpPath[0] != '/') { if (getcwd(un.sun_path, sizeof(un.sun_path)-(n+1)) == NULL) return SA_RC(SA_ERR_MEM); cp = un.sun_path + strlen(un.sun_path); } else cp = un.sun_path; memcpy(cp, cpPath, n+1); un.sun_family = AF_LOCAL; /* provide results */ sa = (struct sockaddr *)&un; sl = (socklen_t)sizeof(un); sf = AF_LOCAL; } else if (strncmp(uri, "inet://", 7) == 0) { /* parse URI into host, port and protocol parts */ cpHost = uri+7; bIPv6 = FALSE; if (cpHost[0] == '[') { /* IPv6 address (see RFC2732) */ #ifndef AF_INET6 return SA_RC(SA_ERR_IMP); #else bIPv6 = TRUE; cpHost++; if ((cp = strchr(cpHost, ']')) == NULL) return SA_RC(SA_ERR_ARG); *cp++ = '\0'; if (*cp != ':') return SA_RC(SA_ERR_ARG); cp++; #endif } else { /* IPv4 address or hostname */ if ((cp = strrchr(cpHost, ':')) == NULL) return SA_RC(SA_ERR_ARG); *cp++ = '\0'; } cpPort = cp; cpProto = "tcp"; if ((cp = strchr(cpPort, '#')) != NULL) { *cp++ = '\0'; cpProto = cp; } /* resolve port */ nPort = 0; bNumeric = 1; for (i = 0; cpPort[i] != '\0'; i++) { if (!isdigit((int)cpPort[i])) { bNumeric = 0; break; } } if (bNumeric) nPort = (unsigned int)atoi(cpPort); else { if ((se = getservbyname(cpPort, cpProto)) == NULL) return SA_RC(SA_ERR_SYS); nPort = ntohs(se->s_port); } #ifdef HAVE_GETADDRINFO memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = PF_UNSPEC; if ((err = getaddrinfo(cpHost, NULL, &ai_hints, &ai)) != 0) { if (err == EAI_MEMORY) return SA_RC(SA_ERR_MEM); else if (err == EAI_SYSTEM) return SA_RC(SA_ERR_SYS); else return SA_RC(SA_ERR_ARG); } sa = ai->ai_addr; sl = ai->ai_addrlen; sf = ai->ai_family; if (sf == AF_INET) ((struct sockaddr_in *)sa)->sin_port = htons(nPort); else if (sf == AF_INET6) ((struct sockaddr_in6 *)sa)->sin6_port = htons(nPort); else return SA_RC(SA_ERR_ARG); #else /* !HAVE_GETADDRINFO */ /* mandatory(!) socket address structure initialization */ memset(&sa4, 0, sizeof(sa4)); #ifdef AF_INET6 memset(&sa6, 0, sizeof(sa6)); #endif /* resolve host by trying to parse it as either directly an IPv4 or IPv6 address or by resolving it to either an IPv4 or IPv6 address */ if (!bIPv6 && sa_inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) { sa4.sin_family = AF_INET; sa4.sin_port = htons(nPort); sa = (struct sockaddr *)&sa4; sl = (socklen_t)sizeof(sa4); sf = AF_INET; } #ifdef AF_INET6 else if (bIPv6 && sa_inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(nPort); sa = (struct sockaddr *)&sa6; sl = (socklen_t)sizeof(sa6); sf = AF_INET6; } #endif else if ((he = gethostbyname(cpHost)) != NULL) { if (he->h_addrtype == AF_INET) { sa4.sin_family = AF_INET; sa4.sin_port = htons(nPort); memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0], sizeof(sa4.sin_addr.s_addr)); sa = (struct sockaddr *)&sa4; sl = (socklen_t)sizeof(sa4); sf = AF_INET; } #ifdef AF_INET6 else if (he->h_addrtype == AF_INET6) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(nPort); memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], sizeof(sa6.sin6_addr.s6_addr)); sa = (struct sockaddr *)&sa6; sl = (socklen_t)sizeof(sa6); sf = AF_INET6; } #endif else return SA_RC(SA_ERR_ARG); } else return SA_RC(SA_ERR_ARG); #endif /* !HAVE_GETADDRINFO */ } else return SA_RC(SA_ERR_ARG); /* fill-in result address structure */ if (saa->saBuf != NULL) free(saa->saBuf); if ((saa->saBuf = (struct sockaddr *)malloc((size_t)sl)) == NULL) return SA_RC(SA_ERR_MEM); memcpy(saa->saBuf, sa, (size_t)sl); saa->slBuf = sl; saa->nFamily = (int)sf; #ifdef HAVE_GETADDRINFO if (ai != NULL) freeaddrinfo(ai); #endif return SA_OK; } /* import "struct sockaddr" into address object */ sa_rc_t sa_addr_s2a(sa_addr_t *saa, const struct sockaddr *sabuf, socklen_t salen) { /* argument sanity check(s) */ if (saa == NULL || sabuf == NULL || salen == 0) return SA_RC(SA_ERR_ARG); /* make sure we import only supported addresses */ if (!( sabuf->sa_family == AF_LOCAL || sabuf->sa_family == AF_INET #ifdef AF_INET6 || sabuf->sa_family == AF_INET6 #endif )) return SA_RC(SA_ERR_USE); /* create result address structure */ if (saa->saBuf != NULL) free(saa->saBuf); if ((saa->saBuf = (struct sockaddr *)malloc((size_t)salen)) == NULL) return SA_RC(SA_ERR_MEM); memcpy(saa->saBuf, sabuf, (size_t)salen); saa->slBuf = salen; /* remember family */ saa->nFamily = (int)(sabuf->sa_family); return SA_OK; } /* export address object into URI */ sa_rc_t sa_addr_a2u(const sa_addr_t *saa, char **uri) { char uribuf[1024]; struct sockaddr_un *un; struct sockaddr_in *sa4; #ifdef AF_INET6 struct sockaddr_in6 *sa6; #endif char caHost[512]; unsigned int nPort; /* argument sanity check(s) */ if (saa == NULL || uri == NULL) return SA_RC(SA_ERR_ARG); /* export object contents */ if (saa->nFamily == AF_LOCAL) { un = (struct sockaddr_un *)((void *)saa->saBuf); if ( ( saa->slBuf >= (socklen_t)(&(((struct sockaddr_un *)0)->sun_path[0])) && un->sun_path[0] == '\0') || (size_t)(saa->slBuf) < sizeof(struct sockaddr_un)) { /* in case the remote side of a Unix Domain socket was not bound, a "struct sockaddr_un" can occur with a length less than the expected one. Then there is actually no path at all. This has been verified under FreeBSD, Linux and Solaris. */ if (sa_msnprintf(uribuf, sizeof(uribuf), "unix:/NOT-BOUND") == -1) return SA_RC(SA_ERR_FMT); } else { if (sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path) == -1) return SA_RC(SA_ERR_FMT); } } else if (saa->nFamily == AF_INET) { sa4 = (struct sockaddr_in *)((void *)saa->saBuf); if (sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)) == NULL) return SA_RC(SA_ERR_NET); nPort = ntohs(sa4->sin_port); if (sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort) == -1) return SA_RC(SA_ERR_FMT); } #ifdef AF_INET6 else if (saa->nFamily == AF_INET6) { sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf); if (sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)) == NULL) return SA_RC(SA_ERR_NET); nPort = ntohs(sa6->sin6_port); if (sa_msnprintf(uribuf, sizeof(uribuf), "inet://[%s]:%d", caHost, nPort) == -1) return SA_RC(SA_ERR_FMT); } #endif else return SA_RC(SA_ERR_INT); /* pass result to caller */ *uri = strdup(uribuf); return SA_OK; } /* export address object into "struct sockaddr" */ sa_rc_t sa_addr_a2s(const sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen) { /* argument sanity check(s) */ if (saa == NULL || sabuf == NULL || salen == 0) return SA_RC(SA_ERR_ARG); /* export underlying address structure */ if ((*sabuf = (struct sockaddr *)malloc((size_t)saa->slBuf)) == NULL) return SA_RC(SA_ERR_MEM); memmove(*sabuf, saa->saBuf, (size_t)saa->slBuf); *salen = saa->slBuf; return SA_OK; } sa_rc_t sa_addr_match(const sa_addr_t *saa1, const sa_addr_t *saa2, int prefixlen) { const unsigned char *ucp1, *ucp2; unsigned int uc1, uc2, mask; size_t l1, l2; int nBytes; int nBits; #ifdef AF_INET6 int i; const unsigned char *ucp0; #endif unsigned int np1, np2; int bMatchPort; /* argument sanity check(s) */ if (saa1 == NULL || saa2 == NULL) return SA_RC(SA_ERR_ARG); /* short circuiting for wildcard matching */ if (prefixlen == 0) return SA_OK; /* determine address representation pointer and size */ if (saa1->nFamily == AF_LOCAL) { np1 = 0; np2 = 0; ucp1 = (const unsigned char *)(((struct sockaddr_un *)saa1->saBuf)->sun_path); ucp2 = (const unsigned char *)(((struct sockaddr_un *)saa2->saBuf)->sun_path); l1 = strlen(((struct sockaddr_un *)saa1->saBuf)->sun_path) * 8; l2 = strlen(((struct sockaddr_un *)saa2->saBuf)->sun_path) * 8; if (prefixlen < 0) { if (l1 != l2) return SA_RC(SA_ERR_MTC); nBits = (int)l1; } else { if ((int)l1 < prefixlen || (int)l2 < prefixlen) return SA_RC(SA_ERR_MTC); nBits = prefixlen; } } #ifdef AF_INET6 else if ( (saa1->nFamily == AF_INET && saa2->nFamily == AF_INET6) || (saa1->nFamily == AF_INET6 && saa2->nFamily == AF_INET )) { /* special case of comparing a regular IPv4 address (1.2.3.4) with an "IPv4-mapped IPv6 address" (::ffff:1.2.3.4). For details see RFC 2373. */ if (saa1->nFamily == AF_INET6) { np1 = (unsigned int)(((struct sockaddr_in6 *)saa1->saBuf)->sin6_port); np2 = (unsigned int)(((struct sockaddr_in *)saa2->saBuf)->sin_port); ucp1 = (const unsigned char *)&(((struct sockaddr_in6 *)saa1->saBuf)->sin6_addr); ucp2 = (const unsigned char *)&(((struct sockaddr_in *)saa2->saBuf)->sin_addr); ucp0 = ucp1; ucp1 += 12; } else { np1 = (unsigned int)(((struct sockaddr_in *)saa1->saBuf)->sin_port); np2 = (unsigned int)(((struct sockaddr_in6 *)saa2->saBuf)->sin6_port); ucp1 = (const unsigned char *)&(((struct sockaddr_in *)saa1->saBuf)->sin_addr); ucp2 = (const unsigned char *)&(((struct sockaddr_in6 *)saa2->saBuf)->sin6_addr); ucp0 = ucp2; ucp2 += 12; } for (i = 0; i < 10; i++) if ((int)ucp0[i] != 0x00) return SA_RC(SA_ERR_MTC); if (!((int)ucp0[10] == 0xFF && (int)ucp0[11] == 0xFF)) return SA_RC(SA_ERR_MTC); nBits = 32; } #endif else if (saa1->nFamily == AF_INET) { np1 = (unsigned int)(((struct sockaddr_in *)saa1->saBuf)->sin_port); np2 = (unsigned int)(((struct sockaddr_in *)saa2->saBuf)->sin_port); ucp1 = (const unsigned char *)&(((struct sockaddr_in *)saa1->saBuf)->sin_addr); ucp2 = (const unsigned char *)&(((struct sockaddr_in *)saa2->saBuf)->sin_addr); nBits = 32; } #ifdef AF_INET6 else if (saa1->nFamily == AF_INET6) { np1 = (unsigned int)(((struct sockaddr_in6 *)saa1->saBuf)->sin6_port); np2 = (unsigned int)(((struct sockaddr_in6 *)saa2->saBuf)->sin6_port); ucp1 = (const unsigned char *)&(((struct sockaddr_in6 *)saa1->saBuf)->sin6_addr); ucp2 = (const unsigned char *)&(((struct sockaddr_in6 *)saa2->saBuf)->sin6_addr); nBits = 128; } #endif else return SA_RC(SA_ERR_INT); /* make sure we do not compare than possible */ if (prefixlen > (nBits+1)) return SA_RC(SA_ERR_ARG); /* support equal matching (= all bits plus optionally port) */ bMatchPort = FALSE; if (prefixlen < 0) { if (prefixlen < -1) bMatchPort = TRUE; prefixlen = nBits; } /* perform address representation comparison (assumption guaranteed by API: network byte order is used) */ nBytes = (prefixlen / 8); nBits = (prefixlen % 8); if (nBytes > 0) { if (memcmp(ucp1, ucp2, (size_t)nBytes) != 0) return SA_RC(SA_ERR_MTC); } if (nBits > 0) { uc1 = (unsigned int)ucp1[nBytes]; uc2 = (unsigned int)ucp2[nBytes]; mask = ((unsigned int)0xFF << (8-nBits)) & (unsigned int)0xFF; if ((uc1 & mask) != (uc2 & mask)) return SA_RC(SA_ERR_MTC); } /* optionally perform additional port matching */ if (bMatchPort) if (np1 != np2) return SA_RC(SA_ERR_MTC); return SA_OK; } /* set timeouts if timeouts or done in kernel */ static sa_rc_t sa_socket_settimeouts(const sa_t *sa) { /* stop processing if socket is still not allocated */ if (sa->fdSocket == -1) return SA_OK; #if defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO) if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) { if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_RCVTIMEO, (const void *)(&sa->tvTimeout[SA_TIMEOUT_READ]), (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_READ]))) < 0) return SA_RC(SA_ERR_SYS); } if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO, (const void *)(&sa->tvTimeout[SA_TIMEOUT_WRITE]), (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE]))) < 0) return SA_RC(SA_ERR_SYS); } #endif return SA_OK; } /* set socket options */ static sa_rc_t sa_socket_setoptions(sa_t *sa) { int i; sa_rc_t rv; /* stop processing if socket is still not allocated */ if (sa->fdSocket == -1) return SA_OK; /* check for pending options */ rv = SA_OK; for (i = 0; i < (int)(sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) { if (sa->optInfo[i].todo) { switch (i) { /* enable/disable Nagle's Algorithm (see RFC898) */ case SA_OPTION_NAGLE: { #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) int mode = sa->optInfo[i].value; if (setsockopt(sa->fdSocket, IPPROTO_TCP, TCP_NODELAY, (const void *)&mode, (socklen_t)sizeof(mode)) < 0) rv = SA_ERR_SYS; else sa->optInfo[i].todo = FALSE; #endif break; } /* enable/disable linger close semantics */ case SA_OPTION_LINGER: { #if defined(SO_LINGER) struct linger linger; linger.l_onoff = (sa->optInfo[i].value == 0 ? 0 : 1); linger.l_linger = (sa->optInfo[i].value <= 0 ? 0 : sa->optInfo[i].value); if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_LINGER, (const void *)&linger, (socklen_t)sizeof(struct linger)) < 0) rv = SA_ERR_SYS; else sa->optInfo[i].todo = FALSE; #endif break; } /* enable/disable reusability of binding to address */ case SA_OPTION_REUSEADDR: { #if defined(SO_REUSEADDR) int mode = sa->optInfo[i].value; if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_REUSEADDR, (const void *)&mode, (socklen_t)sizeof(mode)) < 0) rv = SA_ERR_SYS; else sa->optInfo[i].todo = FALSE; #endif break; } /* enable/disable reusability of binding to port */ case SA_OPTION_REUSEPORT: { #if defined(SO_REUSEPORT) int mode = sa->optInfo[i].value; if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_REUSEPORT, (const void *)&mode, (socklen_t)sizeof(mode)) < 0) rv = SA_ERR_SYS; else sa->optInfo[i].todo = FALSE; #endif break; } /* enable/disable non-blocking I/O mode */ case SA_OPTION_NONBLOCK: { int mode = sa->optInfo[i].value; int flags; if ((flags = fcntl(sa->fdSocket, F_GETFL, 0)) < 0) { rv = SA_ERR_SYS; break; } if (mode == 0) flags &= ~(O_NONBLOCK); else flags |= O_NONBLOCK; if (fcntl(sa->fdSocket, F_SETFL, flags) < 0) rv = SA_ERR_SYS; else sa->optInfo[i].todo = FALSE; break; } default: /* NOTREACHED */ break; } } } return SA_RC(rv); } /* internal lazy/delayed initialization of underlying socket */ static sa_rc_t sa_socket_init(sa_t *sa, int nFamily) { int nType; int nProto; #if !(defined(IPPROTO_TCP) && defined(IPPROTO_UDP)) struct protoent *pe; #endif sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); /* only perform operation if socket still does not exist */ if (sa->fdSocket != -1) return SA_RC(SA_ERR_USE); /* determine socket type */ if (sa->eType == SA_TYPE_STREAM) nType = SOCK_STREAM; else if (sa->eType == SA_TYPE_DATAGRAM) nType = SOCK_DGRAM; else return SA_RC(SA_ERR_INT); /* determine socket protocol */ if (nFamily == AF_LOCAL) nProto = 0; #ifdef AF_INET6 else if (nFamily == AF_INET || nFamily == AF_INET6) { #else else if (nFamily == AF_INET) { #endif #if defined(IPPROTO_TCP) && defined(IPPROTO_UDP) if (nType == SOCK_STREAM) nProto = IPPROTO_TCP; else if (nType == SOCK_DGRAM) nProto = IPPROTO_UDP; else return SA_RC(SA_ERR_INT); #else if (nType == SOCK_STREAM) pe = getprotobyname("tcp"); else if (nType == SOCK_DGRAM) pe = getprotobyname("udp"); else return SA_RC(SA_ERR_INT); if (pe == NULL) return SA_RC(SA_ERR_SYS); nProto = pe->p_proto; #endif } else return SA_RC(SA_ERR_INT); /* create the underlying socket */ if ((sa->fdSocket = socket(nFamily, nType, nProto)) == -1) return SA_RC(SA_ERR_SYS); /* optionally set kernel timeouts */ if ((rv = sa_socket_settimeouts(sa)) != SA_OK) return SA_RC(rv); /* optionally configure changed options */ if ((rv = sa_socket_setoptions(sa)) != SA_OK) return SA_RC(rv); return SA_OK; } /* internal destruction of underlying socket */ static sa_rc_t sa_socket_kill(sa_t *sa) { /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); /* check context */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* close socket */ (void)close(sa->fdSocket); sa->fdSocket = -1; return SA_OK; } /* create abstract socket object */ sa_rc_t sa_create(sa_t **sap) { sa_t *sa; int i; /* argument sanity check(s) */ if (sap == NULL) return SA_RC(SA_ERR_ARG); /* allocate and initialize socket object */ if ((sa = (sa_t *)malloc(sizeof(sa_t))) == NULL) return SA_RC(SA_ERR_MEM); /* init object attributes */ sa->eType = SA_TYPE_STREAM; sa->fdSocket = -1; sa->nReadLen = 0; sa->nReadSize = 0; sa->cpReadBuf = NULL; sa->nWriteLen = 0; sa->nWriteSize = 0; sa->cpWriteBuf = NULL; /* init timeval object attributes */ for (i = 0; i < (int)(sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) { sa->tvTimeout[i].tv_sec = 0; sa->tvTimeout[i].tv_usec = 0; } /* init options object attributes */ for (i = 0; i < (int)(sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) { sa->optInfo[i].todo = FALSE; sa->optInfo[i].value = 0; } /* init syscall object attributes */ SA_SC_ASSIGN(sa, connect, connect, NULL); SA_SC_ASSIGN(sa, accept, accept, NULL); SA_SC_ASSIGN(sa, select, select, NULL); SA_SC_ASSIGN(sa, read, read, NULL); SA_SC_ASSIGN(sa, write, write, NULL); SA_SC_ASSIGN(sa, recvfrom, recvfrom, NULL); SA_SC_ASSIGN(sa, sendto, sendto, NULL); /* pass object to caller */ *sap = sa; return SA_OK; } /* destroy abstract socket object */ sa_rc_t sa_destroy(sa_t *sa) { /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); /* kill underlying socket */ (void)sa_socket_kill(sa); /* free object and sub-objects */ if (sa->cpReadBuf != NULL) free(sa->cpReadBuf); if (sa->cpWriteBuf != NULL) free(sa->cpWriteBuf); free(sa); return SA_OK; } /* switch communication type of socket */ sa_rc_t sa_type(sa_t *sa, sa_type_t type) { /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); if (!(type == SA_TYPE_STREAM || type == SA_TYPE_DATAGRAM)) return SA_RC(SA_ERR_ARG); /* kill underlying socket if type changes */ if (sa->eType != type) (void)sa_socket_kill(sa); /* set new type */ sa->eType = type; return SA_OK; } /* configure I/O timeout */ sa_rc_t sa_timeout(sa_t *sa, sa_timeout_t id, long sec, long usec) { int i; sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); if (id == SA_TIMEOUT_ALL) { for (i = 0; i < (int)(sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) { sa->tvTimeout[i].tv_sec = sec; sa->tvTimeout[i].tv_usec = usec; } } else { sa->tvTimeout[id].tv_sec = sec; sa->tvTimeout[id].tv_usec = usec; } /* try to already set timeouts */ if ((rv = sa_socket_settimeouts(sa)) != SA_OK) return SA_RC(rv); return SA_OK; } /* configure I/O buffers */ sa_rc_t sa_buffer(sa_t *sa, sa_buffer_t id, size_t size) { char *cp; /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); if (id == SA_BUFFER_READ) { /* configure read/incoming buffer */ if (sa->nReadLen > (int)size) return SA_RC(SA_ERR_USE); if (size > 0) { if (sa->cpReadBuf == NULL) cp = (char *)malloc(size); else cp = (char *)realloc(sa->cpReadBuf, size); if (cp == NULL) return SA_RC(SA_ERR_MEM); sa->cpReadBuf = cp; sa->nReadSize = (int)size; } else { if (sa->cpReadBuf != NULL) free(sa->cpReadBuf); sa->cpReadBuf = NULL; sa->nReadSize = 0; } } else if (id == SA_BUFFER_WRITE) { /* configure write/outgoing buffer */ if (sa->nWriteLen > (int)size) return SA_RC(SA_ERR_USE); if (size > 0) { if (sa->cpWriteBuf == NULL) cp = (char *)malloc(size); else cp = (char *)realloc(sa->cpWriteBuf, size); if (cp == NULL) return SA_RC(SA_ERR_MEM); sa->cpWriteBuf = cp; sa->nWriteSize = (int)size; } else { if (sa->cpWriteBuf != NULL) free(sa->cpWriteBuf); sa->cpWriteBuf = NULL; sa->nWriteSize = 0; } } else return SA_RC(SA_ERR_ARG); return SA_OK; } /* configure socket option */ sa_rc_t sa_option(sa_t *sa, sa_option_t id, ...) { sa_rc_t rv; va_list ap; /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); /* process option */ rv = SA_OK; va_start(ap, id); switch (id) { case SA_OPTION_NAGLE: { #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) int mode = ((int)va_arg(ap, int) ? 1 : 0); sa->optInfo[SA_OPTION_NAGLE].value = mode; sa->optInfo[SA_OPTION_NAGLE].todo = TRUE; #else rv = SA_ERR_IMP; #endif break; } case SA_OPTION_LINGER: { #if defined(SO_LINGER) int amount = (int)va_arg(ap, int); sa->optInfo[SA_OPTION_LINGER].value = amount; sa->optInfo[SA_OPTION_LINGER].todo = TRUE; #else rv = SA_ERR_IMP; #endif break; } case SA_OPTION_REUSEADDR: { #if defined(SO_REUSEADDR) int mode = ((int)va_arg(ap, int) ? 1 : 0); sa->optInfo[SA_OPTION_REUSEADDR].value = mode; sa->optInfo[SA_OPTION_REUSEADDR].todo = TRUE; #else rv = SA_ERR_IMP; #endif break; } case SA_OPTION_REUSEPORT: { #if defined(SO_REUSEPORT) int mode = ((int)va_arg(ap, int) ? 1 : 0); sa->optInfo[SA_OPTION_REUSEPORT].value = mode; sa->optInfo[SA_OPTION_REUSEPORT].todo = TRUE; #else rv = SA_ERR_IMP; #endif break; } case SA_OPTION_NONBLOCK: { int mode = (int)va_arg(ap, int); sa->optInfo[SA_OPTION_NONBLOCK].value = mode; sa->optInfo[SA_OPTION_NONBLOCK].todo = TRUE; break; } default: { rv = SA_ERR_ARG; } } va_end(ap); /* if an error already occured, stop here */ if (rv != SA_OK) return SA_RC(rv); /* else already (re)set) options (if possible) */ if ((rv = sa_socket_setoptions(sa)) != SA_OK) return SA_RC(rv); return SA_OK; } /* override system call */ sa_rc_t sa_syscall(sa_t *sa, sa_syscall_t id, void (*fptr)(void), void *fctx) { sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL || fptr == NULL) return SA_RC(SA_ERR_ARG); /* assign system call */ rv = SA_OK; switch (id) { case SA_SYSCALL_CONNECT: SA_SC_ASSIGN(sa, connect, fptr, fctx); break; case SA_SYSCALL_ACCEPT: SA_SC_ASSIGN(sa, accept, fptr, fctx); break; case SA_SYSCALL_SELECT: SA_SC_ASSIGN(sa, select, fptr, fctx); break; case SA_SYSCALL_READ: SA_SC_ASSIGN(sa, read, fptr, fctx); break; case SA_SYSCALL_WRITE: SA_SC_ASSIGN(sa, write, fptr, fctx); break; case SA_SYSCALL_RECVFROM: SA_SC_ASSIGN(sa, recvfrom, fptr, fctx); break; case SA_SYSCALL_SENDTO: SA_SC_ASSIGN(sa, sendto, fptr, fctx); break; default: rv = SA_ERR_ARG; } return SA_RC(rv); } /* bind socket to a local address */ sa_rc_t sa_bind(sa_t *sa, const sa_addr_t *laddr) { sa_rc_t rv; struct sockaddr_un *un; /* argument sanity check(s) */ if (sa == NULL || laddr == NULL) return SA_RC(SA_ERR_ARG); /* lazy creation of underlying socket */ if (sa->fdSocket == -1) if ((rv = sa_socket_init(sa, laddr->nFamily)) != SA_OK) return SA_RC(rv); /* remove a possibly existing old Unix Domain socket on filesystem */ if (laddr->nFamily == AF_LOCAL) { un = (struct sockaddr_un *)((void *)laddr->saBuf); (void)unlink(un->sun_path); } /* perform bind operation on underlying socket */ if (bind(sa->fdSocket, laddr->saBuf, laddr->slBuf) == -1) return SA_RC(SA_ERR_SYS); return SA_OK; } /* connect socket to a remote address */ sa_rc_t sa_connect(sa_t *sa, const sa_addr_t *raddr) { int flags, n, error; fd_set rset, wset; socklen_t len; sa_rc_t rv; struct timeval *tv; struct timeval tv_buf; /* argument sanity check(s) */ if (sa == NULL || raddr == NULL) return SA_RC(SA_ERR_ARG); /* connecting is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* lazy creation of underlying socket */ if (sa->fdSocket == -1) if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK) return SA_RC(rv); /* prepare return code decision */ rv = SA_OK; error = 0; /* temporarily switch underlying socket to non-blocking mode (necessary under timeout-aware operation only) */ flags = 0; if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) { flags = fcntl(sa->fdSocket, F_GETFL, 0); (void)fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK); } /* perform the connect operation */ if ((n = SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) { if (errno != EINTR && errno != EINPROGRESS) { /* we have to perform the following post-processing under EINPROGRESS anway, but actually also for EINTR according to Unix Network Programming, volume 1, section 5.9, W. Richard Stevens: "What we are doing [] is restarting the interrupted system call ourself. This is fine for accept, along with the functions such as read, write, select and open. But there is one function that we cannot restart ourself: connect. If this function returns EINTR, we cannot call it again, as doing so will return an immediate error. When connect is interrupted by a caught signal and is not automatically restarted, we must call select to wait for the connection to complete, as we describe in section 15.3." */ error = errno; goto done; } } /* ok if connect completed immediately */ if (n == 0) goto done; /* wait for read or write possibility */ FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(sa->fdSocket, &rset); FD_SET(sa->fdSocket, &wset); if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) { memcpy(&tv_buf, &sa->tvTimeout[SA_TIMEOUT_CONNECT], sizeof(struct timeval)); tv = &tv_buf; } else tv = NULL; do { n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &rset, &wset, (fd_set *)NULL, tv); } while (n == -1 && errno == EINTR); /* decide on return semantic */ if (n < 0) { error = errno; goto done; } else if (n == 0) { (void)close(sa->fdSocket); /* stop TCP three-way handshake */ sa->fdSocket = -1; rv = SA_ERR_TMT; goto done; } /* fetch pending error */ len = (socklen_t)sizeof(error); if (getsockopt(sa->fdSocket, SOL_SOCKET, SO_ERROR, (void *)&error, &len) < 0) error = errno; done: /* reset socket flags (necessary under timeout-aware operation only) */ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) (void)fcntl(sa->fdSocket, F_SETFL, flags); /* optionally set errno */ if (error != 0) { (void)close(sa->fdSocket); /* just in case */ sa->fdSocket = -1; errno = error; rv = SA_ERR_SYS; } return SA_RC(rv); } /* listen on socket for connections */ sa_rc_t sa_listen(sa_t *sa, int backlog) { /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); /* listening is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least sa_bind() has to be already performed */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* perform listen operation on underlying socket */ if (listen(sa->fdSocket, backlog) == -1) return SA_RC(SA_ERR_SYS); return SA_OK; } /* accept a connection on socket */ sa_rc_t sa_accept(sa_t *sa, sa_addr_t **caddr, sa_t **csa) { sa_rc_t rv; int n; fd_set fds; union { struct sockaddr_un un; struct sockaddr_in sa4; #ifdef AF_INET6 struct sockaddr_in6 sa6; #endif } sa_buf; socklen_t sa_size; struct timeval tv; int s; int i; /* argument sanity check(s) */ if (sa == NULL || caddr == NULL || csa == NULL) return SA_RC(SA_ERR_ARG); /* accepting connections is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least sa_listen() has to be already performed */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* if timeout is enabled, perform a smart-blocking wait */ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_ACCEPT])) { FD_ZERO(&fds); FD_SET(sa->fdSocket, &fds); memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_ACCEPT], sizeof(struct timeval)); do { n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &tv); } while (n == -1 && errno == EINTR); if (n == 0) return SA_RC(SA_ERR_TMT); if (n <= 0) return SA_RC(SA_ERR_SYS); } /* perform accept operation on underlying socket */ sa_size = (socklen_t)sizeof(sa_buf); do { s = SA_SC_CALL_3(sa, accept, sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size); } while (s == -1 && errno == EINTR); if (s == -1) return SA_RC(SA_ERR_SYS); /* create result address object */ if ((rv = sa_addr_create(caddr)) != SA_OK) return SA_RC(rv); if ((rv = sa_addr_s2a(*caddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) { (void)sa_addr_destroy(*caddr); return SA_RC(rv); } /* create result socket object */ if ((rv = sa_create(csa)) != SA_OK) { (void)sa_addr_destroy(*caddr); return SA_RC(rv); } /* fill-in child socket */ (*csa)->fdSocket = s; /* copy-over original system calls */ SA_SC_COPY((*csa), sa, connect); SA_SC_COPY((*csa), sa, accept); SA_SC_COPY((*csa), sa, select); SA_SC_COPY((*csa), sa, read); SA_SC_COPY((*csa), sa, write); SA_SC_COPY((*csa), sa, recvfrom); SA_SC_COPY((*csa), sa, sendto); /* copy-over original timeout values */ for (i = 0; i < (int)(sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) { (*csa)->tvTimeout[i].tv_sec = sa->tvTimeout[i].tv_sec; (*csa)->tvTimeout[i].tv_usec = sa->tvTimeout[i].tv_usec; } return SA_OK; } /* determine remote address */ sa_rc_t sa_getremote(sa_t *sa, sa_addr_t **raddr) { sa_rc_t rv; union { struct sockaddr_in sa4; #ifdef AF_INET6 struct sockaddr_in6 sa6; #endif } sa_buf; socklen_t sa_size; /* argument sanity check(s) */ if (sa == NULL || raddr == NULL) return SA_RC(SA_ERR_ARG); /* peers exist only for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least sa_connect() or sa_accept() has to be already performed */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* determine remote address of underlying socket */ sa_size = (socklen_t)sizeof(sa_buf); if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size) < 0) return SA_RC(SA_ERR_SYS); /* create result address object */ if ((rv = sa_addr_create(raddr)) != SA_OK) return SA_RC(rv); if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) { (void)sa_addr_destroy(*raddr); return SA_RC(rv); } return SA_OK; } /* determine local address */ sa_rc_t sa_getlocal(sa_t *sa, sa_addr_t **laddr) { sa_rc_t rv; union { struct sockaddr_in sa4; #ifdef AF_INET6 struct sockaddr_in6 sa6; #endif } sa_buf; socklen_t sa_size; /* argument sanity check(s) */ if (sa == NULL || laddr == NULL) return SA_RC(SA_ERR_ARG); /* at least sa_bind() has to be already performed */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* determine local address of underlying socket */ sa_size = (socklen_t)sizeof(sa_buf); if (getsockname(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size) < 0) return SA_RC(SA_ERR_SYS); /* create result address object */ if ((rv = sa_addr_create(laddr)) != SA_OK) return SA_RC(rv); if ((rv = sa_addr_s2a(*laddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) { (void)sa_addr_destroy(*laddr); return SA_RC(rv); } return SA_OK; } /* peek at underlying socket */ sa_rc_t sa_getfd(sa_t *sa, int *fd) { /* argument sanity check(s) */ if (sa == NULL || fd == NULL) return SA_RC(SA_ERR_ARG); /* if still no socket exists, say this explicitly */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* pass socket to caller */ *fd = sa->fdSocket; return SA_OK; } /* internal raw read operation */ static int sa_read_raw(sa_t *sa, char *cpBuf, int nBufLen) { int rv; #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)) fd_set fds; struct timeval tv; #endif /* if timeout is enabled, perform explicit/smart blocking instead of implicitly/hard blocking in the read(2) system call */ #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)) if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) { FD_ZERO(&fds); FD_SET(sa->fdSocket, &fds); memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_READ], sizeof(struct timeval)); do { rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &tv); } while (rv == -1 && errno == EINTR); if (rv == 0) { errno = ETIMEDOUT; return -1; } } #endif /* perform read operation on underlying socket */ do { rv = (int)SA_SC_CALL_3(sa, read, sa->fdSocket, cpBuf, (size_t)nBufLen); } while (rv == -1 && errno == EINTR); #if defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO) if (rv == -1 && errno == EWOULDBLOCK) errno = ETIMEDOUT; #endif return rv; } /* read data from socket */ sa_rc_t sa_read(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes) { int n; sa_rc_t rv; int res; /* argument sanity check(s) */ if (sa == NULL || cpBuf == NULL || nBufReq == 0) return SA_RC(SA_ERR_ARG); /* reading is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least a connection has to exist */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* perform read operation */ rv = SA_OK; if (sa->nReadSize == 0) { /* user-space unbuffered I/O */ if (sa->nWriteLen > 0) (void)sa_flush(sa); res = sa_read_raw(sa, cpBuf, (int)nBufReq); if (res == 0) rv = SA_ERR_EOF; else if (res < 0 && errno == ETIMEDOUT) rv = SA_ERR_TMT; else if (res < 0) rv = SA_ERR_SYS; } else { /* user-space buffered I/O */ res = 0; for (;;) { if ((int)nBufReq <= sa->nReadLen) { /* buffer holds enough data, so just use this */ memmove(cpBuf, sa->cpReadBuf, nBufReq); memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq); sa->nReadLen -= nBufReq; res += nBufReq; } else { if (sa->nReadLen > 0) { /* fetch already existing buffer contents as a start */ memmove(cpBuf, sa->cpReadBuf, (size_t)sa->nReadLen); nBufReq -= sa->nReadLen; cpBuf += sa->nReadLen; res += sa->nReadLen; sa->nReadLen = 0; } if (sa->nWriteLen > 0) (void)sa_flush(sa); if ((int)nBufReq >= sa->nReadSize) { /* buffer is too small at all, so read directly */ n = sa_read_raw(sa, cpBuf, (int)nBufReq); if (n > 0) res += n; else if (n == 0) rv = (res == 0 ? SA_ERR_EOF : SA_OK); else if (n < 0 && errno == ETIMEDOUT) rv = (res == 0 ? SA_ERR_TMT : SA_OK); else if (n < 0) rv = (res == 0 ? SA_ERR_SYS : SA_OK); } else { /* fill buffer with new data */ n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize); if (n < 0 && errno == ETIMEDOUT) /* timeout on this read, but perhaps ok as a whole */ rv = (res == 0 ? SA_ERR_TMT : SA_OK); else if (n < 0) /* error on this read, but perhaps ok as a whole */ rv = (res == 0 ? SA_ERR_SYS : SA_OK); else if (n == 0) /* EOF on this read, but perhaps ok as a whole */ rv = (res == 0 ? SA_ERR_EOF : SA_OK); else { sa->nReadLen = n; continue; /* REPEAT OPERATION! */ } } } break; } } /* pass number of actually read bytes to caller */ if (nBufRes != NULL) *nBufRes = (size_t)res; return SA_RC(rv); } /* read data from socket until [CR]LF (convenience function) */ sa_rc_t sa_readln(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes) { char c; size_t n; size_t res; sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL || cpBuf == NULL || nBufReq == 0) return SA_RC(SA_ERR_ARG); /* reading is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least a connection has to exist */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* we just perform a plain sa_read() per character, because if buffers are enabled, this is not as stupid as it looks at the first hand and if buffers are disabled, there is no better solution anyway. */ rv = SA_OK; res = 0; while (res < (nBufReq-1)) { rv = sa_read(sa, &c, 1, &n); if (rv != SA_OK) break; if (n == 0) break; cpBuf[res++] = c; if (c == '\n') break; } cpBuf[res] = '\0'; /* pass number of actually read characters to caller */ if (nBufRes != NULL) *nBufRes = res; return SA_RC(rv); } /* internal raw write operation */ static int sa_write_raw(sa_t *sa, const char *cpBuf, int nBufLen) { int rv; #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)) fd_set fds; struct timeval tv; #endif /* if timeout is enabled, perform explicit/smart blocking instead of implicitly/hard blocking in the write(2) system call */ #if !(defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)) if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { FD_ZERO(&fds); FD_SET(sa->fdSocket, &fds); memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_WRITE], sizeof(struct timeval)); do { rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, (fd_set *)NULL, &fds, (fd_set *)NULL, &tv); } while (rv == -1 && errno == EINTR); if (rv == 0) { errno = ETIMEDOUT; return -1; } } #endif /* perform write operation on underlying socket */ do { rv = (int)SA_SC_CALL_3(sa, write, sa->fdSocket, cpBuf, (size_t)nBufLen); } while (rv == -1 && errno == EINTR); #if defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO) if (rv == -1 && errno == EWOULDBLOCK) errno = ETIMEDOUT; #endif return rv; } /* write data to socket */ sa_rc_t sa_write(sa_t *sa, const char *cpBuf, size_t nBufReq, size_t *nBufRes) { int n; int res; sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL || cpBuf == NULL || nBufReq == 0) return SA_RC(SA_ERR_ARG); /* writing is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least a connection has to exist */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); rv = SA_OK; if (sa->nWriteSize == 0) { /* user-space unbuffered I/O */ res = sa_write_raw(sa, cpBuf, (int)nBufReq); if (res < 0 && errno == ETIMEDOUT) rv = SA_ERR_TMT; else if (res < 0) rv = SA_ERR_SYS; } else { /* user-space buffered I/O */ if ((int)nBufReq > (sa->nWriteSize - sa->nWriteLen)) { /* not enough space in buffer, so flush buffer first */ (void)sa_flush(sa); } res = 0; if ((int)nBufReq >= sa->nWriteSize) { /* buffer too small at all, so write immediately */ while (nBufReq > 0) { n = sa_write_raw(sa, cpBuf, (int)nBufReq); if (n < 0 && errno == ETIMEDOUT) rv = (res == 0 ? SA_ERR_TMT : SA_OK); else if (n < 0) rv = (res == 0 ? SA_ERR_SYS : SA_OK); if (n <= 0) break; nBufReq -= n; cpBuf += n; res += n; } } else { /* (again) enough sprace in buffer, so store data */ memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq); sa->nWriteLen += nBufReq; res = (int)nBufReq; } } /* pass number of actually written bytes to caller */ if (nBufRes != NULL) *nBufRes = (size_t)res; return SA_RC(rv); } /* output callback function context for sa_writef() */ typedef struct { sa_t *sa; sa_rc_t rv; } sa_writef_cb_t; /* output callback function for sa_writef() */ static int sa_writef_cb(void *_ctx, const char *buffer, size_t bufsize) { size_t n; sa_writef_cb_t *ctx = (sa_writef_cb_t *)_ctx; if ((ctx->rv = sa_write(ctx->sa, buffer, bufsize, &n)) != SA_OK) return -1; return (int)n; } /* write formatted string to socket (convenience function) */ sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...) { va_list ap; int n; sa_writef_cb_t ctx; /* argument sanity check(s) */ if (sa == NULL || cpFmt == NULL) return SA_RC(SA_ERR_ARG); /* writing is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least a connection has to exist */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* format string into temporary buffer */ va_start(ap, cpFmt); ctx.sa = sa; ctx.rv = SA_OK; n = sa_mvxprintf(sa_writef_cb, &ctx, cpFmt, ap); if (n == -1 && ctx.rv == SA_OK) ctx.rv = SA_ERR_FMT; va_end(ap); return ctx.rv; } /* flush write/outgoing I/O buffer */ sa_rc_t sa_flush(sa_t *sa) { int n; sa_rc_t rv; /* argument sanity check(s) */ if (sa == NULL) return SA_RC(SA_ERR_ARG); /* flushing is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least a connection has to exist */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* try to flush buffer */ rv = SA_OK; if (sa->nWriteSize > 0) { while (sa->nWriteLen > 0) { n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen); if (n < 0 && errno == ETIMEDOUT) rv = SA_ERR_TMT; else if (n < 0) rv = SA_ERR_SYS; if (n <= 0) break; memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, (size_t)(sa->nWriteLen-n)); sa->nWriteLen -= n; } sa->nWriteLen = 0; } return SA_RC(rv); } /* shutdown a socket connection */ sa_rc_t sa_shutdown(sa_t *sa, const char *flags) { int how; /* argument sanity check(s) */ if (sa == NULL || flags == NULL) return SA_RC(SA_ERR_ARG); /* shutdown is only possible for stream communication */ if (sa->eType != SA_TYPE_STREAM) return SA_RC(SA_ERR_USE); /* at least a connection has to exist */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* determine flags for shutdown(2) */ how = 0; if (strcmp(flags, "r") == 0) how = SHUT_RD; else if (strcmp(flags, "w") == 0) how = SHUT_WR; else if (strcmp(flags, "rw") == 0 || strcmp(flags, "wr") == 0) how = SHUT_RDWR; else return SA_RC(SA_ERR_ARG); /* flush write buffers */ if ((how & SHUT_WR) || (how & SHUT_RDWR)) (void)sa_flush(sa); /* perform shutdown operation on underlying socket */ if (shutdown(sa->fdSocket, how) == -1) return SA_RC(SA_ERR_SYS); return SA_OK; } /* receive data via socket */ sa_rc_t sa_recv(sa_t *sa, sa_addr_t **raddr, char *buf, size_t buflen, size_t *bufdone) { sa_rc_t rv; union { struct sockaddr_in sa4; #ifdef AF_INET6 struct sockaddr_in6 sa6; #endif } sa_buf; socklen_t sa_size; ssize_t n; int k; fd_set fds; struct timeval tv; /* argument sanity check(s) */ if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) return SA_RC(SA_ERR_ARG); /* receiving is only possible for datagram communication */ if (sa->eType != SA_TYPE_DATAGRAM) return SA_RC(SA_ERR_USE); /* at least a sa_bind() has to be performed */ if (sa->fdSocket == -1) return SA_RC(SA_ERR_USE); /* if timeout is enabled, perform explicit/smart blocking instead of implicitly/hard blocking in the recvfrom(2) system call */ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) { FD_ZERO(&fds); FD_SET(sa->fdSocket, &fds); memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_READ], sizeof(struct timeval)); do { k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &tv); } while (k == -1 && errno == EINTR); if (k == 0) errno = ETIMEDOUT; if (k <= 0) return SA_RC(SA_ERR_SYS); } /* perform receive operation on underlying socket */ sa_size = (socklen_t)sizeof(sa_buf); if ((n = SA_SC_CALL_6(sa, recvfrom, sa->fdSocket, buf, buflen, 0, (struct sockaddr *)&sa_buf, &sa_size)) == -1) return SA_RC(SA_ERR_SYS); /* create result address object */ if ((rv = sa_addr_create(raddr)) != SA_OK) return rv; if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_size)) != SA_OK) { (void)sa_addr_destroy(*raddr); return rv; } /* pass actual number of received bytes to caller */ if (bufdone != NULL) *bufdone = (size_t)n; return SA_OK; } /* send data via socket */ sa_rc_t sa_send(sa_t *sa, sa_addr_t *raddr, const char *buf, size_t buflen, size_t *bufdone) { ssize_t n; int k; fd_set fds; sa_rc_t rv; struct timeval tv; /* argument sanity check(s) */ if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) return SA_RC(SA_ERR_ARG); /* sending is only possible for datagram communication */ if (sa->eType != SA_TYPE_DATAGRAM) return SA_RC(SA_ERR_USE); /* lazy creation of underlying socket */ if (sa->fdSocket == -1) if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK) return rv; /* if timeout is enabled, perform explicit/smart blocking instead of implicitly/hard blocking in the sendto(2) system call */ if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { FD_ZERO(&fds); FD_SET(sa->fdSocket, &fds); memcpy(&tv, &sa->tvTimeout[SA_TIMEOUT_WRITE], sizeof(struct timeval)); do { k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, (fd_set *)NULL, &fds, (fd_set *)NULL, &tv); } while (k == -1 && errno == EINTR); if (k == 0) errno = ETIMEDOUT; if (k <= 0) return SA_RC(SA_ERR_SYS); } /* perform send operation on underlying socket */ if ((n = SA_SC_CALL_6(sa, sendto, sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1) return SA_RC(SA_ERR_SYS); /* pass actual number of sent bytes to caller */ if (bufdone != NULL) *bufdone = (size_t)n; return SA_OK; } /* send formatted string to socket (convenience function) */ sa_rc_t sa_sendf(sa_t *sa, sa_addr_t *raddr, const char *cpFmt, ...) { va_list ap; va_list apbak; int nBuf; char *cpBuf; sa_rc_t rv; char caBuf[1024]; /* argument sanity check(s) */ if (sa == NULL || raddr == NULL || cpFmt == NULL) return SA_RC(SA_ERR_ARG); /* format string into temporary buffer */ va_start(ap, cpFmt); va_copy(apbak, ap); if ((nBuf = sa_mvsnprintf(NULL, 0, cpFmt, ap)) == -1) return SA_RC(SA_ERR_FMT); va_copy(ap, apbak); if ((nBuf+1) > (int)sizeof(caBuf)) { /* requires a larger buffer, so allocate dynamically */ if ((cpBuf = (char *)malloc((size_t)(nBuf+1))) == NULL) return SA_RC(SA_ERR_MEM); } else { /* fits into small buffer, so allocate statically */ cpBuf = caBuf; } rv = SA_OK; if (sa_mvsnprintf(cpBuf, (size_t)(nBuf+1), cpFmt, ap) == -1) rv = SA_ERR_FMT; va_end(ap); /* pass-through to sa_send() */ if (rv == SA_OK) rv = sa_send(sa, raddr, cpBuf, (size_t)nBuf, NULL); /* cleanup dynamically allocated buffer */ if ((nBuf+1) > (int)sizeof(caBuf)) free(cpBuf); return rv; } /* return error string */ char *sa_error(sa_rc_t rv) { char *sz; /* translate result value into corresponding string */ if (rv == SA_OK) sz = "Everything Ok"; else if (rv == SA_ERR_ARG) sz = "Invalid Argument"; else if (rv == SA_ERR_USE) sz = "Invalid Use Or Context"; else if (rv == SA_ERR_MEM) sz = "Not Enough Memory"; else if (rv == SA_ERR_MTC) sz = "Matching Failed"; else if (rv == SA_ERR_EOF) sz = "End Of Communication"; else if (rv == SA_ERR_TMT) sz = "Communication Timeout"; else if (rv == SA_ERR_SYS) sz = "Operating System Error"; else if (rv == SA_ERR_NET) sz = "Networking Error"; else if (rv == SA_ERR_FMT) sz = "Formatting Error"; else if (rv == SA_ERR_IMP) sz = "Implementation Not Available"; else if (rv == SA_ERR_INT) sz = "Internal Error"; else sz = "Invalid Result Code"; return sz; } @ 1.91 log @Removed SA_SYSCALL_GETHOSTBYNAME because gethostbyname(3) cannot be overridden as at is use point (function sa_addr_u2a) the sa_t object is not available. Additionally, for IPv6 getaddrinfo(3) would have been overridden, too. This fixed compilation on platforms without IPv6 APIs. @ text @d3 2 a4 2 ** Copyright (c) 2001-2005 Ralf S. Engelschall ** Copyright (c) 2001-2005 The OSSP Project @ 1.90 log @Follow FlexeLint suggestions and "constify" some API function parameters. @ text @a219 1 SA_SC_DECLARE_1(struct hostent *, gethostbyname, const char *name) d657 1 a657 1 else if ((he = SA_SC_CALL_1(sa, gethostbyname, cpHost)) != NULL) { a1186 1 SA_SC_ASSIGN(sa, gethostbyname, gethostbyname, NULL); a1415 1 case SA_SYSCALL_GETHOSTBYNAME: SA_SC_ASSIGN(sa, gethostbyname, fptr, fctx); break; a1658 1 SA_SC_COPY((*csa), sa, gethostbyname); @ 1.89 log @Fix timeout handling in various functions by fixing the internal select(2) usage: the timeout structure is (standard compliantly) modified in place on some platforms (like Linux), so its values have to be preserved before calls to select(2)). Problem found by: Robert Wood @ text @d270 2 a276 1 #endif d279 1 d737 1 a737 1 sa_rc_t sa_addr_a2u(sa_addr_t *saa, char **uri) d798 1 a798 1 sa_rc_t sa_addr_a2s(sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen) d813 1 a813 1 sa_rc_t sa_addr_match(sa_addr_t *saa1, sa_addr_t *saa2, int prefixlen) d939 1 a939 1 static sa_rc_t sa_socket_settimeouts(sa_t *sa) d1227 1 a1227 1 sa_socket_kill(sa); d1426 1 a1426 1 sa_rc_t sa_bind(sa_t *sa, sa_addr_t *laddr) d1454 1 a1454 1 sa_rc_t sa_connect(sa_t *sa, sa_addr_t *raddr) d2136 1 a2136 1 sa_rc_t sa_shutdown(sa_t *sa, char *flags) @ 1.88 log @welcome 2005 in OSSP sa, too @ text @d1459 1 d1516 4 a1519 2 if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) tv = &sa->tvTimeout[SA_TIMEOUT_CONNECT]; d1597 1 d1617 1 d1619 1 a1619 2 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &sa->tvTimeout[SA_TIMEOUT_ACCEPT]); d1770 1 d1779 1 d1781 1 a1781 2 rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &sa->tvTimeout[SA_TIMEOUT_READ]); d1950 1 d1959 1 d1961 1 a1961 2 rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, (fd_set *)NULL, &fds, (fd_set *)NULL, &sa->tvTimeout[SA_TIMEOUT_WRITE]); d2186 1 d2205 1 d2207 1 a2207 2 k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, (fd_set *)NULL, (fd_set *)NULL, &sa->tvTimeout[SA_TIMEOUT_READ]); d2243 1 d2263 1 d2265 1 a2265 2 k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, (fd_set *)NULL, &fds, (fd_set *)NULL, &sa->tvTimeout[SA_TIMEOUT_WRITE]); @ 1.87 log @Do not pass NULL directly to variadic functions because NULL is not guarrantied to be a pointer. @ text @d3 3 a5 3 ** Copyright (c) 2001-2004 Ralf S. Engelschall ** Copyright (c) 2001-2004 The OSSP Project ** Copyright (c) 2001-2004 Cable & Wireless @ 1.86 log @Added SA_SYSCALL_GETHOSTBYNAME for supporting the overloading of gethostbyname(2) system calls. @ text @d1520 1 a1520 1 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &rset, &wset, NULL, tv); d1614 1 a1614 1 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, d1775 1 a1775 1 rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, d1954 1 a1954 1 rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, d2199 1 a2199 1 k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, d2256 1 a2256 1 k = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, @ 1.85 log @o Provide Autoconf check (AC_CHECK_VA_COPY) for va_copy(d,s) macro and fallback implementations and now that we can be sure that va_copy() exists for us, use it in var_formatv() and ts.c instead of the direct assignments (which are not sufficiently portable). o Remove "#undef socklen_t" from sa.h because the socklen_t fallback is a "typedef" since a longer time. @ text @d213 8 a220 7 SA_SC_DECLARE_3(int, connect, int, const struct sockaddr *, socklen_t) SA_SC_DECLARE_3(int, accept, int, struct sockaddr *, socklen_t *) SA_SC_DECLARE_5(int, select, int, fd_set *, fd_set *, fd_set *, struct timeval *) SA_SC_DECLARE_3(ssize_t, read, int, void *, size_t) SA_SC_DECLARE_3(ssize_t, write, int, const void *, size_t) SA_SC_DECLARE_6(ssize_t, recvfrom, int, void *, size_t, int, struct sockaddr *, socklen_t *) SA_SC_DECLARE_6(ssize_t, sendto, int, const void *, size_t, int, const struct sockaddr *, socklen_t) d501 2 a502 2 char *cpHost; char *cpPort; d563 1 a563 1 cpHost = (char *)(uri+7); d656 1 a656 1 else if ((he = gethostbyname(cpHost)) != NULL) { d1179 8 a1186 7 SA_SC_ASSIGN(sa, connect, connect, NULL); SA_SC_ASSIGN(sa, accept, accept, NULL); SA_SC_ASSIGN(sa, select, select, NULL); SA_SC_ASSIGN(sa, read, read, NULL); SA_SC_ASSIGN(sa, write, write, NULL); SA_SC_ASSIGN(sa, recvfrom, recvfrom, NULL); SA_SC_ASSIGN(sa, sendto, sendto, NULL); d1409 8 a1416 7 case SA_SYSCALL_CONNECT: SA_SC_ASSIGN(sa, connect, fptr, fctx); break; case SA_SYSCALL_ACCEPT: SA_SC_ASSIGN(sa, accept, fptr, fctx); break; case SA_SYSCALL_SELECT: SA_SC_ASSIGN(sa, select, fptr, fctx); break; case SA_SYSCALL_READ: SA_SC_ASSIGN(sa, read, fptr, fctx); break; case SA_SYSCALL_WRITE: SA_SC_ASSIGN(sa, write, fptr, fctx); break; case SA_SYSCALL_RECVFROM: SA_SC_ASSIGN(sa, recvfrom, fptr, fctx); break; case SA_SYSCALL_SENDTO: SA_SC_ASSIGN(sa, sendto, fptr, fctx); break; d1656 1 @ 1.84 log @Allow negative values for SA_OPTION_LINGER to cover the special case of l_onoff=1/l_linger=0 where a TCP RST based connection termination is performed on close(2). Submitted by: Alexandre Balaban @ text @d2276 1 d2288 1 d2291 1 a2291 1 va_end(ap); a2300 1 va_start(ap, cpFmt); @ 1.83 log @Fix timeout implementation related to SO_RCVTIMEO/SO_SNDTIMEO. @ text @d992 1 a992 1 linger.l_linger = sa->optInfo[i].value; d1342 1 a1342 1 int amount = ((int)va_arg(ap, int) ? 1 : 0); @ 1.82 log @remember the reason directly in the source, too @ text @d943 1 a943 1 if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) { d949 1 a949 1 if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { @ 1.81 log @In sa_connect() also select(2) on the socket if EINTR was returned similar to what is done already for EINPROGRESS, because one is not allowed to re-start connect(2) according to STEVENS (Unix Network Programming, volume 1, section 5.9) but instead have to perform the same post-processing under EINTR (under both timeout and non-timeout situations) what we already did for EINPROGRESS (in timeout-aware situation). @ text @d1485 13 @ 1.80 log @Adjusted all copyright messages for new year 2004. @ text @d1455 1 d1470 1 d1472 1 a1472 8 if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_CONNECT])) { /* standard/non-timeout-aware connect operation */ if (SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0) rv = SA_ERR_SYS; } else { /* emulated/timeout-aware connect operation */ error = 0; d1474 4 a1477 1 /* temporarily switch underlying socket to non-blocking mode */ d1480 1 d1482 3 a1484 24 /* perform the connect operation */ if ((n = SA_SC_CALL_3(sa, connect, sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) { if (errno != EINPROGRESS) { error = errno; goto done; } } /* ok if connect completed immediately */ if (n == 0) goto done; /* wait for read or write possibility */ FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(sa->fdSocket, &rset); FD_SET(sa->fdSocket, &wset); do { n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout[SA_TIMEOUT_CONNECT]); } while (n == -1 && errno == EINTR); /* decide on return semantic */ if (n < 0) { d1488 1 a1488 6 else if (n == 0) { (void)close(sa->fdSocket); /* stop TCP three-way handshake */ sa->fdSocket = -1; rv = SA_ERR_TMT; goto done; } d1490 28 a1517 6 /* fetch pending error */ len = (socklen_t)sizeof(error); if (getsockopt(sa->fdSocket, SOL_SOCKET, SO_ERROR, (void *)&error, &len) < 0) error = errno; done: d1519 10 a1528 1 /* reset socket flags */ d1531 6 a1536 7 /* optionally set errno */ if (error != 0) { (void)close(sa->fdSocket); /* just in case */ sa->fdSocket = -1; errno = error; rv = SA_ERR_SYS; } d1538 1 @ 1.79 log @restart accept(2) after interrupted system call @ text @d3 3 a5 3 ** Copyright (c) 2001-2003 Ralf S. Engelschall ** Copyright (c) 2001-2003 The OSSP Project ** Copyright (c) 2001-2003 Cable & Wireless Deutschland @ 1.78 log @add more compiler warning options and fix prototype declarations found by -Wstrict-prototypes @ text @d1605 4 a1608 1 if ((s = SA_SC_CALL_3(sa, accept, sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_size)) == -1) @ 1.77 log @Add autoconf check to detect unimplemented setsockopt features. Reported by Amos Gouaux. @ text @d119 1 a119 1 union { void (*any)(); \ d126 1 a126 1 union { void (*any)(); \ d133 1 a133 1 union { void (*any)(); \ d140 1 a140 1 union { void (*any)(); \ d147 1 a147 1 union { void (*any)(); \ d154 1 a154 1 union { void (*any)(); \ d161 1 a161 1 union { void (*any)(); \ d170 1 a170 1 (sa)->scSysCall.sc_##fn.fptr.any = (void (*)())(ptr); \ d185 1 a185 1 : ((sa)->scSysCall.sc_##fn.fptr.std)() ) d1396 1 a1396 1 sa_rc_t sa_syscall(sa_t *sa, sa_syscall_t id, void (*fptr)(), void *fctx) @ 1.76 log @correct spelling: privileges, convenient; @ text @d942 1 a942 1 #if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO) d1740 1 a1740 1 #if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)) d1746 1 a1746 1 #if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)) d1766 1 a1766 1 #if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO) d1919 1 a1919 1 #if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)) d1925 1 a1925 1 #if !(defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)) d1945 1 a1945 1 #if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO) @ 1.75 log @be consistent in quoting ;-) @ text @d1870 1 a1870 1 /* read data from socket until [CR]LF (convinience function) */ d2035 1 a2035 1 /* write formatted string to socket (convinience function) */ d2252 1 a2252 1 /* send formatted string to socket (convinience function) */ @ 1.74 log @Add internal address resolving support via the new IEEE Std 1003.1g-2000 ("POSIX.1") getaddrinfo(3) API. This especially provides more portable IPv6 address resolving. @ text @d42 1 a42 1 #include /* for isXXX() */ @ 1.73 log @- Adjusted all copyright messages for new year 2003. - Fixed generated owner in distribution tarball. - Prepare for 1.0.3 release @ text @d255 1 d282 1 d486 5 a494 1 int bIPv6; d496 1 d498 1 d609 22 d681 1 d694 5 @ 1.72 log @fix a few bugs, remove a lot of warnings; all found by splint @ text @d3 3 a5 3 ** Copyright (c) 2001-2002 Ralf S. Engelschall ** Copyright (c) 2001-2002 The OSSP Project ** Copyright (c) 2001-2002 Cable & Wireless Deutschland @ 1.71 log @be pedantic about unused variables @ text @d332 1 a332 1 n = sizeof(char); d338 1 a338 1 n = sizeof(char); d344 1 a344 1 n = strlen(cp); d355 1 a355 1 n = strlen(cp); d369 1 a369 1 n = format - cp; d373 1 a373 1 if ((n = output(ctx, cp, n)) == -1) d396 1 a396 1 return bufsize; d500 2 a501 1 int n; d509 1 a509 1 sa_mvsnprintf(uribuf, sizeof(uribuf), uri, ap); d511 2 d549 1 a549 1 sl = sizeof(un); d613 1 a613 1 sl = sizeof(sa4); d621 1 a621 1 sl = sizeof(sa6); d632 1 a632 1 sl = sizeof(sa4); d642 1 a642 1 sl = sizeof(sa6); d658 1 a658 1 if ((saa->saBuf = (struct sockaddr *)malloc(sl)) == NULL) d660 1 a660 1 memcpy(saa->saBuf, sa, sl); d662 1 a662 1 saa->nFamily = sf; d686 1 a686 1 if ((saa->saBuf = (struct sockaddr *)malloc(salen)) == NULL) d688 1 a688 1 memcpy(saa->saBuf, sabuf, salen); d692 1 a692 1 saa->nFamily = sabuf->sa_family; d718 1 a718 1 || saa->slBuf < sizeof(struct sockaddr_un)) d723 7 a729 3 sa_msnprintf(uribuf, sizeof(uribuf), "unix:/NOT-BOUND"); else sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path); d733 2 a734 1 sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)); d736 2 a737 1 sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort); d742 2 a743 1 sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)); d745 2 a746 1 sa_msnprintf(uribuf, sizeof(uribuf), "inet://[%s]:%d", caHost, nPort); d766 1 a766 1 if ((*sabuf = (struct sockaddr *)malloc(saa->slBuf)) == NULL) d768 1 a768 1 memmove(*sabuf, saa->saBuf, saa->slBuf); d807 1 a807 1 nBits = l1; d810 1 a810 1 if (l1 < prefixlen || l2 < prefixlen) d837 1 a837 1 if (ucp0[i] != 0x00) d839 1 a839 1 if (!(ucp0[10] == 0xFF && ucp0[11] == 0xFF)) d880 1 a880 1 if (memcmp(ucp1, ucp2, nBytes) != 0) d884 3 a886 3 uc1 = ucp1[nBytes]; uc2 = ucp2[nBytes]; mask = (0xFF << (8-nBits)) & 0xFF; d935 1 a935 1 for (i = 0; i < (sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) { d1099 1 a1099 1 close(sa->fdSocket); d1130 1 a1130 1 for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) { d1136 1 a1136 1 for (i = 0; i < (sizeof(sa->optInfo)/sizeof(sa->optInfo[0])); i++) { d1164 1 a1164 1 sa_socket_kill(sa); d1206 1 a1206 1 for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) { d1234 1 a1234 1 if (sa->nReadLen > size) d1244 1 a1244 1 sa->nReadSize = size; d1255 1 a1255 1 if (sa->nWriteLen > size) d1265 1 a1265 1 sa->nWriteSize = size; d1402 1 a1402 1 unlink(un->sun_path); d1445 1 a1445 1 fcntl(sa->fdSocket, F_SETFL, flags|O_NONBLOCK); d1475 1 a1475 1 close(sa->fdSocket); /* stop TCP three-way handshake */ d1482 1 a1482 1 len = sizeof(error); d1489 1 a1489 1 fcntl(sa->fdSocket, F_SETFL, flags); d1493 1 a1493 1 close(sa->fdSocket); /* just in case */ d1568 1 a1568 1 sa_size = sizeof(sa_buf); d1576 1 a1576 1 sa_addr_destroy(*caddr); d1582 1 a1582 1 sa_addr_destroy(*caddr); d1599 1 a1599 1 for (i = 0; i < (sizeof(sa->tvTimeout)/sizeof(sa->tvTimeout[0])); i++) { d1632 1 a1632 1 sa_size = sizeof(sa_buf); d1640 1 a1640 1 sa_addr_destroy(*raddr); d1668 1 a1668 1 sa_size = sizeof(sa_buf); d1676 1 a1676 1 sa_addr_destroy(*laddr); d1727 1 a1727 1 rv = SA_SC_CALL_3(sa, read, sa->fdSocket, cpBuf, nBufLen); d1762 2 a1763 2 sa_flush(sa); res = sa_read_raw(sa, cpBuf, nBufReq); d1775 1 a1775 1 if (nBufReq <= sa->nReadLen) { d1785 1 a1785 1 memmove(cpBuf, sa->cpReadBuf, sa->nReadLen); d1792 2 a1793 2 sa_flush(sa); if (nBufReq >= sa->nReadSize) { d1795 1 a1795 1 n = sa_read_raw(sa, cpBuf, nBufReq); d1864 1 a1864 1 if (n <= 0) d1906 1 a1906 1 rv = SA_SC_CALL_3(sa, write, sa->fdSocket, cpBuf, nBufLen); d1939 1 a1939 1 res = sa_write_raw(sa, cpBuf, nBufReq); d1947 1 a1947 1 if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) { d1949 1 a1949 1 sa_flush(sa); d1952 1 a1952 1 if (nBufReq >= sa->nWriteSize) { d1955 1 a1955 1 n = sa_write_raw(sa, cpBuf, nBufReq); d1971 1 a1971 1 res = nBufReq; d2003 1 a2003 1 size_t n; d2023 2 d2059 1 a2059 1 memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, sa->nWriteLen-n); d2097 1 a2097 1 sa_flush(sa); d2117 2 a2118 1 size_t n; d2139 1 a2139 1 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, d2141 2 a2142 2 } while (n == -1 && errno == EINTR); if (n == 0) d2144 1 a2144 1 if (n <= 0) d2149 1 a2149 1 sa_size = sizeof(sa_buf); d2158 1 a2158 1 sa_addr_destroy(*raddr); d2164 1 a2164 1 *bufdone = n; d2172 2 a2173 1 size_t n; d2196 1 a2196 1 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, d2198 2 a2199 2 } while (n == -1 && errno == EINTR); if (n == 0) d2201 1 a2201 1 if (n <= 0) d2211 1 a2211 1 *bufdone = n; d2220 1 a2220 1 size_t nBuf; d2231 2 a2232 1 nBuf = sa_mvsnprintf(NULL, 0, cpFmt, ap); d2234 1 a2234 1 if ((nBuf+1) > sizeof(caBuf)) { d2236 1 a2236 1 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL) d2244 3 a2246 1 sa_mvsnprintf(cpBuf, nBuf+1, cpFmt, ap); d2250 2 a2251 1 rv = sa_send(sa, raddr, cpBuf, nBuf, NULL); d2254 1 a2254 1 if ((nBuf+1) > sizeof(caBuf)) d2274 2 @ 1.70 log @Make sure that "va_list" variables are not compared against NULL because this is not possible on all platforms. @ text @d288 1 d291 1 @ 1.69 log @Ported to brain-dead OpenUNIX where shutdown(3)'s SHUT_XX values are not easily available and where variables named sa_len conflict with structure fields in vendor headers. @ text @d321 1 a321 1 if (format == NULL || ap == NULL) d403 1 a403 1 if (format == NULL || ap == NULL) @ 1.68 log @Fixed two compile-time warnings. Submitted by: Alvaro Lopez Ortega @ text @d50 1 a50 1 #include /* for "PF_XXX", "AF_XXX" and "SOCK_XXX" */ d93 13 d1524 1 a1524 1 socklen_t sa_len; d1555 2 a1556 2 sa_len = sizeof(sa_buf); if ((s = SA_SC_CALL_3(sa, accept, sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1) d1562 1 a1562 1 if ((rv = sa_addr_s2a(*caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { d1604 1 a1604 1 socklen_t sa_len; d1619 2 a1620 2 sa_len = sizeof(sa_buf); if (getpeername(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) d1626 1 a1626 1 if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { d1644 1 a1644 1 socklen_t sa_len; d1655 2 a1656 2 sa_len = sizeof(sa_buf); if (getsockname(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) d1662 1 a1662 1 if ((rv = sa_addr_s2a(*laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { d2101 1 a2101 1 socklen_t sa_len; d2133 1 a2133 1 sa_len = sizeof(sa_buf); d2135 1 a2135 1 (struct sockaddr *)&sa_buf, &sa_len)) == -1) d2141 1 a2141 1 if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { @ 1.67 log @Consistently return SA_ERR_MEM instead of SA_ERR_SYS if memory allocations failed. @ text @d1969 2 a1970 2 n = -1; return n; d2201 1 a2201 1 nBuf = sa_mvsnprintf(NULL, NULL, cpFmt, ap); @ 1.66 log @API Cleanups: use politically correct use "extern" keyword in sa.h; use leading underscores for variables names in prototypes; move "raddr" argument to second position in sa_{send,recv,sendf}. @ text @d1216 1 a1216 1 return SA_RC(SA_ERR_SYS); d1237 1 a1237 1 return SA_RC(SA_ERR_SYS); d2206 1 a2206 1 return SA_RC(SA_ERR_SYS); @ 1.65 log @Added a function sa_sendf(3) which is a convience wrapper to sa_send(3) for sending formatted data. This is similar to what sa_writef(3) does for sa_write(3). The difference is just that sa_writef(3) does not need a temporary buffer (because can use the stream I/O write buffer) while sa_sendf(3) requires a temporary buffer for its operation. Nevertheless the temporary buffer is allocated only if the formatted data is large. For small formatted data a fast stack-based buffer is used for higher performance. @ text @d2079 1 a2079 1 sa_rc_t sa_recv(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr) d2141 1 a2141 1 sa_rc_t sa_send(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr) d2217 1 a2217 1 rv = sa_send(sa, cpBuf, nBuf, NULL, raddr); @ 1.64 log @flush write buffer on write-side shutdown @ text @d2186 40 @ 1.63 log @Rewrote the implementation of sa_option(3) in order to make options configurable _before_ the underlying socket is implicitly allocated. This fixes especially sa_option(sa, SA_OPTION_REUSEADDR, 1) before sa_bind(). @ text @d2067 4 @ 1.62 log @strip trailing whitespaces @ text @d209 6 d227 1 d876 4 d881 11 a891 13 if (sa->fdSocket != -1) { if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_READ])) { if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_RCVTIMEO, (const void *)(&sa->tvTimeout[SA_TIMEOUT_READ]), (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_READ]))) < 0) return SA_RC(SA_ERR_SYS); } if (SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_WRITE])) { if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_SNDTIMEO, (const void *)(&sa->tvTimeout[SA_TIMEOUT_WRITE]), (socklen_t)(sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE]))) < 0) return SA_RC(SA_ERR_SYS); } d897 93 d998 1 d1051 6 a1056 1 sa_socket_settimeouts(sa); d1109 6 d1173 1 d1190 3 a1192 2 /* optionally set kernel timeouts */ sa_socket_settimeouts(sa); a1268 1 /* enable/disable Nagle's Algorithm (see RFC898) */ d1271 2 a1272 7 if (sa->fdSocket == -1) { rv = SA_ERR_USE; break; } if (setsockopt(sa->fdSocket, IPPROTO_TCP, TCP_NODELAY, (const void *)&mode, (socklen_t)sizeof(mode)) < 0) rv = SA_ERR_SYS; d1280 3 a1282 8 struct linger *linger = (struct linger *)va_arg(ap, void *); if (sa->fdSocket == -1) { rv = SA_ERR_USE; break; } if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_LINGER, (const void *)linger, (socklen_t)sizeof(struct linger)) < 0) rv = SA_ERR_SYS; d1289 10 a1298 1 #ifdef SA_OPTION_REUSEPORT a1299 1 #endif d1301 1 a1301 1 /* enable/disable reusability of binding to address or port */ d1303 4 a1306 9 int flag; if (sa->fdSocket == -1) { rv = SA_ERR_USE; break; } switch (id) { case SA_OPTION_REUSEADDR: flag = SO_REUSEADDR; break; #ifdef SA_OPTION_REUSEPORT case SA_OPTION_REUSEPORT: flag = SO_REUSEPORT; break; a1307 5 default: flag = 0; break; } if (setsockopt(sa->fdSocket, SOL_SOCKET, flag, (const void *)&mode, (socklen_t)sizeof(mode)) < 0) rv = SA_ERR_SYS; a1310 2 /* enable/disable non-blocking I/O mode */ int flags; d1312 2 a1313 14 if (sa->fdSocket == -1) { rv = SA_ERR_USE; break; } if ((flags = fcntl(sa->fdSocket, F_GETFL, 0)) < 0) { rv = SA_ERR_SYS; break; } if (mode == 0) flags &= ~(O_NONBLOCK); else flags |= O_NONBLOCK; if (fcntl(sa->fdSocket, F_SETFL, flags) < 0) rv = SA_ERR_SYS; d1322 9 a1330 1 return SA_RC(rv); @ 1.61 log @be smart enough also for people who cannot read @ text @d208 1 a208 1 d275 1 a275 1 if ((cp = inet_ntoa(*((struct in_addr *)src))) == NULL) d294 1 a294 1 char ibuf[((sizeof(int)*8)/3)+10]; d387 1 a387 1 if (buffer == NULL) d494 1 a494 1 /* parse URI and resolve contents. d614 1 a614 1 memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], d696 1 a696 1 than the expected one. Then there is actually no path at all. d785 1 a785 1 /* special case of comparing a regular IPv4 address (1.2.3.4) with an d842 1 a842 1 /* perform address representation comparison d914 1 a914 1 if (nFamily == AF_LOCAL) d1160 1 a1160 1 if (setsockopt(sa->fdSocket, IPPROTO_TCP, TCP_NODELAY, d1175 1 a1175 1 if (setsockopt(sa->fdSocket, SOL_SOCKET, SO_LINGER, d1185 1 a1185 1 case SA_OPTION_REUSEPORT: d1202 1 a1202 1 if (setsockopt(sa->fdSocket, SOL_SOCKET, flag, d1255 1 a1255 1 default: rv = SA_ERR_ARG; d1301 1 a1301 1 /* connecting is only possible for stream communication */ d1334 1 a1334 1 goto done; d1386 1 a1386 1 /* listening is only possible for stream communication */ d1422 1 a1422 1 /* accepting connections is only possible for stream communication */ d1438 1 a1438 1 if (n == 0) d1500 1 a1500 1 /* peers exist only for stream communication */ d1585 1 a1585 1 /* if timeout is enabled, perform explicit/smart blocking instead d1592 1 a1592 1 rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, d1626 1 a1626 1 /* reading is only possible for stream communication */ d1704 1 a1704 1 /* pass number of actually read bytes to caller */ d1723 1 a1723 1 /* reading is only possible for stream communication */ d1764 1 a1764 1 /* if timeout is enabled, perform explicit/smart blocking instead d1771 1 a1771 1 rv = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, d1805 1 a1805 1 /* writing is only possible for stream communication */ d1887 1 a1887 1 /* writing is only possible for stream communication */ d1915 1 a1915 1 /* flushing is only possible for stream communication */ d1951 1 a1951 1 /* shutdown is only possible for stream communication */ d1995 1 a1995 1 /* receiving is only possible for datagram communication */ d2003 1 a2003 1 /* if timeout is enabled, perform explicit/smart blocking instead d2009 1 a2009 1 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, &fds, NULL, NULL, d2012 1 a2012 1 if (n == 0) d2020 1 a2020 1 if ((n = SA_SC_CALL_6(sa, recvfrom, sa->fdSocket, buf, buflen, 0, d2050 1 a2050 1 /* sending is only possible for datagram communication */ d2059 1 a2059 1 /* if timeout is enabled, perform explicit/smart blocking instead d2065 1 a2065 1 n = SA_SC_CALL_5(sa, select, sa->fdSocket+1, NULL, &fds, NULL, d2068 1 a2068 1 if (n == 0) @ 1.60 log @Fix error processing logic in sa_read(). Submitted by: Ulrich Dessauer @ text @d1965 1 a1965 1 else if (strcmp(flags, "rw") == 0) @ 1.59 log @- fix inet_ntoa(3) usage - fallback to usage of inet_addr(3) if inet_aton(3) does not exists - add casts for arguments to [gs]etsockopt(3) to shut down warnings (This especially gets OSSP sa running under dead Solaris 2.6...) @ text @d1691 1 a1691 1 if (n == 0) @ 1.58 log @polishing for release @ text @d243 1 d248 6 a267 1 struct in_addr in_val; d272 1 d275 1 a275 1 if ((cp = inet_ntoa(src)) == NULL) d283 1 d825 1 a826 1 } d873 2 a874 2 &sa->tvTimeout[SA_TIMEOUT_READ], sizeof(sa->tvTimeout[SA_TIMEOUT_READ])) < 0) d879 2 a880 2 &sa->tvTimeout[SA_TIMEOUT_WRITE], sizeof(sa->tvTimeout[SA_TIMEOUT_WRITE])) < 0) d1161 1 a1161 1 (void *)&mode, sizeof(mode)) < 0) d1176 1 a1176 1 linger, sizeof(struct linger)) < 0) d1203 1 a1203 1 (void *)&mode, sizeof(mode)) < 0) d1360 1 a1360 1 if (getsockopt(sa->fdSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0) @ 1.57 log @Hell, why did the compiler not complain? The ctx stuff is only allowed to be used if real output is written. Hmmmm... @ text @d2 1 a2 1 ** SA - OSSP Socket Abstraction Library d7 2 a8 2 ** This file is part of OSSP SA, a socket abstraction library which ** can be found at http://www.ossp.org/pkg/sa/. @ 1.56 log @Oops, the NUL-terminating check is done later, so do not destroy it by already reducing the size in advance. Else an exactly sized buffer (including space for the NUL-termination) would result in an unexpected error. @ text @d387 4 a391 4 if (n != -1 && ctx.buflen == 0) n = -1; if (n != -1) *(ctx.bufptr) = '\0'; @ 1.55 log @add optional OSSP ex based exception handling support @ text @d385 1 a385 1 ctx.buflen = bufsize - 1; @ 1.54 log @cleanups triggered by Intel ICC @ text @d56 13 d416 1 a416 1 return SA_ERR_ARG; d420 1 a420 1 return SA_ERR_MEM; d436 1 a436 1 return SA_ERR_ARG; d474 1 a474 1 return SA_ERR_ARG; d501 1 a501 1 return SA_ERR_ARG; d503 1 a503 1 return SA_ERR_MEM; d506 1 a506 1 return SA_ERR_MEM; d526 1 a526 1 return SA_ERR_IMP; d531 1 a531 1 return SA_ERR_ARG; d534 1 a534 1 return SA_ERR_ARG; d541 1 a541 1 return SA_ERR_ARG; d564 1 a564 1 return SA_ERR_SYS; d614 1 a614 1 return SA_ERR_ARG; d617 1 a617 1 return SA_ERR_ARG; d620 1 a620 1 return SA_ERR_ARG; d626 1 a626 1 return SA_ERR_MEM; d639 1 a639 1 return SA_ERR_ARG; d648 1 a648 1 return SA_ERR_USE; d654 1 a654 1 return SA_ERR_MEM; d678 1 a678 1 return SA_ERR_ARG; d709 1 a709 1 return SA_ERR_INT; d722 1 a722 1 return SA_ERR_ARG; d726 1 a726 1 return SA_ERR_MEM; d749 1 a749 1 return SA_ERR_ARG; d765 1 a765 1 return SA_ERR_MTC; d770 1 a770 1 return SA_ERR_MTC; d797 1 a797 1 return SA_ERR_MTC; d799 1 a799 1 return SA_ERR_MTC; d820 1 a820 1 return SA_ERR_INT; d824 1 a824 1 return SA_ERR_ARG; d840 1 a840 1 return SA_ERR_MTC; d847 1 a847 1 return SA_ERR_MTC; d853 1 a853 1 return SA_ERR_MTC; d867 1 a867 1 return SA_ERR_SYS; d873 1 a873 1 return SA_ERR_SYS; d891 1 a891 1 return SA_ERR_ARG; d895 1 a895 1 return SA_ERR_USE; d903 1 a903 1 return SA_ERR_INT; d919 1 a919 1 return SA_ERR_INT; d926 1 a926 1 return SA_ERR_INT; d928 1 a928 1 return SA_ERR_SYS; d933 1 a933 1 return SA_ERR_INT; d937 1 a937 1 return SA_ERR_SYS; d950 1 a950 1 return SA_ERR_ARG; d954 1 a954 1 return SA_ERR_USE; d971 1 a971 1 return SA_ERR_ARG; d975 1 a975 1 return SA_ERR_MEM; d1013 1 a1013 1 return SA_ERR_ARG; d1033 1 a1033 1 return SA_ERR_ARG; d1035 1 a1035 1 return SA_ERR_ARG; d1054 1 a1054 1 return SA_ERR_ARG; d1080 1 a1080 1 return SA_ERR_ARG; d1085 1 a1085 1 return SA_ERR_USE; d1092 1 a1092 1 return SA_ERR_SYS; d1106 1 a1106 1 return SA_ERR_USE; d1113 1 a1113 1 return SA_ERR_SYS; d1125 1 a1125 1 return SA_ERR_ARG; d1138 1 a1138 1 return SA_ERR_ARG; d1225 1 a1225 1 return rv; d1235 1 a1235 1 return SA_ERR_ARG; d1250 1 a1250 1 return rv; d1261 1 a1261 1 return SA_ERR_ARG; d1266 1 a1266 1 return rv; d1276 1 a1276 1 return SA_ERR_SYS; d1291 1 a1291 1 return SA_ERR_ARG; d1295 1 a1295 1 return SA_ERR_USE; d1300 1 a1300 1 return rv; d1368 1 a1368 1 return rv; d1376 1 a1376 1 return SA_ERR_ARG; d1380 1 a1380 1 return SA_ERR_USE; d1384 1 a1384 1 return SA_ERR_USE; d1388 1 a1388 1 return SA_ERR_SYS; d1412 1 a1412 1 return SA_ERR_ARG; d1416 1 a1416 1 return SA_ERR_USE; d1420 1 a1420 1 return SA_ERR_USE; d1431 1 a1431 1 return SA_ERR_TMT; d1433 1 a1433 1 return SA_ERR_SYS; d1439 1 a1439 1 return SA_ERR_SYS; d1443 1 a1443 1 return rv; d1446 1 a1446 1 return rv; d1452 1 a1452 1 return rv; d1490 1 a1490 1 return SA_ERR_ARG; d1494 1 a1494 1 return SA_ERR_USE; d1498 1 a1498 1 return SA_ERR_USE; d1503 1 a1503 1 return SA_ERR_SYS; d1507 1 a1507 1 return rv; d1510 1 a1510 1 return rv; d1530 1 a1530 1 return SA_ERR_ARG; d1534 1 a1534 1 return SA_ERR_USE; d1539 1 a1539 1 return SA_ERR_SYS; d1543 1 a1543 1 return rv; d1546 1 a1546 1 return rv; d1557 1 a1557 1 return SA_ERR_ARG; d1561 1 a1561 1 return SA_ERR_USE; d1616 1 a1616 1 return SA_ERR_ARG; d1620 1 a1620 1 return SA_ERR_USE; d1624 1 a1624 1 return SA_ERR_USE; d1700 1 a1700 1 return rv; d1713 1 a1713 1 return SA_ERR_ARG; d1717 1 a1717 1 return SA_ERR_USE; d1721 1 a1721 1 return SA_ERR_USE; d1745 1 a1745 1 return rv; d1795 1 a1795 1 return SA_ERR_ARG; d1799 1 a1799 1 return SA_ERR_USE; d1803 1 a1803 1 return SA_ERR_USE; d1848 1 a1848 1 return rv; d1877 1 a1877 1 return SA_ERR_ARG; d1881 1 a1881 1 return SA_ERR_USE; d1885 1 a1885 1 return SA_ERR_USE; d1905 1 a1905 1 return SA_ERR_ARG; d1909 1 a1909 1 return SA_ERR_USE; d1913 1 a1913 1 return SA_ERR_USE; d1931 1 a1931 1 return rv; d1941 1 a1941 1 return SA_ERR_ARG; d1945 1 a1945 1 return SA_ERR_USE; d1949 1 a1949 1 return SA_ERR_USE; d1960 1 a1960 1 return SA_ERR_ARG; d1964 1 a1964 1 return SA_ERR_SYS; d1985 1 a1985 1 return SA_ERR_ARG; d1989 1 a1989 1 return SA_ERR_USE; d1993 1 a1993 1 return SA_ERR_USE; d2007 1 a2007 1 return SA_ERR_SYS; d2014 1 a2014 1 return SA_ERR_SYS; d2040 1 a2040 1 return SA_ERR_ARG; d2044 1 a2044 1 return SA_ERR_USE; d2063 1 a2063 1 return SA_ERR_SYS; d2068 1 a2068 1 return SA_ERR_SYS; @ 1.53 log @Ops, an else was killed... add it again @ text @d146 1 a146 1 } while(0) d153 1 a153 1 } while(0) d452 1 a452 1 int nPort; d548 1 a548 1 nPort = atoi(cpPort); d661 1 a661 1 int nPort; d1599 1 a1599 1 size_t res; d1630 1 a1630 1 while (1) { d1685 1 a1685 1 *nBufRes = res; d1776 2 a1777 2 size_t n; size_t res; d1833 1 a1833 1 *nBufRes = res; d1887 1 a1887 1 size_t n; @ 1.52 log @Linux does not have SA_OPTION_REUSEPORT, so use this optionally only. @ text @d1111 1 @ 1.51 log @fix syntax @ text @d1162 4 a1165 1 case SA_OPTION_REUSEPORT: { d1175 1 d1177 1 @ 1.50 log @rewrite with a more readable switch() statement @ text @d1221 8 a1228 8 case SA_SYSCALL_CONNECT: SA_SC_ASSIGN(sa, connect, fptr, fctx); case SA_SYSCALL_ACCEPT: SA_SC_ASSIGN(sa, accept, fptr, fctx); case SA_SYSCALL_SELECT: SA_SC_ASSIGN(sa, select, fptr, fctx); case SA_SYSCALL_READ: SA_SC_ASSIGN(sa, read, fptr, fctx); case SA_SYSCALL_WRITE: SA_SC_ASSIGN(sa, write, fptr, fctx); case SA_SYSCALL_RECVFROM: SA_SC_ASSIGN(sa, recvfrom, fptr, fctx); case SA_SYSCALL_SENDTO: SA_SC_ASSIGN(sa, sendto, fptr, fctx); case: rv = SA_ERR_ARG; @ 1.49 log @add support for lingering on close @ text @d1212 3 d1217 15 a1231 17 if (id == SA_SYSCALL_CONNECT) SA_SC_ASSIGN(sa, connect, fptr, fctx); else if (id == SA_SYSCALL_ACCEPT) SA_SC_ASSIGN(sa, accept, fptr, fctx); else if (id == SA_SYSCALL_SELECT) SA_SC_ASSIGN(sa, select, fptr, fctx); else if (id == SA_SYSCALL_READ) SA_SC_ASSIGN(sa, read, fptr, fctx); else if (id == SA_SYSCALL_WRITE) SA_SC_ASSIGN(sa, write, fptr, fctx); else if (id == SA_SYSCALL_RECVFROM) SA_SC_ASSIGN(sa, recvfrom, fptr, fctx); else if (id == SA_SYSCALL_SENDTO) SA_SC_ASSIGN(sa, sendto, fptr, fctx); else return SA_ERR_ARG; return SA_OK; @ 1.48 log @add support for reusability of address or port @ text @d1146 15 @ 1.47 log @complain if no socket is available for SA_OPTION_NONBLOCK @ text @d1146 19 @ 1.46 log @ops, syntax error @ text @d1150 4 @ 1.45 log @first cut for PF_XXX usage @ text @d70 1 a70 1 if !defined(PF_LOCAL) && defined(AF_LOCAL) d73 1 a73 1 if !defined(PF_INET) && defined(AF_INET) d76 1 a76 1 if !defined(PF_INET6) && defined(AF_INET6) @ 1.44 log @conditionalize variable, too. @ text @d50 1 a50 1 #include /* for "AF_XXX" and "SOCK_XXX" */ d67 11 @ 1.43 log @use faster IPPROTO_XXX instead of getprotobyname if available @ text @d861 1 d863 1 @ 1.42 log @bump copyright year @ text @a861 1 char *cpProto; d887 1 d889 1 a889 1 cpProto = "tcp"; d891 1 a891 1 cpProto = "udp"; d894 8 a901 1 if ((pe = getprotobyname(cpProto)) == NULL) d904 1 @ 1.41 log @ops, uninitialized variable. @ text @d3 3 a5 3 ** Copyright (c) 2001 Ralf S. Engelschall ** Copyright (c) 2001 The OSSP Project ** Copyright (c) 2001 Cable & Wireless Deutschland @ 1.40 log @add support for optional port matching @ text @d803 1 @ 1.39 log @Make sure we import only supported addresses on sa_addr_s2a(). @ text @d720 2 d724 1 a724 1 if (saa1 == NULL || saa2 == NULL || prefixlen < -1) d733 2 d739 1 a739 1 if (prefixlen == -1) { d756 2 d764 2 d780 2 d788 2 d799 1 a799 1 if (prefixlen > nBits) d802 4 a805 2 /* support equal matching (= all bits) */ if (prefixlen == -1) d807 1 d824 6 @ 1.38 log @- provide additional SA_ERR_IMP result code for indicating that an implementation part is not available on the current platform. - provide new sa_option() function for setting various socket options. - implement SA_OPTION_NAGLE (for enabling/disabling Nagle's Algorithm) and SA_OPTION_NONBLOCK (for enabling/disabling non-blocking I/O mode). @ text @d617 9 @ 1.37 log @Improve performance by only flushing the output buffer of we really read from the socket. If we fulfill the request with data from the user-space buffer we now no longer force a flush of the output buffer. @ text @d74 5 d502 1 a502 1 return SA_ERR_ARG; d1064 55 d1981 1 @ 1.36 log @Add support for ACLs with "IPv4-mapped IPv6 addresses" (e.g. "::ffff:1.2.3.4") as defined by RFC 2373. This is important for security reasons because these are IPv6 addresses but which have to match incoming IPv4 addresses in ACLs. @ text @a1455 4 /* trigger a write buffer flush */ if (sa->nWriteLen > 0) sa_flush(sa); d1460 2 d1490 2 @ 1.35 log @fix socket descriptor handling in case of connect errors @ text @a699 1 int nFamily; d702 4 a714 4 /* short circuiting handling different families */ if (saa1->nFamily != saa2->nFamily) return SA_ERR_MTC; d716 1 a716 2 nFamily = saa1->nFamily; if (nFamily == AF_LOCAL) { d732 26 a757 1 else if (nFamily == AF_INET) { d763 1 a763 1 else if (nFamily == AF_INET6) { @ 1.34 log @Enhance/Bugfix IPv6 address handling. @ text @d1152 1 d1170 1 @ 1.33 log @make prefixlen=0 a real wildcard @ text @d429 1 d491 1 a491 1 /* parse URI */ d493 4 a496 1 if ((cp = strrchr(cpHost, ':')) == NULL) d498 17 a514 1 *cp++ = '\0'; d516 5 a533 5 cpProto = "tcp"; if ((cp = strchr(cpPort, '#')) != NULL) { *cp++ = '\0'; cpProto = cp; } d545 3 a547 3 /* resolve host by trying to parse it as either directly a IPv4 or IPv6 address or by resolving it to either a IPv4 or IPv6 address */ if (sa_inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) { d555 1 a555 1 else if (sa_inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) { a655 3 #ifdef AF_INET6 else if (saa->nFamily == AF_INET || saa->nFamily == AF_INET6) { #else d657 5 a661 6 #endif if (saa->nFamily == AF_INET) { sa4 = (struct sockaddr_in *)((void *)saa->saBuf); sa_inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)); nPort = ntohs(sa4->sin_port); } d663 6 a668 5 else { sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf); sa_inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)); nPort = ntohs(sa6->sin6_port); } a669 2 sa_msnprintf(uribuf, sizeof(uribuf), "inet://%s:%d", caHost, nPort); } @ 1.32 log @- Fix Unix Domain socket handling in case of unbound remote side. - Simplified family detection in sa_addr_s2a() in order to correctly support again the situation of unbound remote Unix Domain sockets. - Fixed sa_readln() return code semantics by passing through sa_read() return value. @ text @d693 5 a697 1 /* match short circuiting */ @ 1.31 log @fix two nasty bugs @ text @a587 7 struct sockaddr_un *un; struct sockaddr_in *sa4; #ifdef AF_INET6 struct sockaddr_in6 *sa6; #endif int sf; d600 2 a601 22 /* resolve family */ sf = 0; if (sizeof(struct sockaddr_un) == salen) { un = (struct sockaddr_un *)((void *)sabuf); if (un->sun_family == AF_LOCAL) sf = AF_LOCAL; } if (sf == 0 && sizeof(struct sockaddr_in) == salen) { sa4 = (struct sockaddr_in *)((void *)sabuf); if (sa4->sin_family == AF_INET) sf = AF_INET; } #ifdef AF_INET6 if (sf == 0 && sizeof(struct sockaddr_in6) == salen) { sa6 = (struct sockaddr_in6 *)((void *)sabuf); if (sa6->sin6_family == AF_INET6) sf = AF_INET6; } #endif if (sf == 0) return SA_ERR_ARG; saa->nFamily = sf; d625 10 a634 1 sa_msnprintf(uribuf, sizeof(uribuf), "unix:%s", un->sun_path); d1495 1 d1513 1 d1516 3 a1518 1 sa_read(sa, &c, 1, &n); d1531 1 a1531 1 return SA_OK; @ 1.30 log @support for ssize_t fallback @ text @d481 1 a481 1 memcpy(un.sun_path, cpPath, n+1); d1204 1 @ 1.29 log @complain on 'unix:' (no path) @ text @d56 18 a200 8 /* boolean values */ #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (!FALSE) #endif a203 5 /* backward compatibility for AF_LOCAL */ #if !defined(AF_LOCAL) && defined(AF_UNIX) #define AF_LOCAL AF_UNIX #endif @ 1.28 log @Revert my idea of using an L2-style error handling also in SA, because this is not possible in a consistent way due to the multiple objects and the fact that we cannot remember anything without an object (in L2 an environment object will be introduced soon, so there is it is a different game). So we use a plain sa_error() which is now similar to strerror(3). @ text @d465 2 @ 1.27 log @Woohoooo, fully revamp formatting stuff: - evolve sa_mvsnprintf() into an sa_mvxprintf() which 1. is output independent 2. support especially no-output mode (length determination only) 3. performs better by output plain text in largest possible chunks 4. does output operation only once in the loop (smaller code-size) - write a new sa_mvsnprintf() for buffer output based on sa_mvxprintf() - change sa_writef() from brain-dead and problematic "first format anything into temporary buffer following by a single write" to the expected "format on-the-fly into the SA_BUFFER_WRITE and if not enough space exists, write directly to the socket". @ text @a172 3 char szError[256]; /* error temporary buffer */ char szErrorInfo[128]; /* error additional information text */ sa_rc_t rvErrorInfo; /* error additional information code */ a374 82 /* remember extra error information */ static sa_rc_t sa_errorinfo(sa_t *sa, sa_rc_t rv, const char *fmt, ...) { va_list ap; /* argument sanity check */ if (sa == NULL) return SA_ERR_ARG; if (rv == SA_OK && fmt == NULL) { /* reset error information */ sa->szErrorInfo[0] = '\0'; sa->rvErrorInfo = SA_OK; } else { /* remember error information */ va_start(ap, fmt); sa_mvsnprintf(sa->szErrorInfo, sizeof(sa->szErrorInfo), fmt, ap); sa->rvErrorInfo = rv; va_end(ap); } return SA_OK; } /* return error string */ sa_rc_t sa_error(sa_t *sa, sa_rc_t rv, char **str) { char *sz; char *cpBuf; int nBuf; int n; /* argument sanity check */ if (str == NULL) return SA_ERR_ARG; /* translate result value into corresponding string */ if (rv == SA_OK) sz = "everything ok"; else if (rv == SA_ERR_ARG) sz = "invalid argument"; else if (rv == SA_ERR_USE) sz = "invalid use or context"; else if (rv == SA_ERR_MEM) sz = "not enough memory available"; else if (rv == SA_ERR_MTC) sz = "matching operation failed"; else if (rv == SA_ERR_EOF) sz = "communication end"; else if (rv == SA_ERR_TMT) sz = "communication timeout"; else if (rv == SA_ERR_SYS) sz = "operating system error"; else if (rv == SA_ERR_INT) sz = "internal error"; else return SA_ERR_ARG; /* if no context is available we have to stop */ if (sa == NULL) { *str = sz; return SA_OK; } /* start at begin of buffer */ cpBuf = sa->szError; nBuf = sizeof(sa->szError); n = sa_msnprintf(cpBuf, nBuf, "%s", sz); cpBuf += n; nBuf -= n; /* optionally annotate with error information */ if (rv == sa->rvErrorInfo && sa->szErrorInfo[0] != '\0') { n = sa_msnprintf(cpBuf, nBuf, "; %s", sa->szErrorInfo); cpBuf += n; nBuf -= n; sa_errorinfo(sa, SA_OK, NULL); } /* optionally annotate with operating system error information */ if (rv == SA_ERR_SYS) { n = sa_msnprintf(cpBuf, nBuf, "; %s (%d)", strerror(errno), errno); cpBuf += n; nBuf -= n; } /* return pointer to internal buffer */ *str = sa->szError; return SA_OK; } d557 2 d560 2 d563 2 a564 4 /* make sure result variables are set */ if (sa == NULL || sl == 0 || sf == 0) return SA_ERR_INT; a884 3 sa->szError[0] = '\0'; sa->szErrorInfo[0] = '\0'; sa->rvErrorInfo = SA_OK; d1867 19 @ 1.26 log @Change sa_rc_t sa_buffers (sa_t *sa, size_t rsize, size_t wsize); to sa_rc_t sa_buffer (sa_t *sa, sa_buffer_t id, size_t size); in order to make this API function more flexible. @ text @d251 2 a252 2 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */ static int sa_mvsnprintf(char *buffer, size_t bufsize, const char *format, va_list ap) a253 2 char *bufptr; char *bufend; d260 1 d262 17 a278 11 bufptr = buffer; bufend = buffer + bufsize - 1; while ((c = *format++) != '\0' && bufptr < bufend) { if (c == '%') { c = *format++; if (c == '%') /* implement "%%" */ *bufptr++ = c; else if (c == 'c') /* implement "%c" */ *bufptr++ = (char)va_arg(ap, int); d280 1 a280 1 /* implement "%s" */ a283 4 if ((bufptr + n) > bufend) n = bufend - bufptr; memcpy(bufptr, cp, n); bufptr += n; d286 1 a286 1 /* implement "%d" */ d293 2 a294 3 n = strlen(ibuf); memcpy(bufptr, ibuf, n); bufptr += n; d297 3 a299 3 *bufptr++ = '%'; if (bufptr < bufend) *bufptr++ = c; d301 1 d303 12 a314 2 else *bufptr++ = c; d316 46 a361 2 *bufptr = '\0'; return (bufptr - buffer); a369 4 /* argument sanity check(s) */ if (buffer == NULL || bufsize == 0 || format == NULL) return 0; d1729 17 d1751 1 a1751 2 char caBuf[1024]; sa_rc_t rv; d1767 3 a1769 1 n = sa_mvsnprintf(caBuf, sizeof(caBuf), cpFmt, ap); d1772 1 a1772 4 /* write result buffer to socket */ rv = sa_write(sa, caBuf, n, &n); return rv; @ 1.25 log @[still untested stuff] - Remove SA_TIMEOUT_RECV and SA_TIMEOUT_SEND, because SA_TIMEOUT_READ and SA_TIMEOUT_WRITE are sufficient because you cannot use datagram and stream communication at the same time anyway. So there is no need to distinguish between the two communications in timeouts. Additionally this enables the next point from being able to be implemented at all. - Implement the read/write timeouts alternatively in kernel-space through the newer SO_RCVTIMEO and SO_SNDTIMEO socket options. This means a select(2) system call less for each read/write operation. @ text @d1008 1 a1008 1 sa_rc_t sa_buffers(sa_t *sa, size_t rsize, size_t wsize) d1016 20 a1035 14 /* make sure buffered were already flushed sufficiently */ if (sa->nReadLen > rsize || sa->nWriteLen > wsize) return SA_ERR_USE; /* configure read/incoming buffer */ if (rsize > 0) { if (sa->cpReadBuf == NULL) cp = (char *)malloc(rsize); else cp = (char *)realloc(sa->cpReadBuf, rsize); if (cp == NULL) return SA_ERR_SYS; sa->cpReadBuf = cp; sa->nReadSize = rsize; d1037 20 a1056 23 else { if (sa->cpReadBuf != NULL) free(sa->cpReadBuf); sa->cpReadBuf = NULL; sa->nReadSize = 0; } /* configure write/outgoing buffer */ if (wsize > 0) { if (sa->cpWriteBuf == NULL) cp = (char *)malloc(wsize); else cp = (char *)realloc(sa->cpWriteBuf, wsize); if (cp == NULL) return SA_ERR_SYS; sa->cpWriteBuf = cp; sa->nWriteSize = wsize; } else { if (sa->cpWriteBuf != NULL) free(sa->cpWriteBuf); sa->cpWriteBuf = NULL; sa->nWriteSize = 0; d1058 1 @ 1.24 log @According to STEVENS' "Unix Network Programming, Vol.1" (p.412) we have to close the socket if a connection timeout occured to make sure that the TCP three-way handshake does not proceed further. @ text @d166 1 a166 1 struct timeval tvTimeout[6]; /* timeout values (sec, usec) */ d799 22 d870 3 d1001 3 d1400 1 d1402 1 d1406 1 d1419 1 d1426 5 d1575 1 d1577 1 d1581 1 d1594 1 d1601 5 d1805 1 a1805 1 if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_RECV])) { d1810 1 a1810 1 &sa->tvTimeout[SA_TIMEOUT_RECV]); d1861 1 a1861 1 if (!SA_TVISZERO(sa->tvTimeout[SA_TIMEOUT_SEND])) { d1866 1 a1866 1 &sa->tvTimeout[SA_TIMEOUT_SEND]); @ 1.23 log @- error text cleanup - allow "sa_t *" to be NULL @ text @d1146 1 d1163 1 @ 1.22 log @cosmetics only. @ text @d360 1 a360 1 if (sa == NULL || str == NULL) a362 4 /* start at begin of buffer */ cpBuf = sa->szError; nBuf = sizeof(sa->szError); d366 5 a370 2 else if (rv == SA_ERR_USE) sz = "invalid use"; else if (rv == SA_ERR_MEM) sz = "no more memory available"; a371 1 else if (rv == SA_ERR_EOF) sz = "end of file in socket communication"; d373 12 a384 1 else sz = "unknown error"; @ 1.21 log @cosmetics only. @ text @d153 5 a157 5 SA_SC_DECLARE_3(int, connect, int, const struct sockaddr *, socklen_t) SA_SC_DECLARE_3(int, accept, int, struct sockaddr *, socklen_t *) SA_SC_DECLARE_5(int, select, int, fd_set *, fd_set *, fd_set *, struct timeval *) SA_SC_DECLARE_3(ssize_t, read, int, void *, size_t) SA_SC_DECLARE_3(ssize_t, write, int, const void *, size_t) d159 1 a159 1 SA_SC_DECLARE_6(ssize_t, sendto, int, const void *, size_t, int, const struct sockaddr *, socklen_t) @ 1.20 log @cosmetics only. @ text @d39 2 a40 2 #include /* for "va_xxx()" and "va_list" */ #include /* for "strxxx()" and "size_t" */ d42 1 a42 1 #include /* for isxxx() */ d51 1 a51 1 #include /* for "inet_xtoy" */ @ 1.19 log @Change return code semantic for timeouts from "rv == SA_ERR_SYS && errno == ETIMEDOUT" to a dedicated timeout return value "rv == SA_ERR_TMT". @ text @a55 7 /* socket address abstraction object */ struct sa_addr_st { int nFamily; /* the socket family (AF_XXX) */ struct sockaddr *saBuf; /* the "struct sockaddr_xx" actually */ socklen_t slBuf; /* the length of "struct sockaddr_xx" */ }; d164 20 a183 13 sa_type_t eType; /* socket type (stream or datagram) */ int fdSocket; /* socket file descriptor */ struct timeval tvTimeout[6];/* timeout values (sec, usec) */ int nReadLen; /* read buffer current length */ int nReadSize; /* read buffer current size */ char *cpReadBuf; /* read buffer memory chunk */ int nWriteLen; /* write buffer current length */ int nWriteSize; /* write buffer current size */ char *cpWriteBuf; /* write buffer memory chunk */ char szError[256]; char szErrorInfo[128]; sa_rc_t rvErrorInfo; sa_syscall_tab_t scSysCall; @ 1.18 log @support relative paths for 'unix:path' @ text @d1137 1 a1137 1 error = ETIMEDOUT; d1219 1 a1219 1 errno = ETIMEDOUT; d1416 2 d1448 2 d1456 4 a1459 1 if (n < 0) d1575 3 a1577 1 if (res < 0) d1591 3 a1593 1 if (n < 0) d1671 3 a1673 1 if (n < 0) @ 1.17 log @bugfix sa_addr_match() @ text @a483 2 if (cpPath[0] != '/') return SA_ERR_ARG; d492 7 @ 1.16 log @Support prefixlen == 0 for an "always matches" and instead use prefixlen == -1 for "full match". Credits to Thomas ;) @ text @d772 1 a772 1 if (memcmp(ucp1, ucp1, nBytes) != 0) @ 1.15 log @Implement new sa_addr_match() function which can be used for Access Control List (ACL) implementations. It is capable of matching Unix Domain and Internet Addresses (both IPv4 and IPv6) with prefix lengths. @ text @d709 1 a709 1 sa_rc_t sa_addr_match(sa_addr_t *saa1, sa_addr_t *saa2, size_t prefixlen) d719 1 a719 1 if (saa1 == NULL || saa2 == NULL) d733 1 a733 1 if (prefixlen == 0) { d764 1 a764 1 if (prefixlen == 0) @ 1.14 log @support IPv6 addresses @ text @d709 76 @ 1.13 log @clone parameters on sa_accept() @ text @d505 1 a505 1 if ((cp = strchr(cpHost, ':')) == NULL) @ 1.12 log @Various cleanups @ text @d121 7 d1115 1 d1161 2 d1164 15 @ 1.11 log @Implement (still fully untested!) system call overriding support through a new function sa_syscall(). Anybody here who finds predestinated to test this out in depth? @ text @d3 3 a5 2 ** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) @ 1.10 log @Provide distinct timeouts, i.e., an own timeout for each individual I/O function. This is especially useful because an accept(2) timeout is usually something totally different than a connect(2) timeout. @ text @d62 99 d175 1 d784 15 a798 2 sa->eType = SA_TYPE_STREAM; sa->fdSocket = -1; d803 9 a811 9 sa->nReadLen = 0; sa->nReadSize = 0; sa->cpReadBuf = NULL; sa->nWriteLen = 0; sa->nWriteSize = 0; sa->cpWriteBuf = NULL; sa->szError[0] = '\0'; sa->szErrorInfo[0] = '\0'; sa->rvErrorInfo = SA_OK; d933 24 d1009 1 a1009 1 if (connect(sa->fdSocket, raddr->saBuf, raddr->slBuf) < 0) d1021 1 a1021 1 if ((n = connect(sa->fdSocket, raddr->saBuf, raddr->slBuf)) < 0) { d1038 2 a1039 2 n = select(sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout[SA_TIMEOUT_CONNECT]); d1125 2 a1126 2 n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout[SA_TIMEOUT_ACCEPT]); d1136 1 a1136 1 if ((s = accept(sa->fdSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1) d1262 2 a1263 2 rv = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout[SA_TIMEOUT_READ]); d1273 1 a1273 1 rv = read(sa->fdSocket, cpBuf, nBufLen); d1421 2 a1422 2 rv = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout[SA_TIMEOUT_WRITE]); d1432 1 a1432 1 rv = write(sa->fdSocket, cpBuf, nBufLen); d1632 2 a1633 2 n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout[SA_TIMEOUT_RECV]); d1643 2 a1644 2 if ((n = recvfrom(sa->fdSocket, buf, buflen, 0, (struct sockaddr *)&sa_buf, &sa_len)) == -1) d1688 2 a1689 2 n = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout[SA_TIMEOUT_SEND]); d1698 1 a1698 1 if ((n = sendto(sa->fdSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1) @ 1.9 log @Add sa_error() function. @ text @d66 1 a66 2 int bTimeout; /* timeout enabling flag */ struct timeval tvTimeout; /* timeout value (sec, usec) */ d86 4 d675 1 d686 4 a689 3 sa->bTimeout = FALSE; sa->tvTimeout.tv_sec = 0; sa->tvTimeout.tv_usec = 0; d746 1 a746 1 sa_rc_t sa_timeout(sa_t *sa, long sec, long usec) d748 2 d754 10 a763 7 /* configure timeout */ if (sec == 0 && usec == 0) sa->bTimeout = FALSE; /* deactivate timeout */ else sa->bTimeout = TRUE; /* activate timeout */ sa->tvTimeout.tv_sec = sec; sa->tvTimeout.tv_usec = usec; d870 1 a870 1 if (!sa->bTimeout) { d901 2 a902 1 n = select(sa->fdSocket+1, &rset, &wset, NULL, &sa->tvTimeout); d984 1 a984 1 if (sa->bTimeout) { d988 2 a989 1 n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout); d1121 1 a1121 1 if (sa->bTimeout) { d1125 2 a1126 1 rv = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout); d1280 1 a1280 1 if (sa->bTimeout) { d1284 2 a1285 1 rv = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout); d1491 1 a1491 1 if (sa->bTimeout) { d1495 2 a1496 1 n = select(sa->fdSocket+1, &fds, NULL, NULL, &sa->tvTimeout); d1547 1 a1547 1 if (sa->bTimeout) { d1551 2 a1552 1 n = select(sa->fdSocket+1, NULL, &fds, NULL, &sa->tvTimeout); @ 1.8 log @Woohhhooo: Second major revamp of Socket Abstraction (SA) library. This time is gets really close to a 100% clean and polished library. @ text @d74 3 d216 73 d691 3 @ 1.7 log @fix sa_connect() if no timeout is set @ text @d36 15 a50 16 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include d57 3 a59 3 int nFamily; struct sockaddr *saBuf; socklen_t slBuf; d64 10 a73 10 sa_type_t eType; int sSocket; int bTimeout; struct timeval tvTimeout; int nReadLen; int nReadSize; char *cpReadBuf; int nWriteLen; int nWriteSize; char *cpWriteBuf; d76 9 a84 1 /* make sure AF_LOCAL define exists */ d89 2 a90 5 /* make sure inet_pton() exists */ #if defined(HAVE_INET_PTON) #define sa_inet_pton inet_pton #elif defined(HAVE_INET_ATON) static int inet_pton(int family, const char *strptr, void *addrptr) d92 3 d98 6 a103 5 if (inet_aton(strptr, &in_val)) { memcpy(addrptr, &in_val, sizeof(struct in_addr)); return 1; } return 0; d106 2 a107 1 return -1; a108 3 #else #error "neither inet_pton nor inet_aton available" #endif d110 2 a111 5 /* make sure inet_ntop() exists */ #if defined(HAVE_INET_NTOP) #define sa_inet_ntop inet_ntop #elif defined(HAVE_INET_NTOA) static char *inet_ntop(int family, const void *src, char *dst, size_t size) d113 3 d121 10 a130 9 if ((cp = inet_ntoa(src)) != NULL) { n = strlen(cp); if (n > size-1) n = size-1; memcpy(dst, cp, n); dst[n] = '\0'; return 1; } return 0; d133 2 a134 1 return -1; a135 3 #else #error "neither inet_ntop nor inet_ntoa available" #endif d137 1 a137 1 /* minimal vsnprintf(3) */ d142 2 a143 1 char ibuf[((sizeof(int)*8)/3)+10]; /* (available bits) x log_10(2) + safety */ d155 1 d158 1 d161 1 d171 1 d174 1 a174 1 snprintf(ibuf, sizeof(ibuf), "%d", d); /* inherently secure */ d176 1 a176 1 sprintf(ibuf, "%d", d); /* nevertheless secure; see ibuf above */ d195 1 a195 1 /* minimal snprintf(3) */ d201 5 d209 1 d213 2 a214 1 sa_rc_t sa_addr_create(sa_addr_t **saa) d216 4 a219 1 if (saa == NULL) d221 3 a223 1 if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL) d225 7 a231 3 (*saa)->saBuf = NULL; (*saa)->slBuf = 0; (*saa)->nFamily = 0; d235 1 d238 1 d241 2 d246 1 d250 1 d254 1 a254 1 int nPort; a264 3 int i; int n; char *cpProto; d267 3 a269 1 char *cpPath; d272 2 a273 1 int sf; d275 1 a275 1 /* argument sanity check */ d284 9 a292 1 /* parse URI into parts */ d294 25 a318 12 cpProto = "unix"; if ((cp = strchr(uri, ':')) != NULL) { cpProto = (char *)uri; *cp = '\0'; uri = cp+1; } cpHost = ""; cpPort = ""; if (strncmp(uri, "//", 2) == 0) { uri += 2; cpHost = (char *)uri; if ((cp = strchr(uri, ':')) == NULL) a321 7 uri = cp; if ((cp = strchr(uri, '/')) != NULL) uri = cp; else uri = strchr(uri, '\0'); } cpPath = (char *)uri; d323 2 a324 3 /* resolve port */ nPort = 0; if (strcmp(cpProto, "inet") == 0) { d335 5 a343 1 } d345 2 a346 3 /* mandatory(!) socket address structure initialization */ memset(&un, 0, sizeof(un)); memset(&sa4, 0, sizeof(sa4)); d348 1 a348 1 memset(&sa6, 0, sizeof(sa6)); d351 3 a353 19 /* resolve host by trying to parse it as either directly a IPv4 or IPv6 address or by resolving it to either a IPv4 or IPv6 address */ sa = NULL; sl = 0; sf = 0; if (strcmp(cpProto, "unix") == 0) { if (cpProto[0] != '/') return SA_ERR_ARG; n = strlen(cpProto); if ((n+1) > sizeof(un.sun_path)) return SA_ERR_MEM; memcpy(un.sun_path, cpPath, n+1); un.sun_family = AF_LOCAL; sa = (struct sockaddr *)&un; sl = sizeof(un); sf = AF_LOCAL; } else if (strcmp(cpProto, "inet") == 0) { if (inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) { d361 1 a361 1 else if (inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) { d373 2 a374 1 memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0], sizeof(sa4.sin_addr.s_addr)); d383 2 a384 1 memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], sizeof(sa6.sin6_addr.s6_addr)); d392 8 a399 4 if (sa == NULL) return SA_ERR_ARG; /* create result address structure */ d405 1 d409 1 d419 1 d424 2 d431 1 a431 1 /* fill in family */ d457 1 d469 1 d472 2 d478 5 a482 1 else { d485 1 a485 1 inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)); d489 1 a489 1 else if (saa->nFamily == AF_INET6) { d491 1 a491 1 inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)); a494 2 else return SA_ERR_ARG; d497 4 d502 1 d506 1 d509 1 d513 1 d518 1 d522 1 d530 1 d534 2 a535 2 /* only perform operation if socket still do not exists */ if (sa->sSocket != -1) d544 1 a544 1 return SA_ERR_ARG; d565 1 a565 1 return SA_ERR_ARG; d567 2 a568 2 /* create the socket */ if ((sa->sSocket = socket(nFamily, nType, nProto)) == -1) d574 1 d577 1 d580 9 a588 4 if (sa->sSocket != -1) { close(sa->sSocket); sa->sSocket = -1; } d592 1 d597 5 d604 3 a606 3 sa->eType = SA_TYPE_STREAM; sa->sSocket = -1; sa->bTimeout = 0; d609 8 a616 6 sa->nReadLen = 0; sa->nReadSize = 0; sa->cpReadBuf = NULL; sa->nWriteLen = 0; sa->nWriteSize = 0; sa->cpWriteBuf = NULL; d618 1 d622 1 d625 1 d628 2 d631 2 d638 1 d642 1 d645 1 d648 1 a648 1 if (type != SA_TYPE_STREAM && type != SA_TYPE_DATAGRAM) d650 6 d657 1 d661 1 d664 1 d667 9 a675 10 if (sec == 0 && usec == 0) { sa->bTimeout = 0; sa->tvTimeout.tv_sec = 0; sa->tvTimeout.tv_usec = 0; } else { sa->bTimeout = 1; sa->tvTimeout.tv_sec = sec; sa->tvTimeout.tv_usec = usec; } d679 1 d684 1 d687 2 d690 3 a692 1 return SA_ERR_ARG; d709 2 a726 2 return SA_OK; } a727 19 sa_rc_t sa_getoption(sa_t *sa, int optname, void *optval, socklen_t *optlen) { if (sa == NULL) return SA_ERR_ARG; if (sa->sSocket == -1) return SA_ERR_USE; if (getsockopt(sa->sSocket, SOL_SOCKET, optname, optval, optlen) == -1) return SA_ERR_SYS; return SA_OK; } sa_rc_t sa_setoption(sa_t *sa, int optname, const void *optval, socklen_t optlen) { if (sa == NULL) return SA_ERR_ARG; if (sa->sSocket == -1) return SA_ERR_USE; if (setsockopt(sa->sSocket, SOL_SOCKET, optname, optval, optlen) == -1) return SA_ERR_SYS; d731 1 d737 1 d740 3 a742 1 if (sa->sSocket == -1) d745 2 d751 3 a753 1 if (bind(sa->sSocket, laddr->saBuf, laddr->slBuf) == -1) d755 1 d759 1 d767 2 a768 1 if (sa == NULL) d770 2 d774 3 a776 1 if (sa->sSocket == -1) d780 1 d782 3 a784 4 if (connect(sa->sSocket, raddr->saBuf, raddr->slBuf) < 0) return SA_ERR_SYS; else return SA_OK; d786 3 d790 11 a800 2 error = 0; rv = SA_OK; d802 12 a813 2 /* remember socket flags */ flags = fcntl(sa->sSocket, F_GETFL, 0); d815 2 a816 6 /* switch to non-blocking mode */ fcntl(sa->sSocket, F_SETFL, flags|O_NONBLOCK); /* perform the connect */ if ((n = connect(sa->sSocket, raddr->saBuf, raddr->slBuf)) < 0) { if (errno != EINPROGRESS) { d820 4 a823 1 } d825 4 a828 12 /* if connect completed immediately */ if (n == 0) goto done; /* wait for read or write possibility */ FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(sa->sSocket, &rset); FD_SET(sa->sSocket, &wset); do { n = select(sa->sSocket+1, &rset, &wset, NULL, &sa->tvTimeout); } while (n == -1 && errno == EINTR); d830 1 a830 9 /* decide on return semantic */ if (n < 0) { error = errno; goto done; } else if (n == 0) { error = ETIMEDOUT; goto done; } d832 2 a833 4 /* fetch pending error */ len = sizeof(error); if (getsockopt(sa->sSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0) error = errno; d835 5 a839 9 done: /* reset socket flags */ fcntl(sa->sSocket, F_SETFL, flags); /* optionally set errno */ if (error != 0) { errno = error; rv = SA_ERR_SYS; a840 1 d844 1 d847 1 d850 2 d854 3 a856 2 if (sa->sSocket == -1) /* at least sa_bind() has to be already performed */ d858 3 a860 1 if (listen(sa->sSocket, backlog) == -1) d862 1 d866 1 d881 2 a882 1 if (sa == NULL) d884 2 d888 3 a890 2 if (sa->sSocket == -1) /* at least sa_listen() has to be already performed */ d892 2 d896 1 a896 1 FD_SET(sa->sSocket, &fds); d898 1 a898 1 n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout); d905 2 d908 1 a908 1 if ((s = accept(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len)) == -1) d910 2 d918 2 d924 2 a925 1 (*csa)->sSocket = s; d929 1 d941 2 a942 1 if (sa == NULL) d944 10 d955 1 a955 3 if (sa->sSocket == -1) return SA_ERR_USE; if (getpeername(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) d957 2 d965 1 d969 1 d981 2 a982 1 if (sa == NULL) d984 3 a986 1 if (sa->sSocket == -1) d988 2 d991 1 a991 1 if (getsockname(sa->sSocket, (struct sockaddr *)&sa_buf, &sa_len) < 0) d993 2 d1001 1 d1005 1 d1008 1 d1011 3 a1013 1 if (sa->sSocket == -1) d1015 4 a1018 1 *fd = sa->sSocket; d1022 1 d1028 2 d1032 1 a1032 1 FD_SET(sa->sSocket, &fds); d1034 1 a1034 1 rv = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout); d1041 2 d1044 1 a1044 1 rv = read(sa->sSocket, cpBuf, nBufLen); d1046 1 d1050 1 d1057 1 d1060 2 d1064 3 a1066 1 if (sa->sSocket == -1) d1069 1 a1069 1 /* flush write buffer */ d1076 1 a1076 1 /* unbuffered I/O */ d1078 3 a1080 1 if (res <= 0) d1084 1 a1084 1 /* buffered I/O */ d1088 1 a1088 1 /* buffer holds enough data, so use this */ d1108 4 a1111 2 else if (n <= 0) rv = SA_ERR_SYS; d1116 6 a1121 2 if (n <= 0) rv = SA_ERR_SYS; d1124 1 a1124 1 continue; d1131 2 d1135 1 d1139 2 a1140 45 sa_rc_t sa_readfrom(sa_t *sa, char *buf, size_t buflen, size_t *bufdone, sa_addr_t **raddr) { sa_rc_t rv; union { struct sockaddr_in sa4; #ifdef AF_INET6 struct sockaddr_in6 sa6; #endif } sa_buf; socklen_t sa_len; size_t n; fd_set fds; if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) return SA_ERR_ARG; if (sa->eType != SA_TYPE_DATAGRAM) return SA_ERR_USE; if (sa->sSocket == -1) return SA_ERR_USE; if (sa->bTimeout) { FD_ZERO(&fds); FD_SET(sa->sSocket, &fds); do { n = select(sa->sSocket+1, &fds, NULL, NULL, &sa->tvTimeout); } while (n == -1 && errno == EINTR); if (n == 0) errno = ETIMEDOUT; if (n <= 0) return SA_ERR_SYS; } sa_len = sizeof(sa_buf); if ((n = recvfrom(sa->sSocket, buf, buflen, 0, (struct sockaddr *)&sa_buf, &sa_len)) == -1) return SA_ERR_SYS; if ((rv = sa_addr_create(raddr)) != SA_OK) return rv; if ((rv = sa_addr_s2a(*raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) { sa_addr_destroy(*raddr); return rv; } if (bufdone != NULL) *bufdone = n; return SA_OK; } sa_rc_t sa_readline(sa_t *sa, char *cpBuf, size_t nBufReq, size_t *nBufRes) d1146 2 a1147 1 if (sa == NULL) d1149 2 d1153 3 a1155 1 if (sa->sSocket == -1) d1157 5 d1172 2 d1176 1 d1180 1 d1186 2 d1190 1 a1190 1 FD_SET(sa->sSocket, &fds); d1192 1 a1192 1 rv = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout); d1199 2 d1202 1 a1202 1 rv = write(sa->sSocket, cpBuf, nBufLen); d1204 1 d1208 1 d1215 2 a1216 1 if (sa == NULL) d1218 2 d1222 3 a1224 1 if (sa->sSocket == -1) d1229 1 a1229 1 /* unbuffered I/O */ d1231 1 a1231 2 if (res < 0) { res = 0; a1232 1 } d1235 1 a1235 1 /* buffered I/O */ d1237 1 a1237 1 /* not enough space in buffer, so flush buffer */ d1246 1 a1246 1 rv = SA_ERR_SYS; d1261 2 d1265 1 d1269 2 a1270 1 sa_rc_t sa_writeto(sa_t *sa, const char *buf, size_t buflen, size_t *bufdone, sa_addr_t *raddr) d1272 1 d1274 1 a1274 1 fd_set fds; d1277 2 a1278 1 if (sa == NULL || buf == NULL || buflen == 0 || raddr == NULL) d1280 3 a1282 1 if (sa->eType != SA_TYPE_DATAGRAM) d1284 3 a1286 4 if (sa->sSocket == -1) if ((rv = sa_socket_init(sa, raddr->nFamily)) != SA_OK) return rv; if (sa->sSocket == -1) a1287 23 if (sa->bTimeout) { FD_ZERO(&fds); FD_SET(sa->sSocket, &fds); do { n = select(sa->sSocket+1, NULL, &fds, NULL, &sa->tvTimeout); } while (n == -1 && errno == EINTR); if (n == 0) errno = ETIMEDOUT; if (n <= 0) return SA_ERR_SYS; } if ((n = sendto(sa->sSocket, buf, buflen, 0, raddr->saBuf, raddr->slBuf)) == -1) return SA_ERR_SYS; if (bufdone != NULL) *bufdone = n; return SA_OK; } sa_rc_t sa_writef(sa_t *sa, const char *cpFmt, ...) { va_list ap; size_t n; char caBuf[1024]; d1289 1 a1289 2 if (sa == NULL) return SA_ERR_ARG; a1291 1 sa_write(sa, caBuf, n, &n); d1293 5 a1297 1 return SA_OK; d1300 1 d1304 1 d1306 1 d1309 2 d1313 3 a1315 1 if (sa->sSocket == -1) d1317 3 d1323 2 d1332 1 a1332 1 return SA_OK; d1335 1 d1340 1 d1343 2 d1347 3 a1349 1 if (sa->sSocket == -1) d1351 2 d1360 107 a1466 1 if (shutdown(sa->sSocket, how) == -1) d1468 5 @ 1.6 log @Support fully unbuffered I/O and make this the default. @ text @d685 6 a690 2 if (!sa->bTimeout) return connect(sa->sSocket, raddr->saBuf, raddr->slBuf); @ 1.5 log @use snprintf(3) if exists @ text @d534 2 d537 2 a538 5 sa->nReadSize = 1024; if ((sa->cpReadBuf = (char *)malloc(sa->nReadSize)) == NULL) { free(sa); return SA_ERR_MEM; } d540 2 a541 6 sa->nWriteSize = 1024; if ((sa->cpWriteBuf = (char *)malloc(sa->nWriteSize)) == NULL) { free(sa->cpReadBuf); free(sa); return SA_ERR_MEM; } d594 32 a625 8 if ((cp = (char *)realloc(sa->cpReadBuf, rsize)) == NULL) return SA_ERR_SYS; sa->cpReadBuf = cp; sa->nReadSize = rsize; if ((cp = (char *)realloc(sa->cpWriteBuf, wsize)) == NULL) return SA_ERR_SYS; sa->cpWriteBuf = cp; sa->nWriteSize = wsize; d899 1 a899 1 int rv; d912 2 d915 16 a930 25 res = 0; while (1) { if (nBufReq <= sa->nReadLen) { /* buffer holds enough data, so use this */ memmove(cpBuf, sa->cpReadBuf, nBufReq); memmove(sa->cpReadBuf, sa->cpReadBuf+nBufReq, sa->nReadLen-nBufReq); sa->nReadLen -= nBufReq; res += nBufReq; } else { if (sa->nReadLen > 0) { /* fetch already existing buffer contents as a start */ memmove(cpBuf, sa->cpReadBuf, sa->nReadLen); nBufReq -= sa->nReadLen; cpBuf += sa->nReadLen; res += sa->nReadLen; sa->nReadLen = 0; } if (nBufReq >= sa->nReadSize) { /* buffer is too small at all, so read directly */ n = sa_read_raw(sa, cpBuf, nBufReq); if (n > 0) res += n; else if (n <= 0) rv = SA_ERR_SYS; d933 16 a948 4 /* fill buffer with new data */ n = sa_read_raw(sa, sa->cpReadBuf, sa->nReadSize); if (n <= 0) rv = SA_ERR_SYS; d950 8 a957 2 sa->nReadLen = n; continue; d960 1 a961 1 break; d1065 1 d1074 7 a1080 14 if (nBufReq > (sa->nWriteSize - sa->nWriteLen)) { /* not enough space in buffer, so flush buffer */ sa_flush(sa); } res = 0; if (nBufReq >= sa->nWriteSize) { /* buffer too small at all, so write immediately */ while (nBufReq > 0) { n = sa_write_raw(sa, cpBuf, nBufReq); if (n <= 0) break; nBufReq -= n; cpBuf += n; res += n; d1084 25 a1108 4 /* (again) enough sprace in buffer, so store data */ memmove(sa->cpWriteBuf+sa->nWriteLen, cpBuf, nBufReq); sa->nWriteLen += nBufReq; res = nBufReq; d1112 1 a1112 1 return SA_OK; d1173 9 a1181 6 while (sa->nWriteLen > 0) { n = sa_write_raw(sa, sa->cpWriteBuf, sa->nWriteLen); if (n <= 0) break; memmove(sa->cpWriteBuf, sa->cpWriteBuf+n, sa->nWriteLen-n); sa->nWriteLen -= n; a1182 1 sa->nWriteLen = 0; @ 1.4 log @Major revamp of SA library in order to support Unix Domain sockets. - "{tcp,udp}://host:port" addresses are now "inet://host:port" addresses - "unix:/path/to/socket" is the address for Unix Domain sockets - sa_type() was introduced which sets socket type to stream or datagram @ text @d163 5 a167 1 sprintf(ibuf, "%d", d); @ 1.3 log @Change sa_addr_t API: - there is now a constructor and destructor - renamed sa_x2x functions to sa_addr_x2x @ text @d58 1 a60 2 int nFamily; int nProto; d65 1 a201 1 (*saa)->nProto = 0; d221 1 a227 1 struct protoent *pe; d230 1 a231 1 int nProto; d234 1 d248 1 a248 1 /* parse URI into protocol, host and port parts */ d250 2 a251 2 cpProto = "tcp"; if ((cp = strstr(uri, "://")) != NULL) { d254 1 a254 1 uri = cp+3; d256 16 a271 10 cpHost = (char *)uri; if ((cp = strchr(uri, ':')) == NULL) return SA_ERR_ARG; *cp++ = '\0'; cpPort = cp; /* resolve protocol */ if ((pe = getprotobyname(cpProto)) == NULL) return SA_ERR_SYS; nProto = pe->p_proto; d274 15 a288 5 bNumeric = 1; for (i = 0; cpPort[i] != '\0'; i++) { if (!isdigit((int)cpPort[i])) { bNumeric = 0; break; a290 7 if (bNumeric) nPort = atoi(cpPort); else { if ((se = getservbyname(cpPort, cpProto)) == NULL) return SA_ERR_SYS; nPort = ntohs(se->s_port); } d293 1 d304 11 a314 14 if (inet_pton(AF_INET, cpHost, &sa4.sin_addr.s_addr) == 1) { sa4.sin_family = AF_INET; sa4.sin_port = htons(nPort); sa = (struct sockaddr *)&sa4; sl = sizeof(sa4); sf = AF_INET; } #ifdef AF_INET6 else if (inet_pton(AF_INET6, cpHost, &sa6.sin6_addr.s6_addr) == 1) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(nPort); sa = (struct sockaddr *)&sa6; sl = sizeof(sa6); sf = AF_INET6; d316 2 a317 3 #endif else if ((he = gethostbyname(cpHost)) != NULL) { if (he->h_addrtype == AF_INET) { a319 1 memcpy(&sa4.sin_addr.s_addr, he->h_addr_list[0], sizeof(sa4.sin_addr.s_addr)); d325 1 a325 1 else if (he->h_addrtype == AF_INET6) { a327 1 memcpy(&sa6.sin6_addr.s6_addr, he->h_addr_list[0], sizeof(sa6.sin6_addr.s6_addr)); d333 20 a362 1 saa->nProto = nProto; d368 1 a372 1 struct protoent *pe; d386 6 a391 1 if (sizeof(struct sockaddr_in) == salen) { d397 1 a397 1 else if (sizeof(struct sockaddr_in6) == salen) { d403 2 a406 5 /* fill in protocol */ if ((pe = getprotobyname("tcp")) != NULL) saa->nProto = pe->p_proto; else saa->nProto = 0; d413 1 a413 1 struct protoent *pe; d423 3 a425 6 if ((pe = getprotobynumber(saa->nProto)) == NULL) return SA_ERR_SYS; if (saa->nFamily == AF_INET) { sa4 = (struct sockaddr_in *)((void *)saa->saBuf); inet_ntop(AF_INET, &sa4->sin_addr.s_addr, caHost, sizeof(caHost)); nPort = ntohs(sa4->sin_port); d427 6 d434 9 a442 4 else if (saa->nFamily == AF_INET6) { sa6 = (struct sockaddr_in6 *)((void *)saa->saBuf); inet_ntop(AF_INET6, &sa6->sin6_addr.s6_addr, caHost, sizeof(caHost)); nPort = ntohs(sa6->sin6_port); a443 4 #endif else return SA_ERR_ARG; sa_msnprintf(uribuf, sizeof(uribuf), "%s://%s:%d", pe->p_name, caHost, nPort); d460 1 a460 1 static sa_rc_t sa_socket_init(sa_t *sa, int family, int proto) d462 4 a465 1 int type; d469 10 a478 4 if (proto == IPPROTO_TCP) type = SOCK_STREAM; else if (proto == IPPROTO_UDP) type = SOCK_DGRAM; d481 24 a504 3 if (sa->sSocket != -1) return SA_ERR_USE; if ((sa->sSocket = socket(family, type, proto)) == -1) d506 1 d527 1 d560 10 d631 1 d636 1 a636 1 if ((rv = sa_socket_init(sa, laddr->nFamily, laddr->nProto)) != SA_OK) d638 4 d656 2 a657 1 d659 1 a659 1 if ((rv = sa_socket_init(sa, raddr->nFamily, raddr->nProto)) != SA_OK) d728 2 d754 2 d801 2 d827 2 d845 2 d880 6 a885 2 return 0; d949 4 d986 4 d1034 4 d1070 1 d1074 7 d1120 4 d1141 4 @ 1.2 log @- Use an own minimal snprintf(3) - Rename sa_printf to sa_writef @ text @d193 24 a216 1 sa_rc_t sa_u2a(sa_addr_t **saa, const char *uri, ...) d334 1 a334 1 if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL) d336 4 a339 8 if (((*saa)->saBuf = (struct sockaddr *)malloc(sl)) == NULL) { free(*saa); return SA_ERR_MEM; } memcpy((*saa)->saBuf, sa, sl); (*saa)->slBuf = sl; (*saa)->nFamily = sf; (*saa)->nProto = nProto; d343 1 a343 1 sa_rc_t sa_s2a(sa_addr_t **saa, const struct sockaddr *sabuf, socklen_t salen) d356 1 a356 4 if ((*saa = (sa_addr_t *)malloc(sizeof(sa_addr_t))) == NULL) return SA_ERR_MEM; if (((*saa)->saBuf = (struct sockaddr *)malloc(salen)) == NULL) { free(*saa); d358 2 a359 3 } memcpy((*saa)->saBuf, sabuf, salen); (*saa)->slBuf = salen; d375 1 a375 1 (*saa)->nFamily = sf; d379 1 a379 1 (*saa)->nProto = pe->p_proto; d381 1 a381 1 (*saa)->nProto = 0; d385 1 a385 1 sa_rc_t sa_a2u(const sa_addr_t *saa, char **uri) d419 1 a419 1 sa_rc_t sa_a2s(const sa_addr_t *saa, struct sockaddr **sabuf, socklen_t *salen) d692 4 a695 1 if ((rv = sa_s2a(caddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) d697 1 d699 1 a699 1 free(*caddr); d722 1 a722 1 if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) d724 4 d747 4 a750 1 if ((rv = sa_s2a(laddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) d752 1 d872 4 a875 1 if ((rv = sa_s2a(raddr, (struct sockaddr *)&sa_buf, sa_len)) != SA_OK) d877 1 @ 1.1 log @Initial revision @ text @d132 2 a133 5 /* make sure vsnprintf() exists */ #if defined(HAVE_VSNPRINTF) #define sa_vsnprintf vsnprintf #elif defined(HAVE_VSPRINTF) static int sa_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) d135 41 a175 6 int rv; rv = vsprintf(str, fmt, ap); if (rv > size) { fprintf(stderr, "ERROR: vsprintf(3) buffer overflow!\n"); abort(); d177 2 a179 3 #else #error "neither vsnprintf nor vsprintf available" #endif d181 2 a182 5 /* make sure snprintf() exists */ #if defined(HAVE_SNPRINTF) #define sa_snprintf snprintf #else static int sa_snprintf(char *str, size_t size, const char *fmt, ...) d184 1 a185 1 int rv; d187 2 a188 2 va_start(ap, fmt); rv = sa_vsnprintf(str, size, fmt, ap); d190 1 a190 1 return rv; a191 1 #endif d222 1 a222 1 sa_vsnprintf(uribuf, sizeof(uribuf), uri, ap); d399 1 a399 1 sa_snprintf(uribuf, sizeof(uribuf), "%s://%s:%d", pe->p_name, caHost, nPort); d520 22 d957 1 a957 1 sa_rc_t sa_printf(sa_t *sa, const char *cpFmt, ...) d966 1 a966 1 n = sa_vsnprintf(caBuf, sizeof(caBuf), cpFmt, ap); @ 1.1.1.1 log @Import first cut of OSSP SA @ text @@