clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name connio.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c connio.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | #include "nsd.h" |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | #define IOBUFSZ 8192 |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | #define MAX_CHARS_CHUNK_HEADER 12 |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | static Ns_ReturnCode ConnSend(Ns_Conn *conn, ssize_t nsend, Tcl_Channel chan, |
60 | FILE *fp, int fd) |
61 | NS_GNUC_NONNULL(1); |
62 | |
63 | static Ns_ReturnCode ConnCopy(const Ns_Conn *conn, size_t toCopy, Tcl_Channel chan, |
64 | FILE *fp, int fd) |
65 | NS_GNUC_NONNULL(1); |
66 | |
67 | static bool CheckKeep(const Conn *connPtr) |
68 | NS_GNUC_NONNULL(1); |
69 | |
70 | static int CheckCompress(const Conn *connPtr, const struct iovec *bufs, int nbufs, unsigned int ioflags) |
71 | NS_GNUC_NONNULL(1); |
72 | |
73 | static bool HdrEq(const Ns_Set *set, const char *name, const char *value) |
74 | NS_GNUC_NONNULL(1) NS_GNUC_NONNULL(2) NS_GNUC_NONNULL(3); |
75 | |
76 |
|
77 | |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | |
89 | |
90 | |
91 | |
92 | |
93 | |
94 | |
95 | |
96 | Ns_ReturnCode |
97 | Ns_ConnWriteChars(Ns_Conn *conn, const char *buf, size_t toWrite, unsigned int flags) |
98 | { |
99 | struct iovec sbuf; |
100 | |
101 | sbuf.iov_base = (void *) buf; |
102 | sbuf.iov_len = toWrite; |
103 | return Ns_ConnWriteVChars(conn, &sbuf, 1, flags); |
104 | } |
105 | |
106 | Ns_ReturnCode |
107 | Ns_ConnWriteVChars(Ns_Conn *conn, struct iovec *bufs, int nbufs, unsigned int flags) |
108 | { |
109 | Conn *connPtr = (Conn *) conn; |
110 | Ns_DString encDs, gzDs; |
111 | struct iovec iov; |
112 | Ns_ReturnCode status; |
113 | |
114 | Ns_DStringInit(&encDs); |
115 | Ns_DStringInit(&gzDs); |
116 | |
117 | |
118 | |
119 | |
120 | |
121 | |
122 | |
123 | |
124 | |
125 | if (connPtr->outputEncoding != NULL |
126 | && nbufs > 0 |
127 | && bufs[0].iov_len > 0u) { |
128 | int i; |
129 | |
130 | for (i = 0; i < nbufs; i++) { |
131 | const char *utfBytes; |
132 | size_t utfLen; |
133 | |
134 | utfBytes = bufs[i].iov_base; |
135 | utfLen = bufs[i].iov_len; |
136 | |
137 | if (utfLen > 0u) { |
138 | (void) Tcl_UtfToExternalDString(connPtr->outputEncoding, |
139 | utfBytes, (int)utfLen, &encDs); |
140 | } |
141 | } |
142 | (void)Ns_SetVec(&iov, 0, encDs.string, (size_t)encDs.length); |
143 | bufs = &iov; |
144 | nbufs = 1; |
145 | } |
146 | |
147 | |
148 | |
149 | |
150 | |
151 | if (connPtr->compress < 0) { |
152 | connPtr->compress = CheckCompress(connPtr, bufs, nbufs, flags); |
153 | } |
154 | if (connPtr->compress > 0 |
155 | && (nbufs > 0 || (flags & NS_CONN_STREAM_CLOSE) != 0u) |
156 | ) { |
157 | bool flush = ((flags & NS_CONN_STREAM) == 0u); |
158 | |
159 | if (Ns_CompressBufsGzip(&connPtr->cStream, bufs, nbufs, &gzDs, |
160 | connPtr->compress, flush) == NS_OK) { |
161 | |
162 | (void)Ns_SetVec(&iov, 0, gzDs.string, (size_t)gzDs.length); |
163 | bufs = &iov; |
164 | nbufs = 1; |
165 | } |
166 | } |
167 | |
168 | status = Ns_ConnWriteVData(conn, bufs, nbufs, flags); |
169 | |
170 | Ns_DStringFree(&encDs); |
171 | Ns_DStringFree(&gzDs); |
172 | |
173 | return status; |
174 | } |
175 | |
176 | |
177 |
|
178 | |
179 | |
180 | |
181 | |
182 | |
183 | |
184 | |
185 | |
186 | |
187 | |
188 | |
189 | |
190 | |
191 | |
192 | |
193 | |
194 | static int |
195 | CheckCompress(const Conn *connPtr, const struct iovec *bufs, int nbufs, unsigned int ioflags) |
196 | { |
197 | const Ns_Conn *conn = (Ns_Conn *) connPtr; |
198 | const NsServer *servPtr; |
199 | int configuredCompressionLevel, compressionLevel = 0; |
200 | |
201 | NS_NONNULL_ASSERT(connPtr != NULL); |
202 | |
203 | servPtr = connPtr->poolPtr->servPtr; |
204 | |
205 | |
206 | |
207 | |
208 | configuredCompressionLevel = Ns_ConnGetCompression(conn); |
209 | |
210 | if (configuredCompressionLevel > 0) { |
211 | |
212 | |
213 | |
214 | |
215 | if (((ioflags & NS_CONN_STREAM) != 0u) |
216 | || (bufs != NULL && Ns_SumVec(bufs, nbufs) >= (size_t)servPtr->compress.minsize) |
217 | || connPtr->responseLength >= servPtr->compress.minsize) { |
218 | |
219 | |
220 | |
221 | if (((connPtr->flags & NS_CONN_SENTHDRS) == 0u) |
222 | && ((connPtr->flags & NS_CONN_SKIPBODY) == 0u)) { |
223 | Ns_ConnSetHeaders(conn, "Vary", "Accept-Encoding"); |
224 | |
225 | if ((connPtr->flags & NS_CONN_ZIPACCEPTED) != 0u) { |
226 | Ns_ConnSetHeaders(conn, "Content-Encoding", "gzip"); |
227 | compressionLevel = configuredCompressionLevel; |
228 | } |
229 | } |
230 | } |
231 | } |
232 | return compressionLevel; |
233 | } |
234 | |
235 |
|
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 | |
244 | |
245 | |
246 | |
247 | |
248 | |
249 | |
250 | |
251 | |
252 | |
253 | |
254 | |
255 | |
256 | |
257 | Ns_ReturnCode |
258 | Ns_ConnWriteData(Ns_Conn *conn, const void *buf, size_t toWrite, unsigned int flags) |
259 | { |
260 | struct iovec vbuf; |
261 | |
262 | vbuf.iov_base = (void *) buf; |
263 | vbuf.iov_len = toWrite; |
264 | |
265 | return Ns_ConnWriteVData(conn, &vbuf, 1, flags); |
266 | } |
267 | |
268 | Ns_ReturnCode |
269 | Ns_ConnWriteVData(Ns_Conn *conn, struct iovec *bufs, int nbufs, unsigned int flags) |
270 | { |
271 | Ns_DString ds; |
272 | int nsbufs, sbufIdx; |
273 | size_t bodyLength, toWrite, neededBufs; |
274 | ssize_t nwrote; |
275 | struct iovec sbufs[32], *sbufPtr; |
276 | char hdr[MAX_CHARS_CHUNK_HEADER]; |
277 | |
278 | |
279 | |
280 | |
281 | |
282 | |
283 | NS_NONNULL_ASSERT(conn != NULL); |
284 | |
285 | |
286 | Ns_DStringInit(&ds); |
287 | |
288 | |
289 | |
290 | |
291 | |
292 | |
293 | |
294 | neededBufs = (size_t)nbufs + 2u + 1u; |
295 | if (neededBufs > (sizeof(sbufs) / sizeof(struct iovec))) { |
296 | sbufPtr = ns_calloc(neededBufs, sizeof(struct iovec)); |
297 | } else { |
298 | sbufPtr = sbufs; |
299 | } |
300 | nsbufs = 0; |
301 | sbufIdx = 0; |
302 | |
303 | |
304 | |
305 | |
306 | |
307 | bodyLength = (bufs != NULL) ? Ns_SumVec(bufs, nbufs) : 0u; |
308 | toWrite = 0u; |
309 | |
310 | if ((flags & NS_CONN_STREAM) != 0u) { |
311 | conn->flags |= NS_CONN_STREAM; |
312 | } |
313 | |
314 | |
315 | |
316 | |
317 | |
318 | if (((conn->flags & NS_CONN_SENTHDRS) == 0u)) { |
319 | conn->flags |= NS_CONN_SENTHDRS; |
320 | if (Ns_CompleteHeaders(conn, bodyLength, flags, &ds) == NS_TRUE) { |
321 | toWrite += Ns_SetVec(sbufPtr, sbufIdx++, |
322 | Ns_DStringValue(&ds), |
323 | (size_t)Ns_DStringLength(&ds)); |
324 | nsbufs++; |
325 | } |
326 | } |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | if ((conn->flags & NS_CONN_SKIPBODY) == 0u) { |
333 | |
334 | if ((conn->flags & NS_CONN_CHUNK) == 0u) { |
335 | |
336 | |
337 | |
338 | |
339 | |
340 | if (sbufIdx == 0) { |
341 | sbufPtr = bufs; |
342 | nsbufs = nbufs; |
343 | } else if (nbufs > 0) { |
344 | NS_NONNULL_ASSERT(bufs != NULL); |
345 | (void) memcpy(sbufPtr + sbufIdx, bufs, (size_t)nbufs * sizeof(struct iovec)); |
346 | nsbufs += nbufs; |
347 | } |
348 | toWrite += bodyLength; |
349 | |
350 | } else { |
351 | |
352 | |
353 | |
354 | |
355 | |
356 | if (bodyLength > 0u) { |
357 | size_t len; |
358 | |
359 | assert(nbufs > 0); |
360 | assert(bufs != NULL); |
361 | |
362 | |
363 | |
364 | |
365 | |
366 | len = (size_t)snprintf(hdr, sizeof(hdr), "%lx\r\n", (unsigned long)bodyLength); |
367 | toWrite += Ns_SetVec(sbufPtr, sbufIdx++, hdr, len); |
368 | |
369 | (void) memcpy(sbufPtr + sbufIdx, bufs, (size_t)nbufs * sizeof(struct iovec)); |
370 | sbufIdx += nbufs; |
371 | toWrite += bodyLength; |
372 | |
373 | toWrite += Ns_SetVec(sbufPtr, sbufIdx++, "\r\n", (size_t)2); |
374 | |
375 | nsbufs += nbufs + 2; |
376 | } |
377 | |
378 | if ((flags & NS_CONN_STREAM_CLOSE) != 0u) { |
379 | |
380 | |
381 | |
382 | |
383 | toWrite += Ns_SetVec(sbufPtr, sbufIdx, "0\r\n\r\n", (size_t)5); |
384 | |
385 | nsbufs += 1; |
386 | conn->flags &= ~NS_CONN_STREAM; |
387 | conn->flags |= NS_CONN_SENT_LAST_CHUNK; |
388 | } |
389 | } |
390 | } |
391 | |
392 | |
393 | |
394 | |
395 | |
396 | nwrote = Ns_ConnSend(conn, sbufPtr, nsbufs); |
397 | |
398 | Ns_DStringFree(&ds); |
399 | if (sbufPtr != sbufs && sbufPtr != bufs) { |
400 | ns_free(sbufPtr); |
401 | } |
402 | |
403 | return (nwrote < (ssize_t)toWrite) ? NS_ERROR : NS_OK; |
404 | } |
405 | |
406 |
|
407 | |
408 | |
409 | |
410 | |
411 | |
412 | |
413 | |
414 | |
415 | |
416 | |
417 | |
418 | |
419 | |
420 | |
421 | |
422 | |
423 | |
424 | Ns_ReturnCode |
425 | Ns_ConnSendChannel(Ns_Conn *conn, Tcl_Channel chan, ssize_t nsend) |
426 | { |
427 | return ConnSend(conn, nsend, chan, NULL, -1); |
428 | } |
429 | |
430 | Ns_ReturnCode |
431 | Ns_ConnSendFp(Ns_Conn *conn, FILE *fp, ssize_t nsend) |
432 | { |
433 | return ConnSend(conn, nsend, NULL, fp, -1); |
434 | } |
435 | |
436 | Ns_ReturnCode |
437 | Ns_ConnSendFd(Ns_Conn *conn, int fd, ssize_t nsend) |
438 | { |
439 | return ConnSend(conn, nsend, NULL, NULL, fd); |
440 | } |
441 | |
442 |
|
443 | |
444 | |
445 | |
446 | |
447 | |
448 | |
449 | |
450 | |
451 | |
452 | |
453 | |
454 | |
455 | |
456 | |
457 | |
458 | |
459 | |
460 | |
461 | static Ns_ReturnCode |
462 | ConnSend(Ns_Conn *conn, ssize_t nsend, Tcl_Channel chan, FILE *fp, int fd) |
463 | { |
464 | Ns_ReturnCode status; |
465 | unsigned int flags = 0; |
466 | |
467 | NS_NONNULL_ASSERT(conn != NULL); |
468 | assert(chan != NULL || fp != NULL || fd > -1); |
469 | |
470 | if (nsend == 0) { |
471 | |
472 | |
473 | |
474 | status = Ns_ConnWriteVData(conn, NULL, 0, flags); |
475 | |
476 | } else { |
477 | bool stream = NS_FALSE, eod = NS_FALSE; |
478 | char buf[IOBUFSZ]; |
479 | struct iovec vbuf; |
480 | |
481 | vbuf.iov_base = (void *)buf; |
482 | |
483 | |
484 | |
485 | |
486 | if (nsend == -1) { |
487 | stream = NS_TRUE; |
488 | flags |= NS_CONN_STREAM; |
489 | } |
490 | |
491 | |
492 | |
493 | |
494 | |
495 | |
496 | status = NS_OK; |
497 | |
498 | while (status == NS_OK && (nsend > 0 || (stream && !eod))) { |
499 | ssize_t nread = 0; |
500 | size_t toRead = 0; |
501 | |
502 | if (stream) { |
503 | toRead = sizeof(buf); |
504 | } else { |
505 | toRead = ((size_t)nsend > sizeof(buf)) ? sizeof(buf) : (size_t)nsend; |
506 | } |
507 | if (chan != NULL) { |
508 | nread = Tcl_Read(chan, buf, (int)toRead); |
509 | if (stream && Tcl_Eof(chan)) { |
510 | eod = NS_TRUE; |
511 | } |
512 | } else if (fp != NULL) { |
513 | nread = (int)fread(buf, 1u, toRead, fp); |
514 | if (ferror(fp)) { |
515 | nread = -1; |
516 | } else if (stream && feof(fp)) { |
517 | eod = NS_TRUE; |
518 | } |
519 | } else if (fd > -1) { |
520 | nread = ns_read(fd, buf, toRead); |
521 | if (stream && nread == 0) { |
522 | eod = NS_TRUE; |
523 | } |
524 | } else { |
525 | status = NS_ERROR; |
526 | } |
527 | if (nread == -1 || (!stream && nread == 0) ) { |
528 | status = NS_ERROR; |
529 | } else if (nread > 0) { |
530 | vbuf.iov_len = (size_t)nread; |
531 | status = Ns_ConnWriteVData(conn, &vbuf, 1, flags); |
532 | if (status == NS_OK && !stream) { |
533 | nsend -= nread; |
534 | } |
535 | } |
536 | } |
537 | } |
538 | |
539 | return status; |
540 | } |
541 | |
542 |
|
543 | |
544 | |
545 | |
546 | |
547 | |
548 | |
549 | |
550 | |
551 | |
552 | |
553 | |
554 | |
555 | |
556 | |
557 | |
558 | |
559 | |
560 | Ns_ReturnCode |
561 | Ns_ConnSendFileVec(Ns_Conn *conn, Ns_FileVec *bufs, int nbufs) |
562 | { |
563 | Conn *connPtr; |
564 | Sock *sockPtr; |
565 | int i; |
566 | size_t nwrote = 0u, towrite = 0u; |
567 | Ns_Time waitTimeout; |
568 | |
569 | NS_NONNULL_ASSERT(conn != NULL); |
570 | NS_NONNULL_ASSERT(bufs != NULL); |
571 | |
572 | connPtr = (Conn *)conn; |
573 | sockPtr = (Sock *)connPtr->sockPtr; |
574 | |
575 | NS_NONNULL_ASSERT(sockPtr != NULL); |
576 | NS_NONNULL_ASSERT(sockPtr->drvPtr != NULL); |
577 | |
578 | waitTimeout.sec = sockPtr->drvPtr->sendwait.sec; |
579 | waitTimeout.usec = sockPtr->drvPtr->sendwait.usec; |
580 | |
581 | for (i = 0; i < nbufs; i++) { |
582 | towrite += bufs[i].length; |
583 | } |
584 | |
585 | while (nwrote < towrite) { |
586 | ssize_t sent; |
587 | |
588 | sent = NsDriverSendFile(sockPtr, bufs, nbufs, 0u); |
589 | if (sent == -1) { |
590 | break; |
591 | } |
592 | nwrote += (size_t)sent; |
593 | if (nwrote < towrite) { |
594 | Ns_Sock *sock = (Ns_Sock *)sockPtr; |
595 | |
596 | if (sent > 0) { |
597 | (void)Ns_ResetFileVec(bufs, nbufs, (size_t)sent); |
598 | } |
599 | if (Ns_SockTimedWait(sock->sock, NS_SOCK_WRITE, |
600 | &waitTimeout) != NS_OK) { |
601 | break; |
602 | } |
603 | } |
604 | } |
605 | |
606 | if (likely(nwrote > 0u)) { |
607 | connPtr->nContentSent += nwrote; |
608 | } |
609 | |
610 | return (nwrote != towrite) ? NS_ERROR : NS_OK; |
611 | } |
612 | |
613 | |
614 |
|
615 | |
616 | |
617 | |
618 | |
619 | |
620 | |
621 | |
622 | |
623 | |
624 | |
625 | |
626 | |
627 | |
628 | |
629 | |
630 | |
631 | |
632 | Ns_ReturnCode |
633 | Ns_ConnPuts(Ns_Conn *conn, const char *s) |
634 | { |
635 | struct iovec vbuf; |
636 | |
637 | NS_NONNULL_ASSERT(conn != NULL); |
638 | NS_NONNULL_ASSERT(s != NULL); |
639 | |
640 | vbuf.iov_base = (void *) s; |
641 | vbuf.iov_len = strlen(s); |
642 | |
643 | return Ns_ConnWriteVData(conn, &vbuf, 1, NS_CONN_STREAM); |
644 | } |
645 | |
646 |
|
647 | |
648 | |
649 | |
650 | |
651 | |
652 | |
653 | |
654 | |
655 | |
656 | |
657 | |
658 | |
659 | |
660 | |
661 | |
662 | |
663 | Ns_ReturnCode |
664 | Ns_ConnSendDString(Ns_Conn *conn, const Ns_DString *dsPtr) |
665 | { |
666 | struct iovec vbuf; |
667 | |
668 | NS_NONNULL_ASSERT(conn != NULL); |
669 | NS_NONNULL_ASSERT(dsPtr != NULL); |
670 | |
671 | vbuf.iov_base = dsPtr->string; |
672 | vbuf.iov_len = (size_t)dsPtr->length; |
673 | |
674 | return Ns_ConnWriteVData(conn, &vbuf, 1, NS_CONN_STREAM); |
675 | } |
676 | |
677 |
|
678 | |
679 | |
680 | |
681 | |
682 | |
683 | |
684 | |
685 | |
686 | |
687 | |
688 | |
689 | |
690 | |
691 | |
692 | |
693 | |
694 | |
695 | ssize_t |
696 | Ns_ConnSend(Ns_Conn *conn, struct iovec *bufs, int nbufs) |
697 | { |
698 | ssize_t sent; |
699 | int i; |
700 | size_t towrite = 0u; |
701 | |
702 | for (i = 0; i < nbufs; i++) { |
703 | towrite += bufs[i].iov_len; |
704 | } |
705 | |
706 | if (towrite == 0u) { |
707 | sent = 0; |
708 | |
709 | } else if (NsWriterQueue(conn, towrite, NULL, NULL, NS_INVALID_FD, |
710 | bufs, nbufs, NULL, 0, NS_FALSE) == NS_OK) { |
711 | Ns_Log(Debug, "==== writer sent %" PRIuz " bytes\n", towrite); |
712 | sent = (ssize_t)towrite; |
713 | |
714 | } else { |
715 | Ns_Time waitTimeout; |
716 | Conn *connPtr; |
717 | Sock *sockPtr; |
718 | |
719 | NS_NONNULL_ASSERT(conn != NULL); |
720 | |
721 | connPtr = (Conn *)conn; |
722 | sockPtr = connPtr->sockPtr; |
723 | |
724 | NS_NONNULL_ASSERT(sockPtr != NULL); |
725 | NS_NONNULL_ASSERT(sockPtr->drvPtr != NULL); |
726 | |
727 | waitTimeout.sec = sockPtr->drvPtr->sendwait.sec; |
728 | waitTimeout.usec = sockPtr->drvPtr->sendwait.usec; |
729 | |
730 | sent = Ns_SockSendBufs((Ns_Sock*)sockPtr, bufs, nbufs, &waitTimeout, 0u); |
731 | |
732 | if (likely(sent > 0)) { |
733 | connPtr->nContentSent += (size_t)sent; |
734 | } |
735 | NsPoolAddBytesSent(((Conn *)conn)->poolPtr, (Tcl_WideInt)connPtr->nContentSent); |
736 | } |
737 | |
738 | return sent; |
739 | } |
740 | |
741 |
|
742 | |
743 | |
744 | |
745 | |
746 | |
747 | |
748 | |
749 | |
750 | |
751 | |
752 | |
753 | |
754 | |
755 | |
756 | |
757 | |
758 | Ns_ReturnCode |
759 | Ns_ConnFlushContent(const Ns_Conn *conn) |
760 | { |
761 | const Conn *connPtr = (const Conn *) conn; |
762 | Request *reqPtr = connPtr->reqPtr; |
763 | Ns_ReturnCode status = NS_OK; |
764 | |
765 | if (connPtr->sockPtr == NULL) { |
766 | status = NS_ERROR; |
767 | } else { |
768 | reqPtr->next += reqPtr->avail; |
769 | reqPtr->avail = 0u; |
770 | } |
771 | return status; |
772 | } |
773 | |
774 |
|
775 | |
776 | |
777 | |
778 | |
779 | |
780 | |
781 | |
782 | |
783 | |
784 | |
785 | |
786 | |
787 | |
788 | |
789 | |
790 | |
791 | |
792 | |
793 | Ns_ReturnCode |
794 | Ns_ConnClose(Ns_Conn *conn) |
795 | { |
796 | Conn *connPtr; |
797 | |
798 | NS_NONNULL_ASSERT(conn != NULL); |
799 | |
800 | connPtr = (Conn *) conn; |
801 | Ns_Log(Debug, "Ns_ConnClose %p stream %.6x chunk %.6x via writer %.6x sockPtr %p", |
802 | (void *)connPtr, |
803 | connPtr->flags & NS_CONN_STREAM, |
804 | connPtr->flags & NS_CONN_CHUNK, |
805 | connPtr->flags & NS_CONN_SENT_VIA_WRITER, |
806 | (void *)connPtr->sockPtr); |
807 | |
808 | if (connPtr->sockPtr != NULL) { |
809 | |
810 | if ((connPtr->flags & NS_CONN_STREAM) != 0u |
811 | && ((connPtr->flags & NS_CONN_CHUNK) != 0u |
812 | || (connPtr->compress > 0) |
813 | )) { |
814 | |
815 | |
816 | |
817 | |
818 | |
819 | (void) Ns_ConnWriteVChars(conn, NULL, 0, NS_CONN_STREAM_CLOSE); |
820 | } |
821 | |
822 | |
823 | |
824 | |
825 | |
826 | if ((connPtr->flags & NS_CONN_SENT_VIA_WRITER) == 0u) { |
827 | NsSockClose(connPtr->sockPtr, connPtr->keep); |
828 | } |
829 | |
830 | |
831 | connPtr->sockPtr = NULL; |
832 | connPtr->flags |= NS_CONN_CLOSED; |
833 | Ns_Log(Ns_LogRequestDebug, "connection closed"); |
834 | |
835 | if (connPtr->itPtr != NULL) { |
836 | NsTclRunAtClose(connPtr->itPtr); |
837 | } |
838 | } |
839 | |
840 | return NS_OK; |
841 | } |
842 | |
843 |
|
844 | |
845 | |
846 | |
847 | |
848 | |
849 | |
850 | |
851 | |
852 | |
853 | |
854 | |
855 | |
856 | |
857 | |
858 | |
859 | |
860 | int |
861 | Ns_ConnWrite(Ns_Conn *conn, const void *buf, size_t toWrite) |
862 | { |
863 | const Conn *connPtr = (const Conn *) conn; |
864 | size_t n; |
865 | Ns_ReturnCode status; |
866 | int result; |
867 | struct iovec vbuf; |
868 | |
869 | vbuf.iov_base = (void *) buf; |
870 | vbuf.iov_len = toWrite; |
871 | |
872 | n = connPtr->nContentSent; |
873 | status = Ns_ConnWriteVData(conn, &vbuf, 1, 0u); |
874 | if (status == NS_OK) { |
875 | result = (int)connPtr->nContentSent - (int)n; |
876 | } else { |
877 | result = -1; |
878 | } |
879 | return result; |
880 | } |
881 | |
882 | Ns_ReturnCode |
883 | Ns_WriteConn(Ns_Conn *conn, const char *buf, size_t toWrite) |
884 | { |
885 | struct iovec vbuf; |
886 | |
887 | |
888 | |
889 | NS_NONNULL_ASSERT(conn != NULL); |
890 | |
891 | vbuf.iov_base = (void *) buf; |
892 | vbuf.iov_len = toWrite; |
893 | |
894 | return Ns_ConnWriteVData(conn, &vbuf, 1, NS_CONN_STREAM); |
895 | } |
896 | |
897 | Ns_ReturnCode |
898 | Ns_WriteCharConn(Ns_Conn *conn, const char *buf, size_t toWrite) |
899 | { |
900 | struct iovec sbuf; |
901 | |
902 | sbuf.iov_base = (void *)buf; |
903 | sbuf.iov_len = toWrite; |
904 | |
905 | return Ns_ConnWriteVChars(conn, &sbuf, 1, NS_CONN_STREAM); |
906 | } |
907 | |
908 |
|
909 | |
910 | |
911 | |
912 | |
913 | |
914 | |
915 | |
916 | |
917 | |
918 | |
919 | |
920 | |
921 | |
922 | |
923 | |
924 | |
925 | |
926 | char * |
927 | Ns_ConnGets(char *buf, size_t bufsize, const Ns_Conn *conn) |
928 | { |
929 | char *p, *result = buf; |
930 | |
931 | NS_NONNULL_ASSERT(buf != NULL); |
932 | NS_NONNULL_ASSERT(conn != NULL); |
933 | |
934 | p = buf; |
935 | while (bufsize > 1u) { |
936 | if (Ns_ConnRead(conn, p, 1u) != 0u) { |
937 | result = NULL; |
938 | break; |
939 | } |
940 | if (*p++ == '\n') { |
941 | break; |
942 | } |
943 | --bufsize; |
944 | } |
945 | if (likely(result != NULL)) { |
946 | *p = '\0'; |
947 | } |
948 | |
949 | return result; |
950 | } |
951 | |
952 |
|
953 | |
954 | |
955 | |
956 | |
957 | |
958 | |
959 | |
960 | |
961 | |
962 | |
963 | |
964 | |
965 | |
966 | |
967 | |
968 | |
969 | size_t |
970 | Ns_ConnRead(const Ns_Conn *conn, void *vbuf, size_t toRead) |
971 | { |
972 | const Conn *connPtr = (const Conn *) conn; |
973 | Request *reqPtr = connPtr->reqPtr; |
974 | |
975 | if (connPtr->sockPtr == NULL) { |
976 | toRead = 0u; |
977 | } else { |
978 | if (toRead > reqPtr->avail) { |
979 | toRead = reqPtr->avail; |
980 | } |
981 | memcpy(vbuf, reqPtr->next, toRead); |
982 | reqPtr->next += toRead; |
983 | reqPtr->avail -= toRead; |
984 | } |
985 | |
986 | return toRead; |
987 | } |
988 | |
989 |
|
990 | |
991 | |
992 | |
993 | |
994 | |
995 | |
996 | |
997 | |
998 | |
999 | |
1000 | |
1001 | |
1002 | |
1003 | |
1004 | |
1005 | |
1006 | |
1007 | Ns_ReturnCode |
1008 | Ns_ConnReadLine(const Ns_Conn *conn, Ns_DString *dsPtr, size_t *nreadPtr) |
1009 | { |
1010 | const Conn *connPtr; |
1011 | Request *reqPtr; |
1012 | const Driver *drvPtr; |
1013 | const char *eol; |
1014 | Ns_ReturnCode status; |
1015 | |
1016 | NS_NONNULL_ASSERT(conn != NULL); |
1017 | NS_NONNULL_ASSERT(dsPtr != NULL); |
1018 | |
1019 | connPtr = (const Conn *) conn; |
1020 | reqPtr = connPtr->reqPtr; |
1021 | assert(reqPtr != NULL); |
1022 | |
1023 | drvPtr = connPtr->drvPtr; |
1024 | eol = strchr(reqPtr->next, INTCHAR('\n')); |
1025 | |
1026 | if ((connPtr->sockPtr == NULL) || (eol == NULL)) { |
| 4 | | Assuming field 'sockPtr' is not equal to NULL | |
|
| 5 | | Assuming 'eol' is not equal to NULL | |
|
| |
1027 | status = NS_ERROR; |
1028 | } else { |
1029 | ptrdiff_t nread = eol - reqPtr->next; |
1030 | |
1031 | if (nread > drvPtr->maxline) { |
| 7 | | Assuming 'nread' is <= field 'maxline' | |
|
| |
1032 | status = NS_ERROR; |
1033 | } else { |
1034 | ptrdiff_t ncopy = nread; |
1035 | |
1036 | ++nread; |
1037 | if (nreadPtr != NULL) { |
| |
1038 | *nreadPtr = (size_t)nread; |
1039 | } |
1040 | |
1041 | |
1042 | |
1043 | |
1044 | |
1045 | |
1046 | if (ncopy > 0u && *(eol-1) == '\r') { |
| 10 | | Assuming 'ncopy' is > 0 | |
|
| 11 | | Access out-of-bound array element (buffer overflow) |
|
1047 | --ncopy; |
1048 | } |
1049 | Ns_DStringNAppend(dsPtr, reqPtr->next, (int)ncopy); |
1050 | reqPtr->next += nread; |
1051 | reqPtr->avail -= (size_t)nread; |
1052 | |
1053 | status = NS_OK; |
1054 | } |
1055 | } |
1056 | return status; |
1057 | } |
1058 | |
1059 |
|
1060 | |
1061 | |
1062 | |
1063 | |
1064 | |
1065 | |
1066 | |
1067 | |
1068 | |
1069 | |
1070 | |
1071 | |
1072 | |
1073 | |
1074 | |
1075 | |
1076 | Ns_ReturnCode |
1077 | Ns_ConnReadHeaders(const Ns_Conn *conn, Ns_Set *set, size_t *nreadPtr) |
1078 | { |
1079 | Ns_DString ds; |
1080 | const Conn *connPtr = (const Conn *) conn; |
1081 | size_t nread, maxhdr; |
1082 | Ns_ReturnCode status = NS_OK; |
1083 | |
1084 | Ns_DStringInit(&ds); |
1085 | nread = 0u; |
1086 | maxhdr = (size_t)connPtr->drvPtr->maxheaders; |
1087 | while (nread < maxhdr && status == NS_OK) { |
| 1 | Assuming 'nread' is < 'maxhdr' | |
|
| 2 | | Loop condition is true. Entering loop body | |
|
1088 | size_t nline; |
1089 | |
1090 | Ns_DStringSetLength(&ds, 0); |
1091 | status = Ns_ConnReadLine(conn, &ds, &nline); |
| 3 | | Calling 'Ns_ConnReadLine' | |
|
1092 | if (status == NS_OK) { |
1093 | nread += nline; |
1094 | if (nread > maxhdr) { |
1095 | status = NS_ERROR; |
1096 | } else { |
1097 | if (ds.string[0] == '\0') { |
1098 | break; |
1099 | } |
1100 | status = Ns_ParseHeader(set, ds.string, NULL, |
1101 | connPtr->poolPtr->servPtr->opts.hdrcase, |
1102 | NULL); |
1103 | } |
1104 | } |
1105 | } |
1106 | if (nreadPtr != NULL) { |
1107 | *nreadPtr = nread; |
1108 | } |
1109 | Ns_DStringFree(&ds); |
1110 | |
1111 | return status; |
1112 | } |
1113 | |
1114 |
|
1115 | |
1116 | |
1117 | |
1118 | |
1119 | |
1120 | |
1121 | |
1122 | |
1123 | |
1124 | |
1125 | |
1126 | |
1127 | |
1128 | |
1129 | |
1130 | |
1131 | Ns_ReturnCode |
1132 | Ns_ConnCopyToDString(const Ns_Conn *conn, size_t toCopy, Ns_DString *dsPtr) |
1133 | { |
1134 | const Conn *connPtr; |
1135 | Request *reqPtr; |
1136 | Ns_ReturnCode status = NS_OK; |
1137 | |
1138 | NS_NONNULL_ASSERT(conn != NULL); |
1139 | NS_NONNULL_ASSERT(dsPtr != NULL); |
1140 | |
1141 | connPtr = (const Conn *)conn; |
1142 | reqPtr = connPtr->reqPtr; |
1143 | |
1144 | if (connPtr->sockPtr == NULL || reqPtr->avail < toCopy) { |
1145 | status = NS_ERROR; |
1146 | } else { |
1147 | Ns_DStringNAppend(dsPtr, reqPtr->next, (int)toCopy); |
1148 | reqPtr->next += toCopy; |
1149 | reqPtr->avail -= toCopy; |
1150 | } |
1151 | |
1152 | return status; |
1153 | } |
1154 | |
1155 |
|
1156 | |
1157 | |
1158 | |
1159 | |
1160 | |
1161 | |
1162 | |
1163 | |
1164 | |
1165 | |
1166 | |
1167 | |
1168 | |
1169 | |
1170 | |
1171 | |
1172 | Ns_ReturnCode |
1173 | Ns_ConnCopyToChannel(const Ns_Conn *conn, size_t ncopy, Tcl_Channel chan) |
1174 | { |
1175 | return ConnCopy(conn, ncopy, chan, NULL, -1); |
1176 | } |
1177 | |
1178 | Ns_ReturnCode |
1179 | Ns_ConnCopyToFile(const Ns_Conn *conn, size_t ncopy, FILE *fp) |
1180 | { |
1181 | return ConnCopy(conn, ncopy, NULL, fp, -1); |
1182 | } |
1183 | |
1184 | Ns_ReturnCode |
1185 | Ns_ConnCopyToFd(const Ns_Conn *conn, size_t ncopy, int fd) |
1186 | { |
1187 | return ConnCopy(conn, ncopy, NULL, NULL, fd); |
1188 | } |
1189 | |
1190 | static Ns_ReturnCode |
1191 | ConnCopy(const Ns_Conn *conn, size_t toCopy, Tcl_Channel chan, FILE *fp, int fd) |
1192 | { |
1193 | const Conn *connPtr; |
1194 | Request *reqPtr; |
1195 | size_t ncopy = toCopy; |
1196 | Ns_ReturnCode status = NS_OK; |
1197 | |
1198 | NS_NONNULL_ASSERT(conn != NULL); |
1199 | |
1200 | connPtr = (const Conn *) conn; |
1201 | reqPtr = connPtr->reqPtr; |
1202 | assert(reqPtr != NULL); |
1203 | |
1204 | if (connPtr->sockPtr == NULL || reqPtr->avail < toCopy) { |
1205 | status = NS_ERROR; |
1206 | } else { |
1207 | |
1208 | |
1209 | |
1210 | while (ncopy > 0u) { |
1211 | ssize_t nwrote; |
1212 | |
1213 | |
1214 | |
1215 | |
1216 | |
1217 | if (chan != NULL) { |
1218 | nwrote = Tcl_Write(chan, reqPtr->next, (int)ncopy); |
1219 | } else if (fp != NULL) { |
1220 | nwrote = (ssize_t)fwrite(reqPtr->next, 1u, ncopy, fp); |
1221 | if (ferror(fp) != 0) { |
1222 | nwrote = -1; |
1223 | } |
1224 | } else { |
1225 | nwrote = ns_write(fd, reqPtr->next, ncopy); |
1226 | } |
1227 | if (nwrote < 0) { |
1228 | status = NS_ERROR; |
1229 | break; |
1230 | } else { |
1231 | ncopy -= (size_t)nwrote; |
1232 | reqPtr->next += nwrote; |
1233 | reqPtr->avail -= (size_t)nwrote; |
1234 | } |
1235 | } |
1236 | } |
1237 | return status; |
1238 | } |
1239 | |
1240 |
|
1241 | |
1242 | |
1243 | |
1244 | |
1245 | |
1246 | |
1247 | |
1248 | |
1249 | |
1250 | |
1251 | |
1252 | |
1253 | |
1254 | |
1255 | |
1256 | |
1257 | |
1258 | bool |
1259 | Ns_CompleteHeaders(Ns_Conn *conn, size_t dataLength, |
1260 | unsigned int flags, Ns_DString *dsPtr) |
1261 | { |
1262 | Conn *connPtr = (Conn *) conn; |
1263 | bool success; |
1264 | |
1265 | NS_NONNULL_ASSERT(conn != NULL); |
1266 | NS_NONNULL_ASSERT(dsPtr != NULL); |
1267 | |
1268 | if ((conn->flags & NS_CONN_SKIPHDRS) != 0u) { |
1269 | |
1270 | |
1271 | |
1272 | if (conn->request.version < 1.0) { |
1273 | connPtr->keep = 0; |
1274 | } |
1275 | success = NS_FALSE; |
1276 | } else { |
1277 | const char *keepString; |
1278 | |
1279 | |
1280 | |
1281 | |
1282 | |
1283 | if ((flags & NS_CONN_STREAM) != 0u) { |
1284 | |
1285 | conn->flags |= NS_CONN_STREAM; |
1286 | |
1287 | if ((connPtr->responseLength < 0) |
1288 | && (conn->request.version > 1.0) |
1289 | && (connPtr->keep != 0) |
1290 | && (HdrEq(connPtr->outputheaders, "Content-Type", |
1291 | "multipart/byteranges") == NS_FALSE)) { |
1292 | conn->flags |= NS_CONN_CHUNK; |
1293 | } |
1294 | |
1295 | } else if (connPtr->responseLength < 0) { |
1296 | Ns_ConnSetLengthHeader(conn, dataLength, NS_FALSE); |
1297 | } |
1298 | |
1299 | |
1300 | |
1301 | |
1302 | |
1303 | connPtr->keep = (CheckKeep(connPtr) ? 1 : 0); |
1304 | if (connPtr->keep != 0) { |
1305 | keepString = "keep-alive"; |
1306 | } else { |
1307 | keepString = "close"; |
1308 | } |
1309 | Ns_ConnSetHeaders(conn, "Connection", keepString); |
1310 | |
1311 | if ((conn->flags & NS_CONN_CHUNK) != 0u) { |
1312 | Ns_ConnSetHeaders(conn, "Transfer-Encoding", "chunked"); |
1313 | } |
1314 | Ns_ConnConstructHeaders(conn, dsPtr); |
1315 | success = NS_TRUE; |
1316 | } |
1317 | |
1318 | return success; |
1319 | } |
1320 | |
1321 |
|
1322 | |
1323 | |
1324 | |
1325 | |
1326 | |
1327 | |
1328 | |
1329 | |
1330 | |
1331 | |
1332 | |
1333 | |
1334 | |
1335 | |
1336 | |
1337 | |
1338 | static bool |
1339 | CheckKeep(const Conn *connPtr) |
1340 | { |
1341 | bool result = NS_FALSE; |
1342 | |
1343 | NS_NONNULL_ASSERT(connPtr != NULL); |
1344 | |
1345 | do { |
1346 | if (connPtr->drvPtr->keepwait.sec > 0 || connPtr->drvPtr->keepwait.usec > 0 ) { |
1347 | |
1348 | |
1349 | |
1350 | |
1351 | if (connPtr->keep > 0) { |
1352 | result = NS_TRUE; |
1353 | break; |
1354 | } |
1355 | |
1356 | |
1357 | |
1358 | |
1359 | if ((connPtr->keep == -1) |
1360 | && (connPtr->request.line != NULL)) { |
1361 | |
1362 | |
1363 | |
1364 | |
1365 | if (( (connPtr->request.version == 1.0) |
1366 | && (HdrEq(connPtr->headers, "connection", "keep-alive") == NS_TRUE) ) |
1367 | || ( (connPtr->request.version > 1.0) |
1368 | && (HdrEq(connPtr->headers, "connection", "close") == NS_FALSE) ) |
1369 | ) { |
1370 | |
1371 | |
1372 | |
1373 | |
1374 | |
1375 | if ((connPtr->contentLength > 0u) |
1376 | && (Ns_SetIGet(connPtr->headers, "Content-Length") == NULL)) { |
1377 | |
1378 | |
1379 | |
1380 | break; |
1381 | } |
1382 | |
1383 | if ( (connPtr->drvPtr->keepmaxuploadsize > 0u) |
1384 | && (connPtr->contentLength > connPtr->drvPtr->keepmaxuploadsize) ) { |
1385 | Ns_Log(Notice, |
1386 | "Disallow keep-alive: content-Length %" PRIdz |
1387 | " larger keepmaxuploadsize %" PRIdz ": %s", |
1388 | connPtr->contentLength, connPtr->drvPtr->keepmaxuploadsize, |
1389 | connPtr->request.line); |
1390 | break; |
1391 | } else if ( (connPtr->drvPtr->keepmaxdownloadsize > 0u) |
1392 | && (connPtr->responseLength > 0) |
1393 | && ((size_t)connPtr->responseLength > connPtr->drvPtr->keepmaxdownloadsize) ) { |
1394 | Ns_Log(Notice, |
1395 | "Disallow keep-alive: response length %" PRIdz " " |
1396 | "larger keepmaxdownloadsize %" PRIdz ": %s", |
1397 | connPtr->responseLength, connPtr->drvPtr->keepmaxdownloadsize, |
1398 | connPtr->request.line); |
1399 | break; |
1400 | } |
1401 | |
1402 | |
1403 | |
1404 | |
1405 | |
1406 | if (((connPtr->flags & NS_CONN_CHUNK) != 0u) |
1407 | || (Ns_SetIGet(connPtr->outputheaders, "Content-Length") != NULL) |
1408 | || (HdrEq(connPtr->outputheaders, "Content-Type", "multipart/byteranges") == NS_TRUE)) { |
1409 | |
1410 | result = NS_TRUE; |
1411 | break; |
1412 | } |
1413 | } |
1414 | } |
1415 | } |
1416 | } while (NS_FALSE); |
1417 | |
1418 | |
1419 | |
1420 | |
1421 | return result; |
1422 | } |
1423 | |
1424 |
|
1425 | |
1426 | |
1427 | |
1428 | |
1429 | |
1430 | |
1431 | |
1432 | |
1433 | |
1434 | |
1435 | |
1436 | |
1437 | |
1438 | |
1439 | |
1440 | |
1441 | |
1442 | static bool |
1443 | HdrEq(const Ns_Set *set, const char *name, const char *value) |
1444 | { |
1445 | const char *hdrvalue; |
1446 | |
1447 | NS_NONNULL_ASSERT(set != NULL); |
1448 | NS_NONNULL_ASSERT(name != NULL); |
1449 | NS_NONNULL_ASSERT(value != NULL); |
1450 | |
1451 | hdrvalue = Ns_SetIGet(set, name); |
1452 | |
1453 | return ((hdrvalue != NULL) && (strncasecmp(hdrvalue, value, strlen(value)) == 0)); |
1454 | } |
1455 | |
1456 | |
1457 | |
1458 | |
1459 | |
1460 | |
1461 | |
1462 | |
1463 | |