/* ** L2 - OSSP Logging Library ** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) ** ** This file is part of OSSP L2, a flexible logging library which ** can be found at http://www.ossp.org/pkg/l2/. ** ** 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. ** ** l2_ch_socket.c: socket channel implementation */ #include #include #include #include #include #include #include #include "l2.h" #include "l2_p.h" #if defined(HAVE_INET_PTON) #define l2_inet_pton inet_pton #elif defined(HAVE_INET_ATON) /****************************************************************** IPv4 source used from Unix Network Programming by Richard STEVENS ******************************************************************/ static int l2_inet_pton(int iFamily, const char *pszAddress, void *pvMemref) { int i = 0; struct in_addr IP4Addr; struct in6_addr IP6Addr; const char *pszIndex = NULL; /* To index IPv6 validity */ char *pszNextfield = ""; /* For IPv6 address trans */ if (iFamily == AF_INET) { if (inet_aton(pszAddress, &IP4Addr)) { memcpy(pvMemref, &IP4Addr, sizeof(struct in_addr)); return 1; /* Success */ } return 0; } else if (iFamily == AF_INET6) { /* Translate IPv6 addresses */ pszIndex = pszAddress; /* Initialize, verify in loop */ for (i = 0; (i < 8) && pszNextfield; i++) { /* Iterate through fields */ IP6Addr.__u6_addr.__u6_addr16[i] =\ ntohs((u_int16_t)strtol(pszIndex, &pszNextfield, 16)); pszIndex = pszNextfield + 1; } if ((i != 8) || (*pszNextfield)) return 0; else { /* Success */ memcpy(pvMemref, &IP6Addr, sizeof(struct in6_addr)); return 1; } } else { /* User converting from an address family that we don't support */ errno = EAFNOSUPPORT; return -1; } } #else #error "neither inet_pton nor inet_aton available" #endif /* declare private channel configuration */ typedef struct { char *pszHost; /* IP Address or name of host to connect to */ int iFamily; /* IP version to use, AF_INET4 or AF_INET6 */ int iProto; /* Protocol to use, IPPROTO_TCP or IPPROTO_UDP */ int iPort; /* TCP Port to connect to */ int iSocket; /* Socket descriptor used during writing */ } l2_ch_socket_t; /* create channel */ static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_socket_t *cfg = NULL; /* allocate private channel configuration */ if ((cfg = (l2_ch_socket_t *)malloc(sizeof(l2_ch_socket_t))) == NULL) return L2_ERR_MEM; /* initialize configuration with reasonable defaults */ cfg->pszHost = NULL; cfg->iFamily = -1; cfg->iProto = -1; cfg->iPort = 0; cfg->iSocket = -1; /* link private channel configuration into channel context */ ctx->vp = cfg; return L2_OK; } /* configure channel */ static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list ap) { l2_ch_socket_t *cfg = (l2_ch_socket_t *)ctx->vp; l2_param_t pa[5]; l2_result_t rv; /* feed and call generic parameter parsing engine */ L2_PARAM_SET(pa[0], ipversion, INT, &cfg->iFamily); L2_PARAM_SET(pa[1], protocol, INT, &cfg->iProto ); L2_PARAM_SET(pa[2], host, CHARPTR, &cfg->pszHost); L2_PARAM_SET(pa[3], port, INT, &cfg->iPort ); L2_PARAM_END(pa[4]); rv = l2_util_setparams(pa, fmt, ap); return rv; } /* open channel */ static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_socket_t *cfg = (l2_ch_socket_t *)ctx->vp; struct hostent *pHostentry; struct sockaddr_in IP4Localsock, IP4Sockaddr; struct sockaddr_in6 IP6Localsock, IP6Sockaddr; /* make sure a target is configured */ if (cfg->pszHost == NULL) return L2_ERR_USE; /* open channel socket */ if (cfg->iFamily == AF_INET) { /* IPv4 */ /* initialize address structures */ memset(&IP4Localsock, 0, sizeof(IP4Localsock)); memset(&IP4Sockaddr, 0, sizeof(IP4Sockaddr)); /* resolve host numerically */ if (l2_inet_pton(AF_INET, cfg->pszHost, &IP4Sockaddr.sin_addr.s_addr) == 1) { IP4Sockaddr.sin_family = AF_INET; IP4Sockaddr.sin_port = htons(cfg->iPort); } /* resolve host nominally */ else if ((pHostentry = gethostbyname(cfg->pszHost)) != NULL) { if (pHostentry->h_addrtype == AF_INET) { IP4Sockaddr.sin_family = AF_INET; IP4Sockaddr.sin_port = htons(cfg->iPort); memcpy(&IP4Sockaddr.sin_addr.s_addr, pHostentry->h_addr_list[0], sizeof(IP4Sockaddr.sin_addr.s_addr)); } else return L2_ERR_USE; /* what?? we specify IPv4 when it is not? */ } else /* host string was not parsable for some reason */ return L2_ERR_SYS; if (cfg->iProto == IPPROTO_TCP) { /* with TCP we must connect */ if ((cfg->iSocket = socket(PF_INET, SOCK_STREAM, cfg->iProto)) == -1) return L2_ERR_SYS; /* if we have a socket, then bind ourselves to any port number */ IP4Localsock.sin_family = AF_INET; IP4Localsock.sin_addr.s_addr = htonl(INADDR_ANY); IP4Localsock.sin_port = htons(0); if (bind(cfg->iSocket, (struct sockaddr *)&IP4Localsock,\ sizeof(IP4Localsock))) { return L2_ERR_SYS; } if (connect(cfg->iSocket, (struct sockaddr *)&IP4Sockaddr, sizeof(IP4Sockaddr))) { close(cfg->iSocket); return L2_ERR_SYS; } } else if (cfg->iProto == IPPROTO_UDP) { /* with UDP we do not connect */ if ((cfg->iSocket = socket(PF_INET, SOCK_DGRAM, cfg->iProto)) == -1) return L2_ERR_SYS; /* if we have a socket, then bind ourselves to any port number */ IP4Localsock.sin_family = AF_INET; IP4Localsock.sin_addr.s_addr = htonl(INADDR_ANY); IP4Localsock.sin_port = htons(0); if (bind(cfg->iSocket, (struct sockaddr *)&IP4Localsock,\ sizeof(IP4Localsock))) { return L2_ERR_SYS; } } return L2_OK; } else if (cfg->iFamily == AF_INET6) { /* IPv6 */ /* initialize address structures */ memset(&IP6Localsock, 0, sizeof(IP6Localsock)); memset(&IP6Sockaddr, 0, sizeof(IP6Sockaddr)); /* resolve host numerically */ if (l2_inet_pton(AF_INET6, cfg->pszHost, &IP6Sockaddr.sin6_addr.s6_addr) == 1) { IP6Sockaddr.sin6_family = AF_INET6; IP6Sockaddr.sin6_port = htons(cfg->iPort); } /* resolve host nominally */ else if ((pHostentry = gethostbyname(cfg->pszHost)) != NULL) { if (pHostentry->h_addrtype == AF_INET6) { IP6Sockaddr.sin6_family = AF_INET6; IP6Sockaddr.sin6_port = htons(cfg->iPort); memcpy(&IP6Sockaddr.sin6_addr.__u6_addr, pHostentry->h_addr_list[0],\ sizeof(IP6Sockaddr.sin6_addr.__u6_addr)); } else return L2_ERR_USE; /* what?? we specify IPv6 when it is not? */ } else /* host string was not parsable for some reason */ return L2_ERR_SYS; if (cfg->iProto == IPPROTO_TCP) { /* with TCP we must connect */ if ((cfg->iSocket = socket(PF_INET6, SOCK_STREAM, cfg->iProto)) == -1) return L2_ERR_SYS; /* if we have a socket, then bind ourselves to any port number */ IP6Localsock.sin6_family = AF_INET6; IP6Localsock.sin6_addr = in6addr_any; IP6Localsock.sin6_port = htons(0); if (bind(cfg->iSocket, (struct sockaddr *)&IP6Localsock,\ sizeof(IP6Localsock))) { return L2_ERR_SYS; } if (connect(cfg->iSocket, (struct sockaddr *)&IP6Sockaddr,\ sizeof(IP6Sockaddr))) { close(cfg->iSocket); return L2_ERR_SYS; } } else if (cfg->iProto == IPPROTO_UDP) { /* with UDP we do not connect */ if ((cfg->iSocket = socket(PF_INET, SOCK_DGRAM, cfg->iProto)) == -1) return L2_ERR_SYS; /* if we have a socket, then bind ourselves to any port number */ IP6Localsock.sin6_family = AF_INET6; IP6Localsock.sin6_addr = in6addr_any; IP6Localsock.sin6_port = htons(0); if (bind(cfg->iSocket, (struct sockaddr *)&IP6Localsock,\ sizeof(IP6Localsock))) { return L2_ERR_SYS; } } return L2_OK; } return L2_ERR_USE; } /* write to channel */ static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch, l2_level_t level, const char *buf, size_t buf_size) { struct sockaddr_in IP4Sockaddr, IP6Sockaddr; l2_ch_socket_t *cfg = (l2_ch_socket_t *)ctx->vp; size_t sizeWrite; size_t sizeRemain; /* parameter checks */ if (cfg->iSocket == -1) return L2_ERR_ARG; if (cfg->iProto == IPPROTO_TCP) { /* write message to channel socket, but check to make */ /* sure that the whole message was successfully written */ sizeWrite = 0; sizeRemain = buf_size; while(sizeRemain) { sizeWrite = send(cfg->iSocket, buf + sizeWrite, sizeRemain, 0); sizeRemain = sizeRemain - sizeWrite; /* how much is left? */ if (sizeWrite == -1) return L2_ERR_SYS; } } else if (cfg->iProto == IPPROTO_UDP) { /* write message to the host target, but check to make */ /* sure that the whole message was successfully written */ sizeWrite = 0; sizeRemain = buf_size; while(sizeRemain) { if (cfg->iFamily == AF_INET) { /* IPv4 */ /* TODO !!! */ sizeWrite = sendto(cfg->iSocket, buf + sizeWrite, sizeRemain, 0,\ /* Fill in */ (struct sockaddr *)&IP4Sockaddr, sizeof(IP4Sockaddr)); /* the host */ /* address */ sizeRemain = sizeRemain - sizeWrite; /* how much is left? */ /* structs */ if (sizeWrite == -1) return L2_ERR_SYS; } else if (cfg->iFamily == AF_INET6) { /* IPv6 */ /* TODO !!! */ sizeWrite = sendto(cfg->iSocket, buf + sizeWrite, sizeRemain, 0,\ /* Fill in */ (struct sockaddr *)&IP6Sockaddr, sizeof(IP6Sockaddr)); /* the host */ /* address */ sizeRemain = sizeRemain - sizeWrite; /* how much is left? */ /* structs */ if (sizeWrite == -1) return L2_ERR_USE; } else /* Neither IPv4 nor IPv6 */ return L2_ERR_USE; } } return L2_OK; } /* close channel */ static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_socket_t *cfg = (l2_ch_socket_t *)ctx->vp; /* parameter checks */ if (cfg->iSocket == -1) return L2_ERR_USE; /* close channel socket */ if ((shutdown(cfg->iSocket, SHUT_RDWR)) || (close(cfg->iSocket))) return L2_ERR_SYS; return L2_OK; } /* destroy channel */ static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_socket_t *cfg = (l2_ch_socket_t *)ctx->vp; /* destroy channel configuration */ free(cfg); return L2_OK; } /* exported channel handler structure */ l2_handler_t l2_handler_socket = { L2_CHANNEL_OUTPUT, hook_create, hook_configure, hook_open, hook_write, NULL, hook_close, hook_destroy };