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