Reply.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 84 Helper process maintenance */
10 
11 #include "squid.h"
12 #include "ConfigParser.h"
13 #include "debug/Stream.h"
14 #include "helper.h"
15 #include "helper/Reply.h"
16 #include "rfc1738.h"
17 #include "SquidString.h"
18 
20  result(Helper::Unknown)
21 {
22 }
23 
24 bool
25 Helper::Reply::accumulate(const char *buf, size_t len)
26 {
27  if (other_.isNull())
28  other_.init(4*1024, 1*1024*1024);
29 
30  if (other_.potentialSpaceSize() < static_cast<mb_size_t>(len))
31  return false; // no space left
32 
33  other_.append(buf, len);
34  return true;
35 }
36 
37 void
39 {
40  debugs(84, 3, "Parsing helper buffer");
41  // check we have something to parse
42  if (!other_.hasContent()) {
43  // empty line response was the old URL-rewriter interface ERR response.
44  result = Helper::Error;
45  // for now ensure that legacy handlers are not presented with NULL strings.
46  debugs(84, 3, "Zero length reply");
47  return;
48  }
49 
50  char *p = other_.content();
51  size_t len = other_.contentSize();
52  bool sawNA = false;
53 
54  // optimization: do not consider parsing result code if the response is short.
55  // URL-rewriter may return relative URLs or empty response for a large portion
56  // of its replies.
57  if (len >= 2) {
58  debugs(84, 3, "Buff length is larger than 2");
59  // some helper formats (digest auth, URL-rewriter) just send a data string
60  // we must also check for the ' ' character after the response token (if anything)
61  if (!strncmp(p,"OK",2) && (len == 2 || p[2] == ' ')) {
62  debugs(84, 3, "helper Result = OK");
63  result = Helper::Okay;
64  p+=2;
65  } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) {
66  debugs(84, 3, "helper Result = ERR");
67  result = Helper::Error;
68  p+=3;
69  } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) {
70  debugs(84, 3, "helper Result = BH");
71  result = Helper::BrokenHelper;
72  p+=2;
73  } else if (!strncmp(p,"TT ",3)) {
74  // NTLM challenge token
75  result = Helper::TT;
76  p+=3;
77  // followed by an auth token
78  char *w1 = strwordtok(nullptr, &p);
79  if (w1 != nullptr) {
80  const char *authToken = w1;
81  notes.add("token",authToken);
82  } else {
83  // token field is mandatory on this response code
84  result = Helper::BrokenHelper;
85  notes.add("message","Missing 'token' data");
86  }
87 
88  } else if (!strncmp(p,"AF ",3)) {
89  // NTLM/Negotiate OK response
90  result = Helper::Okay;
91  p+=3;
92  // followed by:
93  // an optional auth token and user field
94  // or, an optional username field
95  char *w1 = strwordtok(nullptr, &p);
96  char *w2 = strwordtok(nullptr, &p);
97  if (w2 != nullptr) {
98  // Negotiate "token user"
99  const char *authToken = w1;
100  notes.add("token",authToken);
101 
102  const char *user = w2;
103  notes.add("user",user);
104 
105  } else if (w1 != nullptr) {
106  // NTLM "user"
107  const char *user = w1;
108  notes.add("user",user);
109  }
110  } else if (!strncmp(p,"NA ",3)) {
111  // NTLM fail-closed ERR response
112  result = Helper::Error;
113  p+=3;
114  sawNA=true;
115  }
116 
117  for (; xisspace(*p); ++p); // skip whitespace
118  }
119 
120  other_.consume(p - other_.content());
121  other_.consumeWhitespacePrefix();
122 
123  // Hack for backward-compatibility: Do not parse for kv-pairs on NA response
124  if (!sawNA)
125  parseResponseKeys();
126 
127  // Hack for backward-compatibility: BH and NA used to be a text message...
128  if (other_.hasContent() && (sawNA || result == Helper::BrokenHelper)) {
129  notes.add("message", other_.content());
130  other_.clean();
131  }
132 }
133 
135 static bool
137 {
138  if (c >= 'a' && c <= 'z')
139  return true;
140 
141  if (c >= 'A' && c <= 'Z')
142  return true;
143 
144  if (c >= '0' && c <= '9')
145  return true;
146 
147  if (c == '-' || c == '_')
148  return true;
149 
150  // prevent other characters matching the key=value
151  return false;
152 }
153 
154 void
156 {
157  // parse a "key=value" pair off the 'other()' buffer.
158  while (other_.hasContent()) {
159  char *p = other_.content();
160  const char *key = p;
161  while (*p && isKeyNameChar(*p)) ++p;
162  if (*p != '=')
163  return; // done. Not a key.
164 
165  // whitespace between key and value is prohibited.
166  // workaround strwordtok() which skips whitespace prefix.
167  if (xisspace(*(p+1)))
168  return; // done. Not a key.
169 
170  *p = '\0';
171  ++p;
172 
173  // the value may be a quoted string or a token
174  const bool urlDecode = (*p != '"'); // check before moving p.
175  char *v = strwordtok(nullptr, &p);
176  if (v != nullptr && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes
177  rfc1738_unescape(v);
178 
179  notes.add(key, v ? v : ""); // value can be empty, but must not be NULL
180 
181  other_.consume(p - other_.content());
182  other_.consumeWhitespacePrefix();
183  }
184 }
185 
186 const MemBuf &
188 {
189  static MemBuf empty;
190  if (empty.isNull())
191  empty.init(1, 1);
192  return empty;
193 }
194 
195 std::ostream &
196 operator <<(std::ostream &os, const Helper::Reply &r)
197 {
198  os << "{result=";
199  switch (r.result) {
200  case Helper::Okay:
201  os << "OK";
202  break;
203  case Helper::Error:
204  os << "ERR";
205  break;
207  os << "BH";
208  break;
209  case Helper::TT:
210  os << "TT";
211  break;
212  case Helper::TimedOut:
213  os << "Timeout";
214  break;
215  case Helper::Unknown:
216  os << "Unknown";
217  break;
218  }
219 
220  // dump the helper key=pair "notes" list
221  if (!r.notes.empty()) {
222  os << ", notes={";
223  os << r.notes.toString("; ");
224  os << "}";
225  }
226 
227  MemBuf const &o = r.other();
228  if (o.hasContent())
229  os << ", other: \"" << o.content() << '\"';
230 
231  os << '}';
232 
233  return os;
234 }
235 
ssize_t mb_size_t
Definition: MemBuf.h:17
static bool isKeyNameChar(char c)
restrict key names to alphanumeric, hyphen, underscore characters
Definition: Reply.cc:136
std::ostream & operator<<(std::ostream &os, const Helper::Reply &r)
Definition: Reply.cc:196
char * strwordtok(char *buf, char **t)
Definition: String.cc:393
const MemBuf & emptyBuf() const
Return an empty MemBuf.
Definition: Reply.cc:187
const MemBuf & other() const
Definition: Reply.h:42
NotePairs notes
Definition: Reply.h:62
Helper::ResultCode result
The helper response 'result' field.
Definition: Reply.h:59
Reply()
Creates a NULL reply.
Definition: Reply.cc:19
void finalize()
Definition: Reply.cc:38
bool accumulate(const char *buf, size_t len)
Definition: Reply.cc:25
void parseResponseKeys()
Definition: Reply.cc:155
Definition: MemBuf.h:24
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
char * content()
start of the added data
Definition: MemBuf.h:41
int isNull() const
Definition: MemBuf.cc:145
bool hasContent() const
Definition: MemBuf.h:54
bool empty() const
Definition: Notes.h:253
const char * toString(const char *sep="\r\n") const
Definition: Notes.cc:286
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
helper protocol primitives
Definition: ChildConfig.h:13
@ Unknown
Definition: ResultCode.h:17
@ BrokenHelper
Definition: ResultCode.h:20
@ Error
Definition: ResultCode.h:19
@ TimedOut
Definition: ResultCode.h:21
@ Okay
Definition: ResultCode.h:18
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
#define xisspace(x)
Definition: xis.h:17

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors