X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Ftcp_sockets.c;h=d4795c83042fd6c9f3cf47cb1f4213857cf01877;hb=8b18a83940a18192e8fb8dd5f8f0a593e4733bb6;hp=2835344f47eef51393adcfdc1ef66714ec07cc42;hpb=156d3eaa1b5d85cb70a79046bd874ab832df6ff1;p=citadel.git diff --git a/webcit/tcp_sockets.c b/webcit/tcp_sockets.c index 2835344f4..d4795c830 100644 --- a/webcit/tcp_sockets.c +++ b/webcit/tcp_sockets.c @@ -53,7 +53,6 @@ int uds_connectsock(char *sockpath) close(s); return(-1); } - return s; } @@ -123,7 +122,28 @@ int tcp_connectsock(char *host, char *service) } 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 { @@ -153,7 +173,7 @@ int serv_getln(char *strbuf, int bufsize) FlushStrBuf(WCC->MigrateReadLineBuf); strbuf[len] = '\0'; #ifdef SERV_TRACE - syslog(LOG_DEBUG, "%3d<<<%s\n", WC->serv_sock, strbuf); + syslog(LOG_DEBUG, "%3d<<<%s\n", WCC->serv_sock, strbuf); #endif return len; } @@ -436,6 +456,284 @@ int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf) } +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; + int is_gzip = 0; + StrBuf *BufHeader = NULL; + StrBuf *Buf; + StrBuf *pBuf = NULL; + void *SC = NULL; + IOBuffer ReadBuffer; + IOBuffer WriteBuffer; + + + 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(&BufHeader); + FreeStrBuf(&Buf); + return; + } + CT = GuessMimeType(SKEY(WCC->WBuf)); + FlushStrBuf(WCC->WBuf); + StrBufPlain(MimeType, CT, -1); + CheckGZipCompressionAllowed(SKEY(MimeType)); + detect_mime = 0; + FreeStrBuf(&Buf); + } + + memset(&WriteBuffer, 0, sizeof(IOBuffer)); + if (chunked && !DisableGzip && WCC->Hdr->HR.gzip_ok) + { + is_gzip = 1; + SC = StrBufNewStreamContext (eZLibEncode); + + memset(&ReadBuffer, 0, sizeof(IOBuffer)); + ReadBuffer.Buf = WCC->WBuf; + + WriteBuffer.Buf = NewStrBufPlain(NULL, SIZ*2);; + pBuf = WriteBuffer.Buf; + } + else + { + pBuf = WCC->WBuf; + } + + if (!detect_mime) + { + http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip); + + if (send_http(WCC->HBuf) < 0) + { + FreeStrBuf(&Buf); + FreeStrBuf(&WriteBuffer.Buf); + FreeStrBuf(&BufHeader); + StrBufDestroyStreamContext(eZLibEncode, SC); + return; + } + } + + while ((bytes_read < total_len) && + (ServerRc == 6) && + (client_con_state == 0)) + { + + if (WCC->serv_sock==-1) { + FlushStrBuf(WCC->WBuf); + FreeStrBuf(&Buf); + FreeStrBuf(&WriteBuffer.Buf); + FreeStrBuf(&BufHeader); + StrBufDestroyStreamContext(eZLibEncode, SC); + 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); + if (is_gzip) { + CheckGZipCompressionAllowed(SKEY(MimeType)); + is_gzip = WCC->Hdr->HR.gzip_ok; + } + http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip); + + client_con_state = send_http(WCC->HBuf); + } + + if (is_gzip) + { + int done = (bytes_read == total_len); + while ((IOBufferStrLength(&ReadBuffer) > 0) && (client_con_state == 0)) { + int rc; + + do { + rc = StrBufStreamTranscode(eZLibEncode, &WriteBuffer, &ReadBuffer, NULL, -1, SC, done); + + if (StrLength (pBuf) > 0) { + StrBufPrintf(BufHeader, "%s%x\r\n", + (first)?"":"\r\n", + StrLength (pBuf)); + first = 0; + client_con_state = send_http(BufHeader); + if (client_con_state == 0) { + client_con_state = send_http(pBuf); + } + FlushStrBuf(pBuf); + } + } while (rc == 1); + } + FlushStrBuf(WCC->WBuf); + } + else { + if ((chunked) && (client_con_state == 0)) + { + StrBufPrintf(BufHeader, "%s%x\r\n", + (first)?"":"\r\n", + StrLength (pBuf)); + first = 0; + client_con_state = send_http(BufHeader); + } + + if (client_con_state == 0) + client_con_state = send_http(pBuf); + + FlushStrBuf(pBuf); + } + } + + StrBufDestroyStreamContext(eZLibEncode, SC); + FreeStrBuf(&WriteBuffer.Buf); + if ((chunked) && (client_con_state == 0)) + { + StrBufPlain(BufHeader, HKEY("\r\n0\r\n\r\n")); + if (send_http(BufHeader) < 0) + { + FreeStrBuf(&Buf); + FreeStrBuf(&BufHeader); + return; + } + } + FreeStrBuf(&BufHeader); + FreeStrBuf(&Buf); +} + int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target) { const char *Error; @@ -512,14 +810,17 @@ int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target) * port_number port number to bind * queue_len number of incoming connections to allow in the queue */ -int webcit_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) { + 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; @@ -529,6 +830,7 @@ int webcit_tcp_server(char *ip_addr, int port_number, int queue_len) || (IsEmptyStr(ip_addr)) || (!strcmp(ip_addr, "*")) ) { + IsDefault = 1; ip_version = 6; sin6.sin6_addr = in6addr_any; } @@ -565,6 +867,12 @@ int webcit_tcp_server(char *ip_addr, int port_number, int queue_len) s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto)); if (s < 0) { + 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); } @@ -833,7 +1141,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) {