1 /*
2  * "tcp" rules processing
3  *
4  * Copyright 2000-2016 Willy Tarreau <w@1wt.eu>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  *
11  */
12 #include <common/cfgparse.h>
13 #include <common/compat.h>
14 #include <common/config.h>
15 #include <common/debug.h>
16 #include <common/initcall.h>
17 #include <common/mini-clist.h>
18 #include <common/standard.h>
19 #include <common/ticks.h>
20 #include <common/time.h>
21 
22 #include <types/arg.h>
23 #include <types/capture.h>
24 #include <types/connection.h>
25 #include <types/global.h>
26 
27 #include <proto/acl.h>
28 #include <proto/action.h>
29 #include <proto/channel.h>
30 #include <proto/connection.h>
31 #include <proto/log.h>
32 #include <proto/proxy.h>
33 #include <proto/sample.h>
34 #include <proto/stick_table.h>
35 #include <proto/stream.h>
36 #include <proto/stream_interface.h>
37 #include <proto/tcp_rules.h>
38 
39 /* List head of all known action keywords for "tcp-request connection" */
40 struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
41 struct list tcp_req_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_keywords);
42 struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
43 struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
44 
45 /*
46  * Register keywords.
47  */
tcp_req_conn_keywords_register(struct action_kw_list * kw_list)48 void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
49 {
50 	LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
51 }
52 
tcp_req_sess_keywords_register(struct action_kw_list * kw_list)53 void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
54 {
55 	LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
56 }
57 
tcp_req_cont_keywords_register(struct action_kw_list * kw_list)58 void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
59 {
60 	LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
61 }
62 
tcp_res_cont_keywords_register(struct action_kw_list * kw_list)63 void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
64 {
65 	LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
66 }
67 
68 /*
69  * Return the struct tcp_req_action_kw associated to a keyword.
70  */
tcp_req_conn_action(const char * kw)71 static struct action_kw *tcp_req_conn_action(const char *kw)
72 {
73 	return action_lookup(&tcp_req_conn_keywords, kw);
74 }
75 
tcp_req_sess_action(const char * kw)76 static struct action_kw *tcp_req_sess_action(const char *kw)
77 {
78 	return action_lookup(&tcp_req_sess_keywords, kw);
79 }
80 
tcp_req_cont_action(const char * kw)81 static struct action_kw *tcp_req_cont_action(const char *kw)
82 {
83 	return action_lookup(&tcp_req_cont_keywords, kw);
84 }
85 
tcp_res_cont_action(const char * kw)86 static struct action_kw *tcp_res_cont_action(const char *kw)
87 {
88 	return action_lookup(&tcp_res_cont_keywords, kw);
89 }
90 
91 /* This function performs the TCP request analysis on the current request. It
92  * returns 1 if the processing can continue on next analysers, or zero if it
93  * needs more data, encounters an error, or wants to immediately abort the
94  * request. It relies on buffers flags, and updates s->req->analysers. The
95  * function may be called for frontend rules and backend rules. It only relies
96  * on the backend pointer so this works for both cases.
97  */
tcp_inspect_request(struct stream * s,struct channel * req,int an_bit)98 int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit)
99 {
100 	struct session *sess = s->sess;
101 	struct act_rule *rule;
102 	struct stksess *ts;
103 	struct stktable *t;
104 	int partial;
105 	int act_flags = 0;
106 
107 	DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%lu analysers=%02x\n",
108 		now_ms, __FUNCTION__,
109 		s,
110 		req,
111 		req->rex, req->wex,
112 		req->flags,
113 		ci_data(req),
114 		req->analysers);
115 
116 	/* We don't know whether we have enough data, so must proceed
117 	 * this way :
118 	 * - iterate through all rules in their declaration order
119 	 * - if one rule returns MISS, it means the inspect delay is
120 	 *   not over yet, then return immediately, otherwise consider
121 	 *   it as a non-match.
122 	 * - if one rule returns OK, then return OK
123 	 * - if one rule returns KO, then return KO
124 	 */
125 
126 	if ((req->flags & CF_SHUTR) || channel_full(req, global.tune.maxrewrite) ||
127 	    !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
128 		partial = SMP_OPT_FINAL;
129 	else
130 		partial = 0;
131 
132 	/* If "the current_rule_list" match the executed rule list, we are in
133 	 * resume condition. If a resume is needed it is always in the action
134 	 * and never in the ACL or converters. In this case, we initialise the
135 	 * current rule, and go to the action execution point.
136 	 */
137 	if (s->current_rule) {
138 		rule = s->current_rule;
139 		s->current_rule = NULL;
140 		if (s->current_rule_list == &s->be->tcp_req.inspect_rules)
141 			goto resume_execution;
142 	}
143 	s->current_rule_list = &s->be->tcp_req.inspect_rules;
144 
145 	list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
146 		enum acl_test_res ret = ACL_TEST_PASS;
147 
148 		if (rule->cond) {
149 			ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ | partial);
150 			if (ret == ACL_TEST_MISS)
151 				goto missing_data;
152 
153 			ret = acl_pass(ret);
154 			if (rule->cond->pol == ACL_COND_UNLESS)
155 				ret = !ret;
156 		}
157 
158 		if (ret) {
159 			act_flags |= ACT_FLAG_FIRST;
160 resume_execution:
161 			/* we have a matching rule. */
162 			if (rule->action == ACT_ACTION_ALLOW) {
163 				break;
164 			}
165 			else if (rule->action == ACT_ACTION_DENY) {
166 				si_must_kill_conn(chn_prod(req));
167 				channel_abort(req);
168 				channel_abort(&s->res);
169 				req->analysers = 0;
170 
171 				HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
172 				HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
173 				if (sess->listener && sess->listener->counters)
174 					HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
175 
176 				if (!(s->flags & SF_ERR_MASK))
177 					s->flags |= SF_ERR_PRXCOND;
178 				if (!(s->flags & SF_FINST_MASK))
179 					s->flags |= SF_FINST_R;
180 				return 0;
181 			}
182 			else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
183 				/* Note: only the first valid tracking parameter of each
184 				 * applies.
185 				 */
186 				struct stktable_key *key;
187 				struct sample smp;
188 
189 				if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]))
190 					continue;
191 
192 				t = rule->arg.trk_ctr.table.t;
193 				key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.trk_ctr.expr, &smp);
194 
195 				if ((smp.flags & SMP_F_MAY_CHANGE) && !(partial & SMP_OPT_FINAL))
196 					goto missing_data; /* key might appear later */
197 
198 				if (key && (ts = stktable_get_entry(t, key))) {
199 					stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
200 					stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
201 					if (sess->fe != s->be)
202 						stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
203 				}
204 			}
205 			else if (rule->action == ACT_TCP_CAPTURE) {
206 				struct sample *key;
207 				struct cap_hdr *h = rule->arg.cap.hdr;
208 				char **cap = s->req_cap;
209 				int len;
210 
211 				key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
212 				if (!key)
213 					continue;
214 
215 				if (key->flags & SMP_F_MAY_CHANGE)
216 					goto missing_data;
217 
218 				if (cap[h->index] == NULL)
219 					cap[h->index] = pool_alloc(h->pool);
220 
221 				if (cap[h->index] == NULL) /* no more capture memory */
222 					continue;
223 
224 				len = key->data.u.str.data;
225 				if (len > h->len)
226 					len = h->len;
227 
228 				memcpy(cap[h->index], key->data.u.str.area,
229 				       len);
230 				cap[h->index][len] = 0;
231 			}
232 			else {
233 				/* Custom keywords. */
234 				if (!rule->action_ptr)
235 					continue;
236 
237 				if (partial & SMP_OPT_FINAL)
238 					act_flags |= ACT_FLAG_FINAL;
239 
240 				switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
241 				case ACT_RET_ERR:
242 				case ACT_RET_CONT:
243 					continue;
244 				case ACT_RET_STOP:
245 					break;
246 				case ACT_RET_YIELD:
247 					s->current_rule = rule;
248 					goto missing_data;
249 				}
250 				break; /* ACT_RET_STOP */
251 			}
252 		}
253 	}
254 
255 	/* if we get there, it means we have no rule which matches, or
256 	 * we have an explicit accept, so we apply the default accept.
257 	 */
258 	req->analysers &= ~an_bit;
259 	req->analyse_exp = TICK_ETERNITY;
260 	return 1;
261 
262  missing_data:
263 	channel_dont_connect(req);
264 	/* just set the request timeout once at the beginning of the request */
265 	if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
266 		req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
267 	return 0;
268 
269 }
270 
271 /* This function performs the TCP response analysis on the current response. It
272  * returns 1 if the processing can continue on next analysers, or zero if it
273  * needs more data, encounters an error, or wants to immediately abort the
274  * response. It relies on buffers flags, and updates s->rep->analysers. The
275  * function may be called for backend rules.
276  */
tcp_inspect_response(struct stream * s,struct channel * rep,int an_bit)277 int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
278 {
279 	struct session *sess = s->sess;
280 	struct act_rule *rule;
281 	int partial;
282 	int act_flags = 0;
283 
284 	DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%lu analysers=%02x\n",
285 		now_ms, __FUNCTION__,
286 		s,
287 		rep,
288 		rep->rex, rep->wex,
289 		rep->flags,
290 		ci_data(rep),
291 		rep->analysers);
292 
293 	/* We don't know whether we have enough data, so must proceed
294 	 * this way :
295 	 * - iterate through all rules in their declaration order
296 	 * - if one rule returns MISS, it means the inspect delay is
297 	 *   not over yet, then return immediately, otherwise consider
298 	 *   it as a non-match.
299 	 * - if one rule returns OK, then return OK
300 	 * - if one rule returns KO, then return KO
301 	 */
302 	if ((rep->flags & CF_SHUTR) || channel_full(rep, global.tune.maxrewrite) ||
303 	    !s->be->tcp_rep.inspect_delay || tick_is_expired(rep->analyse_exp, now_ms))
304 		partial = SMP_OPT_FINAL;
305 	else
306 		partial = 0;
307 
308 	/* If "the current_rule_list" match the executed rule list, we are in
309 	 * resume condition. If a resume is needed it is always in the action
310 	 * and never in the ACL or converters. In this case, we initialise the
311 	 * current rule, and go to the action execution point.
312 	 */
313 	if (s->current_rule) {
314 		rule = s->current_rule;
315 		s->current_rule = NULL;
316 		if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
317 			goto resume_execution;
318 	}
319 	s->current_rule_list = &s->be->tcp_rep.inspect_rules;
320 
321 	list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
322 		enum acl_test_res ret = ACL_TEST_PASS;
323 
324 		if (rule->cond) {
325 			ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
326 			if (ret == ACL_TEST_MISS) {
327 				/* just set the analyser timeout once at the beginning of the response */
328 				if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
329 					rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
330 				return 0;
331 			}
332 
333 			ret = acl_pass(ret);
334 			if (rule->cond->pol == ACL_COND_UNLESS)
335 				ret = !ret;
336 		}
337 
338 		if (ret) {
339 			act_flags |= ACT_FLAG_FIRST;
340 resume_execution:
341 			/* we have a matching rule. */
342 			if (rule->action == ACT_ACTION_ALLOW) {
343 				break;
344 			}
345 			else if (rule->action == ACT_ACTION_DENY) {
346 				si_must_kill_conn(chn_prod(rep));
347 				channel_abort(rep);
348 				channel_abort(&s->req);
349 				rep->analysers = 0;
350 
351 				HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
352 				HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_resp, 1);
353 				if (sess->listener && sess->listener->counters)
354 					HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1);
355 
356 				if (!(s->flags & SF_ERR_MASK))
357 					s->flags |= SF_ERR_PRXCOND;
358 				if (!(s->flags & SF_FINST_MASK))
359 					s->flags |= SF_FINST_D;
360 				return 0;
361 			}
362 			else if (rule->action == ACT_TCP_CLOSE) {
363 				chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
364 				si_must_kill_conn(chn_prod(rep));
365 				si_shutr(chn_prod(rep));
366 				si_shutw(chn_prod(rep));
367 				break;
368 			}
369 			else {
370 				/* Custom keywords. */
371 				if (!rule->action_ptr)
372 					continue;
373 
374 				if (partial & SMP_OPT_FINAL)
375 					act_flags |= ACT_FLAG_FINAL;
376 
377 				switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
378 				case ACT_RET_ERR:
379 				case ACT_RET_CONT:
380 					continue;
381 				case ACT_RET_STOP:
382 					break;
383 				case ACT_RET_YIELD:
384 					channel_dont_close(rep);
385 					s->current_rule = rule;
386 					if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
387 						rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
388 					return 0;
389 				}
390 				break; /* ACT_RET_STOP */
391 			}
392 		}
393 	}
394 
395 	/* if we get there, it means we have no rule which matches, or
396 	 * we have an explicit accept, so we apply the default accept.
397 	 */
398 	rep->analysers &= ~an_bit;
399 	rep->analyse_exp = TICK_ETERNITY;
400 	return 1;
401 }
402 
403 
404 /* This function performs the TCP layer4 analysis on the current request. It
405  * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
406  * matches or if no more rule matches. It can only use rules which don't need
407  * any data. This only works on connection-based client-facing stream interfaces.
408  */
tcp_exec_l4_rules(struct session * sess)409 int tcp_exec_l4_rules(struct session *sess)
410 {
411 	struct act_rule *rule;
412 	struct stksess *ts;
413 	struct stktable *t = NULL;
414 	struct connection *conn = objt_conn(sess->origin);
415 	int result = 1;
416 	enum acl_test_res ret;
417 
418 	if (!conn)
419 		return result;
420 
421 	list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
422 		ret = ACL_TEST_PASS;
423 
424 		if (rule->cond) {
425 			ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
426 			ret = acl_pass(ret);
427 			if (rule->cond->pol == ACL_COND_UNLESS)
428 				ret = !ret;
429 		}
430 
431 		if (ret) {
432 			/* we have a matching rule. */
433 			if (rule->action == ACT_ACTION_ALLOW) {
434 				break;
435 			}
436 			else if (rule->action == ACT_ACTION_DENY) {
437 				HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
438 				if (sess->listener && sess->listener->counters)
439 					HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
440 
441 				result = 0;
442 				break;
443 			}
444 			else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
445 				/* Note: only the first valid tracking parameter of each
446 				 * applies.
447 				 */
448 				struct stktable_key *key;
449 
450 				if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
451 					continue;
452 
453 				t = rule->arg.trk_ctr.table.t;
454 				key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
455 
456 				if (key && (ts = stktable_get_entry(t, key)))
457 					stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
458 			}
459 			else if (rule->action == ACT_TCP_EXPECT_PX) {
460 				conn->flags |= CO_FL_ACCEPT_PROXY;
461 				conn_sock_want_recv(conn);
462 			}
463 			else if (rule->action == ACT_TCP_EXPECT_CIP) {
464 				conn->flags |= CO_FL_ACCEPT_CIP;
465 				conn_sock_want_recv(conn);
466 			}
467 			else {
468 				/* Custom keywords. */
469 				if (!rule->action_ptr)
470 					break;
471 				switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
472 				case ACT_RET_YIELD:
473 					/* yield is not allowed at this point. If this return code is
474 					 * used it is a bug, so I prefer to abort the process.
475 					 */
476 					send_log(sess->fe, LOG_WARNING,
477 					         "Internal error: yield not allowed with tcp-request connection actions.");
478 				case ACT_RET_STOP:
479 					break;
480 				case ACT_RET_CONT:
481 					continue;
482 				case ACT_RET_ERR:
483 					result = 0;
484 					break;
485 				}
486 				break; /* ACT_RET_STOP */
487 			}
488 		}
489 	}
490 	return result;
491 }
492 
493 /* This function performs the TCP layer5 analysis on the current request. It
494  * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
495  * matches or if no more rule matches. It can only use rules which don't need
496  * any data. This only works on session-based client-facing stream interfaces.
497  * An example of valid use case is to track a stick-counter on the source
498  * address extracted from the proxy protocol.
499  */
tcp_exec_l5_rules(struct session * sess)500 int tcp_exec_l5_rules(struct session *sess)
501 {
502 	struct act_rule *rule;
503 	struct stksess *ts;
504 	struct stktable *t = NULL;
505 	int result = 1;
506 	enum acl_test_res ret;
507 
508 	list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
509 		ret = ACL_TEST_PASS;
510 
511 		if (rule->cond) {
512 			ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
513 			ret = acl_pass(ret);
514 			if (rule->cond->pol == ACL_COND_UNLESS)
515 				ret = !ret;
516 		}
517 
518 		if (ret) {
519 			/* we have a matching rule. */
520 			if (rule->action == ACT_ACTION_ALLOW) {
521 				break;
522 			}
523 			else if (rule->action == ACT_ACTION_DENY) {
524 				HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
525 				if (sess->listener && sess->listener->counters)
526 					HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
527 
528 				result = 0;
529 				break;
530 			}
531 			else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
532 				/* Note: only the first valid tracking parameter of each
533 				 * applies.
534 				 */
535 				struct stktable_key *key;
536 
537 				if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
538 					continue;
539 
540 				t = rule->arg.trk_ctr.table.t;
541 				key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
542 
543 				if (key && (ts = stktable_get_entry(t, key)))
544 					stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
545 			}
546 			else {
547 				/* Custom keywords. */
548 				if (!rule->action_ptr)
549 					break;
550 				switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
551 				case ACT_RET_YIELD:
552 					/* yield is not allowed at this point. If this return code is
553 					 * used it is a bug, so I prefer to abort the process.
554 					 */
555 					send_log(sess->fe, LOG_WARNING,
556 					         "Internal error: yield not allowed with tcp-request session actions.");
557 				case ACT_RET_STOP:
558 					break;
559 				case ACT_RET_CONT:
560 					continue;
561 				case ACT_RET_ERR:
562 					result = 0;
563 					break;
564 				}
565 				break; /* ACT_RET_STOP */
566 			}
567 		}
568 	}
569 	return result;
570 }
571 
572 /* Parse a tcp-response rule. Return a negative value in case of failure */
tcp_parse_response_rule(char ** args,int arg,int section_type,struct proxy * curpx,struct proxy * defpx,struct act_rule * rule,char ** err,unsigned int where,const char * file,int line)573 static int tcp_parse_response_rule(char **args, int arg, int section_type,
574                                    struct proxy *curpx, struct proxy *defpx,
575                                    struct act_rule *rule, char **err,
576                                    unsigned int where,
577                                    const char *file, int line)
578 {
579 	if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
580 		memprintf(err, "%s %s is only allowed in 'backend' sections",
581 		          args[0], args[1]);
582 		return -1;
583 	}
584 
585 	if (strcmp(args[arg], "accept") == 0) {
586 		arg++;
587 		rule->action = ACT_ACTION_ALLOW;
588 	}
589 	else if (strcmp(args[arg], "reject") == 0) {
590 		arg++;
591 		rule->action = ACT_ACTION_DENY;
592 	}
593 	else if (strcmp(args[arg], "close") == 0) {
594 		arg++;
595 		rule->action = ACT_TCP_CLOSE;
596 	}
597 	else {
598 		struct action_kw *kw;
599 		kw = tcp_res_cont_action(args[arg]);
600 		if (kw) {
601 			arg++;
602 			rule->from = ACT_F_TCP_RES_CNT;
603 			rule->kw = kw;
604 			if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
605 				return -1;
606 		} else {
607 			action_build_list(&tcp_res_cont_keywords, &trash);
608 			memprintf(err,
609 			          "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
610 			          args[0], args[1], trash.area,
611 			          proxy_type_str(curpx), curpx->id, args[arg]);
612 			return -1;
613 		}
614 	}
615 
616 	if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
617 		if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
618 			memprintf(err,
619 			          "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
620 			          args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
621 			return -1;
622 		}
623 	}
624 	else if (*args[arg]) {
625 		memprintf(err,
626 			 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
627 			 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
628 		return -1;
629 	}
630 	return 0;
631 }
632 
633 
634 
635 /* Parse a tcp-request rule. Return a negative value in case of failure */
tcp_parse_request_rule(char ** args,int arg,int section_type,struct proxy * curpx,struct proxy * defpx,struct act_rule * rule,char ** err,unsigned int where,const char * file,int line)636 static int tcp_parse_request_rule(char **args, int arg, int section_type,
637                                   struct proxy *curpx, struct proxy *defpx,
638                                   struct act_rule *rule, char **err,
639                                   unsigned int where, const char *file, int line)
640 {
641 	if (curpx == defpx) {
642 		memprintf(err, "%s %s is not allowed in 'defaults' sections",
643 		          args[0], args[1]);
644 		return -1;
645 	}
646 
647 	if (!strcmp(args[arg], "accept")) {
648 		arg++;
649 		rule->action = ACT_ACTION_ALLOW;
650 	}
651 	else if (!strcmp(args[arg], "reject")) {
652 		arg++;
653 		rule->action = ACT_ACTION_DENY;
654 	}
655 	else if (strcmp(args[arg], "capture") == 0) {
656 		struct sample_expr *expr;
657 		struct cap_hdr *hdr;
658 		int kw = arg;
659 		int len = 0;
660 
661 		if (!(curpx->cap & PR_CAP_FE)) {
662 			memprintf(err,
663 			          "'%s %s %s' : proxy '%s' has no frontend capability",
664 			          args[0], args[1], args[kw], curpx->id);
665 			return -1;
666 		}
667 
668 		if (!(where & SMP_VAL_FE_REQ_CNT)) {
669 			memprintf(err,
670 				  "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
671 				  args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
672 			return -1;
673 		}
674 
675 		arg++;
676 
677 		curpx->conf.args.ctx = ARGC_CAP;
678 		expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
679 		if (!expr) {
680 			memprintf(err,
681 			          "'%s %s %s' : %s",
682 			          args[0], args[1], args[kw], *err);
683 			return -1;
684 		}
685 
686 		if (!(expr->fetch->val & where)) {
687 			memprintf(err,
688 			          "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
689 			          args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
690 			release_sample_expr(expr);
691 			return -1;
692 		}
693 
694 		if (strcmp(args[arg], "len") == 0) {
695 			arg++;
696 			if (!args[arg]) {
697 				memprintf(err,
698 					  "'%s %s %s' : missing length value",
699 					  args[0], args[1], args[kw]);
700 				release_sample_expr(expr);
701 				return -1;
702 			}
703 			/* we copy the table name for now, it will be resolved later */
704 			len = atoi(args[arg]);
705 			if (len <= 0) {
706 				memprintf(err,
707 					  "'%s %s %s' : length must be > 0",
708 					  args[0], args[1], args[kw]);
709 				release_sample_expr(expr);
710 				return -1;
711 			}
712 			arg++;
713 		}
714 
715 		if (!len) {
716 			memprintf(err,
717 				  "'%s %s %s' : a positive 'len' argument is mandatory",
718 				  args[0], args[1], args[kw]);
719 			free(expr);
720 			return -1;
721 		}
722 
723 		hdr = calloc(1, sizeof(*hdr));
724 		hdr->next = curpx->req_cap;
725 		hdr->name = NULL; /* not a header capture */
726 		hdr->namelen = 0;
727 		hdr->len = len;
728 		hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
729 		hdr->index = curpx->nb_req_cap++;
730 
731 		curpx->req_cap = hdr;
732 		curpx->to_log |= LW_REQHDR;
733 
734 		/* check if we need to allocate an hdr_idx struct for HTTP parsing */
735 		curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
736 
737 		rule->arg.cap.expr = expr;
738 		rule->arg.cap.hdr = hdr;
739 		rule->action = ACT_TCP_CAPTURE;
740 	}
741 	else if (strncmp(args[arg], "track-sc", 8) == 0) {
742 		struct sample_expr *expr;
743 		int kw = arg;
744 		unsigned int tsc_num;
745 		const char *tsc_num_str;
746 
747 		arg++;
748 
749 		tsc_num_str = &args[kw][8];
750 		if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
751 			memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
752 			return -1;
753 		}
754 
755 		curpx->conf.args.ctx = ARGC_TRK;
756 		expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
757 		if (!expr) {
758 			memprintf(err,
759 			          "'%s %s %s' : %s",
760 			          args[0], args[1], args[kw], *err);
761 			return -1;
762 		}
763 
764 		if (!(expr->fetch->val & where)) {
765 			memprintf(err,
766 			          "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
767 			          args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
768 			release_sample_expr(expr);
769 			return -1;
770 		}
771 
772 		/* check if we need to allocate an hdr_idx struct for HTTP parsing */
773 		curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
774 
775 		if (strcmp(args[arg], "table") == 0) {
776 			arg++;
777 			if (!args[arg]) {
778 				memprintf(err,
779 					  "'%s %s %s' : missing table name",
780 					  args[0], args[1], args[kw]);
781 				release_sample_expr(expr);
782 				return -1;
783 			}
784 			/* we copy the table name for now, it will be resolved later */
785 			rule->arg.trk_ctr.table.n = strdup(args[arg]);
786 			arg++;
787 		}
788 		rule->arg.trk_ctr.expr = expr;
789 		rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
790 		rule->check_ptr = check_trk_action;
791 	}
792 	else if (strcmp(args[arg], "expect-proxy") == 0) {
793 		if (strcmp(args[arg+1], "layer4") != 0) {
794 			memprintf(err,
795 				  "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
796 				  args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
797 			return -1;
798 		}
799 
800 		if (!(where & SMP_VAL_FE_CON_ACC)) {
801 			memprintf(err,
802 				  "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
803 				  args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
804 			return -1;
805 		}
806 
807 		arg += 2;
808 		rule->action = ACT_TCP_EXPECT_PX;
809 	}
810 	else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
811 		if (strcmp(args[arg+1], "layer4") != 0) {
812 			memprintf(err,
813 				  "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
814 				  args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
815 			return -1;
816 		}
817 
818 		if (!(where & SMP_VAL_FE_CON_ACC)) {
819 			memprintf(err,
820 				  "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
821 				  args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
822 			return -1;
823 		}
824 
825 		arg += 2;
826 		rule->action = ACT_TCP_EXPECT_CIP;
827 	}
828 	else {
829 		struct action_kw *kw;
830 		if (where & SMP_VAL_FE_CON_ACC) {
831 			/* L4 */
832 			kw = tcp_req_conn_action(args[arg]);
833 			rule->kw = kw;
834 			rule->from = ACT_F_TCP_REQ_CON;
835 		} else if (where & SMP_VAL_FE_SES_ACC) {
836 			/* L5 */
837 			kw = tcp_req_sess_action(args[arg]);
838 			rule->kw = kw;
839 			rule->from = ACT_F_TCP_REQ_SES;
840 		} else {
841 			/* L6 */
842 			kw = tcp_req_cont_action(args[arg]);
843 			rule->kw = kw;
844 			rule->from = ACT_F_TCP_REQ_CNT;
845 		}
846 		if (kw) {
847 			arg++;
848 			if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
849 				return -1;
850 		} else {
851 			if (where & SMP_VAL_FE_CON_ACC)
852 				action_build_list(&tcp_req_conn_keywords, &trash);
853 			else if (where & SMP_VAL_FE_SES_ACC)
854 				action_build_list(&tcp_req_sess_keywords, &trash);
855 			else
856 				action_build_list(&tcp_req_cont_keywords, &trash);
857 			memprintf(err,
858 			          "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
859 			          "in %s '%s' (got '%s').\n",
860 			          args[0], args[1], MAX_SESS_STKCTR-1,
861 			          trash.area, proxy_type_str(curpx),
862 			          curpx->id, args[arg]);
863 			return -1;
864 		}
865 	}
866 
867 	if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
868 		if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
869 			memprintf(err,
870 			          "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
871 			          args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
872 			return -1;
873 		}
874 	}
875 	else if (*args[arg]) {
876 		memprintf(err,
877 			 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
878 			 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
879 		return -1;
880 	}
881 	return 0;
882 }
883 
884 /* This function should be called to parse a line starting with the "tcp-response"
885  * keyword.
886  */
tcp_parse_tcp_rep(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)887 static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
888                              struct proxy *defpx, const char *file, int line,
889                              char **err)
890 {
891 	const char *ptr = NULL;
892 	unsigned int val;
893 	int warn = 0;
894 	int arg;
895 	struct act_rule *rule;
896 	unsigned int where;
897 	const struct acl *acl;
898 	const char *kw;
899 
900 	if (!*args[1]) {
901 		memprintf(err, "missing argument for '%s' in %s '%s'",
902 		          args[0], proxy_type_str(curpx), curpx->id);
903 		return -1;
904 	}
905 
906 	if (strcmp(args[1], "inspect-delay") == 0) {
907 		if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
908 			memprintf(err, "%s %s is only allowed in 'backend' sections",
909 			          args[0], args[1]);
910 			return -1;
911 		}
912 
913 		if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
914 			memprintf(err,
915 			          "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
916 			          args[0], args[1], proxy_type_str(curpx), curpx->id);
917 			if (ptr)
918 				memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
919 			return -1;
920 		}
921 
922 		if (curpx->tcp_rep.inspect_delay) {
923 			memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
924 			          args[0], args[1], proxy_type_str(curpx), curpx->id);
925 			return 1;
926 		}
927 		curpx->tcp_rep.inspect_delay = val;
928 		return 0;
929 	}
930 
931 	rule = calloc(1, sizeof(*rule));
932 	LIST_INIT(&rule->list);
933 	arg = 1;
934 	where = 0;
935 
936 	if (strcmp(args[1], "content") == 0) {
937 		arg++;
938 
939 		if (curpx->cap & PR_CAP_FE)
940 			where |= SMP_VAL_FE_RES_CNT;
941 		if (curpx->cap & PR_CAP_BE)
942 			where |= SMP_VAL_BE_RES_CNT;
943 
944 		if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
945 			goto error;
946 
947 		acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
948 		if (acl) {
949 			if (acl->name && *acl->name)
950 				memprintf(err,
951 					  "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
952 					  acl->name, args[0], args[1], sample_ckp_names(where));
953 			else
954 				memprintf(err,
955 					  "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
956 					  args[0], args[1],
957 					  LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
958 					  sample_ckp_names(where));
959 
960 			warn++;
961 		}
962 		else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
963 			if (acl->name && *acl->name)
964 				memprintf(err,
965 					  "acl '%s' involves keyword '%s' which is incompatible with '%s'",
966 					  acl->name, kw, sample_ckp_names(where));
967 			else
968 				memprintf(err,
969 					  "anonymous acl involves keyword '%s' which is incompatible with '%s'",
970 					  kw, sample_ckp_names(where));
971 			warn++;
972 		}
973 
974 		LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
975 	}
976 	else {
977 		memprintf(err,
978 		          "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
979 		          args[0], proxy_type_str(curpx), curpx->id, args[1]);
980 		goto error;
981 	}
982 
983 	return warn;
984  error:
985 	free(rule);
986 	return -1;
987 }
988 
989 
990 /* This function should be called to parse a line starting with the "tcp-request"
991  * keyword.
992  */
tcp_parse_tcp_req(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)993 static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
994                              struct proxy *defpx, const char *file, int line,
995                              char **err)
996 {
997 	const char *ptr = NULL;
998 	unsigned int val;
999 	int warn = 0;
1000 	int arg;
1001 	struct act_rule *rule;
1002 	unsigned int where;
1003 	const struct acl *acl;
1004 	const char *kw;
1005 
1006 	if (!*args[1]) {
1007 		if (curpx == defpx)
1008 			memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1009 		else
1010 			memprintf(err, "missing argument for '%s' in %s '%s'",
1011 			          args[0], proxy_type_str(curpx), curpx->id);
1012 		return -1;
1013 	}
1014 
1015 	if (!strcmp(args[1], "inspect-delay")) {
1016 		if (curpx == defpx) {
1017 			memprintf(err, "%s %s is not allowed in 'defaults' sections",
1018 			          args[0], args[1]);
1019 			return -1;
1020 		}
1021 
1022 		if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1023 			memprintf(err,
1024 			          "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1025 			          args[0], args[1], proxy_type_str(curpx), curpx->id);
1026 			if (ptr)
1027 				memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1028 			return -1;
1029 		}
1030 
1031 		if (curpx->tcp_req.inspect_delay) {
1032 			memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1033 			          args[0], args[1], proxy_type_str(curpx), curpx->id);
1034 			return 1;
1035 		}
1036 		curpx->tcp_req.inspect_delay = val;
1037 		return 0;
1038 	}
1039 
1040 	rule = calloc(1, sizeof(*rule));
1041 	LIST_INIT(&rule->list);
1042 	arg = 1;
1043 	where = 0;
1044 
1045 	if (strcmp(args[1], "content") == 0) {
1046 		arg++;
1047 
1048 		if (curpx->cap & PR_CAP_FE)
1049 			where |= SMP_VAL_FE_REQ_CNT;
1050 		if (curpx->cap & PR_CAP_BE)
1051 			where |= SMP_VAL_BE_REQ_CNT;
1052 
1053 		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1054 			goto error;
1055 
1056 		acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1057 		if (acl) {
1058 			if (acl->name && *acl->name)
1059 				memprintf(err,
1060 					  "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1061 					  acl->name, args[0], args[1], sample_ckp_names(where));
1062 			else
1063 				memprintf(err,
1064 					  "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1065 					  args[0], args[1],
1066 					  LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1067 					  sample_ckp_names(where));
1068 
1069 			warn++;
1070 		}
1071 		else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1072 			if (acl->name && *acl->name)
1073 				memprintf(err,
1074 					  "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1075 					  acl->name, kw, sample_ckp_names(where));
1076 			else
1077 				memprintf(err,
1078 					  "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1079 					  kw, sample_ckp_names(where));
1080 			warn++;
1081 		}
1082 
1083 		/* the following function directly emits the warning */
1084 		warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1085 		LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1086 	}
1087 	else if (strcmp(args[1], "connection") == 0) {
1088 		arg++;
1089 
1090 		if (!(curpx->cap & PR_CAP_FE)) {
1091 			memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1092 			          args[0], args[1], proxy_type_str(curpx), curpx->id);
1093 			goto error;
1094 		}
1095 
1096 		where |= SMP_VAL_FE_CON_ACC;
1097 
1098 		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1099 			goto error;
1100 
1101 		acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1102 		if (acl) {
1103 			if (acl->name && *acl->name)
1104 				memprintf(err,
1105 					  "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1106 					  acl->name, args[0], args[1], sample_ckp_names(where));
1107 			else
1108 				memprintf(err,
1109 					  "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1110 					  args[0], args[1],
1111 					  LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1112 					  sample_ckp_names(where));
1113 
1114 			warn++;
1115 		}
1116 		else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1117 			if (acl->name && *acl->name)
1118 				memprintf(err,
1119 					  "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1120 					  acl->name, kw, sample_ckp_names(where));
1121 			else
1122 				memprintf(err,
1123 					  "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1124 					  kw, sample_ckp_names(where));
1125 			warn++;
1126 		}
1127 
1128 		/* the following function directly emits the warning */
1129 		warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1130 		LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1131 	}
1132 	else if (strcmp(args[1], "session") == 0) {
1133 		arg++;
1134 
1135 		if (!(curpx->cap & PR_CAP_FE)) {
1136 			memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1137 			          args[0], args[1], proxy_type_str(curpx), curpx->id);
1138 			goto error;
1139 		}
1140 
1141 		where |= SMP_VAL_FE_SES_ACC;
1142 
1143 		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1144 			goto error;
1145 
1146 		acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1147 		if (acl) {
1148 			if (acl->name && *acl->name)
1149 				memprintf(err,
1150 					  "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1151 					  acl->name, args[0], args[1], sample_ckp_names(where));
1152 			else
1153 				memprintf(err,
1154 					  "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1155 					  args[0], args[1],
1156 					  LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1157 					  sample_ckp_names(where));
1158 			warn++;
1159 		}
1160 		else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1161 			if (acl->name && *acl->name)
1162 				memprintf(err,
1163 					  "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1164 					  acl->name, kw, sample_ckp_names(where));
1165 			else
1166 				memprintf(err,
1167 					  "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1168 					  kw, sample_ckp_names(where));
1169 			warn++;
1170 		}
1171 
1172 		/* the following function directly emits the warning */
1173 		warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1174 		LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1175 	}
1176 	else {
1177 		if (curpx == defpx)
1178 			memprintf(err,
1179 			          "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1180 			          args[0], args[1]);
1181 		else
1182 			memprintf(err,
1183 			          "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1184 			          args[0], proxy_type_str(curpx), curpx->id, args[1]);
1185 		goto error;
1186 	}
1187 
1188 	return warn;
1189  error:
1190 	free(rule);
1191 	return -1;
1192 }
1193 
1194 static struct cfg_kw_list cfg_kws = {ILH, {
1195 	{ CFG_LISTEN, "tcp-request",  tcp_parse_tcp_req },
1196 	{ CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1197 	{ 0, NULL, NULL },
1198 }};
1199 
1200 INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
1201 
1202 /*
1203  * Local variables:
1204  *  c-indent-level: 8
1205  *  c-basic-offset: 8
1206  * End:
1207  */
1208