X-Git-Url: https://code.citadel.org/?p=citadel.git;a=blobdiff_plain;f=webcit%2Ftcp_sockets.c;h=302c908c1031da4aaf0aedf37ffc27086f7d6e61;hp=058f7c821f1588299c94a73d1e6e6641800a8c8e;hb=921ebdb7dbcdc32ab677a531b291663b95c6b6a5;hpb=6c114e1487e4b4032be88563f0f5b76922edd601 diff --git a/webcit/tcp_sockets.c b/webcit/tcp_sockets.c index 058f7c821..302c908c1 100644 --- a/webcit/tcp_sockets.c +++ b/webcit/tcp_sockets.c @@ -1,5 +1,13 @@ /* - * $Id$ + * Copyright (c) 1987-2012 by the citadel.org team + * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ /* @@ -7,27 +15,23 @@ #define SERV_TRACE 1 */ - #include "webcit.h" #include "webserver.h" -extern int DisableGzip; +long MaxRead = -1; /* should we do READ scattered or all at once? */ /* - * register the timeout - * signum signalhandler number - * \return signals + * register the timeout */ RETSIGTYPE timeout(int signum) { - lprintf(1, "Connection timed out; unable to reach citserver\n"); + syslog(LOG_WARNING, "Connection timed out; unable to reach citserver\n"); /* no exit here, since we need to server the connection unreachable thing. exit(3); */ } /* - * Connect a unix domain socket - * sockpath where to open a unix domain socket + * Client side - connect to a unix domain socket */ int uds_connectsock(char *sockpath) { @@ -40,102 +44,118 @@ int uds_connectsock(char *sockpath) s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { - lprintf(1, "Can't create socket[%s]: %s\n", - sockpath, - strerror(errno)); + syslog(LOG_WARNING, "Can't create socket [%s]: %s\n", sockpath, strerror(errno)); return(-1); } if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - lprintf(1, "Can't connect [%s]: %s\n", - sockpath, - strerror(errno)); + syslog(LOG_WARNING, "Can't connect [%s]: %s\n", sockpath, strerror(errno)); close(s); return(-1); } - return s; } /* - * Connect a TCP/IP socket - * host the host to connect to - * service the service on the host to call + * TCP client - connect to a host/port */ int tcp_connectsock(char *host, char *service) { - int fdflags; - struct hostent *phe; - struct servent *pse; - struct protoent *ppe; - struct sockaddr_in sin; - int s; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - - pse = getservbyname(service, "tcp"); - if (pse) { - sin.sin_port = pse->s_port; - } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) { - lprintf(1, "Can't get %s service entry\n", service); + struct in6_addr serveraddr; + struct addrinfo hints; + struct addrinfo *res = NULL; + struct addrinfo *ai = NULL; + int rc = (-1); + int s = (-1); + + if ((host == NULL) || IsEmptyStr(host)) return (-1); - } - phe = gethostbyname(host); - if (phe) { - memcpy(&sin.sin_addr, phe->h_addr, phe->h_length); - } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { - lprintf(1, "Can't get %s host entry: %s\n", - host, strerror(errno)); - return (-1); - } - if ((ppe = getprotobyname("tcp")) == 0) { - lprintf(1, "Can't get TCP protocol entry: %s\n", - strerror(errno)); + if ((service == NULL) || IsEmptyStr(service)) return (-1); + + syslog(LOG_DEBUG, "tcp_connectsock(%s,%s)\n", host, service); + + memset(&hints, 0x00, sizeof(hints)); + hints.ai_flags = AI_NUMERICSERV; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + /* + * Handle numeric IPv4 and IPv6 addresses + */ + rc = inet_pton(AF_INET, host, &serveraddr); + if (rc == 1) { /* dotted quad */ + hints.ai_family = AF_INET; + hints.ai_flags |= AI_NUMERICHOST; + } else { + rc = inet_pton(AF_INET6, host, &serveraddr); + if (rc == 1) { /* IPv6 address */ + hints.ai_family = AF_INET6; + hints.ai_flags |= AI_NUMERICHOST; + } } - s = socket(PF_INET, SOCK_STREAM, ppe->p_proto); - if (s < 0) { - lprintf(1, "Can't create socket: %s\n", strerror(errno)); - return (-1); + /* Begin the connection process */ + + rc = getaddrinfo(host, service, &hints, &res); + if (rc != 0) { + syslog(LOG_DEBUG, "%s: %s\n", host, gai_strerror(rc)); + freeaddrinfo(res); + return(-1); } - fdflags = fcntl(s, F_GETFL); - if (fdflags < 0) - lprintf(1, "unable to get socket flags! %s.%s: %s \n", - host, service, strerror(errno)); - fdflags = fdflags | O_NONBLOCK; - if (fcntl(s, F_SETFD, fdflags) < 0) - lprintf(1, "unable to set socket nonblocking flags! %s.%s: %s \n", - host, service, strerror(errno)); + /* + * Try all available addresses until we connect to one or until we run out. + */ + for (ai = res; ai != NULL; ai = ai->ai_next) { - signal(SIGALRM, timeout); - alarm(30); + if (ai->ai_family == AF_INET) syslog(LOG_DEBUG, "Trying IPv4\n"); + else if (ai->ai_family == AF_INET6) syslog(LOG_DEBUG, "Trying IPv6\n"); + else syslog(LOG_WARNING, "This is going to fail.\n"); - if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - lprintf(1, "Can't connect to %s.%s: %s\n", - host, service, strerror(errno)); - close(s); - return (-1); + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s < 0) { + syslog(LOG_WARNING, "socket() failed: %s\n", strerror(errno)); + freeaddrinfo(res); + return(-1); + } + rc = connect(s, ai->ai_addr, ai->ai_addrlen); + if (rc >= 0) { + int fdflags; + freeaddrinfo(res); + + fdflags = fcntl(rc, F_GETFL); + if (fdflags < 0) { + syslog(LOG_ERR, + "unable to get socket %d flags! %s \n", + rc, + strerror(errno)); + close(rc); + return -1; + } + fdflags = fdflags | O_NONBLOCK; + if (fcntl(rc, F_SETFL, fdflags) < 0) { + syslog(LOG_ERR, + "unable to set socket %d nonblocking flags! %s \n", + rc, + strerror(errno)); + close(s); + return -1; + } + + return(s); + } + else { + syslog(LOG_WARNING, "connect() failed: %s\n", strerror(errno)); + close(s); + } } - alarm(0); - signal(SIGALRM, SIG_IGN); - - fdflags = fcntl(s, F_GETFL); - if (fdflags < 0) - lprintf(1, "unable to get socket flags! %s.%s: %s \n", - host, service, strerror(errno)); - fdflags = fdflags | O_NONBLOCK; - if (fcntl(s, F_SETFD, fdflags) < 0) - lprintf(1, "unable to set socket nonblocking flags! %s.%s: %s \n", - host, service, strerror(errno)); - return (s); + freeaddrinfo(res); + return(-1); } - /* * input string from pipe */ @@ -153,7 +173,7 @@ int serv_getln(char *strbuf, int bufsize) FlushStrBuf(WCC->MigrateReadLineBuf); strbuf[len] = '\0'; #ifdef SERV_TRACE - lprintf(9, "%3d>%s\n", WC->serv_sock, strbuf); + syslog(LOG_DEBUG, "%3d<<<%s\n", WCC->serv_sock, strbuf); #endif return len; } @@ -164,7 +184,11 @@ int StrBuf_ServGetln(StrBuf *buf) wcsession *WCC = WC; const char *ErrStr = NULL; int rc; + + if (!WCC->connected) + return -1; + FlushStrBuf(buf); rc = StrBufTCP_read_buffered_line_fast(buf, WCC->ReadBuf, &WCC->ReadPos, @@ -173,20 +197,30 @@ int StrBuf_ServGetln(StrBuf *buf) &ErrStr); if (rc < 0) { - lprintf(1, "Server connection broken: %s\n", - ErrStr); - wc_backtrace(); + syslog(LOG_INFO, "StrBuf_ServGetln(): Server connection broken: %s\n", + (ErrStr)?ErrStr:""); + wc_backtrace(LOG_INFO); + if (WCC->serv_sock > 0) close(WCC->serv_sock); WCC->serv_sock = (-1); WCC->connected = 0; WCC->logged_in = 0; } +#ifdef SERV_TRACE + else + { + long pos = 0; + if (WCC->ReadPos != NULL) + pos = WCC->ReadPos - ChrPtr(WCC->ReadBuf); + syslog(LOG_DEBUG, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf)); + } +#endif return rc; } int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize) { wcsession *WCC = WC; - const char *Err; + const char *ErrStr; int rc; rc = StrBufReadBLOBBuffered(buf, @@ -196,62 +230,118 @@ int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize) 1, BlobSize, NNN_TERM, - &Err); + &ErrStr); if (rc < 0) { - lprintf(1, "Server connection broken: %s\n", - Err); - wc_backtrace(); + syslog(LOG_INFO, "StrBuf_ServGetBLOBBuffered(): Server connection broken: %s\n", + (ErrStr)?ErrStr:""); + wc_backtrace(LOG_INFO); + if (WCC->serv_sock > 0) close(WCC->serv_sock); WCC->serv_sock = (-1); WCC->connected = 0; WCC->logged_in = 0; } +#ifdef SERV_TRACE + else + syslog(LOG_DEBUG, "%3d<<serv_sock, StrLength(buf)); +#endif + return rc; } int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize) { wcsession *WCC = WC; - const char *Err; + const char *ErrStr; int rc; WCC->ReadPos = NULL; - rc = StrBufReadBLOB(buf, &WCC->serv_sock, 1, BlobSize, &Err); + rc = StrBufReadBLOB(buf, &WCC->serv_sock, 1, BlobSize, &ErrStr); if (rc < 0) { - lprintf(1, "Server connection broken: %s\n", - Err); - wc_backtrace(); + syslog(LOG_INFO, "StrBuf_ServGetBLOB(): Server connection broken: %s\n", + (ErrStr)?ErrStr:""); + wc_backtrace(LOG_INFO); + if (WCC->serv_sock > 0) close(WCC->serv_sock); WCC->serv_sock = (-1); WCC->connected = 0; WCC->logged_in = 0; } +#ifdef SERV_TRACE + else + syslog(LOG_DEBUG, "%3d<<serv_sock, StrLength(buf)); +#endif + return rc; } + +void FlushReadBuf (void) +{ + long len; + const char *pch; + const char *pche; + wcsession *WCC = WC; + + len = StrLength(WCC->ReadBuf); + if ((len > 0) && + (WCC->ReadPos != NULL) && + (WCC->ReadPos != StrBufNOTNULL)) + + { + pch = ChrPtr(WCC->ReadBuf); + pche = pch + len; + if (WCC->ReadPos != pche) + { + syslog(LOG_EMERG, + "ERROR: somebody didn't eat his soup! Remaing Chars: %ld [%s]\n", + (long)(pche - WCC->ReadPos), + pche + ); + syslog(LOG_EMERG, + "--------------------------------------------------------------------------------\n" + "Whole buf: [%s]\n" + "--------------------------------------------------------------------------------\n", + pch); + AppendImportantMessage(HKEY("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew.")); + } + } + + FlushStrBuf(WCC->ReadBuf); + WCC->ReadPos = NULL; + + +} + + /* * send binary to server * buf the buffer to write to citadel server * nbytes how many bytes to send to citadel server */ -void serv_write(const char *buf, int nbytes) +int serv_write(const char *buf, int nbytes) { + wcsession *WCC = WC; int bytes_written = 0; int retval; + + FlushReadBuf(); while (bytes_written < nbytes) { - retval = write(WC->serv_sock, &buf[bytes_written], + retval = write(WCC->serv_sock, &buf[bytes_written], nbytes - bytes_written); if (retval < 1) { - lprintf(1, "Server connection broken: %s\n", - strerror(errno)); - close(WC->serv_sock); - WC->serv_sock = (-1); - WC->connected = 0; - WC->logged_in = 0; - return; + const char *ErrStr = strerror(errno); + syslog(LOG_INFO, "serv_write(): Server connection broken: %s\n", + (ErrStr)?ErrStr:""); + if (WCC->serv_sock > 0) close(WCC->serv_sock); + WCC->serv_sock = (-1); + WCC->connected = 0; + WCC->logged_in = 0; + return 0; } bytes_written = bytes_written + retval; } + return 1; } @@ -259,34 +349,32 @@ void serv_write(const char *buf, int nbytes) * send line to server * string the line to send to the citadel server */ -void serv_puts(const char *string) +int serv_puts(const char *string) { - wcsession *WCC = WC; #ifdef SERV_TRACE - lprintf(9, "%3d<%s\n", WC->serv_sock, string); + syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, string); #endif - FlushStrBuf(WCC->ReadBuf); - WCC->ReadPos = NULL; + FlushReadBuf(); - serv_write(string, strlen(string)); - serv_write("\n", 1); + if (!serv_write(string, strlen(string))) + return 0; + return serv_write("\n", 1); } /* * send line to server * string the line to send to the citadel server */ -void serv_putbuf(const StrBuf *string) +int serv_putbuf(const StrBuf *string) { - wcsession *WCC = WC; #ifdef SERV_TRACE - lprintf(9, "%3d<%s\n", WC->serv_sock, ChrPtr(string)); + syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string)); #endif - FlushStrBuf(WCC->ReadBuf); - WCC->ReadPos = NULL; + FlushReadBuf(); - serv_write(ChrPtr(string), StrLength(string)); - serv_write("\n", 1); + if (!serv_write(ChrPtr(string), StrLength(string))) + return 0; + return serv_write("\n", 1); } @@ -295,15 +383,14 @@ void serv_putbuf(const StrBuf *string) * format the formatstring * ... the entities to insert into format */ -void serv_printf(const char *format,...) +int serv_printf(const char *format,...) { - wcsession *WCC = WC; va_list arg_ptr; char buf[SIZ]; size_t len; + int rc; - FlushStrBuf(WCC->ReadBuf); - WCC->ReadPos = NULL; + FlushReadBuf(); va_start(arg_ptr, format); vsnprintf(buf, sizeof buf, format, arg_ptr); @@ -312,24 +399,287 @@ void serv_printf(const char *format,...) len = strlen(buf); buf[len++] = '\n'; buf[len] = '\0'; - serv_write(buf, len); + rc = serv_write(buf, len); #ifdef SERV_TRACE - lprintf(9, "<%s", buf); + syslog(LOG_DEBUG, ">>>%s", buf); #endif + return rc; +} + + +/* + * Read binary data from server into memory using a series of server READ commands. + * returns the read content as StrBuf + */ +int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf) +{ + wcsession *WCC = WC; + size_t bytes_read = 0; + size_t this_block = 0; + int rc = 6; + int ServerRc = 6; + + if (Ret == NULL) { + return -1; + } + + while ((bytes_read < total_len) && (ServerRc == 6)) { + + if (WCC->serv_sock==-1) { + FlushStrBuf(Ret); + return -1; + } + + serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, bytes_read, total_len-bytes_read); + if ( (rc = StrBuf_ServGetln(Buf) > 0) && + (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) ) + { + if (rc < 0) + return rc; + StrBufCutLeft(Buf, 4); + this_block = StrTol(Buf); + rc = StrBuf_ServGetBLOBBuffered(Ret, this_block); + if (rc < 0) { + syslog(LOG_INFO, "Server connection broken during download\n"); + wc_backtrace(LOG_INFO); + if (WCC->serv_sock > 0) close(WCC->serv_sock); + WCC->serv_sock = (-1); + WCC->connected = 0; + WCC->logged_in = 0; + return rc; + } + bytes_read += rc; + } + } + + return StrLength(Ret); } +int client_write(StrBuf *ThisBuf) +{ + wcsession *WCC = WC; + const char *ptr, *eptr; + long count; + ssize_t res = 0; + fd_set wset; + int fdflags; + ptr = ChrPtr(ThisBuf); + count = StrLength(ThisBuf); + eptr = ptr + count; + + fdflags = fcntl(WC->Hdr->http_sock, F_GETFL); + + while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)) { + if ((fdflags & O_NONBLOCK) == O_NONBLOCK) { + FD_ZERO(&wset); + FD_SET(WCC->Hdr->http_sock, &wset); + if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) { + syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno)); + return -1; + } + } + + if ((WCC->Hdr->http_sock == -1) || + ((res = write(WCC->Hdr->http_sock, ptr, count)), + (res == -1))) + { + syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno)); + wc_backtrace(LOG_INFO); + return -1; + } + count -= res; + ptr += res; + } + return 0; +} + + +int +read_serv_chunk( + + StrBuf *Buf, + size_t total_len, + size_t *bytes_read + ) +{ + int rc; + int ServerRc; + wcsession *WCC = WC; + + serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, *bytes_read, total_len-(*bytes_read)); + if ( (rc = StrBuf_ServGetln(Buf) > 0) && + (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) ) + { + size_t this_block = 0; + + if (rc < 0) + return rc; + + StrBufCutLeft(Buf, 4); + this_block = StrTol(Buf); + rc = StrBuf_ServGetBLOBBuffered(WCC->WBuf, this_block); + if (rc < 0) { + syslog(LOG_INFO, "Server connection broken during download\n"); + wc_backtrace(LOG_INFO); + if (WCC->serv_sock > 0) close(WCC->serv_sock); + WCC->serv_sock = (-1); + WCC->connected = 0; + WCC->logged_in = 0; + return rc; + } + *bytes_read += rc; + } + return 6; +} + +static inline int send_http(StrBuf *Buf) +{ +#ifdef HAVE_OPENSSL + if (is_https) + return client_write_ssl(Buf); + else +#endif + return client_write(Buf); +} +/* + * Read binary data from server into memory using a series of server READ commands. + * returns the read content as StrBuf + */ +void serv_read_binary_to_http(StrBuf *MimeType, size_t total_len, int is_static, int detect_mime) +{ + int ServerRc = 6; + wcsession *WCC = WC; + size_t bytes_read = 0; + int first = 1; + int client_con_state = 0; + int chunked = 0; + StrBuf *BufHeader = NULL; + StrBuf *Buf; + + Buf = NewStrBuf(); + + if (WCC->Hdr->HaveRange) + { + WCC->Hdr->HaveRange++; + WCC->Hdr->TotalBytes = total_len; + /* open range? or beyound file border? correct the numbers. */ + if ((WCC->Hdr->RangeTil == -1) || (WCC->Hdr->RangeTil>= total_len)) + WCC->Hdr->RangeTil = total_len - 1; + bytes_read = WCC->Hdr->RangeStart; + total_len = WCC->Hdr->RangeTil; + } + else + chunked = total_len > SIZ * 10; /* TODO: disallow for HTTP / 1.0 */ + + if (chunked) + { + BufHeader=NewStrBuf(); + } + + if ((detect_mime != 0) && (bytes_read != 0)) + { + /* need to read first chunk to detect mime, though the client doesn't care */ + size_t bytes_read = 0; + const char *CT; + + ServerRc = read_serv_chunk( + Buf, + total_len, + &bytes_read); + + if (ServerRc != 6) + { + FreeStrBuf(&Buf); + return; + } + CT = GuessMimeType(SKEY(WCC->WBuf)); + FlushStrBuf(WCC->WBuf); + StrBufPlain(MimeType, CT, -1); + detect_mime = 0; + FreeStrBuf(&Buf); + } + + if (!detect_mime) + { + http_transmit_headers(ChrPtr(MimeType), is_static, chunked); + + if (send_http(WCC->HBuf) < 0) + { + FreeStrBuf(&Buf); + return; + } + } + + while ((bytes_read < total_len) && + (ServerRc == 6) && + (client_con_state == 0)) + { + + if (WCC->serv_sock==-1) { + FlushStrBuf(WCC->WBuf); + FreeStrBuf(&Buf); + return; + } + + ServerRc = read_serv_chunk( + Buf, + total_len, + &bytes_read); + if (ServerRc != 6) + break; + + if (detect_mime) + { + const char *CT; + detect_mime = 0; + + CT = GuessMimeType(SKEY(WCC->WBuf)); + StrBufPlain(MimeType, CT, -1); + http_transmit_headers(ChrPtr(MimeType), is_static, chunked); + + client_con_state = send_http(WCC->HBuf); + } + + if ((chunked) && (client_con_state == 0)) + { + StrBufPrintf(BufHeader, "%s%x\r\n", + (first)?"":"\r\n", + StrLength (WCC->WBuf)); + first = 0; + client_con_state = send_http(BufHeader); + } + + if (client_con_state == 0) + client_con_state = send_http(WCC->WBuf); + + FlushStrBuf(WCC->WBuf); + } + + if ((chunked) && (client_con_state == 0)) + { + StrBufPlain(BufHeader, HKEY("\r\n0\r\n\r\n")); + if (send_http(BufHeader) < 0) + { + FreeStrBuf(&Buf); + return; + } + } + FreeStrBuf(&Buf); +} int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target) { - const char *Error, *pch, *pchs; + const char *Error; +#ifdef HAVE_OPENSSL + const char *pch, *pchs; int rlen, len, retval = 0; -#ifdef HAVE_OPENSSL if (is_https) { int ntries = 0; - if (StrLength(Hdr->ReadBuf) > 0) { + if (StrLength(Hdr->ReadBuf) > 0) + { pchs = ChrPtr(Hdr->ReadBuf); pch = strchr(pchs, '\n'); if (pch != NULL) { @@ -352,6 +702,8 @@ int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target) retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING); pchs = ChrPtr(Hdr->ReadBuf); pch = strchr(pchs, '\n'); + if (pch == NULL) + retval = 0; } if (retval == 0) { sleeeeeeeeeep(1); @@ -384,72 +736,113 @@ int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target) &Error); } + /* * 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) +int webcit_tcp_server(const char *ip_addr, int port_number, int queue_len) { - struct sockaddr_in sin; - int s, i; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (ip_addr == NULL) { - sin.sin_addr.s_addr = INADDR_ANY; - } else { - sin.sin_addr.s_addr = inet_addr(ip_addr); + const char *ipv4broadcast = "0.0.0.0"; + int IsDefault = 0; + struct protoent *p; + struct sockaddr_in6 sin6; + struct sockaddr_in sin4; + int s, i, b; + int ip_version = 6; + +retry: + 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, "*")) + ) { + IsDefault = 1; + ip_version = 6; + sin6.sin6_addr = in6addr_any; } - - if (sin.sin_addr.s_addr == INADDR_NONE) { - sin.sin_addr.s_addr = INADDR_ANY; + else if (!strcmp(ip_addr, "0.0.0.0")) /* any IPv4 */ + { + ip_version = 4; + sin4.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) { + syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno)); + return (-WC_EXIT_BIND); + } + } + else /* specific IPv6 */ + { + ip_version = 6; + if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) { + syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno)); + return (-WC_EXIT_BIND); + } } if (port_number == 0) { - lprintf(1, "Cannot start: no port number specified.\n"); - exit(WC_EXIT_BIND); + syslog(LOG_WARNING, "Cannot start: no port number specified.\n"); + return (-WC_EXIT_BIND); } - sin.sin_port = htons((u_short) port_number); + sin6.sin6_port = htons((u_short) port_number); + sin4.sin_port = htons((u_short) port_number); + + p = getprotobyname("tcp"); - s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto)); + s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto)); if (s < 0) { - lprintf(1, "Can't create a socket: %s\n", strerror(errno)); - exit(WC_EXIT_BIND); + if (IsDefault && (errno == EAFNOSUPPORT)) + { + s = 0; + ip_addr = ipv4broadcast; + goto retry; + } + syslog(LOG_WARNING, "Can't create a listening socket: %s\n", strerror(errno)); + return (-WC_EXIT_BIND); } /* Set some socket options that make sense. */ i = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); - #ifndef __APPLE__ - fcntl(s, F_SETFL, O_NONBLOCK); /* maide: this statement is incorrect - there should be a preceding F_GETFL - and a bitwise OR with the previous - fd flags */ - #endif - - if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - lprintf(1, "Can't bind: %s\n", strerror(errno)); - exit(WC_EXIT_BIND); + if (ip_version == 6) { + b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6)); } + else { + b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4)); + } + + if (b < 0) { + syslog(LOG_EMERG, "Can't bind: %s\n", strerror(errno)); + close(s); + return (-WC_EXIT_BIND); + } + if (listen(s, queue_len) < 0) { - lprintf(1, "Can't listen: %s\n", strerror(errno)); - exit(WC_EXIT_BIND); + syslog(LOG_EMERG, "Can't listen: %s\n", strerror(errno)); + close(s); + return (-WC_EXIT_BIND); } return (s); } - /* * Create a Unix domain socket and listen on it * sockpath - file name of the unix domain socket * queue_len - Number of incoming connections to allow in the queue */ -int ig_uds_server(char *sockpath, int queue_len) +int webcit_uds_server(char *sockpath, int queue_len) { struct sockaddr_un addr; int s; @@ -461,9 +854,9 @@ int ig_uds_server(char *sockpath, int queue_len) i = unlink(sockpath); if ((i != 0) && (errno != ENOENT)) { - lprintf(1, "webcit: can't unlink %s: %s\n", + syslog(LOG_WARNING, "webcit: can't unlink %s: %s\n", sockpath, strerror(errno)); - exit(WC_EXIT_BIND); + return (-WC_EXIT_BIND); } memset(&addr, 0, sizeof(addr)); @@ -472,21 +865,20 @@ int ig_uds_server(char *sockpath, int queue_len) s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { - lprintf(1, "webcit: Can't create a socket: %s\n", - strerror(errno)); - exit(WC_EXIT_BIND); + syslog(LOG_WARNING, "webcit: Can't create a unix domain socket: %s\n", strerror(errno)); + return (-WC_EXIT_BIND); } if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - lprintf(1, "webcit: Can't bind: %s\n", - strerror(errno)); - exit(WC_EXIT_BIND); + syslog(LOG_WARNING, "webcit: Can't bind: %s\n", strerror(errno)); + close(s); + return (-WC_EXIT_BIND); } if (listen(s, actual_queue_len) < 0) { - lprintf(1, "webcit: Can't listen: %s\n", - strerror(errno)); - exit(WC_EXIT_BIND); + syslog(LOG_WARNING, "webcit: Can't listen: %s\n", strerror(errno)); + close(s); + return (-WC_EXIT_BIND); } chmod(sockpath, 0777); @@ -516,27 +908,40 @@ int client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout) #ifdef HAVE_OPENSSL if (is_https) { - long bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf)); - StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0); - Hdr->Pos = NULL; - FlushStrBuf(Hdr->ReadBuf); - - while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes) && - (retval >= 0)) - retval = client_read_sslbuffer(Hdr->ReadBuf, timeout); - if (retval >= 0) { - StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */ -#ifdef HTTP_TRACING - write(2, "\033[32m", 5); - write(2, buf, bytes); - write(2, "\033[30m", 5); -#endif - return 1; + long bufremain = 0; + long baselen; + + baselen = StrLength(Target); + + if (Hdr->Pos == NULL) + Hdr->Pos = ChrPtr(Hdr->ReadBuf); + + if (StrLength(Hdr->ReadBuf) > 0) + { + bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf)); + + if (bytes < bufremain) + bufremain = bytes; + StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0); + StrBufCutLeft(Hdr->ReadBuf, bufremain); } - else { - lprintf(2, "client_read_ssl() failed\n"); - return -1; + + if (bytes > bufremain) + { + while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) && + (retval >= 0)) + retval = client_read_sslbuffer(Hdr->ReadBuf, timeout); + if (retval >= 0) { + StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */ + return 1; + } + else { + syslog(LOG_INFO, "client_read_ssl() failed\n"); + return -1; + } } + else + return 1; } #endif @@ -549,16 +954,12 @@ int client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout) O_TERM, &Error); if (retval < 0) { - lprintf(2, "client_read() failed: %s\n", + syslog(LOG_INFO, "client_read() failed: %s\n", Error); + wc_backtrace(LOG_DEBUG); return retval; } -#ifdef HTTP_TRACING - write(2, "\033[32m", 5); - write(2, buf, bytes); - write(2, "\033[30m", 5); -#endif return 1; } @@ -582,15 +983,27 @@ long end_burst(void) wcsession *WCC = WC; const char *ptr, *eptr; long count; - ssize_t res; + ssize_t res = 0; fd_set wset; int fdflags; - if (!DisableGzip && (WCC->Hdr->HR.gzip_ok) && CompressBuffer(WCC->WBuf)) + if (!DisableGzip && (WCC->Hdr->HR.gzip_ok)) { - hprintf("Content-encoding: gzip\r\n"); + if (CompressBuffer(WCC->WBuf) > 0) + hprintf("Content-encoding: gzip\r\n"); + else { + syslog(LOG_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno)); + wc_backtrace(LOG_INFO); + } } + if (WCC->WFBuf != NULL) { + WildFireSerializePayload(WCC->WFBuf, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL); + FreeStrBuf(&WCC->WFBuf); + } + + if (WCC->Hdr->HR.prohibit_caching) + hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n"); hprintf("Content-length: %d\r\n\r\n", StrLength(WCC->WBuf)); ptr = ChrPtr(WCC->HBuf); @@ -605,30 +1018,26 @@ long end_burst(void) } #endif - -#ifdef HTTP_TRACING - - write(2, "\033[34m", 5); - write(2, ptr, StrLength(WCC->WBuf)); - write(2, "\033[30m", 5); -#endif + if (WCC->Hdr->http_sock == -1) + return -1; fdflags = fcntl(WC->Hdr->http_sock, F_GETFL); - while (ptr < eptr) { + while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)){ if ((fdflags & O_NONBLOCK) == O_NONBLOCK) { FD_ZERO(&wset); FD_SET(WCC->Hdr->http_sock, &wset); if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) { - lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno)); + syslog(LOG_DEBUG, "client_write: Socket select failed (%s)\n", strerror(errno)); return -1; } } - if ((res = write(WCC->Hdr->http_sock, + if ((WCC->Hdr->http_sock == -1) || + (res = write(WCC->Hdr->http_sock, ptr, count)) == -1) { - lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno)); - wc_backtrace(); + syslog(LOG_DEBUG, "client_write: Socket write failed (%s)\n", strerror(errno)); + wc_backtrace(LOG_INFO); return res; } count -= res; @@ -639,28 +1048,22 @@ long end_burst(void) count = StrLength(WCC->WBuf); eptr = ptr + count; -#ifdef HTTP_TRACING - - write(2, "\033[34m", 5); - write(2, ptr, StrLength(WCC->WBuf)); - write(2, "\033[30m", 5); -#endif - - while (ptr < eptr) { + while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)) { if ((fdflags & O_NONBLOCK) == O_NONBLOCK) { FD_ZERO(&wset); FD_SET(WCC->Hdr->http_sock, &wset); if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) { - lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno)); + syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno)); return -1; } } - if ((res = write(WCC->Hdr->http_sock, + if ((WCC->Hdr->http_sock == -1) || + (res = write(WCC->Hdr->http_sock, ptr, count)) == -1) { - lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno)); - wc_backtrace(); + syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno)); + wc_backtrace(LOG_INFO); return res; } count -= res; @@ -673,7 +1076,7 @@ long end_burst(void) /* * lingering_close() a`la Apache. see - * http://www.apache.org/docs/misc/fin_wait_2.html for rationale + * http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html for rationale */ int lingering_close(int fd) { @@ -683,6 +1086,8 @@ int lingering_close(int fd) struct timeval tv, start; gettimeofday(&start, NULL); + if (fd == -1) + return -1; shutdown(fd, 1); do { do { @@ -707,6 +1112,31 @@ int lingering_close(int fd) return close(fd); } +void +HttpNewModule_TCPSOCKETS +(ParsedHttpHdrs *httpreq) +{ + + httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4); +} + +void +HttpDetachModule_TCPSOCKETS +(ParsedHttpHdrs *httpreq) +{ + + FlushStrBuf(httpreq->ReadBuf); + ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ); +} + +void +HttpDestroyModule_TCPSOCKETS +(ParsedHttpHdrs *httpreq) +{ + + FreeStrBuf(&httpreq->ReadBuf); +} + void SessionNewModule_TCPSOCKETS @@ -722,7 +1152,12 @@ SessionDestroyModule_TCPSOCKETS { FreeStrBuf(&sess->CLineBuf); FreeStrBuf(&sess->ReadBuf); + sess->connected = 0; + sess->ReadPos = NULL; FreeStrBuf(&sess->MigrateReadLineBuf); - if (sess->serv_sock > 0) + if (sess->serv_sock > 0) { + syslog(LOG_DEBUG, "Closing socket %d", sess->serv_sock); close(sess->serv_sock); + } + sess->serv_sock = -1; }