From 81a70dbb01015530fe1446c8421ca21651f7fc66 Mon Sep 17 00:00:00 2001 From: deuce <> Date: Sat, 31 Aug 2013 02:40:37 +0000 Subject: [PATCH] Add a new multiple listening socket API. Takes a hostname or address as a string, and listen()s on all the associated addresses in all families. Untested on Win32, and MSDN says some weird dance is required to support older (than Windows XP) OSs. Despite the face that these OSs are all officially dead as doornails, I'm sure a lively argument will spring up regarding if we'll support them. --- src/xpdev/multisock.c | 171 ++++++++++++++++++++++++++++++++++++++++++ src/xpdev/multisock.h | 37 +++++++++ src/xpdev/objects.mk | 2 + 3 files changed, 210 insertions(+) create mode 100644 src/xpdev/multisock.c create mode 100644 src/xpdev/multisock.h diff --git a/src/xpdev/multisock.c b/src/xpdev/multisock.c new file mode 100644 index 0000000000..3ca1b24d06 --- /dev/null +++ b/src/xpdev/multisock.c @@ -0,0 +1,171 @@ +// Multi-socket versions ofthe socket API... + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gen_defs.h> +#include <sockwrap.h> +#include <multisock.h> + +struct xpms_set *xpms_create(unsigned int retries, unsigned int wait_secs, + int (*lprintf)(int level, const char *fmt, ...)) +{ + struct xpms_set *ret=(struct xpms_set *)calloc(1, sizeof(struct xpms_set)); + + if(ret==NULL) + return ret; + ret->retries = retries; + ret->wait_secs = wait_secs; + ret->lprintf=lprintf; + return ret; +} + +void xpms_destroy(struct xpms_set *xpms_set) +{ + int i; + + for(i=0; i<xpms_set->sock_count; i++) { + if(xpms_set->socks[i].sock != INVALID_SOCKET) { + if(xpms_set->lprintf!=NULL) + xpms_set->lprintf(LOG_INFO, "%04d closing %s socket on port %d" + , xpms_set->socks[i].sock, xpms_set->socks[i].prot?xpms_set->socks[i].prot:"unknown" + , xpms_set->socks[i].port); + closesocket(xpms_set->socks[i].sock); + } + xpms_set->socks[i].sock = INVALID_SOCKET; + FREE_AND_NULL(xpms_set->socks[i].address); + FREE_AND_NULL(xpms_set->socks[i].prot); + } + FREE_AND_NULL(xpms_set->socks); + free(xpms_set); +} + +BOOL xpms_add(struct xpms_set *xpms_set, int domain, int type, + int protocol, const char *addr, uint16_t port, const char *prot, + void *cbdata) +{ + struct xpms_sockdef *new_socks; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *cur; + unsigned int added = 0; + int ret; + char port_str[6]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags=AI_PASSIVE; + hints.ai_family=domain; + hints.ai_socktype=type; + hints.ai_protocol=protocol; + hints.ai_flags=AI_NUMERICSERV; +#ifdef AI_ADDRCONFIG + hints.ai_flags|=AI_ADDRCONFIG; +#endif + sprintf(port_str, "%hu", port); + if((ret=getaddrinfo(addr, port_str, &hints, &res))!=0) { + if(xpms_set->lprintf) + xpms_set->lprintf(LOG_CRIT, "!ERROR %d calling getaddrinfo() on %s", ret, addr); + return FALSE; + } + + for(cur=res; cur; cur=cur->ai_next) { + new_socks=(struct xpms_sockdef *)realloc(xpms_set->socks, sizeof(struct xpms_set)*xpms_set->sock_count+1); + if(new_socks==NULL) { + /* This may be a partial failure */ + if(xpms_set->lprintf) + xpms_set->lprintf(LOG_CRIT, "!ERROR out of memory adding to multisocket"); + if(added==0) + return FALSE; + return TRUE; + } + xpms_set->socks=new_socks; + xpms_set->socks[xpms_set->sock_count].address = strdup(addr); + xpms_set->socks[xpms_set->sock_count].cb_data = cbdata; + xpms_set->socks[xpms_set->sock_count].domain = cur->ai_family; /* Address/Protocol Family */ + xpms_set->socks[xpms_set->sock_count].type = cur->ai_socktype; + xpms_set->socks[xpms_set->sock_count].protocol = protocol; + xpms_set->socks[xpms_set->sock_count].port = port; + xpms_set->socks[xpms_set->sock_count].prot = strdup(prot); + xpms_set->socks[xpms_set->sock_count].sock = socket(cur->ai_family, cur->ai_socktype, protocol); + if(xpms_set->socks[xpms_set->sock_count].sock == INVALID_SOCKET) { + FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].address); + FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].prot); + continue; + } + + if(retry_bind(xpms_set->socks[xpms_set->sock_count].sock, cur->ai_addr, cur->ai_addrlen, xpms_set->retries, xpms_set->wait_secs, prot, xpms_set->lprintf)==-1) { + closesocket(xpms_set->socks[xpms_set->sock_count].sock); + FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].address); + FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].prot); + continue; + } + + if(!listen(xpms_set->socks[xpms_set->sock_count].sock, SOMAXCONN)) { + if(xpms_set->lprintf) + xpms_set->lprintf(LOG_WARNING, "%04d !ERROR %d listen()ing op port %d" + , xpms_set->socks[xpms_set->sock_count].sock, ERROR_VALUE, port); + closesocket(xpms_set->socks[xpms_set->sock_count].sock); + FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].address); + FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].prot); + continue; + } + + added++; + xpms_set->sock_count++; + } + + if(added) + return TRUE; + return FALSE; +} + +SOCKET xpms_accept(struct xpms_set *xpms_set, struct sockaddr * addr, + socklen_t * addrlen, unsigned int timeout, void **cb_data) +{ + fd_set read_fs; + fd_set except_fs; + int i; + struct timeval tv; + struct timeval *tvp; + SOCKET max_sock=0; + + FD_ZERO(&read_fs); + for(i=0; i<xpms_set->sock_count; i++) { + if(xpms_set->socks[i].sock == INVALID_SOCKET) + continue; + FD_SET(xpms_set->socks[i].sock, &read_fs); + FD_SET(xpms_set->socks[i].sock, &except_fs); + if(xpms_set->socks[i].sock > max_sock) + max_sock=xpms_set->socks[i].sock+1; + } + + if(timeout==XPMS_FOREVER) + tvp=NULL; + else { + tv.tv_sec=timeout/1000; + tv.tv_usec=(timeout%1000)*1000; + tvp=&tv; + } + switch(select(max_sock, &read_fs, NULL, &except_fs, tvp)) { + case 0: + return INVALID_SOCKET; + case -1: + return SOCKET_ERROR; + default: + for(i=0; i<xpms_set->sock_count; i++) { + if(xpms_set->socks[i].sock == INVALID_SOCKET) + continue; + if(FD_ISSET(xpms_set->socks[i].sock, &read_fs)) { + if(cb_data) + *cb_data=xpms_set->socks[i].cb_data; + return accept(xpms_set->socks[i].sock, addr, addrlen); + } + if(FD_ISSET(xpms_set->socks[i].sock, &except_fs)) { + closesocket(xpms_set->socks[i].sock); + xpms_set->socks[i].sock = INVALID_SOCKET; + } + } + } + + return INVALID_SOCKET; +} diff --git a/src/xpdev/multisock.h b/src/xpdev/multisock.h new file mode 100644 index 0000000000..2441b979b1 --- /dev/null +++ b/src/xpdev/multisock.h @@ -0,0 +1,37 @@ +#ifndef MULTISOCK_H +#define MULTISOCK_H + +#include <sys/limits.h> + +struct xpms_sockdef +{ + void *cb_data; + int domain; + int type; + int protocol; + SOCKET sock; + char *address; + uint16_t port; + char *prot; +}; + +struct xpms_set { + struct xpms_sockdef *socks; + int (*lprintf)(int level, const char *fmt, ...); + size_t sock_count; + unsigned int retries; + unsigned int wait_secs; +}; + +#define XPMS_FOREVER UINT_MAX + +struct xpms_set *xpms_create(unsigned int retries, unsigned int wait_secs, + int (*lprintf)(int level, const char *fmt, ...)); +void xpms_destroy(struct xpms_set *xpms_set); +BOOL xpms_add(struct xpms_set *xpms_set, int domain, int type, + int protocol, const char *addr, uint16_t port, const char *prot, + void *cbdata); +SOCKET xpms_accept(struct xpms_set *, struct sockaddr * addr, + socklen_t * addrlen, unsigned int timeout, void **cb_data); + +#endif diff --git a/src/xpdev/objects.mk b/src/xpdev/objects.mk index ec0e4dcf2d..6cccb5385a 100644 --- a/src/xpdev/objects.mk +++ b/src/xpdev/objects.mk @@ -15,6 +15,7 @@ OBJS = \ $(OBJODIR)$(DIRSEP)genwrap$(OFILE) \ $(OBJODIR)$(DIRSEP)ini_file$(OFILE) \ $(OBJODIR)$(DIRSEP)link_list$(OFILE) \ + $(OBJODIR)$(DIRSEP)multisock$(OFILE) \ $(OBJODIR)$(DIRSEP)netwrap$(OFILE) \ $(OBJODIR)$(DIRSEP)sockwrap$(OFILE) \ $(OBJODIR)$(DIRSEP)semfile$(OFILE) \ @@ -37,6 +38,7 @@ MTOBJS = \ $(MTOBJODIR)$(DIRSEP)ini_file$(OFILE) \ $(MTOBJODIR)$(DIRSEP)link_list$(OFILE) \ $(MTOBJODIR)$(DIRSEP)msg_queue$(OFILE) \ + $(MTOBJODIR)$(DIRSEP)multisock$(OFILE) \ $(MTOBJODIR)$(DIRSEP)semwrap$(OFILE) \ $(MTOBJODIR)$(DIRSEP)netwrap$(OFILE) \ $(MTOBJODIR)$(DIRSEP)sockwrap$(OFILE) \ -- GitLab