Additional work on the alias table. This is taking longer than it ought to because...
[citadel] / webcit / tcp_sockets.c
1 /*
2  * Copyright (c) 1987-2021 by the citadel.org team
3  *
4  * This program is open source software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License, version 3.
6  * 
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12
13 /*
14  * Uncomment this to log all communications with the Citadel server
15 #define SERV_TRACE 1
16  */
17
18 #include "webcit.h"
19 #include "webserver.h"
20
21 long MaxRead = -1; /* should we do READ scattered or all at once? */
22
23 /*
24  * register the timeout
25  */
26 RETSIGTYPE timeout(int signum)
27 {
28         syslog(LOG_WARNING, "Connection timed out; unable to reach citserver\n");
29         /* no exit here, since we need to server the connection unreachable thing. exit(3); */
30 }
31
32
33 /*
34  * Client side - connect to a unix domain socket
35  */
36 int uds_connectsock(char *sockpath)
37 {
38         struct sockaddr_un addr;
39         int s;
40
41         memset(&addr, 0, sizeof(addr));
42         addr.sun_family = AF_UNIX;
43         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
44
45         s = socket(AF_UNIX, SOCK_STREAM, 0);
46         if (s < 0) {
47                 syslog(LOG_WARNING, "Can't create socket [%s]: %s\n", sockpath, strerror(errno));
48                 return(-1);
49         }
50
51         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
52                 syslog(LOG_WARNING, "Can't connect [%s]: %s\n", sockpath, strerror(errno));
53                 close(s);
54                 return(-1);
55         }
56         return s;
57 }
58
59
60 /*
61  * TCP client - connect to a host/port 
62  */
63 int tcp_connectsock(char *host, char *service)
64 {
65         struct in6_addr serveraddr;
66         struct addrinfo hints;
67         struct addrinfo *res = NULL;
68         struct addrinfo *ai = NULL;
69         int rc = (-1);
70         int s = (-1);
71
72         if ((host == NULL) || IsEmptyStr(host))
73                 return (-1);
74         if ((service == NULL) || IsEmptyStr(service))
75                 return (-1);
76
77         syslog(LOG_DEBUG, "tcp_connectsock(%s,%s)\n", host, service);
78
79         memset(&hints, 0x00, sizeof(hints));
80         hints.ai_flags = AI_NUMERICSERV;
81         hints.ai_family = AF_UNSPEC;
82         hints.ai_socktype = SOCK_STREAM;
83
84         /*
85          * Handle numeric IPv4 and IPv6 addresses
86          */
87         rc = inet_pton(AF_INET, host, &serveraddr);
88         if (rc == 1) {                                          /* dotted quad */
89                 hints.ai_family = AF_INET;
90                 hints.ai_flags |= AI_NUMERICHOST;
91         } else {
92                 rc = inet_pton(AF_INET6, host, &serveraddr);
93                 if (rc == 1) {                                  /* IPv6 address */
94                         hints.ai_family = AF_INET6;
95                         hints.ai_flags |= AI_NUMERICHOST;
96                 }
97         }
98
99         /* Begin the connection process */
100
101         rc = getaddrinfo(host, service, &hints, &res);
102         if (rc != 0) {
103                 syslog(LOG_DEBUG, "%s: %s\n", host, gai_strerror(rc));
104                 freeaddrinfo(res);
105                 return(-1);
106         }
107
108         /*
109          * Try all available addresses until we connect to one or until we run out.
110          */
111         for (ai = res; ai != NULL; ai = ai->ai_next) {
112
113                 if (ai->ai_family == AF_INET) syslog(LOG_DEBUG, "Trying IPv4\n");
114                 else if (ai->ai_family == AF_INET6) syslog(LOG_DEBUG, "Trying IPv6\n");
115                 else syslog(LOG_WARNING, "This is going to fail.\n");
116
117                 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
118                 if (s < 0) {
119                         syslog(LOG_WARNING, "socket() failed: %s\n", strerror(errno));
120                         freeaddrinfo(res);
121                         return(-1);
122                 }
123                 rc = connect(s, ai->ai_addr, ai->ai_addrlen);
124                 if (rc >= 0) {
125                         int fdflags;
126                         freeaddrinfo(res);
127
128                         fdflags = fcntl(rc, F_GETFL);
129                         if (fdflags < 0) {
130                                 syslog(LOG_ERR,
131                                        "unable to get socket %d flags! %s \n",
132                                        rc,
133                                        strerror(errno));
134                                 close(rc);
135                                 return -1;
136                         }
137                         fdflags = fdflags | O_NONBLOCK;
138                         if (fcntl(rc, F_SETFL, fdflags) < 0) {
139                                 syslog(LOG_ERR,
140                                        "unable to set socket %d nonblocking flags! %s \n",
141                                        rc,
142                                        strerror(errno));
143                                 close(s);
144                                 return -1;
145                         }
146
147                         return(s);
148                 }
149                 else {
150                         syslog(LOG_WARNING, "connect() failed: %s\n", strerror(errno));
151                         close(s);
152                 }
153         }
154         freeaddrinfo(res);
155         return(-1);
156 }
157
158
159 /*
160  *  input string from pipe
161  */
162 int serv_getln(char *strbuf, int bufsize)
163 {
164         int len;
165
166         *strbuf = '\0';
167         StrBuf_ServGetln(WC->MigrateReadLineBuf);
168         len = StrLength(WC->MigrateReadLineBuf);
169         if (len > bufsize)
170                 len = bufsize - 1;
171         memcpy(strbuf, ChrPtr(WC->MigrateReadLineBuf), len);
172         FlushStrBuf(WC->MigrateReadLineBuf);
173         strbuf[len] = '\0';
174 #ifdef SERV_TRACE
175         syslog(LOG_DEBUG, "%3d<<<%s\n", WC->serv_sock, strbuf);
176 #endif
177         return len;
178 }
179
180
181 int StrBuf_ServGetln(StrBuf *buf)
182 {
183         const char *ErrStr = NULL;
184         int rc;
185         
186         if (!WC->connected)
187                 return -1;
188
189         FlushStrBuf(buf);
190         rc = StrBufTCP_read_buffered_line_fast(buf, 
191                                                WC->ReadBuf, 
192                                                &WC->ReadPos, 
193                                                &WC->serv_sock, 
194                                                5, 1, 
195                                                &ErrStr);
196         if (rc < 0)
197         {
198                 syslog(LOG_INFO, "StrBuf_ServGetln(): Server connection broken: %s\n",
199                         (ErrStr)?ErrStr:"");
200                 wc_backtrace(LOG_INFO);
201                 if (WC->serv_sock > 0) close(WC->serv_sock);
202                 WC->serv_sock = (-1);
203                 WC->connected = 0;
204                 WC->logged_in = 0;
205         }
206 #ifdef SERV_TRACE
207         else 
208         {
209                 long pos = 0;
210                 if (WC->ReadPos != NULL)
211                         pos = WC->ReadPos - ChrPtr(WC->ReadBuf);
212                 syslog(LOG_DEBUG, "%3d<<<[%ld]%s\n", WC->serv_sock, pos, ChrPtr(buf));
213         }
214 #endif
215         return rc;
216 }
217
218 int StrBuf_ServGetBLOBBuffered(StrBuf *buf, long BlobSize)
219 {
220         const char *ErrStr;
221         int rc;
222         
223         rc = StrBufReadBLOBBuffered(buf, 
224                                     WC->ReadBuf, 
225                                     &WC->ReadPos,
226                                     &WC->serv_sock, 
227                                     1, 
228                                     BlobSize, 
229                                     NNN_TERM,
230                                     &ErrStr);
231         if (rc < 0)
232         {
233                 syslog(LOG_INFO, "StrBuf_ServGetBLOBBuffered(): Server connection broken: %s\n",
234                         (ErrStr)?ErrStr:"");
235                 wc_backtrace(LOG_INFO);
236                 if (WC->serv_sock > 0) close(WC->serv_sock);
237                 WC->serv_sock = (-1);
238                 WC->connected = 0;
239                 WC->logged_in = 0;
240         }
241 #ifdef SERV_TRACE
242         else
243                 syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
244 #endif
245
246         return rc;
247 }
248
249 int StrBuf_ServGetBLOB(StrBuf *buf, long BlobSize)
250 {
251         const char *ErrStr;
252         int rc;
253         
254         WC->ReadPos = NULL;
255         rc = StrBufReadBLOB(buf, &WC->serv_sock, 1, BlobSize, &ErrStr);
256         if (rc < 0)
257         {
258                 syslog(LOG_INFO, "StrBuf_ServGetBLOB(): Server connection broken: %s\n",
259                         (ErrStr)?ErrStr:"");
260                 wc_backtrace(LOG_INFO);
261                 if (WC->serv_sock > 0) close(WC->serv_sock);
262                 WC->serv_sock = (-1);
263                 WC->connected = 0;
264                 WC->logged_in = 0;
265         }
266 #ifdef SERV_TRACE
267         else
268                 syslog(LOG_DEBUG, "%3d<<<BLOB: %d bytes\n", WC->serv_sock, StrLength(buf));
269 #endif
270
271         return rc;
272 }
273
274
275 void FlushReadBuf (void)
276 {
277         long len;
278         const char *pch;
279         const char *pche;
280
281         len = StrLength(WC->ReadBuf);
282         if ((len > 0) &&
283             (WC->ReadPos != NULL) && 
284             (WC->ReadPos != StrBufNOTNULL))
285                 
286         {
287                 pch = ChrPtr(WC->ReadBuf);
288                 pche = pch + len;
289                 if (WC->ReadPos != pche)
290                 {
291                         syslog(LOG_ERR,
292                                 "ERROR: somebody didn't eat his soup! Remaing Chars: %ld [%s]\n", 
293                                 (long)(pche - WC->ReadPos),
294                                 pche
295                         );
296                         syslog(LOG_ERR, 
297                                 "--------------------------------------------------------------------------------\n"
298                                 "Whole buf: [%s]\n"
299                                 "--------------------------------------------------------------------------------\n", 
300                                 pch);
301                         AppendImportantMessage(HKEY("Suppenkasper alert! watch your webcit logfile and get connected to your favourite opensource Crew."));
302                 }
303         }
304
305         FlushStrBuf(WC->ReadBuf);
306         WC->ReadPos = NULL;
307
308
309 }
310
311
312 /*
313  *  send binary to server
314  *  buf the buffer to write to citadel server
315  *  nbytes how many bytes to send to citadel server
316  */
317 int serv_write(const char *buf, int nbytes)
318 {
319         int bytes_written = 0;
320         int retval;
321
322         FlushReadBuf();
323         while (bytes_written < nbytes) {
324                 retval = write(WC->serv_sock, &buf[bytes_written],
325                                nbytes - bytes_written);
326                 if (retval < 1) {
327                         const char *ErrStr = strerror(errno);
328                         syslog(LOG_INFO, "serv_write(): Server connection broken: %s\n",
329                                 (ErrStr)?ErrStr:"");
330                         if (WC->serv_sock > 0) close(WC->serv_sock);
331                         WC->serv_sock = (-1);
332                         WC->connected = 0;
333                         WC->logged_in = 0;
334                         return 0;
335                 }
336                 bytes_written = bytes_written + retval;
337         }
338         return 1;
339 }
340
341
342 /*
343  *  send line to server
344  *  string the line to send to the citadel server
345  */
346 int serv_puts(const char *string)
347 {
348 #ifdef SERV_TRACE
349         syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, string);
350 #endif
351         FlushReadBuf();
352
353         if (!serv_write(string, strlen(string)))
354                 return 0;
355         return serv_write("\n", 1);
356 }
357
358 /*
359  *  send line to server
360  *  string the line to send to the citadel server
361  */
362 int serv_putbuf(const StrBuf *string)
363 {
364 #ifdef SERV_TRACE
365         syslog(LOG_DEBUG, "%3d>>>%s\n", WC->serv_sock, ChrPtr(string));
366 #endif
367         FlushReadBuf();
368
369         if (!serv_write(ChrPtr(string), StrLength(string)))
370                 return 0;
371         return serv_write("\n", 1);
372 }
373
374
375 /*
376  *  convenience function to send stuff to the server
377  *  format the formatstring
378  *  ... the entities to insert into format 
379  */
380 int serv_printf(const char *format,...)
381 {
382         va_list arg_ptr;
383         char buf[SIZ];
384         size_t len;
385         int rc;
386
387         FlushReadBuf();
388
389         va_start(arg_ptr, format);
390         vsnprintf(buf, sizeof buf, format, arg_ptr);
391         va_end(arg_ptr);
392
393         len = strlen(buf);
394         buf[len++] = '\n';
395         buf[len] = '\0';
396         rc = serv_write(buf, len);
397 #ifdef SERV_TRACE
398         syslog(LOG_DEBUG, ">>>%s", buf);
399 #endif
400         return rc;
401 }
402
403
404 /*
405  * Read binary data from server into memory using a series of server READ commands.
406  * returns the read content as StrBuf
407  */
408 int serv_read_binary(StrBuf *Ret, size_t total_len, StrBuf *Buf) 
409 {
410         size_t bytes_read = 0;
411         size_t this_block = 0;
412         int rc = 6;
413         int ServerRc = 6;
414
415         if (Ret == NULL) {
416                 return -1;
417         }
418
419         while ((bytes_read < total_len) && (ServerRc == 6)) {
420
421                 if (WC->serv_sock==-1) {
422                         FlushStrBuf(Ret); 
423                         return -1; 
424                 }
425
426                 serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, bytes_read, total_len-bytes_read);
427                 if ( (rc = StrBuf_ServGetln(Buf) > 0) &&
428                      (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) ) 
429                 {
430                         if (rc < 0)
431                                 return rc;
432                         StrBufCutLeft(Buf, 4);
433                         this_block = StrTol(Buf);
434                         rc = StrBuf_ServGetBLOBBuffered(Ret, this_block);
435                         if (rc < 0) {
436                                 syslog(LOG_INFO, "Server connection broken during download\n");
437                                 wc_backtrace(LOG_INFO);
438                                 if (WC->serv_sock > 0) close(WC->serv_sock);
439                                 WC->serv_sock = (-1);
440                                 WC->connected = 0;
441                                 WC->logged_in = 0;
442                                 return rc;
443                         }
444                         bytes_read += rc;
445                 }
446         }
447
448         return StrLength(Ret);
449 }
450
451
452 int client_write(StrBuf *ThisBuf)
453 {
454         const char *ptr, *eptr;
455         long count;
456         ssize_t res = 0;
457         fd_set wset;
458         int fdflags;
459
460         ptr = ChrPtr(ThisBuf);
461         count = StrLength(ThisBuf);
462         eptr = ptr + count;
463
464         fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
465
466         while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
467                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
468                         FD_ZERO(&wset);
469                         FD_SET(WC->Hdr->http_sock, &wset);
470                         if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
471                                 syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
472                                 return -1;
473                         }
474                 }
475
476                 if ((WC->Hdr->http_sock == -1) || 
477                     ((res = write(WC->Hdr->http_sock, ptr, count)),
478                      (res == -1)))
479                 {
480                         syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
481                         wc_backtrace(LOG_INFO);
482                         return -1;
483                 }
484                 count -= res;
485                 ptr += res;
486         }
487         return 0;
488 }
489
490
491 int
492 read_serv_chunk(
493
494         StrBuf *Buf,
495         size_t total_len,
496         size_t *bytes_read
497         )
498 {
499         int rc;
500         int ServerRc;
501
502         serv_printf("READ "SIZE_T_FMT"|"SIZE_T_FMT, *bytes_read, total_len-(*bytes_read));
503         if ( (rc = StrBuf_ServGetln(Buf) > 0) &&
504              (ServerRc = GetServerStatus(Buf, NULL), ServerRc == 6) ) 
505         {
506                 size_t this_block = 0;
507
508                 if (rc < 0)
509                         return rc;
510
511                 StrBufCutLeft(Buf, 4);
512                 this_block = StrTol(Buf);
513                 rc = StrBuf_ServGetBLOBBuffered(WC->WBuf, this_block);
514                 if (rc < 0) {
515                         syslog(LOG_INFO, "Server connection broken during download\n");
516                         wc_backtrace(LOG_INFO);
517                         if (WC->serv_sock > 0) close(WC->serv_sock);
518                         WC->serv_sock = (-1);
519                         WC->connected = 0;
520                         WC->logged_in = 0;
521                         return rc;
522                 }
523                 *bytes_read += rc;
524         }
525         return 6;
526 }
527
528 static inline int send_http(StrBuf *Buf)
529 {
530 #ifdef HAVE_OPENSSL
531         if (is_https)
532                 return client_write_ssl(Buf);
533         else
534 #endif
535                 return client_write(Buf);
536 }
537 /*
538  * Read binary data from server into memory using a series of server READ commands.
539  * returns the read content as StrBuf
540  */
541 void serv_read_binary_to_http(StrBuf *MimeType, size_t total_len, int is_static, int detect_mime)
542 {
543         int ServerRc = 6;
544         size_t bytes_read = 0;
545         int first = 1;
546         int client_con_state = 0;
547         int chunked = 0;
548         int is_gzip = 0;
549         const char *Err = NULL;
550         StrBuf *BufHeader = NULL;
551         StrBuf *Buf;
552         StrBuf *pBuf = NULL;
553         vStreamT *SC = NULL;
554         IOBuffer ReadBuffer;
555         IOBuffer WriteBuffer;
556         
557
558         Buf = NewStrBuf();
559
560         if (WC->Hdr->HaveRange)
561         {
562                 WC->Hdr->HaveRange++;
563                 WC->Hdr->TotalBytes = total_len;
564                 /* open range? or beyound file border? correct the numbers. */
565                 if ((WC->Hdr->RangeTil == -1) || (WC->Hdr->RangeTil>= total_len))
566                         WC->Hdr->RangeTil = total_len - 1;
567                 bytes_read = WC->Hdr->RangeStart;
568                 total_len = WC->Hdr->RangeTil;
569         }
570         else
571                 chunked = total_len > SIZ * 10; /* TODO: disallow for HTTP / 1.0 */
572
573         if (chunked)
574         {
575                 BufHeader = NewStrBuf();
576         }
577
578         if ((detect_mime != 0) && (bytes_read != 0))
579         {
580                 /* need to read first chunk to detect mime, though the client doesn't care */
581                 size_t bytes_read = 0;
582                 const char *CT;
583
584                 ServerRc = read_serv_chunk(
585                         Buf,
586                         total_len,
587                         &bytes_read);
588
589                 if (ServerRc != 6)
590                 {
591                         FreeStrBuf(&BufHeader);
592                         FreeStrBuf(&Buf);
593                         return;
594                 }
595                 CT = GuessMimeType(SKEY(WC->WBuf));
596                 FlushStrBuf(WC->WBuf);
597                 StrBufPlain(MimeType, CT, -1);
598                 CheckGZipCompressionAllowed(SKEY(MimeType));
599                 detect_mime = 0;
600                 FreeStrBuf(&Buf);
601         }
602
603         memset(&WriteBuffer, 0, sizeof(IOBuffer));
604         if (chunked && !DisableGzip && WC->Hdr->HR.gzip_ok)
605         {
606                 is_gzip = 1;
607                 SC = StrBufNewStreamContext (eZLibEncode, &Err);
608                 if (SC == NULL) {
609                         syslog(LOG_ERR, "Error while initializing stream context: %s", Err);
610                         FreeStrBuf(&Buf);
611                         return;
612                 }
613
614                 memset(&ReadBuffer, 0, sizeof(IOBuffer));
615                 ReadBuffer.Buf = WC->WBuf;
616
617                 WriteBuffer.Buf = NewStrBufPlain(NULL, SIZ*2);;
618                 pBuf = WriteBuffer.Buf;
619         }
620         else
621         {
622                 pBuf = WC->WBuf;
623         }
624
625         if (!detect_mime)
626         {
627                 http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
628                 
629                 if (send_http(WC->HBuf) < 0)
630                 {
631                         FreeStrBuf(&Buf);
632                         FreeStrBuf(&WriteBuffer.Buf);
633                         FreeStrBuf(&BufHeader);
634                         if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
635                                 syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
636                         }
637                         return;
638                 }
639         }
640
641         while ((bytes_read < total_len) &&
642                (ServerRc == 6) &&
643                (client_con_state == 0))
644         {
645
646                 if (WC->serv_sock==-1) {
647                         FlushStrBuf(WC->WBuf); 
648                         FreeStrBuf(&Buf);
649                         FreeStrBuf(&WriteBuffer.Buf);
650                         FreeStrBuf(&BufHeader);
651                         StrBufDestroyStreamContext(eZLibEncode, &SC, &Err);
652                         if (StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
653                                 syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
654                         }
655                         return;
656                 }
657
658                 ServerRc = read_serv_chunk(
659                         Buf,
660                         total_len,
661                         &bytes_read);
662                 if (ServerRc != 6)
663                         break;
664
665                 if (detect_mime)
666                 {
667                         const char *CT;
668                         detect_mime = 0;
669                         
670                         CT = GuessMimeType(SKEY(WC->WBuf));
671                         StrBufPlain(MimeType, CT, -1);
672                         if (is_gzip) {
673                                 CheckGZipCompressionAllowed(SKEY(MimeType));
674                                 is_gzip = WC->Hdr->HR.gzip_ok;
675                         }
676                         http_transmit_headers(ChrPtr(MimeType), is_static, chunked, is_gzip);
677                         
678                         client_con_state = send_http(WC->HBuf);
679                 }
680
681                 if (is_gzip)
682                 {
683                         int done = (bytes_read == total_len);
684                         while ((IOBufferStrLength(&ReadBuffer) > 0) && (client_con_state == 0)) {
685                                 int rc;
686
687                                 do {
688                                         rc = StrBufStreamTranscode(eZLibEncode, &WriteBuffer, &ReadBuffer, NULL, -1, SC, done, &Err);
689
690                                         if (StrLength (pBuf) > 0) {
691                                                 StrBufPrintf(BufHeader, "%s%x\r\n", 
692                                                      (first)?"":"\r\n",
693                                                              StrLength (pBuf));
694                                                 first = 0;
695                                                 client_con_state = send_http(BufHeader);
696                                                 if (client_con_state == 0) {
697                                                         client_con_state = send_http(pBuf);
698                                                 }
699                                                 FlushStrBuf(pBuf);
700                                         }
701                                 } while ((rc == 1) && (StrLength(pBuf) > 0));
702                         }
703                         FlushStrBuf(WC->WBuf);
704                 }
705                 else {
706                         if ((chunked) && (client_con_state == 0))
707                         {
708                                 StrBufPrintf(BufHeader, "%s%x\r\n", 
709                                              (first)?"":"\r\n",
710                                              StrLength (pBuf));
711                                 first = 0;
712                                 client_con_state = send_http(BufHeader);
713                         }
714
715                         if (client_con_state == 0)
716                                 client_con_state = send_http(pBuf);
717
718                         FlushStrBuf(pBuf);
719                 }
720         }
721
722         if (SC && StrBufDestroyStreamContext(eZLibEncode, &SC, &Err) && Err) {
723                 syslog(LOG_ERR, "Error while destroying stream context: %s", Err);
724         }
725         FreeStrBuf(&WriteBuffer.Buf);
726         if ((chunked) && (client_con_state == 0))
727         {
728                 StrBufPlain(BufHeader, HKEY("\r\n0\r\n\r\n"));
729                 if (send_http(BufHeader) < 0)
730                 {
731                         FreeStrBuf(&Buf);
732                         FreeStrBuf(&BufHeader);
733                         return;
734                 }
735         }
736         FreeStrBuf(&BufHeader);
737         FreeStrBuf(&Buf);
738 }
739
740 int ClientGetLine(ParsedHttpHdrs *Hdr, StrBuf *Target)
741 {
742         const char *Error;
743 #ifdef HAVE_OPENSSL
744         const char *pch, *pchs;
745         int rlen, len, retval = 0;
746
747         if (is_https) {
748                 int ntries = 0;
749                 if (StrLength(Hdr->ReadBuf) > 0)
750                 {
751                         pchs = ChrPtr(Hdr->ReadBuf);
752                         pch = strchr(pchs, '\n');
753                         if (pch != NULL) {
754                                 rlen = 0;
755                                 len = pch - pchs;
756                                 if (len > 0 && (*(pch - 1) == '\r') )
757                                         rlen ++;
758                                 StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
759                                 StrBufCutLeft(Hdr->ReadBuf, len + 1);
760                                 return len - rlen;
761                         }
762                 }
763
764                 while (retval == 0) { 
765                                 pch = NULL;
766                                 pchs = ChrPtr(Hdr->ReadBuf);
767                                 if (*pchs != '\0')
768                                         pch = strchr(pchs, '\n');
769                                 if (pch == NULL) {
770                                         retval = client_read_sslbuffer(Hdr->ReadBuf, SLEEPING);
771                                         pchs = ChrPtr(Hdr->ReadBuf);
772                                         pch = strchr(pchs, '\n');
773                                         if (pch == NULL)
774                                                 retval = 0;
775                                 }
776                                 if (retval == 0) {
777                                         sleeeeeeeeeep(1);
778                                         ntries ++;
779                                 }
780                                 if (ntries > 10)
781                                         return 0;
782                 }
783                 if ((retval > 0) && (pch != NULL)) {
784                         rlen = 0;
785                         len = pch - pchs;
786                         if (len > 0 && (*(pch - 1) == '\r') )
787                                 rlen ++;
788                         StrBufSub(Target, Hdr->ReadBuf, 0, len - rlen);
789                         StrBufCutLeft(Hdr->ReadBuf, len + 1);
790                         return len - rlen;
791
792                 }
793                 else 
794                         return -1;
795         }
796         else 
797 #endif
798                 return StrBufTCP_read_buffered_line_fast(Target, 
799                                                          Hdr->ReadBuf,
800                                                          &Hdr->Pos,
801                                                          &Hdr->http_sock,
802                                                          5,
803                                                          1,
804                                                          &Error);
805 }
806
807
808 /* 
809  * This is a generic function to set up a master socket for listening on
810  * a TCP port.  The server shuts down if the bind fails.  (IPv4/IPv6 version)
811  *
812  * ip_addr      IP address to bind
813  * port_number  port number to bind
814  * queue_len    number of incoming connections to allow in the queue
815  */
816 int webcit_tcp_server(const char *ip_addr, int port_number, int queue_len)
817 {
818         const char *ipv4broadcast = "0.0.0.0";
819         int IsDefault = 0;
820         struct protoent *p;
821         struct sockaddr_in6 sin6;
822         struct sockaddr_in sin4;
823         int s, i, b;
824         int ip_version = 6;
825
826 retry:
827         memset(&sin6, 0, sizeof(sin6));
828         memset(&sin4, 0, sizeof(sin4));
829         sin6.sin6_family = AF_INET6;
830         sin4.sin_family = AF_INET;
831
832         if (    (ip_addr == NULL)                                                       /* any IPv6 */
833                 || (IsEmptyStr(ip_addr))
834                 || (!strcmp(ip_addr, "*"))
835         ) {
836                 IsDefault = 1;
837                 ip_version = 6;
838                 sin6.sin6_addr = in6addr_any;
839         }
840         else if (!strcmp(ip_addr, "0.0.0.0"))                                           /* any IPv4 */
841         {
842                 ip_version = 4;
843                 sin4.sin_addr.s_addr = INADDR_ANY;
844         }
845         else if ((strchr(ip_addr, '.')) && (!strchr(ip_addr, ':')))                     /* specific IPv4 */
846         {
847                 ip_version = 4;
848                 if (inet_pton(AF_INET, ip_addr, &sin4.sin_addr) <= 0) {
849                         syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
850                         return (-WC_EXIT_BIND);
851                 }
852         }
853         else                                                                            /* specific IPv6 */
854         {
855                 ip_version = 6;
856                 if (inet_pton(AF_INET6, ip_addr, &sin6.sin6_addr) <= 0) {
857                         syslog(LOG_WARNING, "Error binding to [%s] : %s\n", ip_addr, strerror(errno));
858                         return (-WC_EXIT_BIND);
859                 }
860         }
861
862         if (port_number == 0) {
863                 syslog(LOG_WARNING, "Cannot start: no port number specified.\n");
864                 return (-WC_EXIT_BIND);
865         }
866         sin6.sin6_port = htons((u_short) port_number);
867         sin4.sin_port = htons((u_short) port_number);
868
869         p = getprotobyname("tcp");
870
871         s = socket( ((ip_version == 6) ? PF_INET6 : PF_INET), SOCK_STREAM, (p->p_proto));
872         if (s < 0) {
873                 if (IsDefault && (errno == EAFNOSUPPORT))
874                 {
875                         s = 0;
876                         ip_addr = ipv4broadcast;
877                         goto retry;
878                 }
879                 syslog(LOG_WARNING, "Can't create a listening socket: %s\n", strerror(errno));
880                 return (-WC_EXIT_BIND);
881         }
882         /* Set some socket options that make sense. */
883         i = 1;
884         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
885
886         if (ip_version == 6) {
887                 b = bind(s, (struct sockaddr *) &sin6, sizeof(sin6));
888         }
889         else {
890                 b = bind(s, (struct sockaddr *) &sin4, sizeof(sin4));
891         }
892
893         if (b < 0) {
894                 syslog(LOG_ERR, "Can't bind: %s\n", strerror(errno));
895                 close(s);
896                 return (-WC_EXIT_BIND);
897         }
898
899         if (listen(s, queue_len) < 0) {
900                 syslog(LOG_ERR, "Can't listen: %s\n", strerror(errno));
901                 close(s);
902                 return (-WC_EXIT_BIND);
903         }
904         return (s);
905 }
906
907
908 /*
909  * Create a Unix domain socket and listen on it
910  * sockpath - file name of the unix domain socket
911  * queue_len - Number of incoming connections to allow in the queue
912  */
913 int webcit_uds_server(char *sockpath, int queue_len) {
914         struct sockaddr_un addr;
915         int s;
916         int i;
917         int actual_queue_len;
918
919         actual_queue_len = queue_len;
920         if (actual_queue_len < 5) actual_queue_len = 5;
921
922         i = unlink(sockpath);
923         if ((i != 0) && (errno != ENOENT)) {
924                 syslog(LOG_WARNING, "webcit: can't unlink %s: %s\n",
925                         sockpath, strerror(errno));
926                 return (-WC_EXIT_BIND);
927         }
928
929         memset(&addr, 0, sizeof(addr));
930         addr.sun_family = AF_UNIX;
931         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
932
933         s = socket(AF_UNIX, SOCK_STREAM, 0);
934         if (s < 0) {
935                 syslog(LOG_WARNING, "webcit: Can't create a unix domain socket: %s\n", strerror(errno));
936                 return (-WC_EXIT_BIND);
937         }
938
939         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
940                 syslog(LOG_WARNING, "webcit: Can't bind: %s\n", strerror(errno));
941                 close(s);
942                 return (-WC_EXIT_BIND);
943         }
944
945         if (listen(s, actual_queue_len) < 0) {
946                 syslog(LOG_WARNING, "webcit: Can't listen: %s\n", strerror(errno));
947                 close(s);
948                 return (-WC_EXIT_BIND);
949         }
950
951         chmod(sockpath, 0777);
952         return(s);
953 }
954
955
956 /*
957  * Read data from the client socket.
958  *
959  * sock         socket fd to read from
960  * buf          buffer to read into 
961  * bytes        number of bytes to read
962  * timeout      Number of seconds to wait before timing out
963  *
964  * Possible return values:
965  *      1       Requested number of bytes has been read.
966  *      0       Request timed out.
967  *      -1      Connection is broken, or other error.
968  */
969 int client_read_to(ParsedHttpHdrs *Hdr, StrBuf *Target, int bytes, int timeout) {
970         const char *Error;
971         int retval = 0;
972
973 #ifdef HAVE_OPENSSL
974         if (is_https) {
975                 long bufremain = 0;
976                 long baselen;
977
978                 baselen = StrLength(Target);
979
980                 if (Hdr->Pos == NULL) {
981                         Hdr->Pos = ChrPtr(Hdr->ReadBuf);
982                 }
983
984                 if (StrLength(Hdr->ReadBuf) > 0) {
985                         bufremain = StrLength(Hdr->ReadBuf) - (Hdr->Pos - ChrPtr(Hdr->ReadBuf));
986                         
987                         if (bytes < bufremain)
988                                 bufremain = bytes;
989                         StrBufAppendBufPlain(Target, Hdr->Pos, bufremain, 0);
990                         StrBufCutLeft(Hdr->ReadBuf, bufremain);
991                 }
992
993                 if (bytes > bufremain) {
994                         while ((StrLength(Hdr->ReadBuf) + StrLength(Target) < bytes + baselen) &&
995                                (retval >= 0))
996                                 retval = client_read_sslbuffer(Hdr->ReadBuf, timeout);
997                         if (retval >= 0) {
998                                 StrBufAppendBuf(Target, Hdr->ReadBuf, 0); /* todo: Buf > bytes? */
999                                 return 1;
1000                         }
1001                         else {
1002                                 syslog(LOG_INFO, "client_read_ssl() failed\n");
1003                                 return -1;
1004                         }
1005                 }
1006                 else 
1007                         return 1;
1008         }
1009 #endif
1010         retval = StrBufReadBLOBBuffered(Target, 
1011                                         Hdr->ReadBuf, 
1012                                         &Hdr->Pos, 
1013                                         &Hdr->http_sock, 
1014                                         1, 
1015                                         bytes,
1016                                         O_TERM,
1017                                         &Error);
1018         if (retval < 0) {
1019                 syslog(LOG_INFO, "client_read() failed: %s\n", Error);
1020                 wc_backtrace(LOG_DEBUG);
1021                 return retval;
1022         }
1023
1024         return 1;
1025 }
1026
1027
1028 /*
1029  * Begin buffering HTTP output so we can transmit it all in one write operation later.
1030  */
1031 void begin_burst(void)
1032 {
1033         if (WC->WBuf == NULL) {
1034                 WC->WBuf = NewStrBufPlain(NULL, 32768);
1035         }
1036 }
1037
1038
1039 /*
1040  * Finish buffering HTTP output.  [Compress using zlib and] output with a Content-Length: header.
1041  */
1042 long end_burst(void)
1043 {
1044         const char *ptr, *eptr;
1045         long count;
1046         ssize_t res = 0;
1047         fd_set wset;
1048         int fdflags;
1049
1050         if (!DisableGzip && (WC->Hdr->HR.gzip_ok))
1051         {
1052                 if (CompressBuffer(WC->WBuf) > 0)
1053                         hprintf("Content-encoding: gzip\r\n");
1054                 else {
1055                         syslog(LOG_ALERT, "Compression failed: %d [%s] sending uncompressed\n", errno, strerror(errno));
1056                         wc_backtrace(LOG_INFO);
1057                 }
1058         }
1059
1060         if (WC->WFBuf != NULL) {
1061                 WildFireSerializePayload(WC->WFBuf, WC->HBuf, &WC->Hdr->nWildfireHeaders, NULL);
1062                 FreeStrBuf(&WC->WFBuf);
1063         }
1064
1065         if (WC->Hdr->HR.prohibit_caching)
1066                 hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
1067         hprintf("Content-length: %d\r\n\r\n", StrLength(WC->WBuf));
1068
1069         ptr = ChrPtr(WC->HBuf);
1070         count = StrLength(WC->HBuf);
1071         eptr = ptr + count;
1072
1073 #ifdef HAVE_OPENSSL
1074         if (is_https) {
1075                 client_write_ssl(WC->HBuf);
1076                 client_write_ssl(WC->WBuf);
1077                 return (count);
1078         }
1079 #endif
1080
1081         if (WC->Hdr->http_sock == -1) {
1082                 return -1;
1083         }
1084         fdflags = fcntl(WC->Hdr->http_sock, F_GETFL);
1085
1086         while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
1087                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
1088                         FD_ZERO(&wset);
1089                         FD_SET(WC->Hdr->http_sock, &wset);
1090                         if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
1091                                 syslog(LOG_DEBUG, "client_write: Socket select failed (%s)\n", strerror(errno));
1092                                 return -1;
1093                         }
1094                 }
1095
1096                 if ((WC->Hdr->http_sock == -1) || 
1097                     (res = write(WC->Hdr->http_sock, 
1098                                  ptr,
1099                                  count)) == -1) {
1100                         syslog(LOG_DEBUG, "client_write: Socket write failed (%s)\n", strerror(errno));
1101                         wc_backtrace(LOG_INFO);
1102                         return res;
1103                 }
1104                 count -= res;
1105                 ptr += res;
1106         }
1107
1108         ptr = ChrPtr(WC->WBuf);
1109         count = StrLength(WC->WBuf);
1110         eptr = ptr + count;
1111
1112         while ((ptr < eptr) && (WC->Hdr->http_sock != -1)) {
1113                 if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
1114                         FD_ZERO(&wset);
1115                         FD_SET(WC->Hdr->http_sock, &wset);
1116                         if (select(WC->Hdr->http_sock + 1, NULL, &wset, NULL, NULL) == -1) {
1117                                 syslog(LOG_INFO, "client_write: Socket select failed (%s)\n", strerror(errno));
1118                                 return -1;
1119                         }
1120                 }
1121
1122                 if ((WC->Hdr->http_sock == -1) || 
1123                     (res = write(WC->Hdr->http_sock, 
1124                                  ptr,
1125                                  count)) == -1) {
1126                         syslog(LOG_INFO, "client_write: Socket write failed (%s)\n", strerror(errno));
1127                         wc_backtrace(LOG_INFO);
1128                         return res;
1129                 }
1130                 count -= res;
1131                 ptr += res;
1132         }
1133
1134         return StrLength(WC->WBuf);
1135 }
1136
1137
1138 /*
1139  * lingering_close() a`la Apache. see
1140  * http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html for rationale
1141  */
1142 int lingering_close(int fd)
1143 {
1144         char buf[SIZ];
1145         int i;
1146         fd_set set;
1147         struct timeval tv, start;
1148
1149         gettimeofday(&start, NULL);
1150         if (fd == -1)
1151                 return -1;
1152         shutdown(fd, 1);
1153         do {
1154                 do {
1155                         gettimeofday(&tv, NULL);
1156                         tv.tv_sec = SLEEPING - (tv.tv_sec - start.tv_sec);
1157                         tv.tv_usec = start.tv_usec - tv.tv_usec;
1158                         if (tv.tv_usec < 0) {
1159                                 tv.tv_sec--;
1160                                 tv.tv_usec += 1000000;
1161                         }
1162                         FD_ZERO(&set);
1163                         FD_SET(fd, &set);
1164                         i = select(fd + 1, &set, NULL, NULL, &tv);
1165                 } while (i == -1 && errno == EINTR);
1166
1167                 if (i <= 0)
1168                         break;
1169
1170                 i = read(fd, buf, sizeof buf);
1171         } while (i != 0 && (i != -1 || errno == EINTR));
1172
1173         return close(fd);
1174 }
1175
1176 void
1177 HttpNewModule_TCPSOCKETS
1178 (ParsedHttpHdrs *httpreq)
1179 {
1180
1181         httpreq->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
1182 }
1183
1184 void
1185 HttpDetachModule_TCPSOCKETS
1186 (ParsedHttpHdrs *httpreq)
1187 {
1188
1189         FlushStrBuf(httpreq->ReadBuf);
1190         ReAdjustEmptyBuf(httpreq->ReadBuf, 4 * SIZ, SIZ);
1191 }
1192
1193 void
1194 HttpDestroyModule_TCPSOCKETS
1195 (ParsedHttpHdrs *httpreq)
1196 {
1197
1198         FreeStrBuf(&httpreq->ReadBuf);
1199 }
1200
1201
1202 void
1203 SessionNewModule_TCPSOCKETS
1204 (wcsession *sess)
1205 {
1206         sess->CLineBuf = NewStrBuf();
1207         sess->MigrateReadLineBuf = NewStrBuf();
1208 }
1209
1210 void 
1211 SessionDestroyModule_TCPSOCKETS
1212 (wcsession *sess)
1213 {
1214         FreeStrBuf(&sess->CLineBuf);
1215         FreeStrBuf(&sess->ReadBuf);
1216         sess->connected = 0;
1217         sess->ReadPos = NULL;
1218         FreeStrBuf(&sess->MigrateReadLineBuf);
1219         if (sess->serv_sock > 0) {
1220                 syslog(LOG_DEBUG, "Closing socket %d", sess->serv_sock);
1221                 close(sess->serv_sock);
1222         }
1223         sess->serv_sock = -1;
1224 }