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