2 * Copyright (c) 1987-2010 by the citadel.org team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Uncomment this to log all communications with the Citadel server
26 #include "webserver.h"
28 extern int DisableGzip;
29 long MaxRead = -1; /* should we do READ scattered or all at once? */
32 * register the timeout
34 RETSIGTYPE timeout(int signum)
36 lprintf(1, "Connection timed out; unable to reach citserver\n");
37 /* no exit here, since we need to server the connection unreachable thing. exit(3); */
42 * Client side - connect to a unix domain socket
44 int uds_connectsock(char *sockpath)
46 struct sockaddr_un addr;
49 memset(&addr, 0, sizeof(addr));
50 addr.sun_family = AF_UNIX;
51 strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
53 s = socket(AF_UNIX, SOCK_STREAM, 0);
55 lprintf(1, "Can't create socket[%s]: %s\n", sockpath, strerror(errno));
59 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
60 lprintf(1, "Can't connect [%s]: %s\n", sockpath, strerror(errno));
70 * TCP client - connect to a host/port
72 int tcp_connectsock(char *host, char *service)
74 struct in6_addr serveraddr;
75 struct addrinfo hints;
76 struct addrinfo *res = NULL;
77 struct addrinfo *ai = NULL;
81 if ((host == NULL) || IsEmptyStr(host))
83 if ((service == NULL) || IsEmptyStr(service))
86 lprintf(9, "tcp_connectsock(%s,%s)\n", host, service);
88 memset(&hints, 0x00, sizeof(hints));
89 hints.ai_flags = AI_NUMERICSERV;
90 hints.ai_family = AF_UNSPEC;
91 hints.ai_socktype = SOCK_STREAM;
94 * Handle numeric IPv4 and IPv6 addresses
96 rc = inet_pton(AF_INET, host, &serveraddr);
97 if (rc == 1) { /* dotted quad */
98 hints.ai_family = AF_INET;
99 hints.ai_flags |= AI_NUMERICHOST;
101 rc = inet_pton(AF_INET6, host, &serveraddr);
102 if (rc == 1) { /* IPv6 address */
103 hints.ai_family = AF_INET6;
104 hints.ai_flags |= AI_NUMERICHOST;
108 /* Begin the connection process */
110 rc = getaddrinfo(host, service, &hints, &res);
112 lprintf(1, "%s: %s\n", host, gai_strerror(rc));
117 * Try all available addresses until we connect to one or until we run out.
119 for (ai = res; ai != NULL; ai = ai->ai_next) {
121 if (ai->ai_family == AF_INET) lprintf(9, "Trying IPv4\n");
122 else if (ai->ai_family == AF_INET6) lprintf(9, "Trying IPv6\n");
123 else lprintf(9, "This is going to fail.\n");
125 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
127 lprintf(1, "socket() failed: %s\n", strerror(errno));
130 rc = connect(s, ai->ai_addr, ai->ai_addrlen);
135 lprintf(1, "connect() failed: %s\n", strerror(errno));
145 * input string from pipe
147 int serv_getln(char *strbuf, int bufsize)
153 StrBuf_ServGetln(WCC->MigrateReadLineBuf);
154 len = StrLength(WCC->MigrateReadLineBuf);
157 memcpy(strbuf, ChrPtr(WCC->MigrateReadLineBuf), len);
158 FlushStrBuf(WCC->MigrateReadLineBuf);
161 lprintf(9, "%3d<<<%s\n", WC->serv_sock, strbuf);
167 int StrBuf_ServGetln(StrBuf *buf)
170 const char *ErrStr = NULL;
174 rc = StrBufTCP_read_buffered_line_fast(buf,
182 lprintf(1, "Server connection broken: %s\n",
185 WCC->serv_sock = (-1);
193 if (WCC->ReadPos != NULL)
194 pos = WCC->ReadPos - ChrPtr(WCC->ReadBuf);
195 lprintf(9, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf));
201 int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize)
207 rc = StrBufReadBLOBBuffered(buf,
217 lprintf(1, "Server connection broken: %s\n",
220 WCC->serv_sock = (-1);
226 lprintf(9, "%3d<<<BLOB: %ld bytes\n", WC->serv_sock, StrLength(buf));
232 int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize)
239 rc = StrBufReadBLOB(buf, &WCC->serv_sock, 1, BlobSize, &ErrStr);
242 lprintf(1, "Server connection broken: %s\n",
245 WCC->serv_sock = (-1);
251 lprintf(9, "%3d<<<BLOB: %ld bytes\n", WC->serv_sock, StrLength(buf));
258 void FlushReadBuf (void)
265 len = StrLength(WCC->ReadBuf);
267 (WCC->ReadPos != NULL) &&
268 (WCC->ReadPos != StrBufNOTNULL))
271 pch = ChrPtr(WCC->ReadBuf);
273 if (WCC->ReadPos != pche)
275 lprintf(1, "ERROR: somebody didn't eat his soup! Remaing Chars: %d [%s]\n",
276 pche - WCC->ReadPos, pche);
278 "--------------------------------------------------------------------------------\n"
280 "--------------------------------------------------------------------------------\n",
282 AppendImportantMessage(HKEY("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew."));
286 FlushStrBuf(WCC->ReadBuf);
294 * send binary to server
295 * buf the buffer to write to citadel server
296 * nbytes how many bytes to send to citadel server
298 void serv_write(const char *buf, int nbytes)
301 int bytes_written = 0;
305 while (bytes_written < nbytes) {
306 retval = write(WCC->serv_sock, &buf[bytes_written],
307 nbytes - bytes_written);
309 const char *ErrStr = strerror(errno);
310 lprintf(1, "Server connection broken: %s\n",
312 close(WCC->serv_sock);
313 WCC->serv_sock = (-1);
318 bytes_written = bytes_written + retval;
324 * send line to server
325 * string the line to send to the citadel server
327 void serv_puts(const char *string)
330 lprintf(9, "%3d>>>%s\n", WC->serv_sock, string);
334 serv_write(string, strlen(string));
339 * send line to server
340 * string the line to send to the citadel server
342 void serv_putbuf(const StrBuf *string)
345 lprintf(9, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string));
349 serv_write(ChrPtr(string), StrLength(string));
355 * convenience function to send stuff to the server
356 * format the formatstring
357 * ... the entities to insert into format
359 void serv_printf(const char *format,...)
367 va_start(arg_ptr, format);
368 vsnprintf(buf, sizeof buf, format, arg_ptr);
374 serv_write(buf, len);
376 lprintf(9, ">>>%s", buf);
383 * Read binary data from server into memory using a series of
384 * server READ commands.
385 * \return the read content as StrBuf
387 int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf)
391 size_t thisblock = 0;
398 serv_printf("READ %d|"SIZE_T_FMT, 0, total_len);
399 if (StrBuf_ServGetln(Buf) > 0)
406 if (GetServerStatus(Buf, NULL) == 6)
408 StrBufCutLeft(Buf, 4);
409 thisblock = StrTol(Buf);
410 if (WCC->serv_sock==-1) {
415 if (WCC->ReadPos != NULL) {
416 pch = ChrPtr(WCC->ReadBuf);
418 YetRead = WCC->ReadPos - pch;
423 StillThere = StrLength(WCC->ReadBuf) -
429 total_len -= StillThere;
431 FlushStrBuf(WCC->ReadBuf);
436 rc = StrBufReadBLOB(Ret,
443 lprintf(1, "Server connection broken: %s\n",
446 WCC->serv_sock = (-1);
452 return StrLength(Ret);
455 return StrLength(Ret);
461 else while ((WCC->serv_sock!=-1) &&
462 (bytes < total_len)) {
464 if ((total_len - bytes) < thisblock) {
465 thisblock = total_len - bytes;
466 if (thisblock == 0) {
471 serv_printf("READ %d|%d", (int)bytes, (int)thisblock);
472 if (StrBuf_ServGetln(Buf) > 0)
474 if (GetServerStatus(Buf, NULL) == 6)
476 StrBufCutLeft(Buf, 4);
477 thisblock = StrTol(Buf);
478 if (WCC->serv_sock==-1) {
482 StrBuf_ServGetBLOBBuffered(Ret, thisblock);
486 lprintf(3, "Error: %s\n", ChrPtr(Buf) + 4);
491 return StrLength(Ret);
495 int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target)
499 const char *pch, *pchs;
500 int rlen, len, retval = 0;
504 if (StrLength(Hdr->ReadBuf) > 0) {
505 pchs = ChrPtr(Hdr->ReadBuf);
506 pch = strchr(pchs, '\n');
510 if (len > 0 && (*(pch - 1) == '\r') )
512 StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
513 StrBufCutLeft(Hdr->ReadBuf, len + 1);
518 while (retval == 0) {
520 pchs = ChrPtr(Hdr->ReadBuf);
522 pch = strchr(pchs, '\n');
524 retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING);
525 pchs = ChrPtr(Hdr->ReadBuf);
526 pch = strchr(pchs, '\n');
535 if ((retval > 0) && (pch != NULL)) {
538 if (len > 0 && (*(pch - 1) == '\r') )
540 StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
541 StrBufCutLeft(Hdr->ReadBuf, len + 1);
550 return StrBufTCP_read_buffered_line_fast(Target,
561 * This is a generic function to set up a master socket for listening on
562 * a TCP port. The server shuts down if the bind fails. (IPv4/IPv6 version)
564 * ip_addr IP address to bind
565 * port_number port number to bind
566 * queue_len number of incoming connections to allow in the queue
568 int webcit_tcp_server(char *ip_addr, int port_number, int queue_len)
571 struct sockaddr_in6 sin6;
572 struct sockaddr_in sin4;
576 memset(&sin6, 0, sizeof(sin6));
577 memset(&sin4, 0, sizeof(sin4));
578 sin6.sin6_family = AF_INET6;
579 sin4.sin_family = AF_INET;
581 if ( (ip_addr == NULL) /* any IPv6 */
582 || (IsEmptyStr(ip_addr))
583 || (!strcmp(ip_addr, "*"))
586 sin6.sin6_addr = in6addr_any;
588 else if (!strcmp(ip_addr, "0.0.0.0")) /* any IPv4 */
591 sin4.sin_addr.s_addr = INADDR_ANY;
593 else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':'))) /* specific IPv4 */
596 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
597 lprintf(1, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
598 return (-WC_EXIT_BIND);
601 else /* specific IPv6 */
604 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
605 lprintf(1, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
606 return (-WC_EXIT_BIND);
610 if (port_number == 0) {
611 lprintf(1, "Cannot start: no port number specified.\n");
612 return (-WC_EXIT_BIND);
614 sin6.sin6_port = htons((u_short) port_number);
615 sin4.sin_port = htons((u_short) port_number);
617 p = getprotobyname("tcp");
619 s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
621 lprintf(1, "Can't create a listening socket: %s\n", strerror(errno));
622 return (-WC_EXIT_BIND);
624 /* Set some socket options that make sense. */
626 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
628 if (ip_version == 6) {
629 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
632 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
636 lprintf(1, "Can't bind: %s\n", strerror(errno));
637 return (-WC_EXIT_BIND);
640 if (listen(s, queue_len) < 0) {
641 lprintf(1, "Can't listen: %s\n", strerror(errno));
642 return (-WC_EXIT_BIND);
649 * Create a Unix domain socket and listen on it
650 * sockpath - file name of the unix domain socket
651 * queue_len - Number of incoming connections to allow in the queue
653 int webcit_uds_server(char *sockpath, int queue_len)
655 struct sockaddr_un addr;
658 int actual_queue_len;
660 actual_queue_len = queue_len;
661 if (actual_queue_len < 5) actual_queue_len = 5;
663 i = unlink(sockpath);
664 if ((i != 0) && (errno != ENOENT)) {
665 lprintf(1, "webcit: can't unlink %s: %s\n",
666 sockpath, strerror(errno));
667 return (-WC_EXIT_BIND);
670 memset(&addr, 0, sizeof(addr));
671 addr.sun_family = AF_UNIX;
672 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
674 s = socket(AF_UNIX, SOCK_STREAM, 0);
676 lprintf(1, "webcit: Can't create a unix domain socket: %s\n", strerror(errno));
677 return (-WC_EXIT_BIND);
680 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
681 lprintf(1, "webcit: Can't bind: %s\n",
683 return (-WC_EXIT_BIND);
686 if (listen(s, actual_queue_len) < 0) {
687 lprintf(1, "webcit: Can't listen: %s\n",
689 return (-WC_EXIT_BIND);
692 chmod(sockpath, 0777);
700 * Read data from the client socket.
702 * sock socket fd to read from
703 * buf buffer to read into
704 * bytes number of bytes to read
705 * timeout Number of seconds to wait before timing out
707 * Possible return values:
708 * 1 Requested number of bytes has been read.
709 * 0 Request timed out.
710 * -1 Connection is broken, or other error.
712 int client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout)
722 baselen = StrLength(Target);
724 if (Hdr->Pos == NULL)
725 Hdr->Pos = ChrPtr(Hdr->ReadBuf);
726 bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf));
728 if (bytes < bufremain)
730 StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0);
731 StrBufCutLeft(Hdr->ReadBuf, bufremain);
733 if (bytes > bufremain)
735 while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) &&
737 retval = client_read_sslbuffer(Hdr->ReadBuf, timeout);
739 StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */
741 write(2, "\033[32m", 5);
742 write(2, buf, bytes);
743 write(2, "\033[30m", 5);
748 lprintf(2, "client_read_ssl() failed\n");
757 retval = StrBufReadBLOBBuffered(Target,
766 lprintf(2, "client_read() failed: %s\n",
773 write(2, "\033[32m", 5);
774 write(2, buf, bytes);
775 write(2, "\033[30m", 5);
782 * Begin buffering HTTP output so we can transmit it all in one write operation later.
784 void begin_burst(void)
786 if (WC->WBuf == NULL) {
787 WC->WBuf = NewStrBufPlain(NULL, 32768);
793 * Finish buffering HTTP output. [Compress using zlib and] output with a Content-Length: header.
798 const char *ptr, *eptr;
804 if (!DisableGzip && (WCC->Hdr->HR.gzip_ok))
806 if (CompressBuffer(WCC->WBuf) > 0)
807 hprintf("Content-encoding: gzip\r\n");
809 lprintf(CTDL_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno));
814 if (WCC->WFBuf != NULL) {
815 WildFireSerializePayload(WCC->WFBuf, WCC->HBuf, &WCC->Hdr->nWildfireHeaders, NULL);
816 FreeStrBuf(&WCC->WFBuf);
819 if (WCC->Hdr->HR.prohibit_caching)
820 hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
821 hprintf("Content-length: %d\r\n\r\n", StrLength(WCC->WBuf));
823 ptr = ChrPtr(WCC->HBuf);
824 count = StrLength(WCC->HBuf);
829 client_write_ssl(WCC->HBuf);
830 client_write_ssl(WCC->WBuf);
838 write(2, "\033[34m", 5);
839 write(2, ptr, StrLength(WCC->WBuf));
840 write(2, "\033[30m", 5);
842 if (WCC->Hdr->http_sock == -1)
844 fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
846 while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)){
847 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
849 FD_SET(WCC->Hdr->http_sock, &wset);
850 if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
851 lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno));
856 if ((WCC->Hdr->http_sock == -1) ||
857 (res = write(WCC->Hdr->http_sock,
860 lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno));
868 ptr = ChrPtr(WCC->WBuf);
869 count = StrLength(WCC->WBuf);
874 write(2, "\033[34m", 5);
875 write(2, ptr, StrLength(WCC->WBuf));
876 write(2, "\033[30m", 5);
879 while ((ptr < eptr) && (WCC->Hdr->http_sock != -1)) {
880 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
882 FD_SET(WCC->Hdr->http_sock, &wset);
883 if (select(WCC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
884 lprintf(2, "client_write: Socket select failed (%s)\n", strerror(errno));
889 if ((WCC->Hdr->http_sock == -1) ||
890 (res = write(WCC->Hdr->http_sock,
893 lprintf(2, "client_write: Socket write failed (%s)\n", strerror(errno));
901 return StrLength(WCC->WBuf);
906 * lingering_close() a`la Apache. see
907 * http://www.apache.org/docs/misc/fin_wait_2.html for rationale
909 int lingering_close(int fd)
914 struct timeval tv, start;
916 gettimeofday(&start, NULL);
922 gettimeofday(&tv, NULL);
923 tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
924 tv.tv_usec = start.tv_usec - tv.tv_usec;
925 if (tv.tv_usec < 0) {
927 tv.tv_usec += 1000000;
931 i = select(fd + 1, &set, NULL, NULL, &tv);
932 } while (i == -1 && errno == EINTR);
937 i = read(fd, buf, sizeof buf);
938 } while (i != 0 && (i != -1 || errno == EINTR));
944 HttpNewModule_TCPSOCKETS
945 (ParsedHttpHdrs *httpreq)
948 httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
952 HttpDetachModule_TCPSOCKETS
953 (ParsedHttpHdrs *httpreq)
956 FlushStrBuf(httpreq->ReadBuf);
957 ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ);
961 HttpDestroyModule_TCPSOCKETS
962 (ParsedHttpHdrs *httpreq)
965 FreeStrBuf(&httpreq->ReadBuf);
970 SessionNewModule_TCPSOCKETS
973 sess->CLineBuf = NewStrBuf();
974 sess->MigrateReadLineBuf = NewStrBuf();
978 SessionDestroyModule_TCPSOCKETS
981 FreeStrBuf(&sess->CLineBuf);
982 FreeStrBuf(&sess->ReadBuf);
983 sess->ReadPos = NULL;
984 FreeStrBuf(&sess->MigrateReadLineBuf);
985 if (sess->serv_sock > 0)
986 close(sess->serv_sock);