client_side.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 33 Client-side Routines */
10 
60 #include "squid.h"
61 #include "acl/FilledChecklist.h"
62 #include "anyp/PortCfg.h"
63 #include "base/Subscription.h"
64 #include "base/TextException.h"
65 #include "CachePeer.h"
66 #include "client_db.h"
67 #include "client_side.h"
68 #include "client_side_reply.h"
69 #include "client_side_request.h"
70 #include "ClientRequestContext.h"
71 #include "clientStream.h"
72 #include "comm.h"
73 #include "comm/Connection.h"
74 #include "comm/Loops.h"
75 #include "comm/Read.h"
76 #include "comm/TcpAcceptor.h"
77 #include "comm/Write.h"
78 #include "CommCalls.h"
79 #include "errorpage.h"
80 #include "fd.h"
81 #include "fde.h"
82 #include "fqdncache.h"
83 #include "FwdState.h"
84 #include "globals.h"
85 #include "helper.h"
86 #include "helper/Reply.h"
87 #include "http.h"
88 #include "http/one/RequestParser.h"
90 #include "http/Stream.h"
91 #include "HttpHdrContRange.h"
92 #include "HttpHeaderTools.h"
93 #include "HttpReply.h"
94 #include "HttpRequest.h"
95 #include "ident/Config.h"
96 #include "ident/Ident.h"
97 #include "internal.h"
98 #include "ipc/FdNotes.h"
99 #include "ipc/StartListening.h"
100 #include "log/access_log.h"
101 #include "MemBuf.h"
102 #include "MemObject.h"
103 #include "mime_header.h"
104 #include "parser/Tokenizer.h"
105 #include "profiler/Profiler.h"
106 #include "proxyp/Header.h"
107 #include "proxyp/Parser.h"
109 #include "servers/forward.h"
110 #include "SquidConfig.h"
111 #include "SquidTime.h"
112 #include "StatCounters.h"
113 #include "StatHist.h"
114 #include "Store.h"
115 #include "TimeOrTag.h"
116 #include "tools.h"
117 
118 #if USE_AUTH
119 #include "auth/UserRequest.h"
120 #endif
121 #if USE_DELAY_POOLS
122 #include "ClientInfo.h"
123 #include "MessageDelayPools.h"
124 #endif
125 #if USE_OPENSSL
126 #include "ssl/bio.h"
127 #include "ssl/context_storage.h"
128 #include "ssl/gadgets.h"
129 #include "ssl/helper.h"
130 #include "ssl/ProxyCerts.h"
131 #include "ssl/ServerBump.h"
132 #include "ssl/support.h"
133 #endif
134 
135 // for tvSubUsec() which should be in SquidTime.h
136 #include "util.h"
137 
138 #include <climits>
139 #include <cmath>
140 #include <limits>
141 
142 #if LINGERING_CLOSE
143 #define comm_close comm_lingering_close
144 #endif
145 
148 {
149 public:
152  handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
153 
154  virtual void print(std::ostream &os) const {
155  startPrint(os) <<
156  ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
157  }
158 
159  virtual bool canDial(AsyncCall &) const { return true; }
160  virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
161 
162 public:
164 
165 private:
169 };
170 
172 
175 #if USE_IDENT
177 #endif
179 static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
180 
181 static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);
182 static void clientUpdateStatCounters(const LogTags &logType);
184 static bool clientPingHasFinished(ping_data const *aPing);
187 
188 char *skipLeadingSpace(char *aString);
189 
190 #if USE_IDENT
191 static void
192 clientIdentDone(const char *ident, void *data)
193 {
194  ConnStateData *conn = (ConnStateData *)data;
195  xstrncpy(conn->clientConnection->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
196 }
197 #endif
198 
199 void
201 {
203 
204  if (logType.isTcpHit())
206 
207  if (logType.oldType == LOG_TCP_HIT)
209  else if (logType.oldType == LOG_TCP_MEM_HIT)
211 }
212 
213 void
214 clientUpdateStatHistCounters(const LogTags &logType, int svc_time)
215 {
224  switch (logType.oldType) {
225 
228  break;
229 
230  case LOG_TCP_INM_HIT:
231  case LOG_TCP_IMS_HIT:
233  break;
234 
235  case LOG_TCP_HIT:
236 
237  case LOG_TCP_MEM_HIT:
238 
239  case LOG_TCP_OFFLINE_HIT:
241  break;
242 
243  case LOG_TCP_MISS:
244 
247  break;
248 
249  default:
250  /* make compiler warnings go away */
251  break;
252  }
253 }
254 
255 bool
257 {
258  if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
259  return true;
260 
261  return false;
262 }
263 
264 void
266 {
267  ping_data *i;
268 
269  switch (someEntry->code) {
270 #if USE_CACHE_DIGESTS
271 
272  case CD_PARENT_HIT:
273 
274  case CD_SIBLING_HIT:
276  break;
277 #endif
278 
279  case SIBLING_HIT:
280 
281  case PARENT_HIT:
282 
283  case FIRST_PARENT_MISS:
284 
285  case CLOSEST_PARENT_MISS:
287  i = &someEntry->ping;
288 
289  if (clientPingHasFinished(i))
291 
292  if (i->timeout)
294 
295  break;
296 
297  case CLOSEST_PARENT:
298 
299  case CLOSEST_DIRECT:
301 
302  break;
303 
304  default:
305  break;
306  }
307 }
308 
309 void
311 {
312  clientUpdateStatCounters(logType);
313 
314  if (request->errType != ERR_NONE)
316 
318  tvSubMsec(al->cache.start_time, current_time));
319 
321 }
322 
323 void
325 {
326  assert(request);
327  assert(aLogEntry != NULL);
328 
329  if (Config.onoff.log_mime_hdrs) {
330  MemBuf mb;
331  mb.init();
332  request->header.packInto(&mb);
333  //This is the request after adaptation or redirection
334  aLogEntry->headers.adapted_request = xstrdup(mb.buf);
335 
336  // the virgin request is saved to aLogEntry->request
337  if (aLogEntry->request) {
338  mb.reset();
339  aLogEntry->request->header.packInto(&mb);
340  aLogEntry->headers.request = xstrdup(mb.buf);
341  }
342 
343 #if USE_ADAPTATION
344  const Adaptation::History::Pointer ah = request->adaptLogHistory();
345  if (ah != NULL) {
346  mb.reset();
347  ah->lastMeta.packInto(&mb);
348  aLogEntry->adapt.last_meta = xstrdup(mb.buf);
349  }
350 #endif
351 
352  mb.clean();
353  }
354 
355 #if ICAP_CLIENT
356  const Adaptation::Icap::History::Pointer ih = request->icapHistory();
357  if (ih != NULL)
358  ih->processingTime(aLogEntry->icap.processingTime);
359 #endif
360 
361  aLogEntry->http.method = request->method;
362  aLogEntry->http.version = request->http_ver;
363  aLogEntry->hier = request->hier;
364  aLogEntry->cache.extuser = request->extacl_user.termedBuf();
365 
366  // Adapted request, if any, inherits and then collects all the stats, but
367  // the virgin request gets logged instead; copy the stats to log them.
368  // TODO: avoid losses by keeping these stats in a shared history object?
369  if (aLogEntry->request) {
370  aLogEntry->request->dnsWait = request->dnsWait;
371  aLogEntry->request->errType = request->errType;
372  aLogEntry->request->errDetail = request->errDetail;
373  }
374 }
375 
376 void
378 {
379  if (!out.size && logType.oldType == LOG_TAG_NONE)
380  debugs(33, 5, "logging half-baked transaction: " << log_uri);
381 
382  al->icp.opcode = ICP_INVALID;
383  al->url = log_uri;
384  debugs(33, 9, "clientLogRequest: al.url='" << al->url << "'");
385 
386  const auto findReply = [this]() -> const HttpReply * {
387  if (al->reply)
388  return al->reply.getRaw();
389  if (const auto le = loggingEntry())
390  return le->hasFreshestReply();
391  return nullptr;
392  };
393  if (const auto reply = findReply()) {
394  al->http.code = reply->sline.status();
395  al->http.content_type = reply->content_type.termedBuf();
396  }
397 
398  debugs(33, 9, "clientLogRequest: http.code='" << al->http.code << "'");
399 
400  if (loggingEntry() && loggingEntry()->mem_obj && loggingEntry()->objectLen() >= 0)
401  al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
402 
403  al->http.clientRequestSz.header = req_sz;
404  // the virgin request is saved to al->request
405  if (al->request && al->request->body_pipe)
406  al->http.clientRequestSz.payloadData = al->request->body_pipe->producedSize();
407  al->http.clientReplySz.header = out.headers_sz;
408  // XXX: calculate without payload encoding or headers !!
409  al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
410 
411  al->cache.highOffset = out.offset;
412 
413  al->cache.code = logType;
414 
415  tvSub(al->cache.trTime, al->cache.start_time, current_time);
416 
417  if (request)
419 
420 #if USE_OPENSSL && 0
421 
422  /* This is broken. Fails if the connection has been closed. Needs
423  * to snarf the ssl details some place earlier..
424  */
425  if (getConn() != NULL)
426  al->cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
427 
428 #endif
429 
430  /* Add notes (if we have a request to annotate) */
431  if (request) {
432  SBuf matched;
433  for (auto h: Config.notes) {
434  if (h->match(request, al->reply.getRaw(), al, matched)) {
435  request->notes()->add(h->key(), matched);
436  debugs(33, 3, h->key() << " " << matched);
437  }
438  }
439  // The al->notes and request->notes must point to the same object.
440  al->syncNotes(request);
441  }
442 
443  ACLFilledChecklist checklist(NULL, request, NULL);
444  if (al->reply) {
445  checklist.reply = al->reply.getRaw();
446  HTTPMSGLOCK(checklist.reply);
447  }
448 
449  if (request) {
450  HTTPMSGUNLOCK(al->adapted_request);
451  al->adapted_request = request;
452  HTTPMSGLOCK(al->adapted_request);
453  }
454  // no need checklist.syncAle(): already synced
455  checklist.al = al;
456  accessLogLog(al, &checklist);
457 
458  bool updatePerformanceCounters = true;
461  statsCheck.al = al;
462  if (al->reply) {
463  statsCheck.reply = al->reply.getRaw();
464  HTTPMSGLOCK(statsCheck.reply);
465  }
466  updatePerformanceCounters = statsCheck.fastCheck().allowed();
467  }
468 
469  if (updatePerformanceCounters) {
470  if (request)
471  updateCounters();
472 
473  if (getConn() != NULL && getConn()->clientConnection != NULL)
474  clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
475  }
476 }
477 
478 void
480 {
481  safe_free(uri);
482  safe_free(redirect.location);
483  range_iter.boundary.clean();
484  clearRequest();
485 
486  if (client_stream.tail)
487  clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
488 }
489 
490 void
492 {
493  ClientHttpRequest *http = (ClientHttpRequest *)data;
494  assert(http != NULL);
495  delete http;
496 }
497 
498 /* This is a handler normally called by comm_close() */
500 {
501  deleteThis("ConnStateData::connStateClosed");
502 }
503 
504 #if USE_AUTH
505 void
507 {
508  if (auth_ == NULL) {
509  if (aur != NULL) {
510  debugs(33, 2, "Adding connection-auth to " << clientConnection << " from " << by);
511  auth_ = aur;
512  }
513  return;
514  }
515 
516  // clobered with self-pointer
517  // NP: something nasty is going on in Squid, but harmless.
518  if (aur == auth_) {
519  debugs(33, 2, "WARNING: Ignoring duplicate connection-auth for " << clientConnection << " from " << by);
520  return;
521  }
522 
523  /*
524  * Connection-auth relies on a single set of credentials being preserved
525  * for all requests on a connection once they have been setup.
526  * There are several things which need to happen to preserve security
527  * when connection-auth credentials change unexpectedly or are unset.
528  *
529  * 1) auth helper released from any active state
530  *
531  * They can only be reserved by a handshake process which this
532  * connection can now never complete.
533  * This prevents helpers hanging when their connections close.
534  *
535  * 2) pinning is expected to be removed and server conn closed
536  *
537  * The upstream link is authenticated with the same credentials.
538  * Expecting the same level of consistency we should have received.
539  * This prevents upstream being faced with multiple or missing
540  * credentials after authentication.
541  * NP: un-pin is left to the cleanup in ConnStateData::swanSong()
542  * we just trigger that cleanup here via comm_reset_close() or
543  * ConnStateData::stopReceiving()
544  *
545  * 3) the connection needs to close.
546  *
547  * This prevents attackers injecting requests into a connection,
548  * or gateways wrongly multiplexing users into a single connection.
549  *
550  * When credentials are missing closure needs to follow an auth
551  * challenge for best recovery by the client.
552  *
553  * When credentials change there is nothing we can do but abort as
554  * fast as possible. Sending TCP RST instead of an HTTP response
555  * is the best-case action.
556  */
557 
558  // clobbered with nul-pointer
559  if (aur == NULL) {
560  debugs(33, 2, "WARNING: Graceful closure on " << clientConnection << " due to connection-auth erase from " << by);
561  auth_->releaseAuthServer();
562  auth_ = NULL;
563  // XXX: need to test whether the connection re-auth challenge is sent. If not, how to trigger it from here.
564  // NP: the current situation seems to fix challenge loops in Safari without visible issues in others.
565  // we stop receiving more traffic but can leave the Job running to terminate after the error or challenge is delivered.
566  stopReceiving("connection-auth removed");
567  return;
568  }
569 
570  // clobbered with alternative credentials
571  if (aur != auth_) {
572  debugs(33, 2, "ERROR: Closing " << clientConnection << " due to change of connection-auth from " << by);
573  auth_->releaseAuthServer();
574  auth_ = NULL;
575  // this is a fatal type of problem.
576  // Close the connection immediately with TCP RST to abort all traffic flow
577  comm_reset_close(clientConnection);
578  return;
579  }
580 
581  /* NOT REACHABLE */
582 }
583 #endif
584 
585 // cleans up before destructor is called
586 void
588 {
589  debugs(33, 2, HERE << clientConnection);
590  checkLogging();
591 
592  flags.readMore = false;
593  clientdbEstablished(clientConnection->remote, -1); /* decrement */
594  pipeline.terminateAll(0);
595 
596  // XXX: Closing pinned conn is too harsh: The Client may want to continue!
597  unpinConnection(true);
598 
599  Server::swanSong(); // closes the client connection
600 
601 #if USE_AUTH
602  // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
603  setAuth(NULL, "ConnStateData::SwanSong cleanup");
604 #endif
605 
606  flags.swanSang = true;
607 }
608 
609 bool
611 {
612  return cbdataReferenceValid(this) && // XXX: checking "this" in a method
613  Comm::IsConnOpen(clientConnection) &&
614  !fd_table[clientConnection->fd].closing();
615 }
616 
618 {
619  debugs(33, 3, HERE << clientConnection);
620 
621  if (isOpen())
622  debugs(33, DBG_IMPORTANT, "BUG: ConnStateData did not close " << clientConnection);
623 
624  if (!flags.swanSang)
625  debugs(33, DBG_IMPORTANT, "BUG: ConnStateData was not destroyed properly; " << clientConnection);
626 
627  if (bodyPipe != NULL)
628  stopProducingFor(bodyPipe, false);
629 
630  delete bodyParser; // TODO: pool
631 
632 #if USE_OPENSSL
633  delete sslServerBump;
634 #endif
635 }
636 
643 void
645 {
646  HttpRequest *request = http->request;
647 
648  debugs(33, 3, "http_ver = " << request->http_ver);
649  debugs(33, 3, "method = " << request->method);
650 
651  // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
652  request->flags.proxyKeepalive = request->persistent();
653 }
654 
656 static int
658 {
659  // No Content-Length means this request just has no body, but conflicting
660  // Content-Lengths mean a message framing error (RFC 7230 Section 3.3.3 #4).
662  return 0;
663 
664  switch (r->method.id()) {
665 
666  case Http::METHOD_GET:
667 
668  case Http::METHOD_HEAD:
669  /* We do not want to see a request entity on GET/HEAD requests */
670  return (r->content_length <= 0 || Config.onoff.request_entities);
671 
672  default:
673  /* For other types of requests we don't care */
674  return 1;
675  }
676 
677  /* NOT REACHED */
678 }
679 
680 int
682 {
684  bodyLength > Config.maxRequestBodySize)
685  return 1; /* too large */
686 
687  return 0;
688 }
689 
690 bool
692 {
693  return request->multipartRangeRequest();
694 }
695 
696 void
698 {
699  mb->appendf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
700  debugs(33, 6, "buf offset: " << mb->size);
701 }
702 
703 void
704 clientPackRangeHdr(const HttpReplyPointer &rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
705 {
706  HttpHeader hdr(hoReply);
707  assert(rep);
708  assert(spec);
709 
710  /* put boundary */
711  debugs(33, 5, "appending boundary: " << boundary);
712  /* rfc2046 requires to _prepend_ boundary with <crlf>! */
713  mb->appendf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
714 
715  /* stuff the header with required entries and pack it */
716 
717  if (rep->header.has(Http::HdrType::CONTENT_TYPE))
719 
720  httpHeaderAddContRange(&hdr, *spec, rep->content_length);
721 
722  hdr.packInto(mb);
723  hdr.clean();
724 
725  /* append <crlf> (we packed a header, not a reply) */
726  mb->append("\r\n", 2);
727 }
728 
734 int
736 {
737  int64_t clen = 0;
738  MemBuf mb;
739 
740  assert(memObject());
741 
742  mb.init();
743  HttpHdrRange::iterator pos = request->range->begin();
744 
745  while (pos != request->range->end()) {
746  /* account for headers for this range */
747  mb.reset();
748  clientPackRangeHdr(&storeEntry()->mem().freshestReply(),
749  *pos, range_iter.boundary, &mb);
750  clen += mb.size;
751 
752  /* account for range content */
753  clen += (*pos)->length;
754 
755  debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
756  ++pos;
757  }
758 
759  /* account for the terminating boundary */
760  mb.reset();
761 
762  clientPackTermBound(range_iter.boundary, &mb);
763 
764  clen += mb.size;
765 
766  mb.clean();
767 
768  return clen;
769 }
770 
774 String
776 {
777  const char *key;
778  String b(APP_FULLNAME);
779  b.append(":",1);
780  key = storeEntry()->getMD5Text();
781  b.append(key, strlen(key));
782  return b;
783 }
784 
794 void
796  HttpReply * rep, StoreIOBuffer receivedData)
797 {
798  // do not try to deliver if client already ABORTED
799  if (!http->getConn() || !cbdataReferenceValid(http->getConn()) || !Comm::IsConnOpen(http->getConn()->clientConnection))
800  return;
801 
802  /* Test preconditions */
803  assert(node != NULL);
805  /* TODO: handle this rather than asserting
806  * - it should only ever happen if we cause an abort and
807  * the callback chain loops back to here, so we can simply return.
808  * However, that itself shouldn't happen, so it stays as an assert for now.
809  */
811  assert(node->node.next == NULL);
812  Http::StreamPointer context = dynamic_cast<Http::Stream *>(node->data.getRaw());
813  assert(context != NULL);
814 
815  /* TODO: check offset is what we asked for */
816 
817  // TODO: enforces HTTP/1 MUST on pipeline order, but is irrelevant to HTTP/2
818  if (context != http->getConn()->pipeline.front())
819  context->deferRecipientForLater(node, rep, receivedData);
820  else if (http->getConn()->cbControlMsgSent) // 1xx to the user is pending
821  context->deferRecipientForLater(node, rep, receivedData);
822  else
823  http->getConn()->handleReply(rep, receivedData);
824 
826 }
827 
833 void
835 {
836  /* Test preconditions */
837  assert(node != NULL);
838  /* TODO: handle this rather than asserting
839  * - it should only ever happen if we cause an abort and
840  * the callback chain loops back to here, so we can simply return.
841  * However, that itself shouldn't happen, so it stays as an assert for now.
842  */
844  /* Set null by ContextFree */
845  assert(node->node.next == NULL);
846  /* this is the assert discussed above */
847  assert(NULL == dynamic_cast<Http::Stream *>(node->data.getRaw()));
848  /* We are only called when the client socket shutsdown.
849  * Tell the prev pipeline member we're finished
850  */
851  clientStreamDetach(node, http);
852 }
853 
854 void
856 {
857  debugs(33, 5, HERE << clientConnection << " reading next req");
858 
859  fd_note(clientConnection->fd, "Idle client: Waiting for next request");
864  AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
865  TimeoutDialer, this, ConnStateData::requestTimeout);
866  commSetConnTimeout(clientConnection, clientConnection->timeLeft(idleTimeout()), timeoutCall);
867 
868  readSomeData();
870 }
871 
872 static void
874 {
875  debugs(33, 2, HERE << conn->clientConnection << " Sending next");
876 
879  if (deferredRequest->flags.deferred) {
881  assert(deferredRequest->http->out.size == 0);
883  clientSocketRecipient(deferredRequest->deferredparams.node,
884  deferredRequest->http,
885  deferredRequest->deferredparams.rep,
886  deferredRequest->deferredparams.queuedBuffer);
887  }
888 
892 }
893 
894 void
896 {
897  if (!Comm::IsConnOpen(clientConnection)) {
898  debugs(33, 2, clientConnection << " Connection was closed");
899  return;
900  }
901 
902  if (pinning.pinned && !Comm::IsConnOpen(pinning.serverConnection)) {
903  debugs(33, 2, clientConnection << " Connection was pinned but server side gone. Terminating client connection");
904  clientConnection->close();
905  return;
906  }
907 
922  if (const char *reason = stoppedReceiving()) {
923  debugs(33, 3, "closing for earlier request error: " << reason);
924  clientConnection->close();
925  return;
926  }
927 
938  if (clientParseRequests()) {
939  debugs(33, 3, clientConnection << ": parsed next request from buffer");
940  }
941 
949  if (commIsHalfClosed(clientConnection->fd) && pipeline.empty()) {
950  debugs(33, 3, "half-closed client with no pending requests, closing");
951  clientConnection->close();
952  return;
953  }
954 
962  Http::StreamPointer deferredRequest = pipeline.front();
963  if (deferredRequest != nullptr) {
964  debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded");
965  ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
966  } else if (flags.readMore) {
967  debugs(33, 3, clientConnection << ": calling readNextRequest()");
968  readNextRequest();
969  } else {
970  // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
971  debugs(33, DBG_IMPORTANT, MYNAME << "abandoning " << clientConnection);
972  }
973 }
974 
975 void
977 {
978  debugs(33, 4, HERE << "sending error (" << clientConnection << "): " << error <<
979  "; old receiving error: " <<
980  (stoppedReceiving() ? stoppedReceiving_ : "none"));
981 
982  if (const char *oldError = stoppedSending()) {
983  debugs(33, 3, HERE << "already stopped sending: " << oldError);
984  return; // nothing has changed as far as this connection is concerned
985  }
986  stoppedSending_ = error;
987 
988  if (!stoppedReceiving()) {
989  if (const int64_t expecting = mayNeedToReadMoreBody()) {
990  debugs(33, 5, HERE << "must still read " << expecting <<
991  " request body bytes with " << inBuf.length() << " unused");
992  return; // wait for the request receiver to finish reading
993  }
994  }
995 
996  clientConnection->close();
997 }
998 
999 void
1001 {
1002  if (pipeline.empty())
1003  return;
1004 
1005  auto ctx = pipeline.front();
1006  if (size) {
1008  if (ctx->http->logType.isTcpHit())
1010  }
1011  ctx->writeComplete(size);
1012 }
1013 
1014 Http::Stream *
1016 {
1017  ClientHttpRequest *http = new ClientHttpRequest(this);
1018  http->req_sz = inBuf.length();
1019  http->setErrorUri(uri);
1020  auto *context = new Http::Stream(clientConnection, http);
1021  StoreIOBuffer tempBuffer;
1022  tempBuffer.data = context->reqbuf;
1023  tempBuffer.length = HTTP_REQBUF_SZ;
1026  clientSocketDetach, context, tempBuffer);
1027  return context;
1028 }
1029 
1030 void
1032 {
1033  // RegisteredRunner API callback - Squid has been shut down
1034 
1035  // if connection is idle terminate it now,
1036  // otherwise wait for grace period to end
1037  if (pipeline.empty())
1038  endingShutdown();
1039 }
1040 
1041 void
1043 {
1044  // RegisteredRunner API callback - Squid shutdown grace period is over
1045 
1046  // force the client connection to close immediately
1047  // swanSong() in the close handler will cleanup.
1048  if (Comm::IsConnOpen(clientConnection))
1049  clientConnection->close();
1050 }
1051 
1052 char *
1053 skipLeadingSpace(char *aString)
1054 {
1055  char *result = aString;
1056 
1057  while (xisspace(*aString))
1058  ++aString;
1059 
1060  return result;
1061 }
1062 
1068 const char *
1069 findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
1070 {
1071  if (NULL == end) {
1072  end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1073  assert(end);
1074  }
1075 
1076  for (; end > uriAndHTTPVersion; --end) {
1077  if (*end == '\n' || *end == '\r')
1078  continue;
1079 
1080  if (xisspace(*end)) {
1081  if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1082  return end + 1;
1083  else
1084  break;
1085  }
1086  }
1087 
1088  return NULL;
1089 }
1090 
1091 static char *
1093 {
1094  int vhost = conn->port->vhost;
1095  int vport = conn->port->vport;
1096  static char ipbuf[MAX_IPSTRLEN];
1097 
1098  /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1099 
1100  static const SBuf cache_object("cache_object://");
1101  if (hp->requestUri().startsWith(cache_object))
1102  return nullptr; /* already in good shape */
1103 
1104  // XXX: re-use proper URL parser for this
1105  SBuf url = hp->requestUri(); // use full provided URI if we abort
1106  do { // use a loop so we can break out of it
1107  ::Parser::Tokenizer tok(url);
1108  if (tok.skip('/')) // origin-form URL already.
1109  break;
1110 
1111  if (conn->port->vhost)
1112  return nullptr; /* already in good shape */
1113 
1114  // skip the URI scheme
1115  static const CharacterSet uriScheme = CharacterSet("URI-scheme","+-.") + CharacterSet::ALPHA + CharacterSet::DIGIT;
1116  static const SBuf uriSchemeEnd("://");
1117  if (!tok.skipAll(uriScheme) || !tok.skip(uriSchemeEnd))
1118  break;
1119 
1120  // skip the authority segment
1121  // RFC 3986 complex nested ABNF for "authority" boils down to this:
1122  static const CharacterSet authority = CharacterSet("authority","-._~%:@[]!$&'()*+,;=") +
1124  if (!tok.skipAll(authority))
1125  break;
1126 
1127  static const SBuf slashUri("/");
1128  const SBuf t = tok.remaining();
1129  if (t.isEmpty())
1130  url = slashUri;
1131  else if (t[0]=='/') // looks like path
1132  url = t;
1133  else if (t[0]=='?' || t[0]=='#') { // looks like query or fragment. fix '/'
1134  url = slashUri;
1135  url.append(t);
1136  } // else do nothing. invalid path
1137 
1138  } while(false);
1139 
1140 #if SHOULD_REJECT_UNKNOWN_URLS
1141  // reject URI which are not well-formed even after the processing above
1142  if (url.isEmpty() || url[0] != '/') {
1143  hp->parseStatusCode = Http::scBadRequest;
1144  return conn->abortRequestParsing("error:invalid-request");
1145  }
1146 #endif
1147 
1148  if (vport < 0)
1149  vport = conn->clientConnection->local.port();
1150 
1151  char *host = NULL;
1152  if (vhost && (host = hp->getHostHeaderField())) {
1153  debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
1154  char thost[256];
1155  if (vport > 0) {
1156  thost[0] = '\0';
1157  char *t = NULL;
1158  if (host[strlen(host) - 1] != ']' && (t = strrchr(host,':')) != nullptr) {
1159  strncpy(thost, host, (t-host));
1160  snprintf(thost+(t-host), sizeof(thost)-(t-host), ":%d", vport);
1161  host = thost;
1162  } else if (!t) {
1163  snprintf(thost, sizeof(thost), "%s:%d",host, vport);
1164  host = thost;
1165  }
1166  } // else nothing to alter port-wise.
1167  const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1168  const int url_sz = scheme.length() + strlen(host) + url.length() + 32;
1169  char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1170  snprintf(uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH, SQUIDSBUFPRINT(scheme), host, SQUIDSBUFPRINT(url));
1171  debugs(33, 5, "ACCEL VHOST REWRITE: " << uri);
1172  return uri;
1173  } else if (conn->port->defaultsite /* && !vhost */) {
1174  debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
1175  char vportStr[32];
1176  vportStr[0] = '\0';
1177  if (vport > 0) {
1178  snprintf(vportStr, sizeof(vportStr),":%d",vport);
1179  }
1180  const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1181  const int url_sz = scheme.length() + strlen(conn->port->defaultsite) + sizeof(vportStr) + url.length() + 32;
1182  char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1183  snprintf(uri, url_sz, SQUIDSBUFPH "://%s%s" SQUIDSBUFPH,
1184  SQUIDSBUFPRINT(scheme), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
1185  debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << uri);
1186  return uri;
1187  } else if (vport > 0 /* && (!vhost || no Host:) */) {
1188  debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
1189  /* Put the local socket IP address as the hostname, with whatever vport we found */
1191  const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1192  const int url_sz = scheme.length() + sizeof(ipbuf) + url.length() + 32;
1193  char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1194  snprintf(uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
1195  SQUIDSBUFPRINT(scheme), ipbuf, vport, SQUIDSBUFPRINT(url));
1196  debugs(33, 5, "ACCEL VPORT REWRITE: " << uri);
1197  return uri;
1198  }
1199 
1200  return nullptr;
1201 }
1202 
1203 static char *
1205 {
1206  char *uri = nullptr;
1207  /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1208  if (const char *host = hp->getHostHeaderField()) {
1209  const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1210  const int url_sz = scheme.length() + strlen(host) + hp->requestUri().length() + 32;
1211  uri = static_cast<char *>(xcalloc(url_sz, 1));
1212  snprintf(uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH,
1213  SQUIDSBUFPRINT(scheme),
1214  host,
1215  SQUIDSBUFPRINT(hp->requestUri()));
1216  }
1217  return uri;
1218 }
1219 
1220 char *
1222 {
1223  Must(switchedToHttps());
1224 
1225  if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
1226  return nullptr; /* already in good shape */
1227 
1228  char *uri = buildUrlFromHost(this, hp);
1229 #if USE_OPENSSL
1230  if (!uri) {
1231  Must(tlsConnectPort);
1232  Must(!tlsConnectHostOrIp.isEmpty());
1233  SBuf useHost;
1234  if (!tlsClientSni().isEmpty())
1235  useHost = tlsClientSni();
1236  else
1237  useHost = tlsConnectHostOrIp;
1238 
1239  const SBuf &scheme = AnyP::UriScheme(transferProtocol.protocol).image();
1240  const int url_sz = scheme.length() + useHost.length() + hp->requestUri().length() + 32;
1241  uri = static_cast<char *>(xcalloc(url_sz, 1));
1242  snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH ":%d" SQUIDSBUFPH,
1243  SQUIDSBUFPRINT(scheme),
1244  SQUIDSBUFPRINT(useHost),
1245  tlsConnectPort,
1246  SQUIDSBUFPRINT(hp->requestUri()));
1247  }
1248 #endif
1249  if (uri)
1250  debugs(33, 5, "TLS switching host rewrite: " << uri);
1251  return uri;
1252 }
1253 
1254 static char *
1256 {
1257  // TODO Must() on URI !empty when the parser supports throw. For now avoid assert().
1258  if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
1259  return nullptr; /* already in good shape */
1260 
1261  char *uri = buildUrlFromHost(conn, hp);
1262  if (!uri) {
1263  /* Put the local socket IP address as the hostname. */
1264  static char ipbuf[MAX_IPSTRLEN];
1266  const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1267  const int url_sz = sizeof(ipbuf) + hp->requestUri().length() + 32;
1268  uri = static_cast<char *>(xcalloc(url_sz, 1));
1269  snprintf(uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
1270  SQUIDSBUFPRINT(scheme),
1271  ipbuf, conn->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
1272  }
1273 
1274  if (uri)
1275  debugs(33, 5, "TRANSPARENT REWRITE: " << uri);
1276  return uri;
1277 }
1278 
1279 Http::Stream *
1281 {
1282  /* Attempt to parse the first line; this will define where the method, url, version and header begin */
1283  {
1284  Must(hp);
1285 
1286  if (preservingClientData_)
1287  preservedClientData = inBuf;
1288 
1289  const bool parsedOk = hp->parse(inBuf);
1290 
1291  // sync the buffers after parsing.
1292  inBuf = hp->remaining();
1293 
1294  if (hp->needsMoreData()) {
1295  debugs(33, 5, "Incomplete request, waiting for end of request line");
1296  return NULL;
1297  }
1298 
1299  if (!parsedOk) {
1300  const bool tooBig =
1301  hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge ||
1302  hp->parseStatusCode == Http::scUriTooLong;
1303  auto result = abortRequestParsing(
1304  tooBig ? "error:request-too-large" : "error:invalid-request");
1305  // assume that remaining leftovers belong to this bad request
1306  if (!inBuf.isEmpty())
1307  consumeInput(inBuf.length());
1308  return result;
1309  }
1310  }
1311 
1312  /* We know the whole request is in parser now */
1313  debugs(11, 2, "HTTP Client " << clientConnection);
1314  debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
1315  hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol() << "\n" <<
1316  hp->mimeHeader() <<
1317  "\n----------");
1318 
1319  /* deny CONNECT via accelerated ports */
1320  if (hp->method() == Http::METHOD_CONNECT && port != NULL && port->flags.accelSurrogate) {
1321  debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << transferProtocol << " Accelerator port " << port->s.port());
1322  debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1323  hp->parseStatusCode = Http::scMethodNotAllowed;
1324  return abortRequestParsing("error:method-not-allowed");
1325  }
1326 
1327  /* RFC 7540 section 11.6 registers the method PRI as HTTP/2 specific
1328  * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
1329  * If seen it signals a broken client or proxy has corrupted the traffic.
1330  */
1331  if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
1332  debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << transferProtocol << " port " << port->s.port());
1333  debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1334  hp->parseStatusCode = Http::scMethodNotAllowed;
1335  return abortRequestParsing("error:method-not-allowed");
1336  }
1337 
1338  if (hp->method() == Http::METHOD_NONE) {
1339  debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1340  hp->parseStatusCode = Http::scMethodNotAllowed;
1341  return abortRequestParsing("error:unsupported-request-method");
1342  }
1343 
1344  // Process headers after request line
1345  debugs(33, 3, "complete request received. " <<
1346  "prefix_sz = " << hp->messageHeaderSize() <<
1347  ", request-line-size=" << hp->firstLineSize() <<
1348  ", mime-header-size=" << hp->headerBlockSize() <<
1349  ", mime header block:\n" << hp->mimeHeader() << "\n----------");
1350 
1351  /* Ok, all headers are received */
1352  ClientHttpRequest *http = new ClientHttpRequest(this);
1353 
1354  http->req_sz = hp->messageHeaderSize();
1355  Http::Stream *result = new Http::Stream(clientConnection, http);
1356 
1357  StoreIOBuffer tempBuffer;
1358  tempBuffer.data = result->reqbuf;
1359  tempBuffer.length = HTTP_REQBUF_SZ;
1360 
1361  ClientStreamData newServer = new clientReplyContext(http);
1362  ClientStreamData newClient = result;
1363  clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
1365  clientSocketDetach, newClient, tempBuffer);
1366 
1367  /* set url */
1368  debugs(33,5, "Prepare absolute URL from " <<
1369  (transparent()?"intercept":(port->flags.accelSurrogate ? "accel":"")));
1370  /* Rewrite the URL in transparent or accelerator mode */
1371  /* NP: there are several cases to traverse here:
1372  * - standard mode (forward proxy)
1373  * - transparent mode (TPROXY)
1374  * - transparent mode with failures
1375  * - intercept mode (NAT)
1376  * - intercept mode with failures
1377  * - accelerator mode (reverse proxy)
1378  * - internal relative-URL
1379  * - mixed combos of the above with internal URL
1380  * - remote interception with PROXY protocol
1381  * - remote reverse-proxy with PROXY protocol
1382  */
1383  if (switchedToHttps()) {
1384  http->uri = prepareTlsSwitchingURL(hp);
1385  } else if (transparent()) {
1386  /* intercept or transparent mode, properly working with no failures */
1387  http->uri = prepareTransparentURL(this, hp);
1388 
1389  } else if (internalCheck(hp->requestUri())) { // NP: only matches relative-URI
1390  /* internal URL mode */
1391  /* prepend our name & port */
1392  http->uri = xstrdup(internalLocalUri(NULL, hp->requestUri()));
1393  // We just re-wrote the URL. Must replace the Host: header.
1394  // But have not parsed there yet!! flag for local-only handling.
1395  http->flags.internal = true;
1396 
1397  } else if (port->flags.accelSurrogate) {
1398  /* accelerator mode */
1399  http->uri = prepareAcceleratedURL(this, hp);
1400  http->flags.accel = true;
1401  }
1402 
1403  if (!http->uri) {
1404  /* No special rewrites have been applied above, use the
1405  * requested url. may be rewritten later, so make extra room */
1406  int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
1407  http->uri = (char *)xcalloc(url_sz, 1);
1408  SBufToCstring(http->uri, hp->requestUri());
1409  }
1410 
1411  result->flags.parsed_ok = 1;
1412  return result;
1413 }
1414 
1415 bool
1417 {
1418  if (size == 0) {
1419  if (pipeline.empty() && inBuf.isEmpty()) {
1420  /* no current or pending requests */
1421  debugs(33, 4, HERE << clientConnection << " closed");
1422  return true;
1423  } else if (!Config.onoff.half_closed_clients) {
1424  /* admin doesn't want to support half-closed client sockets */
1425  debugs(33, 3, HERE << clientConnection << " aborted (half_closed_clients disabled)");
1426  pipeline.terminateAll(0);
1427  return true;
1428  }
1429  }
1430 
1431  return false;
1432 }
1433 
1434 void
1435 ConnStateData::consumeInput(const size_t byteCount)
1436 {
1437  assert(byteCount > 0 && byteCount <= inBuf.length());
1438  inBuf.consume(byteCount);
1439  debugs(33, 5, "inBuf has " << inBuf.length() << " unused bytes");
1440 }
1441 
1442 void
1444 {
1445  // Were we expecting to read more request body from half-closed connection?
1446  if (mayNeedToReadMoreBody() && commIsHalfClosed(clientConnection->fd)) {
1447  debugs(33, 3, HERE << "truncated body: closing half-closed " << clientConnection);
1448  clientConnection->close();
1449  return;
1450  }
1451 
1452  if (flags.readMore)
1453  readSomeData();
1454 }
1455 
1456 void
1458 {
1459  // From HTTP p.o.v., we do not have to close after every error detected
1460  // at the client-side, but many such errors do require closure and the
1461  // client-side code is bad at handling errors so we play it safe.
1462  if (request)
1463  request->flags.proxyKeepalive = false;
1464  flags.readMore = false;
1465  debugs(33,4, HERE << "Will close after error: " << clientConnection);
1466 }
1467 
1468 #if USE_OPENSSL
1470 {
1471  ClientHttpRequest *http = context->http;
1472 
1473  if (!sslServerBump)
1474  return false;
1475 
1476  assert(sslServerBump->entry);
1477  // Did we create an error entry while processing CONNECT?
1478  if (!sslServerBump->entry->isEmpty()) {
1479  quitAfterError(http->request);
1480 
1481  // Get the saved error entry and send it to the client by replacing the
1482  // ClientHttpRequest store entry with it.
1484  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1485  assert(repContext);
1486  debugs(33, 5, "Responding with delated error for " << http->uri);
1487  repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
1488 
1489  // Get error details from the fake certificate-peeking request.
1490  http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
1491  context->pullData();
1492  return true;
1493  }
1494 
1495  // In bump-server-first mode, we have not necessarily seen the intended
1496  // server name at certificate-peeking time. Check for domain mismatch now,
1497  // when we can extract the intended name from the bumped HTTP request.
1498  if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
1499  HttpRequest *request = http->request;
1500  if (!Ssl::checkX509ServerValidity(srvCert.get(), request->url.host())) {
1501  debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
1502  "does not match domainname " << request->url.host());
1503 
1504  bool allowDomainMismatch = false;
1507  check.al = http->al;
1509  check.syncAle(request, http->log_uri);
1510  allowDomainMismatch = check.fastCheck().allowed();
1511  delete check.sslErrors;
1512  check.sslErrors = NULL;
1513  }
1514 
1515  if (!allowDomainMismatch) {
1516  quitAfterError(request);
1517 
1519  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1520  assert (repContext);
1521 
1522  request->hier = sslServerBump->request->hier;
1523 
1524  // Create an error object and fill it
1525  const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request, http->al);
1526  err->src_addr = clientConnection->remote;
1527  Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
1529  srvCert.get(), nullptr);
1530  err->detail = errDetail;
1531  repContext->setReplyToError(request->method, err);
1532  assert(context->http->out.offset == 0);
1533  context->pullData();
1534  return true;
1535  }
1536  }
1537  }
1538 
1539  return false;
1540 }
1541 #endif // USE_OPENSSL
1542 
1544 bool
1546 {
1547  assert(conn);
1548  assert(conn->pipeline.front() == context);
1549  return conn->tunnelOnError(method, requestError);
1550 }
1551 
1553 bool
1555 {
1557  debugs(33, 5, "disabled; send error: " << requestError);
1558  return false;
1559  }
1560 
1561  if (!preservingClientData_) {
1562  debugs(33, 3, "may have forgotten client data; send error: " << requestError);
1563  return false;
1564  }
1565 
1566  const auto context = pipeline.front();
1567  const auto http = context ? context->http : nullptr;
1568  const auto request = http ? http->request : nullptr;
1569 
1571  checklist.al = http ? http->al : nullptr;
1572  checklist.requestErrorType = requestError;
1573  checklist.src_addr = clientConnection->remote;
1574  checklist.my_addr = clientConnection->local;
1575  checklist.conn(this);
1576  const char *log_uri = http ? http->log_uri : nullptr;
1577  checklist.syncAle(request, log_uri);
1578  auto answer = checklist.fastCheck();
1579  if (answer.allowed() && answer.kind == 1) {
1580  debugs(33, 3, "Request will be tunneled to server");
1581  if (context)
1582  context->finished(); // Will remove from pipeline queue
1583  Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
1584  return initiateTunneledRequest(request, Http::METHOD_NONE, "unknown-protocol", preservedClientData);
1585  }
1586  debugs(33, 3, "denied; send error: " << requestError);
1587  return false;
1588 }
1589 
1590 void
1592 {
1593  /*
1594  * DPW 2007-05-18
1595  * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
1596  * to here because calling comm_reset_close() causes http to
1597  * be freed before accessing.
1598  */
1599  if (request != NULL && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
1600  debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
1601  conn->flags.readMore = false;
1603  }
1604 }
1605 
1606 void
1608 {
1609  ClientHttpRequest *http = context->http;
1610  bool chunked = false;
1611  bool mustReplyToOptions = false;
1612  bool unsupportedTe = false;
1613  bool expectBody = false;
1614 
1615  // We already have the request parsed and checked, so we
1616  // only need to go through the final body/conn setup to doCallouts().
1617  assert(http->request);
1619 
1620  // temporary hack to avoid splitting this huge function with sensitive code
1621  const bool isFtp = !hp;
1622 
1623  // Some blobs below are still HTTP-specific, but we would have to rewrite
1624  // this entire function to remove them from the FTP code path. Connection
1625  // setup and body_pipe preparation blobs are needed for FTP.
1626 
1627  request->manager(conn, http->al);
1628 
1629  request->flags.accelerated = http->flags.accel;
1630  request->flags.sslBumped=conn->switchedToHttps();
1631  // TODO: decouple http->flags.accel from request->flags.sslBumped
1632  request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
1633  !conn->port->allow_direct : 0;
1634  request->sources |= isFtp ? Http::Message::srcFtp :
1635  ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
1636 #if USE_AUTH
1637  if (request->flags.sslBumped) {
1638  if (conn->getAuth() != NULL)
1639  request->auth_user_request = conn->getAuth();
1640  }
1641 #endif
1642 
1643  if (internalCheck(request->url.path())) {
1644  if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
1645  debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
1646  http->flags.internal = true;
1647  } else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
1648  debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
1649  request->url.setScheme(AnyP::PROTO_HTTP, "http");
1650  request->url.host(internalHostname());
1651  request->url.port(getMyPort());
1652  http->flags.internal = true;
1653  http->setLogUriToRequestUri();
1654  } else
1655  debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
1656  }
1657 
1658  request->flags.internal = http->flags.internal;
1659 
1660  if (!isFtp) {
1661  // XXX: for non-HTTP messages instantiate a different Http::Message child type
1662  // for now Squid only supports HTTP requests
1663  const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
1664  assert(request->http_ver.protocol == http_ver.protocol);
1665  request->http_ver.major = http_ver.major;
1666  request->http_ver.minor = http_ver.minor;
1667  }
1668 
1669  if (request->header.chunked()) {
1670  chunked = true;
1671  } else if (request->header.has(Http::HdrType::TRANSFER_ENCODING)) {
1673  // HTTP/1.1 requires chunking to be the last encoding if there is one
1674  unsupportedTe = te.size() && te != "identity";
1675  } // else implied identity coding
1676 
1677  mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
1678  (request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0);
1679  if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions || unsupportedTe) {
1681  conn->quitAfterError(request.getRaw());
1682  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1683  assert (repContext);
1685  conn->clientConnection->remote, request.getRaw(), NULL, NULL);
1686  assert(context->http->out.offset == 0);
1687  context->pullData();
1688  clientProcessRequestFinished(conn, request);
1689  return;
1690  }
1691 
1692  if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
1694  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1695  assert (repContext);
1696  conn->quitAfterError(request.getRaw());
1697  repContext->setReplyToError(ERR_INVALID_REQ,
1698  Http::scLengthRequired, request->method, NULL,
1699  conn->clientConnection->remote, request.getRaw(), NULL, NULL);
1700  assert(context->http->out.offset == 0);
1701  context->pullData();
1702  clientProcessRequestFinished(conn, request);
1703  return;
1704  }
1705 
1706  clientSetKeepaliveFlag(http);
1707  // Let tunneling code be fully responsible for CONNECT requests
1708  if (http->request->method == Http::METHOD_CONNECT) {
1709  context->mayUseConnection(true);
1710  conn->flags.readMore = false;
1711  }
1712 
1713 #if USE_OPENSSL
1714  if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
1715  clientProcessRequestFinished(conn, request);
1716  return;
1717  }
1718 #endif
1719 
1720  /* Do we expect a request-body? */
1721  expectBody = chunked || request->content_length > 0;
1722  if (!context->mayUseConnection() && expectBody) {
1723  request->body_pipe = conn->expectRequestBody(
1724  chunked ? -1 : request->content_length);
1725 
1726  /* Is it too large? */
1727  if (!chunked && // if chunked, we will check as we accumulate
1730  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1731  assert (repContext);
1732  conn->quitAfterError(request.getRaw());
1733  repContext->setReplyToError(ERR_TOO_BIG,
1735  conn->clientConnection->remote, http->request, NULL, NULL);
1736  assert(context->http->out.offset == 0);
1737  context->pullData();
1738  clientProcessRequestFinished(conn, request);
1739  return;
1740  }
1741 
1742  if (!isFtp) {
1743  // We may stop producing, comm_close, and/or call setReplyToError()
1744  // below, so quit on errors to avoid http->doCallouts()
1745  if (!conn->handleRequestBodyData()) {
1746  clientProcessRequestFinished(conn, request);
1747  return;
1748  }
1749 
1750  if (!request->body_pipe->productionEnded()) {
1751  debugs(33, 5, "need more request body");
1752  context->mayUseConnection(true);
1753  assert(conn->flags.readMore);
1754  }
1755  }
1756  }
1757 
1758  http->calloutContext = new ClientRequestContext(http);
1759 
1760  http->doCallouts();
1761 
1762  clientProcessRequestFinished(conn, request);
1763 }
1764 
1765 int
1767 {
1768  // TODO: Support pipelined requests through pinned connections.
1769  if (pinning.pinned)
1770  return 0;
1772 }
1773 
1779 bool
1781 {
1782  const int existingRequestCount = pipeline.count();
1783 
1784  // default to the configured pipeline size.
1785  // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
1786 #if USE_OPENSSL
1787  const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
1788 #else
1789  const int internalRequest = 0;
1790 #endif
1791  const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
1792 
1793  // when queue filled already we cant add more.
1794  if (existingRequestCount >= concurrentRequestLimit) {
1795  debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
1796  debugs(33, 5, clientConnection << " deferring new request until one is done");
1797  return true;
1798  }
1799 
1800  return false;
1801 }
1802 
1808 bool
1810 {
1812  return proxyProtocolError("PROXY client not permitted by default ACL");
1813 
1814  ACLFilledChecklist ch(Config.accessList.proxyProtocol, NULL, clientConnection->rfc931);
1815  ch.src_addr = clientConnection->remote;
1816  ch.my_addr = clientConnection->local;
1817  ch.conn(this);
1818 
1819  if (!ch.fastCheck().allowed())
1820  return proxyProtocolError("PROXY client not permitted by ACLs");
1821 
1822  return true;
1823 }
1824 
1830 bool
1832 {
1833  if (msg) {
1834  // This is important to know, but maybe not so much that flooding the log is okay.
1835 #if QUIET_PROXY_PROTOCOL
1836  // display the first of every 32 occurances at level 1, the others at level 2.
1837  static uint8_t hide = 0;
1838  debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
1839 #else
1840  debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
1841 #endif
1842  mustStop(msg);
1843  }
1844  return false;
1845 }
1846 
1851 bool
1853 {
1854  try {
1855  const auto parsed = ProxyProtocol::Parse(inBuf);
1856  proxyProtocolHeader_ = parsed.header;
1857  assert(bool(proxyProtocolHeader_));
1858  inBuf.consume(parsed.size);
1859  needProxyProtocolHeader_ = false;
1860  if (proxyProtocolHeader_->hasForwardedAddresses()) {
1861  clientConnection->local = proxyProtocolHeader_->destinationAddress;
1862  clientConnection->remote = proxyProtocolHeader_->sourceAddress;
1863  if ((clientConnection->flags & COMM_TRANSPARENT))
1864  clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
1865  debugs(33, 5, "PROXY/" << proxyProtocolHeader_->version() << " upgrade: " << clientConnection);
1866  }
1867  } catch (const Parser::BinaryTokenizer::InsufficientInput &) {
1868  debugs(33, 3, "PROXY protocol: waiting for more than " << inBuf.length() << " bytes");
1869  return false;
1870  } catch (const std::exception &e) {
1871  return proxyProtocolError(e.what());
1872  }
1873  return true;
1874 }
1875 
1876 void
1878 {
1879  if (receivedFirstByte_)
1880  return;
1881 
1882  receivedFirstByte_ = true;
1883  // Set timeout to Config.Timeout.request
1885  AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
1886  TimeoutDialer, this, ConnStateData::requestTimeout);
1887  commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
1888 }
1889 
1895 bool
1897 {
1898  bool parsed_req = false;
1899 
1900  debugs(33, 5, HERE << clientConnection << ": attempting to parse");
1901 
1902  // Loop while we have read bytes that are not needed for producing the body
1903  // On errors, bodyPipe may become nil, but readMore will be cleared
1904  while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
1905 
1906  // Prohibit concurrent requests when using a pinned to-server connection
1907  // because our Client classes do not support request pipelining.
1908  if (pinning.pinned && !pinning.readHandler) {
1909  debugs(33, 3, clientConnection << " waits for busy " << pinning.serverConnection);
1910  break;
1911  }
1912 
1913  /* Limit the number of concurrent requests */
1914  if (concurrentRequestQueueFilled())
1915  break;
1916 
1917  // try to parse the PROXY protocol header magic bytes
1918  if (needProxyProtocolHeader_) {
1919  if (!parseProxyProtocolHeader())
1920  break;
1921 
1922  // we have been waiting for PROXY to provide client-IP
1923  // for some lookups, ie rDNS and IDENT.
1924  whenClientIpKnown();
1925 
1926  // Done with PROXY protocol which has cleared preservingClientData_.
1927  // If the next protocol supports on_unsupported_protocol, then its
1928  // parseOneRequest() must reset preservingClientData_.
1929  assert(!preservingClientData_);
1930  }
1931 
1932  if (Http::StreamPointer context = parseOneRequest()) {
1933  debugs(33, 5, clientConnection << ": done parsing a request");
1934 
1935  AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
1936  CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
1937  commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
1938 
1939  context->registerWithConn();
1940 
1941 #if USE_OPENSSL
1942  if (switchedToHttps())
1943  parsedBumpedRequestCount++;
1944 #endif
1945 
1946  processParsedRequest(context);
1947 
1948  parsed_req = true; // XXX: do we really need to parse everything right NOW ?
1949 
1950  if (context->mayUseConnection()) {
1951  debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
1952  break;
1953  }
1954  } else {
1955  debugs(33, 5, clientConnection << ": not enough request data: " <<
1956  inBuf.length() << " < " << Config.maxRequestHeaderSize);
1957  Must(inBuf.length() < Config.maxRequestHeaderSize);
1958  break;
1959  }
1960  }
1961 
1962  /* XXX where to 'finish' the parsing pass? */
1963  return parsed_req;
1964 }
1965 
1966 void
1968 {
1969 #if USE_OPENSSL
1970  if (parsingTlsHandshake) {
1971  parseTlsHandshake();
1972  return;
1973  }
1974 #endif
1975 
1976  /* Process next request */
1977  if (pipeline.empty())
1978  fd_note(clientConnection->fd, "Reading next request");
1979 
1980  if (!clientParseRequests()) {
1981  if (!isOpen())
1982  return;
1983  /*
1984  * If the client here is half closed and we failed
1985  * to parse a request, close the connection.
1986  * The above check with connFinishedWithConn() only
1987  * succeeds _if_ the buffer is empty which it won't
1988  * be if we have an incomplete request.
1989  * XXX: This duplicates ConnStateData::kick
1990  */
1991  if (pipeline.empty() && commIsHalfClosed(clientConnection->fd)) {
1992  debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
1993  clientConnection->close();
1994  return;
1995  }
1996  }
1997 
1998  if (!isOpen())
1999  return;
2000 
2001  clientAfterReadingRequests();
2002 }
2003 
2010 bool
2012 {
2013  // if we are reading a body, stuff data into the body pipe
2014  if (bodyPipe != NULL)
2015  return handleRequestBodyData();
2016  return true;
2017 }
2018 
2026 bool
2028 {
2029  assert(bodyPipe != NULL);
2030 
2031  if (bodyParser) { // chunked encoding
2032  if (const err_type error = handleChunkedRequestBody()) {
2033  abortChunkedRequestBody(error);
2034  return false;
2035  }
2036  } else { // identity encoding
2037  debugs(33,5, HERE << "handling plain request body for " << clientConnection);
2038  const size_t putSize = bodyPipe->putMoreData(inBuf.c_str(), inBuf.length());
2039  if (putSize > 0)
2040  consumeInput(putSize);
2041 
2042  if (!bodyPipe->mayNeedMoreData()) {
2043  // BodyPipe will clear us automagically when we produced everything
2044  bodyPipe = NULL;
2045  }
2046  }
2047 
2048  if (!bodyPipe) {
2049  debugs(33,5, HERE << "produced entire request body for " << clientConnection);
2050 
2051  if (const char *reason = stoppedSending()) {
2052  /* we've finished reading like good clients,
2053  * now do the close that initiateClose initiated.
2054  */
2055  debugs(33, 3, HERE << "closing for earlier sending error: " << reason);
2056  clientConnection->close();
2057  return false;
2058  }
2059  }
2060 
2061  return true;
2062 }
2063 
2065 err_type
2067 {
2068  debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
2069 
2070  try { // the parser will throw on errors
2071 
2072  if (inBuf.isEmpty()) // nothing to do
2073  return ERR_NONE;
2074 
2075  BodyPipeCheckout bpc(*bodyPipe);
2076  bodyParser->setPayloadBuffer(&bpc.buf);
2077  const bool parsed = bodyParser->parse(inBuf);
2078  inBuf = bodyParser->remaining(); // sync buffers
2079  bpc.checkIn();
2080 
2081  // dechunk then check: the size limit applies to _dechunked_ content
2082  if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
2083  return ERR_TOO_BIG;
2084 
2085  if (parsed) {
2086  finishDechunkingRequest(true);
2087  Must(!bodyPipe);
2088  return ERR_NONE; // nil bodyPipe implies body end for the caller
2089  }
2090 
2091  // if chunk parser needs data, then the body pipe must need it too
2092  Must(!bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
2093 
2094  // if parser needs more space and we can consume nothing, we will stall
2095  Must(!bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
2096  } catch (...) { // TODO: be more specific
2097  debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
2098  return ERR_INVALID_REQ;
2099  }
2100 
2101  debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
2102  return ERR_NONE;
2103 }
2104 
2106 void
2108 {
2109  finishDechunkingRequest(false);
2110 
2111  // XXX: The code below works if we fail during initial request parsing,
2112  // but if we fail when the server connection is used already, the server may send
2113  // us its response too, causing various assertions. How to prevent that?
2114 #if WE_KNOW_HOW_TO_SEND_ERRORS
2115  Http::StreamPointer context = pipeline.front();
2116  if (context != NULL && !context->http->out.offset) { // output nothing yet
2117  clientStreamNode *node = context->getClientReplyContext();
2118  clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
2119  assert(repContext);
2120  const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
2121  Http::scPayloadTooLarge : HTTP_BAD_REQUEST;
2122  repContext->setReplyToError(error, scode,
2123  repContext->http->request->method,
2124  repContext->http->uri,
2125  CachePeer,
2126  repContext->http->request,
2127  inBuf, NULL);
2128  context->pullData();
2129  } else {
2130  // close or otherwise we may get stuck as nobody will notice the error?
2131  comm_reset_close(clientConnection);
2132  }
2133 #else
2134  debugs(33, 3, HERE << "aborting chunked request without error " << error);
2135  comm_reset_close(clientConnection);
2136 #endif
2137  flags.readMore = false;
2138 }
2139 
2140 void
2142 {
2143  // request reader may get stuck waiting for space if nobody consumes body
2144  if (bodyPipe != NULL)
2145  bodyPipe->enableAutoConsumption();
2146 
2147  // kids extend
2148 }
2149 
2151 void
2153 {
2154  if (!Comm::IsConnOpen(io.conn))
2155  return;
2156 
2157  const err_type error = receivedFirstByte_ ? ERR_REQUEST_PARSE_TIMEOUT : ERR_REQUEST_START_TIMEOUT;
2158  if (tunnelOnError(HttpRequestMethod(), error))
2159  return;
2160 
2161  /*
2162  * Just close the connection to not confuse browsers
2163  * using persistent connections. Some browsers open
2164  * a connection and then do not use it until much
2165  * later (presumeably because the request triggering
2166  * the open has already been completed on another
2167  * connection)
2168  */
2169  debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
2170  io.conn->close();
2171 }
2172 
2173 static void
2175 {
2176  ClientHttpRequest *http = static_cast<ClientHttpRequest *>(io.data);
2177  debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout");
2178  debugs(33, DBG_IMPORTANT, "\t" << http->uri);
2179  http->logType.err.timedout = true;
2180  if (Comm::IsConnOpen(io.conn))
2181  io.conn->close();
2182 }
2183 
2185  AsyncJob("ConnStateData"), // kids overwrite
2186  Server(xact),
2187  bodyParser(nullptr),
2188 #if USE_OPENSSL
2189  sslBumpMode(Ssl::bumpEnd),
2190 #endif
2191  needProxyProtocolHeader_(false),
2192 #if USE_OPENSSL
2193  switchedToHttps_(false),
2194  parsingTlsHandshake(false),
2195  parsedBumpedRequestCount(0),
2196  tlsConnectPort(0),
2197  sslServerBump(NULL),
2198  signAlgorithm(Ssl::algSignTrusted),
2199 #endif
2200  stoppedSending_(NULL),
2201  stoppedReceiving_(NULL)
2202 {
2203  flags.readMore = true; // kids may overwrite
2204  flags.swanSang = false;
2205 
2206  pinning.host = NULL;
2207  pinning.port = -1;
2208  pinning.pinned = false;
2209  pinning.auth = false;
2210  pinning.zeroReply = false;
2211  pinning.peerAccessDenied = false;
2212  pinning.peer = NULL;
2213 
2214  // store the details required for creating more MasterXaction objects as new requests come in
2215  log_addr = xact->tcpClient->remote;
2217 
2218  // register to receive notice of Squid signal events
2219  // which may affect long persisting client connections
2220  registerRunner();
2221 }
2222 
2223 void
2225 {
2228 
2229  if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
2230  (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
2231 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
2232  int i = IP_PMTUDISC_DONT;
2233  if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0) {
2234  int xerrno = errno;
2235  debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerr(xerrno));
2236  }
2237 #else
2238  static bool reported = false;
2239 
2240  if (!reported) {
2241  debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is not supported on your platform.");
2242  reported = true;
2243  }
2244 #endif
2245  }
2246 
2248  AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, ConnStateData::connStateClosed);
2250 
2251  needProxyProtocolHeader_ = port->flags.proxySurrogate;
2253  if (!proxyProtocolValidateClient()) // will close the connection on failure
2254  return;
2255  } else
2257 
2258  // requires needProxyProtocolHeader_ which is initialized above
2260 }
2261 
2262 void
2264 {
2265  if (Config.onoff.log_fqdn)
2267 
2268 #if USE_IDENT
2269  if (Ident::TheConfig.identLookup) {
2270  ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
2271  identChecklist.src_addr = clientConnection->remote;
2272  identChecklist.my_addr = clientConnection->local;
2273  if (identChecklist.fastCheck().allowed())
2275  }
2276 #endif
2277 
2279 
2280 #if USE_DELAY_POOLS
2281  fd_table[clientConnection->fd].clientInfo = NULL;
2282 
2283  if (!Config.onoff.client_db)
2284  return; // client delay pools require client_db
2285 
2286  const auto &pools = ClientDelayPools::Instance()->pools;
2287  if (pools.size()) {
2289 
2290  // TODO: we check early to limit error response bandwith but we
2291  // should recheck when we can honor delay_pool_uses_indirect
2292  // TODO: we should also pass the port details for myportname here.
2295 
2296  for (unsigned int pool = 0; pool < pools.size(); ++pool) {
2297 
2298  /* pools require explicit 'allow' to assign a client into them */
2299  if (pools[pool]->access) {
2300  ch.changeAcl(pools[pool]->access);
2301  auto answer = ch.fastCheck();
2302  if (answer.allowed()) {
2303 
2304  /* request client information from db after we did all checks
2305  this will save hash lookup if client failed checks */
2307  assert(cli);
2308 
2309  /* put client info in FDE */
2310  fd_table[clientConnection->fd].clientInfo = cli;
2311 
2312  /* setup write limiter for this request */
2313  const double burst = floor(0.5 +
2314  (pools[pool]->highwatermark * Config.ClientDelay.initial)/100.0);
2315  cli->setWriteLimiter(pools[pool]->rate, burst, pools[pool]->highwatermark);
2316  break;
2317  } else {
2318  debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
2319  }
2320  }
2321  }
2322  }
2323 #endif
2324 
2325  // kids must extend to actually start doing something (e.g., reading)
2326 }
2327 
2329 void
2331 {
2332  MasterXaction::Pointer xact = params.xaction;
2333  AnyP::PortCfgPointer s = xact->squidPort;
2334 
2335  // NP: it is possible the port was reconfigured when the call or accept() was queued.
2336 
2337  if (params.flag != Comm::OK) {
2338  // Its possible the call was still queued when the client disconnected
2339  debugs(33, 2, s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2340  return;
2341  }
2342 
2343  debugs(33, 4, params.conn << ": accepted");
2344  fd_note(params.conn->fd, "client http connect");
2345 
2346  if (s->tcp_keepalive.enabled)
2347  commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
2348 
2350 
2351  // Socket is ready, setup the connection manager to start using it
2352  auto *srv = Http::NewServer(xact);
2353  AsyncJob::Start(srv); // usually async-calls readSomeData()
2354 }
2355 
2357 static bool
2359 {
2360  const auto conn = connState->clientConnection;
2361  if (Security::CreateServerSession(ctx, conn, connState->port->secure, "client https start")) {
2362  debugs(33, 5, "will negotiate TLS on " << conn);
2363  return true;
2364  }
2365 
2366  debugs(33, DBG_IMPORTANT, "ERROR: could not create TLS server context for " << conn);
2367  conn->close();
2368  return false;
2369 }
2370 
2377 static int
2379 {
2380  // TODO: maybe throw instead of returning -1
2381  // see https://github.com/squid-cache/squid/pull/81#discussion_r153053278
2382  int fd = conn->clientConnection->fd;
2383  auto session = fd_table[fd].ssl.get();
2384 
2385  errno = 0;
2386 
2387 #if USE_OPENSSL
2388  const auto ret = SSL_accept(session);
2389  if (ret > 0)
2390  return 1;
2391 
2392  const int xerrno = errno;
2393  const auto ssl_error = SSL_get_error(session, ret);
2394 
2395  switch (ssl_error) {
2396 
2397  case SSL_ERROR_WANT_READ:
2398  Comm::SetSelect(fd, COMM_SELECT_READ, callback, (callback ? conn : nullptr), 0);
2399  return 0;
2400 
2401  case SSL_ERROR_WANT_WRITE:
2402  Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, (callback ? conn : nullptr), 0);
2403  return 0;
2404 
2405  case SSL_ERROR_SYSCALL:
2406  if (ret == 0) {
2407  debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
2408  } else {
2409  debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
2410  (xerrno == 0 ? Security::ErrorString(ssl_error) : xstrerr(xerrno)));
2411  }
2412  break;
2413 
2414  case SSL_ERROR_ZERO_RETURN:
2415  debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
2416  break;
2417 
2418  default:
2419  debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
2420  fd << ": " << Security::ErrorString(ssl_error) <<
2421  " (" << ssl_error << "/" << ret << ")");
2422  }
2423 
2424 #elif USE_GNUTLS
2425 
2426  const auto x = gnutls_handshake(session);
2427  if (x == GNUTLS_E_SUCCESS)
2428  return 1;
2429 
2430  if (gnutls_error_is_fatal(x)) {
2431  debugs(83, 2, "Error negotiating TLS on " << conn->clientConnection << ": Aborted by client: " << Security::ErrorString(x));
2432 
2433  } else if (x == GNUTLS_E_INTERRUPTED || x == GNUTLS_E_AGAIN) {
2434  const auto ioAction = (gnutls_record_get_direction(session)==0 ? COMM_SELECT_READ : COMM_SELECT_WRITE);
2435  Comm::SetSelect(fd, ioAction, callback, (callback ? conn : nullptr), 0);
2436  return 0;
2437  }
2438 
2439 #else
2440  // Performing TLS handshake should never be reachable without a TLS/SSL library.
2441  (void)session; // avoid compiler and static analysis complaints
2442  fatal("FATAL: HTTPS not supported by this Squid.");
2443 #endif
2444 
2445  return -1;
2446 }
2447 
2449 static void
2450 clientNegotiateSSL(int fd, void *data)
2451 {
2452  ConnStateData *conn = (ConnStateData *)data;
2453 
2454  const int ret = tlsAttemptHandshake(conn, clientNegotiateSSL);
2455  if (ret <= 0) {
2456  if (ret < 0) // An error
2457  conn->clientConnection->close();
2458  return;
2459  }
2460 
2461  Security::SessionPointer session(fd_table[fd].ssl);
2462 
2463 #if USE_OPENSSL
2464  if (Security::SessionIsResumed(session)) {
2465  debugs(83, 2, "Session " << SSL_get_session(session.get()) <<
2466  " reused on FD " << fd << " (" << fd_table[fd].ipaddr <<
2467  ":" << (int)fd_table[fd].remote_port << ")");
2468  } else {
2469  if (Debug::Enabled(83, 4)) {
2470  /* Write out the SSL session details.. actually the call below, but
2471  * OpenSSL headers do strange typecasts confusing GCC.. */
2472  /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
2473 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
2474  PEM_ASN1_write(reinterpret_cast<i2d_of_void *>(i2d_SSL_SESSION),
2475  PEM_STRING_SSL_SESSION, debug_log,
2476  reinterpret_cast<char *>(SSL_get_session(session.get())),
2477  nullptr, nullptr, 0, nullptr, nullptr);
2478 
2479 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2480 
2481  /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
2482  * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
2483  * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
2484  * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
2485  * Because there are two possible usable cast, if you get an error here, try the other
2486  * commented line. */
2487 
2488  PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2489  debug_log,
2490  reinterpret_cast<char *>(SSL_get_session(session.get())),
2491  nullptr, nullptr, 0, nullptr, nullptr);
2492  /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2493  debug_log,
2494  reinterpret_cast<char *>(SSL_get_session(session.get())),
2495  nullptr, nullptr, 0, nullptr, nullptr);
2496  */
2497 #else
2498  debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source.");
2499 
2500 #endif
2501  /* Note: This does not automatically fflush the log file.. */
2502  }
2503 
2504  debugs(83, 2, "New session " << SSL_get_session(session.get()) <<
2505  " on FD " << fd << " (" << fd_table[fd].ipaddr << ":" <<
2506  fd_table[fd].remote_port << ")");
2507  }
2508 #else
2509  debugs(83, 2, "TLS session reuse not yet implemented.");
2510 #endif
2511 
2512  // Connection established. Retrieve TLS connection parameters for logging.
2514 
2515 #if USE_OPENSSL
2516  X509 *client_cert = SSL_get_peer_certificate(session.get());
2517 
2518  if (client_cert) {
2519  debugs(83, 3, "FD " << fd << " client certificate: subject: " <<
2520  X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
2521 
2522  debugs(83, 3, "FD " << fd << " client certificate: issuer: " <<
2523  X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
2524 
2525  X509_free(client_cert);
2526  } else {
2527  debugs(83, 5, "FD " << fd << " has no client certificate.");
2528  }
2529 #else
2530  debugs(83, 2, "Client certificate requesting not yet implemented.");
2531 #endif
2532 
2533  conn->readSomeData();
2534 }
2535 
2540 static void
2542 {
2543  assert(connState);
2544  const Comm::ConnectionPointer &details = connState->clientConnection;
2545 
2546  if (!ctx || !httpsCreate(connState, ctx))
2547  return;
2548 
2550  AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
2551  connState, ConnStateData::requestTimeout);
2552  commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
2553 
2554  Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
2555 }
2556 
2557 #if USE_OPENSSL
2558 
2561 static void
2563 {
2564  ConnStateData *connState = (ConnStateData *) data;
2565 
2566  // if the connection is closed or closing, just return.
2567  if (!connState->isOpen())
2568  return;
2569 
2570  if (answer.allowed()) {
2571  debugs(33, 2, "sslBump action " << Ssl::bumpMode(answer.kind) << "needed for " << connState->clientConnection);
2572  connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
2573  } else {
2574  debugs(33, 3, "sslBump not needed for " << connState->clientConnection);
2575  connState->sslBumpMode = Ssl::bumpSplice;
2576  }
2577 
2578  if (connState->sslBumpMode == Ssl::bumpTerminate) {
2579  connState->clientConnection->close();
2580  return;
2581  }
2582 
2583  if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
2584  connState->clientConnection->close();
2585 }
2586 #endif
2587 
2589 static void
2591 {
2592  MasterXaction::Pointer xact = params.xaction;
2593  const AnyP::PortCfgPointer s = xact->squidPort;
2594 
2595  // NP: it is possible the port was reconfigured when the call or accept() was queued.
2596 
2597  if (params.flag != Comm::OK) {
2598  // Its possible the call was still queued when the client disconnected
2599  debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2600  return;
2601  }
2602 
2603  debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
2604  fd_note(params.conn->fd, "client https connect");
2605 
2606  if (s->tcp_keepalive.enabled) {
2607  commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
2608  }
2610 
2611  // Socket is ready, setup the connection manager to start using it
2612  auto *srv = Https::NewServer(xact);
2613  AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
2614 }
2615 
2616 void
2618 {
2619  if (port->flags.tunnelSslBumping) {
2620 #if USE_OPENSSL
2621  debugs(33, 5, "accept transparent connection: " << clientConnection);
2622 
2623  if (!Config.accessList.ssl_bump) {
2625  return;
2626  }
2627 
2630  // Create a fake HTTP request and ALE for the ssl_bump ACL check,
2631  // using tproxy/intercept provided destination IP and port.
2632  // XXX: Merge with subsequent fakeAConnectRequest(), buildFakeRequest().
2633  // XXX: Do this earlier (e.g., in Http[s]::One::Server constructor).
2634  HttpRequest *request = new HttpRequest(mx);
2635  static char ip[MAX_IPSTRLEN];
2637  request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
2638  request->url.port(clientConnection->local.port());
2639  request->myportname = port->name;
2640  const AccessLogEntry::Pointer connectAle = new AccessLogEntry;
2641  CodeContext::Reset(connectAle);
2642  // TODO: Use these request/ALE when waiting for new bumped transactions.
2643 
2644  ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
2645  acl_checklist->src_addr = clientConnection->remote;
2646  acl_checklist->my_addr = port->s;
2647  // Build a local AccessLogEntry to allow requiresAle() acls work
2648  acl_checklist->al = connectAle;
2649  acl_checklist->al->cache.start_time = current_time;
2650  acl_checklist->al->tcpClient = clientConnection;
2651  acl_checklist->al->cache.port = port;
2652  acl_checklist->al->cache.caddr = log_addr;
2653  acl_checklist->al->proxyProtocolHeader = proxyProtocolHeader_;
2654  HTTPMSGUNLOCK(acl_checklist->al->request);
2655  acl_checklist->al->request = request;
2656  HTTPMSGLOCK(acl_checklist->al->request);
2657  Http::StreamPointer context = pipeline.front();
2658  ClientHttpRequest *http = context ? context->http : nullptr;
2659  const char *log_uri = http ? http->log_uri : nullptr;
2660  acl_checklist->syncAle(request, log_uri);
2661  acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, this);
2662 #else
2663  fatal("FATAL: SSL-Bump requires --with-openssl");
2664 #endif
2665  return;
2666  } else {
2667  httpsEstablish(this, port->secure.staticContext);
2668  }
2669 }
2670 
2671 #if USE_OPENSSL
2672 void
2674 {
2675  ConnStateData * state_data = (ConnStateData *)(data);
2676  state_data->sslCrtdHandleReply(reply);
2677 }
2678 
2679 void
2681 {
2682  if (!isOpen()) {
2683  debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
2684  return;
2685  }
2686 
2687  if (reply.result == Helper::BrokenHelper) {
2688  debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
2689  } else if (!reply.other().hasContent()) {
2690  debugs(1, DBG_IMPORTANT, HERE << "\"ssl_crtd\" helper returned <NULL> reply.");
2691  } else {
2693  if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
2694  debugs(33, 5, "Reply from ssl_crtd for " << tlsConnectHostOrIp << " is incorrect");
2695  } else {
2696  if (reply.result != Helper::Okay) {
2697  debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
2698  } else {
2699  debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " was successfully recieved from ssl_crtd");
2702  auto ssl = fd_table[clientConnection->fd].ssl.get();
2703  bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
2704  if (!ret)
2705  debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2706 
2709  } else {
2711  if (ctx && !sslBumpCertKey.isEmpty())
2713  getSslContextDone(ctx);
2714  }
2715  return;
2716  }
2717  }
2718  }
2720  getSslContextDone(nil);
2721 }
2722 
2724 {
2726 
2727  const bool connectedOk = sslServerBump && sslServerBump->connectedOk();
2728  if (connectedOk) {
2729  if (X509 *mimicCert = sslServerBump->serverCert.get())
2730  certProperties.mimicCert.resetAndLock(mimicCert);
2731 
2735 
2736  for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
2737  // If the algorithm already set, then ignore it.
2738  if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
2739  (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
2740  (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
2741  continue;
2742 
2743  if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
2744  const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
2745  const char *param = ca->param;
2746 
2747  // For parameterless CN adaptation, use hostname from the
2748  // CONNECT request.
2749  if (ca->alg == Ssl::algSetCommonName) {
2750  if (!param)
2751  param = tlsConnectHostOrIp.c_str();
2752  certProperties.commonName = param;
2753  certProperties.setCommonName = true;
2754  } else if (ca->alg == Ssl::algSetValidAfter)
2755  certProperties.setValidAfter = true;
2756  else if (ca->alg == Ssl::algSetValidBefore)
2757  certProperties.setValidBefore = true;
2758 
2759  debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
2760  alg << " param: " << (param ? param : "-"));
2761  }
2762  }
2763 
2764  certProperties.signAlgorithm = Ssl::algSignEnd;
2765  for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
2766  if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
2767  certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
2768  break;
2769  }
2770  }
2771  } else {// did not try to connect (e.g. client-first) or failed to connect
2772  // In case of an error while connecting to the secure server, use a
2773  // trusted certificate, with no mimicked fields and no adaptation
2774  // algorithms. There is nothing we can mimic, so we want to minimize the
2775  // number of warnings the user will have to see to get to the error page.
2776  // We will close the connection, so that the trust is not extended to
2777  // non-Squid content.
2778  certProperties.signAlgorithm = Ssl::algSignTrusted;
2779  }
2780 
2781  assert(certProperties.signAlgorithm != Ssl::algSignEnd);
2782 
2783  if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
2784  assert(port->secure.untrustedSigningCa.cert);
2785  certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCa.cert.get());
2786  certProperties.signWithPkey.resetAndLock(port->secure.untrustedSigningCa.pkey.get());
2787  } else {
2788  assert(port->secure.signingCa.cert.get());
2789  certProperties.signWithX509.resetAndLock(port->secure.signingCa.cert.get());
2790 
2791  if (port->secure.signingCa.pkey)
2792  certProperties.signWithPkey.resetAndLock(port->secure.signingCa.pkey.get());
2793  }
2794  signAlgorithm = certProperties.signAlgorithm;
2795 
2796  certProperties.signHash = Ssl::DefaultSignHash;
2797 }
2798 
2801 {
2802  debugs(33, 5, "Finding SSL certificate for " << cacheKey << " in cache");
2804  if (Security::ContextPointer *ctx = ssl_ctx_cache ? ssl_ctx_cache->get(cacheKey) : nullptr) {
2805  if (Ssl::verifySslCertificate(*ctx, certProperties)) {
2806  debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is valid");
2807  return *ctx;
2808  } else {
2809  debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is out of date. Delete this certificate from cache");
2810  if (ssl_ctx_cache)
2811  ssl_ctx_cache->del(cacheKey);
2812  }
2813  }
2814  return Security::ContextPointer(nullptr);
2815 }
2816 
2817 void
2819 {
2821  if (!ssl_ctx_cache || !ssl_ctx_cache->add(cacheKey, new Security::ContextPointer(ctx))) {
2822  // If it is not in storage delete after using. Else storage deleted it.
2823  fd_table[clientConnection->fd].html?amicTlsContext = ctx;
2824  }
2825 }
2826 
2827 void
2829 {
2830  // If we are called, then CONNECT has succeeded. Finalize it.
2831  if (auto xact = pipeline.front()) {
2832  if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
2833  xact->finished();
2834  // cannot proceed with encryption if requests wait for plain responses
2835  Must(pipeline.empty());
2836  }
2837  /* careful: finished() above frees request, host, etc. */
2838 
2839  if (port->secure.generateHostCertificates) {
2840  Ssl::CertificateProperties certProperties;
2841  buildSslCertGenerationParams(certProperties);
2842 
2843  // Disable caching for bumpPeekAndSplice mode
2846  Ssl::InRamCertificateDbKey(certProperties, sslBumpCertKey);
2848 
2850  if (ctx) {
2851  getSslContextDone(ctx);
2852  return;
2853  }
2854  }
2855 
2856 #if USE_SSL_CRTD
2857  try {
2858  debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
2861  request_message.composeRequest(certProperties);
2862  debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
2863  Ssl::Helper::Submit(request_message, sslCrtdHandleReplyWrapper, this);
2864  return;
2865  } catch (const std::exception &e) {
2866  debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
2867  "request for " << certProperties.commonName <<
2868  " certificate: " << e.what() << "; will now block to " <<
2869  "generate that certificate.");
2870  // fall through to do blocking in-process generation.
2871  }
2872 #endif // USE_SSL_CRTD
2873 
2874  debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
2877  auto ssl = fd_table[clientConnection->fd].ssl.get();
2878  if (!Ssl::configureSSL(ssl, certProperties, *port))
2879  debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2880 
2883  } else {
2885  if (dynCtx && !sslBumpCertKey.isEmpty())
2887  getSslContextDone(dynCtx);
2888  }
2889  return;
2890  }
2891 
2893  getSslContextDone(nil);
2894 }
2895 
2896 void
2898 {
2899  if (port->secure.generateHostCertificates && !ctx) {
2900  debugs(33, 2, "Failed to generate TLS context for " << tlsConnectHostOrIp);
2901  }
2902 
2903  // If generated ssl context = NULL, try to use static ssl context.
2904  if (!ctx) {
2905  if (!port->secure.staticContext) {
2906  debugs(83, DBG_IMPORTANT, "Closing " << clientConnection->remote << " as lacking TLS context");
2908  return;
2909  } else {
2910  debugs(33, 5, "Using static TLS context.");
2911  ctx = port->secure.staticContext;
2912  }
2913  }
2914 
2915  if (!httpsCreate(this, ctx))
2916  return;
2917 
2918  // bumped intercepted conns should already have Config.Timeout.request set
2919  // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
2920  // to make sure the connection does not get stuck on non-SSL clients.
2922  AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
2925 
2926  switchedToHttps_ = true;
2927 
2928  auto ssl = fd_table[clientConnection->fd].ssl.get();
2929  BIO *b = SSL_get_rbio(ssl);
2930  Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2931  bio->setReadBufData(inBuf);
2932  inBuf.clear();
2934 }
2935 
2936 void
2938 {
2940  Must(http->request);
2941  auto &request = http->request;
2942 
2943  // Depending on receivedFirstByte_, we are at the start of either an
2944  // established CONNECT tunnel with the client or an intercepted TCP (and
2945  // presumably TLS) connection from the client. Expect TLS Client Hello.
2946  const auto insideConnectTunnel = receivedFirstByte_;
2947  debugs(33, 5, (insideConnectTunnel ? "post-CONNECT " : "raw TLS ") << clientConnection);
2948 
2949  tlsConnectHostOrIp = request->url.hostOrIp();
2950  tlsConnectPort = request->url.port();
2951  resetSslCommonName(request->url.host());
2952 
2953  // We are going to read new request
2954  flags.readMore = true;
2955 
2956  // keep version major.minor details the same.
2957  // but we are now performing the HTTPS handshake traffic
2959 
2960  // If sslServerBump is set, then we have decided to deny CONNECT
2961  // and now want to switch to SSL to send the error to the client
2962  // without even peeking at the origin server certificate.
2963  if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
2964  request->flags.sslPeek = true;
2965  sslServerBump = new Ssl::ServerBump(http);
2966  } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
2967  request->flags.sslPeek = true;
2968  sslServerBump = new Ssl::ServerBump(http, nullptr, bumpServerMode);
2969  }
2970 
2971  // commSetConnTimeout() was called for this request before we switched.
2972  // Fix timeout to request_start_timeout
2974  AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
2975  TimeoutDialer, this, ConnStateData::requestTimeout);
2977  // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
2978  // a bumbed "connect" request on non transparent port.
2979  receivedFirstByte_ = false;
2980  // Get more data to peek at Tls
2981  parsingTlsHandshake = true;
2982 
2983  // If the protocol has changed, then reset preservingClientData_.
2984  // Otherwise, its value initially set in start() is still valid/fresh.
2985  // shouldPreserveClientData() uses parsingTlsHandshake which is reset above.
2986  if (insideConnectTunnel)
2988 
2989  readSomeData();
2990 }
2991 
2992 void
2994 {
2996 
2997  assert(!inBuf.isEmpty());
2999  fd_note(clientConnection->fd, "Parsing TLS handshake");
3000 
3001  bool unsupportedProtocol = false;
3002  try {
3003  if (!tlsParser.parseHello(inBuf)) {
3004  // need more data to finish parsing
3005  readSomeData();
3006  return;
3007  }
3008  }
3009  catch (const std::exception &ex) {
3010  debugs(83, 2, "error on FD " << clientConnection->fd << ": " << ex.what());
3011  unsupportedProtocol = true;
3012  }
3013 
3014  parsingTlsHandshake = false;
3015 
3016  // client data may be needed for splicing and for
3017  // tunneling unsupportedProtocol after an error
3019 
3020  // Even if the parser failed, each TLS detail should either be set
3021  // correctly or still be "unknown"; copying unknown detail is a no-op.
3024  if (details && !details->serverName.isEmpty()) {
3025  resetSslCommonName(details->serverName.c_str());
3026  tlsClientSni_ = details->serverName;
3027  }
3028 
3029  // We should disable read/write handlers
3031 
3032  if (unsupportedProtocol) {
3033  Http::StreamPointer context = pipeline.front();
3034  Must(context && context->http);
3035  HttpRequest::Pointer request = context->http->request;
3036  debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
3038  context->http->al->ssl.bumpMode = Ssl::bumpSplice;
3039  if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_PROTOCOL_UNKNOWN))
3041  return;
3042  }
3043 
3044  if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
3046  return;
3047  } else if (sslServerBump->act.step1 == Ssl::bumpServerFirst) {
3048  Http::StreamPointer context = pipeline.front();
3049  ClientHttpRequest *http = context ? context->http : nullptr;
3050  // will call httpsPeeked() with certificate and connection, eventually
3052  } else {
3055  }
3056 }
3057 
3059 {
3060  ConnStateData *connState = (ConnStateData *) data;
3061 
3062  // if the connection is closed or closing, just return.
3063  if (!connState->isOpen())
3064  return;
3065 
3066  debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
3067  assert(connState->serverBump());
3068  Ssl::BumpMode bumpAction;
3069  if (answer.allowed()) {
3070  bumpAction = (Ssl::BumpMode)answer.kind;
3071  } else
3072  bumpAction = Ssl::bumpSplice;
3073 
3074  connState->serverBump()->act.step2 = bumpAction;
3075  connState->sslBumpMode = bumpAction;
3076  Http::StreamPointer context = connState->pipeline.front();
3077  if (ClientHttpRequest *http = (context ? context->http : nullptr))
3078  http->al->ssl.bumpMode = bumpAction;
3079 
3080  if (bumpAction == Ssl::bumpTerminate) {
3081  connState->clientConnection->close();
3082  } else if (bumpAction != Ssl::bumpSplice) {
3083  connState->startPeekAndSplice();
3084  } else if (!connState->splice())
3085  connState->clientConnection->close();
3086 }
3087 
3088 bool
3090 {
3091  // normally we can splice here, because we just got client hello message
3092 
3093  // fde::ssl/tls_read_method() probably reads from our own inBuf. If so, then
3094  // we should not lose any raw bytes when switching to raw I/O here.
3095  if (fd_table[clientConnection->fd].ssl.get())
3096  fd_table[clientConnection->fd].useDefaultIo();
3097 
3098  // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
3099  // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
3101  assert(!pipeline.empty());
3102  Http::StreamPointer context = pipeline.front();
3103  Must(context);
3104  Must(context->http);
3105  ClientHttpRequest *http = context->http;
3107  context->finished();
3108  if (transparent()) {
3109  // For transparent connections, make a new fake CONNECT request, now
3110  // with SNI as target. doCallout() checks, adaptations may need that.
3111  return fakeAConnectRequest("splice", preservedClientData);
3112  } else {
3113  // For non transparent connections make a new tunneled CONNECT, which
3114  // also sets the HttpRequest::flags::forceTunnel flag to avoid
3115  // respond with "Connection Established" to the client.
3116  // This fake CONNECT request required to allow use of SNI in
3117  // doCallout() checks and adaptations.
3119  }
3120 }
3121 
3122 void
3124 {
3125  // This is the Step2 of the SSL bumping
3127  Http::StreamPointer context = pipeline.front();
3128  ClientHttpRequest *http = context ? context->http : nullptr;
3129 
3132  // Run a accessList check to check if want to splice or continue bumping
3133 
3135  acl_checklist->al = http ? http->al : nullptr;
3136  //acl_checklist->src_addr = params.conn->remote;
3137  //acl_checklist->my_addr = s->s;
3141  const char *log_uri = http ? http->log_uri : nullptr;
3142  acl_checklist->syncAle(sslServerBump->request.getRaw(), log_uri);
3143  acl_checklist->nonBlockingCheck(httpsSslBumpStep2AccessCheckDone, this);
3144  return;
3145  }
3146 
3147  // will call httpsPeeked() with certificate and connection, eventually
3148  Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCa.cert, port->secure.signingCa.pkey, port->secure));
3149  fd_table[clientConnection->fd].html?amicTlsContext = unConfiguredCTX;
3150 
3151  if (!httpsCreate(this, unConfiguredCTX))
3152  return;
3153 
3154  switchedToHttps_ = true;
3155 
3156  auto ssl = fd_table[clientConnection->fd].ssl.get();
3157  BIO *b = SSL_get_rbio(ssl);
3158  Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3159  bio->setReadBufData(inBuf);
3160  bio->hold(true);
3161 
3162  // Here squid should have all of the client hello message so the
3163  // tlsAttemptHandshake() should return 0.
3164  // This block exist only to force openSSL parse client hello and detect
3165  // ERR_SECURE_ACCEPT_FAIL error, which should be checked and splice if required.
3166  if (tlsAttemptHandshake(this, nullptr) < 0) {
3167  debugs(83, 2, "TLS handshake failed.");
3168  HttpRequest::Pointer request(http ? http->request : nullptr);
3169  if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_SECURE_ACCEPT_FAIL))
3171  return;
3172  }
3173 
3174  // We need to reset inBuf here, to be used by incoming requests in the case
3175  // of SSL bump
3176  inBuf.clear();
3177 
3178  debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
3180 }
3181 
3182 void
3184 {
3185  auto ssl = fd_table[clientConnection->fd].ssl.get();
3186  BIO *b = SSL_get_rbio(ssl);
3187  assert(b);
3188  Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3189 
3190  debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
3191  bio->hold(false);
3192 
3194  switchedToHttps_ = true;
3195 }
3196 
3197 void
3199 {
3200  Must(sslServerBump != NULL);
3201  Must(sslServerBump->request == pic.request);
3202  Must(pipeline.empty() || pipeline.front()->http == nullptr || pipeline.front()->http->request == pic.request.getRaw());
3203 
3204  if (Comm::IsConnOpen(pic.connection)) {
3206  debugs(33, 5, "bumped HTTPS server: " << tlsConnectHostOrIp);
3207  } else
3208  debugs(33, 5, "Error while bumping: " << tlsConnectHostOrIp);
3209 
3211 }
3212 
3213 #endif /* USE_OPENSSL */
3214 
3215 bool
3216 ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload)
3217 {
3218  // fake a CONNECT request to force connState to tunnel
3219  SBuf connectHost;
3220  unsigned short connectPort = 0;
3221 
3222  if (pinning.serverConnection != nullptr) {
3223  static char ip[MAX_IPSTRLEN];
3224  connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
3225  connectPort = pinning.serverConnection->remote.port();
3226  } else if (cause) {
3227  connectHost = cause->url.hostOrIp();
3228  connectPort = cause->url.port();
3229 #if USE_OPENSSL
3230  } else if (!tlsConnectHostOrIp.isEmpty()) {
3231  connectHost = tlsConnectHostOrIp;
3232  connectPort = tlsConnectPort;
3233 #endif
3234  } else if (transparent()) {
3235  static char ip[MAX_IPSTRLEN];
3236  connectHost = clientConnection->local.toStr(ip, sizeof(ip));
3237  connectPort = clientConnection->local.port();
3238  } else {
3239  debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
3240  return false;
3241  }
3242 
3243  debugs(33, 2, "Request tunneling for " << reason);
3244  ClientHttpRequest *http = buildFakeRequest(method, connectHost, connectPort, payload);
3246  request->flags.forceTunnel = true;
3247  http->calloutContext = new ClientRequestContext(http);
3248  http->doCallouts();
3249  clientProcessRequestFinished(this, request);
3250  return true;
3251 }
3252 
3253 bool
3254 ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
3255 {
3256  debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
3257 
3258  SBuf connectHost;
3259  assert(transparent());
3260  const unsigned short connectPort = clientConnection->local.port();
3261 
3262 #if USE_OPENSSL
3263  if (!tlsClientSni_.isEmpty())
3264  connectHost.assign(tlsClientSni_);
3265  else
3266 #endif
3267  {
3268  static char ip[MAX_IPSTRLEN];
3269  clientConnection->local.toHostStr(ip, sizeof(ip));
3270  connectHost.assign(ip);
3271  }
3272 
3273  ClientHttpRequest *http = buildFakeRequest(Http::METHOD_CONNECT, connectHost, connectPort, payload);
3274 
3275  http->calloutContext = new ClientRequestContext(http);
3277  http->doCallouts();
3278  clientProcessRequestFinished(this, request);
3279  return true;
3280 }
3281 
3283 ConnStateData::buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload)
3284 {
3285  ClientHttpRequest *http = new ClientHttpRequest(this);
3286  Http::Stream *stream = new Http::Stream(clientConnection, http);
3287 
3288  StoreIOBuffer tempBuffer;
3289  tempBuffer.data = stream->reqbuf;
3290  tempBuffer.length = HTTP_REQBUF_SZ;
3291 
3292  ClientStreamData newServer = new clientReplyContext(http);
3293  ClientStreamData newClient = stream;
3296  clientSocketDetach, newClient, tempBuffer);
3297 
3298  stream->flags.parsed_ok = 1; // Do we need it?
3299  stream->mayUseConnection(true);
3300 
3301  AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
3304 
3305  stream->registerWithConn();
3306 
3309  // Setup Http::Request object. Maybe should be replaced by a call to (modified)
3310  // clientProcessRequest
3313  request->url.setScheme(proto, nullptr);
3314  request->method = method;
3315  request->url.host(useHost.c_str());
3316  request->url.port(usePort);
3317 
3318  http->uri = SBufToCstring(request->effectiveRequestUri());
3319  http->initRequest(request.getRaw());
3320 
3321  request->manager(this, http->al);
3322 
3323  if (proto == AnyP::PROTO_HTTP)
3324  request->header.putStr(Http::HOST, useHost.c_str());
3325 
3326  request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
3327 #if USE_AUTH
3328  if (getAuth())
3329  request->auth_user_request = getAuth();
3330 #endif
3331 
3332  inBuf = payload;
3333  flags.readMore = false;
3334 
3335  return http;
3336 }
3337 
3339 static bool
3341 {
3342  if (!Comm::IsConnOpen(c)) {
3343  Must(NHttpSockets > 0); // we tried to open some
3344  --NHttpSockets; // there will be fewer sockets than planned
3345  Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3346 
3347  if (!NHttpSockets) // we could not open any listen sockets at all
3348  fatalf("Unable to open %s",FdNote(portType));
3349 
3350  return false;
3351  }
3352  return true;
3353 }
3354 
3356 static bool
3358 {
3359  bool found = false;
3360  for (int i = 0; i < NHttpSockets && !found; ++i) {
3361  if ((found = HttpSockets[i] < 0))
3362  HttpSockets[i] = conn->fd;
3363  }
3364  return found;
3365 }
3366 
3367 static void
3369 {
3370  for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
3371  const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
3372 
3374  debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines." <<
3375  Debug::Extra << "The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
3376  continue;
3377  }
3378 
3379 #if USE_OPENSSL
3380  if (s->flags.tunnelSslBumping) {
3381  if (!Config.accessList.ssl_bump) {
3382  debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << scheme << "_port " << s->s);
3383  s->flags.tunnelSslBumping = false;
3384  }
3385  if (!s->secure.staticContext && !s->secure.generateHostCertificates) {
3386  debugs(1, DBG_IMPORTANT, "Will not bump SSL at " << scheme << "_port " << s->s << " due to TLS initialization failure.");
3387  s->flags.tunnelSslBumping = false;
3388  if (s->transport.protocol == AnyP::PROTO_HTTP)
3389  s->secure.encryptTransport = false;
3390  }
3391  if (s->flags.tunnelSslBumping) {
3392  // Create ssl_ctx cache for this port.
3393  Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->secure.html?amicCertMemCacheSize);
3394  }
3395  }
3396 #endif
3397 
3398  if (s->secure.encryptTransport && !s->secure.staticContext) {
3399  debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
3400  continue;
3401  }
3402 
3403  // Fill out a Comm::Connection which IPC will open as a listener for us
3404  // then pass back when active so we can start a TcpAcceptor subscription.
3405  s->listenConn = new Comm::Connection;
3406  s->listenConn->local = s->s;
3407 
3408  s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
3409  (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
3410 
3411  typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3412  if (s->transport.protocol == AnyP::PROTO_HTTP) {
3413  // setup the subscriptions such that new connections accepted by listenConn are handled by HTTP
3416 
3417  AsyncCall::Pointer listenCall = asyncCall(33,2, "clientListenerConnectionOpened",
3419  Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
3420 
3421  } else if (s->transport.protocol == AnyP::PROTO_HTTPS) {
3422  // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
3425 
3426  AsyncCall::Pointer listenCall = asyncCall(33, 2, "clientListenerConnectionOpened",
3428  s, Ipc::fdnHttpsSocket, sub));
3429  Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
3430  }
3431 
3432  HttpSockets[NHttpSockets] = -1; // set in clientListenerConnectionOpened
3433  ++NHttpSockets;
3434  }
3435 }
3436 
3437 void
3439 {
3440  // Fill out a Comm::Connection which IPC will open as a listener for us
3441  port->listenConn = new Comm::Connection;
3442  port->listenConn->local = port->s;
3443  port->listenConn->flags =
3445  (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
3446  (port->flags.natIntercept ? COMM_INTERCEPTION : 0);
3447 
3448  // route new connections to subCall
3449  typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3451  AsyncCall::Pointer listenCall =
3452  asyncCall(33, 2, "clientListenerConnectionOpened",
3454  port, fdNote, sub));
3455  Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, listenCall);
3456 
3458  HttpSockets[NHttpSockets] = -1;
3459  ++NHttpSockets;
3460 }
3461 
3463 static void
3465 {
3466  Must(s != NULL);
3467 
3468  if (!OpenedHttpSocket(s->listenConn, portTypeNote))
3469  return;
3470 
3471  Must(Comm::IsConnOpen(s->listenConn));
3472 
3473  // TCP: setup a job to handle accept() with subscribed handler
3474  AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
3475 
3476  debugs(1, DBG_IMPORTANT, "Accepting " <<
3477  (s->flags.natIntercept ? "NAT intercepted " : "") <<
3478  (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
3479  (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
3480  (s->flags.accelSurrogate ? "reverse-proxy " : "")
3481  << FdNote(portTypeNote) << " connections at "
3482  << s->listenConn);
3483 
3484  Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
3485 }
3486 
3487 void
3489 {
3492 
3493  if (NHttpSockets < 1)
3494  fatal("No HTTP, HTTPS, or FTP ports configured");
3495 }
3496 
3497 void
3499 {
3500  for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
3501  if (s->listenConn != NULL) {
3502  debugs(1, DBG_IMPORTANT, "Closing HTTP(S) port " << s->listenConn->local);
3503  s->listenConn->close();
3504  s->listenConn = NULL;
3505  }
3506  }
3507 
3509 
3510  // TODO see if we can drop HttpSockets array entirely */
3511  for (int i = 0; i < NHttpSockets; ++i) {
3512  HttpSockets[i] = -1;
3513  }
3514 
3515  NHttpSockets = 0;
3516 }
3517 
3518 int
3520 {
3521  SBuf vary(request->vary_headers);
3522  const auto &reply = entry->mem().freshestReply();
3523  auto has_vary = reply.header.has(Http::HdrType::VARY);
3524 #if X_ACCELERATOR_VARY
3525 
3526  has_vary |=
3527  reply.header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
3528 #endif
3529 
3530  if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
3531  if (!vary.isEmpty()) {
3532  /* Oops... something odd is going on here.. */
3533  debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3534  entry->mem_obj->urlXXX() << "' '" << vary << "'");
3535  request->vary_headers.clear();
3536  return VARY_CANCEL;
3537  }
3538 
3539  if (!has_vary) {
3540  /* This is not a varying object */
3541  return VARY_NONE;
3542  }
3543 
3544  /* virtual "vary" object found. Calculate the vary key and
3545  * continue the search
3546  */
3547  vary = httpMakeVaryMark(request, &reply);
3548 
3549  if (!vary.isEmpty()) {
3550  request->vary_headers = vary;
3551  return VARY_OTHER;
3552  } else {
3553  /* Ouch.. we cannot handle this kind of variance */
3554  /* XXX This cannot really happen, but just to be complete */
3555  return VARY_CANCEL;
3556  }
3557  } else {
3558  if (vary.isEmpty()) {
3559  vary = httpMakeVaryMark(request, &reply);
3560 
3561  if (!vary.isEmpty())
3562  request->vary_headers = vary;
3563  }
3564 
3565  if (vary.isEmpty()) {
3566  /* Ouch.. we cannot handle this kind of variance */
3567  /* XXX This cannot really happen, but just to be complete */
3568  return VARY_CANCEL;
3569  } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
3570  return VARY_MATCH;
3571  } else {
3572  /* Oops.. we have already been here and still haven't
3573  * found the requested variant. Bail out
3574  */
3575  debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3576  entry->mem_obj->urlXXX() << "' '" << vary << "'");
3577  return VARY_CANCEL;
3578  }
3579  }
3580 }
3581 
3584 {
3585  const auto checklist = new ACLFilledChecklist(acl, nullptr, nullptr);
3586  clientAclChecklistFill(*checklist, http);
3587  return checklist;
3588 }
3589 
3590 void
3592 {
3593  checklist.setRequest(http->request);
3594  checklist.al = http->al;
3595  checklist.syncAle(http->request, http->log_uri);
3596 
3597  // TODO: If http->getConn is always http->request->clientConnectionManager,
3598  // then call setIdent() inside checklist.setRequest(). Otherwise, restore
3599  // USE_IDENT lost in commit 94439e4.
3600  ConnStateData * conn = http->getConn();
3601  const char *ident = (cbdataReferenceValid(conn) &&
3602  conn && conn->clientConnection) ?
3603  conn->clientConnection->rfc931 : dash_str;
3604  checklist.setIdent(ident);
3605 }
3606 
3607 bool
3609 {
3611 }
3612 
3615 {
3616  bodyPipe = new BodyPipe(this);
3617  if (size >= 0)
3618  bodyPipe->setBodySize(size);
3619  else
3621  return bodyPipe;
3622 }
3623 
3624 int64_t
3626 {
3627  if (!bodyPipe)
3628  return 0; // request without a body or read/produced all body bytes
3629 
3630  if (!bodyPipe->bodySizeKnown())
3631  return -1; // probably need to read more, but we cannot be sure
3632 
3633  const int64_t needToProduce = bodyPipe->unproducedSize();
3634  const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
3635 
3636  if (needToProduce <= haveAvailable)
3637  return 0; // we have read what we need (but are waiting for pipe space)
3638 
3639  return needToProduce - haveAvailable;
3640 }
3641 
3642 void
3644 {
3645  debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " << error <<
3646  "; old sending error: " <<
3647  (stoppedSending() ? stoppedSending_ : "none"));
3648 
3649  if (const char *oldError = stoppedReceiving()) {
3650  debugs(33, 3, HERE << "already stopped receiving: " << oldError);
3651  return; // nothing has changed as far as this connection is concerned
3652  }
3653 
3655 
3656  if (const char *sendError = stoppedSending()) {
3657  debugs(33, 3, HERE << "closing because also stopped sending: " << sendError);
3659  }
3660 }
3661 
3662 void
3664 {
3665  if (bodyPipe != NULL) {
3666  debugs(33, 4, HERE << "no consumer for virgin body " << bodyPipe->status());
3668  }
3669 }
3670 
3672 void
3674 {
3675  Must(bodyPipe != NULL);
3676  debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
3677  assert(!bodyParser);
3679 }
3680 
3682 void
3684 {
3685  debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
3686 
3687  if (bodyPipe != NULL) {
3688  debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
3689  BodyPipe::Pointer myPipe = bodyPipe;
3690  stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3691  Must(!bodyPipe); // we rely on it being nil after we are done with body
3692  if (withSuccess) {
3693  Must(myPipe->bodySizeKnown());
3694  Http::StreamPointer context = pipeline.front();
3695  if (context != NULL && context->http && context->http->request)
3696  context->http->request->setContentLength(myPipe->bodySize());
3697  }
3698  }
3699 
3700  delete bodyParser;
3701  bodyParser = NULL;
3702 }
3703 
3704 // XXX: this is an HTTP/1-only operation
3705 void
3707 {
3708  if (const auto context = pipeline.front()) {
3709  if (context->http)
3710  context->http->al->reply = msg.reply;
3711  }
3712 
3713  if (!isOpen()) {
3714  debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
3715  return;
3716  }
3717 
3718  // HTTP/1 1xx status messages are only valid when there is a transaction to trigger them
3719  if (!pipeline.empty()) {
3720  HttpReply::Pointer rep(msg.reply);
3721  Must(rep);
3722  // remember the callback
3724 
3727 
3728  if (!writeControlMsgAndCall(rep.getRaw(), call)) {
3729  // but still inform the caller (so it may resume its operation)
3731  }
3732  return;
3733  }
3734 
3735  debugs(33, 3, HERE << " closing due to missing context for 1xx");
3737 }
3738 
3739 void
3741 {
3743 
3744  if (Http::StreamPointer deferredRequest = pipeline.front()) {
3745  debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
3746  ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
3747  }
3748 }
3749 
3751 void
3753 {
3754  // FwdState might repin a failed connection sooner than this close
3755  // callback is called for the failed connection.
3756  assert(pinning.serverConnection == io.conn);
3757  pinning.closeHandler = NULL; // Comm unregisters handlers before calling
3758  const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
3759  pinning.serverConnection->noteClosure();
3760  unpinConnection(false);
3761 
3762  if (sawZeroReply && clientConnection != NULL) {
3763  debugs(33, 3, "Closing client connection on pinned zero reply.");
3765  }
3766 
3767 }
3768 
3769 void
3771 {
3772  pinConnection(pinServer, *request);
3773 }
3774 
3775 void
3777 {
3778  Must(pic.connection);
3779  Must(pic.request);
3780  pinConnection(pic.connection, *pic.request);
3781 
3782  // monitor pinned server connection for remote-end closures.
3784 
3785  if (pipeline.empty())
3786  kick(); // in case clientParseRequests() was blocked by a busy pic.connection
3787 }
3788 
3790 void
3792 {
3793  if (Comm::IsConnOpen(pinning.serverConnection) &&
3794  pinning.serverConnection->fd == pinServer->fd) {
3795  debugs(33, 3, "already pinned" << pinServer);
3796  return;
3797  }
3798 
3799  unpinConnection(true); // closes pinned connection, if any, and resets fields
3800 
3801  pinning.serverConnection = pinServer;
3802 
3803  debugs(33, 3, HERE << pinning.serverConnection);
3804 
3805  Must(pinning.serverConnection != NULL);
3806 
3807  const char *pinnedHost = "[unknown]";
3808  pinning.host = xstrdup(request.url.host());
3809  pinning.port = request.url.port();
3810  pinnedHost = pinning.host;
3811  pinning.pinned = true;
3812  if (CachePeer *aPeer = pinServer->getPeer())
3813  pinning.peer = cbdataReference(aPeer);
3814  pinning.auth = request.flags.connectionAuth;
3815  char stmp[MAX_IPSTRLEN];
3816  char desc[FD_DESC_SZ];
3817  snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
3818  (pinning.auth || !pinning.peer) ? pinnedHost : pinning.peer->name,
3820  clientConnection->fd);
3821  fd_note(pinning.serverConnection->fd, desc);
3822 
3824  pinning.closeHandler = JobCallback(33, 5,
3826  // remember the pinned connection so that cb does not unpin a fresher one
3827  typedef CommCloseCbParams Params;
3828  Params &params = GetCommParams<Params>(pinning.closeHandler);
3829  params.conn = pinning.serverConnection;
3830  comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3831 }
3832 
3835 void
3837 {
3838  if (pinning.readHandler != NULL)
3839  return; // already monitoring
3840 
3842  pinning.readHandler = JobCallback(33, 3,
3844  Comm::Read(pinning.serverConnection, pinning.readHandler);
3845 }
3846 
3847 void
3849 {
3850  if (pinning.readHandler != NULL) {
3851  Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
3852  pinning.readHandler = NULL;
3853  }
3854 }
3855 
3856 #if USE_OPENSSL
3857 bool
3859 {
3860  // A ready-for-reading connection means that the TLS server either closed
3861  // the connection, sent us some unexpected HTTP data, or started TLS
3862  // renegotiations. We should close the connection except for the last case.
3863 
3864  Must(pinning.serverConnection != nullptr);
3865  auto ssl = fd_table[pinning.serverConnection->fd].ssl.get();
3866  if (!ssl)
3867  return false;
3868 
3869  char buf[1];
3870  const int readResult = SSL_read(ssl, buf, sizeof(buf));
3871 
3872  if (readResult > 0 || SSL_pending(ssl) > 0) {
3873  debugs(83, 2, pinning.serverConnection << " TLS application data read");
3874  return false;
3875  }
3876 
3877  switch(const int error = SSL_get_error(ssl, readResult)) {
3878  case SSL_ERROR_WANT_WRITE:
3879  debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
3880  // fall through to restart monitoring, for now
3881  case SSL_ERROR_NONE:
3882  case SSL_ERROR_WANT_READ:
3884  return true;
3885 
3886  default:
3887  debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
3888  return false;
3889  }
3890 
3891  // not reached
3892  return true;
3893 }
3894 #endif
3895 
3898 void
3900 {
3901  pinning.readHandler = NULL; // Comm unregisters handlers before calling
3902 
3903  if (io.flag == Comm::ERR_CLOSING)
3904  return; // close handler will clean up
3905 
3906  Must(pinning.serverConnection == io.conn);
3907 
3908 #if USE_OPENSSL
3910  return;
3911 #endif
3912 
3913  const bool clientIsIdle = pipeline.empty();
3914 
3915  debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
3916  io.size << (clientIsIdle ? " with idle client" : ""));
3917 
3918  pinning.serverConnection->close();
3919 
3920  // If we are still sending data to the client, do not close now. When we are done sending,
3921  // ConnStateData::kick() checks pinning.serverConnection and will close.
3922  // However, if we are idle, then we must close to inform the idle client and minimize races.
3923  if (clientIsIdle && clientConnection != NULL)
3925 }
3926 
3929 {
3930  debugs(33, 7, pinning.serverConnection);
3931  Must(request);
3932 
3933  const auto pinningError = [&](const err_type type) {
3934  unpinConnection(true);
3935  HttpRequestPointer requestPointer = request;
3936  return ErrorState::NewForwarding(type, requestPointer, ale);
3937  };
3938 
3939  if (!Comm::IsConnOpen(pinning.serverConnection))
3940  throw pinningError(ERR_ZERO_SIZE_OBJECT);
3941 
3942  if (pinning.auth && pinning.host && strcasecmp(pinning.host, request->url.host()) != 0)
3943  throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3944 
3945  if (pinning.port != request->url.port())
3946  throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3947 
3948  if (pinning.peer && !cbdataReferenceValid(pinning.peer))
3949  throw pinningError(ERR_ZERO_SIZE_OBJECT);
3950 
3951  if (pinning.peerAccessDenied)
3952  throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_FORWARDING_DENIED
3953 
3955  return pinning.serverConnection;
3956 }
3957 
3960 {
3961  if (const auto connManager = request ? request->pinnedConnection() : nullptr)
3962  return connManager->borrowPinnedConnection(request, ale);
3963 
3964  // ERR_CANNOT_FORWARD is somewhat misleading here; we can still forward, but
3965  // there is no point since the client connection is now gone
3966  HttpRequestPointer requestPointer = request;
3967  throw ErrorState::NewForwarding(ERR_CANNOT_FORWARD, requestPointer, ale);
3968 }
3969 
3970 void
3972 {
3973  debugs(33, 3, HERE << pinning.serverConnection);
3974 
3975  if (pinning.peer)
3977 
3978  if (Comm::IsConnOpen(pinning.serverConnection)) {
3979  if (pinning.closeHandler != NULL) {
3980  comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3981  pinning.closeHandler = NULL;
3982  }
3983 
3985 
3986  // close the server side socket if requested
3987  if (andClose)
3988  pinning.serverConnection->close();
3989  pinning.serverConnection = NULL;
3990  }
3991 
3992  safe_free(pinning.host);
3993 
3994  pinning.zeroReply = false;
3995  pinning.peerAccessDenied = false;
3996 
3997  /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
3998  * connection has gone away */
3999 }
4000 
4001 void
4003 {
4004  // if we are parsing request body, its request is responsible for logging
4005  if (bodyPipe)
4006  return;
4007 
4008  // a request currently using this connection is responsible for logging
4009  if (!pipeline.empty() && pipeline.back()->mayUseConnection())
4010  return;
4011 
4012  /* Either we are waiting for the very first transaction, or
4013  * we are done with the Nth transaction and are waiting for N+1st.
4014  * XXX: We assume that if anything was added to inBuf, then it could
4015  * only be consumed by actions already covered by the above checks.
4016  */
4017 
4018  // do not log connections that closed after a transaction (it is normal)
4019  // TODO: access_log needs ACLs to match received-no-bytes connections
4020  if (pipeline.nrequests && inBuf.isEmpty())
4021  return;
4022 
4023  /* Create a temporary ClientHttpRequest object. Its destructor will log. */
4024  ClientHttpRequest http(this);
4025  http.req_sz = inBuf.length();
4026  // XXX: Or we died while waiting for the pinned connection to become idle.
4027  http.setErrorUri("error:transaction-end-before-headers");
4028 }
4029 
4030 bool
4032 {
4033  // PROXY protocol bytes are meant for us and, hence, cannot be tunneled
4035  return false;
4036 
4037  // If our decision here is negative, configuration changes are irrelevant.
4038  // Otherwise, clientTunnelOnError() rechecks configuration before tunneling.
4040  return false;
4041 
4042  // TODO: Figure out whether/how we can support FTP tunneling.
4043  if (port->transport.protocol == AnyP::PROTO_FTP)
4044  return false;
4045 
4046 #if USE_OPENSSL
4047  if (parsingTlsHandshake)
4048  return true;
4049 
4050  // the 1st HTTP request on a bumped connection
4052  return true;
4053 #endif
4054 
4055  // the 1st HTTP request on a connection to a plain intercepting port
4056  if (!pipeline.nrequests && !port->secure.encryptTransport && transparent())
4057  return true;
4058 
4059  return false;
4060 }
4061 
4064 {
4065  if (!theNotes)
4066  theNotes = new NotePairs;
4067  return theNotes;
4068 }
4069 
4070 std::ostream &
4071 operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
4072 {
4073  return os << pic.connection << ", request=" << pic.request;
4074 }
4075 
sslproxy_cert_adapt * cert_adapt
Definition: SquidConfig.h:518
static void clientHttpConnectionsOpen(void)
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:404
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:238
int internalHostnameIs(const char *arg)
Definition: internal.cc:156
const Security::CertErrors * sslErrors() const
SSL [certificate validation] errors.
Definition: ServerBump.cc:69
bool setCommonName
Replace the CN field of the mimicing subject with the given.
Definition: gadgets.h:221
HierarchyLogEntry hier
Definition: HttpRequest.h:157
static Comm::ConnectionPointer BorrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &)
bool splice()
Splice a bumped client connection on peek-and-splice mode.
virtual ~ConnStateData()
Definition: client_side.cc:617
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
char * skipLeadingSpace(char *aString)
void clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
#define debug_log
change-avoidance macro; new code should call DebugStream() instead
Definition: Debug.h:114
#define HTTP_REQBUF_SZ
Definition: defines.h:216
static bool clientPingHasFinished(ping_data const *aPing)
Definition: client_side.cc:256
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
const char * status() const
Definition: BodyPipe.cc:445
IdentConfig TheConfig
Definition: Ident.cc:73
#define SQUIDSTRINGPH
Definition: SquidString.h:21
const char * FdNote(int fdNodeId)
converts FdNoteId into a string
Definition: FdNotes.cc:16
err_type errType
Definition: HttpRequest.h:161
void count(double val)
Definition: StatHist.cc:57
static CTCB clientLifetimeTimeout
Definition: client_side.cc:174
void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause)
Definition: client_side.cc:506
#define fd_table
Definition: fde.h:182
Ssl::ServerBump * serverBump()
Definition: client_side.h:250
bool setValidAfter
Do not mimic "Not Valid After" field.
Definition: gadgets.h:219
ConnStateData * pinnedConnection()
Definition: HttpRequest.cc:686
int timeout
Definition: PingData.h:24
NotePairs::Pointer theNotes
Definition: client_side.h:439
#define COMM_SELECT_READ
Definition: defines.h:36
void StartListening(int sock_type, int proto, const Comm::ConnectionPointer &listenConn, FdNoteId fdNote, AsyncCall::Pointer &callback)
void(* Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub)
Definition: client_side.cc:150
unsigned short port() const
Definition: Address.cc:778
StatCounters statCounter
Definition: StatCounters.cc:12
#define assert(EX)
Definition: assert.h:17
SBuf & authority(bool requirePort=false) const
Definition: Uri.cc:528
bool isOpen(const int fd)
Definition: comm.cc:86
void const char HLPCB * callback
Definition: stub_helper.cc:16
#define SQUIDSTRINGPRINT(s)
Definition: SquidString.h:22
void clientStreamInit(dlink_list *list, CSR *func, CSD *rdetach, CSS *readstatus, ClientStreamData readdata, CSCB *callback, CSD *cdetach, ClientStreamData callbackdata, StoreIOBuffer tailBuffer)
time_t request
Definition: SquidConfig.h:112
bool setValidBefore
Do not mimic "Not Valid Before" field.
Definition: gadgets.h:220
static int tlsAttemptHandshake(ConnStateData *conn, PF *callback)
SBuf & assign(const SBuf &S)
Definition: SBuf.cc:83
bool timedout
_TIMEDOUT: terminated due to a lifetime or I/O timeout
Definition: LogTags.h:73
bool verifySslCertificate(Security::ContextPointer &, CertificateProperties const &)
Definition: support.cc:872
const char * stoppedReceiving() const
true if we stopped receiving the request
Definition: client_side.h:152
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
char reqbuf[HTTP_REQBUF_SZ]
Definition: Stream.h:134
Ssl::BumpMode step1
The SSL bump mode at step1.
Definition: ServerBump.h:59
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:216
SBuf image() const
Definition: UriScheme.h:50
https_port or bumped http_port tunnel; HTTPS server
Definition: Message.h:33
Security::NegotiationHistory * tlsNegotiations()
Definition: Connection.cc:132
ConnStateData * NewServer(MasterXactionPointer &xact)
create a new HTTP connection handler; never returns NULL
Definition: Http1Server.cc:358
bool internalCheck(const SBuf &urlPath)
Definition: internal.cc:69
std::vector< HttpHdrRangeSpec * >::iterator iterator
int errDetail
errType-specific detail about the transaction error
Definition: HttpRequest.h:162
void parseTlsHandshake()
void fd_note(int fd, const char *s)
Definition: fd.cc:246
Definition: Server.h:28
err_type handleChunkedRequestBody()
parses available chunked encoded body bytes, checks size, returns errors
void setReplyToError(err_type, Http::StatusCode, const HttpRequestMethod &, char const *, Ip::Address &, HttpRequest *, const char *, Auth::UserRequest::Pointer)
builds error using clientBuildError() and calls setReplyToError() below
bool persistent() const
Definition: Message.cc:261
bool conflictingContentLength() const
Definition: HttpHeader.h:113
void CTCB(const CommTimeoutCbParams &params)
Definition: CommCalls.h:39
SBuf vary_headers
The variant second-stage cache key. Generated from Vary header pattern for this request.
Definition: HttpRequest.h:171
ConnStateData(const MasterXactionPointer &xact)
void accessLogLog(AccessLogEntry::Pointer &, ACLChecklist *checklist)
Definition: access_log.cc:144
static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time)
Definition: client_side.cc:214
static char * prepareAcceleratedURL(ConnStateData *conn, const Http1::RequestParserPointer &hp)
int type
Definition: errorpage.cc:152
HttpRequest::Pointer request
to-server request that initiated serverConnection
Definition: client_side.h:176
bool needProxyProtocolHeader_
whether PROXY protocol header is still expected
Definition: client_side.h:402
int NHttpSockets
Definition: PortCfg.cc:24
bool multipartRangeRequest() const
Definition: client_side.cc:691
virtual void afterClientRead()
processing to be done after a Comm::Read()
void resetSslCommonName(const char *name)
Definition: client_side.h:258
void path(const char *p)
Definition: Uri.h:97
Definition: SBuf.h:86
bool fakeAConnectRequest(const char *reason, const SBuf &payload)
void configureUnconfiguredSslContext(Security::ContextPointer &, Ssl::CertSignAlgorithm signAlgorithm, AnyP::PortCfg &)
Definition: support.cc:822
void startPinnedConnectionMonitoring()
virtual void swanSong()
Definition: Server.cc:47
SQUIDCEXTERN CSD clientReplyDetach
Definition: client_side.h:465
ClientHttpRequest * buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload)
build a fake http request
Subscription::Pointer sub
The handler to be subscribed for this connetion listener.
Definition: client_side.cc:168
virtual bool canDial(AsyncCall &) const
Definition: client_side.cc:159
#define xcalloc
Definition: membanger.c:57
static void httpsAccept(const CommAcceptCbParams &params)
ByteCounter hit_kbytes_out
Definition: StatCounters.h:47
Ip::Address src_addr
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:831
HttpRequestMethod method
Definition: HttpRequest.h:114
virtual bool connFinishedWithConn(int size)
??
Ssl::ServerBump * sslServerBump
HTTPS server cert. fetching state for bump-ssl-server-first.
Definition: client_side.h:428
void setErrorUri(const char *errorUri)
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void error(char *format,...)
int i
Definition: membanger.c:49
SBuf sslBumpCertKey
Key to use to store/retrieve generated certificate.
Definition: client_side.h:425
#define xstrdup
SBuf & append(const SBuf &S)
Definition: SBuf.cc:195
SBuf preservedClientData
Definition: client_side.h:314
virtual void dial(AsyncCall &)
Definition: client_side.cc:160
#define SQUID_X509_V_ERR_DOMAIN_MISMATCH
Definition: support.h:44
ConnStateData * getConn() const
T * get() const
Returns raw and possibly nullptr pointer.
ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub)
Definition: client_side.cc:151
int HttpSockets[MAXTCPLISTENPORTS]
Definition: PortCfg.cc:25
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition: client_db.cc:183
struct timeval start_time
The time the master transaction started.
#define safe_free(x)
Definition: xalloc.h:73
void stopPinnedConnectionMonitoring()
The caller assumes responsibility for connection closure detection.
const char * bumpMode(int bm)
Definition: support.h:147
bool CreateServerSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, Security::PeerOptions &, const char *squidCtx)
Definition: Session.cc:194
Definition: Flag.h:16
Adaptation::History::Pointer adaptLogHistory() const
Returns possibly nil history, creating it if adapt. logging is enabled.
Definition: HttpRequest.cc:418
void banAction(const Acl::Answer &action)
add action to the list of banned actions
Definition: Checklist.cc:402
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
void StopListening()
reject new connections to any configured ftp_port
Definition: FtpServer.cc:285
virtual void swanSong()
Definition: client_side.cc:587
http_port or HTTP server
Definition: Message.h:39
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:160
bool bodySizeKnown() const
Definition: BodyPipe.h:109
void clientdbUpdate(const Ip::Address &addr, const LogTags &ltype, AnyP::ProtocolType p, size_t size)
Definition: client_db.cc:139
void registerWithConn()
register this stream with the Server
Definition: Stream.cc:52
Ssl::BumpMode step2
The SSL bump mode at step2.
Definition: ServerBump.h:60
std::string commonName
A CN to use for the generated certificate.
Definition: gadgets.h:222
void clear()
Definition: SBuf.cc:178
void clientStreamAbort(clientStreamNode *thisObject, ClientHttpRequest *http)
CachePeer * getPeer() const
Definition: Connection.cc:100
void doPeekAndSpliceStep()
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
bool isEmpty() const
Definition: SBuf.h:420
char * prepareTlsSwitchingURL(const Http1::RequestParserPointer &hp)
#define COMM_INTERCEPTION
Definition: Connection.h:51
Comm::ConnectionPointer tcpClient
TCP/IP level details about the client connection.
bool SessionIsResumed(const Security::SessionPointer &)
whether the session is a resumed one
Definition: Session.cc:213
const char * CertAdaptAlgorithmStr[]
Definition: gadgets.cc:202
AnyP::ProtocolVersion http_ver
Definition: Message.h:73
#define xisspace(x)
Definition: xis.h:17
int64_t mayNeedToReadMoreBody() const
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition: LogTags.cc:91
#define DBG_CRITICAL
Definition: Debug.h:45
bool at(const BumpStep stp) const
whether we are currently performing the given processing step
Definition: ServerBump.h:47
struct timeval stop
Definition: PingData.h:20
int conn
the current server connection FD
Definition: Transport.cc:26
void setBodySize(uint64_t aSize)
Definition: BodyPipe.cc:147
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:21
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:741
void expectNoConsumption()
there will be no more setConsumer() calls
Definition: BodyPipe.cc:267
void applyClientMask(const Address &mask)
Definition: Address.cc:105
uint64_t unproducedSize() const
Definition: BodyPipe.cc:179
void notePinnedConnectionBecameIdle(PinnedIdleContext pic)
Called when a pinned connection becomes available for forwarding the next request.
void connStateClosed(const CommCloseCbParams &io)
Definition: client_side.cc:499
static const CharacterSet ALPHA
Definition: CharacterSet.h:73
static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
process clientHttpConnectionsOpen result
bool isOpen() const
Definition: client_side.cc:610
Ssl::CertSignAlgorithm signAlgorithm
The signing algorithm to use.
Definition: client_side.h:429
Ip::Address log_addr
Definition: client_side.h:129
struct timeval current_time
Definition: stub_time.cc:15
void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &)
Definition: client_side.cc:324
void clientAclChecklistFill(ACLFilledChecklist &checklist, ClientHttpRequest *http)
void setReplyToStoreEntry(StoreEntry *e, const char *reason)
replaces current response store entry with the given one
void requestTimeout(const CommTimeoutCbParams &params)
void append(char const *buf, int len)
Definition: String.cc:161
SBuf httpMakeVaryMark(HttpRequest *request, HttpReply const *reply)
Definition: http.cc:609
Security::CertPointer serverCert
Definition: ServerBump.h:57
const char * findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
std::string const & getBody() const
Current body. If parsing is not finished the method returns incompleted body.
struct SquidConfig::@123 ssl_client
Acl::Answer const & fastCheck()
Definition: Checklist.cc:336
void clientSetKeepaliveFlag(ClientHttpRequest *http)
decide whether to expect multiple requests on the corresponding connection
Definition: client_side.cc:644
Security::ContextPointer GenerateSslContextUsingPkeyAndCertFromMemory(const char *data, Security::ServerOptions &, bool trusted)
Definition: support.cc:769
void finishDechunkingRequest(bool withSuccess)
put parsed content into input buffer and clean up
bool readMore
needs comm_read (for this request or new requests)
Definition: client_side.h:132
StatHist hitSvcTime
Definition: StatCounters.h:51
ftp_port or FTP server
Definition: Message.h:40
void startPeekAndSplice()
Initializes and starts a peek-and-splice negotiation with the SSL client.
void stopSending(const char *error)
note response sending error and close as soon as we read the request
Definition: client_side.cc:976
bool connectionAuth
Definition: RequestFlags.h:76
struct StatCounters::@135 cd
void * BIO_get_data(BIO *table)
Definition: openssl.h:60
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:161
char * toUrl(char *buf, unsigned int len) const
Definition: Address.cc:884
FdNoteId
We cannot send char* FD notes to other processes. Pass int IDs and convert.
Definition: FdNotes.h:20
Helper::ResultCode result
The helper response &#39;result&#39; field.
Definition: Reply.h:59
void manager(const CbcPointer< ConnStateData > &aMgr, const AccessLogEntryPointer &al)
associates the request with a from-client connection manager
Definition: HttpRequest.cc:735
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:96
static const CharacterSet HEXDIG
Definition: CharacterSet.h:85
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
void Read(const Comm::ConnectionPointer &conn, AsyncCall::Pointer &callback)
Definition: Read.cc:40
sslproxy_cert_sign * cert_sign
Definition: SquidConfig.h:517
Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties)
static const std::string code_new_certificate
String code for "new_certificate" messages.
Definition: crtd_message.h:76
Security::ContextPointer GenerateSslContext(CertificateProperties const &, Security::ServerOptions &, bool trusted)
Definition: support.cc:783
size_t appendDomainLen
Definition: SquidConfig.h:216
Security::PrivateKeyPointer signWithPkey
The key of the signing certificate.
Definition: gadgets.h:218
static Pointer Start(AsyncJob *job)
starts a freshly created job (i.e., makes the job asynchronous)
Definition: AsyncJob.cc:23
parameters for the async notePinnedConnectionBecameIdle() call
Definition: client_side.h:170
void IOACB(const CommAcceptCbParams &params)
Definition: CommCalls.h:33
std::vector< ClientDelayPool::Pointer > pools
const char * stoppedSending() const
true if we stopped sending the response
Definition: client_side.h:154
ACLFilledChecklist * clientAclChecklistCreate(const acl_access *acl, ClientHttpRequest *http)
time_t lifetime
Definition: SquidConfig.h:108
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:705
int tvSubMsec(struct timeval, struct timeval)
Definition: stub_time.cc:20
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
const char * xstrerr(int error)
Definition: xstrerror.cc:83
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:26
Http::StreamPointer front() const
get the first request context in the pipeline
Definition: Pipeline.cc:28
static void clientUpdateHierCounters(HierarchyLogEntry *)
Definition: client_side.cc:265
common API for all StartListening() callbacks
void storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx)
unsigned int major
major version number
const char * stoppedReceiving_
the reason why we no longer read the request or nil
Definition: client_side.h:435
int pipeline_max_prefetch
Definition: SquidConfig.h:350
void const char HLPCB void * data
Definition: stub_helper.cc:16
bool configureSSLUsingPkeyAndCertFromMemory(SSL *ssl, const char *data, AnyP::PortCfg &port)
Definition: support.cc:852
CbDataList< Security::CertError > CertErrors
Holds a list of X.509 certificate errors.
Definition: forward.h:57
LogTags logType
the processing tags associated with this request transaction.
Definition: parse.c:104
static int clientIsContentLengthValid(HttpRequest *r)
checks body length of non-chunked requests
Definition: client_side.cc:657
int urlCheckRequest(const HttpRequest *r)
Definition: Uri.cc:818
struct Ssl::ServerBump::@128 act
bumping actions at various bumping steps
StatusCode
Definition: StatusCode.h:20
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:802
int64_t content_length
Definition: Message.h:84
virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io)
Our close handler called by Comm when the pinned connection is closed.
int getMyPort(void)
Definition: tools.cc:1003
void port(unsigned short p)
Definition: Uri.h:92
virtual void sendControlMsg(HttpControlMsg)
called to send the 1xx message and notify the Source
bool parseProxyProtocolHeader()
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
MemObject & mem()
Definition: Store.h:52
thrown by modern "incremental" parsers when they need more data
Definition: forward.h:18
#define cbdataReference(var)
Definition: cbdata.h:341
Http1::TeChunkedParser * bodyParser
parses HTTP/1.1 chunked request body
Definition: client_side.h:101
virtual int pipelinePrefetchMax() const
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
void setIdent(const char *userIdentity)
configure rfc931 user identity for the first time
void sslCrtdHandleReply(const Helper::Reply &reply)
Proccess response from ssl_crtd.
#define DBG_IMPORTANT
Definition: Debug.h:46
void readSomeData()
maybe grow the inBuf and schedule Comm::Read()
Definition: Server.cc:85
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
Comm::ConnectionPointer conn
opened listening socket
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
ConnStateData * conn() const
The client connection manager.
Security::ContextPointer GetFrom(Security::SessionPointer &s)
Helper function to retrieve a (non-locked) ContextPointer from a SessionPointer.
Definition: Session.h:90
ConnStateData * NewServer(MasterXactionPointer &xact)
create a new HTTPS connection handler; never returns NULL
Definition: Http1Server.cc:364
GlobalContextStorage TheGlobalContextStorage
Global cache for store all SSL server certificates.
virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)=0
handle a control message received by context from a peer and call back
Security::CertPointer mimicCert
Certificate to mimic.
Definition: gadgets.h:216
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
bool allowed() const
Definition: Acl.h:143
static const CharacterSet DIGIT
Definition: CharacterSet.h:81
struct StatCounters::@136 netdb
bool connectedOk() const
whether there was a successful connection to (and peeking at) the origin server
Definition: ServerBump.h:44
#define FD_DESC_SZ
Definition: defines.h:46
AnyP::ProtocolVersion transferProtocol
Definition: Server.h:104
SBuf tlsConnectHostOrIp
The TLS server host name appears in CONNECT request or the server ip address for the intercepted requ...
Definition: client_side.h:419
NotePairs::Pointer notes()
bool add(const Key &key, EntryValue *t)
Add an entry to the map.
Definition: LruMap.h:140
void unpinConnection(const bool andClose)
Undo pinConnection() and, optionally, close the pinned connection.
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
bool parseHello(const SBuf &data)
Definition: Handshake.cc:517
void reset()
Definition: MemBuf.cc:132
void setScheme(const AnyP::ProtocolType &p, const char *str)
convert the URL scheme to that given
Definition: Uri.h:70
void expectNoForwarding()
cleans up virgin request [body] forwarding state
#define USER_IDENT_SZ
Definition: defines.h:60
dlink_node node
Definition: clientStream.h:88
enum Http::_method_t MethodType
CertSignAlgorithm
Definition: gadgets.h:150
bool chunked() const
whether message uses chunked Transfer-Encoding
Definition: HttpHeader.h:196
mb_size_t size
Definition: MemBuf.h:135
AnyP::PortCfgPointer portCfg
from HttpPortList
Definition: client_side.cc:166
virtual void endingShutdown()
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
Definition: client_side.cc:681
ClientHttpRequest * http
Definition: Stream.h:132
int varyEvaluateMatch(StoreEntry *entry, HttpRequest *request)
bool receivedFirstByte_
true if at least one byte received on this connection
Definition: Server.h:112
SQUIDCEXTERN CSR clientGetMoreData
Definition: client_side.h:463
void httpsSslBumpStep2AccessCheckDone(Acl::Answer answer, void *data)
#define COMM_SELECT_WRITE
Definition: defines.h:37
void quitAfterError(HttpRequest *request)
Definition: LruMap.h:17
bool initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload)
generates and sends to tunnel.cc a fake request with a given payload
BodyPipe::Pointer expectRequestBody(int64_t size)
static ClientDelayPools * Instance()
void addLocalStorage(Ip::Address const &address, size_t size_of_store)
Create new SSL context storage for the local listening address/port.
std::ostream & startPrint(std::ostream &os) const
starts printing arguments, return os
static ErrorState * NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &)
Creates a general request forwarding error with the right http_status.
Definition: errorpage.cc:667
void getSslContextDone(Security::ContextPointer &)
finish configuring the newly created SSL context"
bundles HTTP 1xx reply and the "successfully forwarded" callback
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
virtual void afterClientWrite(size_t)
processing to sync state after a Comm::Write()
void stopReceiving(const char *error)
note request receiving error and close as soon as we write the response
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:29
void fatal(const char *message)
Definition: fatal.cc:28
void abortChunkedRequestBody(const err_type error)
quit on errors related to chunked request body handling
void clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT< CommAcceptCbPtrFun > > &subCall, const Ipc::FdNoteId fdNote)
accept requests to a given port and inform subCall about them
bool handleRequestBodyData()
Security::ContextPointer createSSLContext(Security::CertPointer &x509, Security::PrivateKeyPointer &pkey, Security::ServerOptions &)
Create SSL context and apply ssl certificate and private key to it.
Definition: support.cc:752
void retrieveParsedInfo(Security::TlsDetails::Pointer const &details)
Extract information from parser stored in TlsDetails object.
static bool AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
find any unused HttpSockets[] slot and store fd there or return false
struct SquidConfig::@113 accessList
HttpRequest * request
int incoming_sockets_accepted
ClientInfo * clientdbGetInfo(const Ip::Address &addr)
Definition: client_db.cc:120
std::string compose() const
static bool OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
void PF(int, void *)
Definition: forward.h:18
const char * internalHostname(void)
Definition: internal.cc:140
bool concurrentRequestQueueFilled() const
StatHist nearMissSvcTime
Definition: StatCounters.h:49
Ip::Address client_netmask
Definition: SquidConfig.h:242
const char * c_str()
Definition: SBuf.cc:526
const SBuf & remaining() const
the remaining unprocessed section of buffer
Definition: Tokenizer.h:44
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
void setReadBufData(SBuf &data)
Definition: bio.h:91
ClientStreamData data
Definition: clientStream.h:94
AccessLogEntry::Pointer al
access.log entry
ClientDelayConfig ClientDelay
Definition: SquidConfig.h:445
struct Http::Stream::@80 flags
#define FQDN_LOOKUP_IF_MISS
Definition: defines.h:48
Http::StatusLine sline
Definition: HttpReply.h:60
Definition: parse.c:160
void host(const char *src)
Definition: Uri.cc:48
Http::MethodType id() const
Definition: RequestMethod.h:76
bool commIsHalfClosed(int fd)
Definition: comm.h:101
const MemBuf & other() const
Definition: Reply.h:42
#define APP_FULLNAME
Definition: version.h:22
void wroteControlMsg(const CommIoCbParams &)
callback to handle Comm::Write completion
#define MAXTCPLISTENPORTS
Definition: PortCfg.h:83
ClientRequestContext * calloutContext
static int port
Definition: ldap_backend.cc:69
MemObject * mem_obj
Definition: Store.h:209
virtual void doneWithControlMsg()
time_t request_start_timeout
Definition: SquidConfig.h:119
void setLogUriToRequestUri()
sets log_uri when we know the current request
SQUIDCEXTERN int tvSubUsec(struct timeval, struct timeval)
Definition: util.c:39
clientStreamNode * getClientReplyContext() const
Definition: Stream.cc:549
void setCode(std::string const &aCode)
Set new request/reply code to compose.
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:75
bool switchedToHttps() const
Definition: client_side.h:249
Security::HandshakeParser tlsParser
Definition: client_side.h:273
char const * termedBuf() const
Definition: SquidString.h:91
char * internalLocalUri(const char *dir, const SBuf &name)
Definition: internal.cc:130
int log_mime_hdrs
Definition: SquidConfig.h:286
char * content()
start of the added data
Definition: MemBuf.h:41
struct ClientHttpRequest::Out out
static void clientNegotiateSSL(int fd, void *data)
unsigned short tlsConnectPort
The TLS server port number as passed in the CONNECT request.
Definition: client_side.h:420
bool proxyProtocolError(const char *reason)
static void clientUpdateStatCounters(const LogTags &logType)
Definition: client_side.cc:200
Parsed Parse(const SBuf &)
Definition: Parser.cc:241
static void httpsEstablish(ConnStateData *connState, const Security::ContextPointer &ctx)
void const char * buf
Definition: stub_helper.cc:16
int kind
which custom access list verb matched
Definition: Acl.h:155
virtual void receivedFirstByte()
Update flags and timeout after the first byte received.
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:157
class AccessLogEntry::CacheDetails cache
struct SquidConfig::@112 onoff
void commSetTcpKeepalive(int fd, int idle, int interval, int timeout)
Definition: comm.cc:1183
Http::StreamPointer back() const
get the last request context in the pipeline
Definition: Pipeline.cc:40
acl_access * cert_error
Definition: SquidConfig.h:516
bool skip(const SBuf &tokenToSkip)
Definition: Tokenizer.cc:179
const char * fqdncache_gethostbyaddr(const Ip::Address &addr, int flags)
Definition: fqdncache.cc:480
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const
assigns uninitialized adapted_request and url ALE components
CertSignAlgorithm signAlgorithm
The signing algorithm to use.
Definition: gadgets.h:223
struct SquidConfig::@107 Addrs
struct StatCounters::@131 icp
Ip::Address local
Definition: Connection.h:141
void clientSocketDetach(clientStreamNode *node, ClientHttpRequest *http)
Definition: client_side.cc:834
HttpHeader header
Definition: Message.h:75
static std::ostream & Extra(std::ostream &os)
prefixes each grouped debugs() line after the first one in the group
Definition: Debug.h:104
bool handleIdleClientPinnedTlsRead()
bool accelerated
Definition: RequestFlags.h:58
bool mayUseConnection() const
Definition: Stream.h:140
virtual void doneWithControlMsg()
void StartListening()
accept connections on all configured ftp_ports
Definition: FtpServer.cc:266
void clean()
Definition: MemBuf.cc:113
struct timeval start
Definition: PingData.h:18
SBuf vary_headers
Definition: MemObject.h:208
bool serveDelayedError(Http::Stream *)
void postHttpsAccept()
the second part of old httpsAccept, waiting for future HttpsServer home
void tvSub(struct timeval &res, struct timeval const &t1, struct timeval const &t2)
Definition: time.cc:41
Security::CertPointer signWithX509
Certificate to sign the generated request.
Definition: gadgets.h:217
StatHist allSvcTime
Definition: StatCounters.h:52
void composeRequest(Ssl::CertificateProperties const &)
bool clientParseRequests()
Traffic parsing.
void comm_reset_close(const Comm::ConnectionPointer &conn)
Definition: comm.cc:793
Http::Stream * abortRequestParsing(const char *const errUri)
stop parsing the request and create context for relaying error info
char rfc931[USER_IDENT_SZ]
Definition: Connection.h:171
Ipc::FdNoteId portTypeNote
Type of IPC socket being opened.
Definition: client_side.cc:167
bool configureSSL(SSL *ssl, CertificateProperties const &properties, AnyP::PortCfg &port)
Definition: support.cc:829
int half_closed_clients
Definition: SquidConfig.h:291
char * buf
Definition: MemBuf.h:134
bool checkX509ServerValidity(X509 *cert, const char *server)
Definition: support.cc:251
RequestFlags flags
Definition: HttpRequest.h:141
unsigned short initial
void clientOpenListenSockets(void)
#define MYNAME
Definition: Debug.h:170
char * url
Definition: tcp-banger2.c:114
TlsDetails::Pointer details
TLS handshake meta info or nil.
Definition: Handshake.h:70
bool empty() const
whether there are none or any requests currently pipelined
Definition: Pipeline.h:56
int has(Http::HdrType id) const
Definition: HttpHeader.cc:980
SBuf sslCommonName_
CN name for SSL certificate generation.
Definition: client_side.h:421
bool proxyProtocolValidateClient()
Ip::Address remote
Definition: Connection.h:144
StatHist missSvcTime
Definition: StatCounters.h:48
#define COMM_TRANSPARENT
Definition: Connection.h:50
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition: LogTags.h:84
Definition: Xaction.cc:47
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1041
void InRamCertificateDbKey(const Ssl::CertificateProperties &certProperties, SBuf &key)
Definition: support.cc:1199
void kick()
try to make progress on a transaction or read more I/O
Definition: client_side.cc:895
void readNextRequest()
Definition: client_side.cc:855
void clean()
Definition: HttpHeader.cc:189
const char * stoppedSending_
the reason why we no longer write the response or nil
Definition: client_side.h:433
MasterXaction::Pointer xaction
Transaction which this call is part of.
Definition: CommCalls.h:105
String rangeBoundaryStr() const
Definition: client_side.cc:775
const Acl::Tree * changeAcl(const Acl::Tree *t)
Definition: Checklist.h:176
int dnsWait
sum of DNS lookup delays in milliseconds, for dt
Definition: HttpRequest.h:159
void stopProducingFor(RefCount< BodyPipe > &, bool atEof)
Definition: BodyPipe.cc:107
void getSslContextStart()
Start to create dynamic Security::ContextPointer for host or uses static port SSL context...
#define PROF_start(probename)
Definition: Profiler.h:62
bool proxyKeepalive
Definition: RequestFlags.h:38
bool shouldPreserveClientData() const
void httpsPeeked(PinnedIdleContext pic)
called by FwdState when it is done bumping the server
virtual bool handleReadData()
LocalContextStorage * getLocalStorage(Ip::Address const &address)
Return the local storage for the given listening address/port.
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:264
bool transparent() const
SQUIDCEXTERN CSS clientReplyStatus
Definition: client_side.h:464
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:149
AnyP::PortCfgPointer port
<