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