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