From 7ead4dce463c76a42b50aa3c7581fc88eea15f8d Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Mon, 16 Aug 2010 19:41:05 +0000 Subject: [PATCH] * Initial work on IPv6-enabling citserver --- citadel/locate_host.c | 5 ++ citadel/serv_extensions.c | 15 ++-- citadel/sysdep.c | 175 ++++++++++++++++++++++---------------- citadel/sysdep_decls.h | 4 +- 4 files changed, 115 insertions(+), 84 deletions(-) diff --git a/citadel/locate_host.c b/citadel/locate_host.c index 0331d82df..d8a2c5b91 100644 --- a/citadel/locate_host.c +++ b/citadel/locate_host.c @@ -49,6 +49,11 @@ void locate_host(char *tbuf, size_t n, char *abuf, size_t na, int client_socket) getpeername(client_socket, (struct sockaddr *)&clientaddr, &addrlen); getnameinfo((struct sockaddr *)&clientaddr, addrlen, tbuf, n, NULL, 0, 0); getnameinfo((struct sockaddr *)&clientaddr, addrlen, abuf, na, NULL, 0, NI_NUMERICHOST); + + /* Convert IPv6-mapped IPv4 addresses back to traditional dotted quad */ + if ( (strlen(abuf) > 7) && (!strncasecmp(abuf, "::ffff:", 7)) ) { + strcpy(abuf, &abuf[7]); + } } diff --git a/citadel/serv_extensions.c b/citadel/serv_extensions.c index 0617cce0a..c74dcb6d3 100644 --- a/citadel/serv_extensions.c +++ b/citadel/serv_extensions.c @@ -727,12 +727,11 @@ void CtdlRegisterServiceHook(int tcp_port, { struct ServiceFunctionHook *newfcn; char *message; - char *error; + char error[SIZ]; - error = NULL; - newfcn = (struct ServiceFunctionHook *) - malloc(sizeof(struct ServiceFunctionHook)); - message = (char*) malloc (SIZ); + strcpy(error, ""); + newfcn = (struct ServiceFunctionHook *) malloc(sizeof(struct ServiceFunctionHook)); + message = (char*) malloc (SIZ + SIZ); newfcn->next = ServiceHookTable; newfcn->tcp_port = tcp_port; @@ -743,7 +742,7 @@ void CtdlRegisterServiceHook(int tcp_port, newfcn->ServiceName = ServiceName; if (sockpath != NULL) { - newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions, &error); + newfcn->msock = ctdl_uds_server(sockpath, config.c_maxsessions, error); snprintf(message, SIZ, "Unix domain socket '%s': ", sockpath); } else if (tcp_port <= 0) { /* port -1 to disable */ @@ -753,10 +752,10 @@ void CtdlRegisterServiceHook(int tcp_port, return; } else { - newfcn->msock = ig_tcp_server(config.c_ip_addr, + newfcn->msock = ctdl_tcp_server(config.c_ip_addr, tcp_port, config.c_maxsessions, - &error); + error); snprintf(message, SIZ, "TCP port %s:%d: (%s) ", config.c_ip_addr, tcp_port, ServiceName); } diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 7da7aa547..f4ac8d70b 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -257,90 +257,121 @@ void init_sysdep(void) { } - - -/* +/* * This is a generic function to set up a master socket for listening on - * a TCP port. The server shuts down if the bind fails. + * a TCP port. The server shuts down if the bind fails. (IPv4/IPv6 version) * + * ip_addr IP address to bind + * port_number port number to bind + * queue_len number of incoming connections to allow in the queue */ -int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormessage) +int ctdl_tcp_server(char *ip_addr, int port_number, int queue_len, char *errormessage) { - struct sockaddr_in sin; - int s, i; - int actual_queue_len; - - actual_queue_len = queue_len; - if (actual_queue_len < 5) actual_queue_len = 5; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons((u_short)port_number); - if (ip_addr == NULL) { - sin.sin_addr.s_addr = INADDR_ANY; + struct protoent *p; + struct sockaddr_in6 sin6; + struct sockaddr_in sin4; + int s, i, b; + int ip_version = 6; + + memset(&sin6, 0, sizeof(sin6)); + memset(&sin4, 0, sizeof(sin4)); + sin6.sin6_family = AF_INET6; + sin4.sin_family = AF_INET; + + if ( (ip_addr == NULL) /* any IPv6 */ + || (IsEmptyStr(ip_addr)) + || (!strcmp(ip_addr, "*")) + ) { + ip_version = 6; + sin6.sin6_addr = in6addr_any; } - else { - sin.sin_addr.s_addr = inet_addr(ip_addr); + else if (!strcmp(ip_addr, "0.0.0.0")) /* any IPv4 */ + { + ip_version = 4; + sin4.sin_addr.s_addr = INADDR_ANY; } - - if (sin.sin_addr.s_addr == !INADDR_ANY) { - sin.sin_addr.s_addr = INADDR_ANY; + else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) /* specific IPv4 */ + { + ip_version = 4; + if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) { + snprintf(errormessage, SIZ, + "Error binding to [%s] : %s", ip_addr, strerror(errno) + ); + CtdlLogPrintf(CTDL_ALERT, "%s\n", errormessage); + return (-1); + } + } + else /* specific IPv6 */ + { + ip_version = 6; + if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) { + snprintf(errormessage, SIZ, + "Error binding to [%s] : %s", ip_addr, strerror(errno) + ); + CtdlLogPrintf(CTDL_ALERT, "%s\n", errormessage); + return (-1); + } } - s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (port_number == 0) { + snprintf(errormessage, SIZ, + "Can't start: no port number specified." + ); + CtdlLogPrintf(CTDL_ALERT, "%s\n", errormessage); + return (-1); + } + sin6.sin6_port = htons((u_short) port_number); + sin4.sin_port = htons((u_short) port_number); + + p = getprotobyname("tcp"); + s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto)); if (s < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, - "citserver: Can't create a socket: %s", - strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); - return(-1); + snprintf(errormessage, SIZ, + "Can't create a listening socket: %s", strerror(errno) + ); + CtdlLogPrintf(CTDL_ALERT, "%s\n", errormessage); + return (-1); } - + /* Set some socket options that make sense. */ i = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); - if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, - "citserver: Can't bind: %s", - strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); - close(s); - return(-1); + if (ip_version == 6) { + b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6)); } - - /* set to nonblock - we need this for some obscure situations */ - if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, - "citserver: Can't set socket to non-blocking: %s", - strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); - close(s); - return(-1); + else { + b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4)); } - if (listen(s, actual_queue_len) < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, - "citserver: Can't listen: %s", - strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); - close(s); - return(-1); + if (b < 0) { + snprintf(errormessage, SIZ, + "Can't bind: %s", strerror(errno) + ); + CtdlLogPrintf(CTDL_ALERT, "%s\n", errormessage); + return (-1); } - return(s); + fcntl(s, F_SETFL, O_NONBLOCK); + + if (listen(s, ((queue_len >= 5) ? queue_len : 5) ) < 0) { + snprintf(errormessage, SIZ, + "Can't listen: %s", strerror(errno) + ); + CtdlLogPrintf(CTDL_ALERT, "%s\n", errormessage); + return (-1); + } + return (s); } + + /* * Create a Unix domain socket and listen on it */ -int ig_uds_server(char *sockpath, int queue_len, char **errormessage) +int ctdl_uds_server(char *sockpath, int queue_len, char *errormessage) { struct sockaddr_un addr; int s; @@ -355,10 +386,10 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) i = unlink(sockpath); if ((i != 0) && (errno != ENOENT)) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, "citserver: can't unlink %s: %s", - sockpath, strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); + snprintf(errormessage, SIZ, "citserver: can't unlink %s: %s", + sockpath, strerror(errno) + ); + CtdlLogPrintf(CTDL_EMERG, "%s\n", errormessage); return(-1); } @@ -368,40 +399,36 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, + snprintf(errormessage, SIZ, "citserver: Can't create a socket: %s", strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", errormessage); return(-1); } if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, + snprintf(errormessage, SIZ, "citserver: Can't bind: %s", strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", errormessage); return(-1); } /* set to nonblock - we need this for some obscure situations */ if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, + snprintf(errormessage, SIZ, "citserver: Can't set socket to non-blocking: %s", strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", errormessage); close(s); return(-1); } if (listen(s, actual_queue_len) < 0) { - *errormessage = (char*) malloc(SIZ + 1); - snprintf(*errormessage, SIZ, + snprintf(errormessage, SIZ, "citserver: Can't listen: %s", strerror(errno)); - CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", errormessage); return(-1); } diff --git a/citadel/sysdep_decls.h b/citadel/sysdep_decls.h index 3eaccf20f..1a80dee2f 100644 --- a/citadel/sysdep_decls.h +++ b/citadel/sysdep_decls.h @@ -55,8 +55,8 @@ extern int enable_syslog; extern int print_to_logfile; void init_sysdep (void); -int ig_tcp_server (char *ip_addr, int port_number, int queue_len,char **errormessage); -int ig_uds_server(char *sockpath, int queue_len, char **errormessage); +int ctdl_tcp_server(char *ip_addr, int port_number, int queue_len, char *errormessage); +int ctdl_uds_server(char *sockpath, int queue_len, char *errormessage); void buffer_output(void); void unbuffer_output(void); void flush_output(void); -- 2.30.2